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 if (usefpdresult < 0) {
893 // Estimate background roughly for a failed case
894 estimateBackground(vecX, vecY, i_min, i_max, vecbkgdparvalue);
895 }
896
897 for (size_t i = 0; i < vecbkgdparvalue.size(); ++i)
898 if (i < m_bkgdOrder)
899 m_backgroundFunction->setParameter(i, vecbkgdparvalue[i]);
900
901 // Estimate peak parameters
902 double est_height(0.0), est_fwhm(0.0);
903 size_t i_obscentre(0);
904 double est_leftfwhm(0.0), est_rightfwhm(0.0);
905 std::string errmsg = estimatePeakParameters(vecX, vecY, i_min, i_max, vecbkgdparvalue, i_obscentre, est_height,
906 est_fwhm, est_leftfwhm, est_rightfwhm);
907 if (!errmsg.empty()) {
908 // Unable to estimate peak
909 i_obscentre = i_centre;
910 est_fwhm = 1.;
911 est_height = 1.;
912 g_log.warning(errmsg);
913 }
914
915 // Set peak parameters to
916 if (m_useObsCentre)
917 m_peakFunction->setCentre(vecX[i_obscentre]);
918 else
919 m_peakFunction->setCentre(vecX[i_centre]);
920 m_peakFunction->setHeight(est_height);
921 m_peakFunction->setFwhm(est_fwhm);
922
923 if (usefpdresult < 0) {
924 // Estimate peak range based on estimated linear background and peak
925 // parameter estimated from observation
926 if (!m_useObsCentre)
927 i_obscentre = i_centre;
928 estimatePeakRange(vecX, i_obscentre, i_min, i_max, est_leftfwhm, est_rightfwhm, vecpeakrange);
929 }
930
931 //-------------------------------------------------------------------------
932 // Fit Peak
933 //-------------------------------------------------------------------------
934 std::vector<double> fitwindow(2);
935 fitwindow[0] = vecX[i_min];
936 fitwindow[1] = vecX[i_max];
937
938 double costfuncvalue = callFitPeak(input, spectrum, m_peakFunction, m_backgroundFunction, fitwindow, vecpeakrange,
940
941 bool fitsuccess = false;
942 if (costfuncvalue < DBL_MAX && costfuncvalue >= 0. && m_peakFunction->height() > m_minHeight) {
943 fitsuccess = true;
944 }
945 if (fitsuccess && m_usePeakPositionTolerance) {
946 fitsuccess = (fabs(m_peakFunction->centre() - vecX[i_centre]) < m_peakPositionTolerance);
947 }
948
949 //-------------------------------------------------------------------------
950 // Process Fit result
951 //-------------------------------------------------------------------------
952 // Update output
953 if (fitsuccess)
955 else
956 addNonFitRecord(spectrum, m_peakFunction->centre());
957}
958
959//----------------------------------------------------------------------------------------------
963int FindPeaks::findPeakBackground(const MatrixWorkspace_sptr &input, int spectrum, size_t i_min, size_t i_max,
964 std::vector<double> &vecBkgdParamValues, std::vector<double> &vecpeakrange) {
965 const auto &vecX = input->x(spectrum);
966
967 // Call FindPeakBackground
968 auto estimate = createChildAlgorithm("FindPeakBackground");
969 estimate->setLoggingOffset(1);
970 estimate->setProperty("InputWorkspace", input);
971 estimate->setProperty("WorkspaceIndex", spectrum);
972 // estimate->setProperty("SigmaConstant", 1.0);
973 std::vector<double> fwvec;
974 fwvec.emplace_back(vecX[i_min]);
975 fwvec.emplace_back(vecX[i_max]);
976 estimate->setProperty("BackgroundType", m_backgroundType);
977 estimate->setProperty("FitWindow", fwvec);
978 estimate->executeAsChildAlg();
979 // Get back the result
980 Mantid::API::ITableWorkspace_sptr peaklisttablews = estimate->getProperty("OutputWorkspace");
981
982 // Determine whether to use FindPeakBackground's result.
983 int fitresult = -1;
984 if (peaklisttablews->columnCount() < 7)
985 throw std::runtime_error("No 7th column for use FindPeakBackground result or not. ");
986
987 if (peaklisttablews->rowCount() > 0) {
993 const int hiddenFitresult = peaklisttablews->Int(0, 6);
994 g_log.information() << "fitresult=" << hiddenFitresult << "\n";
995 }
996
997 // Local check whether FindPeakBackground gives a reasonable value
998 vecpeakrange.resize(2);
999 if (fitresult > 0) {
1000 // Use FitPeakBackgroud's result
1001 size_t i_peakmin, i_peakmax;
1002 i_peakmin = peaklisttablews->Int(0, 1);
1003 i_peakmax = peaklisttablews->Int(0, 2);
1004
1005 g_log.information() << "FindPeakBackground successful. "
1006 << "iMin = " << i_min << ", iPeakMin = " << i_peakmin << ", iPeakMax = " << i_peakmax
1007 << ", iMax = " << i_max << "\n";
1008
1009 if (i_peakmin < i_peakmax && i_peakmin > i_min + 2 && i_peakmax < i_max - 2) {
1010 // FIXME - It is assumed that there are 3 background parameters set to
1011 // FindPeaksBackground
1012 double bg0, bg1, bg2;
1013 bg0 = peaklisttablews->Double(0, 3);
1014 bg1 = peaklisttablews->Double(0, 4);
1015 bg2 = peaklisttablews->Double(0, 5);
1016
1017 // Set output
1018 vecBkgdParamValues.resize(3, 0.);
1019 vecBkgdParamValues[0] = bg0;
1020 vecBkgdParamValues[1] = bg1;
1021 vecBkgdParamValues[2] = bg2;
1022
1023 g_log.information() << "Background parameters (from FindPeakBackground) A0=" << bg0 << ", A1=" << bg1
1024 << ", A2=" << bg2 << "\n";
1025
1026 vecpeakrange[0] = vecX[i_peakmin];
1027 vecpeakrange[1] = vecX[i_peakmax];
1028 } else {
1029 // Do manual estimation again
1030 g_log.debug("FindPeakBackground result is ignored due to wrong in peak range. ");
1031 }
1032 } else {
1033 g_log.information("Failed to get background estimation\n");
1034 }
1035
1036 std::stringstream outx;
1037 outx << "FindPeakBackground Result: Given window (" << vecX[i_min] << ", " << vecX[i_max]
1038 << "); Determine peak range: (" << vecpeakrange[0] << ", " << vecpeakrange[1] << "). ";
1039 g_log.information(outx.str());
1040
1041 return fitresult;
1042}
1043
1044//----------------------------------------------------------------------------------------------
1061std::string FindPeaks::estimatePeakParameters(const HistogramX &vecX, const HistogramY &vecY, size_t i_min,
1062 size_t i_max, const std::vector<double> &vecbkgdparvalues,
1063 size_t &iobscentre, double &height, double &fwhm, double &leftfwhm,
1064 double &rightfwhm) {
1065 // Search for maximum considering background
1066 const double bg0 = vecbkgdparvalues[0];
1067 double bg1 = 0;
1068 double bg2 = 0;
1069 if (vecbkgdparvalues.size() >= 2) {
1070 bg1 = vecbkgdparvalues[1];
1071 if (vecbkgdparvalues.size() >= 3)
1072 bg2 = vecbkgdparvalues[2];
1073 }
1074
1075 // Starting value
1076 iobscentre = i_min;
1077 const double tmpx = vecX[i_min];
1078 height = vecY[i_min] - (bg0 + bg1 * tmpx + bg2 * tmpx * tmpx);
1079 double lowest = height;
1080
1081 // Extreme case
1082 if (i_max == vecY.size())
1083 i_max = i_max - 1;
1084
1085 // Searching
1086 for (size_t i = i_min + 1; i <= i_max; ++i) {
1087 const double x = vecX[i];
1088 const double tmpheight = vecY[i] - (bg0 + bg1 * x + bg2 * x * x);
1089
1090 if (tmpheight > height) {
1091 iobscentre = i;
1092 height = tmpheight;
1093 } else if (tmpheight < lowest) {
1094 lowest = tmpheight;
1095 }
1096 }
1097
1098 // Summarize on peak centre
1099 double obscentre = vecX[iobscentre];
1100 double drop = height - lowest;
1101 if (drop == 0) {
1102 // Flat spectrum. No peak parameter can be estimated.
1103 // FIXME - should have a second notice such that there will be no more
1104 // fitting.
1105 return "Flat spectrum";
1106 } else if (height <= m_minHeight) {
1107 // The peak is not high enough!
1108 // FIXME - should have a second notice such that there will be no more
1109 // fitting.
1110 return "Fluctuation is less than minimum allowed value.";
1111 }
1112
1113 // If maximum point is on the edge 2 points, return false. One side of peak
1114 // must have at least 3 points
1115 if (iobscentre <= i_min + 1 || iobscentre >= i_max - 1) {
1116 std::stringstream dbss;
1117 dbss << "Maximum value on edge. Fit window is between " << vecX[i_min] << " and " << vecX[i_max]
1118 << ". Maximum value " << vecX[iobscentre] << " is located on (" << iobscentre << ").";
1119 return dbss.str();
1120 }
1121
1122 // Search for half-maximum: no need to very precise
1123
1124 // Slope at the left side of peak.
1125 leftfwhm = -1.;
1126 for (int i = static_cast<int>(iobscentre) - 1; i >= 0; --i) {
1127 double xleft = vecX[i];
1128 double yleft = vecY[i] - (bg0 + bg1 * xleft + bg2 * xleft * xleft);
1129 if (yleft <= 0.5 * height) {
1130 // Ideal case
1131 // FIXME - Need a linear interpolation on it!
1132 leftfwhm = obscentre - 0.5 * (vecX[i] + vecX[i + 1]);
1133 break;
1134 }
1135 }
1136
1137 // Slope at the right side of peak
1138 rightfwhm = -1.;
1139 for (size_t i = iobscentre + 1; i <= i_max; ++i) {
1140 double xright = vecX[i];
1141 double yright = vecY[i] - (bg0 + bg1 * xright + bg2 * xright * xright);
1142 if (yright <= 0.5 * height) {
1143 rightfwhm = 0.5 * (vecX[i] + vecX[i - 1]) - obscentre;
1144 break;
1145 }
1146 }
1147
1148 if (leftfwhm <= 0 || rightfwhm <= 0) {
1149 std::stringstream errmsg;
1150 errmsg << "Estimate peak parameters error (FWHM cannot be zero): Input "
1151 "data size = "
1152 << vecX.size() << ", Xmin = " << vecX[i_min] << "(" << i_min << "), Xmax = " << vecX[i_max] << "(" << i_max
1153 << "); "
1154 << "Estimated peak centre @ " << vecX[iobscentre] << "(" << iobscentre << ") with height = " << height
1155 << "; Lowest Y value = " << lowest << "; Output error: . leftfwhm = " << leftfwhm
1156 << ", right fwhm = " << rightfwhm << ".";
1157 return errmsg.str();
1158 }
1159
1160 fwhm = leftfwhm + rightfwhm;
1161 if (fwhm < 1.e-200) // very narrow peak
1162 {
1163 std::stringstream errmsg;
1164 errmsg << "Estimate peak parameters error (FWHM cannot be zero): Input "
1165 "data size = "
1166 << vecX.size() << ", Xmin = " << vecX[i_min] << "(" << i_min << "), Xmax = " << vecX[i_max] << "(" << i_max
1167 << "); "
1168 << "Estimated peak centre @ " << vecX[iobscentre] << "(" << iobscentre << ") with height = " << height
1169 << "; Lowest Y value = " << lowest << "; Output error: . fwhm = " << fwhm << ".";
1170 return errmsg.str();
1171 }
1172
1173 g_log.information() << "Estimated peak parameters: Centre = " << obscentre << ", Height = " << height
1174 << ", FWHM = " << fwhm << " = " << leftfwhm << " + " << rightfwhm << ".\n";
1175
1176 return std::string();
1177}
1178
1179//----------------------------------------------------------------------------------------------
1190void FindPeaks::estimateBackground(const HistogramX &X, const HistogramY &Y, const size_t i_min, const size_t i_max,
1191 std::vector<double> &vecbkgdparvalues) {
1192 // Validate input
1193 if (i_min >= i_max)
1194 throw std::runtime_error("when trying to estimate the background parameter "
1195 "values: i_min cannot larger or equal to i_max");
1196 if (vecbkgdparvalues.size() < 3)
1197 vecbkgdparvalues.resize(3, 0.);
1198
1199 // FIXME - THIS IS A MAGIC NUMBER
1200 const size_t MAGICNUMBER = 12;
1201 size_t numavg;
1202 if (i_max - i_min > MAGICNUMBER)
1203 numavg = 3;
1204 else
1205 numavg = 1;
1206
1207 // Get (x0, y0) and (xf, yf)
1208 double x0 = 0.0;
1209 double y0 = 0.0;
1210 double xf = 0.0;
1211 double yf = 0.0;
1212
1213 for (size_t i = 0; i < numavg; ++i) {
1214 x0 += X[i_min + i];
1215 y0 += Y[i_min + i];
1216
1217 xf += X[i_max - i];
1218 // X has one more value than Y
1219 yf += Y[i_max - i - 1];
1220 }
1221 x0 = x0 / static_cast<double>(numavg);
1222 y0 = y0 / static_cast<double>(numavg);
1223 xf = xf / static_cast<double>(numavg);
1224 yf = yf / static_cast<double>(numavg);
1225
1226 // Esitmate
1227 vecbkgdparvalues[2] = 0.;
1228 if (m_bkgdOrder >= 1) {
1229 // linear background
1230 vecbkgdparvalues[1] = (y0 - yf) / (x0 - xf);
1231 vecbkgdparvalues[0] = (xf * y0 - x0 * yf) / (xf - x0);
1232 } else {
1233 // flat background
1234 vecbkgdparvalues[1] = 0.;
1235 vecbkgdparvalues[0] = 0.5 * (y0 + yf);
1236 }
1237}
1238
1239//----------------------------------------------------------------------------------------------
1243void FindPeaks::estimatePeakRange(const HistogramX &vecX, size_t i_centre, size_t i_min, size_t i_max,
1244 const double &leftfwhm, const double &rightfwhm, std::vector<double> &vecpeakrange) {
1245 // Check
1246 if (vecpeakrange.size() < 2)
1247 vecpeakrange.resize(2, 0.);
1248
1249 if (i_centre < i_min || i_centre > i_max)
1250 throw std::runtime_error("Estimate peak range input centre is out of fit window. ");
1251
1252 // Search peak left by using 6 * half of FWHM
1253 double peakleftbound = vecX[i_centre] - 6. * leftfwhm;
1254 double peakrightbound = vecX[i_centre] + 6. * rightfwhm;
1255
1256 // Deal with case the peak boundary is too close to fit window
1257 auto ipeakleft = static_cast<size_t>(getIndex(vecX, peakleftbound));
1258 if (ipeakleft <= i_min) {
1259 size_t numbkgdpts = (i_centre - i_min) / 6;
1260 // FIXME - 3 is a magic number
1261 if (numbkgdpts < 3)
1262 numbkgdpts = 3;
1263 ipeakleft = i_min + numbkgdpts;
1264 if (ipeakleft >= i_centre)
1265 ipeakleft = i_min + 1;
1266
1267 peakleftbound = vecX[ipeakleft];
1268 }
1269
1270 auto ipeakright = static_cast<size_t>(getIndex(vecX, peakrightbound));
1271 if (ipeakright >= i_max) {
1272 size_t numbkgdpts = (i_max - i_centre) / 6;
1273 // FIXME - 3 is a magic number
1274 if (numbkgdpts < 3)
1275 numbkgdpts = 3;
1276 ipeakright = i_max - numbkgdpts;
1277 if (ipeakright <= i_centre)
1278 ipeakright = i_max - 1;
1279
1280 peakrightbound = vecX[ipeakright];
1281 }
1282
1283 // Set result to output vector
1284 vecpeakrange[0] = peakleftbound;
1285 vecpeakrange[1] = peakrightbound;
1286}
1287
1288//----------------------------------------------------------------------------------------------
1296void FindPeaks::addInfoRow(const size_t spectrum, const API::IPeakFunction_const_sptr &peakfunction,
1297 const API::IBackgroundFunction_sptr &bkgdfunction, const bool isoutputraw,
1298 const double mincost) {
1299 // Check input validity
1300 if (mincost < 0. || mincost >= DBL_MAX - 1.0E-10)
1301 throw std::runtime_error("Minimum cost indicates that fit fails. This "
1302 "method should not be called "
1303 "under this circumstance. ");
1304
1305 // Add fitted parameters to output table workspace
1306 API::TableRow t = m_outPeakTableWS->appendRow();
1307
1308 // spectrum
1309 t << static_cast<int>(spectrum);
1310
1311 // peak and background function parameters
1312 if (isoutputraw) {
1313 // Output of raw peak parameters
1314 size_t nparams = peakfunction->nParams();
1315 size_t nparamsb = bkgdfunction->nParams();
1316
1317 size_t numcols = m_outPeakTableWS->columnCount();
1318 if (nparams + nparamsb + 2 != numcols) {
1319 throw std::runtime_error("Error 1307 number of columns do not matches");
1320 }
1321
1322 for (size_t i = 0; i < nparams; ++i) {
1323 t << peakfunction->getParameter(i);
1324 }
1325 for (size_t i = 0; i < nparamsb; ++i) {
1326 t << bkgdfunction->getParameter(i);
1327 }
1328 } else {
1329 // Output of effective peak parameters
1330 // Set up parameters to peak function and get effective value
1331 double peakcentre = peakfunction->centre();
1332 double fwhm = peakfunction->fwhm();
1333 double height = peakfunction->height();
1334
1335 t << peakcentre << fwhm << height;
1336
1337 // Set up parameters to background function
1338
1339 // FIXME - Use Polynomial for all 3 background types.
1340 double a0 = bkgdfunction->getParameter("A0");
1341 double a1 = 0.;
1342 if (bkgdfunction->name() != "FlatBackground")
1343 a1 = bkgdfunction->getParameter("A1");
1344 double a2 = 0;
1345 if (bkgdfunction->name() != "LinearBackground" && bkgdfunction->name() != "FlatBackground")
1346 a2 = bkgdfunction->getParameter("A2");
1347
1348 t << a0 << a1 << a2;
1349
1350 g_log.debug() << "Peak parameters found: cen=" << peakcentre << " fwhm=" << fwhm << " height=" << height
1351 << " a0=" << a0 << " a1=" << a1 << " a2=" << a2;
1352 }
1353 g_log.debug() << " chsq=" << mincost << "\n";
1354 // Minimum cost function value
1355 t << mincost;
1356}
1357
1358//----------------------------------------------------------------------------------------------
1363void FindPeaks::addNonFitRecord(const size_t spectrum, const double centre) {
1364 // Add a new row
1365 API::TableRow t = m_outPeakTableWS->appendRow();
1366
1367 g_log.information() << "Failed to fit peak at " << centre << "\n";
1368 // 1st column
1369 t << static_cast<int>(spectrum);
1370
1371 // Parameters
1372 for (std::size_t i = 0; i < m_numTableParams; i++) {
1373 if (i == m_centreIndex)
1374 t << centre;
1375 else
1376 t << 0.;
1377 }
1378
1379 // HUGE chi-square
1380 t << DBL_MAX;
1381}
1382
1383//----------------------------------------------------------------------------------------------
1387 // Setup the background
1388 // FIXME (No In This Ticket) Need to have a uniformed routine to name
1389 // background function
1390 std::string backgroundposix;
1391 if (m_backgroundType != "Quadratic") {
1392 // FlatBackground, LinearBackground, Quadratic
1393 backgroundposix = "Background";
1394 }
1395 m_backgroundFunction = std::dynamic_pointer_cast<IBackgroundFunction>(
1396 API::FunctionFactory::Instance().createFunction(m_backgroundType + backgroundposix));
1397 g_log.information() << "Background function (" << m_backgroundFunction->name() << ") has been created. "
1398 << "\n";
1399
1400 m_bkgdParameterNames = m_backgroundFunction->getParameterNames();
1401 // FIXME - Need to add method nOrder to background function;
1402 m_bkgdOrder = m_backgroundFunction->nParams() - 1;
1403
1404 // Set up peak function
1406 std::dynamic_pointer_cast<IPeakFunction>(API::FunctionFactory::Instance().createFunction(m_peakFuncType));
1407 m_peakParameterNames = m_peakFunction->getParameterNames();
1408}
1409
1410//----------------------------------------------------------------------------------------------
1413double FindPeaks::callFitPeak(const MatrixWorkspace_sptr &dataws, int wsindex,
1414 const API::IPeakFunction_sptr &peakfunction,
1415 const API::IBackgroundFunction_sptr &backgroundfunction,
1416 const std::vector<double> &vec_fitwindow, const std::vector<double> &vec_peakrange,
1417 int minGuessFWHM, int maxGuessFWHM, int guessedFWHMStep, int estBackResult) {
1418 std::stringstream dbss;
1419 dbss << "[Call FitPeak] Fit 1 peak at X = " << peakfunction->centre() << " of spectrum " << wsindex;
1420 g_log.information(dbss.str());
1421
1422 double userFWHM = m_peakFunction->fwhm();
1423 bool fitwithsteppedfwhm = (guessedFWHMStep > 0);
1424
1425 FitOneSinglePeak fitpeak;
1426 fitpeak.setChild(true);
1427 fitpeak.setWorskpace(dataws, wsindex);
1428 fitpeak.setFitWindow(vec_fitwindow[0], vec_fitwindow[1]);
1430 fitpeak.setFunctions(peakfunction, backgroundfunction);
1431 fitpeak.setupGuessedFWHM(userFWHM, minGuessFWHM, maxGuessFWHM, guessedFWHMStep, fitwithsteppedfwhm);
1432 fitpeak.setPeakRange(vec_peakrange[0], vec_peakrange[1]);
1433
1434 if (estBackResult == 1) {
1435 g_log.information("simpleFit");
1436 fitpeak.simpleFit();
1437 } else if (m_highBackground) {
1438 g_log.information("highBkgdFit");
1439 fitpeak.highBkgdFit();
1440 } else {
1441 g_log.information("simpleFit");
1442 fitpeak.simpleFit();
1443 }
1444
1445 double costfuncvalue = fitpeak.getFitCostFunctionValue();
1446 std::string dbinfo = fitpeak.getDebugMessage();
1447 g_log.information(dbinfo);
1448
1449 return costfuncvalue;
1450}
1451
1452//----------------------------------------------------------------------------------------------
1458 size_t numpeakpars = m_peakFunction->nParams();
1459 std::vector<double> vec_value(numpeakpars);
1460 for (size_t i = 0; i < numpeakpars; ++i) {
1461 double parvalue = m_peakFunction->getParameter(i);
1462 vec_value[i] = parvalue;
1463 }
1464
1465 return vec_value;
1466}
1467
1468} // namespace Mantid::Algorithms
1469
1470// 0.5044, 0.5191, 0.535, 0.5526, 0.5936, 0.6178, 0.6453, 0.6768, 0.7134,
1471// 0.7566, 0.8089, 0.8737, 0.9571, 1.0701, 1.2356, 1.5133, 2.1401
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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:422
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.