29#include "MantidHistogramData/EstimatePolynomial.h"
30#include "MantidHistogramData/Histogram.h"
31#include "MantidHistogramData/HistogramBuilder.h"
32#include "MantidHistogramData/HistogramIterator.h"
41#include "boost/algorithm/string.hpp"
42#include "boost/algorithm/string/trim.hpp"
48using namespace Algorithms::PeakParameterHelper;
54using Mantid::HistogramData::Histogram;
63const std::string START_WKSP_INDEX(
"StartWorkspaceIndex");
64const std::string STOP_WKSP_INDEX(
"StopWorkspaceIndex");
65const std::string PEAK_CENTERS(
"PeakCenters");
66const std::string PEAK_CENTERS_WKSP(
"PeakCentersWorkspace");
67const std::string PEAK_FUNC(
"PeakFunction");
68const std::string BACK_FUNC(
"BackgroundType");
69const std::string FIT_WINDOW_LIST(
"FitWindowBoundaryList");
70const std::string FIT_WINDOW_WKSP(
"FitPeakWindowWorkspace");
71const std::string PEAK_WIDTH_PERCENT(
"PeakWidthPercent");
72const std::string PEAK_PARAM_NAMES(
"PeakParameterNames");
73const std::string PEAK_PARAM_VALUES(
"PeakParameterValues");
74const std::string PEAK_PARAM_TABLE(
"PeakParameterValueTable");
75const std::string FIT_FROM_RIGHT(
"FitFromRight");
76const std::string MINIMIZER(
"Minimizer");
77const std::string COST_FUNC(
"CostFunction");
78const std::string STRICT_CONVERGENCE(
"StrictConvergence");
79const std::string MAX_FIT_ITER(
"MaxFitIterations");
80const std::string BACKGROUND_Z_SCORE(
"FindBackgroundSigma");
81const std::string HIGH_BACKGROUND(
"HighBackground");
82const std::string POSITION_TOL(
"PositionTolerance");
83const std::string POSITION_TOL_MODE(
"PositionToleranceMode");
84const std::string POSITION_TOL_FRACTIONAL(
"PositionToleranceFractional");
85const std::string PEAK_MIN_HEIGHT(
"MinimumPeakHeight");
86const std::string CONSTRAIN_PEAK_POS(
"ConstrainPeakPositions");
87const std::string CALC_UNCONSTRAINED_ERRORS(
"CalculateUnconstrainedErrors");
88const std::string COPY_LAST_GOOD_PEAK_PARAMS(
"CopyLastGoodPeakParameters");
89const std::string RESPECT_FIXED_PEAK_PARAMS(
"RespectFixedPeakParameters");
90const std::string OUTPUT_WKSP_MODEL(
"FittedPeaksWorkspace");
91const std::string OUTPUT_WKSP_PARAMS(
"OutputPeakParametersWorkspace");
92const std::string OUTPUT_WKSP_PARAM_ERRS(
"OutputParameterFitErrorsWorkspace");
93const std::string RAW_PARAMS(
"RawPeakParameters");
94const std::string PEAK_MIN_SIGNAL_TO_NOISE_RATIO(
"MinimumSignalToNoiseRatio");
95const std::string PEAK_MIN_TOTAL_COUNT(
"MinimumPeakTotalCount");
96const std::string PEAK_MIN_SIGNAL_TO_SIGMA_RATIO(
"MinimumSignalToSigmaRatio");
100namespace FitPeaksAlgorithm {
106 if (num_peaks == 0 || num_params == 0)
107 throw std::runtime_error(
"No peak or no parameter error.");
111 m_costs.resize(num_peaks, DBL_MAX);
114 for (
size_t ipeak = 0; ipeak < num_peaks; ++ipeak) {
161 throw std::runtime_error(
"Peak index is out of range.");
170 size_t peak_num_params = fit_functions.
peakfunction->nParams();
171 for (
size_t ipar = 0; ipar < peak_num_params; ++ipar) {
176 for (
size_t ipar = 0; ipar < fit_functions.
bkgdfunction->nParams(); ++ipar) {
191 throw std::runtime_error(
"Peak index is out of range");
192 if (peak_position >= 0.)
193 throw std::runtime_error(
"Can only set negative postion for bad record");
241 assert(individual_rejection_count <= 1);
243 return individual_rejection_count == 1;
253 std::ostringstream os;
264 os <<
m_low_snr <<
" peak(s) rejected: low signal-to-noise ratio.\n";
272 : m_fitPeaksFromRight(true), m_fitIterations(50), m_numPeaksToFit(0), m_minPeakHeight(0.),
273 m_minSignalToNoiseRatio(0.), m_minPeakTotalCount(0.), m_peakPosTolCase234(false) {}
280 "Name of the input workspace for peak fitting.");
283 "Name of the output workspace containing peak centers for "
285 "The output workspace is point data."
286 "Each workspace index corresponds to a spectrum. "
287 "Each X value ranges from 0 to N-1, where N is the number of "
289 "Each Y value is the peak position obtained by peak fitting. "
290 "Negative value is used for error signals. "
291 "-1 for data is zero; -2 for maximum value is smaller than "
292 "specified minimum value."
293 "and -3 for non-converged fitting.");
296 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
297 mustBePositive->setLower(0);
298 declareProperty(PropertyNames::START_WKSP_INDEX, 0, mustBePositive,
"Starting workspace index for fit");
300 PropertyNames::STOP_WKSP_INDEX,
EMPTY_INT(),
301 "Last workspace index for fit is the smaller of this value and the workspace index of last spectrum.");
304 "List of peak centers to use as initial guess for fit.");
308 "MatrixWorkspace containing referent peak centers for each spectrum, defined at the same workspace indices.");
310 const std::string peakcentergrp(
"Peak Positions");
315 const std::vector<std::string> peakNames = FunctionFactory::Instance().getFunctionNames<
API::IPeakFunction>();
316 declareProperty(PropertyNames::PEAK_FUNC,
"Gaussian", std::make_shared<StringListValidator>(peakNames),
317 "Use of a BackToBackExponential profile is only reccomended if the "
318 "coeficients to calculate A and B are defined in the instrument "
319 "Parameters.xml file.");
320 const vector<string> bkgdtypes{
"Flat",
"Linear",
"Quadratic"};
321 declareProperty(PropertyNames::BACK_FUNC,
"Linear", std::make_shared<StringListValidator>(bkgdtypes),
322 "Type of Background.");
324 const std::string funcgroup(
"Function Types");
331 "List of boundaries of the peak fitting window corresponding to "
336 "MatrixWorkspace containing peak windows for each peak center in each spectrum, defined at the same "
337 "workspace indices.");
339 auto min = std::make_shared<BoundedValidator<double>>();
343 "The estimated peak width as a "
344 "percentage of the d-spacing "
345 "of the center of the peak. Value must be less than 1.");
347 const std::string fitrangeegrp(
"Peak Range Setup");
354 "List of peak parameters' names");
356 "List of peak parameters' value");
361 "Name of the an optional workspace, whose each column "
362 "corresponds to given peak parameter names, "
363 "and each row corresponds to a subset of spectra.");
365 const std::string startvaluegrp(
"Starting Parameters Setup");
372 "Flag for the order to fit peaks. If true, peaks are fitted "
374 "Otherwise peaks are fitted from leftmost.");
376 const std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys();
379 "Minimizer to use for fitting.");
381 const std::array<string, 3> costFuncOptions = {{
"Least squares",
"Rwp",
"Unweighted least squares"}};
386 "If true, a peak fit is only accepted when the minimizer reports the exact status "
387 "'success'. If false, fits that stop because the changes in function or parameter "
388 "value have become too small are also accepted as converged.");
390 auto min_max_iter = std::make_shared<BoundedValidator<int>>();
391 min_max_iter->setLower(49);
392 declareProperty(PropertyNames::MAX_FIT_ITER, 50, min_max_iter,
"Maximum number of function fitting iterations.");
394 const std::string optimizergrp(
"Optimization Setup");
399 std::ostringstream os;
400 os <<
"Deprecated property. Use " << PropertyNames::PEAK_MIN_SIGNAL_TO_NOISE_RATIO <<
" instead.";
404 "Flag whether the input data has high background compared to peak heights.");
407 "List of tolerance on fitted peak positions against given peak positions."
408 "If there is only one value given, then ");
410 const std::vector<std::string> posTolModes{
"Check",
"Constrain"};
411 declareProperty(PropertyNames::POSITION_TOL_MODE,
"Check", std::make_shared<StringListValidator>(posTolModes),
412 "How PositionTolerance is applied. 'Check' (default): the tolerance is only a "
413 "post-fit acceptance criterion - a fitted centre further than the tolerance from "
414 "its expected position is rejected. 'Constrain': the tolerance additionally bounds "
415 "the peak centre during the fit (expected position +/- tolerance). Unlike "
416 "ConstrainPeakPositions, the reported position error is recomputed free of the "
417 "constraint penalty so it remains a genuine covariance error. Requires "
418 "PositionTolerance to be specified.");
421 "If true, each PositionTolerance value is interpreted as a fraction of this peak's "
422 "fit window width rather than an absolute value: the effective tolerance becomes "
423 "tolerance*(window_max - window_min). Because the fit window can differ per spectrum "
424 "(e.g. via FitPeakWindowWorkspace), this gives a per-spectrum tolerance. Applies to "
425 "both the 'Check' and 'Constrain' modes.");
428 "Used for validating peaks before and after fitting. If a peak's observed/estimated or "
429 "fitted height is under this value, the peak will be marked as error.");
432 "If true peak position will be constrained by estimated positions "
433 "(highest Y value position) and "
434 "the peak width either estimted by observation or calculate.");
437 "If true, and a peak-position constraint is applied during fitting "
438 "(ConstrainPeakPositions or PositionToleranceMode='Constrain'), the reported "
439 "parameter errors are recomputed from the unconstrained cost function at the fitted "
440 "values. A position constraint contributes curvature to the Hessian that the error "
441 "calculation inverts, which reduces the reported position error; enabling this option "
442 "instead reports the covariance error from the data alone. Costs one extra "
443 "(zero-iteration) fit per constrained peak.");
446 "If true, initial peak parameters (with the exception of peak centre) "
447 "may be copied from the last successfully fit peak in the spectra.");
450 "If true, peak function parameters that are marked as fixed "
451 "(e.g. parameters calculated from the instrument geometry, such as A and B "
452 "of a BackToBackExponential) remain fixed during fitting. "
453 "If false (default), such parameters are unfixed so they can be refined.");
458 "Name of the output matrix workspace with fitted peak. "
459 "This output workspace has the same dimension as the input workspace."
460 "The Y values belonged to peaks to fit are replaced by fitted value. "
461 "Values of estimated background are used if peak fails to be fit.");
465 "Name of table workspace containing all fitted peak parameters.");
471 "Name of workspace containing all fitted peak parameters' fitting error."
472 "It must be used along with FittedPeaksWorkspace and RawPeakParameters "
476 "false generates table with effective centre/width/height "
477 "parameters. true generates a table with peak function "
481 PropertyNames::PEAK_MIN_SIGNAL_TO_NOISE_RATIO, 0.,
482 "Used for validating peaks before fitting. If the signal-to-noise ratio is under this value, "
483 "the peak will be marked as error. This does not apply to peaks for which the noise cannot be estimated.");
486 "Used for validating peaks before fitting. If the total peak window Y-value count "
487 "is under this value, the peak will be excluded from fitting and calibration.");
490 "Used for validating peaks after fitting. If the signal-to-sigma ratio is under this value, "
491 "the peak will be excluded from fitting and calibration.");
493 const std::string addoutgrp(
"Analysis");
504 map<std::string, std::string> issues;
507 if (!(
isDefault(PropertyNames::START_WKSP_INDEX) &&
isDefault(PropertyNames::STOP_WKSP_INDEX))) {
508 const int startIndex =
getProperty(PropertyNames::START_WKSP_INDEX);
509 const int stopIndex =
getProperty(PropertyNames::STOP_WKSP_INDEX);
510 if (startIndex > stopIndex) {
511 const std::string msg =
512 PropertyNames::START_WKSP_INDEX +
" must be less than or equal to " + PropertyNames::STOP_WKSP_INDEX;
513 issues[PropertyNames::START_WKSP_INDEX] = msg;
514 issues[PropertyNames::STOP_WKSP_INDEX] = msg;
520 const std::string posTolMode =
getPropertyValue(PropertyNames::POSITION_TOL_MODE);
521 if (posTolMode ==
"Constrain") {
522 const std::vector<double> posTolerances =
getProperty(PropertyNames::POSITION_TOL);
523 if (posTolerances.empty()) {
524 issues[PropertyNames::POSITION_TOL] =
525 "PositionTolerance must be specified when PositionToleranceMode is 'Constrain'.";
530 const bool constrainPeakPositions =
getProperty(PropertyNames::CONSTRAIN_PEAK_POS);
531 if (constrainPeakPositions) {
532 const std::string msg =
"PositionToleranceMode='Constrain' and ConstrainPeakPositions both "
533 "constrain the peak centre during fitting and are mutually exclusive. "
534 "Set ConstrainPeakPositions to false to use 'Constrain' mode.";
535 issues[PropertyNames::CONSTRAIN_PEAK_POS] = msg;
536 issues[PropertyNames::POSITION_TOL_MODE] = msg;
541 bool haveCommonPeakParameters(
false);
542 std::vector<string> suppliedParameterNames =
getProperty(PropertyNames::PEAK_PARAM_NAMES);
543 std::vector<double> peakParamValues =
getProperty(PropertyNames::PEAK_PARAM_VALUES);
544 if ((!suppliedParameterNames.empty()) || (!peakParamValues.empty())) {
545 haveCommonPeakParameters =
true;
546 if (suppliedParameterNames.size() != peakParamValues.size()) {
547 issues[PropertyNames::PEAK_PARAM_NAMES] =
"must have same number of values as PeakParameterValues";
548 issues[PropertyNames::PEAK_PARAM_VALUES] =
"must have same number of values as PeakParameterNames";
553 std::string partablename =
getPropertyValue(PropertyNames::PEAK_PARAM_TABLE);
554 if (!partablename.empty()) {
555 if (haveCommonPeakParameters) {
556 const std::string msg =
"Parameter value table and initial parameter "
557 "name/value vectors cannot be given "
559 issues[PropertyNames::PEAK_PARAM_TABLE] = msg;
560 issues[PropertyNames::PEAK_PARAM_NAMES] = msg;
561 issues[PropertyNames::PEAK_PARAM_VALUES] = msg;
569 if (!suppliedParameterNames.empty()) {
572 std::dynamic_pointer_cast<IPeakFunction>(API::FunctionFactory::Instance().createFunction(peakfunctiontype));
575 std::vector<string> functionParameterNames;
577 functionParameterNames.emplace_back(
m_peakFunction->parameterName(i));
580 const bool failed = std::any_of(suppliedParameterNames.cbegin(), suppliedParameterNames.cend(),
581 [&functionParameterNames](
const auto &parName) {
582 return std::find(functionParameterNames.begin(), functionParameterNames.end(),
583 parName) == functionParameterNames.end();
586 std::string msg =
"Specified invalid parameter for peak function";
587 if (haveCommonPeakParameters)
588 issues[PropertyNames::PEAK_PARAM_NAMES] = msg;
590 issues[PropertyNames::PEAK_PARAM_TABLE] = msg;
595 const std::string error_table_name =
getPropertyValue(PropertyNames::OUTPUT_WKSP_PARAM_ERRS);
596 if (!error_table_name.empty()) {
597 const bool use_raw_params =
getProperty(PropertyNames::RAW_PARAMS);
598 if (!use_raw_params) {
599 issues[PropertyNames::OUTPUT_WKSP_PARAM_ERRS] =
"Cannot be used with " + PropertyNames::RAW_PARAMS +
"=False";
600 issues[PropertyNames::RAW_PARAMS] =
601 "Cannot be False with " + PropertyNames::OUTPUT_WKSP_PARAM_ERRS +
" specified";
640 int start_wi =
getProperty(PropertyNames::START_WKSP_INDEX);
644 int stop_wi =
getProperty(PropertyNames::STOP_WKSP_INDEX);
662 const std::string posTolMode =
getProperty(PropertyNames::POSITION_TOL_MODE);
674 throw std::runtime_error(
"number of peaks to fit is zero.");
680 throw std::runtime_error(
"PeakWidthPercent must be less than 1");
685 double temp =
getProperty(PropertyNames::BACKGROUND_Z_SCORE);
687 std::ostringstream os;
688 os <<
"FitPeaks property \"" << PropertyNames::BACKGROUND_Z_SCORE <<
"\" is deprecated and will be ignored."
720 std::dynamic_pointer_cast<IPeakFunction>(API::FunctionFactory::Instance().createFunction(peakfunctiontype));
724 std::string bkgdname;
725 if (bkgdfunctiontype ==
"Linear")
726 bkgdname =
"LinearBackground";
727 else if (bkgdfunctiontype ==
"Flat") {
728 g_log.
warning(
"There may be problems with Flat background");
729 bkgdname =
"FlatBackground";
731 bkgdname = bkgdfunctiontype;
733 std::dynamic_pointer_cast<IBackgroundFunction>(API::FunctionFactory::Instance().createFunction(bkgdname));
736 API::FunctionFactory::Instance().createFunction(
"LinearBackground"));
742 std::string partablename =
getPropertyValue(PropertyNames::PEAK_PARAM_TABLE);
759 }
else if (peakfunctiontype !=
"Gaussian") {
761 g_log.
warning(
"Neither parameter value table nor initial "
762 "parameter name/value vectors is specified. Fitting might "
763 "not be reliable for peak profile other than Gaussian");
775 std::vector<double> peakwindow =
getProperty(PropertyNames::FIT_WINDOW_LIST);
776 std::string peakwindowname =
getPropertyValue(PropertyNames::FIT_WINDOW_WKSP);
781 if ((!peakwindow.empty()) && peakwindowname.empty()) {
786 throw std::invalid_argument(
787 "Specifying peak windows with a list requires also specifying peak positions with a list.");
790 throw std::invalid_argument(
"Peak window vector must be twice as large as number of peaks.");
795 std::vector<double> peakranges(2);
796 peakranges[0] = peakwindow[i * 2];
797 peakranges[1] = peakwindow[i * 2 + 1];
804 std::stringstream errss;
805 errss <<
"Peak " << i <<
": user specifies an invalid range and peak center against " << peakranges[0] <<
" < "
807 throw std::invalid_argument(errss.str());
810 m_getPeakFitWindow = [
this](std::size_t wi, std::size_t ipeak) -> std::pair<double, double> {
819 }
else if (peakwindow.empty() && peakwindowws !=
nullptr) {
827 if (peakWindowX.empty()) {
828 std::stringstream errss;
829 errss <<
"Peak window required at workspace index " << wi <<
" "
830 <<
"which is undefined in the peak window workspace. "
831 <<
"Ensure workspace indices correspond in peak window workspace and input workspace "
832 <<
"when using start and stop indices.";
833 throw std::invalid_argument(errss.str());
836 if (peakWindowX.size() % 2 != 0) {
837 throw std::invalid_argument(
"The peak window vector must be even, with two edges for each peak center.");
839 if (peakWindowX.size() != peakCenterX.size() * 2) {
840 std::stringstream errss;
841 errss <<
"Peak window workspace index " << wi <<
" has incompatible number of fit windows "
842 << peakWindowX.size() / 2 <<
" with the number of peaks " << peakCenterX.size() <<
" to fit.";
843 throw std::invalid_argument(errss.str());
846 for (
size_t ipeak = 0; ipeak < peakCenterX.size(); ++ipeak) {
847 double left_w_bound = peakWindowX[ipeak * 2];
848 double right_w_bound = peakWindowX[ipeak * 2 + 1];
849 double center = peakCenterX[ipeak];
851 if (!(left_w_bound < center && center < right_w_bound)) {
852 std::stringstream errss;
853 errss <<
"Workspace index " << wi <<
" has incompatible peak window "
854 <<
"(" << left_w_bound <<
", " << right_w_bound <<
") "
855 <<
"with " << ipeak <<
"-th expected peak's center " << center;
856 throw std::runtime_error(errss.str());
860 m_getPeakFitWindow = [
this](std::size_t wi, std::size_t ipeak) -> std::pair<double, double> {
869 }
else if (peakwindow.empty()) {
875 m_getPeakFitWindow = [
this](std::size_t wi, std::size_t ipeak) -> std::pair<double, double> {
884 double left = peak_pos - estimate_peak_width * THREE;
885 double right = peak_pos + estimate_peak_width * THREE;
890 throw std::invalid_argument(
"Without definition of peak window, the "
891 "input workspace must be in unit of dSpacing "
892 "and Delta(D)/D must be given!");
896 throw std::invalid_argument(
"One and only one of peak window array and "
897 "peak window workspace can be specified.");
917 g_log.
notice(
"Peak centers are not specified by peak center workspace");
919 std::string peakpswsname =
getPropertyValue(PropertyNames::PEAK_CENTERS_WKSP);
929 }
else if (
m_peakCenters.empty() && peakcenterws !=
nullptr) {
939 std::stringstream errss;
942 <<
"However, the peak center workspace does not have values defined "
943 <<
"at workspace index " << wi <<
". "
944 <<
"Make sure the workspace indices between input and peak center workspaces correspond.";
946 throw std::invalid_argument(errss.str());
956 std::stringstream errss;
957 errss <<
"One and only one in 'PeakCenters' (vector) and "
958 "'PeakCentersWorkspace' shall be given. "
959 <<
"'PeakCenters' has size " <<
m_peakCenters.size() <<
", and name of peak center workspace "
960 <<
"is " << peakpswsname;
961 throw std::invalid_argument(errss.str());
976 throw std::runtime_error(
"ProcessInputPeakTolerance() must be called after "
977 "ProcessInputPeakCenters()");
994 throw std::runtime_error(
"Number of peak position tolerances and number of "
995 "peaks to fit are inconsistent.");
1033 std::map<std::string, size_t> parname_index_map;
1034 for (
size_t iparam = 0; iparam <
m_peakFunction->nParams(); ++iparam)
1035 parname_index_map.insert(std::make_pair(
m_peakFunction->parameterName(iparam), iparam));
1043 auto locator = parname_index_map.find(paramName);
1044 if (locator != parname_index_map.end()) {
1049 g_log.
warning() <<
"Given peak parameter " << paramName
1050 <<
" is not an allowed parameter of peak "
1068 std::vector<std::shared_ptr<FitPeaksAlgorithm::PeakFitResult>> fit_result_vector(
m_numSpectraToFit);
1070 const int nThreads = FrameworkManager::Instance().getNumOMPThreads();
1073 std::shared_ptr<FitPeaksAlgorithm::PeakFitPreCheckResult> pre_check_result =
1074 std::make_shared<FitPeaksAlgorithm::PeakFitPreCheckResult>();
1076 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
1077 for (
int ithread = 0; ithread < nThreads; ithread++) {
1083 std::vector<std::vector<double>> lastGoodPeakParameters(
m_numPeaksToFit,
1088 for (
auto wi = iws_begin; wi < iws_end; ++wi) {
1094 std::shared_ptr<FitPeaksAlgorithm::PeakFitResult> fit_result =
1095 std::make_shared<FitPeaksAlgorithm::PeakFitResult>(
m_numPeaksToFit, numfuncparams);
1097 std::shared_ptr<FitPeaksAlgorithm::PeakFitPreCheckResult> spectrum_pre_check_result =
1098 std::make_shared<FitPeaksAlgorithm::PeakFitPreCheckResult>();
1100 fitSpectrumPeaks(
static_cast<size_t>(wi), expected_peak_centers, fit_result, lastGoodPeakParameters,
1101 lastGoodPeakSpectra, spectrum_pre_check_result);
1104 writeFitResult(
static_cast<size_t>(wi), expected_peak_centers, fit_result);
1106 *pre_check_result += *spectrum_pre_check_result;
1114 return fit_result_vector;
1119bool estimateBackgroundParameters(
const Histogram &histogram,
const std::pair<size_t, size_t> &peak_window,
1122 std::vector<double> &vec_y);
1123template <
typename vector_like>
1124void rangeToIndexBounds(
const vector_like &vecx,
const double range_left,
const double range_right,
size_t &left_index,
1125 size_t &right_index);
1128std::vector<std::string> supported_peak_profiles{
"Gaussian",
"Lorentzian",
"PseudoVoigt",
"Voigt",
1129 "BackToBackExponential"};
1136double estimateBackgroundNoise(
const std::vector<double> &vec_y) {
1138 size_t half_number_of_bkg_datapoints{5};
1139 if (vec_y.size() < 2 * half_number_of_bkg_datapoints + 3 )
1144 std::vector<double> vec_bkg;
1145 vec_bkg.resize(2 * half_number_of_bkg_datapoints);
1146 std::copy(vec_y.begin(), vec_y.begin() + half_number_of_bkg_datapoints, vec_bkg.begin());
1147 std::copy(vec_y.end() - half_number_of_bkg_datapoints, vec_y.end(), vec_bkg.begin() + half_number_of_bkg_datapoints);
1151 std::vector<double> vec_bkg_no_outliers;
1152 vec_bkg_no_outliers.resize(vec_bkg.size());
1153 double zscore_crit = 3.;
1154 for (
size_t ii = 0; ii < vec_bkg.size(); ii++) {
1155 if (zscore_vec[ii] <= zscore_crit)
1156 vec_bkg_no_outliers.push_back(vec_bkg[ii]);
1159 if (vec_bkg_no_outliers.size() < half_number_of_bkg_datapoints)
1163 return intensityStatistics.standard_deviation;
1174template <
typename vector_like>
1175void rangeToIndexBounds(
const vector_like &elems,
const double range_left,
const double range_right,
size_t &left_index,
1176 size_t &right_index) {
1177 const auto left_iter = std::lower_bound(elems.cbegin(), elems.cend(), range_left);
1178 const auto right_iter = std::upper_bound(elems.cbegin(), elems.cend(), range_right);
1180 left_index = std::distance(elems.cbegin(), left_iter);
1181 right_index = std::distance(elems.cbegin(), right_iter);
1182 right_index = std::min(right_index, elems.size() - 1);
1192 std::vector<double> &vec_y) {
1196 bkgd_func->function(vectorx, vector_bkgd);
1199 for (
size_t i = 0; i < vec_y.size(); ++i) {
1200 (vec_y)[i] -= vector_bkgd[i];
1209class LoggingOffsetSentry {
1227 const std::shared_ptr<FitPeaksAlgorithm::PeakFitResult> &fit_result,
1228 std::vector<std::vector<double>> &lastGoodPeakParameters,
1229 std::vector<size_t> &lastGoodPeakSpectra,
1230 const std::shared_ptr<FitPeaksAlgorithm::PeakFitPreCheckResult> &pre_check_result) {
1236 fit_result->setBadRecord(i, -1.);
1237 pre_check_result->setNumberOfSpectrumPeaksWithLowCount(
m_numPeaksToFit);
1248 peak_fitter->setProperty(
"Minimizer",
m_minimizer);
1250 peak_fitter->setProperty(
"CalcErrors",
true);
1257 bool neighborPeakSameSpectrum =
false;
1258 size_t number_of_out_of_range_peaks{0};
1259 for (
size_t fit_index = 0; fit_index <
m_numPeaksToFit; ++fit_index) {
1261 size_t peak_index(fit_index);
1266 for (
size_t i = 0; i < bkgdfunction->nParams(); ++i)
1267 bkgdfunction->setParameter(i, 0.);
1269 double expected_peak_pos = expected_peak_centers[peak_index];
1273 auto peakfunction = std::dynamic_pointer_cast<API::IPeakFunction>(
m_peakFunction->clone());
1274 peakfunction->setCentre(expected_peak_pos);
1277 peakfunction->setMatrixWorkspace(
m_inputMatrixWS, wi, peak_window_i.first, peak_window_i.second);
1279 std::map<size_t, double> keep_values;
1280 for (
size_t ipar = 0; ipar < peakfunction->nParams(); ++ipar) {
1281 if (peakfunction->isFixed(ipar)) {
1285 keep_values[ipar] = peakfunction->getParameter(ipar);
1290 peakfunction->unfix(ipar);
1297 bool samePeakCrossSpectrum = (lastGoodPeakParameters[peak_index].size() >
1298 static_cast<size_t>(std::count_if(lastGoodPeakParameters[peak_index].begin(),
1299 lastGoodPeakParameters[peak_index].end(),
1300 [&](
auto const &val) {
return val <= 1e-10; })));
1305 if (wi > 0 && samePeakCrossSpectrum) {
1306 size_t lastGoodWi = lastGoodPeakSpectra[peak_index];
1307 std::shared_ptr<const Geometry::Detector> pdetector =
1308 std::dynamic_pointer_cast<const Geometry::Detector>(
m_inputMatrixWS->getDetector(lastGoodWi));
1309 std::shared_ptr<const Geometry::Detector> cdetector =
1310 std::dynamic_pointer_cast<const Geometry::Detector>(
m_inputMatrixWS->getDetector(wi));
1313 if (pdetector && cdetector) {
1314 auto prev_id = pdetector->getID();
1315 auto curr_id = cdetector->getID();
1316 if (prev_id + 1 != curr_id)
1317 samePeakCrossSpectrum =
false;
1319 samePeakCrossSpectrum =
false;
1325 samePeakCrossSpectrum =
false;
1327 }
catch (
const std::runtime_error &) {
1330 samePeakCrossSpectrum =
false;
1334 if (samePeakCrossSpectrum) {
1336 for (
size_t i = 0; i < peakfunction->nParams(); ++i) {
1337 peakfunction->setParameter(i, lastGoodPeakParameters[peak_index][i]);
1341 for (
size_t i = 0; i < peakfunction->nParams(); ++i) {
1342 peakfunction->setParameter(i, lastGoodPeakParameters[prev_peak_index][i]);
1347 peakfunction->setCentre(expected_peak_pos);
1350 for (
const auto &[ipar,
value] : keep_values) {
1351 peakfunction->setParameter(ipar,
value);
1354 double cost(DBL_MAX);
1355 if (expected_peak_pos <= x0 || expected_peak_pos >= xf) {
1357 peakfunction->setIntensity(0);
1358 number_of_out_of_range_peaks++;
1368 auto useUserSpecifedIfGiven =
1373 g_log.
warning(
"Peak width can be estimated as ZERO. The result can be wrong");
1377 std::shared_ptr<FitPeaksAlgorithm::PeakFitPreCheckResult> peak_pre_check_result =
1378 std::make_shared<FitPeaksAlgorithm::PeakFitPreCheckResult>();
1383 double peak_pos_tolerance = -1.0;
1387 peak_pos_tolerance *= (peak_window_i.second - peak_window_i.first);
1389 cost =
fitIndividualPeak(wi, peak_fitter, expected_peak_pos, peak_pos_tolerance, peak_window_i,
1390 observe_peak_width, peakfunction, bkgdfunction, peak_pre_check_result);
1391 if (peak_pre_check_result->isIndividualPeakRejected())
1392 fit_result->setBadRecord(peak_index, -1.);
1396 fit_result->setBadRecord(peak_index, -1.);
1401 *pre_check_result += *peak_pre_check_result;
1403 pre_check_result->setNumberOfOutOfRangePeaks(number_of_out_of_range_peaks);
1415 neighborPeakSameSpectrum =
true;
1416 prev_peak_index = peak_index;
1418 for (
size_t i = 0; i < lastGoodPeakParameters[peak_index].size(); ++i) {
1419 lastGoodPeakParameters[peak_index][i] = peakfunction->getParameter(i);
1421 lastGoodPeakSpectra[peak_index] = wi;
1452 if (firstPeakInSpectrum) {
1459 if (param_index >= peak_function->nParams())
1467 if (std::isfinite(param_value))
1468 peak_function->setParameter(param_index, param_value);
1476 observe_peak_shape =
true;
1479 return observe_peak_shape;
1495 const std::vector<double> &expected_peak_positions,
1497 const std::shared_ptr<FitPeaksAlgorithm::PeakFitResult> &fit_result) {
1499 double postol(DBL_MAX);
1513 throw std::runtime_error(
"Peak tolerance out of index");
1518 postol *= (fitwindow.second - fitwindow.first);
1526 bool good_fit(
false);
1527 if ((cost < 0) || (cost >= DBL_MAX - 1.) || std::isnan(cost)) {
1533 }
else if (case23) {
1536 if (fitwindow.first < fitwindow.second) {
1539 if (peak_pos < fitwindow.first || peak_pos > fitwindow.second) {
1542 g_log.
debug() <<
"Peak position " << peak_pos <<
" is out of fit "
1543 <<
"window boundary " << fitwindow.first <<
", " << fitwindow.second <<
"\n";
1544 }
else if (peak_fwhm > (fitwindow.second - fitwindow.first)) {
1547 g_log.
debug() <<
"Peak position " << peak_pos <<
" has fwhm "
1548 <<
"wider than the fit window " << fitwindow.second - fitwindow.first <<
"\n";
1554 double left_bound(-1);
1556 left_bound = 0.5 * (expected_peak_positions[peakindex] - expected_peak_positions[peakindex - 1]);
1557 double right_bound(-1);
1559 right_bound = 0.5 * (expected_peak_positions[peakindex + 1] - expected_peak_positions[peakindex]);
1561 left_bound = right_bound;
1562 if (right_bound < left_bound)
1563 right_bound = left_bound;
1564 if (left_bound < 0 || right_bound < 0)
1565 throw std::runtime_error(
"Code logic error such that left or right "
1566 "boundary of peak position is negative.");
1567 if (peak_pos < left_bound || peak_pos > right_bound) {
1569 }
else if (peak_fwhm > (right_bound - left_bound)) {
1572 g_log.
debug() <<
"Peak position " << peak_pos <<
" has fwhm "
1573 <<
"wider than the fit window " << right_bound - left_bound <<
"\n";
1578 }
else if (
fabs(fitfunction.
peakfunction->centre() - expected_peak_positions[peakindex]) > postol) {
1582 <<
fabs(fitfunction.
peakfunction->centre() - expected_peak_positions[peakindex])
1583 <<
" is out of range of tolerance: " << postol <<
"\n";
1590 double adjust_cost(cost);
1593 adjust_cost = DBL_MAX;
1597 if (adjust_cost > DBL_MAX - 1) {
1602 fit_result->setRecord(peakindex, adjust_cost, peak_pos, fitfunction);
1616 throw std::runtime_error(
"No parameters");
1626 std::vector<std::vector<IBackgroundFunction_sptr>> bkgd_functions(
1631 const std::shared_ptr<FitPeaksAlgorithm::PeakFitResult> &fit_result_i = fit_results[output_iws];
1634 throw std::runtime_error(
"There is something wroing with PeakFitResult vector!");
1637 const double chi2 = fit_result_i->getCost(ipeak);
1644 for (
size_t iparam = 0; iparam < num_peakfunc_params; ++iparam)
1645 peak_function->setParameter(iparam, fit_result_i->getParameterValue(ipeak, iparam));
1646 for (
size_t iparam = 0; iparam < num_bkgdfunc_params; ++iparam)
1647 bkgd_function->setParameter(iparam, fit_result_i->getParameterValue(ipeak, num_peakfunc_params + iparam));
1650 peak_function->setMatrixWorkspace(
m_inputMatrixWS, iws, peakwindow.first, peakwindow.second);
1652 peak_functions[output_iws][ipeak] = std::move(peak_function);
1653 bkgd_functions[output_iws][ipeak] = std::move(bkgd_function);
1660 const std::size_t iws =
static_cast<std::size_t
>(iiws);
1664 if (!peak_functions[output_iws][ipeak] || !bkgd_functions[output_iws][ipeak])
1671 auto start_x_iter = std::lower_bound(vec_x.begin(), vec_x.end(), peakwindow.first);
1672 auto stop_x_iter = std::lower_bound(vec_x.begin(), vec_x.end(), peakwindow.second);
1674 if (start_x_iter == stop_x_iter)
1675 throw std::runtime_error(
"Range size is zero in calculateFittedPeaks");
1680 comp_func->addFunction(std::move(peak_functions[output_iws][ipeak]));
1681 comp_func->addFunction(std::move(bkgd_functions[output_iws][ipeak]));
1682 comp_func->function(domain, values);
1685 std::size_t
istart =
static_cast<size_t>(start_x_iter - vec_x.begin());
1686 std::size_t istop =
static_cast<size_t>(stop_x_iter - vec_x.begin());
1687 for (std::size_t yindex =
istart; yindex < istop; ++yindex) {
1701 auto startX = std::lower_bound(vecX.begin(), vecX.end(), peakWindow.first);
1702 auto stopX = std::lower_bound(vecX.begin(), vecX.end(), peakWindow.second);
1707 peakFunction->function(domain, values);
1708 auto peakValues = values.
toVector();
1711 auto startE = errors.begin() + (startX - vecX.begin());
1712 auto stopE = errors.begin() + (stopX - vecX.begin());
1713 std::vector<double> peakErrors(startE, stopE);
1715 double peakSum = std::accumulate(peakValues.cbegin(), peakValues.cend(), 0.0);
1722bool estimateBackgroundParameters(
const Histogram &histogram,
const std::pair<size_t, size_t> &peak_window,
1726 const auto POLYNOMIAL_ORDER = std::min<size_t>(1, bkgd_function->nParams());
1728 if (peak_window.first >= peak_window.second)
1729 throw std::runtime_error(
"Invalid peak window");
1732 const auto nParams = bkgd_function->nParams();
1733 for (
size_t i = 0; i < nParams; ++i)
1734 bkgd_function->setParameter(i, 0.);
1737 const size_t iback_start = peak_window.first + 10;
1738 const size_t iback_stop = peak_window.second - 10;
1743 if (iback_start < iback_stop) {
1747 double chisq{DBL_MAX};
1748 HistogramData::estimateBackground(POLYNOMIAL_ORDER, histogram, peak_window.first, peak_window.second, iback_start,
1749 iback_stop, bkgd_a0, bkgd_a1, bkgd_a2, chisq);
1751 bkgd_function->setParameter(0, bkgd_a0);
1753 bkgd_function->setParameter(1, bkgd_a1);
1771 return (std::find(supported_peak_profiles.begin(), supported_peak_profiles.end(), peakprofile) !=
1772 supported_peak_profiles.end());
1780 constexpr size_t MIN_POINTS{10};
1784 const auto &points = histogram.points();
1785 size_t start_index =
findXIndex(points.rawData(), fit_window.first);
1786 size_t expected_peak_index =
findXIndex(points.rawData(), expected_peak_pos, start_index);
1787 size_t stop_index =
findXIndex(points.rawData(), fit_window.second, expected_peak_index);
1790 bool good_fit(
false);
1791 if (expected_peak_index - start_index > MIN_POINTS && stop_index - expected_peak_index > MIN_POINTS) {
1794 const std::pair<double, double> vec_min{fit_window.first, points[expected_peak_index + 5]};
1795 const std::pair<double, double> vec_max{points[expected_peak_index - 5], fit_window.second};
1798 for (
size_t n = 0;
n < bkgd_func->nParams(); ++
n)
1799 bkgd_func->setParameter(
n, 0);
1804 if (chi2 < DBL_MAX - 1) {
1812 g_log.
debug() <<
"Don't know what to do with background fitting with single "
1813 <<
"domain function! " << (expected_peak_index - start_index) <<
" points to the left "
1814 << (stop_index - expected_peak_index) <<
" points to the right\n";
1824 const double peak_pos_tolerance,
const std::pair<double, double> &fitwindow,
1827 const std::shared_ptr<FitPeaksAlgorithm::PeakFitPreCheckResult> &pre_check_result) {
1828 pre_check_result->setNumberOfSubmittedIndividualPeaks(1);
1829 double cost(DBL_MAX);
1832 size_t min_required_datapoints{peakfunction->nParams() + bkgdfunc->nParams() + 2};
1834 if (number_of_datapoints < min_required_datapoints) {
1835 pre_check_result->setNumberOfPeaksWithNotEnoughDataPoints(1);
1841 pre_check_result->setNumberOfIndividualPeaksWithLowCount(1);
1847 pre_check_result->setNumberOfPeaksWithLowSignalToNoise(1);
1854 estimate_peak_width, peakfunction, bkgdfunc);
1858 peak_pos_tolerance, estimate_peak_width,
true);
1887 const std::pair<double, double> &peak_range,
const double &expected_peak_center,
1888 const double peak_pos_tolerance,
bool estimate_peak_width,
bool estimate_background) {
1889 std::stringstream errorid;
1890 errorid <<
"(WorkspaceIndex=" << wsindex <<
" PeakCentre=" << expected_peak_center <<
")";
1893 if (peak_range.first >= peak_range.second) {
1894 std::stringstream msg;
1895 msg <<
"Invalid peak window: xmin>xmax (" << peak_range.first <<
", " << peak_range.second <<
")" << errorid.str();
1896 throw std::runtime_error(msg.str());
1900 const auto &histogram = dataws->histogram(wsindex);
1901 const auto &vector_x = histogram.points();
1902 const auto start_index =
findXIndex(vector_x, peak_range.first);
1903 const auto stop_index =
findXIndex(vector_x, peak_range.second, start_index);
1904 if (start_index == stop_index)
1905 throw std::runtime_error(
"Range size is zero in fitFunctionSD");
1906 std::pair<size_t, size_t> peak_index_window = std::make_pair(start_index, stop_index);
1909 if (estimate_background) {
1910 if (!estimateBackgroundParameters(histogram, peak_index_window, bkgd_function)) {
1916 peak_function->setCentre(expected_peak_center);
1917 int result =
estimatePeakParameters(histogram, peak_index_window, peak_function, bkgd_function, estimate_peak_width,
1920 if (result !=
GOOD) {
1921 peak_function->setCentre(expected_peak_center);
1929 comp_func->addFunction(peak_function);
1930 comp_func->addFunction(bkgd_function);
1931 IFunction_sptr fitfunc = std::dynamic_pointer_cast<IFunction>(comp_func);
1934 fit->setProperty(
"Function", fitfunc);
1935 fit->setProperty(
"InputWorkspace", dataws);
1936 fit->setProperty(
"WorkspaceIndex",
static_cast<int>(wsindex));
1938 fit->setProperty(
"StartX", peak_range.first);
1939 fit->setProperty(
"EndX", peak_range.second);
1940 fit->setProperty(
"IgnoreInvalidData",
true);
1945 bool positionConstrained =
false;
1946 if (constrainByTolerance) {
1948 std::stringstream peak_center_constraint;
1949 peak_center_constraint << std::setprecision(std::numeric_limits<double>::max_digits10);
1950 peak_center_constraint << (expected_peak_center - peak_pos_tolerance) <<
" < f0."
1951 << peak_function->getCentreParameterName() <<
" < "
1952 << (expected_peak_center + peak_pos_tolerance);
1953 fit->setProperty(
"Constraints", peak_center_constraint.str());
1954 positionConstrained =
true;
1957 double peak_center = peak_function->centre();
1958 double peak_width = peak_function->fwhm();
1959 std::stringstream peak_center_constraint;
1960 peak_center_constraint << std::setprecision(std::numeric_limits<double>::max_digits10);
1961 peak_center_constraint << (peak_center - 0.5 * peak_width) <<
" < f0." << peak_function->getCentreParameterName()
1962 <<
" < " << (peak_center + 0.5 * peak_width);
1963 fit->setProperty(
"Constraints", peak_center_constraint.str());
1964 positionConstrained =
true;
1968 g_log.
debug() <<
"[E1201] FitSingleDomain Before fitting, Fit function: " << fit->asString() <<
"\n";
1969 errorid <<
" starting function [" << comp_func->asString() <<
"]";
1972 g_log.
debug() <<
"[E1202] FitSingleDomain After fitting, Fit function: " << fit->asString() <<
"\n";
1974 if (!fit->isExecuted()) {
1975 g_log.
warning() <<
"Fitting peak SD (single domain) failed to execute. " + errorid.str();
1978 }
catch (std::invalid_argument &e) {
1979 errorid <<
": " << e.what();
1985 std::string fitStatus = fit->getProperty(
"OutputStatus");
1986 double chi2{std::numeric_limits<double>::max()};
1988 chi2 = fit->getProperty(
"OutputChi2overDoF");
2012 const std::pair<double, double> &peak_range) {
2016 IPeakFunction_sptr peak_clone = std::dynamic_pointer_cast<IPeakFunction>(peak_function->clone());
2019 comp_func->addFunction(peak_clone);
2020 comp_func->addFunction(bkgd_clone);
2024 fit->setProperty(
"Function", std::dynamic_pointer_cast<IFunction>(comp_func));
2025 fit->setProperty(
"InputWorkspace", dataws);
2026 fit->setProperty(
"WorkspaceIndex",
static_cast<int>(wsindex));
2027 fit->setProperty(
"MaxIterations", 0);
2028 fit->setProperty(
"StartX", peak_range.first);
2029 fit->setProperty(
"EndX", peak_range.second);
2030 fit->setProperty(
"IgnoreInvalidData",
true);
2031 fit->setProperty(
"CalcErrors",
true);
2037 }
catch (
const std::exception &e) {
2040 g_log.
debug() <<
"Unconstrained error re-evaluation failed: " << e.what() <<
"\n";
2043 if (!fit->isExecuted())
2047 for (
size_t i = 0; i < peak_function->nParams(); ++i)
2048 peak_function->setError(i, peak_clone->getError(i));
2049 for (
size_t i = 0; i < bkgd_function->nParams(); ++i)
2050 bkgd_function->setError(i, bkgd_clone->getError(i));
2055 const size_t wsindex,
const std::pair<double, double> &vec_xmin,
2056 const std::pair<double, double> &vec_xmax) {
2062 fit->setProperty(
"CalcErrors",
true);
2066 std::shared_ptr<MultiDomainFunction> md_function = std::make_shared<MultiDomainFunction>();
2069 md_function->addFunction(std::move(fit_function));
2072 md_function->clearDomainIndices();
2073 md_function->setDomainIndices(0, {0, 1});
2076 fit->setProperty(
"Function", std::dynamic_pointer_cast<IFunction>(md_function));
2077 fit->setProperty(
"InputWorkspace", dataws);
2078 fit->setProperty(
"WorkspaceIndex",
static_cast<int>(wsindex));
2079 fit->setProperty(
"StartX", vec_xmin.first);
2080 fit->setProperty(
"EndX", vec_xmax.first);
2081 fit->setProperty(
"InputWorkspace_1", dataws);
2082 fit->setProperty(
"WorkspaceIndex_1",
static_cast<int>(wsindex));
2083 fit->setProperty(
"StartX_1", vec_xmin.second);
2084 fit->setProperty(
"EndX_1", vec_xmax.second);
2086 fit->setProperty(
"IgnoreInvalidData",
true);
2090 if (!fit->isExecuted()) {
2091 throw runtime_error(
"Fit is not executed on multi-domain function/data. ");
2095 std::string fitStatus = fit->getProperty(
"OutputStatus");
2097 double chi2 = DBL_MAX;
2099 chi2 = fit->getProperty(
"OutputChi2overDoF");
2108 const size_t &ws_index,
const double &expected_peak_center,
2109 const double peak_pos_tolerance,
bool observe_peak_shape,
2119 fitBackground(ws_index, fit_window, expected_peak_center, high_bkgd_function);
2122 std::vector<double> vec_x, vec_y, vec_e;
2123 getRangeData(ws_index, fit_window, vec_x, vec_y, vec_e);
2126 reduceByBackground(high_bkgd_function, vec_x, vec_y);
2127 for (std::size_t
n = 0;
n < bkgdfunc->nParams(); ++
n)
2128 bkgdfunc->setParameter(
n, 0);
2136 fitFunctionSD(fit, peakfunction, bkgdfunc, reduced_bkgd_ws, 0, {vec_x.front(), vec_x.back()}, expected_peak_center,
2137 -1.0, observe_peak_shape,
false);
2140 bkgdfunc->setParameter(0, bkgdfunc->getParameter(0) + high_bkgd_function->getParameter(0));
2141 bkgdfunc->setParameter(1, bkgdfunc->getParameter(1) +
2142 high_bkgd_function->getParameter(1));
2145 expected_peak_center, peak_pos_tolerance,
false,
false);
2153 const std::vector<double> &vec_y,
2154 const std::vector<double> &vec_e) {
2155 std::size_t size = vec_x.size();
2156 std::size_t ysize = vec_y.size();
2158 HistogramBuilder builder;
2160 builder.setY(ysize);
2163 auto &dataX = matrix_ws->mutableX(0);
2164 auto &dataY = matrix_ws->mutableY(0);
2165 auto &dataE = matrix_ws->mutableE(0);
2167 dataX.assign(vec_x.cbegin(), vec_x.cend());
2168 dataY.assign(vec_y.cbegin(), vec_y.cend());
2169 dataE.assign(vec_e.cbegin(), vec_e.cend());
2185 for (std::size_t ipeak = 0; ipeak < expected_position.size(); ++ipeak) {
2201 const std::vector<std::string> ¶m_names,
bool with_chi2) {
2203 table_ws->addColumn(
"int",
"wsindex");
2204 table_ws->addColumn(
"int",
"peakindex");
2205 for (
const auto ¶m_name : param_names)
2206 table_ws->addColumn(
"double", param_name);
2208 table_ws->addColumn(
"double",
"chi2");
2215 newRow << static_cast<int>(iws);
2216 newRow << static_cast<int>(ipeak);
2217 for (
size_t iparam = 0; iparam < numParam; ++iparam)
2238 std::vector<std::string> param_vec;
2242 param_vec.emplace_back(
"centre");
2243 param_vec.emplace_back(
"width");
2244 param_vec.emplace_back(
"height");
2245 param_vec.emplace_back(
"intensity");
2248 for (
size_t iparam = 0; iparam <
m_bkgdFunction->nParams(); ++iparam)
2256 std::string fiterror_table_name =
getPropertyValue(PropertyNames::OUTPUT_WKSP_PARAM_ERRS);
2258 if (fiterror_table_name.empty()) {
2276 std::string fit_ws_name =
getPropertyValue(PropertyNames::OUTPUT_WKSP_MODEL);
2277 if (fit_ws_name.size() == 0) {
2302 g_log.
debug(
"about to calcualte fitted peaks");
2315 const auto &vec_y = histogram.y().rawData();
2316 double total = std::accumulate(vec_y.begin(), vec_y.end(), 0.);
2328 std::vector<double> vec_x, vec_y, vec_e;
2331 double total = std::accumulate(vec_y.begin(), vec_y.end(), 0.);
2342 size_t left_index, right_index;
2344 size_t number_dp = right_index - left_index + 1;
2347 assert(number_dp > 0);
2359 size_t &right_index) {
2361 const auto &orig_x = histogram.x();
2362 rangeToIndexBounds(orig_x, range.first, range.second, left_index, right_index);
2366 if (left_index >= right_index || (
m_inputMatrixWS->isHistogramData() && left_index == right_index - 1)) {
2367 std::stringstream err_ss;
2368 err_ss <<
"Unable to get a valid subset of histogram from given fit window. "
2369 <<
"Histogram X: " << orig_x.front() <<
"," << orig_x.back() <<
"; Range: " << range.first <<
","
2371 throw std::runtime_error(err_ss.str());
2384 std::vector<double> &vec_y, std::vector<double> &vec_e) {
2386 size_t left_index, right_index;
2390 size_t num_elements_x = right_index - left_index;
2392 vec_x.resize(num_elements_x);
2394 const auto &orig_x = histogram.x();
2395 std::copy(orig_x.begin() + left_index, orig_x.begin() + right_index, vec_x.begin());
2397 size_t num_datapoints =
m_inputMatrixWS->isHistogramData() ? num_elements_x - 1 : num_elements_x;
2399 const auto &orig_y = histogram.y().rawData();
2400 const auto &orig_e = histogram.e().rawData();
2401 vec_y.resize(num_datapoints);
2402 vec_e.resize(num_datapoints);
2403 std::copy(orig_y.begin() + left_index, orig_y.begin() + left_index + num_datapoints, vec_y.begin());
2404 std::copy(orig_e.begin() + left_index, orig_e.begin() + left_index + num_datapoints, vec_e.begin());
2417 size_t left_index, right_index;
2421 if (!estimateBackgroundParameters(
m_inputMatrixWS->histogram(iws), std::pair<size_t, size_t>(left_index, right_index),
2426 std::vector<double> vec_x, vec_y, vec_e;
2432 reduceByBackground(bkgd_function, vec_x, vec_y);
2435 auto it_max = std::max_element(vec_y.begin(), vec_y.end());
2436 double signal = vec_y[it_max - vec_y.begin()];
2437 if (signal <= DBL_MIN)
2442 double noise = estimateBackgroundNoise(vec_y);
2443 if (noise <= DBL_MIN)
2447 return signal / noise;
2455 std::stringstream errss;
2456 errss <<
"Workspace index " << wi <<
" is out of range "
2458 throw std::runtime_error(errss.str());
2465 std::stringstream errss;
2466 errss <<
"Peak index " << ipeak <<
" is out of range (" <<
m_numPeaksToFit <<
")";
2467 throw std::runtime_error(errss.str());
2473 std::stringstream errss;
2474 errss <<
"Peak window is inappropriate for workspace index: " <<
left <<
" >= " <<
right;
2475 throw std::runtime_error(errss.str());
2488 std::stringstream errss;
2489 errss <<
"The FitPeaks algorithm requires the CurveFitting library";
2491 throw std::runtime_error(errss.str());
2505 const std::shared_ptr<FitPeaksAlgorithm::PeakFitResult> &fit_result) {
2509 g_log.
error() <<
"workspace index " << wi <<
" is out of output peak position workspace "
2513 throw std::runtime_error(
"Out of boundary to set output peak position workspace");
2518 double exp_peak_pos(expected_positions[ipeak]);
2519 double fitted_peak_pos = fit_result->getPeakPosition(ipeak);
2520 double peak_chi2 = fit_result->getCost(ipeak);
2539 <<
" parameters. Parameter table shall have 3 more "
2540 "columns. But not it has "
2542 throw std::runtime_error(
"Peak parameter vector for one peak has different sizes to output "
2549 std::stringstream err_ss;
2550 err_ss <<
"Peak has 4 effective peak parameters and " <<
m_bkgdFunction->nParams() <<
" background parameters "
2551 <<
". Parameter table shall have 3 more columns. But not it has " <<
m_fittedParamTable->columnCount()
2553 throw std::runtime_error(err_ss.str());
2560 size_t num_peakfunc_params = peak_function->nParams();
2570 for (
size_t iparam = 0; iparam < num_peakfunc_params + num_bkgd_params; ++iparam) {
2571 size_t col_index = iparam + 2;
2573 m_fittedParamTable->cell<
double>(row_index, col_index) = fit_result->getParameterValue(ipeak, iparam);
2576 m_fitErrorTable->cell<
double>(row_index, col_index) = fit_result->getParameterError(ipeak, iparam);
2583 for (
size_t iparam = 0; iparam < num_peakfunc_params; ++iparam)
2584 peak_function->setParameter(iparam, fit_result->getParameterValue(ipeak, iparam));
2587 peak_function->setMatrixWorkspace(
m_inputMatrixWS, wi, peak_window.first, peak_window.second);
2596 for (
size_t iparam = 0; iparam < num_bkgd_params; ++iparam)
2598 fit_result->getParameterValue(ipeak, num_peakfunc_params + iparam);
2602 m_fittedParamTable->cell<
double>(row_index, chi2_index) = fit_result->getCost(ipeak);
2610 std::string height_name(
"");
2612 std::vector<std::string> peak_parameters = peak_function->getParameterNames();
2613 for (
const auto &parName : peak_parameters) {
2614 if (parName ==
"Height") {
2615 height_name =
"Height";
2617 }
else if (parName ==
"I") {
2620 }
else if (parName ==
"Intensity") {
2621 height_name =
"Intensity";
2626 if (height_name.empty())
2627 throw std::runtime_error(
"Peak height parameter name cannot be found.");
2635 LoggingOffsetSentry sentry(
this);
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PRAGMA_OMP(expression)
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
Base class from which all concrete algorithm classes should be derived.
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.
void setLoggingOffset(const int value) override
gets the logging priority offset
int getLoggingOffset() const override
returns the logging priority offset
bool isDefault(const std::string &name) const
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Implements FunctionDomain1D with its own storage in form of a std::vector.
A class to store values calculated by a function.
const std::vector< double > & toVector() const
Return the calculated values as a vector.
double getCalculated(size_t i) const
Get i-th calculated value.
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
Helper class for reporting progress from algorithms.
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
size_t m_submitted_spectrum_peaks
void setNumberOfSpectrumPeaksWithLowCount(const size_t n)
std::string getReport() const
size_t m_low_count_individual
PeakFitPreCheckResult & operator+=(const PeakFitPreCheckResult &another)
size_t m_not_enough_datapoints
void setNumberOfSubmittedIndividualPeaks(const size_t n)
void setNumberOfPeaksWithNotEnoughDataPoints(const size_t n)
size_t m_low_count_spectrum
void setNumberOfOutOfRangePeaks(const size_t n)
void setNumberOfPeaksWithLowSignalToNoise(const size_t n)
size_t m_submitted_individual_peaks
void setNumberOfIndividualPeaksWithLowCount(const size_t n)
void setNumberOfSubmittedSpectrumPeaks(const size_t n)
bool isIndividualPeakRejected() const
size_t m_function_parameters_number
number of function parameters
double getPeakPosition(size_t ipeak) const
size_t getNumberPeaks() const
std::vector< std::vector< double > > m_function_parameters_vector
PeakFitResult(size_t num_peaks, size_t num_params)
Holds all of the fitting information for a single spectrum.
double getParameterValue(size_t ipeak, size_t iparam) const
get the fitted value of a particular parameter
std::vector< double > m_costs
std::vector< std::vector< double > > m_function_errors_vector
fitted peak and background parameters' fitting error
size_t getNumberParameters() const
double getCost(size_t ipeak) const
void setRecord(size_t ipeak, const double cost, const double peak_position, const FitFunction &fit_functions)
set the peak fitting record/parameter for one peak
double getParameterError(size_t ipeak, size_t iparam) const
get the fitting error of a particular parameter
void setBadRecord(size_t ipeak, const double peak_position)
The peak postition should be negative and indicates what went wrong.
std::vector< double > m_fitted_peak_positions
Algorithms::PeakParameterHelper::EstimatePeakWidth m_peakWidthEstimateApproach
Flag for observing peak width: there are 3 states (1) no estimation (2) from 'observation' (3) calcul...
void calculateFittedPeaks(const std::vector< std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > > &fit_results)
calculate peak+background for fitted
double calculateSignalToNoiseRatio(size_t iws, const std::pair< double, double > &range, const API::IBackgroundFunction_sptr &bkgd_function)
calculate signal-to-noise ratio in histogram range
API::MatrixWorkspace_const_sptr m_peakCenterWorkspace
void generateFittedParametersValueWorkspaces()
Generate output workspaces.
void setupParameterTableWorkspace(const API::ITableWorkspace_sptr &table_ws, const std::vector< std::string > ¶m_names, bool with_chi2)
Set up parameter table (parameter value or error)
bool m_copyLastGoodPeakParameters
std::vector< double > m_peakPosTolerances
tolerances for fitting peak positions
API::IPeakFunction_sptr m_peakFunction
Peak profile name.
bool fitBackground(const size_t &ws_index, const std::pair< double, double > &fit_window, const double &expected_peak_pos, const API::IBackgroundFunction_sptr &bkgd_func)
fit background
API::MatrixWorkspace_sptr m_outputPeakPositionWorkspace
output workspace for peak positions
API::MatrixWorkspace_sptr m_fittedPeakWS
matrix workspace contained calcalated peaks+background from fitted result it has same number of spect...
bool m_strictConvergence
Require an exact 'success' status to accept a fit, rather than also accepting the "changes too small"...
API::ITableWorkspace_const_sptr m_profileStartingValueTable
table workspace for profile parameters' starting value
std::string m_minimizer
Minimzer.
bool m_respectFixedPeakParameters
API::IBackgroundFunction_sptr m_linearBackgroundFunction
Linear background function for high background fitting.
bool m_constrainPeaksPosition
double fitFunctionHighBackground(const API::IAlgorithm_sptr &fit, const std::pair< double, double > &fit_window, const size_t &ws_index, const double &expected_peak_center, const double peak_pos_tolerance, bool observe_peak_shape, const API::IPeakFunction_sptr &peakfunction, const API::IBackgroundFunction_sptr &bkgdfunc)
fit a single peak with high background
bool m_peakPosTolCase234
peak positon tolerance case b, c and d
void fitSpectrumPeaks(size_t wi, const std::vector< double > &expected_peak_centers, const std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > &fit_result, std::vector< std::vector< double > > &lastGoodPeakParameters, std::vector< size_t > &lastGoodPeakSpectra, const std::shared_ptr< FitPeaksAlgorithm::PeakFitPreCheckResult > &pre_check_result)
fit peaks in a same spectrum
std::map< std::string, std::string > validateInputs() override
Validate inputs.
double m_peakWidthPercentage
flag to estimate peak width from
API::IBackgroundFunction_sptr m_bkgdFunction
Background function.
size_t histRangeToDataPointCount(size_t iws, const std::pair< double, double > &range)
convert a histogram range to index boundaries
void checkPeakIndices(std::size_t const &, std::size_t const &)
void processInputPeakCenters()
peak centers
std::vector< std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > > fitPeaks()
suites of method to fit peaks
double m_minPeakHeight
minimum peak height without background and it also serves as the criteria for observed peak parameter
std::string m_costFunction
Cost function.
void processInputFunctions()
process inputs for peak and background functions
void processInputPeakTolerance()
process inputs about fitted peak positions' tolerance
void writeFitResult(size_t wi, const std::vector< double > &expected_positions, const std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > &fit_result)
Write result of peak fit per spectrum to output analysis workspaces.
void histRangeToIndexBounds(size_t iws, const std::pair< double, double > &range, size_t &left_index, size_t &right_index)
convert a histogram range to index boundaries
void exec() override
Main exec method.
void init() override
Init.
void logNoOffset(const size_t &priority, const std::string &msg)
bool m_calculateUnconstrainedErrors
when true, and a peak-position constraint was applied (ConstrainPeakPositions or PositionToleranceMod...
std::size_t m_numSpectraToFit
total number of spectra to be fit
std::vector< std::string > m_peakParamNames
input peak parameters' names
double calculateSignalToSigmaRatio(const size_t &iws, const std::pair< double, double > &peakWindow, const API::IPeakFunction_sptr &peakFunction)
bool isObservablePeakProfile(const std::string &peakprofile)
check whether FitPeaks supports observation on a certain peak profile's parameters (width!...
void processInputFitRanges()
process inputs for peak fitting range
std::size_t m_numPeaksToFit
the number of peaks to fit in all spectra
double fitIndividualPeak(size_t wi, const API::IAlgorithm_sptr &fitter, const double expected_peak_center, const double peak_pos_tolerance, const std::pair< double, double > &fitwindow, const bool estimate_peak_width, const API::IPeakFunction_sptr &peakfunction, const API::IBackgroundFunction_sptr &bkgdfunc, const std::shared_ptr< FitPeaksAlgorithm::PeakFitPreCheckResult > &pre_check_result)
Fit an individual peak.
std::size_t m_startWorkspaceIndex
start index
API::MatrixWorkspace_sptr createMatrixWorkspace(const std::vector< double > &vec_x, const std::vector< double > &vec_y, const std::vector< double > &vec_e)
Create a single spectrum workspace for fitting.
void generateOutputPeakPositionWS()
main method to create output workspaces
void generateCalculatedPeaksWS()
Generate workspace for calculated values.
static bool fitStatusIsConverged(const std::string &fitStatus, const bool strict)
Decide whether a Fit "OutputStatus" string should be treated as a converged fit.
double fitFunctionMD(API::IFunction_sptr fit_function, const API::MatrixWorkspace_sptr &dataws, const size_t wsindex, const std::pair< double, double > &vec_xmin, const std::pair< double, double > &vec_xmax)
double m_minPeakTotalCount
std::string getPeakHeightParameterName(const API::IPeakFunction_const_sptr &peak_function)
Get the parameter name for peak height (I or height or etc)
API::IAlgorithm_sptr createChildFit()
Create a Fit child algorithm, with a check that the CurveFitting library is available.
bool m_uniformPeakPositions
API::ITableWorkspace_sptr m_fittedParamTable
output analysis workspaces table workspace for fitted parameters
double m_minSignalToNoiseRatio
void convertParametersNameToIndex()
Convert peak function's parameter names to parameter index for fast access.
void processOutputs(std::vector< std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > > fit_result_vec)
Set the workspaces and etc to output properties.
bool m_highBackground
flag for high background
void recalculateErrorsWithoutConstraint(const API::IPeakFunction_sptr &peak_function, const API::IBackgroundFunction_sptr &bkgd_function, const API::MatrixWorkspace_sptr &dataws, size_t wsindex, const std::pair< double, double > &peak_range)
Re-evaluate parameter fitting errors free of any peak-position boundary constraint penalty,...
std::vector< double > m_initParamValues
input peak parameters' starting values corresponding to above peak parameter names
std::vector< double > m_peakCenters
Designed peak positions and tolerance.
std::vector< std::vector< double > > m_peakWindowVector
peak windows
double fitFunctionSD(const API::IAlgorithm_sptr &fit, const API::IPeakFunction_sptr &peak_function, const API::IBackgroundFunction_sptr &bkgd_function, const API::MatrixWorkspace_sptr &dataws, size_t wsindex, const std::pair< double, double > &peak_range, const double &expected_peak_center, const double peak_pos_tolerance, bool estimate_peak_width, bool estimate_background)
Methods to fit functions (general)
std::function< std::pair< double, double >(std::size_t const &, std::size_t const &)> m_getPeakFitWindow
API::MatrixWorkspace_sptr m_inputMatrixWS
mandatory input and output workspaces
std::vector< size_t > m_initParamIndexes
input starting parameters' indexes in peak function
bool m_fitPeaksFromRight
Fit from right or left.
std::function< std::vector< double >(std::size_t const &)> m_getExpectedPeakPositions
bool m_rawPeaksTable
flag to show that the pamarameters in table are raw parameters or effective parameters
void processInputs()
process inputs (main and child algorithms)
void checkWorkspaceIndices(std::size_t const &)
Get the expected peak's position.
void checkPeakWindowEdgeOrder(double const &, double const &)
bool decideToEstimatePeakParams(const bool firstPeakInSpectrum, const size_t wsindex, const API::IPeakFunction_sptr &peak_function)
Decide whether to estimate peak parameters.
int m_fitIterations
Fit iterations.
double m_minSignalToSigmaRatio
double numberCounts(size_t iws)
sum up all counts in histogram
bool m_fractionalPositionTolerance
when true, each PositionTolerance value is interpreted as a fraction of this peak's (per-spectrum) fi...
API::MatrixWorkspace_const_sptr m_peakWindowWorkspace
bool m_uniformProfileStartingValue
flag for profile startng value being uniform or not
API::ITableWorkspace_sptr m_fitErrorTable
table workspace for fitted parameters' fitting error. This is optional
void getRangeData(size_t iws, const std::pair< double, double > &range, std::vector< double > &vec_x, std::vector< double > &vec_y, std::vector< double > &vec_e)
get vector X, Y and E in a given range
bool m_constrainByPositionTolerance
when true, PositionTolerance is applied as an active constraint on the peak centre during fitting (bo...
std::size_t m_stopWorkspaceIndex
stop index (workspace index of the last spectrum included)
bool processSinglePeakFitResult(size_t wsindex, size_t peakindex, const double cost, const std::vector< double > &expected_peak_positions, const FitPeaksAlgorithm::FitFunction &fitfunction, const std::shared_ptr< FitPeaksAlgorithm::PeakFitResult > &fit_result)
Process the result from fitting a single peak.
Support for a property that holds an array of values.
Exception for when an item is not found in a collection.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
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.
void notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
StartsWithValidator is a validator that requires the value of a property to start with one of the str...
const std::string CHANGES_IN_FUNCTION_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the cost function between iterations has fallen be...
const std::string CHANGES_IN_PARAMETER_TOO_SMALL
Reported by Levenberg-Marquardt when the change in the parameter values between iterations has fallen...
const std::string SUCCESS
Reported when a minimizer has fully converged.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
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< IFunction > IFunction_sptr
shared pointer to the function base class
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
std::string const OUTPUT_WKSP("OutputWorkspace")
std::string const INPUT_WKSP("InputWorkspace")
MANTID_ALGORITHMS_DLL size_t findXIndex(const vector_like &vecx, const double x, const size_t startindex=0)
Get an index of a value in a sorted vector.
MANTID_ALGORITHMS_DLL int estimatePeakParameters(const HistogramData::Histogram &histogram, const std::pair< size_t, size_t > &peak_window, const API::IPeakFunction_sptr &peakfunction, const API::IBackgroundFunction_sptr &bkgdfunction, bool observe_peak_width, const EstimatePeakWidth peakWidthEstimateApproach, const double peakWidthPercentage, const double minPeakHeight)
Estimate peak parameters by 'observation'.
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
std::vector< double > getZscore(const std::vector< TYPE > &data)
Return the Z score values for a dataset.
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
const std::string OUTPUT_WKSP("OutputWorkspace")
const std::string INPUT_WKSP("InputWorkspace")
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.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
API::IPeakFunction_sptr peakfunction
API::IBackgroundFunction_sptr bkgdfunction
@ Input
An input workspace.
@ Output
An output workspace.
Functor to accumulate a sum of squares.