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 "Used for validating peaks before fitting. If the signal-to-noise ratio is under this value, "
278 "the peak will be excluded from fitting and calibration. This check does not apply to peaks for "
279 "which the noise cannot be estimated. "
280 "The minimum recommended value is 12.");
282 std::vector<std::string> modes{
"DIFC",
"DIFC+TZERO",
"DIFC+TZERO+DIFA"};
283 declareProperty(
"CalibrationParameters",
"DIFC", std::make_shared<StringListValidator>(modes),
284 "Select which diffractometer constants (GSAS convention) to determine.");
287 "Range for allowable calibrated TZERO value. Default: no restriction.");
290 "Range for allowable calibrated DIFA value. Default: no restriction.");
293 "Defines the weighting scheme used in the least-squares fit of the extracted peak centers "
294 "that determines the diffractometer constants. If true, the peak weight will be "
295 "the inverse square of the error on the fitted peak center. If false, the peak weight will be "
296 "the square of the fitted peak height.");
300 "Output table workspace containing the calibration.");
306 "Mask workspace (optional input / output workspace):"
307 " when specified, if the workspace already exists, any incoming masked detectors will be combined"
308 " with any additional outgoing masked detectors detected by the algorithm");
312 "Auxiliary workspaces containing extended information on the calibration results.");
315 "Used for validating peaks before fitting. If the total peak window Y-value count "
316 "is under this value, the peak will be excluded from fitting and calibration.");
319 "Used for validating peaks after fitting. If the signal-to-sigma ratio is under this value, "
320 "the peak will be excluded from fitting and calibration.");
323 std::string inputGroup(
"Input Options");
331 std::string funcgroup(
"Function Types");
336 std::string fitPeaksGroup(
"Peak Fitting");
349 std::string calGroup(
"Calibration Type");
357 std::map<std::string, std::string> messages;
363 if (maskWS->getInstrument()->getNumberDetectors(
true) != inputWS->getInstrument()->getNumberDetectors(
true)) {
364 messages[
"MaskWorkspace"] =
"incoming mask workspace must have the same instrument as the input workspace";
365 }
else if (maskWS->getNumberHistograms() != inputWS->getInstrument()->getNumberDetectors(
true)) {
366 messages[
"MaskWorkspace"] =
"incoming mask workspace must have one spectrum per detector";
370 vector<double> tzeroRange =
getProperty(
"TZEROrange");
371 if (!tzeroRange.empty()) {
372 if (tzeroRange.size() != 2) {
373 messages[
"TZEROrange"] =
"Require two values [min,max]";
374 }
else if (tzeroRange[0] >= tzeroRange[1]) {
375 messages[
"TZEROrange"] =
"min must be less than max";
379 vector<double> difaRange =
getProperty(
"DIFArange");
380 if (!difaRange.empty()) {
381 if (difaRange.size() != 2) {
382 messages[
"DIFArange"] =
"Require two values [min,max]";
383 }
else if (difaRange[0] >= difaRange[1]) {
384 messages[
"DIFArange"] =
"min must be less than max";
388 vector<double> peakWindow =
getProperty(
"PeakWindow");
389 vector<double> peakCentres =
getProperty(
"PeakPositions");
390 if (peakWindow.size() > 1 && peakWindow.size() != 2 * peakCentres.size()) {
391 messages[
"PeakWindow"] =
"PeakWindow must be a vector with either 1 element (interpreted as half the width of "
392 "the window) or twice the number of peak centres provided.";
402 const auto columnNames = table->getColumnNames();
403 return (std::find(columnNames.begin(), columnNames.end(), std::string(
"dasid")) != columnNames.end());
413double getWidthToFWHM(
const std::string &peakshape) {
415 if (peakshape ==
"Gaussian") {
416 return 2 * std::sqrt(2. * std::log(2.));
417 }
else if (peakshape ==
"Lorentzian") {
419 }
else if (peakshape ==
"BackToBackExponential") {
432 vector<double> tofBinningParams =
getProperty(
"TofBinning");
433 m_tofMin = tofBinningParams.front();
436 vector<double> tzeroRange =
getProperty(
"TZEROrange");
437 if (tzeroRange.size() == 2) {
441 std::stringstream msg;
442 msg <<
"Using tzero range of " <<
m_tzeroMin <<
" <= "
448 m_tzeroMin = std::numeric_limits<double>::lowest();
449 m_tzeroMax = std::numeric_limits<double>::max();
452 vector<double> difaRange =
getProperty(
"DIFArange");
453 if (difaRange.size() == 2) {
457 std::stringstream msg;
458 msg <<
"Using difa range of " <<
m_difaMin <<
" <= "
464 m_difaMin = std::numeric_limits<double>::lowest();
465 m_difaMax = std::numeric_limits<double>::max();
469 std::vector<double> peakWindow =
getProperty(
"PeakWindow");
472 if (peakWindow.size() > 1) {
475 std::map<double, std::pair<double, double>> peakEdgeAndCenter;
477 peakEdgeAndCenter[
m_peaksInDspacing[i]] = {peakWindow[2 * i], peakWindow[2 * i + 1]};
482 peakWindow.reserve(2 * NUMPEAKS);
484 for (
auto it = peakEdgeAndCenter.begin(); it != peakEdgeAndCenter.end(); it++) {
486 peakWindow.push_back(it->second.first);
487 peakWindow.push_back(it->second.second);
494 const double minPeakHeight =
getProperty(
"MinimumPeakHeight");
495 const double minPeakTotalCount =
getProperty(
"MinimumPeakTotalCount");
496 const double minSignalToNoiseRatio =
getProperty(
"MinimumSignalToNoiseRatio");
497 const double minSignalToSigmaRatio =
getProperty(
"MinimumSignalToSigmaRatio");
498 const double maxChiSquared =
getProperty(
"MaxChiSq");
501 if (calParams == std::string(
"DIFC"))
503 else if (calParams == std::string(
"DIFC+TZERO"))
505 else if (calParams == std::string(
"DIFC+TZERO+DIFA"))
508 throw std::runtime_error(
"Encountered impossible CalibrationParameters value");
517 auto uncalibratedEWS = std::dynamic_pointer_cast<EventWorkspace>(
m_uncalibratedWS);
518 auto isEvent = bool(uncalibratedEWS);
521 if ((!
static_cast<std::string
>(
getProperty(
"PreviousCalibrationFile")).empty()) ||
535 g_log.
debug() <<
"[PDCalibration]: CREATING new MaskWorkspace.\n";
537 maskWS = std::make_shared<MaskWorkspace>(
m_uncalibratedWS->getInstrument());
539 g_log.
debug() <<
"[PDCalibration]: Using EXISTING MaskWorkspace.\n";
544 const std::string peakFunction =
getProperty(
"PeakFunction");
545 const double WIDTH_TO_FWHM = getWidthToFWHM(peakFunction);
546 if (WIDTH_TO_FWHM == 1.) {
547 g_log.
notice() <<
"Unknown conversion for \"" << peakFunction
548 <<
"\", found peak widths and resolution should not be "
549 "directly compared to delta-d/d";
560 double peak_width_percent =
getProperty(
"PeakWidthPercent");
562 const std::string diagnostic_prefix =
getPropertyValue(
"DiagnosticWorkspaces");
567 algFitPeaks->setLoggingOffset(3);
576 algFitPeaks->setProperty(
"PeakCentersWorkspace", tof_peak_center_ws);
579 algFitPeaks->setProperty<std::string>(
"PeakFunction", peakFunction);
580 algFitPeaks->setProperty<std::string>(
"BackgroundType",
getProperty(
"BackgroundType"));
582 algFitPeaks->setProperty(
"FitPeakWindowWorkspace", tof_peak_window_ws);
583 algFitPeaks->setProperty(
"PeakWidthPercent", peak_width_percent);
584 algFitPeaks->setProperty(
"MinimumPeakHeight", minPeakHeight);
585 algFitPeaks->setProperty(
"MinimumPeakTotalCount", minPeakTotalCount);
586 algFitPeaks->setProperty(
"MinimumSignalToNoiseRatio", minSignalToNoiseRatio);
587 algFitPeaks->setProperty(
"MinimumSignalToSigmaRatio", minSignalToSigmaRatio);
589 algFitPeaks->setProperty(
"FitFromRight",
true);
590 const bool highBackground =
getProperty(
"HighBackground");
591 algFitPeaks->setProperty(
"HighBackground", highBackground);
592 bool constrainPeakPosition =
getProperty(
"ConstrainPeakPositions");
593 algFitPeaks->setProperty(
"ConstrainPeakPositions",
594 constrainPeakPosition);
596 algFitPeaks->setProperty(
"Minimizer",
"Levenberg-Marquardt");
597 algFitPeaks->setProperty(
"CostFunction",
"Least squares");
602 algFitPeaks->setProperty(
"RawPeakParameters", useChiSq);
613 algFitPeaks->setPropertyValue(
"OutputPeakParametersWorkspace", diagnostic_prefix +
"_fitparam");
618 algFitPeaks->setPropertyValue(
"FittedPeaksWorkspace", diagnostic_prefix +
"_fitted");
620 algFitPeaks->setPropertyValue(
"OutputParameterFitErrorsWorkspace", diagnostic_prefix +
"_fiterrors");
624 algFitPeaks->executeAsChildAlg();
632 errorTable = algFitPeaks->getProperty(
"OutputParameterFitErrorsWorkspace");
637 throw std::runtime_error(
"FitPeaks does not have output OutputPeakParametersWorkspace.");
639 throw std::runtime_error(
"The number of rows in OutputPeakParametersWorkspace is not correct!");
660 if ((isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) || !spectrumInfo.hasDetectors(wkspIndex) ||
661 spectrumInfo.isMonitor(wkspIndex) ||
662 maskWS->isMasked(
m_uncalibratedWS->getSpectrum(wkspIndex).getDetectorIDs())) {
665 if (spectrumInfo.hasDetectors(wkspIndex) && !spectrumInfo.isMonitor(wkspIndex)) {
666 if (isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) {
667 maskWS->setMasked(
m_uncalibratedWS->getSpectrum(wkspIndex).getDetectorIDs(),
true);
668 g_log.
debug() <<
"FULLY masked spectrum, index: " << wkspIndex <<
"\n";
677 const auto [dif_c, dif_a,
tzero] =
686 std::vector<double> tof_vec_full(numPeaks, std::nan(
""));
687 std::vector<double> d_vec;
688 std::vector<double> tof_vec;
690 std::vector<double> width_vec_full(numPeaks, std::nan(
""));
692 std::vector<double> height_vec_full(numPeaks, std::nan(
""));
693 std::vector<double> weights;
698 for (
size_t peakIndex = 0; peakIndex < numPeaks; ++peakIndex) {
699 size_t rowIndexInFitTable = rowNumInFitTableOffset + peakIndex;
702 if (fittedTable->getRef<
int>(
"wsindex", rowIndexInFitTable) != wkspIndex)
703 throw std::runtime_error(
"workspace index mismatch!");
704 if (fittedTable->getRef<
int>(
"peakindex", rowIndexInFitTable) !=
static_cast<int>(peakIndex))
705 throw std::runtime_error(
"peak index mismatch but workspace index matched");
707 const double chi2 = fittedTable->getRef<
double>(
"chi2", rowIndexInFitTable);
709 double centre_error = 0.0;
714 centre = fittedTable->getRef<
double>(
"centre", rowIndexInFitTable);
715 width = fittedTable->getRef<
double>(
"width", rowIndexInFitTable);
716 height = fittedTable->getRef<
double>(
"height", rowIndexInFitTable);
720 auto peakfunc = std::dynamic_pointer_cast<API::IPeakFunction>(
721 API::FunctionFactory::Instance().createFunction(peakFunction));
723 for (
size_t ipar = 0; ipar < peakfunc->nParams(); ipar++) {
724 peakfunc->setParameter(ipar, fittedTable->getRef<
double>(peakfunc->parameterName(ipar), rowIndexInFitTable));
726 centre = peakfunc->centre();
727 width = peakfunc->fwhm();
728 height = peakfunc->height();
729 centre_error = errorTable->getRef<
double>(peakfunc->getCentreParameterName(), rowIndexInFitTable);
733 if (chi2 > maxChiSquared || chi2 < 0.) {
734 g_log.
debug(
"failure to fit: chi2 > maximum");
741 g_log.
debug(
"failure to fit: peak center is out-of-range");
746 if (
height < minPeakHeight + 1.E-15) {
747 g_log.
debug(
"failure to fit: peak height is less than minimum");
752 g_log.
getLogStream(Logger::Priority::PRIO_TRACE) <<
"successful fit: peak centered at " << centre <<
"\n";
755 tof_vec.emplace_back(centre);
759 weights.emplace_back(1 / (centre_error * centre_error));
761 tof_vec_full[peakIndex] = centre;
762 width_vec_full[peakIndex] = width;
763 height_vec_full[peakIndex] =
height;
766 if (d_vec.size() < 2) {
769 maskWS->setMasked(peaks.
detid,
true);
772 for (
const auto &det : peaks.
detid) {
782 double difc = 0., t0 = 0.,
difa = 0.;
784 for (
auto iter = peaks.
detid.begin(); iter != peaks.
detid.end(); ++iter) {
796 for (std::size_t i = 0; i < numPeaks; ++i) {
797 if (std::isnan(tof_vec_full[i]))
801 const double dspacing = dSpacingUnit.
singleFromTOF(tof_vec_full[i]);
805 chisq += (temp * temp);
808 WIDTH_TO_FWHM * (width_vec_full[i] / (2 *
difa * dspacing +
difc));
809 m_peakHeightTable->cell<
double>(rowIndexOutputPeaks, i + 1) = height_vec_full[i];
813 chisq /
static_cast<double>(numPeaks - 1);
831 maskWS->combineToDetectorMasks();
844 auto diagnosticGroup = std::make_shared<API::WorkspaceGroup>();
846 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fitparam", fittedTable);
847 diagnosticGroup->addWorkspace(fittedTable);
848 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fitted", calculatedWS);
849 diagnosticGroup->addWorkspace(calculatedWS);
851 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_fiterror", errorTable);
852 diagnosticGroup->addWorkspace(errorTable);
856 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_dspacing",
m_peakPositionTable);
858 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_width",
m_peakWidthTable);
860 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_height",
m_peakHeightTable);
862 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix +
"_resolution", resolutionWksp);
863 diagnosticGroup->addWorkspace(resolutionWksp);
864 setProperty(
"DiagnosticWorkspaces", diagnosticGroup);
879 const std::vector<double> *peakVec =
reinterpret_cast<std::vector<double> *
>(peaks);
881 const auto numPeaks =
static_cast<size_t>(peakVec->at(0));
883 const auto numParams =
static_cast<size_t>(peakVec->at(1));
886 const std::vector<double> tofObs(peakVec->begin() + 2, peakVec->begin() + 2 + numPeaks);
887 const std::vector<double> dspace(peakVec->begin() + (2 + numPeaks), peakVec->begin() + (2 + 2 * numPeaks));
888 const std::vector<double> weights(peakVec->begin() + (2 + 2 * numPeaks), peakVec->begin() + (2 + 3 * numPeaks));
891 double difc = gsl_vector_get(v, 0);
895 tzero = gsl_vector_get(v, 1);
897 difa = gsl_vector_get(v, 2);
907 for (
size_t i = 0; i < numPeaks; ++i) {
908 const double tofCalib = dSpacingUnit.
singleToTOF(dspace[i]);
909 const double errsum_i = std::pow(tofObs[i] - tofCalib, 2) * weights[i];
934double fitDIFCtZeroDIFA(std::vector<double> &peaks,
double &
difc,
double &t0,
double &
difa) {
935 const auto numParams =
static_cast<size_t>(peaks[1]);
938 gsl_vector *fitParams = gsl_vector_alloc(numParams);
939 gsl_vector_set_all(fitParams, 0.0);
940 gsl_vector_set(fitParams, 0,
difc);
942 gsl_vector_set(fitParams, 1, t0);
944 gsl_vector_set(fitParams, 2,
difa);
949 gsl_vector *stepSizes = gsl_vector_alloc(numParams);
950 gsl_vector_set_all(stepSizes, 0.1);
951 gsl_vector_set(stepSizes, 0, 0.01);
954 gsl_multimin_function minex_func;
955 minex_func.n = numParams;
957 minex_func.params = &peaks;
960 const gsl_multimin_fminimizer_type *minimizerType = gsl_multimin_fminimizer_nmsimplex2;
961 gsl_multimin_fminimizer *minimizer = gsl_multimin_fminimizer_alloc(minimizerType, numParams);
962 gsl_multimin_fminimizer_set(minimizer, &minex_func, fitParams, stepSizes);
966 const size_t MAX_ITER = 75 * numParams;
970 status = gsl_multimin_fminimizer_iterate(minimizer);
974 double size = gsl_multimin_fminimizer_size(minimizer);
975 status = gsl_multimin_test_size(size, 1e-4);
977 }
while (status == GSL_CONTINUE && iter < MAX_ITER);
981 std::string status_msg = gsl_strerror(status);
982 if (status_msg ==
"success") {
983 difc = gsl_vector_get(minimizer->x, 0);
985 t0 = gsl_vector_get(minimizer->x, 1);
987 difa = gsl_vector_get(minimizer->x, 2);
991 errsum = minimizer->fval;
995 gsl_vector_free(fitParams);
996 gsl_vector_free(stepSizes);
997 gsl_multimin_fminimizer_free(minimizer);
1018 const std::vector<double> &weights,
double &
difc,
double &t0,
double &
difa) {
1019 const size_t numPeaks =
d.size();
1020 if (numPeaks <= 1) {
1032 std::vector<double> peaks(numPeaks * 3 + 2, 0.);
1033 peaks[0] =
static_cast<double>(
d.size());
1035 for (
size_t i = 0; i < numPeaks; ++i) {
1036 peaks[i + 2] = tof[i];
1037 peaks[i + 2 + numPeaks] =
d[i];
1038 peaks[i + 2 + 2 * numPeaks] = weights[i];
1042 double difc_start =
difc;
1043 if (difc_start == 0.) {
1044 const double d_sum = std::accumulate(
d.begin(),
d.end(), 0.);
1045 const double tof_sum = std::accumulate(tof.begin(), tof.end(), 0.);
1046 difc_start = tof_sum / d_sum;
1050 double best_errsum = std::numeric_limits<double>::max();
1051 double best_difc = difc_start;
1052 double best_t0 = 0.;
1053 double best_difa = 0.;
1060 for (
size_t numParams = 1; numParams <= maxParams; ++numParams) {
1061 peaks[1] =
static_cast<double>(numParams);
1062 double difc_local = best_difc;
1063 double t0_local = best_t0;
1064 double difa_local = best_difa;
1065 double errsum = fitDIFCtZeroDIFA(peaks, difc_local, t0_local, difa_local);
1068 errsum = errsum /
static_cast<double>(numPeaks - numParams);
1072 if (errsum < best_errsum) {
1077 best_errsum = errsum;
1078 best_difc = difc_local;
1080 best_difa = difa_local;
1087 if (best_difc > 0. && best_errsum < std::numeric_limits<double>::max()) {
1103 const std::vector<double> &windows_in) {
1105 if (!(windows_in.size() == 1 || windows_in.size() / 2 == centres.size()))
1106 throw std::logic_error(
"the peak-window vector must contain either a single peak-width value, or a pair of values "
1107 "for each peak center specified");
1109 const std::size_t numPeaks = centres.size();
1112 if (!(numPeaks >= 2))
1113 throw std::logic_error(
"at least two peak centres must be specified: the distance between these centres will be "
1114 "used to estimate the peak widths");
1116 vector<double> windows_out(2 * numPeaks);
1119 for (std::size_t i = 0; i < numPeaks; ++i) {
1120 if (windows_in.size() == 1) {
1121 left = centres[i] - windows_in[0];
1122 right = centres[i] + windows_in[0];
1124 left = windows_in[2 * i];
1125 right = windows_in[2 * i + 1];
1129 left = std::max(
left, centres[i] - 0.5 * (centres[i] - centres[i - 1]));
1131 if (i < numPeaks - 1) {
1132 right = std::min(
right, centres[i] + 0.5 * (centres[i + 1] - centres[i]));
1135 windows_out[2 * i] =
left;
1136 windows_out[2 * i + 1] =
right;
1153 for (
auto detId : detIds) {
1159 if (detIds.size() > 1) {
1160 double norm = 1. /
static_cast<double>(detIds.size());
1170 const double tzero) {
1177 auto rowNum = rowIter->second;
1184 size_t hasDasIdsOffset = 0;
1202 vector<double> tofminmax(2);
1212 const double maxChunkSize =
getProperty(
"MaxChunkSize");
1213 const double filterBadPulses =
getProperty(
"FilterBadPulses");
1216 alg->setLoggingOffset(1);
1217 alg->setProperty(
"Filename", filename);
1218 alg->setProperty(
"MaxChunkSize", maxChunkSize);
1219 alg->setProperty(
"FilterByTofMin",
m_tofMin);
1220 alg->setProperty(
"FilterByTofMax",
m_tofMax);
1221 alg->setProperty(
"FilterBadPulses", filterBadPulses);
1222 alg->setProperty(
"LoadMonitors",
false);
1223 alg->executeAsChildAlg();
1226 return std::dynamic_pointer_cast<MatrixWorkspace>(
workspace);
1238 rebin->setLoggingOffset(1);
1239 rebin->setProperty(
"InputWorkspace", wksp);
1240 rebin->setProperty(
"OutputWorkspace", wksp);
1242 rebin->setProperty(
"PreserveEvents",
true);
1243 rebin->executeAsChildAlg();
1244 wksp =
rebin->getProperty(
"OutputWorkspace");
1250 std::set<detid_t> detids;
1260 for (std::size_t i = startIndex; i <= stopIndex; ++i) {
1261 const auto detidsForSpectrum =
m_uncalibratedWS->getSpectrum(i).getDetectorIDs();
1262 for (
const auto &detid : detidsForSpectrum) {
1263 detids.emplace(detid);
1298 if (calibrationTableOld ==
nullptr) {
1300 std::string filename =
getProperty(
"PreviousCalibrationFile");
1302 alg->setLoggingOffset(1);
1303 alg->setProperty(
"Filename", filename);
1304 alg->setProperty(
"WorkspaceName",
"NOMold");
1305 alg->setProperty(
"MakeGroupingWorkspace",
false);
1306 alg->setProperty(
"MakeMaskWorkspace",
false);
1307 alg->setProperty(
"TofMin",
m_tofMin);
1308 alg->setProperty(
"TofMax",
m_tofMax);
1309 alg->executeAsChildAlg();
1310 calibrationTableOld = alg->getProperty(
"OutputCalWorkspace");
1319 const bool useAllDetids = includedDetids.empty();
1323 std::size_t rowNum = 0;
1324 for (std::size_t i = 0; i < detIDs.
size(); ++i) {
1326 if ((useAllDetids) || (includedDetids.count(detIDs[i]) > 0)) {
1329 int detid = calibrationTableOld->getRef<
int>(
"detid", i);
1330 double difc = calibrationTableOld->getRef<
double>(
"difc", i);
1331 double difa = calibrationTableOld->getRef<
double>(
"difa", i);
1332 double tzero = calibrationTableOld->getRef<
double>(
"tzero", i);
1336 newRow << calibrationTableOld->getRef<
int>(
"dasid", i);
1340 newRow << tofMinMax[0] << tofMinMax[1];
1355 alg->setLoggingOffset(1);
1357 alg->executeAsChildAlg();
1363 const detid2index_map allDetectors = difcWS->getDetectorIDToWorkspaceIndexMap(
false);
1366 const bool useAllDetids = includedDetids.empty();
1370 for (
auto it = allDetectors.begin(); it != allDetectors.end(); ++it) {
1371 const detid_t detID = it->first;
1373 if (useAllDetids || (includedDetids.count(detID) > 0)) {
1375 const size_t wi = it->second;
1378 newRow << difcWS->y(wi)[0];
1403 std::stringstream namess;
1404 namess <<
"@" << std::setprecision(5) << dSpacing;
1416 detIds[it.second] = it.first;
1420 for (
const auto &detId : detIds) {
1426 newWidthRow << detId;
1427 newHeightRow << detId;
1431 newPosRow << std::nan(
"");
1432 newWidthRow << std::nan(
"");
1433 newHeightRow << std::nan(
"");
1440 std::make_shared<DataObjects::SpecialWorkspace2D>(
m_uncalibratedWS->getInstrument());
1441 resolutionWksp->setTitle(
"average width/height");
1449 std::vector<double> resolution;
1450 for (
size_t rowIndex = 0; rowIndex < numRows; ++rowIndex) {
1454 for (
size_t peakIndex = 1; peakIndex < numPeaks + 1; ++peakIndex) {
1456 if (std::isnormal(pos)) {
1457 resolution.emplace_back(
m_peakWidthTable->Double(rowIndex, peakIndex) / pos);
1460 if (resolution.empty()) {
1461 resolutionWksp->setValue(detId, 0., 0.);
1465 std::accumulate(resolution.begin(), resolution.end(), 0.) /
static_cast<double>(resolution.size());
1467 std::for_each(resolution.cbegin(), resolution.cend(),
1468 [&stddev, mean](
const auto value) { stddev += (value - mean) * (value * mean); });
1469 stddev = std::sqrt(stddev /
static_cast<double>(resolution.size() - 1));
1470 resolutionWksp->setValue(detId, mean, stddev);
1474 return resolutionWksp;
1480 alg->setLoggingOffset(1);
1481 alg->setProperty(
"InputWorkspace", table);
1482 alg->setProperty(
"OutputWorkspace", table);
1483 alg->setProperty(
"Columns",
"detid");
1484 alg->executeAsChildAlg();
1485 table = alg->getProperty(
"OutputWorkspace");
1504std::pair<API::MatrixWorkspace_sptr, API::MatrixWorkspace_sptr>
1506 const std::vector<double> &peakWindow) {
1515 << windowsInDSpacing[2 * i + 1] <<
"\n";
1519 size_t numspec = dataws->getNumberHistograms();
1528 PRAGMA_OMP(parallel
for schedule(dynamic, 1) )
1531 std::size_t iws =
static_cast<std::size_t
>(iiws);
1539 peak_pos_ws->setPoints(iws, peaks.
inTofPos);
1543 for (std::size_t i = 0; i < peaks.
inTofPos.size(); i++) {
1552 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.