Mantid
Loading...
Searching...
No Matches
FindPeaks.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9
13#include "MantidAPI/TableRow.h"
17#include "MantidIndexing/GlobalSpectrumIndex.h"
18#include "MantidIndexing/IndexInfo.h"
22
25#include <boost/algorithm/string.hpp>
26#include <boost/math/special_functions/round.hpp>
27#include <numeric>
28
29#include <fstream>
30
31using namespace Mantid;
32using namespace Mantid::Kernel;
33using namespace Mantid::API;
34using namespace Mantid::DataObjects;
35using namespace Mantid::HistogramData;
36using namespace Mantid::Indexing;
37
38// const double MINHEIGHT = 2.00000001;
39
40namespace Mantid::Algorithms {
41
42// Register the algorithm into the AlgorithmFactory
43DECLARE_ALGORITHM(FindPeaks)
44
45//----------------------------------------------------------------------------------------------
49 : m_peakParameterNames(), m_bkgdParameterNames(), m_bkgdOrder(0), m_outPeakTableWS(), m_dataWS(),
50 m_inputPeakFWHM(0), m_highBackground(false), m_rawPeaksTable(false), m_numTableParams(0),
51 m_centreIndex(1) /* for Gaussian */, m_peakFuncType(""), m_backgroundType(""), m_vecPeakCentre(),
52 m_vecFitWindows(), m_backgroundFunction(), m_peakFunction(), m_minGuessedPeakWidth(0), m_maxGuessedPeakWidth(0),
53 m_stepGuessedPeakWidth(0), m_usePeakPositionTolerance(false), m_peakPositionTolerance(0.0), m_fitFunctions(),
54 m_peakLeftIndexes(), m_peakRightIndexes(), m_minimizer("Levenberg-MarquardtMD"), m_costFunction(),
55 m_minHeight(0.0), m_leastMaxObsY(0.), m_useObsCentre(false) {}
56
57//----------------------------------------------------------------------------------------------
61 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
62 "Name of the workspace to search");
63
64 auto mustBeNonNegative = std::make_shared<BoundedValidator<int>>();
65 mustBeNonNegative->setLower(0);
66 declareProperty("WorkspaceIndex", EMPTY_INT(), mustBeNonNegative,
67 "If set, only this spectrum will be searched for peaks "
68 "(otherwise all are)");
69
70 auto min = std::make_shared<BoundedValidator<int>>();
71 min->setLower(1);
72 // The estimated width of a peak in terms of number of channels
73 declareProperty("FWHM", 7, min, "Estimated number of points covered by the fwhm of a peak (default 7)");
74
75 // The tolerance allowed in meeting the conditions
76 declareProperty("Tolerance", 4, min,
77 "A measure of the strictness desired in "
78 "meeting the condition on peak "
79 "candidates,\n"
80 "Mariscotti recommends 2 (default 4)");
81
82 declareProperty(std::make_unique<ArrayProperty<double>>("PeakPositions"),
83 "Optional: enter a comma-separated list of the expected "
84 "X-position of the centre of the peaks. Only peaks near "
85 "these positions will be fitted.");
86
87 declareProperty(std::make_unique<ArrayProperty<double>>("FitWindows"),
88 "Optional: enter a comma-separated list of the expected "
89 "X-position of windows to fit. The number of values must be "
90 "exactly double the number of specified peaks.");
91
92 std::vector<std::string> peakNames = FunctionFactory::Instance().getFunctionNames<API::IPeakFunction>();
93 declareProperty("PeakFunction", "Gaussian", std::make_shared<StringListValidator>(peakNames));
94
95 std::vector<std::string> bkgdtypes{"Flat", "Linear", "Quadratic"};
96 declareProperty("BackgroundType", "Linear", std::make_shared<StringListValidator>(bkgdtypes), "Type of Background.");
97
98 declareProperty("HighBackground", true, "Flag whether the input data has high background compared to peak heights.");
99
100 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
101 mustBePositive->setLower(1);
102 declareProperty("MinGuessedPeakWidth", 2, mustBePositive,
103 "Minimum guessed peak width for fit. It is in unit of number of pixels.");
104
105 declareProperty("MaxGuessedPeakWidth", 10, mustBePositive,
106 "Maximum guessed peak width for fit. It is in unit of number of pixels.");
107
108 declareProperty("GuessedPeakWidthStep", 2, mustBePositive,
109 "Step of guessed peak width. It is in unit of number of pixels.");
110
111 auto mustBePositiveDBL = std::make_shared<BoundedValidator<double>>();
112 declareProperty("PeakPositionTolerance", EMPTY_DBL(), mustBePositiveDBL,
113 "Tolerance on the found peaks' positions against the input "
114 "peak positions. Non-positive value indicates that this "
115 "option is turned off.");
116
117 // The found peaks in a table
119 "The name of the TableWorkspace in which to store the list "
120 "of peaks found");
121
122 declareProperty("RawPeakParameters", false,
123 "false generates table with effective centre/width/height "
124 "parameters. true generates a table with peak function "
125 "parameters");
126
127 declareProperty("MinimumPeakHeight", DBL_MIN, "Minimum allowed peak height. ");
128
129 declareProperty("MinimumPeakHeightObs", 0.0,
130 "Least value of the maximum observed Y value of a peak within "
131 "specified region. If any peak's maximum observed Y value is smaller, "
132 "then "
133 "this peak will not be fit. It is designed for EventWorkspace with "
134 "integer counts.");
135
136 std::array<std::string, 2> costFuncOptions = {{"Chi-Square", "Rwp"}};
137 declareProperty("CostFunction", "Chi-Square",
138 Kernel::IValidator_sptr(new Kernel::ListValidator<std::string>(costFuncOptions)), "Cost functions");
139
140 std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys();
141
142 declareProperty("Minimizer", "Levenberg-MarquardtMD",
144 "Minimizer to use for fitting. Minimizers available are "
145 "\"Levenberg-Marquardt\", \"Simplex\","
146 "\"Conjugate gradient (Fletcher-Reeves imp.)\", \"Conjugate "
147 "gradient (Polak-Ribiere imp.)\", \"BFGS\", and "
148 "\"Levenberg-MarquardtMD\"");
149
150 declareProperty("StartFromObservedPeakCentre", true, "Use observed value as the starting value of peak centre. ");
151}
152
153//----------------------------------------------------------------------------------------------
157 // Process input
159
160 // Create those functions to fit
162
163 // Set up output table workspace
165
166 // Fit
167 if (!m_vecPeakCentre.empty()) {
168 if (!m_vecFitWindows.empty()) {
169 if (m_vecFitWindows.size() != (m_vecPeakCentre.size() * 2)) {
170 throw std::invalid_argument("Number of FitWindows must be exactly "
171 "twice the number of PeakPositions");
172 }
173 }
174
175 // Perform fit with fixed start positions.
177 } else {
178 // Use Mariscotti's method to find the peak centers
181 }
182
183 // Set output properties
184 g_log.information() << "Total " << m_outPeakTableWS->rowCount() << " peaks found and successfully fitted.\n";
185 setProperty("PeaksList", m_outPeakTableWS);
186} // END: exec()
187
188//----------------------------------------------------------------------------------------------
192 // Input workspace
193 m_dataWS = getProperty("InputWorkspace");
194
195 // WorkspaceIndex
196 int wsIndex = getProperty("WorkspaceIndex");
197 if (!isEmpty(wsIndex)) {
198 if (wsIndex >= static_cast<int>(m_dataWS->getNumberHistograms())) {
199 g_log.warning() << "The value of WorkspaceIndex provided (" << wsIndex
200 << ") is larger than the size of this workspace (" << m_dataWS->getNumberHistograms() << ")\n";
201 throw Kernel::Exception::IndexError(wsIndex, m_dataWS->getNumberHistograms() - 1,
202 "FindPeaks WorkspaceIndex property");
203 }
204 m_indexSet = m_dataWS->indexInfo().makeIndexSet({static_cast<Indexing::GlobalSpectrumIndex>(wsIndex)});
205 } else {
206 m_indexSet = m_dataWS->indexInfo().makeIndexSet();
207 }
208
209 // Peak width
211 int t1 = getProperty("MinGuessedPeakWidth");
212 int t2 = getProperty("MaxGuessedPeakWidth");
213 int t3 = getProperty("GuessedPeakWidthStep");
214 if (t1 > t2 || t1 <= 0 || t3 <= 0) {
215 std::stringstream errss;
216 errss << "User specified wrong guessed peak width parameters (must be "
217 "postive and make sense). "
218 << "User inputs are min = " << t1 << ", max = " << t2 << ", step = " << t3;
219 g_log.warning(errss.str());
220 throw std::runtime_error(errss.str());
221 }
222
226
227 m_peakPositionTolerance = getProperty("PeakPositionTolerance");
231
232 // Specified peak positions, which is optional
233 m_vecPeakCentre = getProperty("PeakPositions");
234 if (!m_vecPeakCentre.empty())
235 std::sort(m_vecPeakCentre.begin(), m_vecPeakCentre.end());
236 m_vecFitWindows = getProperty("FitWindows");
237
238 // Peak and ground type
239 m_peakFuncType = getPropertyValue("PeakFunction");
240 m_backgroundType = getPropertyValue("BackgroundType");
241
242 // Fit algorithm
243 m_highBackground = getProperty("HighBackground");
244
245 // Peak parameters are give via a table workspace
246 m_rawPeaksTable = getProperty("RawPeakParameters");
247
248 // Minimum peak height
249 m_minHeight = getProperty("MinimumPeakHeight");
250
251 // About Fit
252 m_minimizer = getPropertyValue("Minimizer");
253 m_costFunction = getPropertyValue("CostFunction");
254
255 m_useObsCentre = getProperty("StartFromObservedPeakCentre");
256
257 m_leastMaxObsY = getProperty("MinimumPeakHeightObs");
258}
259
260//----------------------------------------------------------------------------------------------
264 m_outPeakTableWS = std::make_shared<TableWorkspace>();
265 m_outPeakTableWS->addColumn("int", "spectrum");
266
267 if (m_rawPeaksTable) {
268 // Output raw peak parameters
269 size_t numpeakpars = m_peakFunction->nParams();
270 size_t numbkgdpars = m_backgroundFunction->nParams();
271 m_numTableParams = numpeakpars + numbkgdpars;
272 if (m_peakFuncType == "Gaussian")
273 m_centreIndex = 1;
274 else if (m_peakFuncType == "LogNormal")
275 m_centreIndex = 1;
276 else if (m_peakFuncType == "Lorentzian")
277 m_centreIndex = 1;
278 else if (m_peakFuncType == "PseudoVoigt")
279 m_centreIndex = 2;
280 else
281 m_centreIndex = m_numTableParams; // bad value
282
283 for (size_t i = 0; i < numpeakpars; ++i)
284 m_outPeakTableWS->addColumn("double", m_peakParameterNames[i]);
285 for (size_t i = 0; i < numbkgdpars; ++i)
286 m_outPeakTableWS->addColumn("double", m_bkgdParameterNames[i]);
287 // m_outPeakTableWS->addColumn("double", "f1.A2");
288 } else {
289 // Output centre, weight, height, A0, A1 and A2
291 m_centreIndex = 0;
292 m_outPeakTableWS->addColumn("double", "centre");
293 m_outPeakTableWS->addColumn("double", "width");
294 m_outPeakTableWS->addColumn("double", "height");
295 m_outPeakTableWS->addColumn("double", "backgroundintercept");
296 m_outPeakTableWS->addColumn("double", "backgroundslope");
297 m_outPeakTableWS->addColumn("double", "A2");
298 }
299
300 m_outPeakTableWS->addColumn("double", "chi2");
301}
302
303//----------------------------------------------------------------------------------------------
310void FindPeaks::findPeaksGivenStartingPoints(const std::vector<double> &peakcentres,
311 const std::vector<double> &fitwindows) {
312 bool useWindows = (!fitwindows.empty());
313 std::size_t numPeaks = peakcentres.size();
314
315 // Loop over the spectra searching for peaks
316 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, m_indexSet.size());
317
318 for (const auto spec : m_indexSet) {
319 const auto &vecX = m_dataWS->x(spec);
320
321 double practical_x_min = vecX.front();
322 double practical_x_max = vecX.back();
323 g_log.information() << "actual x-range = [" << practical_x_min << " -> " << practical_x_max << "]\n";
324 {
325 const auto &vecY = m_dataWS->y(spec);
326 const auto &vecE = m_dataWS->e(spec);
327 const size_t numY = vecY.size();
328 size_t i_min = 1;
329 for (; i_min < numY; ++i_min) {
330 if ((vecY[i_min] != 0.) || (vecE[i_min] != 0)) {
331 --i_min; // bring it back one
332 break;
333 }
334 }
335 practical_x_min = vecX[i_min];
336
337 size_t i_max = numY - 2;
338 for (; i_max > i_min; --i_max) {
339 if ((vecY[i_max] != 0.) || (vecE[i_max] != 0)) {
340 ++i_max; // bring it back one
341 break;
342 }
343 }
344 g_log.debug() << "Finding peaks from giving starting point, with interval i_min = " << i_min
345 << " i_max = " << i_max << '\n';
346 practical_x_max = vecX[i_max];
347 }
348 g_log.information() << "practical x-range = [" << practical_x_min << " -> " << practical_x_max << "]\n";
349
350 for (std::size_t ipeak = 0; ipeak < numPeaks; ipeak++) {
351 // Try to fit at this center
352 double x_center = peakcentres[ipeak];
353
354 std::stringstream infoss;
355 infoss << "Spectrum " << spec << ": Fit peak @ d = " << x_center;
356 if (useWindows) {
357 infoss << " inside fit window [" << fitwindows[2 * ipeak] << ", " << fitwindows[2 * ipeak + 1] << "]";
358 }
359 g_log.information(infoss.str());
360
361 // Check whether it is the in data range
362 if (x_center > practical_x_min && x_center < practical_x_max) {
363 if (useWindows)
364 fitPeakInWindow(m_dataWS, static_cast<int>(spec), x_center, fitwindows[2 * ipeak], fitwindows[2 * ipeak + 1]);
365 else {
366 bool hasLeftPeak = (ipeak > 0);
367 double leftpeakcentre = 0.;
368 if (hasLeftPeak)
369 leftpeakcentre = peakcentres[ipeak - 1];
370
371 bool hasRightPeak = (ipeak < numPeaks - 1);
372 double rightpeakcentre = 0.;
373 if (hasRightPeak)
374 rightpeakcentre = peakcentres[ipeak + 1];
375
376 fitPeakGivenFWHM(m_dataWS, static_cast<int>(spec), x_center, m_inputPeakFWHM, hasLeftPeak, leftpeakcentre,
377 hasRightPeak, rightpeakcentre);
378 }
379 } else {
380 g_log.warning() << "Given peak centre " << x_center << " is out side of given data's range (" << practical_x_min
381 << ", " << practical_x_max << ").\n";
382 addNonFitRecord(spec, x_center);
383 }
384
385 } // loop through the peaks specified
386
387 m_progress->report();
388
389 } // loop over spectra
390}
391
392//----------------------------------------------------------------------------------------------
396 // At this point the data has not been smoothed yet.
397 auto smoothedData = this->calculateSecondDifference(m_dataWS);
398
399 // The optimum number of points in the smoothing, according to Mariscotti, is
400 // 0.6*fwhm
401 auto w = static_cast<int>(0.6 * m_inputPeakFWHM);
402 // w must be odd
403 if (!(w % 2))
404 ++w;
405
406 if (!m_dataWS->isRaggedWorkspace() && (m_dataWS->blocksize() <= static_cast<size_t>(w))) {
407 std::stringstream errss;
408 errss << "Block size of the workspace should be greater than:" << w;
409 throw std::runtime_error(errss.str());
410 }
411
412 smoothData(smoothedData, w, g_z);
413
414 // Now calculate the errors on the smoothed data
415 this->calculateStandardDeviation(m_dataWS, smoothedData, w);
416
417 // Calculate n1 (Mariscotti eqn. 18)
418 const double kz = 1.22; // This kz corresponds to z=5 & w=0.6*fwhm - see Mariscotti Fig. 8
419 const int n1 = boost::math::iround(kz * m_inputPeakFWHM);
420 // Can't calculate n2 or n3 yet because they need i0
421 const int tolerance = getProperty("Tolerance");
422
423 // Loop over the spectra searching for peaks
424 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, m_indexSet.size());
425
426 for (size_t k_out = 0; k_out < m_indexSet.size(); ++k_out) {
427 const size_t k = m_indexSet[k_out];
428 const auto &S = smoothedData[k_out].y();
429 const auto &F = smoothedData[k_out].e();
430
431 // This implements the flow chart given on page 320 of Mariscotti
432 int i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0;
433 for (int i = 1; i < static_cast<int>(S.size()); ++i) {
434
435 int M = 0;
436 if (S[i] > F[i])
437 M = 1;
438 else {
439 S[i] > 0 ? M = 2 : M = 3;
440 }
441
442 if (S[i - 1] > F[i - 1]) {
443 switch (M) {
444 case 3:
445 i3 = i;
446 /* no break */
447 // intentional fall-through
448 case 2:
449 i2 = i - 1;
450 break;
451 case 1:
452 // do nothing
453 break;
454 default:
455 assert(false);
456 // should never happen
457 break;
458 }
459 } else if (S[i - 1] > 0) {
460 switch (M) {
461 case 3:
462 i3 = i;
463 break;
464 case 2:
465 // do nothing
466 break;
467 case 1:
468 i1 = i;
469 break;
470 default:
471 assert(false);
472 // should never happen
473 break;
474 }
475 } else {
476 switch (M) {
477 case 3:
478 // do nothing
479 break;
480 case 2: // fall through (i.e. same action if M = 1 or 2)
481 case 1:
482 i5 = i - 1;
483 break;
484 default:
485 assert(false);
486 // should never happen
487 break;
488 }
489 }
490
491 if (i5 && i1 && i2 && i3) // If i5 has been set then we should have the
492 // full set and can check conditions
493 {
494 i4 = i3; // Starting point for finding i4 - calculated below
495 double num = 0.0, denom = 0.0;
496 for (int j = i3; j <= i5; ++j) {
497 // Calculate i4 - it's at the minimum value of Si between i3 & i5
498 if (S[j] <= S[i4])
499 i4 = j;
500 // Calculate sums for i0 (Mariscotti eqn. 27)
501 num += j * S[j];
502 denom += S[j];
503 }
504 i0 = static_cast<int>(num / denom);
505
506 // Check we have a correctly ordered set of points. If not, reset and
507 // continue
508 if (i1 > i2 || i2 > i3 || i3 > i4 || i5 <= i4) {
509 i5 = 0;
510 continue;
511 }
512
513 // Check if conditions are fulfilled - if any are not, loop onto the
514 // next i in the spectrum
515 // Mariscotti eqn. (14)
516 if (std::abs(S[i4]) < 2 * F[i4]) {
517 i5 = 0;
518 continue;
519 }
520 // Mariscotti eqn. (19)
521 if (abs(i5 - i3 + 1 - n1) > tolerance) {
522 i5 = 0;
523 continue;
524 }
525 // Calculate n2 (Mariscotti eqn. 20)
526 int n2 = std::abs(boost::math::iround(0.5 * (F[i0] / S[i0]) * (n1 + tolerance)));
527 const int n2b = std::abs(boost::math::iround(0.5 * (F[i0] / S[i0]) * (n1 - tolerance)));
528 if (n2b > n2)
529 n2 = n2b;
530 // Mariscotti eqn. (21)
531 const int testVal = n2 ? n2 : 1;
532 if (i3 - i2 - 1 > testVal) {
533 i5 = 0;
534 continue;
535 }
536 // Calculate n3 (Mariscotti eqn. 22)
537 int n3 = std::abs(boost::math::iround((n1 + tolerance) * (1 - 2 * (F[i0] / S[i0]))));
538 const int n3b = std::abs(boost::math::iround((n1 - tolerance) * (1 - 2 * (F[i0] / S[i0]))));
539 if (n3b < n3)
540 n3 = n3b;
541 // Mariscotti eqn. (23)
542 if (i2 - i1 + 1 < n3) {
543 i5 = 0;
544 continue;
545 }
546
547 // If we get to here then we've identified a peak
548 g_log.debug() << "Spectrum=" << k << " i0=" << i0 << " X=" << m_dataWS->x(k)[i0] << " i1=" << i1 << " i2=" << i2
549 << " i3=" << i3 << " i4=" << i4 << " i5=" << i5 << '\n';
550
551 // Use i0, i2 and i4 to find out i_min and i_max, i0: right, i2: left,
552 // i4: centre
553 auto wssize = static_cast<int>(m_dataWS->x(k).size());
554
555 int iwidth = i0 - i2;
556 if (iwidth <= 0)
557 iwidth = 1;
558
559 int i_min = 1;
560 if (i4 > 5 * iwidth)
561 i_min = i4 - 5 * iwidth;
562
563 int i_max = i4 + 5 * iwidth;
564 if (i_max >= wssize)
565 i_max = wssize - 1;
566
567 this->fitSinglePeak(m_dataWS, static_cast<int>(k), i_min, i_max, i4);
568
569 // reset and go searching for the next peak
570 i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0;
571 }
572
573 } // loop through a single spectrum
574
575 m_progress->report();
576
577 } // loop over spectra
578}
579
580//----------------------------------------------------------------------------------------------
590 std::vector<Histogram> diffed;
591
592 // Loop over spectra
593 for (const auto i : m_indexSet) {
594 diffed.emplace_back(input->histogram(i));
595 diffed.back().mutableY() = 0.0;
596 diffed.back().mutableE() = 0.0;
597
598 const auto &Y = input->y(i);
599 auto &S = diffed.back().mutableY();
600 // Go through each spectrum calculating the second difference at each point
601 // First and last points in each spectrum left as zero (you'd never be able
602 // to find peaks that close to the edge anyway)
603 for (size_t j = 1; j < S.size() - 1; ++j) {
604 S[j] = Y[j - 1] - 2 * Y[j] + Y[j + 1];
605 }
606 }
607
608 return diffed;
609}
610
611//----------------------------------------------------------------------------------------------
618void FindPeaks::smoothData(std::vector<Histogram> &histograms, const int w, const int g_z) {
619 for (auto &histogram : histograms)
620 for (int i = 0; i < g_z; ++i)
621 histogram = smooth(histogram, w);
622}
623
624//----------------------------------------------------------------------------------------------
635 std::vector<HistogramData::Histogram> &smoothed, const int &w) {
636 // Guard against anyone changing the value of z, which would mean different
637 // phi values were needed (see Marriscotti p.312)
638 static_assert(g_z == 5, "Value of z has changed!");
639 // Have to adjust for fact that I normalise Si (unlike the paper)
640 const auto factor = static_cast<int>(std::pow(static_cast<double>(w), g_z));
641
642 const double constant = sqrt(static_cast<double>(this->computePhi(w))) / factor;
643
644 for (size_t i = 0; i < m_indexSet.size(); ++i) {
645 size_t i_in = m_indexSet[i];
646 smoothed[i].mutableE() = input->e(i_in) * constant;
647 }
648}
649
650//----------------------------------------------------------------------------------------------
659long long FindPeaks::computePhi(const int &w) const {
660 const int m = (w - 1) / 2;
661 int zz = 0;
662 int max_index_prev = 1;
663 int n_el_prev = 3;
664 std::vector<long long> previous(n_el_prev);
665 previous[0] = 1;
666 previous[1] = -2;
667 previous[2] = 1;
668
669 // Can't happen at present
670 if (g_z == 0)
671 return std::accumulate(previous.begin(), previous.end(), static_cast<long long>(0),
673
674 std::vector<long long> next;
675 // Calculate the Cij iteratively.
676 do {
677 zz++;
678 int max_index = zz * m + 1;
679 int n_el = 2 * max_index + 1;
680 next.resize(n_el);
681 std::fill(next.begin(), next.end(), 0);
682 for (int i = 0; i < n_el; ++i) {
683 int delta = -max_index + i;
684 for (int l = delta - m; l <= delta + m; l++) {
685 int index = l + max_index_prev;
686 if (index >= 0 && index < n_el_prev)
687 next[i] += previous[index];
688 }
689 }
690 previous.resize(n_el);
691 std::copy(next.begin(), next.end(), previous.begin());
692 max_index_prev = max_index;
693 n_el_prev = n_el;
694 } while (zz != g_z);
695
696 const long long retval = std::accumulate(previous.begin(), previous.end(), static_cast<long long>(0),
698 g_log.debug() << "FindPeaks::computePhi - calculated value = " << retval << "\n";
699 return retval;
700}
701
702//----------------------------------------------------------------------------------------------
708int FindPeaks::getIndex(const HistogramX &vecX, double x) {
709 int index;
710
711 if (x <= vecX.front()) {
712 // Left or equal to lower boundary
713 index = 0;
714 } else if (x >= vecX.back()) {
715 // Right or equal to upper boundary
716 index = static_cast<int>(vecX.size()) - 1;
717 } else {
718 // within the range
719 index = static_cast<int>(std::lower_bound(vecX.begin(), vecX.end(), x) - vecX.begin());
720
721 // check lower boundary
722 if (index == 0) {
723 std::stringstream errss;
724 errss << "Returned index = 0 for x = " << x << " with X[0] = " << vecX[0]
725 << ". This situation is ruled out in this algorithm.";
726 g_log.warning(errss.str());
727 throw std::runtime_error(errss.str());
728 } else if (x < vecX[index - 1] || x > vecX[index]) {
729 std::stringstream errss;
730 errss << "Returned x = " << x << " is not between " << vecX[index - 1] << " and " << vecX[index]
731 << ", which are returned by lower_bound.";
732 g_log.warning(errss.str());
733 throw std::runtime_error(errss.str());
734 }
735
736 // Find the index of the nearest value to return
737 if (x - vecX[index - 1] < vecX[index] - x)
738 --index;
739 }
740
741 return index;
742}
743
744//----------------------------------------------------------------------------------------------
761void FindPeaks::fitPeakGivenFWHM(const API::MatrixWorkspace_sptr &input, const int wsIndex, const double center_guess,
762 const int fitWidth, const bool hasleftpeak, const double leftpeakcentre,
763 const bool hasrightpeak, const double rightpeakcentre) {
764 // The X axis you are looking at
765 const auto &vecX = input->x(wsIndex);
766 const auto &vecY = input->y(wsIndex);
767
768 // Find i_center - the index of the center - The guess is within the X axis?
769 int i_centre = this->getIndex(vecX, center_guess);
770
771 // Set up lower fit boundary
772 int i_min = i_centre - 5 * fitWidth;
773 if (i_min < 1)
774 i_min = 1;
775
776 if (hasleftpeak) {
777 // Use 2/3 distance as the seperation for right peak
778 double xmin = vecX[i_min];
779 double peaksepline = center_guess - (center_guess - leftpeakcentre) * 0.66;
780 if (xmin < peaksepline)
781 i_min = getIndex(vecX, peaksepline);
782 }
783
784 // Set up upper boundary
785 int i_max = i_centre + 5 * fitWidth;
786 if (i_max >= static_cast<int>(vecX.size()) - 1)
787 i_max = static_cast<int>(vecY.size()) - 2;
788
789 if (hasrightpeak) {
790 // Use 2/3 distance as the separation for right peak
791 double xmax = vecX[i_max];
792 double peaksepline = center_guess + (rightpeakcentre - center_guess) * 0.66;
793 if (xmax > peaksepline)
794 i_max = getIndex(vecX, peaksepline);
795 }
796
797 // Check
798 if (i_max - i_min <= 0)
799 throw std::runtime_error("Impossible to i_min >= i_max.");
800
801 std::stringstream outss;
802 outss << "Fit peak with guessed FWHM: starting center = " << center_guess << ", FWHM = " << fitWidth
803 << ". Estimated peak fit window from given FWHM: " << vecX[i_min] << ", " << vecX[i_max];
804 g_log.information(outss.str());
805
806 fitSinglePeak(input, wsIndex, i_min, i_max, i_centre);
807}
808
809//----------------------------------------------------------------------------------------------
819void FindPeaks::fitPeakInWindow(const API::MatrixWorkspace_sptr &input, const int wsIndex, const double centre_guess,
820 const double xmin, const double xmax) {
821 // Check
822 g_log.information() << "Fit Peak with given window: Guessed center = " << centre_guess << " x-min = " << xmin
823 << ", x-max = " << xmax << "\n";
824 if (xmin >= centre_guess || xmax <= centre_guess) {
825 g_log.warning("Peak centre is on the edge of Fit window. ");
826 addNonFitRecord(wsIndex, centre_guess);
827 return;
828 }
829
830 // The X axis you are looking at
831 const auto &vecX = input->x(wsIndex);
832
833 // The centre index
834 int i_centre = this->getIndex(vecX, centre_guess);
835
836 // The left index
837 int i_min = getIndex(vecX, xmin);
838 if (i_min >= i_centre) {
839 g_log.warning() << "Input peak centre @ " << centre_guess << " is out side of minimum x = " << xmin
840 << ". Input X ragne = " << vecX.front() << ", " << vecX.back() << "\n";
841 addNonFitRecord(wsIndex, centre_guess);
842 return;
843 }
844
845 // The right index
846 int i_max = getIndex(vecX, xmax);
847 if (i_max < i_centre) {
848 g_log.warning() << "Input peak centre @ " << centre_guess << " is out side of maximum x = " << xmax << "\n";
849 addNonFitRecord(wsIndex, centre_guess);
850 return;
851 }
852
853 // finally do the actual fit
854 fitSinglePeak(input, wsIndex, i_min, i_max, i_centre);
855}
856
857//----------------------------------------------------------------------------------------------
861void FindPeaks::fitSinglePeak(const API::MatrixWorkspace_sptr &input, const int spectrum, const int i_min,
862 const int i_max, const int i_centre) {
863 const auto &vecX = input->x(spectrum);
864 const auto &vecY = input->y(spectrum);
865
866 // Exclude peak with peak counts
867 bool hasHighCounts = false;
868 for (int i = i_min; i <= i_max; ++i)
869 if (vecY[i] > m_leastMaxObsY) {
870 hasHighCounts = true;
871 break;
872 }
873 if (!hasHighCounts) {
874 std::stringstream ess;
875 ess << "Peak supposed at " << vecY[i_centre] << " does not have enough counts as " << m_leastMaxObsY;
876 g_log.debug(ess.str());
877 addNonFitRecord(spectrum, vecY[i_centre]);
878 return;
879 }
880
881 {
882 std::stringstream outss;
883 outss << "Fit single peak in X-range " << vecX[i_min] << ", " << vecX[i_max] << ", centre at " << vecX[i_centre]
884 << " (index = " << i_centre << "). ";
885 g_log.information(outss.str());
886 }
887
888 // Estimate background
889 std::vector<double> vecbkgdparvalue(3, 0.);
890 std::vector<double> vecpeakrange(3, 0.);
891 int usefpdresult = findPeakBackground(input, spectrum, i_min, i_max, vecbkgdparvalue, vecpeakrange);
892 // cppcheck-suppress knownConditionTrueFalse
893 if (usefpdresult < 0) {
894 // Estimate background roughly for a failed case
895 estimateBackground(vecX, vecY, i_min, i_max, vecbkgdparvalue);
896 }
897
898 for (size_t i = 0; i < vecbkgdparvalue.size(); ++i)
899 if (i < m_bkgdOrder)
900 m_backgroundFunction->setParameter(i, vecbkgdparvalue[i]);
901
902 // Estimate peak parameters
903 double est_height(0.0), est_fwhm(0.0);
904 size_t i_obscentre(0);
905 double est_leftfwhm(0.0), est_rightfwhm(0.0);
906 std::string errmsg = estimatePeakParameters(vecX, vecY, i_min, i_max, vecbkgdparvalue, i_obscentre, est_height,
907 est_fwhm, est_leftfwhm, est_rightfwhm);
908 if (!errmsg.empty()) {
909 // Unable to estimate peak
910 i_obscentre = i_centre;
911 est_fwhm = 1.;
912 est_height = 1.;
913 g_log.warning(errmsg);
914 }
915
916 // Set peak parameters to
917 if (m_useObsCentre)
918 m_peakFunction->setCentre(vecX[i_obscentre]);
919 else
920 m_peakFunction->setCentre(vecX[i_centre]);
921 m_peakFunction->setHeight(est_height);
922 m_peakFunction->setFwhm(est_fwhm);
923
924 if (usefpdresult < 0) {
925 // Estimate peak range based on estimated linear background and peak
926 // parameter estimated from observation
927 if (!m_useObsCentre)
928 i_obscentre = i_centre;
929 estimatePeakRange(vecX, i_obscentre, i_min, i_max, est_leftfwhm, est_rightfwhm, vecpeakrange);
930 }
931
932 //-------------------------------------------------------------------------
933 // Fit Peak
934 //-------------------------------------------------------------------------
935 std::vector<double> fitwindow(2);
936 fitwindow[0] = vecX[i_min];
937 fitwindow[1] = vecX[i_max];
938
939 double costfuncvalue = callFitPeak(input, spectrum, m_peakFunction, m_backgroundFunction, fitwindow, vecpeakrange,
941
942 bool fitsuccess = false;
943 if (costfuncvalue < DBL_MAX && costfuncvalue >= 0. && m_peakFunction->height() > m_minHeight) {
944 fitsuccess = true;
945 }
946 if (fitsuccess && m_usePeakPositionTolerance) {
947 fitsuccess = (fabs(m_peakFunction->centre() - vecX[i_centre]) < m_peakPositionTolerance);
948 }
949
950 //-------------------------------------------------------------------------
951 // Process Fit result
952 //-------------------------------------------------------------------------
953 // Update output
954 if (fitsuccess)
956 else
957 addNonFitRecord(spectrum, m_peakFunction->centre());
958}
959
960//----------------------------------------------------------------------------------------------
964int FindPeaks::findPeakBackground(const MatrixWorkspace_sptr &input, int spectrum, size_t i_min, size_t i_max,
965 std::vector<double> &vecBkgdParamValues, std::vector<double> &vecpeakrange) {
966 const auto &vecX = input->x(spectrum);
967
968 // Call FindPeakBackground
969 auto estimate = createChildAlgorithm("FindPeakBackground");
970 estimate->setLoggingOffset(1);
971 estimate->setProperty("InputWorkspace", input);
972 estimate->setProperty("WorkspaceIndex", spectrum);
973 // estimate->setProperty("SigmaConstant", 1.0);
974 std::vector<double> fwvec;
975 fwvec.emplace_back(vecX[i_min]);
976 fwvec.emplace_back(vecX[i_max]);
977 estimate->setProperty("BackgroundType", m_backgroundType);
978 estimate->setProperty("FitWindow", fwvec);
979 estimate->executeAsChildAlg();
980 // Get back the result
981 Mantid::API::ITableWorkspace_sptr peaklisttablews = estimate->getProperty("OutputWorkspace");
982
983 // Determine whether to use FindPeakBackground's result.
984 int fitresult = -1;
985 if (peaklisttablews->columnCount() < 7)
986 throw std::runtime_error("No 7th column for use FindPeakBackground result or not. ");
987
988 if (peaklisttablews->rowCount() > 0) {
994 const int hiddenFitresult = peaklisttablews->Int(0, 6);
995 g_log.information() << "fitresult=" << hiddenFitresult << "\n";
996 }
997
998 // Local check whether FindPeakBackground gives a reasonable value
999 vecpeakrange.resize(2);
1000 if (fitresult > 0) {
1001 // Use FitPeakBackgroud's result
1002 size_t i_peakmin, i_peakmax;
1003 i_peakmin = peaklisttablews->Int(0, 1);
1004 i_peakmax = peaklisttablews->Int(0, 2);
1005
1006 g_log.information() << "FindPeakBackground successful. "
1007 << "iMin = " << i_min << ", iPeakMin = " << i_peakmin << ", iPeakMax = " << i_peakmax
1008 << ", iMax = " << i_max << "\n";
1009
1010 if (i_peakmin < i_peakmax && i_peakmin > i_min + 2 && i_peakmax < i_max - 2) {
1011 // FIXME - It is assumed that there are 3 background parameters set to
1012 // FindPeaksBackground
1013 double bg0, bg1, bg2;
1014 bg0 = peaklisttablews->Double(0, 3);
1015 bg1 = peaklisttablews->Double(0, 4);
1016 bg2 = peaklisttablews->Double(0, 5);
1017
1018 // Set output
1019 vecBkgdParamValues.resize(3, 0.);
1020 vecBkgdParamValues[0] = bg0;
1021 vecBkgdParamValues[1] = bg1;
1022 vecBkgdParamValues[2] = bg2;
1023
1024 g_log.information() << "Background parameters (from FindPeakBackground) A0=" << bg0 << ", A1=" << bg1
1025 << ", A2=" << bg2 << "\n";
1026
1027 vecpeakrange[0] = vecX[i_peakmin];
1028 vecpeakrange[1] = vecX[i_peakmax];
1029 } else {
1030 // Do manual estimation again
1031 g_log.debug("FindPeakBackground result is ignored due to wrong in peak range. ");
1032 }
1033 } else {
1034 g_log.information("Failed to get background estimation\n");
1035 }
1036
1037 std::stringstream outx;
1038 outx << "FindPeakBackground Result: Given window (" << vecX[i_min] << ", " << vecX[i_max]
1039 << "); Determine peak range: (" << vecpeakrange[0] << ", " << vecpeakrange[1] << "). ";
1040 g_log.information(outx.str());
1041
1042 return fitresult;
1043}
1044
1045//----------------------------------------------------------------------------------------------
1062std::string FindPeaks::estimatePeakParameters(const HistogramX &vecX, const HistogramY &vecY, size_t i_min,
1063 size_t i_max, const std::vector<double> &vecbkgdparvalues,
1064 size_t &iobscentre, double &height, double &fwhm, double &leftfwhm,
1065 double &rightfwhm) {
1066 // Search for maximum considering background
1067 const double bg0 = vecbkgdparvalues[0];
1068 double bg1 = 0;
1069 double bg2 = 0;
1070 if (vecbkgdparvalues.size() >= 2) {
1071 bg1 = vecbkgdparvalues[1];
1072 if (vecbkgdparvalues.size() >= 3)
1073 bg2 = vecbkgdparvalues[2];
1074 }
1075
1076 // Starting value
1077 iobscentre = i_min;
1078 const double tmpx = vecX[i_min];
1079 height = vecY[i_min] - (bg0 + bg1 * tmpx + bg2 * tmpx * tmpx);
1080 double lowest = height;
1081
1082 // Extreme case
1083 if (i_max == vecY.size())
1084 i_max = i_max - 1;
1085
1086 // Searching
1087 for (size_t i = i_min + 1; i <= i_max; ++i) {
1088 const double x = vecX[i];
1089 const double tmpheight = vecY[i] - (bg0 + bg1 * x + bg2 * x * x);
1090
1091 if (tmpheight > height) {
1092 iobscentre = i;
1093 height = tmpheight;
1094 } else if (tmpheight < lowest) {
1095 lowest = tmpheight;
1096 }
1097 }
1098
1099 // Summarize on peak centre
1100 double obscentre = vecX[iobscentre];
1101 double drop = height - lowest;
1102 if (drop == 0) {
1103 // Flat spectrum. No peak parameter can be estimated.
1104 // FIXME - should have a second notice such that there will be no more
1105 // fitting.
1106 return "Flat spectrum";
1107 } else if (height <= m_minHeight) {
1108 // The peak is not high enough!
1109 // FIXME - should have a second notice such that there will be no more
1110 // fitting.
1111 return "Fluctuation is less than minimum allowed value.";
1112 }
1113
1114 // If maximum point is on the edge 2 points, return false. One side of peak
1115 // must have at least 3 points
1116 if (iobscentre <= i_min + 1 || iobscentre >= i_max - 1) {
1117 std::stringstream dbss;
1118 dbss << "Maximum value on edge. Fit window is between " << vecX[i_min] << " and " << vecX[i_max]
1119 << ". Maximum value " << vecX[iobscentre] << " is located on (" << iobscentre << ").";
1120 return dbss.str();
1121 }
1122
1123 // Search for half-maximum: no need to very precise
1124
1125 // Slope at the left side of peak.
1126 leftfwhm = -1.;
1127 for (int i = static_cast<int>(iobscentre) - 1; i >= 0; --i) {
1128 double xleft = vecX[i];
1129 double yleft = vecY[i] - (bg0 + bg1 * xleft + bg2 * xleft * xleft);
1130 if (yleft <= 0.5 * height) {
1131 // Ideal case
1132 // FIXME - Need a linear interpolation on it!
1133 leftfwhm = obscentre - 0.5 * (vecX[i] + vecX[i + 1]);
1134 break;
1135 }
1136 }
1137
1138 // Slope at the right side of peak
1139 rightfwhm = -1.;
1140 for (size_t i = iobscentre + 1; i <= i_max; ++i) {
1141 double xright = vecX[i];
1142 double yright = vecY[i] - (bg0 + bg1 * xright + bg2 * xright * xright);
1143 if (yright <= 0.5 * height) {
1144 rightfwhm = 0.5 * (vecX[i] + vecX[i - 1]) - obscentre;
1145 break;
1146 }
1147 }
1148
1149 if (leftfwhm <= 0 || rightfwhm <= 0) {
1150 std::stringstream errmsg;
1151 errmsg << "Estimate peak parameters error (FWHM cannot be zero): Input "
1152 "data size = "
1153 << vecX.size() << ", Xmin = " << vecX[i_min] << "(" << i_min << "), Xmax = " << vecX[i_max] << "(" << i_max
1154 << "); "
1155 << "Estimated peak centre @ " << vecX[iobscentre] << "(" << iobscentre << ") with height = " << height
1156 << "; Lowest Y value = " << lowest << "; Output error: . leftfwhm = " << leftfwhm
1157 << ", right fwhm = " << rightfwhm << ".";
1158 return errmsg.str();
1159 }
1160
1161 fwhm = leftfwhm + rightfwhm;
1162 if (fwhm < 1.e-200) // very narrow peak
1163 {
1164 std::stringstream errmsg;
1165 errmsg << "Estimate peak parameters error (FWHM cannot be zero): Input "
1166 "data size = "
1167 << vecX.size() << ", Xmin = " << vecX[i_min] << "(" << i_min << "), Xmax = " << vecX[i_max] << "(" << i_max
1168 << "); "
1169 << "Estimated peak centre @ " << vecX[iobscentre] << "(" << iobscentre << ") with height = " << height
1170 << "; Lowest Y value = " << lowest << "; Output error: . fwhm = " << fwhm << ".";
1171 return errmsg.str();
1172 }
1173
1174 g_log.information() << "Estimated peak parameters: Centre = " << obscentre << ", Height = " << height
1175 << ", FWHM = " << fwhm << " = " << leftfwhm << " + " << rightfwhm << ".\n";
1176
1177 return std::string();
1178}
1179
1180//----------------------------------------------------------------------------------------------
1191void FindPeaks::estimateBackground(const HistogramX &X, const HistogramY &Y, const size_t i_min, const size_t i_max,
1192 std::vector<double> &vecbkgdparvalues) {
1193 // Validate input
1194 if (i_min >= i_max)
1195 throw std::runtime_error("when trying to estimate the background parameter "
1196 "values: i_min cannot larger or equal to i_max");
1197 if (vecbkgdparvalues.size() < 3)
1198 vecbkgdparvalues.resize(3, 0.);
1199
1200 // FIXME - THIS IS A MAGIC NUMBER
1201 const size_t MAGICNUMBER = 12;
1202 size_t numavg;
1203 if (i_max - i_min > MAGICNUMBER)
1204 numavg = 3;
1205 else
1206 numavg = 1;
1207
1208 // Get (x0, y0) and (xf, yf)
1209 double x0 = 0.0;
1210 double y0 = 0.0;
1211 double xf = 0.0;
1212 double yf = 0.0;
1213
1214 for (size_t i = 0; i < numavg; ++i) {
1215 x0 += X[i_min + i];
1216 y0 += Y[i_min + i];
1217
1218 xf += X[i_max - i];
1219 // X has one more value than Y
1220 yf += Y[i_max - i - 1];
1221 }
1222 x0 = x0 / static_cast<double>(numavg);
1223 y0 = y0 / static_cast<double>(numavg);
1224 xf = xf / static_cast<double>(numavg);
1225 yf = yf / static_cast<double>(numavg);
1226
1227 // Esitmate
1228 vecbkgdparvalues[2] = 0.;
1229 if (m_bkgdOrder >= 1) {
1230 // linear background
1231 vecbkgdparvalues[1] = (y0 - yf) / (x0 - xf);
1232 vecbkgdparvalues[0] = (xf * y0 - x0 * yf) / (xf - x0);
1233 } else {
1234 // flat background
1235 vecbkgdparvalues[1] = 0.;
1236 vecbkgdparvalues[0] = 0.5 * (y0 + yf);
1237 }
1238}
1239
1240//----------------------------------------------------------------------------------------------
1244void FindPeaks::estimatePeakRange(const HistogramX &vecX, size_t i_centre, size_t i_min, size_t i_max,
1245 const double &leftfwhm, const double &rightfwhm, std::vector<double> &vecpeakrange) {
1246 // Check
1247 if (vecpeakrange.size() < 2)
1248 vecpeakrange.resize(2, 0.);
1249
1250 if (i_centre < i_min || i_centre > i_max)
1251 throw std::runtime_error("Estimate peak range input centre is out of fit window. ");
1252
1253 // Search peak left by using 6 * half of FWHM
1254 double peakleftbound = vecX[i_centre] - 6. * leftfwhm;
1255 double peakrightbound = vecX[i_centre] + 6. * rightfwhm;
1256
1257 // Deal with case the peak boundary is too close to fit window
1258 auto ipeakleft = static_cast<size_t>(getIndex(vecX, peakleftbound));
1259 if (ipeakleft <= i_min) {
1260 size_t numbkgdpts = (i_centre - i_min) / 6;
1261 // FIXME - 3 is a magic number
1262 if (numbkgdpts < 3)
1263 numbkgdpts = 3;
1264 ipeakleft = i_min + numbkgdpts;
1265 if (ipeakleft >= i_centre)
1266 ipeakleft = i_min + 1;
1267
1268 peakleftbound = vecX[ipeakleft];
1269 }
1270
1271 auto ipeakright = static_cast<size_t>(getIndex(vecX, peakrightbound));
1272 if (ipeakright >= i_max) {
1273 size_t numbkgdpts = (i_max - i_centre) / 6;
1274 // FIXME - 3 is a magic number
1275 if (numbkgdpts < 3)
1276 numbkgdpts = 3;
1277 ipeakright = i_max - numbkgdpts;
1278 if (ipeakright <= i_centre)
1279 ipeakright = i_max - 1;
1280
1281 peakrightbound = vecX[ipeakright];
1282 }
1283
1284 // Set result to output vector
1285 vecpeakrange[0] = peakleftbound;
1286 vecpeakrange[1] = peakrightbound;
1287}
1288
1289//----------------------------------------------------------------------------------------------
1297void FindPeaks::addInfoRow(const size_t spectrum, const API::IPeakFunction_const_sptr &peakfunction,
1298 const API::IBackgroundFunction_sptr &bkgdfunction, const bool isoutputraw,
1299 const double mincost) {
1300 // Check input validity
1301 if (mincost < 0. || mincost >= DBL_MAX - 1.0E-10)
1302 throw std::runtime_error("Minimum cost indicates that fit fails. This "
1303 "method should not be called "
1304 "under this circumstance. ");
1305
1306 // Add fitted parameters to output table workspace
1307 API::TableRow t = m_outPeakTableWS->appendRow();
1308
1309 // spectrum
1310 t << static_cast<int>(spectrum);
1311
1312 // peak and background function parameters
1313 if (isoutputraw) {
1314 // Output of raw peak parameters
1315 size_t nparams = peakfunction->nParams();
1316 size_t nparamsb = bkgdfunction->nParams();
1317
1318 size_t numcols = m_outPeakTableWS->columnCount();
1319 if (nparams + nparamsb + 2 != numcols) {
1320 throw std::runtime_error("Error 1307 number of columns do not matches");
1321 }
1322
1323 for (size_t i = 0; i < nparams; ++i) {
1324 t << peakfunction->getParameter(i);
1325 }
1326 for (size_t i = 0; i < nparamsb; ++i) {
1327 t << bkgdfunction->getParameter(i);
1328 }
1329 } else {
1330 // Output of effective peak parameters
1331 // Set up parameters to peak function and get effective value
1332 double peakcentre = peakfunction->centre();
1333 double fwhm = peakfunction->fwhm();
1334 double height = peakfunction->height();
1335
1336 t << peakcentre << fwhm << height;
1337
1338 // Set up parameters to background function
1339
1340 // FIXME - Use Polynomial for all 3 background types.
1341 double a0 = bkgdfunction->getParameter("A0");
1342 double a1 = 0.;
1343 if (bkgdfunction->name() != "FlatBackground")
1344 a1 = bkgdfunction->getParameter("A1");
1345 double a2 = 0;
1346 if (bkgdfunction->name() != "LinearBackground" && bkgdfunction->name() != "FlatBackground")
1347 a2 = bkgdfunction->getParameter("A2");
1348
1349 t << a0 << a1 << a2;
1350
1351 g_log.debug() << "Peak parameters found: cen=" << peakcentre << " fwhm=" << fwhm << " height=" << height
1352 << " a0=" << a0 << " a1=" << a1 << " a2=" << a2;
1353 }
1354 g_log.debug() << " chsq=" << mincost << "\n";
1355 // Minimum cost function value
1356 t << mincost;
1357}
1358
1359//----------------------------------------------------------------------------------------------
1364void FindPeaks::addNonFitRecord(const size_t spectrum, const double centre) {
1365 // Add a new row
1366 API::TableRow t = m_outPeakTableWS->appendRow();
1367
1368 g_log.information() << "Failed to fit peak at " << centre << "\n";
1369 // 1st column
1370 t << static_cast<int>(spectrum);
1371
1372 // Parameters
1373 for (std::size_t i = 0; i < m_numTableParams; i++) {
1374 if (i == m_centreIndex)
1375 t << centre;
1376 else
1377 t << 0.;
1378 }
1379
1380 // HUGE chi-square
1381 t << DBL_MAX;
1382}
1383
1384//----------------------------------------------------------------------------------------------
1388 // Setup the background
1389 // FIXME (No In This Ticket) Need to have a uniformed routine to name
1390 // background function
1391 std::string backgroundposix;
1392 if (m_backgroundType != "Quadratic") {
1393 // FlatBackground, LinearBackground, Quadratic
1394 backgroundposix = "Background";
1395 }
1396 m_backgroundFunction = std::dynamic_pointer_cast<IBackgroundFunction>(
1397 API::FunctionFactory::Instance().createFunction(m_backgroundType + backgroundposix));
1398 g_log.information() << "Background function (" << m_backgroundFunction->name() << ") has been created. "
1399 << "\n";
1400
1401 m_bkgdParameterNames = m_backgroundFunction->getParameterNames();
1402 // FIXME - Need to add method nOrder to background function;
1403 m_bkgdOrder = m_backgroundFunction->nParams() - 1;
1404
1405 // Set up peak function
1407 std::dynamic_pointer_cast<IPeakFunction>(API::FunctionFactory::Instance().createFunction(m_peakFuncType));
1408 m_peakParameterNames = m_peakFunction->getParameterNames();
1409}
1410
1411//----------------------------------------------------------------------------------------------
1414double FindPeaks::callFitPeak(const MatrixWorkspace_sptr &dataws, int wsindex,
1415 const API::IPeakFunction_sptr &peakfunction,
1416 const API::IBackgroundFunction_sptr &backgroundfunction,
1417 const std::vector<double> &vec_fitwindow, const std::vector<double> &vec_peakrange,
1418 int minGuessFWHM, int maxGuessFWHM, int guessedFWHMStep, int estBackResult) {
1419 std::stringstream dbss;
1420 dbss << "[Call FitPeak] Fit 1 peak at X = " << peakfunction->centre() << " of spectrum " << wsindex;
1421 g_log.information(dbss.str());
1422
1423 double userFWHM = m_peakFunction->fwhm();
1424 bool fitwithsteppedfwhm = (guessedFWHMStep > 0);
1425
1426 FitOneSinglePeak fitpeak;
1427 fitpeak.setChild(true);
1428 fitpeak.setWorskpace(dataws, wsindex);
1429 fitpeak.setFitWindow(vec_fitwindow[0], vec_fitwindow[1]);
1431 fitpeak.setFunctions(peakfunction, backgroundfunction);
1432 fitpeak.setupGuessedFWHM(userFWHM, minGuessFWHM, maxGuessFWHM, guessedFWHMStep, fitwithsteppedfwhm);
1433 fitpeak.setPeakRange(vec_peakrange[0], vec_peakrange[1]);
1434
1435 if (estBackResult == 1) {
1436 g_log.information("simpleFit");
1437 fitpeak.simpleFit();
1438 } else if (m_highBackground) {
1439 g_log.information("highBkgdFit");
1440 fitpeak.highBkgdFit();
1441 } else {
1442 g_log.information("simpleFit");
1443 fitpeak.simpleFit();
1444 }
1445
1446 double costfuncvalue = fitpeak.getFitCostFunctionValue();
1447 std::string dbinfo = fitpeak.getDebugMessage();
1448 g_log.information(dbinfo);
1449
1450 return costfuncvalue;
1451}
1452
1453//----------------------------------------------------------------------------------------------
1459 size_t numpeakpars = m_peakFunction->nParams();
1460 std::vector<double> vec_value(numpeakpars);
1461 for (size_t i = 0; i < numpeakpars; ++i) {
1462 double parvalue = m_peakFunction->getParameter(i);
1463 vec_value[i] = parvalue;
1464 }
1465
1466 return vec_value;
1467}
1468
1469} // namespace Mantid::Algorithms
1470
1471// 0.5044, 0.5191, 0.535, 0.5526, 0.5936, 0.6178, 0.6453, 0.6768, 0.7134,
1472// 0.7566, 0.8089, 0.8737, 0.9571, 1.0701, 1.2356, 1.5133, 2.1401
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
CostFunctions::CostFuncFitting & m_costFunction
The cost function.
const double MAGICNUMBER
Definition FitPeak.cpp:47
double height
Definition GetAllEi.cpp:155
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
double tolerance
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:423
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
void setChild(const bool isChild) override
To set whether algorithm is a child.
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
This algorithm searches for peaks in a dataset.
Definition FindPeaks.h:50
static const int g_z
The number of smoothing iterations.
Definition FindPeaks.h:156
Indexing::SpectrumIndexSet m_indexSet
list of workspace indicies to check
Definition FindPeaks.h:166
API::ITableWorkspace_sptr m_outPeakTableWS
Storage of the peak data.
Definition FindPeaks.h:159
void init() override
Initialize and declare properties.
Definition FindPeaks.cpp:60
API::IBackgroundFunction_sptr m_backgroundFunction
Definition FindPeaks.h:182
bool m_useObsCentre
Start values.
Definition FindPeaks.h:205
std::vector< double > m_vecPeakCentre
Definition FindPeaks.h:178
void generateOutputPeakParameterTable()
Generate a table workspace for output peak parameters.
std::unique_ptr< API::Progress > m_progress
Progress reporting.
Definition FindPeaks.h:161
void fitPeakGivenFWHM(const API::MatrixWorkspace_sptr &input, const int spectrum, const double center_guess, const int fitWidth, const bool hasleftpeak, const double leftpeakcentre, const bool hasrightpeak, const double rightpeakcentre)
Fit peak by given/guessed FWHM.
int getIndex(const HistogramData::HistogramX &vecX, double x)
needed by FindPeaksBackground
int findPeakBackground(const API::MatrixWorkspace_sptr &input, int spectrum, size_t i_min, size_t i_max, std::vector< double > &vecBkgdParamValues, std::vector< double > &vecpeakrange)
Find peak background.
void processAlgorithmProperties()
Process algorithm's properties.
void createFunctions()
Create peak and background functions.
void fitPeakInWindow(const API::MatrixWorkspace_sptr &input, const int spectrum, const double centre_guess, const double xmin, const double xmax)
Fit peak confined in a given window (x-min, x-max)
void estimatePeakRange(const HistogramData::HistogramX &vecX, size_t i_centre, size_t i_min, size_t i_max, const double &leftfwhm, const double &rightfwhm, std::vector< double > &vecpeakrange)
Estimate peak range based on background peak parameter.
API::MatrixWorkspace_sptr m_dataWS
workspace to check for peaks
Definition FindPeaks.h:164
double m_leastMaxObsY
Minimum value of peak's observed maximum Y value.
Definition FindPeaks.h:202
double m_minHeight
Minimum peak height.
Definition FindPeaks.h:200
API::IPeakFunction_sptr m_peakFunction
Definition FindPeaks.h:183
std::size_t m_numTableParams
parameters or effective (centre, width, height)
Definition FindPeaks.h:172
double callFitPeak(const API::MatrixWorkspace_sptr &dataws, int wsindex, const API::IPeakFunction_sptr &peakfunction, const API::IBackgroundFunction_sptr &backgroundfunction, const std::vector< double > &vec_fitwindow, const std::vector< double > &vec_peakrange, int minGuessFWHM, int maxGuessFWHM, int guessedFWHMStep, int estBackResult=0)
Fit peak by calling 'FitPeak'.
std::vector< double > m_vecFitWindows
Definition FindPeaks.h:179
std::string estimatePeakParameters(const HistogramData::HistogramX &vecX, const HistogramData::HistogramY &vecY, size_t i_min, size_t i_max, const std::vector< double > &vecbkgdparvalues, size_t &iobscentre, double &height, double &fwhm, double &leftfwhm, double &rightfwhm)
Estimate peak parameters.
void calculateStandardDeviation(const API::MatrixWorkspace_const_sptr &input, std::vector< HistogramData::Histogram > &smoothed, const int &w)
Calculates the statistical error on the smoothed data.
long long computePhi(const int &w) const
Calculates the coefficient phi which goes into the calculation of the error on the smoothed data Uses...
void exec() override
Execute the findPeaks algorithm.
bool m_highBackground
flag for find relatively weak peak in high
Definition FindPeaks.h:168
void findPeaksGivenStartingPoints(const std::vector< double > &peakcentres, const std::vector< double > &fitwindows)
Find peaks according to given peak positions.
void findPeaksUsingMariscotti()
Find peaks by searching peak position using Mariscotti algorithm.
void fitSinglePeak(const API::MatrixWorkspace_sptr &input, const int spectrum, const int i_min, const int i_max, const int i_centre)
Fit peak: this is a basic peak fit function as a root function for all different type of user input.
std::vector< std::string > m_bkgdParameterNames
Definition FindPeaks.h:151
std::vector< std::string > m_peakParameterNames
Definition FindPeaks.h:150
void addNonFitRecord(const size_t spectrum, const double centre)
Add the fit record (failure) to output workspace.
std::vector< double > getStartingPeakValues()
Get the peak parameter values from m_peakFunction and output to a list in the same order of m_peakPar...
void estimateBackground(const HistogramData::HistogramX &X, const HistogramData::HistogramY &Y, const size_t i_min, const size_t i_max, std::vector< double > &vecbkgdparvalues)
Estimate background of a given range.
std::vector< HistogramData::Histogram > calculateSecondDifference(const API::MatrixWorkspace_const_sptr &input)
Methods searving for findPeaksUsingMariscotti()
void smoothData(std::vector< HistogramData::Histogram > &histograms, const int w, const int g_z)
Smooth data for Mariscotti.
void addInfoRow(const size_t spectrum, const API::IPeakFunction_const_sptr &peakfunction, const API::IBackgroundFunction_sptr &bkgdfunction, const bool isoutputraw, const double mincost)
Add a new row in output TableWorkspace containing information of the fitted peak+background.
bool m_rawPeaksTable
background
Definition FindPeaks.h:170
int m_inputPeakFWHM
holder for the requested peak FWHM
Definition FindPeaks.h:165
FitOneSinglePeak: a class to perform peak fitting on a single peak.
Definition FitPeak.h:30
void setPeakRange(double xpeakleft, double xpeakright)
Set peak range.
Definition FitPeak.cpp:112
void setupGuessedFWHM(double usrwidth, int minfwhm, int maxfwhm, int stepsize, bool fitwithsteppedfwhm)
Set peak width to guess.
Definition FitPeak.cpp:156
std::string getDebugMessage()
Get debug message.
Definition FitPeak.cpp:276
void setFunctions(const API::IPeakFunction_sptr &peakfunc, const API::IBackgroundFunction_sptr &bkgdfunc)
Set functions.
Definition FitPeak.cpp:82
void highBkgdFit()
Fit peak first considering high background.
Definition FitPeak.cpp:459
bool simpleFit()
Fit peak and background together.
Definition FitPeak.cpp:281
void setFittingMethod(std::string minimizer, const std::string &costfunction)
Set fitting method.
Definition FitPeak.cpp:129
void setFitWindow(double leftwindow, double rightwindow)
Set fit range.
Definition FitPeak.cpp:93
void setWorskpace(const API::MatrixWorkspace_sptr &dataws, size_t wsindex)
Set workspaces.
Definition FitPeak.cpp:65
double getFitCostFunctionValue()
Get cost function value from fitting.
Definition FitPeak.cpp:974
Support for a property that holds an array of values.
Exception for index errors.
Definition Exception.h:284
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
StartsWithValidator is a validator that requires the value of a property to start with one of the str...
std::shared_ptr< IBackgroundFunction > IBackgroundFunction_sptr
std::shared_ptr< IPeakFunction > IPeakFunction_sptr
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< const IPeakFunction > IPeakFunction_const_sptr
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
HistogramData::Histogram smooth(const HistogramData::Histogram &histogram, unsigned int const npts)
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
Definition IValidator.h:26
Helper class which provides the Collimation Length for SANS instruments.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54
Functor to accumulate a sum of squares.