27#include "MantidHistogramData/Histogram.h"
39#include <gsl/gsl_multifit_nlin.h>
40#include <gsl/gsl_multimin.h>
75const auto isNonZero = [](
const double value) {
return value != 0.; };
94 const auto &spectrum = wksp->getSpectrum(
wkspIndex);
95 this->
detid = spectrum.getDetectorIDs();
97 const auto &
X = spectrum.x();
98 const auto &
Y = spectrum.y();
104 for (; minIndex <
Y.size(); ++minIndex) {
105 if (isNonZero(
Y[minIndex])) {
112 size_t maxIndex =
Y.size() - 1;
113 for (; maxIndex > minIndex; --maxIndex) {
114 if (isNonZero(
Y[maxIndex])) {
131 void setPositions(
const std::vector<double> &peaksInD,
const std::vector<double> &peaksInDWindows,
const double difa,
139 inDPos.assign(peaksInD.begin(), peaksInD.end());
140 inTofPos.assign(peaksInD.begin(), peaksInD.end());
141 inTofWindows.assign(peaksInDWindows.begin(), peaksInDWindows.end());
145 std::vector<double> yunused;
187 return "This algorithm determines the diffractometer constants that convert diffraction spectra "
188 "from time-of-flight (TOF) to d-spacing. The results are output to a calibration table.";
196 "Input workspace containing spectra as a function of TOF measured on a standard sample.");
198 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
199 mustBePositive->setLower(0);
200 declareProperty(
"StartWorkspaceIndex", 0, mustBePositive,
"Starting workspace index for fit");
203 "Last workspace index for fit is the smaller of this value and the workspace index of last spectrum.");
207 "Min, Step, and Max of TOF bins. "
208 "Logarithmic binning is used if Step is negative. Chosen binning should ensure sufficient datapoints across "
209 "the peaks to be fitted, considering the number of parameters required by PeakFunction and BackgroundType.");
211 const std::vector<std::string> exts2{
".h5",
".cal"};
213 "An existent file to be loaded into a calibration table, which is used for converting PeakPositions "
214 "from d-spacing to TOF.");
217 "An existent calibration table used for converting PeakPositions from d-spacing to TOF. "
218 "This property has precedence over PreviousCalibrationFile.");
221 std::vector<std::string> peaktypes{
"BackToBackExponential",
"Gaussian",
"Lorentzian",
"PseudoVoigt",
223 declareProperty(
"PeakFunction",
"Gaussian", std::make_shared<StringListValidator>(peaktypes),
224 "Function to fit input peaks.");
225 vector<std::string> bkgdtypes{
"Flat",
"Linear",
"Quadratic"};
226 declareProperty(
"BackgroundType",
"Linear", std::make_shared<StringListValidator>(bkgdtypes),
227 "Function to fit input peaks background.");
229 auto peaksValidator = std::make_shared<CompositeValidator>();
230 auto mustBePosArr = std::make_shared<Kernel::ArrayBoundedValidator<double>>();
231 mustBePosArr->setLower(0.0);
232 peaksValidator->add(mustBePosArr);
235 "Comma-delimited positions (d-spacing) of reference peaks. Care should be taken to avoid using peaks "
236 "whose predicted positions are too close considering their peak windows.");
238 auto windowValidator = std::make_shared<CompositeValidator>();
239 windowValidator->add(mustBePosArr);
240 auto lengthValidator = std::make_shared<Kernel::ArrayLengthValidator<double>>();
241 lengthValidator->setLengthMin(1);
242 windowValidator->add(lengthValidator);
245 "Width of the window (d-spacing) over which to fit a peak. If a single value is supplied, it will be "
247 "the window width for all peaks. Otherwise, the expected input is a comma-delimited list of 2*N "
248 "window boundaries, "
249 "where N is the number of values in PeakPositions. The order of the window boundaries should match "
250 "the order in PeakPositions.");
252 auto min = std::make_shared<BoundedValidator<double>>();
255 "Used for estimating peak width (an initial parameter for peak fitting) by multiplying peak's value "
256 "in PeakPositions by this factor.");
259 "Used for validating peaks before and after fitting. If a peak's observed/estimated or "
260 "fitted height is under this value, the peak will be marked as an error.");
263 "Used for validating peaks after fitting. If the chi-squared value is higher than this value, "
264 "the peak will be excluded from calibration. The recommended value is between 2 and 10.");
267 "If true, a peak center being fit will be constrained by the estimated peak position "
268 "+/- 0.5 * \"estimated peak width\", where the estimated peak position is the highest Y-value "
269 "position in the window, "
270 " and the estimated peak width is FWHM calculated over the window data.");
273 "Flag whether the input data has high background compared to peaks' heights. "
274 "This option is recommended for data with peak-to-background ratios under ~5.");
277 "CopyLastGoodPeakParameters",
true,
278 "If true, for a given peak in a spectrum the initial peak parameters (with the exception of peak centre) "
279 "may be copied from the last successfully fit peak in that spectrum.");
282 "If true, peak function parameters that are marked as fixed "
283 "(e.g. parameters calculated from the instrument geometry, such as A and B "
284 "of a BackToBackExponential) remain fixed during fitting. "
285 "If false (default), such parameters are unfixed so they can be refined.");
288 "Used for validating peaks before fitting. If the signal-to-noise ratio is under this value, "
289 "the peak will be excluded from fitting and calibration. This check does not apply to peaks for "
290 "which the noise cannot be estimated. "
291 "The minimum recommended value is 12.");
293 std::vector<std::string> modes{
"DIFC",
"DIFC+TZERO",
"DIFC+TZERO+DIFA"};
294 declareProperty(
"CalibrationParameters",
"DIFC", std::make_shared<StringListValidator>(modes),
295 "Select which diffractometer constants (GSAS convention) to determine.");
298 "Range for allowable calibrated TZERO value. Default: no restriction.");
301 "Range for allowable calibrated DIFA value. Default: no restriction.");
304 "Defines the weighting scheme used in the least-squares fit of the extracted peak centers "
305 "that determines the diffractometer constants. If true, the peak weight will be "
306 "the inverse square of the error on the fitted peak center. If false, the peak weight will be "
307 "the square of the fitted peak height.");
311 "Output table workspace containing the calibration.");
317 "Mask workspace (optional input / output workspace):"
318 " when specified, if the workspace already exists, any incoming masked detectors will be combined"
319 " with any additional outgoing masked detectors detected by the algorithm");
323 "Auxiliary workspaces containing extended information on the calibration results.");
326 "Used for validating peaks before fitting. If the total peak window Y-value count "
327 "is under this value, the peak will be excluded from fitting and calibration.");
330 "Used for validating peaks after fitting. If the signal-to-sigma ratio is under this value, "
331 "the peak will be excluded from fitting and calibration.");
334 std::string inputGroup(
"Input Options");
342 std::string funcgroup(
"Function Types");
347 std::string fitPeaksGroup(
"Peak Fitting");
362 std::string calGroup(
"Calibration Type");
370 std::map<std::string, std::string> messages;
376 if (maskWS->getInstrument()->getNumberDetectors(
true) != inputWS->getInstrument()->getNumberDetectors(
true)) {
377 messages[
"MaskWorkspace"] =
"incoming mask workspace must have the same instrument as the input workspace";
378 }
else if (maskWS->getNumberHistograms() != inputWS->getInstrument()->getNumberDetectors(
true)) {
379 messages[
"MaskWorkspace"] =
"incoming mask workspace must have one spectrum per detector";
383 vector<double> tzeroRange =
getProperty(
"TZEROrange");
384 if (!tzeroRange.empty()) {
385 if (tzeroRange.size() != 2) {
386 messages[
"TZEROrange"] =
"Require two values [min,max]";
387 }
else if (tzeroRange[0] >= tzeroRange[1]) {
388 messages[
"TZEROrange"] =
"min must be less than max";
392 vector<double> difaRange =
getProperty(
"DIFArange");
393 if (!difaRange.empty()) {
394 if (difaRange.size() != 2) {
395 messages[
"DIFArange"] =
"Require two values [min,max]";
396 }
else if (difaRange[0] >= difaRange[1]) {
397 messages[
"DIFArange"] =
"min must be less than max";
401 vector<double> peakWindow =
getProperty(
"PeakWindow");
402 vector<double> peakCentres =
getProperty(
"PeakPositions");
403 if (peakWindow.size() > 1 && peakWindow.size() != 2 * peakCentres.size()) {
404 messages[
"PeakWindow"] =
"PeakWindow must be a vector with either 1 element (interpreted as half the width of "
405 "the window) or twice the number of peak centres provided.";
415 const auto columnNames = table->getColumnNames();
416 return (std::find(columnNames.begin(), columnNames.end(), std::string(
"dasid")) != columnNames.end());
426double getWidthToFWHM(
const std::string &peakshape) {
428 if (peakshape ==
"Gaussian") {
429 return 2 * std::sqrt(2. * std::log(2.));
430 }
else if (peakshape ==
"Lorentzian") {
432 }
else if (peakshape ==
"BackToBackExponential") {
445 vector<double> tofBinningParams =
getProperty(
"TofBinning");
446 m_tofMin = tofBinningParams.front();
449 vector<double> tzeroRange =
getProperty(
"TZEROrange");
450 if (tzeroRange.size() == 2) {
454 std::stringstream msg;
455 msg <<
"Using tzero range of " <<
m_tzeroMin <<
" <= "
461 m_tzeroMin = std::numeric_limits<double>::lowest();
462 m_tzeroMax = std::numeric_limits<double>::max();
465 vector<double> difaRange =
getProperty(
"DIFArange");
466 if (difaRange.size() == 2) {
470 std::stringstream msg;
471 msg <<
"Using difa range of " <<
m_difaMin <<
" <= "
477 m_difaMin = std::numeric_limits<double>::lowest();
478 m_difaMax = std::numeric_limits<double>::max();
482 std::vector<double> peakWindow =
getProperty(
"PeakWindow");
485 if (peakWindow.size() > 1) {
488 std::map<double, std::pair<double, double>> peakEdgeAndCenter;
490 peakEdgeAndCenter[
m_peaksInDspacing[i]] = {peakWindow[2 * i], peakWindow[2 * i + 1]};
495 peakWindow.reserve(2 * NUMPEAKS);
497 for (
auto it = peakEdgeAndCenter.begin(); it != peakEdgeAndCenter.end(); it++) {
499 peakWindow.push_back(it->second.first);
500 peakWindow.push_back(it->second.second);
507 const double minPeakHeight =
getProperty(
"MinimumPeakHeight");
508 const double minPeakTotalCount =
getProperty(
"MinimumPeakTotalCount");
509 const double minSignalToNoiseRatio =
getProperty(
"MinimumSignalToNoiseRatio");
510 const double minSignalToSigmaRatio =
getProperty(
"MinimumSignalToSigmaRatio");
511 const double maxChiSquared =
getProperty(
"MaxChiSq");
512 const bool copyLastGoodPeakParameters =
getProperty(
"CopyLastGoodPeakParameters");
513 const bool respectFixedPeakParameters =
getProperty(
"RespectFixedPeakParameters");
516 if (calParams == std::string(
"DIFC"))
518 else if (calParams == std::string(
"DIFC+TZERO"))
520 else if (calParams == std::string(
"DIFC+TZERO+DIFA"))
523 throw std::runtime_error(
"Encountered impossible CalibrationParameters value");
532 auto uncalibratedEWS = std::dynamic_pointer_cast<EventWorkspace>(
m_uncalibratedWS);
533 auto isEvent = bool(uncalibratedEWS);
536 if ((!
static_cast<std::string
>(
getProperty(
"PreviousCalibrationFile")).empty()) ||
550 g_log.
debug() <<
"[PDCalibration]: CREATING new MaskWorkspace.\n";
552 maskWS = std::make_shared<MaskWorkspace>(
m_uncalibratedWS->getInstrument());
554 g_log.
debug() <<
"[PDCalibration]: Using EXISTING MaskWorkspace.\n";
559 const std::string peakFunction =
getProperty(
"PeakFunction");
560 const double WIDTH_TO_FWHM = getWidthToFWHM(peakFunction);
561 if (WIDTH_TO_FWHM == 1.) {
562 g_log.
notice() <<
"Unknown conversion for \"" << peakFunction
563 <<
"\", found peak widths and resolution should not be "
564 "directly compared to delta-d/d";
575 double peak_width_percent =
getProperty(
"PeakWidthPercent");
577 const std::string diagnostic_prefix =
getPropertyValue(
"DiagnosticWorkspaces");
582 algFitPeaks->setLoggingOffset(3);
591 algFitPeaks->setProperty(
"PeakCentersWorkspace", tof_peak_center_ws);
594 algFitPeaks->setProperty<std::string>(
"PeakFunction", peakFunction);
595 algFitPeaks->setProperty<std::string>(
"BackgroundType",
getProperty(
"BackgroundType"));
597 algFitPeaks->setProperty(
"FitPeakWindowWorkspace", tof_peak_window_ws);
598 algFitPeaks->setProperty(
"PeakWidthPercent", peak_width_percent);
599 algFitPeaks->setProperty(
"MinimumPeakHeight", minPeakHeight);
600 algFitPeaks->setProperty(
"MinimumPeakTotalCount", minPeakTotalCount);
601 algFitPeaks->setProperty(
"MinimumSignalToNoiseRatio", minSignalToNoiseRatio);
602 algFitPeaks->setProperty(
"MinimumSignalToSigmaRatio", minSignalToSigmaRatio);
604 algFitPeaks->setProperty(
"FitFromRight",
true);
605 const bool highBackground =
getProperty(
"HighBackground");
606 algFitPeaks->setProperty(
"HighBackground", highBackground);
607 bool constrainPeakPosition =
getProperty(
"ConstrainPeakPositions");
608 algFitPeaks->setProperty(
"ConstrainPeakPositions",
609 constrainPeakPosition);
611 algFitPeaks->setProperty(
"Minimizer",
"Levenberg-Marquardt");
612 algFitPeaks->setProperty(
"CostFunction",
"Least squares");
617 algFitPeaks->setProperty(
"RawPeakParameters", useChiSq);
628 algFitPeaks->setPropertyValue(
"OutputPeakParametersWorkspace", diagnostic_prefix +
"_fitparam");
633 algFitPeaks->setPropertyValue(
"FittedPeaksWorkspace", diagnostic_prefix +
"_fitted");
635 algFitPeaks->setPropertyValue(
"OutputParameterFitErrorsWorkspace", diagnostic_prefix +
"_fiterrors");
640 algFitPeaks->setProperty(
"CopyLastGoodPeakParameters", copyLastGoodPeakParameters);
641 algFitPeaks->setProperty(
"RespectFixedPeakParameters", respectFixedPeakParameters);
644 algFitPeaks->executeAsChildAlg();
652 errorTable = algFitPeaks->getProperty(
"OutputParameterFitErrorsWorkspace");
657 throw std::runtime_error(
"FitPeaks does not have output OutputPeakParametersWorkspace.");
659 throw std::runtime_error(
"The number of rows in OutputPeakParametersWorkspace is not correct!");
680 if ((isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) || !spectrumInfo.hasDetectors(wkspIndex) ||
681 spectrumInfo.isMonitor(wkspIndex) ||
682 maskWS->isMasked(
m_uncalibratedWS->getSpectrum(wkspIndex).getDetectorIDs())) {
685 if (spectrumInfo.hasDetectors(wkspIndex) && !spectrumInfo.isMonitor(wkspIndex)) {
686 if (isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) {
687 maskWS->setMasked(
m_uncalibratedWS->getSpectrum(wkspIndex).getDetectorIDs(),
true);
688 g_log.
debug() <<
"FULLY masked spectrum, index: " << wkspIndex <<
"\n";
697 const auto [dif_c, dif_a,
tzero] =
706 std::vector<double> tof_vec_full(numPeaks, std::nan(
""));
707 std::vector<double> d_vec;
708 std::vector<double> tof_vec;
710 std::vector<double> width_vec_full(numPeaks, std::nan(
""));
712 std::vector<double> height_vec_full(numPeaks, std::nan(
""));
713 std::vector<double> weights;
718 for (
size_t peakIndex = 0; peakIndex < numPeaks; ++peakIndex) {
719 size_t rowIndexInFitTable = rowNumInFitTableOffset + peakIndex;
722 if (fittedTable->getRef<
int>(
"wsindex", rowIndexInFitTable) != wkspIndex)
723 throw std::runtime_error(
"workspace index mismatch!");
724 if (fittedTable->getRef<
int>(
"peakindex", rowIndexInFitTable) !=
static_cast<int>(peakIndex))
725 throw std::runtime_error(
"peak index mismatch but workspace index matched");
727 const double chi2 = fittedTable->getRef<
double>(
"chi2", rowIndexInFitTable);
729 double centre_error = 0.0;
734 centre = fittedTable->getRef<
double>(
"centre", rowIndexInFitTable);
735 width = fittedTable->getRef<
double>(
"width", rowIndexInFitTable);
736 height = fittedTable->getRef<
double>(
"height", rowIndexInFitTable);
740 auto peakfunc = std::dynamic_pointer_cast<API::IPeakFunction>(
741 API::FunctionFactory::Instance().createFunction(peakFunction));
743 for (
size_t ipar = 0; ipar < peakfunc->nParams(); ipar++) {
744 peakfunc->setParameter(ipar, fittedTable->getRef<
double>(peakfunc->parameterName(ipar), rowIndexInFitTable));
749 centre = peakfunc->centre();
750 width = peakfunc->fwhm();
751 height = peakfunc->height();
752 centre_error = errorTable->getRef<
double>(peakfunc->getCentreParameterName(), rowIndexInFitTable);
756 if (chi2 > maxChiSquared || chi2 < 0.) {
757 g_log.
debug(
"failure to fit: chi2 > maximum");
764 g_log.
debug(
"failure to fit: peak center is out-of-range");
769 if (
height < minPeakHeight + 1.E-15) {
770 g_log.
debug(
"failure to fit: peak height is less than minimum");
775 g_log.
getLogStream(Logger::Priority::PRIO_TRACE) <<
"successful fit: peak centered at " << centre <<
"\n";
778 tof_vec.emplace_back(centre);
782 weights.emplace_back(1 / (centre_error * centre_error));
784 tof_vec_full[peakIndex] = centre;
785 width_vec_full[peakIndex] = width;
786 height_vec_full[peakIndex] =
height;
789 if (d_vec.size() < 2) {
792 maskWS->setMasked(peaks.
detid,
true);
795 for (
const auto &det : peaks.
detid) {
805 double difc = 0., t0 = 0.,
difa = 0.;
807 for (
auto iter = peaks.
detid.begin(); iter != peaks.
detid.end(); ++iter) {
819 for (std::size_t i = 0; i < numPeaks; ++i) {
820 if (std::isnan(tof_vec_full[i]))
824 const double dspacing = dSpacingUnit.
singleFromTOF(tof_vec_full[i]);
828 chisq += (temp * temp);
831 WIDTH_TO_FWHM * (width_vec_full[i] / (2 *
difa * dspacing +
difc));
832 m_peakHeightTable->cell<
double>(rowIndexOutputPeaks, i + 1) = height_vec_full[i];
836 chisq /
static_cast<double>(numPeaks - 1);
854 maskWS->combineToDetectorMasks();
867 auto diagnosticGroup = std::make_shared<API::WorkspaceGroup>();
869 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fitparam", fittedTable);
870 diagnosticGroup->addWorkspace(fittedTable);
871 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fitted", calculatedWS);
872 diagnosticGroup->addWorkspace(calculatedWS);
874 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fiterror", errorTable);
875 diagnosticGroup->addWorkspace(errorTable);
879 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_dspacing",
m_peakPositionTable);
881 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_width",
m_peakWidthTable);
883 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_height",
m_peakHeightTable);
885 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_resolution", resolutionWksp);
886 diagnosticGroup->addWorkspace(resolutionWksp);
887 setProperty(
"DiagnosticWorkspaces", diagnosticGroup);
902 const std::vector<double> *peakVec =
reinterpret_cast<std::vector<double> *
>(peaks);
904 const auto numPeaks =
static_cast<size_t>(peakVec->at(0));
906 const auto numParams =
static_cast<size_t>(peakVec->at(1));
909 const std::vector<double> tofObs(peakVec->begin() + 2, peakVec->begin() + 2 + numPeaks);
910 const std::vector<double> dspace(peakVec->begin() + (2 + numPeaks), peakVec->begin() + (2 + 2 * numPeaks));
911 const std::vector<double> weights(peakVec->begin() + (2 + 2 * numPeaks), peakVec->begin() + (2 + 3 * numPeaks));
914 double difc = gsl_vector_get(v, 0);
918 tzero = gsl_vector_get(v, 1);
920 difa = gsl_vector_get(v, 2);
930 for (
size_t i = 0; i < numPeaks; ++i) {
931 const double tofCalib = dSpacingUnit.
singleToTOF(dspace[i]);
932 const double errsum_i = std::pow(tofObs[i] - tofCalib, 2) * weights[i];
957double fitDIFCtZeroDIFA(std::vector<double> &peaks,
double &
difc,
double &t0,
double &
difa) {
958 const auto numParams =
static_cast<size_t>(peaks[1]);
961 gsl_vector *fitParams = gsl_vector_alloc(numParams);
962 gsl_vector_set_all(fitParams, 0.0);
963 gsl_vector_set(fitParams, 0,
difc);
965 gsl_vector_set(fitParams, 1, t0);
967 gsl_vector_set(fitParams, 2,
difa);
972 gsl_vector *stepSizes = gsl_vector_alloc(numParams);
973 gsl_vector_set_all(stepSizes, 0.1);
974 gsl_vector_set(stepSizes, 0, 0.01);
977 gsl_multimin_function minex_func;
978 minex_func.n = numParams;
980 minex_func.params = &peaks;
983 const gsl_multimin_fminimizer_type *minimizerType = gsl_multimin_fminimizer_nmsimplex2;
984 gsl_multimin_fminimizer *minimizer = gsl_multimin_fminimizer_alloc(minimizerType, numParams);
985 gsl_multimin_fminimizer_set(minimizer, &minex_func, fitParams, stepSizes);
989 const size_t MAX_ITER = 75 * numParams;
993 status = gsl_multimin_fminimizer_iterate(minimizer);
997 double size = gsl_multimin_fminimizer_size(minimizer);
998 status = gsl_multimin_test_size(size, 1e-4);
1000 }
while (status == GSL_CONTINUE && iter < MAX_ITER);
1004 std::string status_msg = gsl_strerror(status);
1005 if (status_msg ==
"success") {
1006 difc = gsl_vector_get(minimizer->x, 0);
1007 if (numParams > 1) {
1008 t0 = gsl_vector_get(minimizer->x, 1);
1009 if (numParams > 2) {
1010 difa = gsl_vector_get(minimizer->x, 2);
1014 errsum = minimizer->fval;
1018 gsl_vector_free(fitParams);
1019 gsl_vector_free(stepSizes);
1020 gsl_multimin_fminimizer_free(minimizer);
1041 const std::vector<double> &weights,
double &
difc,
double &t0,
double &
difa) {
1042 const size_t numPeaks =
d.size();
1043 if (numPeaks <= 1) {
1055 std::vector<double> peaks(numPeaks * 3 + 2, 0.);
1056 peaks[0] =
static_cast<double>(
d.size());
1058 for (
size_t i = 0; i < numPeaks; ++i) {
1059 peaks[i + 2] = tof[i];
1060 peaks[i + 2 + numPeaks] =
d[i];
1061 peaks[i + 2 + 2 * numPeaks] = weights[i];
1065 double difc_start =
difc;
1066 if (difc_start == 0.) {
1067 const double d_sum = std::accumulate(
d.begin(),
d.end(), 0.);
1068 const double tof_sum = std::accumulate(tof.begin(), tof.end(), 0.);
1069 difc_start = tof_sum / d_sum;
1073 double best_errsum = std::numeric_limits<double>::max();
1074 double best_difc = difc_start;
1075 double best_t0 = 0.;
1076 double best_difa = 0.;
1083 for (
size_t numParams = 1; numParams <= maxParams; ++numParams) {
1084 peaks[1] =
static_cast<double>(numParams);
1085 double difc_local = best_difc;
1086 double t0_local = best_t0;
1087 double difa_local = best_difa;
1088 double errsum = fitDIFCtZeroDIFA(peaks, difc_local, t0_local, difa_local);
1091 errsum = errsum /
static_cast<double>(numPeaks - numParams);
1095 if (errsum < best_errsum) {
1100 best_errsum = errsum;
1101 best_difc = difc_local;
1103 best_difa = difa_local;
1110 if (best_difc > 0. && best_errsum < std::numeric_limits<double>::max()) {
1126 const std::vector<double> &windows_in) {
1128 if (!(windows_in.size() == 1 || windows_in.size() / 2 == centres.size()))
1129 throw std::logic_error(
"the peak-window vector must contain either a single peak-width value, or a pair of values "
1130 "for each peak center specified");
1132 const std::size_t numPeaks = centres.size();
1135 if (!(numPeaks >= 2))
1136 throw std::logic_error(
"at least two peak centres must be specified: the distance between these centres will be "
1137 "used to estimate the peak widths");
1139 vector<double> windows_out(2 * numPeaks);
1142 for (std::size_t i = 0; i < numPeaks; ++i) {
1143 if (windows_in.size() == 1) {
1144 left = centres[i] - windows_in[0];
1145 right = centres[i] + windows_in[0];
1147 left = windows_in[2 * i];
1148 right = windows_in[2 * i + 1];
1152 left = std::max(
left, centres[i] - 0.5 * (centres[i] - centres[i - 1]));
1154 if (i < numPeaks - 1) {
1155 right = std::min(
right, centres[i] + 0.5 * (centres[i + 1] - centres[i]));
1158 windows_out[2 * i] =
left;
1159 windows_out[2 * i + 1] =
right;
1176 for (
auto detId : detIds) {
1182 if (detIds.size() > 1) {
1183 double norm = 1. /
static_cast<double>(detIds.size());
1193 const double tzero) {
1200 auto rowNum = rowIter->second;
1207 size_t hasDasIdsOffset = 0;
1225 vector<double> tofminmax(2);
1235 const double maxChunkSize =
getProperty(
"MaxChunkSize");
1236 const double filterBadPulses =
getProperty(
"FilterBadPulses");
1239 alg->setLoggingOffset(1);
1240 alg->setProperty(
"Filename", filename);
1241 alg->setProperty(
"MaxChunkSize", maxChunkSize);
1242 alg->setProperty(
"FilterByTofMin",
m_tofMin);
1243 alg->setProperty(
"FilterByTofMax",
m_tofMax);
1244 alg->setProperty(
"FilterBadPulses", filterBadPulses);
1245 alg->setProperty(
"LoadMonitors",
false);
1246 alg->executeAsChildAlg();
1249 return std::dynamic_pointer_cast<MatrixWorkspace>(
workspace);
1261 rebin->setLoggingOffset(1);
1262 rebin->setProperty(
"InputWorkspace", wksp);
1263 rebin->setProperty(
"OutputWorkspace", wksp);
1265 rebin->setProperty(
"PreserveEvents",
true);
1266 rebin->executeAsChildAlg();
1267 wksp =
rebin->getProperty(
"OutputWorkspace");
1273 std::set<detid_t> detids;
1283 for (std::size_t i = startIndex; i <= stopIndex; ++i) {
1284 const auto detidsForSpectrum =
m_uncalibratedWS->getSpectrum(i).getDetectorIDs();
1285 for (
const auto &detid : detidsForSpectrum) {
1286 detids.emplace(detid);
1321 if (calibrationTableOld ==
nullptr) {
1323 std::string filename =
getProperty(
"PreviousCalibrationFile");
1325 alg->setLoggingOffset(1);
1326 alg->setProperty(
"Filename", filename);
1327 alg->setProperty(
"WorkspaceName",
"NOMold");
1328 alg->setProperty(
"MakeGroupingWorkspace",
false);
1329 alg->setProperty(
"MakeMaskWorkspace",
false);
1330 alg->setProperty(
"TofMin",
m_tofMin);
1331 alg->setProperty(
"TofMax",
m_tofMax);
1332 alg->executeAsChildAlg();
1333 calibrationTableOld = alg->getProperty(
"OutputCalWorkspace");
1342 const bool useAllDetids = includedDetids.empty();
1346 std::size_t rowNum = 0;
1347 for (std::size_t i = 0; i < detIDs.
size(); ++i) {
1349 if ((useAllDetids) || (includedDetids.count(detIDs[i]) > 0)) {
1352 int detid = calibrationTableOld->getRef<
int>(
"detid", i);
1353 double difc = calibrationTableOld->getRef<
double>(
"difc", i);
1354 double difa = calibrationTableOld->getRef<
double>(
"difa", i);
1355 double tzero = calibrationTableOld->getRef<
double>(
"tzero", i);
1359 newRow << calibrationTableOld->getRef<
int>(
"dasid", i);
1363 newRow << tofMinMax[0] << tofMinMax[1];
1378 alg->setLoggingOffset(1);
1380 alg->executeAsChildAlg();
1386 const detid2index_map allDetectors = difcWS->getDetectorIDToWorkspaceIndexMap(
false);
1389 const bool useAllDetids = includedDetids.empty();
1393 for (
auto it = allDetectors.begin(); it != allDetectors.end(); ++it) {
1394 const detid_t detID = it->first;
1396 if (useAllDetids || (includedDetids.count(detID) > 0)) {
1398 const size_t wi = it->second;
1401 newRow << difcWS->y(wi)[0];
1426 std::stringstream namess;
1427 namess <<
"@" << std::setprecision(5) << dSpacing;
1439 detIds[it.second] = it.first;
1443 for (
const auto &detId : detIds) {
1449 newWidthRow << detId;
1450 newHeightRow << detId;
1454 newPosRow << std::nan(
"");
1455 newWidthRow << std::nan(
"");
1456 newHeightRow << std::nan(
"");
1463 std::make_shared<DataObjects::SpecialWorkspace2D>(
m_uncalibratedWS->getInstrument());
1464 resolutionWksp->setTitle(
"average width/height");
1472 std::vector<double> resolution;
1473 for (
size_t rowIndex = 0; rowIndex < numRows; ++rowIndex) {
1477 for (
size_t peakIndex = 1; peakIndex < numPeaks + 1; ++peakIndex) {
1479 if (std::isnormal(pos)) {
1480 resolution.emplace_back(
m_peakWidthTable->Double(rowIndex, peakIndex) / pos);
1483 if (resolution.empty()) {
1484 resolutionWksp->setValue(detId, 0., 0.);
1488 std::accumulate(resolution.begin(), resolution.end(), 0.) /
static_cast<double>(resolution.size());
1490 std::for_each(resolution.cbegin(), resolution.cend(),
1491 [&stddev, mean](
const auto value) { stddev += (value - mean) * (value * mean); });
1492 stddev = std::sqrt(stddev /
static_cast<double>(resolution.size() - 1));
1493 resolutionWksp->setValue(detId, mean, stddev);
1497 return resolutionWksp;
1503 alg->setLoggingOffset(1);
1504 alg->setProperty(
"InputWorkspace", table);
1505 alg->setProperty(
"OutputWorkspace", table);
1506 alg->setProperty(
"Columns",
"detid");
1507 alg->executeAsChildAlg();
1508 table = alg->getProperty(
"OutputWorkspace");
1527std::pair<API::MatrixWorkspace_sptr, API::MatrixWorkspace_sptr>
1529 const std::vector<double> &peakWindow) {
1538 << windowsInDSpacing[2 * i + 1] <<
"\n";
1542 size_t numspec = dataws->getNumberHistograms();
1551 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
1554 std::size_t iws =
static_cast<std::size_t
>(iiws);
1562 peak_pos_ws->setPoints(iws, peaks.
inTofPos);
1566 for (std::size_t i = 0; i < peaks.
inTofPos.size(); i++) {
1575 return std::make_pair(peak_pos_ws, peak_window_ws);
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
IPeaksWorkspace_sptr workspace
#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_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PRAGMA_OMP(expression)
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
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.
bool isDefault(const std::string &name) const
ColumnVector gives access to the column elements without alowing its resizing.
size_t size()
Size of the vector.
A specialized class for dealing with file properties.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Base MatrixWorkspace Abstract Class.
Helper class for reporting progress from algorithms.
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
std::set< detid_t > detid
void setPositions(const std::vector< double > &peaksInD, const std::vector< double > &peaksInDWindows, const double difa, const double difc, const double tzero)
Store the positions of the peak centers, as well as the left and right fit ranges for each peak,...
std::vector< double > inTofPos
std::vector< double > inTofWindows
std::vector< double > inDPos
FittedPeaks(const API::MatrixWorkspace_const_sptr &wksp, const std::size_t wkspIndex)
Find the bins with non-zero counts and with minimum and maximum TOF.
std::set< detid_t > detIdsForTable()
API::ITableWorkspace_sptr m_peakWidthTable
std::map< detid_t, size_t > m_detidToRow
API::ITableWorkspace_sptr m_peakHeightTable
void setCalibrationValues(const detid_t detid, const double difc, const double difa, const double tzero)
API::MatrixWorkspace_sptr m_uncalibratedWS
int m_startWorkspaceIndex
start index
API::MatrixWorkspace_sptr rebin(API::MatrixWorkspace_sptr wksp)
int m_stopWorkspaceIndex
stop index (workspace index of the last spectrum included)
API::ITableWorkspace_sptr sortTableWorkspace(API::ITableWorkspace_sptr &table)
sort the calibration table according increasing values in column "detid"
std::vector< double > dSpacingWindows(const std::vector< double > ¢res, const std::vector< double > &widthMax)
Fitting ranges to the left and right of peak center (the window cannot exceed half the distance to th...
const std::string category() const override
Algorithm's category for identification.
std::pair< API::MatrixWorkspace_sptr, API::MatrixWorkspace_sptr > createTOFPeakCenterFitWindowWorkspaces(const API::MatrixWorkspace_sptr &dataws, const std::vector< double > &peakWindowMaxInDSpacing)
NEW: convert peak positions in dSpacing to peak centers workspace.
API::ITableWorkspace_sptr m_calibrationTable
PDCalibration()
Constructor.
void init() override
Initialize the algorithm's properties.
API::MatrixWorkspace_sptr loadAndBin()
load input workspace and rebin with parameters "TofBinning" provided by User
std::vector< double > getTOFminmax(const double difc, const double difa, const double tzero)
Adjustment of TofMin and TofMax values, to ensure positive values of d-spacing when converting from T...
void createInformationWorkspaces()
Table workspaces where the first column is the detector ID and subsequent columns are termed "@x....
~PDCalibration() override
Destructor.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
const std::string name() const override
Algorithms name for identification.
void fitDIFCtZeroDIFA_LM(const std::vector< double > &d, const std::vector< double > &tof, const std::vector< double > &height2, double &difc, double &t0, double &difa)
Fit the nominal peak center positions, in d-spacing against the fitted peak center positions,...
double m_tofMin
first bin boundary when rebinning in TOF (user input)
std::tuple< double, double, double > getDSpacingToTof(const std::set< detid_t > &detIds)
Return a function that converts from d-spacing to TOF units for a particular pixel,...
API::MatrixWorkspace_sptr load(const std::string &filename)
void createCalTableNew()
Initialize the calibration table workspace.
void createCalTableHeader()
API::MatrixWorkspace_sptr calculateResolutionTable()
int version() const override
Algorithm's version for identification.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
API::ITableWorkspace_sptr m_peakPositionTable
std::vector< double > m_peaksInDspacing
void exec() override
Execute the algorithm.
void createCalTableFromExisting()
Read a calibration table workspace provided by user, or load from a file provided by User.
double m_tofMax
last bin boundary when rebinning in TOF (user input)
This class is intended to fulfill the design specified in <https://github.com/mantidproject/documents...
Kernel/ArrayBoundedValidator.h.
Support for a property that holds an array of values.
BoundedValidator is a validator that requires the values to be between upper or lower bounds,...
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...
The Logger class is in charge of the publishing messages from the framework through various channels.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
std::ostream & getLogStream(const Priority &priority)
gets the correct log stream for a priority
void information(const std::string &msg)
Logs at information level.
Validator to check that a property is not left empty.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Validator to check the format of a vector providing the rebin parameters to an algorithm.
void initialize(const double &_l1, const int &_emode, const UnitParametersMap ¶ms)
Initialize the unit to perform conversion using singleToTof() and singleFromTof()
void toTOF(std::vector< double > &xdata, std::vector< double > const &ydata, const double &_l1, const int &_emode, std::initializer_list< std::pair< const UnitParams, double > > params)
Convert from the concrete unit to time-of-flight.
double calcTofMax(const double difc, const double difa, const double tzero, const double tofmax=0.)
double calcTofMin(const double difc, const double difa, const double tzero, const double tofmin=0.)
double singleFromTOF(const double tof) const override
DIFA * d^2 + DIFC * d + T0 - TOF = 0.
double singleToTOF(const double x) const override
Convert a single X value to TOF.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Mantid::Kernel::SingletonHolder< AnalysisDataServiceImpl > AnalysisDataService
std::shared_ptr< const ITableWorkspace > ITableWorkspace_const_sptr
shared pointer to Mantid::API::ITableWorkspace (const version)
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
static double gsl_costFunction(const gsl_vector *v, void *params)
The gsl_costFunction is optimized by GSL simplex.
std::shared_ptr< const MaskWorkspace > MaskWorkspace_const_sptr
shared pointer to a const MaskWorkspace
std::shared_ptr< MaskWorkspace > MaskWorkspace_sptr
shared pointer to the MaskWorkspace class
std::shared_ptr< SpecialWorkspace2D > SpecialWorkspace2D_sptr
shared pointer to the SpecialWorkspace2D class
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
std::unordered_map< UnitParams, double > UnitParametersMap
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t detid_t
Typedef for a detector ID.
std::unordered_map< detid_t, size_t > detid2index_map
Map with key = detector ID, value = workspace index.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Enumeration for a mandatory/optional property.
Describes the direction (within an algorithm) of a Property.
@ Input
An input workspace.
@ Output
An output workspace.