Mantid
Loading...
Searching...
No Matches
PDCalibration.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
14#include "MantidAPI/Run.h"
16#include "MantidAPI/TableRow.h"
27#include "MantidHistogramData/Histogram.h"
36#include "MantidKernel/Unit.h"
37
38#include <algorithm>
39#include <gsl/gsl_multifit_nlin.h>
40#include <gsl/gsl_multimin.h>
41#include <limits>
42#include <numeric>
43
44namespace Mantid::Algorithms {
45
46using namespace Mantid::DataObjects;
47using namespace Mantid::HistogramData;
69using std::vector;
70
71// Register the algorithm into the AlgorithmFactory
72DECLARE_ALGORITHM(PDCalibration)
73
74namespace { // anonymous
75const auto isNonZero = [](const double value) { return value != 0.; };
76} // namespace
77
80public:
90 FittedPeaks(const API::MatrixWorkspace_const_sptr &wksp, const std::size_t wkspIndex) {
91 this->wkspIndex = wkspIndex;
92
93 // convert workspace index into detector id
94 const auto &spectrum = wksp->getSpectrum(wkspIndex);
95 this->detid = spectrum.getDetectorIDs();
96
97 const auto &X = spectrum.x();
98 const auto &Y = spectrum.y();
99 tofMin = X.front();
100 tofMax = X.back();
101
102 // determine tof min supported by the workspace (non-zero counts)
103 size_t minIndex = 0; // want to store value
104 for (; minIndex < Y.size(); ++minIndex) {
105 if (isNonZero(Y[minIndex])) {
106 tofMin = X[minIndex];
107 break; // we found the first bin with non-zero counts
108 }
109 }
110
111 // determine tof max supported by the workspace (non-zero counts)
112 size_t maxIndex = Y.size() - 1;
113 for (; maxIndex > minIndex; --maxIndex) {
114 if (isNonZero(Y[maxIndex])) {
115 tofMax = X[maxIndex];
116 break; // we found the last bin with non-zero counts
117 }
118 }
119 }
120
131 void setPositions(const std::vector<double> &peaksInD, const std::vector<double> &peaksInDWindows, const double difa,
132 const double difc, const double tzero) {
133 // clear out old values
134 inDPos.clear();
135 inTofPos.clear();
136 inTofWindows.clear();
137
138 // assign things
139 inDPos.assign(peaksInD.begin(), peaksInD.end());
140 inTofPos.assign(peaksInD.begin(), peaksInD.end());
141 inTofWindows.assign(peaksInDWindows.begin(), peaksInDWindows.end());
142
143 // convert the bits that matter to TOF
144 Kernel::Units::dSpacing dSpacingUnit;
145 std::vector<double> yunused;
146 dSpacingUnit.toTOF(
147 inTofPos, yunused, -1, 0,
149 dSpacingUnit.toTOF(
150 inTofWindows, yunused, -1, 0,
152 }
153
154 std::size_t wkspIndex;
155 std::set<detid_t> detid;
156 double tofMin; // TOF of bin with minimum TOF and non-zero counts
157 double tofMax; // TOF of bin with maximum TOF and non-zero counts
158 std::vector<double> inTofPos; // peak centers, in TOF
159 // left and right fit ranges for each peak center, in TOF
160 std::vector<double> inTofWindows;
161 std::vector<double> inDPos; // peak centers, in d-spacing
162};
163
164//----------------------------------------------------------------------------------------------
168
169//----------------------------------------------------------------------------------------------
173
174//----------------------------------------------------------------------------------------------
175
177const std::string PDCalibration::name() const { return "PDCalibration"; }
178
180int PDCalibration::version() const { return 1; }
181
183const std::string PDCalibration::category() const { return "Diffraction\\Calibration"; }
184
186const std::string PDCalibration::summary() const {
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.";
189}
190
191//----------------------------------------------------------------------------------------------
195 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
196 "Input workspace containing spectra as a function of TOF measured on a standard sample.");
197
198 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
199 mustBePositive->setLower(0);
200 declareProperty("StartWorkspaceIndex", 0, mustBePositive, "Starting workspace index for fit");
202 "StopWorkspaceIndex", EMPTY_INT(),
203 "Last workspace index for fit is the smaller of this value and the workspace index of last spectrum.");
204
206 std::make_unique<ArrayProperty<double>>("TofBinning", std::make_shared<RebinParamsValidator>()),
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.");
210
211 const std::vector<std::string> exts2{".h5", ".cal"};
212 declareProperty(std::make_unique<FileProperty>("PreviousCalibrationFile", "", FileProperty::OptionalLoad, exts2),
213 "An existent file to be loaded into a calibration table, which is used for converting PeakPositions "
214 "from d-spacing to TOF.");
216 "PreviousCalibrationTable", "", Direction::Input, API::PropertyMode::Optional),
217 "An existent calibration table used for converting PeakPositions from d-spacing to TOF. "
218 "This property has precedence over PreviousCalibrationFile.");
219
220 // properties about peak positions to fit
221 std::vector<std::string> peaktypes{"BackToBackExponential", "Gaussian", "Lorentzian", "PseudoVoigt",
222 "IkedaCarpenterPV"};
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.");
228
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);
233 peaksValidator->add(std::make_shared<MandatoryValidator<std::vector<double>>>());
234 declareProperty(std::make_unique<ArrayProperty<double>>("PeakPositions", peaksValidator),
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.");
237
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);
243 windowValidator->add(std::make_shared<MandatoryValidator<std::vector<double>>>());
244 declareProperty(std::make_unique<ArrayProperty<double>>("PeakWindow", "0.1", windowValidator),
245 "Width of the window (d-spacing) over which to fit a peak. If a single value is supplied, it will be "
246 "used as half "
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.");
251
252 auto min = std::make_shared<BoundedValidator<double>>();
253 min->setLower(1e-3);
254 declareProperty("PeakWidthPercent", EMPTY_DBL(), min,
255 "Used for estimating peak width (an initial parameter for peak fitting) by multiplying peak's value "
256 "in PeakPositions by this factor.");
257
258 declareProperty("MinimumPeakHeight", 2.,
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.");
261
262 declareProperty("MaxChiSq", 100.,
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.");
265
266 declareProperty("ConstrainPeakPositions", false,
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.");
271
272 declareProperty("HighBackground", false,
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.");
275
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.");
280
281 declareProperty("RespectFixedPeakParameters", false,
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.");
286
287 declareProperty("MinimumSignalToNoiseRatio", 0.,
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.");
292
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.");
296
297 declareProperty(std::make_unique<ArrayProperty<double>>("TZEROrange"),
298 "Range for allowable calibrated TZERO value. Default: no restriction.");
299
300 declareProperty(std::make_unique<ArrayProperty<double>>("DIFArange"),
301 "Range for allowable calibrated DIFA value. Default: no restriction.");
302
303 declareProperty("UseChiSq", false,
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.");
308
310 std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("OutputCalibrationTable", "", Direction::Output),
311 "Output table workspace containing the calibration.");
312
313 // Mantid's python API _requires_ a non empty-string name for any Output workspace, even when 'PropertyMode::Optional'
314 // is specified.
315 declareProperty(std::make_unique<WorkspaceProperty<MaskWorkspace>>("MaskWorkspace", "_empty_", Direction::Output,
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");
320
322 std::make_unique<WorkspaceProperty<API::WorkspaceGroup>>("DiagnosticWorkspaces", "", Direction::Output),
323 "Auxiliary workspaces containing extended information on the calibration results.");
324
325 declareProperty("MinimumPeakTotalCount", EMPTY_DBL(),
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.");
328
329 declareProperty("MinimumSignalToSigmaRatio", 0.,
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.");
332
333 // make group for Input properties
334 std::string inputGroup("Input Options");
335 setPropertyGroup("InputWorkspace", inputGroup);
336 setPropertyGroup("StartWorkspaceIndex", inputGroup);
337 setPropertyGroup("StopWorkspaceIndex", inputGroup);
338 setPropertyGroup("TofBinning", inputGroup);
339 setPropertyGroup("PreviousCalibrationFile", inputGroup);
340 setPropertyGroup("PreviousCalibrationTable", inputGroup);
341
342 std::string funcgroup("Function Types");
343 setPropertyGroup("PeakFunction", funcgroup);
344 setPropertyGroup("BackgroundType", funcgroup);
345
346 // make group for FitPeaks properties
347 std::string fitPeaksGroup("Peak Fitting");
348 setPropertyGroup("PeakPositions", fitPeaksGroup);
349 setPropertyGroup("PeakWindow", fitPeaksGroup);
350 setPropertyGroup("PeakWidthPercent", fitPeaksGroup);
351 setPropertyGroup("MinimumPeakHeight", fitPeaksGroup);
352 setPropertyGroup("MinimumSignalToNoiseRatio", fitPeaksGroup);
353 setPropertyGroup("MinimumPeakTotalCount", fitPeaksGroup);
354 setPropertyGroup("MinimumSignalToSigmaRatio", fitPeaksGroup);
355 setPropertyGroup("HighBackground", fitPeaksGroup);
356 setPropertyGroup("MaxChiSq", fitPeaksGroup);
357 setPropertyGroup("ConstrainPeakPositions", fitPeaksGroup);
358 setPropertyGroup("CopyLastGoodPeakParameters", fitPeaksGroup);
359 setPropertyGroup("RespectFixedPeakParameters", fitPeaksGroup);
360
361 // make group for type of calibration
362 std::string calGroup("Calibration Type");
363 setPropertyGroup("CalibrationParameters", calGroup);
364 setPropertyGroup("TZEROrange", calGroup);
365 setPropertyGroup("DIFArange", calGroup);
366 setPropertyGroup("UseChiSq", calGroup);
367}
368
369std::map<std::string, std::string> PDCalibration::validateInputs() {
370 std::map<std::string, std::string> messages;
371
372 if (MaskWorkspace_const_sptr maskWS = getProperty("MaskWorkspace")) {
373 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
374
375 // detectors which are monitors are not included in the mask
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";
380 }
381 }
382
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";
389 }
390 }
391
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";
398 }
399 }
400
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.";
406 }
407
408 return messages;
409}
410
411namespace {
412
414bool hasDasIDs(const API::ITableWorkspace_const_sptr &table) {
415 const auto columnNames = table->getColumnNames();
416 return (std::find(columnNames.begin(), columnNames.end(), std::string("dasid")) != columnNames.end());
417}
418
426double getWidthToFWHM(const std::string &peakshape) {
427 // could we use actual peak function here?
428 if (peakshape == "Gaussian") {
429 return 2 * std::sqrt(2. * std::log(2.));
430 } else if (peakshape == "Lorentzian") {
431 return 2.;
432 } else if (peakshape == "BackToBackExponential") {
433 return 1.; // TODO the conversion isn't document in the function
434 } else {
435 return 1.;
436 }
437}
438
439} // end of anonymous namespace
440
441//----------------------------------------------------------------------------------------------
445 vector<double> tofBinningParams = getProperty("TofBinning");
446 m_tofMin = tofBinningParams.front();
447 m_tofMax = tofBinningParams.back();
448
449 vector<double> tzeroRange = getProperty("TZEROrange");
450 if (tzeroRange.size() == 2) {
451 m_tzeroMin = tzeroRange[0];
452 m_tzeroMax = tzeroRange[1];
453
454 std::stringstream msg;
455 msg << "Using tzero range of " << m_tzeroMin << " <= "
456 << "TZERO <= " << m_tzeroMax;
457 g_log.information(msg.str());
458 } else {
459 g_log.information("Using all TZERO values");
460
461 m_tzeroMin = std::numeric_limits<double>::lowest();
462 m_tzeroMax = std::numeric_limits<double>::max();
463 }
464
465 vector<double> difaRange = getProperty("DIFArange");
466 if (difaRange.size() == 2) {
467 m_difaMin = difaRange[0];
468 m_difaMax = difaRange[1];
469
470 std::stringstream msg;
471 msg << "Using difa range of " << m_difaMin << " <= "
472 << "DIFA <= " << m_difaMax;
473 g_log.information(msg.str());
474 } else {
475 g_log.information("Using all DIFA values");
476
477 m_difaMin = std::numeric_limits<double>::lowest();
478 m_difaMax = std::numeric_limits<double>::max();
479 }
480
481 m_peaksInDspacing = getProperty("PeakPositions");
482 std::vector<double> peakWindow = getProperty("PeakWindow");
483 const std::size_t NUMPEAKS = m_peaksInDspacing.size();
484 // if peak windows given for each peak center, sort all by peak center
485 if (peakWindow.size() > 1) {
486 // load windows into ordered map keyed by peak center
487 // this preserves association of centers and window edges
488 std::map<double, std::pair<double, double>> peakEdgeAndCenter;
489 for (std::size_t i = 0; i < m_peaksInDspacing.size(); i++) {
490 peakEdgeAndCenter[m_peaksInDspacing[i]] = {peakWindow[2 * i], peakWindow[2 * i + 1]};
491 }
492 m_peaksInDspacing.clear();
493 m_peaksInDspacing.reserve(NUMPEAKS);
494 peakWindow.clear();
495 peakWindow.reserve(2 * NUMPEAKS);
496 // retrieve ordered center, windows
497 for (auto it = peakEdgeAndCenter.begin(); it != peakEdgeAndCenter.end(); it++) {
498 m_peaksInDspacing.push_back(it->first);
499 peakWindow.push_back(it->second.first);
500 peakWindow.push_back(it->second.second);
501 }
502 } else {
503 // Sort peak positions, required for correct peak window calculations
504 std::sort(m_peaksInDspacing.begin(), m_peaksInDspacing.end());
505 }
506
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");
514
515 const std::string calParams = getPropertyValue("CalibrationParameters");
516 if (calParams == std::string("DIFC"))
518 else if (calParams == std::string("DIFC+TZERO"))
520 else if (calParams == std::string("DIFC+TZERO+DIFA"))
522 else
523 throw std::runtime_error("Encountered impossible CalibrationParameters value");
524
526 setProperty("InputWorkspace", m_uncalibratedWS);
527
528 m_startWorkspaceIndex = getProperty("StartWorkspaceIndex");
529 m_stopWorkspaceIndex = isDefault("StopWorkspaceIndex") ? static_cast<int>(m_uncalibratedWS->getNumberHistograms() - 1)
530 : getProperty("StopWorkspaceIndex");
531
532 auto uncalibratedEWS = std::dynamic_pointer_cast<EventWorkspace>(m_uncalibratedWS);
533 auto isEvent = bool(uncalibratedEWS);
534
535 // Load Previous Calibration or create calibration table from signal file
536 if ((!static_cast<std::string>(getProperty("PreviousCalibrationFile")).empty()) ||
537 (!getPropertyValue("PreviousCalibrationTable").empty())) { //"PreviousCalibrationTable"
539 } else {
540 createCalTableNew(); // calculates "difc" values from instrument geometry
541 }
543
544 // Use the incoming mask workspace, or start a new one if the workspace does not exist.
545 MaskWorkspace_sptr maskWS;
546 if (!isDefault("MaskWorkspace")) {
547 maskWS = getProperty("MaskWorkspace");
548 }
549 if (!maskWS) {
550 g_log.debug() << "[PDCalibration]: CREATING new MaskWorkspace.\n";
551 // A new mask is completely cleared at creation.
552 maskWS = std::make_shared<MaskWorkspace>(m_uncalibratedWS->getInstrument());
553 } else {
554 g_log.debug() << "[PDCalibration]: Using EXISTING MaskWorkspace.\n";
555 }
556 // Include any incoming masked detector flags in the mask-workspace values.
557 maskWS->combineFromDetectorMasks(m_uncalibratedWS->detectorInfo());
558
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";
565 }
566 auto NUMHIST = m_stopWorkspaceIndex - m_startWorkspaceIndex + 1;
567
568 // Create a pair of workspaces, one containing the nominal peak centers in TOF units,
569 // the other containing the left and right fitting ranges around each nominal
570 // peak center, also in TOF units. This for each pixel of the instrument
571 auto matrix_pair = createTOFPeakCenterFitWindowWorkspaces(m_uncalibratedWS, peakWindow);
572 API::MatrixWorkspace_sptr tof_peak_center_ws = matrix_pair.first;
573 API::MatrixWorkspace_sptr tof_peak_window_ws = matrix_pair.second;
574
575 double peak_width_percent = getProperty("PeakWidthPercent");
576
577 const std::string diagnostic_prefix = getPropertyValue("DiagnosticWorkspaces");
578
579 // Refine the position of the peak centers starting from the nominal peak
580 // centers and fitting them against a peak fit function (e.g. a Gaussian)
581 auto algFitPeaks = createChildAlgorithm("FitPeaks", .2, .7);
582 algFitPeaks->setLoggingOffset(3);
583
584 algFitPeaks->setProperty("InputWorkspace", m_uncalibratedWS);
585
586 // limit the spectra to fit
587 algFitPeaks->setProperty("StartWorkspaceIndex", static_cast<int>(m_startWorkspaceIndex));
588 algFitPeaks->setProperty("StopWorkspaceIndex", static_cast<int>(m_stopWorkspaceIndex));
589
590 // theoretical peak center
591 algFitPeaks->setProperty("PeakCentersWorkspace", tof_peak_center_ws);
592
593 // peak and background functions
594 algFitPeaks->setProperty<std::string>("PeakFunction", peakFunction);
595 algFitPeaks->setProperty<std::string>("BackgroundType", getProperty("BackgroundType"));
596 // peak range setup
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);
603 // some fitting strategy
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); // TODO Pete: need to test this option
610 // optimization setup // TODO : need to test LM or LM-MD
611 algFitPeaks->setProperty("Minimizer", "Levenberg-Marquardt");
612 algFitPeaks->setProperty("CostFunction", "Least squares");
613
614 // FitPeaks will abstract the peak parameters if you ask (if using chisq then
615 // need FitPeaks to output fitted params rather than height, width)
616 const bool useChiSq = getProperty("UseChiSq");
617 algFitPeaks->setProperty("RawPeakParameters", useChiSq);
618
619 // Analysis output
620 // If using a Gaussian peak shape plus a constant background, then
621 // OutputPeakParametersWorkspace is a table with columns:
622 // wsindex_0 peakindex_0 centre width height intensity A0 chi2
623 // ...
624 // wsindex_0 peakindex_N centre width height intensity A0 chi2
625 // ...
626 // ...
627 // wsindex_M peakindex_N centre width height intensity
628 algFitPeaks->setPropertyValue("OutputPeakParametersWorkspace", diagnostic_prefix + "_fitparam");
629 // contains the same intensities as input m_uncalibratedWS except within
630 // the fitting range of each successfully fitted peak. Within this range,
631 // the actual intensities are replaced with the values resulting from
632 // evaluating the peak function (e.g. a Gaussian peak function)
633 algFitPeaks->setPropertyValue("FittedPeaksWorkspace", diagnostic_prefix + "_fitted");
634 if (useChiSq) {
635 algFitPeaks->setPropertyValue("OutputParameterFitErrorsWorkspace", diagnostic_prefix + "_fiterrors");
636 }
637
638 // whether, for a given peak in each spectrum, the initial peak parameters (with the exception of peak centre)
639 // may be copied from the last successfully fit peak in that spectrum.
640 algFitPeaks->setProperty("CopyLastGoodPeakParameters", copyLastGoodPeakParameters);
641 algFitPeaks->setProperty("RespectFixedPeakParameters", respectFixedPeakParameters);
642
643 // run and get the result
644 algFitPeaks->executeAsChildAlg();
645 g_log.information("finished FitPeaks");
646
647 // get the fit result
648 API::ITableWorkspace_sptr fittedTable = algFitPeaks->getProperty("OutputPeakParametersWorkspace");
649 API::MatrixWorkspace_sptr calculatedWS = algFitPeaks->getProperty("FittedPeaksWorkspace");
650 API::ITableWorkspace_sptr errorTable; // or nullptr as in FitPeaks L1997
651 if (useChiSq) {
652 errorTable = algFitPeaks->getProperty("OutputParameterFitErrorsWorkspace");
653 }
654
655 // check : for Pete
656 if (!fittedTable)
657 throw std::runtime_error("FitPeaks does not have output OutputPeakParametersWorkspace.");
658 if (fittedTable->rowCount() != NUMHIST * m_peaksInDspacing.size())
659 throw std::runtime_error("The number of rows in OutputPeakParametersWorkspace is not correct!");
660
661 // END-OF (FitPeaks)
662 const std::string backgroundType = getPropertyValue("BackgroundType");
663
664 API::Progress prog(this, 0.7, 1.0, NUMHIST);
665
666 // calculate fitting ranges to the left and right of each nominal peak
667 // center, in d-spacing units
668 const auto windowsInDSpacing = dSpacingWindows(m_peaksInDspacing, peakWindow);
669
670 // get spectrum info to check workspace index correpsonds to a valid spectrum
671 const auto &spectrumInfo = m_uncalibratedWS->spectrumInfo();
672
673 // Scan the table containing the fit parameters for every peak, retrieve the
674 // parameters for peaks that were successfully fitting, then use this info
675 // to obtain difc, difa, and tzero for each pixel
676
677 PRAGMA_OMP(parallel for schedule(dynamic, 1))
678 for (int wkspIndex = m_startWorkspaceIndex; wkspIndex <= m_stopWorkspaceIndex; ++wkspIndex) {
680 if ((isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) || !spectrumInfo.hasDetectors(wkspIndex) ||
681 spectrumInfo.isMonitor(wkspIndex) ||
682 maskWS->isMasked(m_uncalibratedWS->getSpectrum(wkspIndex).getDetectorIDs())) {
683 prog.report();
684
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";
689 }
690 }
691
692 continue;
693 }
694
695 // object to hold information about the peak positions, detid, and workspace index
697 const auto [dif_c, dif_a, tzero] =
698 getDSpacingToTof(peaks.detid); // doesn't matter which one - all have same difc etc.
699 peaks.setPositions(m_peaksInDspacing, windowsInDSpacing, dif_a, dif_c, tzero);
700
701 // includes peaks that aren't used in the fit
702 // The following data structures will hold information for the peaks
703 // found in the current spectrum
704 const size_t numPeaks = m_peaksInDspacing.size();
705 // TOF of fitted peak centers, default `nan` for failed fitted peaks
706 std::vector<double> tof_vec_full(numPeaks, std::nan(""));
707 std::vector<double> d_vec; // nominal peak centers of fitted peaks
708 std::vector<double> tof_vec; // TOF of fitted peak centers only
709 // width of fitted peak centers, default `nan` for failed fitted peaks
710 std::vector<double> width_vec_full(numPeaks, std::nan(""));
711 // height of fitted peak centers, default `nan` for failed fitted peaks
712 std::vector<double> height_vec_full(numPeaks, std::nan(""));
713 std::vector<double> weights; // weights for diff const fits
714 // row where first peak occurs
715 const size_t rowNumInFitTableOffset = (wkspIndex - m_startWorkspaceIndex) * numPeaks;
716 // We assumed that the current spectrum contains peaks near the nominal
717 // peak centers. Now we check how many peaks we actually found
718 for (size_t peakIndex = 0; peakIndex < numPeaks; ++peakIndex) {
719 size_t rowIndexInFitTable = rowNumInFitTableOffset + peakIndex;
720
721 // check indices in PeaksTable
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");
726
727 const double chi2 = fittedTable->getRef<double>("chi2", rowIndexInFitTable);
728 double centre = 0.0;
729 double centre_error = 0.0; // only used if useChiSq true
730 double width = 0.0;
731 double height = 0.0;
732 if (!useChiSq) {
733 // get the effective peak parameters from FitPeaks output
734 centre = fittedTable->getRef<double>("centre", rowIndexInFitTable);
735 width = fittedTable->getRef<double>("width", rowIndexInFitTable);
736 height = fittedTable->getRef<double>("height", rowIndexInFitTable);
737 } else {
738 // FitPeaks outputs actual fitting parameters
739 // extract these from the peak function (not efficient)
740 auto peakfunc = std::dynamic_pointer_cast<API::IPeakFunction>(
741 API::FunctionFactory::Instance().createFunction(peakFunction));
742 // set peak functio nparameters from fit
743 for (size_t ipar = 0; ipar < peakfunc->nParams(); ipar++) {
744 peakfunc->setParameter(ipar, fittedTable->getRef<double>(peakfunc->parameterName(ipar), rowIndexInFitTable));
745 }
746 // provide instrument geometry so functions like IkedaCarpenterPV can
747 // compute wavelength-dependent quantities (e.g. height())
748 peakfunc->setMatrixWorkspace(m_uncalibratedWS, wkspIndex, 0., 0.);
749 centre = peakfunc->centre();
750 width = peakfunc->fwhm();
751 height = peakfunc->height();
752 centre_error = errorTable->getRef<double>(peakfunc->getCentreParameterName(), rowIndexInFitTable);
753 }
754
755 // check chi-square
756 if (chi2 > maxChiSquared || chi2 < 0.) {
757 g_log.debug("failure to fit: chi2 > maximum");
758 continue; // peak fit deemed as failure
759 }
760
761 // rule out of peak with wrong position. `centre` should be within its
762 // left and right window ranges
763 if (peaks.inTofWindows[2 * peakIndex] >= centre || peaks.inTofWindows[2 * peakIndex + 1] <= centre) {
764 g_log.debug("failure to fit: peak center is out-of-range");
765 continue; // peak fit deemed as failure
766 }
767
768 // check height: make sure 0 is smaller than 0
769 if (height < minPeakHeight + 1.E-15) {
770 g_log.debug("failure to fit: peak height is less than minimum");
771 continue; // peak fit deemed as failure
772 }
773
774 // the peak fit was a success. Collect info
775 g_log.getLogStream(Logger::Priority::PRIO_TRACE) << "successful fit: peak centered at " << centre << "\n";
776
777 d_vec.emplace_back(m_peaksInDspacing[peakIndex]);
778 tof_vec.emplace_back(centre);
779 if (!useChiSq) {
780 weights.emplace_back(height * height);
781 } else {
782 weights.emplace_back(1 / (centre_error * centre_error));
783 }
784 tof_vec_full[peakIndex] = centre;
785 width_vec_full[peakIndex] = width;
786 height_vec_full[peakIndex] = height;
787 }
788
789 if (d_vec.size() < 2) {
790 // If less than two peaks were fitted successfully, indicate failure by
791 // masking all of the detectors contributing to the spectrum.
792 maskWS->setMasked(peaks.detid, true);
793
794 g_log.debug() << "MASKING:\n";
795 for (const auto &det : peaks.detid) {
796 g_log.debug() << " " << det << "\n";
797 }
798 g_log.debug() << "\n";
799
800 continue;
801 } else {
802 // obtain difc, difa, and t0 by fitting the nominal peak center
803 // positions, in d-spacing against the fitted peak center positions, in
804 // TOF units.
805 double difc = 0., t0 = 0., difa = 0.;
806 fitDIFCtZeroDIFA_LM(d_vec, tof_vec, weights, difc, t0, difa);
807 for (auto iter = peaks.detid.begin(); iter != peaks.detid.end(); ++iter) {
808 auto det = *iter;
809 const auto rowIndexOutputPeaks = m_detidToRow[det];
810 // chisq represent the deviations between the nominal peak positions
811 // and the peak positions using the GSAS formula with optimized difc,
812 // difa, and tzero
813 double chisq = 0.;
815 dSpacingUnit.initialize(-1., 0,
819 for (std::size_t i = 0; i < numPeaks; ++i) {
820 if (std::isnan(tof_vec_full[i]))
821 continue;
822 // Find d-spacing using the GSAS formula with optimized difc, difa,
823 // t0 for the TOF of the current peak's center.
824 const double dspacing = dSpacingUnit.singleFromTOF(tof_vec_full[i]);
825 // `temp` is residual between the nominal position in d-spacing for
826 // the current peak, and the fitted position in d-spacing
827 const double temp = m_peaksInDspacing[i] - dspacing;
828 chisq += (temp * temp);
829 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, i + 1) = dspacing;
830 m_peakWidthTable->cell<double>(rowIndexOutputPeaks, i + 1) =
831 WIDTH_TO_FWHM * (width_vec_full[i] / (2 * difa * dspacing + difc));
832 m_peakHeightTable->cell<double>(rowIndexOutputPeaks, i + 1) = height_vec_full[i];
833 }
834 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, m_peaksInDspacing.size() + 1) = chisq;
835 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, m_peaksInDspacing.size() + 2) =
836 chisq / static_cast<double>(numPeaks - 1);
837
838 setCalibrationValues(det, difc, difa, t0);
839 }
840 }
841 prog.report();
842
844 }
846
847 // sort the calibration tables by increasing detector ID
849 setProperty("OutputCalibrationTable", m_calibrationTable);
850
851 // Return the mask workspace only if it was specified as a parameter.
852 if (!isDefault("MaskWorkspace")) {
853 // Align the detector mask flags of the mask workspace with the workspace values:
854 maskWS->combineToDetectorMasks();
855 setProperty("MaskWorkspace", maskWS);
856 }
857
858 // fix-up the diagnostic workspaces
862
863 // a derived table from the position and width
864 auto resolutionWksp = calculateResolutionTable();
865
866 // set the diagnostic workspaces out
867 auto diagnosticGroup = std::make_shared<API::WorkspaceGroup>();
868 // add workspaces calculated by FitPeaks
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);
873 if (useChiSq) {
874 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_fiterror", errorTable);
875 diagnosticGroup->addWorkspace(errorTable);
876 }
877
878 // add workspaces calculated by PDCalibration
879 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_dspacing", m_peakPositionTable);
880 diagnosticGroup->addWorkspace(m_peakPositionTable);
881 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_width", m_peakWidthTable);
882 diagnosticGroup->addWorkspace(m_peakWidthTable);
883 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_height", m_peakHeightTable);
884 diagnosticGroup->addWorkspace(m_peakHeightTable);
885 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_resolution", resolutionWksp);
886 diagnosticGroup->addWorkspace(resolutionWksp);
887 setProperty("DiagnosticWorkspaces", diagnosticGroup);
888}
889
890namespace { // anonymous namespace
898double gsl_costFunction(const gsl_vector *v, void *peaks) {
899 // this array is [numPeaks, numParams, vector<tof>, vector<dspace>,
900 // vector<weights>]
901 // index as [0, 1, 2, , 2+n , 2+2n]
902 const std::vector<double> *peakVec = reinterpret_cast<std::vector<double> *>(peaks);
903 // number of peaks being fit
904 const auto numPeaks = static_cast<size_t>(peakVec->at(0));
905 // number of parameters
906 const auto numParams = static_cast<size_t>(peakVec->at(1));
907
908 // isn't strictly necessary, but makes reading the code much easier
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));
912
913 // create the function to convert tof to dspacing
914 double difc = gsl_vector_get(v, 0);
915 double tzero = 0.;
916 double difa = 0.;
917 if (numParams > 1) {
918 tzero = gsl_vector_get(v, 1);
919 if (numParams > 2)
920 difa = gsl_vector_get(v, 2);
921 }
923 dSpacingUnit.initialize(-1., 0,
927
928 // calculate the sum of the residuals from observed peaks
929 double errsum = 0.0;
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];
933 errsum += errsum_i;
934 }
935
936 return errsum;
937}
938
957double fitDIFCtZeroDIFA(std::vector<double> &peaks, double &difc, double &t0, double &difa) {
958 const auto numParams = static_cast<size_t>(peaks[1]);
959
960 // initial starting point as [DIFC, 0, 0]
961 gsl_vector *fitParams = gsl_vector_alloc(numParams);
962 gsl_vector_set_all(fitParams, 0.0); // set all parameters to zero
963 gsl_vector_set(fitParams, 0, difc);
964 if (numParams > 1) {
965 gsl_vector_set(fitParams, 1, t0);
966 if (numParams > 2) {
967 gsl_vector_set(fitParams, 2, difa);
968 }
969 }
970
971 // Set initial step sizes
972 gsl_vector *stepSizes = gsl_vector_alloc(numParams);
973 gsl_vector_set_all(stepSizes, 0.1);
974 gsl_vector_set(stepSizes, 0, 0.01);
975
976 // Initialize method and iterate
977 gsl_multimin_function minex_func;
978 minex_func.n = numParams;
979 minex_func.f = &gsl_costFunction;
980 minex_func.params = &peaks;
981
982 // Set up GSL minimzer - simplex is overkill
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);
986
987 // Finally do the fitting
988 size_t iter = 0; // number of iterations
989 const size_t MAX_ITER = 75 * numParams;
990 int status = 0;
991 do {
992 iter++;
993 status = gsl_multimin_fminimizer_iterate(minimizer);
994 if (status)
995 break;
996
997 double size = gsl_multimin_fminimizer_size(minimizer);
998 status = gsl_multimin_test_size(size, 1e-4);
999
1000 } while (status == GSL_CONTINUE && iter < MAX_ITER);
1001
1002 // only update calibration values on successful fit
1003 double errsum = 0.; // return 0. if fit didn't work
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);
1011 }
1012 }
1013 // return from gsl_costFunction can be accessed as fval
1014 errsum = minimizer->fval;
1015 }
1016
1017 // free memory
1018 gsl_vector_free(fitParams);
1019 gsl_vector_free(stepSizes);
1020 gsl_multimin_fminimizer_free(minimizer);
1021 return errsum;
1022}
1023
1024} // end of anonymous namespace
1025
1040void PDCalibration::fitDIFCtZeroDIFA_LM(const std::vector<double> &d, const std::vector<double> &tof,
1041 const std::vector<double> &weights, double &difc, double &t0, double &difa) {
1042 const size_t numPeaks = d.size();
1043 if (numPeaks <= 1) {
1044 return; // don't do anything
1045 }
1046 // number of fit parameters 1=[DIFC], 2=[DIFC,TZERO], 3=[DIFC,TZERO,DIFA]
1047 // set the maximum number of parameters that will be used
1048 // statistics doesn't support having too few peaks
1049 size_t maxParams = std::min<size_t>(numPeaks - 1, m_numberMaxParams);
1050
1051 // this must have the same layout as the unpacking in gsl_costFunction above
1052 // `peaks` has the following structure for a fit session with three peaks
1053 // and two fit parameters:
1054 // 3, 2, tof1, tof2, tof3, d1, d2, d3, h21, h22, h23
1055 std::vector<double> peaks(numPeaks * 3 + 2, 0.);
1056 peaks[0] = static_cast<double>(d.size());
1057 peaks[1] = 1.; // number of parameters to fit. Initialize to just one
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];
1062 }
1063
1064 // calculate a starting DIFC
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; // number of peaks falls out of division
1070 }
1071
1072 // save the best values so far
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.;
1077
1078 // loop over possible number of parameters, doing up to three sequential fits.
1079 // We first start with equation TOF = DIFC * d and obtain
1080 // optimized DIFC which serves as initial guess for next fit with equation
1081 // TOF = DIFC * d + TZERO, obtaining optimized DIFC and TZERO which serve as
1082 // initial guess for final fit TOF = DIFC * d + DIFA * d^2 + TZERO.
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);
1089 if (errsum > 0.) {
1090 // normalize by degrees of freedom
1091 errsum = errsum / static_cast<double>(numPeaks - numParams);
1092 // save the best and forget the rest
1093 // the selected (DIFC, DIFA, TZERO) correspond to those of the fit that
1094 // minimizes errsum. It doesn't have to be the last fit.
1095 if (errsum < best_errsum) {
1096 if (difa_local > m_difaMax || difa_local < m_difaMin)
1097 continue; // unphysical fit
1098 if (t0_local > m_tzeroMax || t0_local < m_tzeroMin)
1099 continue; // unphysical fit
1100 best_errsum = errsum;
1101 best_difc = difc_local;
1102 best_t0 = t0_local;
1103 best_difa = difa_local;
1104 }
1105 }
1106 }
1107
1108 difc = best_difc;
1109 // check that something actually fit and set to the best result
1110 if (best_difc > 0. && best_errsum < std::numeric_limits<double>::max()) {
1111 t0 = best_t0;
1112 difa = best_difa;
1113 }
1114}
1115
1125vector<double> PDCalibration::dSpacingWindows(const std::vector<double> &centres,
1126 const std::vector<double> &windows_in) {
1127
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");
1131
1132 const std::size_t numPeaks = centres.size();
1133
1134 // assumes distance between peaks can be used for window sizes
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");
1138
1139 vector<double> windows_out(2 * numPeaks);
1140 double left;
1141 double right;
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];
1146 } else {
1147 left = windows_in[2 * i];
1148 right = windows_in[2 * i + 1];
1149 }
1150 // check boundaries don't exceed half dist to adjacent peaks
1151 if (i > 0) {
1152 left = std::max(left, centres[i] - 0.5 * (centres[i] - centres[i - 1]));
1153 }
1154 if (i < numPeaks - 1) {
1155 right = std::min(right, centres[i] + 0.5 * (centres[i + 1] - centres[i]));
1156 }
1157 // set the windows
1158 windows_out[2 * i] = left;
1159 windows_out[2 * i + 1] = right;
1160 }
1161 return windows_out;
1162}
1163
1171std::tuple<double, double, double> PDCalibration::getDSpacingToTof(const std::set<detid_t> &detIds) {
1172 // to start this is the old calibration values
1173 double difc = 0.;
1174 double difa = 0.;
1175 double tzero = 0.;
1176 for (auto detId : detIds) {
1177 auto rowNum = m_detidToRow[detId];
1178 difc += m_calibrationTable->getRef<double>("difc", rowNum);
1179 difa += m_calibrationTable->getRef<double>("difa", rowNum);
1180 tzero += m_calibrationTable->getRef<double>("tzero", rowNum);
1181 }
1182 if (detIds.size() > 1) {
1183 double norm = 1. / static_cast<double>(detIds.size());
1184 difc = norm * difc;
1185 difa = norm * difa;
1186 tzero = norm * tzero;
1187 }
1188
1189 return {difc, difa, tzero};
1190}
1191
1192void PDCalibration::setCalibrationValues(const detid_t detid, const double difc, const double difa,
1193 const double tzero) {
1194 // don't set values that aren't in the table
1195 const auto rowIter = m_detidToRow.find(detid);
1196 if (rowIter == m_detidToRow.end())
1197 return;
1198
1199 // get the row number
1200 auto rowNum = rowIter->second;
1201
1202 // detid is already there
1203 m_calibrationTable->cell<double>(rowNum, 1) = difc;
1204 m_calibrationTable->cell<double>(rowNum, 2) = difa;
1205 m_calibrationTable->cell<double>(rowNum, 3) = tzero;
1206
1207 size_t hasDasIdsOffset = 0; // because it adds a column
1208 if (m_hasDasIds)
1209 hasDasIdsOffset++;
1210
1211 const auto tofMinMax = getTOFminmax(difc, difa, tzero);
1212 m_calibrationTable->cell<double>(rowNum, 4 + hasDasIdsOffset) = tofMinMax[0];
1213 m_calibrationTable->cell<double>(rowNum, 5 + hasDasIdsOffset) = tofMinMax[1];
1214}
1215
1224vector<double> PDCalibration::getTOFminmax(const double difc, const double difa, const double tzero) {
1225 vector<double> tofminmax(2);
1226
1227 Kernel::Units::dSpacing dSpacingUnit;
1228 tofminmax[0] = dSpacingUnit.calcTofMin(difc, difa, tzero, m_tofMin);
1229 tofminmax[1] = dSpacingUnit.calcTofMax(difc, difa, tzero, m_tofMax);
1230
1231 return tofminmax;
1232}
1233MatrixWorkspace_sptr PDCalibration::load(const std::string &filename) {
1234 // TODO this assumes that all files are event-based
1235 const double maxChunkSize = getProperty("MaxChunkSize");
1236 const double filterBadPulses = getProperty("FilterBadPulses");
1237
1238 auto alg = createChildAlgorithm("LoadEventAndCompress");
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();
1247 API::Workspace_sptr workspace = alg->getProperty("OutputWorkspace");
1248
1249 return std::dynamic_pointer_cast<MatrixWorkspace>(workspace);
1250}
1251
1257
1259 g_log.information("Binning data in time-of-flight");
1260 auto rebin = createChildAlgorithm("Rebin");
1261 rebin->setLoggingOffset(1);
1262 rebin->setProperty("InputWorkspace", wksp);
1263 rebin->setProperty("OutputWorkspace", wksp);
1264 rebin->setProperty("Params", getPropertyValue("TofBinning"));
1265 rebin->setProperty("PreserveEvents", true);
1266 rebin->executeAsChildAlg();
1267 wksp = rebin->getProperty("OutputWorkspace");
1268
1269 return wksp;
1270}
1271
1273 std::set<detid_t> detids;
1274
1275 // return early since everything is being used
1276 if (isDefault("StartWorkspaceIndex") && isDefault("StopWorkspaceIndex"))
1277 return detids;
1278
1279 // get the indices to loop over
1280 std::size_t startIndex = static_cast<std::size_t>(m_startWorkspaceIndex);
1281 std::size_t stopIndex = static_cast<std::size_t>(m_stopWorkspaceIndex);
1282
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);
1287 }
1288 }
1289 return detids;
1290}
1291
1293 // create a new workspace
1294 m_calibrationTable = std::make_shared<DataObjects::TableWorkspace>();
1295 // TODO m_calibrationTable->setTitle("");
1296 m_calibrationTable->addColumn("int", "detid");
1297 m_calibrationTable->addColumn("double", "difc");
1298 m_calibrationTable->addColumn("double", "difa");
1299 m_calibrationTable->addColumn("double", "tzero");
1300 if (m_hasDasIds)
1301 m_calibrationTable->addColumn("int", "dasid");
1302 m_calibrationTable->addColumn("double", "tofmin");
1303 m_calibrationTable->addColumn("double", "tofmax");
1304}
1305
1320 API::ITableWorkspace_sptr calibrationTableOld = getProperty("PreviousCalibrationTable");
1321 if (calibrationTableOld == nullptr) {
1322 // load from file
1323 std::string filename = getProperty("PreviousCalibrationFile");
1324 auto alg = createChildAlgorithm("LoadDiffCal");
1325 alg->setLoggingOffset(1);
1326 alg->setProperty("Filename", filename);
1327 alg->setProperty("WorkspaceName", "NOMold"); // TODO
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");
1334 }
1335
1336 m_hasDasIds = hasDasIDs(calibrationTableOld);
1337
1338 // create a new workspace
1339 this->createCalTableHeader();
1340
1341 const auto includedDetids = detIdsForTable();
1342 const bool useAllDetids = includedDetids.empty();
1343
1344 // generate the map of detid -> row
1345 API::ColumnVector<int> detIDs = calibrationTableOld->getVector("detid");
1346 std::size_t rowNum = 0;
1347 for (std::size_t i = 0; i < detIDs.size(); ++i) {
1348 // only add rows for detids that exist in input workspace
1349 if ((useAllDetids) || (includedDetids.count(detIDs[i]) > 0)) {
1350 m_detidToRow[static_cast<detid_t>(detIDs[i])] = rowNum++;
1351 API::TableRow newRow = m_calibrationTable->appendRow();
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);
1356
1357 newRow << detid << difc << difa << tzero;
1358 if (m_hasDasIds)
1359 newRow << calibrationTableOld->getRef<int>("dasid", i);
1360
1361 // adjust tofmin and tofmax for this pixel
1362 const auto tofMinMax = getTOFminmax(difc, difa, tzero);
1363 newRow << tofMinMax[0] << tofMinMax[1]; // tofmin/tofmax
1364 }
1365 }
1366}
1367
1375 // create new calibraion table for when an old one isn't loaded
1376 // using the signal workspace and CalculateDIFC
1377 auto alg = createChildAlgorithm("CalculateDIFC");
1378 alg->setLoggingOffset(1);
1379 alg->setProperty("InputWorkspace", m_uncalibratedWS);
1380 alg->executeAsChildAlg();
1381 API::MatrixWorkspace_const_sptr difcWS = alg->getProperty("OutputWorkspace");
1382
1383 // create a new workspace
1384 this->createCalTableHeader();
1385
1386 const detid2index_map allDetectors = difcWS->getDetectorIDToWorkspaceIndexMap(false);
1387
1388 const auto includedDetids = detIdsForTable();
1389 const bool useAllDetids = includedDetids.empty();
1390
1391 // copy over the values
1392 size_t i = 0;
1393 for (auto it = allDetectors.begin(); it != allDetectors.end(); ++it) {
1394 const detid_t detID = it->first;
1395 // only add rows for detids that exist in input workspace
1396 if (useAllDetids || (includedDetids.count(detID) > 0)) {
1397 m_detidToRow[detID] = i++;
1398 const size_t wi = it->second;
1399 API::TableRow newRow = m_calibrationTable->appendRow();
1400 newRow << detID;
1401 newRow << difcWS->y(wi)[0];
1402 newRow << 0.; // difa
1403 newRow << 0.; // tzero
1404 newRow << 0.; // tofmin
1405 newRow << DBL_MAX; // tofmax
1406 }
1407 }
1408}
1409
1416 // table for the fitted location of the various peaks, in d-spacing units
1417 m_peakPositionTable = std::make_shared<DataObjects::TableWorkspace>();
1418 m_peakWidthTable = std::make_shared<DataObjects::TableWorkspace>();
1419 m_peakHeightTable = std::make_shared<DataObjects::TableWorkspace>();
1420
1421 m_peakPositionTable->addColumn("int", "detid");
1422 m_peakWidthTable->addColumn("int", "detid");
1423 m_peakHeightTable->addColumn("int", "detid");
1424
1425 for (double dSpacing : m_peaksInDspacing) {
1426 std::stringstream namess;
1427 namess << "@" << std::setprecision(5) << dSpacing;
1428 m_peakPositionTable->addColumn("double", namess.str());
1429 m_peakWidthTable->addColumn("double", namess.str());
1430 m_peakHeightTable->addColumn("double", namess.str());
1431 }
1432 m_peakPositionTable->addColumn("double", "chisq");
1433 m_peakPositionTable->addColumn("double", "normchisq");
1434 // residuals aren't needed for FWHM or height
1435
1436 // convert the map of m_detidToRow to be a vector of detector ids
1437 std::vector<detid_t> detIds(m_detidToRow.size());
1438 for (const auto &it : m_detidToRow) {
1439 detIds[it.second] = it.first;
1440 }
1441
1442 // copy the detector ids from the main table and add lots of NaNs
1443 for (const auto &detId : detIds) {
1444 API::TableRow newPosRow = m_peakPositionTable->appendRow();
1445 API::TableRow newWidthRow = m_peakWidthTable->appendRow();
1446 API::TableRow newHeightRow = m_peakHeightTable->appendRow();
1447
1448 newPosRow << detId;
1449 newWidthRow << detId;
1450 newHeightRow << detId;
1451
1452 for (double dSpacing : m_peaksInDspacing) {
1453 UNUSED_ARG(dSpacing);
1454 newPosRow << std::nan("");
1455 newWidthRow << std::nan("");
1456 newHeightRow << std::nan("");
1457 }
1458 }
1459}
1460
1463 std::make_shared<DataObjects::SpecialWorkspace2D>(m_uncalibratedWS->getInstrument());
1464 resolutionWksp->setTitle("average width/height");
1465
1466 // assume both tables have the same number of rows b/c the algorithm created
1467 // both
1468 // they are also in the same order
1469 // accessing cells is done by (row, col)
1470 const size_t numRows = m_peakPositionTable->rowCount();
1471 const size_t numPeaks = m_peaksInDspacing.size();
1472 std::vector<double> resolution; // vector of non-nan resolutions
1473 for (size_t rowIndex = 0; rowIndex < numRows; ++rowIndex) {
1474 resolution.clear();
1475 // first column is detid
1476 const auto detId = static_cast<detid_t>(m_peakPositionTable->Int(rowIndex, 0));
1477 for (size_t peakIndex = 1; peakIndex < numPeaks + 1; ++peakIndex) {
1478 const double pos = m_peakPositionTable->Double(rowIndex, peakIndex);
1479 if (std::isnormal(pos)) {
1480 resolution.emplace_back(m_peakWidthTable->Double(rowIndex, peakIndex) / pos);
1481 }
1482 }
1483 if (resolution.empty()) {
1484 resolutionWksp->setValue(detId, 0., 0.); // instview doesn't like nan
1485 } else {
1486 // calculate the mean
1487 const double mean =
1488 std::accumulate(resolution.begin(), resolution.end(), 0.) / static_cast<double>(resolution.size());
1489 double stddev = 0.;
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);
1494 }
1495 }
1496
1497 return resolutionWksp;
1498}
1499
1502 auto alg = createChildAlgorithm("SortTableWorkspace");
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");
1509
1510 return table;
1511}
1512
1527std::pair<API::MatrixWorkspace_sptr, API::MatrixWorkspace_sptr>
1529 const std::vector<double> &peakWindow) {
1530
1531 // calculate fitting ranges to the left and right of each nominal peak
1532 // center, in d-spacing units
1533 const auto windowsInDSpacing = dSpacingWindows(m_peaksInDspacing, peakWindow);
1534
1535 g_log.information() << "DSPACING WINDOWS\n";
1536 for (std::size_t i = 0; i < m_peaksInDspacing.size(); ++i) {
1537 g_log.information() << "[" << i << "] " << windowsInDSpacing[2 * i] << " < " << m_peaksInDspacing[i] << " < "
1538 << windowsInDSpacing[2 * i + 1] << "\n";
1539 }
1540
1541 // create workspaces for nominal peak centers and fit ranges
1542 size_t numspec = dataws->getNumberHistograms();
1543 size_t numpeaks = m_peaksInDspacing.size();
1544 MatrixWorkspace_sptr peak_pos_ws = create<Workspace2D>(numspec, Points(numpeaks));
1545 MatrixWorkspace_sptr peak_window_ws = create<Workspace2D>(numspec, Points(numpeaks * 2));
1546
1547 const auto NUM_HIST = m_stopWorkspaceIndex - m_startWorkspaceIndex + 1;
1548 API::Progress prog(this, 0., .2, NUM_HIST);
1549
1550 g_log.information() << "TOF WINDOWS\n";
1551 PRAGMA_OMP(parallel for schedule(dynamic, 1) )
1552 for (int64_t iiws = m_startWorkspaceIndex; iiws <= static_cast<int64_t>(m_stopWorkspaceIndex); iiws++) {
1554 std::size_t iws = static_cast<std::size_t>(iiws);
1555 // calculatePositionWindowInTOF
1556 PDCalibration::FittedPeaks peaks(dataws, iws);
1557 // toTof is a function that converts from d-spacing to TOF for a particular
1558 // pixel
1559 auto [difc, difa, tzero] = getDSpacingToTof(peaks.detid);
1560 // setpositions initializes peaks.inTofPos and peaks.inTofWindows
1561 peaks.setPositions(m_peaksInDspacing, windowsInDSpacing, difa, difc, tzero);
1562 peak_pos_ws->setPoints(iws, peaks.inTofPos);
1563 peak_window_ws->setPoints(iws, peaks.inTofWindows);
1564 prog.report();
1565
1566 for (std::size_t i = 0; i < peaks.inTofPos.size(); i++) {
1567 g_log.information() << "[" << iws << "," << i << "] " << peaks.inTofWindows[2 * i] << " < " << peaks.inTofPos[i]
1568 << " < " << peaks.inTofWindows[2 * i + 1] << "\n";
1569 }
1570
1572 }
1574
1575 return std::make_pair(peak_pos_ws, peak_window_ws);
1576}
1577
1578} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
double value
The value of the point.
Definition FitMW.cpp:51
double height
Definition GetAllEi.cpp:155
IPeaksWorkspace_sptr workspace
double left
double right
#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 ...
Definition System.h:44
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:423
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.
Definition Progress.h:25
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
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,...
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.
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
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 > &centres, 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
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.
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.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
std::ostream & getLogStream(const Priority &priority)
gets the correct log stream for a priority
Definition Logger.cpp:404
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
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 &params)
Initialize the unit to perform conversion using singleToTof() and singleFromTof()
Definition Unit.cpp:133
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.
Definition Unit.cpp:148
d-Spacing in Angstrom
Definition Unit.h:351
double calcTofMax(const double difc, const double difa, const double tzero, const double tofmax=0.)
Definition Unit.cpp:782
double calcTofMin(const double difc, const double difa, const double tzero, const double tofmin=0.)
Definition Unit.cpp:775
double singleFromTOF(const double tof) const override
DIFA * d^2 + DIFC * d + T0 - TOF = 0.
Definition Unit.cpp:696
double singleToTOF(const double x) const override
Convert a single X value to TOF.
Definition Unit.cpp:670
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
Definition Unit.h:30
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
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.
Definition EmptyValues.h:42
Enumeration for a mandatory/optional property.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54