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 <cassert>
40#include <gsl/gsl_multifit_nlin.h>
41#include <gsl/gsl_multimin.h>
42#include <limits>
43#include <numeric>
44
45namespace Mantid::Algorithms {
46
47using namespace Mantid::DataObjects;
48using namespace Mantid::HistogramData;
64using std::vector;
65
66// Register the algorithm into the AlgorithmFactory
67DECLARE_ALGORITHM(PDCalibration)
68
69namespace { // anonymous
70const auto isNonZero = [](const double value) { return value != 0.; };
71}
72
75public:
85 FittedPeaks(const API::MatrixWorkspace_const_sptr &wksp, const std::size_t wkspIndex) {
86 this->wkspIndex = wkspIndex;
87
88 // convert workspace index into detector id
89 const auto &spectrum = wksp->getSpectrum(wkspIndex);
90 this->detid = spectrum.getDetectorIDs();
91
92 const auto &X = spectrum.x();
93 const auto &Y = spectrum.y();
94 tofMin = X.front();
95 tofMax = X.back();
96
97 // determine tof min supported by the workspace (non-zero counts)
98 size_t minIndex = 0; // want to store value
99 for (; minIndex < Y.size(); ++minIndex) {
100 if (isNonZero(Y[minIndex])) {
101 tofMin = X[minIndex];
102 break; // we found the first bin with non-zero counts
103 }
104 }
105
106 // determine tof max supported by the workspace (non-zero counts)
107 size_t maxIndex = Y.size() - 1;
108 for (; maxIndex > minIndex; --maxIndex) {
109 if (isNonZero(Y[maxIndex])) {
110 tofMax = X[maxIndex];
111 break; // we found the last bin with non-zero counts
112 }
113 }
114 }
115
126 void setPositions(const std::vector<double> &peaksInD, const std::vector<double> &peaksInDWindows, const double difa,
127 const double difc, const double tzero) {
128 // clear out old values
129 inDPos.clear();
130 inTofPos.clear();
131 inTofWindows.clear();
132
133 // assign things
134 inDPos.assign(peaksInD.begin(), peaksInD.end());
135 inTofPos.assign(peaksInD.begin(), peaksInD.end());
136 inTofWindows.assign(peaksInDWindows.begin(), peaksInDWindows.end());
137
138 // convert the bits that matter to TOF
139 Kernel::Units::dSpacing dSpacingUnit;
140 std::vector<double> yunused;
141 dSpacingUnit.toTOF(
142 inTofPos, yunused, -1, 0,
144 dSpacingUnit.toTOF(
145 inTofWindows, yunused, -1, 0,
147 }
148
149 std::size_t wkspIndex;
150 std::set<detid_t> detid;
151 double tofMin; // TOF of bin with minimum TOF and non-zero counts
152 double tofMax; // TOF of bin with maximum TOF and non-zero counts
153 std::vector<double> inTofPos; // peak centers, in TOF
154 // left and right fit ranges for each peak center, in TOF
155 std::vector<double> inTofWindows;
156 std::vector<double> inDPos; // peak centers, in d-spacing
157};
158
159//----------------------------------------------------------------------------------------------
163
164//----------------------------------------------------------------------------------------------
168
169//----------------------------------------------------------------------------------------------
170
172const std::string PDCalibration::name() const { return "PDCalibration"; }
173
175int PDCalibration::version() const { return 1; }
176
178const std::string PDCalibration::category() const { return "Diffraction\\Calibration"; }
179
181const std::string PDCalibration::summary() const {
182 return "Calibrate the detector pixels and create a calibration table";
183}
184
185//----------------------------------------------------------------------------------------------
189 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::InOut),
190 "Input signal workspace");
191
192 declareProperty(std::make_unique<ArrayProperty<double>>("TofBinning", std::make_shared<RebinParamsValidator>()),
193 "Min, Step, and Max of time-of-flight bins. "
194 "Logarithmic binning is used if Step is negative.");
195
196 const std::vector<std::string> exts2{".h5", ".cal"};
197 declareProperty(std::make_unique<FileProperty>("PreviousCalibrationFile", "", FileProperty::OptionalLoad, exts2),
198 "Previous calibration file");
200 "PreviousCalibrationTable", "", Direction::Input, API::PropertyMode::Optional),
201 "Previous calibration table. This overrides results from previous file.");
202
203 // properties about peak positions to fit
204 std::vector<std::string> peaktypes{"BackToBackExponential", "Gaussian", "Lorentzian", "PseudoVoigt",
205 "IkedaCarpenterPV"};
206 declareProperty("PeakFunction", "Gaussian", std::make_shared<StringListValidator>(peaktypes));
207 vector<std::string> bkgdtypes{"Flat", "Linear", "Quadratic"};
208 declareProperty("BackgroundType", "Linear", std::make_shared<StringListValidator>(bkgdtypes), "Type of Background.");
209
210 auto peaksValidator = std::make_shared<CompositeValidator>();
211 auto mustBePosArr = std::make_shared<Kernel::ArrayBoundedValidator<double>>();
212 mustBePosArr->setLower(0.0);
213 peaksValidator->add(mustBePosArr);
214 peaksValidator->add(std::make_shared<MandatoryValidator<std::vector<double>>>());
215 declareProperty(std::make_unique<ArrayProperty<double>>("PeakPositions", peaksValidator),
216 "Comma delimited d-space positions of reference peaks.");
217
218 auto windowValidator = std::make_shared<CompositeValidator>();
219 windowValidator->add(mustBePosArr);
220 auto lengthValidator = std::make_shared<Kernel::ArrayLengthValidator<double>>();
221 lengthValidator->setLengthMin(1);
222 windowValidator->add(lengthValidator);
223 windowValidator->add(std::make_shared<MandatoryValidator<std::vector<double>>>());
224 declareProperty(std::make_unique<ArrayProperty<double>>("PeakWindow", "0.1", windowValidator),
225 "Window over which to fit a peak in d-spacing (if a single value is supplied it will used as half "
226 "the window width for all peaks, otherwise a comma delimited list of boundaries).");
227
228 auto min = std::make_shared<BoundedValidator<double>>();
229 min->setLower(1e-3);
230 declareProperty("PeakWidthPercent", EMPTY_DBL(), min,
231 "The estimated peak width as a percent of the peak"
232 "center value, in d-spacing or TOF units.");
233
234 declareProperty("MinimumPeakHeight", 2.,
235 "Minimum peak height such that all the fitted peaks with "
236 "height under this value will be excluded.");
237
238 declareProperty("MaxChiSq", 100., "Maximum chisq value for individual peak fit allowed. (Default: 100)");
239
240 declareProperty("ConstrainPeakPositions", false,
241 "If true, peak centers will be constrained by estimated positions "
242 "(highest Y value position) and "
243 "the peak width will either be estimated by observation or calculated.");
244
245 std::vector<std::string> modes{"DIFC", "DIFC+TZERO", "DIFC+TZERO+DIFA"};
246 declareProperty("CalibrationParameters", "DIFC", std::make_shared<StringListValidator>(modes),
247 "Select calibration parameters to fit.");
248
249 declareProperty(std::make_unique<ArrayProperty<double>>("TZEROrange"),
250 "Range for allowable TZERO from calibration (default is all)");
251
252 declareProperty(std::make_unique<ArrayProperty<double>>("DIFArange"),
253 "Range for allowable DIFA from calibration (default "
254 "is all)");
255
256 declareProperty("UseChiSq", false,
257 "By default the square of the peak height is used as weights "
258 "in the least-squares fit to find the diffractometer "
259 "constants, if UseChiSq is true then the inverse square of "
260 "the error on the fitted peak centres will be used instead.");
261
263 std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("OutputCalibrationTable", "", Direction::Output),
264 "Output table workspace containing the calibration");
265
267 std::make_unique<WorkspaceProperty<API::WorkspaceGroup>>("DiagnosticWorkspaces", "", Direction::Output),
268 "Workspaces to promote understanding of calibration results");
269
270 // make group for Input properties
271 std::string inputGroup("Input Options");
272 setPropertyGroup("InputWorkspace", inputGroup);
273 setPropertyGroup("TofBinning", inputGroup);
274 setPropertyGroup("PreviousCalibrationFile", inputGroup);
275 setPropertyGroup("PreviousCalibrationTable", inputGroup);
276
277 std::string funcgroup("Function Types");
278 setPropertyGroup("PeakFunction", funcgroup);
279 setPropertyGroup("BackgroundType", funcgroup);
280
281 // make group for FitPeaks properties
282 std::string fitPeaksGroup("Peak Fitting");
283 setPropertyGroup("PeakPositions", fitPeaksGroup);
284 setPropertyGroup("PeakWindow", fitPeaksGroup);
285 setPropertyGroup("PeakWidthPercent", fitPeaksGroup);
286 setPropertyGroup("MinimumPeakHeight", fitPeaksGroup);
287 setPropertyGroup("MaxChiSq", fitPeaksGroup);
288 setPropertyGroup("ConstrainPeakPositions", fitPeaksGroup);
289
290 // make group for type of calibration
291 std::string calGroup("Calibration Type");
292 setPropertyGroup("CalibrationParameters", calGroup);
293 setPropertyGroup("TZEROrange", calGroup);
294 setPropertyGroup("DIFArange", calGroup);
295 setPropertyGroup("UseChiSq", calGroup);
296}
297
298std::map<std::string, std::string> PDCalibration::validateInputs() {
299 std::map<std::string, std::string> messages;
300
301 vector<double> tzeroRange = getProperty("TZEROrange");
302 if (!tzeroRange.empty()) {
303 if (tzeroRange.size() != 2) {
304 messages["TZEROrange"] = "Require two values [min,max]";
305 } else if (tzeroRange[0] >= tzeroRange[1]) {
306 messages["TZEROrange"] = "min must be less than max";
307 }
308 }
309
310 vector<double> difaRange = getProperty("DIFArange");
311 if (!difaRange.empty()) {
312 if (difaRange.size() != 2) {
313 messages["DIFArange"] = "Require two values [min,max]";
314 } else if (difaRange[0] >= difaRange[1]) {
315 messages["DIFArange"] = "min must be less than max";
316 }
317 }
318
319 vector<double> peakWindow = getProperty("PeakWindow");
320 vector<double> peakCentres = getProperty("PeakPositions");
321 if (peakWindow.size() > 1 && peakWindow.size() != 2 * peakCentres.size()) {
322 messages["PeakWindow"] = "PeakWindow must be a vector with either 1 element (interpreted as half the width of "
323 "the window) or twice the number of peak centres provided.";
324 }
325
326 return messages;
327}
328
329namespace {
330
332bool hasDasIDs(const API::ITableWorkspace_const_sptr &table) {
333 const auto columnNames = table->getColumnNames();
334 return (std::find(columnNames.begin(), columnNames.end(), std::string("dasid")) != columnNames.end());
335}
336
344double getWidthToFWHM(const std::string &peakshape) {
345 // could we use actual peak function here?
346 if (peakshape == "Gaussian") {
347 return 2 * std::sqrt(2. * std::log(2.));
348 } else if (peakshape == "Lorentzian") {
349 return 2.;
350 } else if (peakshape == "BackToBackExponential") {
351 return 1.; // TODO the conversion isn't document in the function
352 } else {
353 return 1.;
354 }
355}
356
357} // end of anonymous namespace
358
359//----------------------------------------------------------------------------------------------
363 vector<double> tofBinningParams = getProperty("TofBinning");
364 m_tofMin = tofBinningParams.front();
365 m_tofMax = tofBinningParams.back();
366
367 vector<double> tzeroRange = getProperty("TZEROrange");
368 if (tzeroRange.size() == 2) {
369 m_tzeroMin = tzeroRange[0];
370 m_tzeroMax = tzeroRange[1];
371
372 std::stringstream msg;
373 msg << "Using tzero range of " << m_tzeroMin << " <= "
374 << "TZERO <= " << m_tzeroMax;
375 g_log.information(msg.str());
376 } else {
377 g_log.information("Using all TZERO values");
378
379 m_tzeroMin = std::numeric_limits<double>::lowest();
380 m_tzeroMax = std::numeric_limits<double>::max();
381 }
382
383 vector<double> difaRange = getProperty("DIFArange");
384 if (difaRange.size() == 2) {
385 m_difaMin = difaRange[0];
386 m_difaMax = difaRange[1];
387
388 std::stringstream msg;
389 msg << "Using difa range of " << m_difaMin << " <= "
390 << "DIFA <= " << m_difaMax;
391 g_log.information(msg.str());
392 } else {
393 g_log.information("Using all DIFA values");
394
395 m_difaMin = std::numeric_limits<double>::lowest();
396 m_difaMax = std::numeric_limits<double>::max();
397 }
398
399 m_peaksInDspacing = getProperty("PeakPositions");
400 // Sort peak positions, required for correct peak window calculations
401 std::sort(m_peaksInDspacing.begin(), m_peaksInDspacing.end());
402
403 const std::vector<double> peakWindow = getProperty("PeakWindow");
404 const double minPeakHeight = getProperty("MinimumPeakHeight");
405 const double maxChiSquared = getProperty("MaxChiSq");
406
407 const std::string calParams = getPropertyValue("CalibrationParameters");
408 if (calParams == std::string("DIFC"))
410 else if (calParams == std::string("DIFC+TZERO"))
412 else if (calParams == std::string("DIFC+TZERO+DIFA"))
414 else
415 throw std::runtime_error("Encountered impossible CalibrationParameters value");
416
418 setProperty("InputWorkspace", m_uncalibratedWS);
419
420 auto uncalibratedEWS = std::dynamic_pointer_cast<EventWorkspace>(m_uncalibratedWS);
421 auto isEvent = bool(uncalibratedEWS);
422
423 // Load Previous Calibration or create calibration table from signal file
424 if ((!static_cast<std::string>(getProperty("PreviousCalibrationFile")).empty()) ||
425 (!getPropertyValue("PreviousCalibrationTable").empty())) { //"PreviousCalibrationTable"
427 } else {
428 createCalTableNew(); // calculates "difc" values from instrument geometry
429 }
431
432 // Initialize the mask workspace, will all detectors masked by default
433 std::string maskWSName = getPropertyValue("OutputCalibrationTable");
434 maskWSName += "_mask";
435 declareProperty(std::make_unique<WorkspaceProperty<>>("MaskWorkspace", maskWSName, Direction::Output),
436 "An output workspace containing the mask");
437
438 MaskWorkspace_sptr maskWS = std::make_shared<DataObjects::MaskWorkspace>(m_uncalibratedWS->getInstrument());
439 for (size_t i = 0; i < maskWS->getNumberHistograms(); ++i) // REMOVE
440 maskWS->setMaskedIndex(i, true); // mask everything to start
441 setProperty("MaskWorkspace", maskWS);
442
443 const std::string peakFunction = getProperty("PeakFunction");
444 const double WIDTH_TO_FWHM = getWidthToFWHM(peakFunction);
445 if (WIDTH_TO_FWHM == 1.) {
446 g_log.notice() << "Unknown conversion for \"" << peakFunction
447 << "\", found peak widths and resolution should not be "
448 "directly compared to delta-d/d";
449 }
450 auto NUMHIST = static_cast<int>(m_uncalibratedWS->getNumberHistograms());
451
452 // A pair of workspaces, one containing the nominal peak centers in TOF units,
453 // the other containing the left and right fitting ranges around each nominal
454 // peak center, also in TOF units. This for each pixel of the instrument
455 auto matrix_pair = createTOFPeakCenterFitWindowWorkspaces(m_uncalibratedWS, peakWindow);
456 API::MatrixWorkspace_sptr tof_peak_center_ws = matrix_pair.first;
457 API::MatrixWorkspace_sptr tof_peak_window_ws = matrix_pair.second;
458
459 double peak_width_percent = getProperty("PeakWidthPercent");
460
461 const std::string diagnostic_prefix = getPropertyValue("DiagnosticWorkspaces");
462
463 // Refine the position of the peak centers starting from the nominal peak
464 // centers and fitting them against a peak fit function (e.g. a Gaussian)
465 auto algFitPeaks = createChildAlgorithm("FitPeaks", .2, .7);
466 algFitPeaks->setLoggingOffset(3);
467
468 algFitPeaks->setProperty("InputWorkspace", m_uncalibratedWS);
469 // theoretical peak center
470 algFitPeaks->setProperty("PeakCentersWorkspace", tof_peak_center_ws);
471
472 // peak and background functions
473 algFitPeaks->setProperty<std::string>("PeakFunction", peakFunction);
474 algFitPeaks->setProperty<std::string>("BackgroundType", getProperty("BackgroundType"));
475 // peak range setup
476 algFitPeaks->setProperty("FitPeakWindowWorkspace", tof_peak_window_ws);
477 algFitPeaks->setProperty("PeakWidthPercent", peak_width_percent);
478 algFitPeaks->setProperty("MinimumPeakHeight", minPeakHeight);
479 // some fitting strategy
480 algFitPeaks->setProperty("FitFromRight", true);
481 algFitPeaks->setProperty("HighBackground", false);
482 bool constrainPeakPosition = getProperty("ConstrainPeakPositions");
483 algFitPeaks->setProperty("ConstrainPeakPositions",
484 constrainPeakPosition); // TODO Pete: need to test this option
485 // optimization setup // TODO : need to test LM or LM-MD
486 algFitPeaks->setProperty("Minimizer", "Levenberg-Marquardt");
487 algFitPeaks->setProperty("CostFunction", "Least squares");
488
489 // FitPeaks will abstract the peak parameters if you ask (if using chisq then
490 // need FitPeaks to output fitted params rather than height, width)
491 const bool useChiSq = getProperty("UseChiSq");
492 algFitPeaks->setProperty("RawPeakParameters", useChiSq);
493
494 // Analysis output
495 // If using a Gaussian peak shape plus a constant background, then
496 // OutputPeakParametersWorkspace is a table with columns:
497 // wsindex_0 peakindex_0 centre width height intensity A0 chi2
498 // ...
499 // wsindex_0 peakindex_N centre width height intensity A0 chi2
500 // ...
501 // ...
502 // wsindex_M peakindex_N centre width height intensity
503 algFitPeaks->setPropertyValue("OutputPeakParametersWorkspace", diagnostic_prefix + "_fitparam");
504 // contains the same intensities as input m_uncalibratedWS except within
505 // the fitting range of each successfully fitted peak. Within this range,
506 // the actual intensities are replaced with the values resulting from
507 // evaluating the peak function (e.g. a Gaussian peak function)
508 algFitPeaks->setPropertyValue("FittedPeaksWorkspace", diagnostic_prefix + "_fitted");
509 if (useChiSq) {
510 algFitPeaks->setPropertyValue("OutputParameterFitErrorsWorkspace", diagnostic_prefix + "_fiterrors");
511 }
512
513 // run and get the result
514 algFitPeaks->executeAsChildAlg();
515 g_log.information("finished FitPeaks");
516
517 // get the fit result
518 API::ITableWorkspace_sptr fittedTable = algFitPeaks->getProperty("OutputPeakParametersWorkspace");
519 API::MatrixWorkspace_sptr calculatedWS = algFitPeaks->getProperty("FittedPeaksWorkspace");
520 API::ITableWorkspace_sptr errorTable; // or nullptr as in FitPeaks L1997
521 if (useChiSq) {
522 errorTable = algFitPeaks->getProperty("OutputParameterFitErrorsWorkspace");
523 }
524
525 // check : for Pete
526 if (!fittedTable)
527 throw std::runtime_error("FitPeaks does not have output OutputPeakParametersWorkspace.");
528 if (fittedTable->rowCount() != NUMHIST * m_peaksInDspacing.size())
529 throw std::runtime_error("The number of rows in OutputPeakParametersWorkspace is not correct!");
530
531 // END-OF (FitPeaks)
532 const std::string backgroundType = getPropertyValue("BackgroundType");
533
534 API::Progress prog(this, 0.7, 1.0, NUMHIST);
535
536 // calculate fitting ranges to the left and right of each nominal peak
537 // center, in d-spacing units
538 const auto windowsInDSpacing = dSpacingWindows(m_peaksInDspacing, peakWindow);
539
540 // get spectrum info to check workspace index correpsonds to a valid spectrum
541 const auto &spectrumInfo = m_uncalibratedWS->spectrumInfo();
542
543 // Scan the table containing the fit parameters for every peak, retrieve the
544 // parameters for peaks that were successfully fitting, then use this info
545 // to obtain difc, difa, and tzero for each pixel
546
547 PRAGMA_OMP(parallel for schedule(dynamic, 1))
548 for (int wkspIndex = 0; wkspIndex < NUMHIST; ++wkspIndex) {
550 if ((isEvent && uncalibratedEWS->getSpectrum(wkspIndex).empty()) || !spectrumInfo.hasDetectors(wkspIndex) ||
551 spectrumInfo.isMonitor(wkspIndex)) {
552 prog.report();
553 continue;
554 }
555
556 // object to hold the information about the peak positions, detid, and wksp
557 // index
559 const auto [dif_c, dif_a, tzero] =
560 getDSpacingToTof(peaks.detid); // doesn't matter which one - all have same difc etc.
561 peaks.setPositions(m_peaksInDspacing, windowsInDSpacing, dif_a, dif_c, tzero);
562
563 // includes peaks that aren't used in the fit
564 // The following data structures will hold information for the peaks
565 // found in the current spectrum
566 const size_t numPeaks = m_peaksInDspacing.size();
567 // TOF of fitted peak centers, default `nan` for failed fitted peaks
568 std::vector<double> tof_vec_full(numPeaks, std::nan(""));
569 std::vector<double> d_vec; // nominal peak centers of fitted peaks
570 std::vector<double> tof_vec; // TOF of fitted peak centers only
571 // width of fitted peak centers, default `nan` for failed fitted peaks
572 std::vector<double> width_vec_full(numPeaks, std::nan(""));
573 // height of fitted peak centers, default `nan` for failed fitted peaks
574 std::vector<double> height_vec_full(numPeaks, std::nan(""));
575 std::vector<double> weights; // weights for diff const fits
576 // for (size_t i = 0; i < fittedTable->rowCount(); ++i) {
577 const size_t rowNumInFitTableOffset = wkspIndex * numPeaks;
578 // We assumed that the current spectrum contains peaks near the nominal
579 // peak centers. Now we check how many peaks we actually found
580 for (size_t peakIndex = 0; peakIndex < numPeaks; ++peakIndex) {
581 size_t rowIndexInFitTable = rowNumInFitTableOffset + peakIndex;
582
583 // check indices in PeaksTable
584 if (fittedTable->getRef<int>("wsindex", rowIndexInFitTable) != wkspIndex)
585 throw std::runtime_error("workspace index mismatch!");
586 if (fittedTable->getRef<int>("peakindex", rowIndexInFitTable) != static_cast<int>(peakIndex))
587 throw std::runtime_error("peak index mismatch but workspace index matched");
588
589 const double chi2 = fittedTable->getRef<double>("chi2", rowIndexInFitTable);
590 double centre = 0.0;
591 double centre_error = 0.0; // only used if useChiSq true
592 double width = 0.0;
593 double height = 0.0;
594 if (!useChiSq) {
595 // get the effective peak parameters from FitPeaks output
596 centre = fittedTable->getRef<double>("centre", rowIndexInFitTable);
597 width = fittedTable->getRef<double>("width", rowIndexInFitTable);
598 height = fittedTable->getRef<double>("height", rowIndexInFitTable);
599 } else {
600 // FitPeaks outputs actual fitting parameters
601 // extract these from the peak function (not efficient)
602 auto peakfunc = std::dynamic_pointer_cast<API::IPeakFunction>(
603 API::FunctionFactory::Instance().createFunction(peakFunction));
604 // set peak functio nparameters from fit
605 for (size_t ipar = 0; ipar < peakfunc->nParams(); ipar++) {
606 peakfunc->setParameter(ipar, fittedTable->getRef<double>(peakfunc->parameterName(ipar), rowIndexInFitTable));
607 }
608 centre = peakfunc->centre();
609 width = peakfunc->fwhm();
610 height = peakfunc->height();
611 centre_error = errorTable->getRef<double>(peakfunc->getCentreParameterName(), rowIndexInFitTable);
612 }
613
614 // check chi-square
615 if (chi2 > maxChiSquared || chi2 < 0.) {
616 continue; // peak fit deemed as failure
617 }
618
619 // rule out of peak with wrong position. `centre` should be within its
620 // left and right window ranges
621 if (peaks.inTofWindows[2 * peakIndex] >= centre || peaks.inTofWindows[2 * peakIndex + 1] <= centre) {
622 continue; // peak fit deemed as failure
623 }
624
625 // check height: make sure 0 is smaller than 0
626 if (height < minPeakHeight + 1.E-15) {
627 continue; // peak fit deemed as failure
628 }
629
630 // the peak fit was a success. Collect info
631 d_vec.emplace_back(m_peaksInDspacing[peakIndex]);
632 tof_vec.emplace_back(centre);
633 if (!useChiSq) {
634 weights.emplace_back(height * height);
635 } else {
636 weights.emplace_back(1 / (centre_error * centre_error));
637 }
638 tof_vec_full[peakIndex] = centre;
639 width_vec_full[peakIndex] = width;
640 height_vec_full[peakIndex] = height;
641 }
642
643 // mask a detector if less than two peaks were fitted successfully
644 maskWS->setMasked(peaks.detid, d_vec.size() < 2);
645
646 if (d_vec.size() < 2) { // not enough peaks were found
647 continue;
648 } else {
649 // obtain difc, difa, and t0 by fitting the nominal peak center
650 // positions, in d-spacing against the fitted peak center positions, in
651 // TOF units.
652 double difc = 0., t0 = 0., difa = 0.;
653 fitDIFCtZeroDIFA_LM(d_vec, tof_vec, weights, difc, t0, difa);
654 for (auto iter = peaks.detid.begin(); iter != peaks.detid.end(); ++iter) {
655 auto det = *iter;
656 const auto rowIndexOutputPeaks = m_detidToRow[det];
657 // chisq represent the deviations between the nominal peak positions
658 // and the peak positions using the GSAS formula with optimized difc,
659 // difa, and tzero
660 double chisq = 0.;
662 dSpacingUnit.initialize(-1., 0,
666 for (std::size_t i = 0; i < numPeaks; ++i) {
667 if (std::isnan(tof_vec_full[i]))
668 continue;
669 // Find d-spacing using the GSAS formula with optimized difc, difa,
670 // t0 for the TOF of the current peak's center.
671 const double dspacing = dSpacingUnit.singleFromTOF(tof_vec_full[i]);
672 // `temp` is residual between the nominal position in d-spacing for
673 // the current peak, and the fitted position in d-spacing
674 const double temp = m_peaksInDspacing[i] - dspacing;
675 chisq += (temp * temp);
676 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, i + 1) = dspacing;
677 m_peakWidthTable->cell<double>(rowIndexOutputPeaks, i + 1) =
678 WIDTH_TO_FWHM * (width_vec_full[i] / (2 * difa * dspacing + difc));
679 m_peakHeightTable->cell<double>(rowIndexOutputPeaks, i + 1) = height_vec_full[i];
680 }
681 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, m_peaksInDspacing.size() + 1) = chisq;
682 m_peakPositionTable->cell<double>(rowIndexOutputPeaks, m_peaksInDspacing.size() + 2) =
683 chisq / static_cast<double>(numPeaks - 1);
684
685 setCalibrationValues(det, difc, difa, t0);
686 }
687 }
688 prog.report();
689
691 }
693
694 // sort the calibration tables by increasing detector ID
696 setProperty("OutputCalibrationTable", m_calibrationTable);
697
698 // fix-up the diagnostic workspaces
702
703 // a derived table from the position and width
704 auto resolutionWksp = calculateResolutionTable();
705
706 // set the diagnostic workspaces out
707 auto diagnosticGroup = std::make_shared<API::WorkspaceGroup>();
708 // add workspaces calculated by FitPeaks
709 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_fitparam", fittedTable);
710 diagnosticGroup->addWorkspace(fittedTable);
711 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_fitted", calculatedWS);
712 diagnosticGroup->addWorkspace(calculatedWS);
713 if (useChiSq) {
714 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_fiterror", errorTable);
715 diagnosticGroup->addWorkspace(errorTable);
716 }
717
718 // add workspaces calculated by PDCalibration
719 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_dspacing", m_peakPositionTable);
720 diagnosticGroup->addWorkspace(m_peakPositionTable);
721 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_width", m_peakWidthTable);
722 diagnosticGroup->addWorkspace(m_peakWidthTable);
723 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_height", m_peakHeightTable);
724 diagnosticGroup->addWorkspace(m_peakHeightTable);
725 API::AnalysisDataService::Instance().addOrReplace(diagnostic_prefix + "_resolution", resolutionWksp);
726 diagnosticGroup->addWorkspace(resolutionWksp);
727 setProperty("DiagnosticWorkspaces", diagnosticGroup);
728}
729
730namespace { // anonymous namespace
738double gsl_costFunction(const gsl_vector *v, void *peaks) {
739 // this array is [numPeaks, numParams, vector<tof>, vector<dspace>,
740 // vector<weights>]
741 // index as [0, 1, 2, , 2+n , 2+2n]
742 const std::vector<double> *peakVec = reinterpret_cast<std::vector<double> *>(peaks);
743 // number of peaks being fit
744 const auto numPeaks = static_cast<size_t>(peakVec->at(0));
745 // number of parameters
746 const auto numParams = static_cast<size_t>(peakVec->at(1));
747
748 // isn't strictly necessary, but makes reading the code much easier
749 const std::vector<double> tofObs(peakVec->begin() + 2, peakVec->begin() + 2 + numPeaks);
750 const std::vector<double> dspace(peakVec->begin() + (2 + numPeaks), peakVec->begin() + (2 + 2 * numPeaks));
751 const std::vector<double> weights(peakVec->begin() + (2 + 2 * numPeaks), peakVec->begin() + (2 + 3 * numPeaks));
752
753 // create the function to convert tof to dspacing
754 double difc = gsl_vector_get(v, 0);
755 double tzero = 0.;
756 double difa = 0.;
757 if (numParams > 1) {
758 tzero = gsl_vector_get(v, 1);
759 if (numParams > 2)
760 difa = gsl_vector_get(v, 2);
761 }
763 dSpacingUnit.initialize(-1., 0,
767
768 // calculate the sum of the residuals from observed peaks
769 double errsum = 0.0;
770 for (size_t i = 0; i < numPeaks; ++i) {
771 const double tofCalib = dSpacingUnit.singleToTOF(dspace[i]);
772 const double errsum_i = std::pow(tofObs[i] - tofCalib, 2) * weights[i];
773 errsum += errsum_i;
774 }
775
776 return errsum;
777}
778
797double fitDIFCtZeroDIFA(std::vector<double> &peaks, double &difc, double &t0, double &difa) {
798 const auto numParams = static_cast<size_t>(peaks[1]);
799
800 // initial starting point as [DIFC, 0, 0]
801 gsl_vector *fitParams = gsl_vector_alloc(numParams);
802 gsl_vector_set_all(fitParams, 0.0); // set all parameters to zero
803 gsl_vector_set(fitParams, 0, difc);
804 if (numParams > 1) {
805 gsl_vector_set(fitParams, 1, t0);
806 if (numParams > 2) {
807 gsl_vector_set(fitParams, 2, difa);
808 }
809 }
810
811 // Set initial step sizes
812 gsl_vector *stepSizes = gsl_vector_alloc(numParams);
813 gsl_vector_set_all(stepSizes, 0.1);
814 gsl_vector_set(stepSizes, 0, 0.01);
815
816 // Initialize method and iterate
817 gsl_multimin_function minex_func;
818 minex_func.n = numParams;
819 minex_func.f = &gsl_costFunction;
820 minex_func.params = &peaks;
821
822 // Set up GSL minimzer - simplex is overkill
823 const gsl_multimin_fminimizer_type *minimizerType = gsl_multimin_fminimizer_nmsimplex2;
824 gsl_multimin_fminimizer *minimizer = gsl_multimin_fminimizer_alloc(minimizerType, numParams);
825 gsl_multimin_fminimizer_set(minimizer, &minex_func, fitParams, stepSizes);
826
827 // Finally do the fitting
828 size_t iter = 0; // number of iterations
829 const size_t MAX_ITER = 75 * numParams;
830 int status = 0;
831 do {
832 iter++;
833 status = gsl_multimin_fminimizer_iterate(minimizer);
834 if (status)
835 break;
836
837 double size = gsl_multimin_fminimizer_size(minimizer);
838 status = gsl_multimin_test_size(size, 1e-4);
839
840 } while (status == GSL_CONTINUE && iter < MAX_ITER);
841
842 // only update calibration values on successful fit
843 double errsum = 0.; // return 0. if fit didn't work
844 std::string status_msg = gsl_strerror(status);
845 if (status_msg == "success") {
846 difc = gsl_vector_get(minimizer->x, 0);
847 if (numParams > 1) {
848 t0 = gsl_vector_get(minimizer->x, 1);
849 if (numParams > 2) {
850 difa = gsl_vector_get(minimizer->x, 2);
851 }
852 }
853 // return from gsl_costFunction can be accessed as fval
854 errsum = minimizer->fval;
855 }
856
857 // free memory
858 gsl_vector_free(fitParams);
859 gsl_vector_free(stepSizes);
860 gsl_multimin_fminimizer_free(minimizer);
861 return errsum;
862}
863
864} // end of anonymous namespace
865
880void PDCalibration::fitDIFCtZeroDIFA_LM(const std::vector<double> &d, const std::vector<double> &tof,
881 const std::vector<double> &weights, double &difc, double &t0, double &difa) {
882 const size_t numPeaks = d.size();
883 if (numPeaks <= 1) {
884 return; // don't do anything
885 }
886 // number of fit parameters 1=[DIFC], 2=[DIFC,TZERO], 3=[DIFC,TZERO,DIFA]
887 // set the maximum number of parameters that will be used
888 // statistics doesn't support having too few peaks
889 size_t maxParams = std::min<size_t>(numPeaks - 1, m_numberMaxParams);
890
891 // this must have the same layout as the unpacking in gsl_costFunction above
892 // `peaks` has the following structure for a fit session with three peaks
893 // and two fit parameters:
894 // 3, 2, tof1, tof2, tof3, d1, d2, d3, h21, h22, h23
895 std::vector<double> peaks(numPeaks * 3 + 2, 0.);
896 peaks[0] = static_cast<double>(d.size());
897 peaks[1] = 1.; // number of parameters to fit. Initialize to just one
898 for (size_t i = 0; i < numPeaks; ++i) {
899 peaks[i + 2] = tof[i];
900 peaks[i + 2 + numPeaks] = d[i];
901 peaks[i + 2 + 2 * numPeaks] = weights[i];
902 }
903
904 // calculate a starting DIFC
905 double difc_start = difc;
906 if (difc_start == 0.) {
907 const double d_sum = std::accumulate(d.begin(), d.end(), 0.);
908 const double tof_sum = std::accumulate(tof.begin(), tof.end(), 0.);
909 difc_start = tof_sum / d_sum; // number of peaks falls out of division
910 }
911
912 // save the best values so far
913 double best_errsum = std::numeric_limits<double>::max();
914 double best_difc = difc_start;
915 double best_t0 = 0.;
916 double best_difa = 0.;
917
918 // loop over possible number of parameters, doing up to three sequential fits.
919 // We first start with equation TOF = DIFC * d and obtain
920 // optimized DIFC which serves as initial guess for next fit with equation
921 // TOF = DIFC * d + TZERO, obtaining optimized DIFC and TZERO which serve as
922 // initial guess for final fit TOF = DIFC * d + DIFA * d^2 + TZERO.
923 for (size_t numParams = 1; numParams <= maxParams; ++numParams) {
924 peaks[1] = static_cast<double>(numParams);
925 double difc_local = best_difc;
926 double t0_local = best_t0;
927 double difa_local = best_difa;
928 double errsum = fitDIFCtZeroDIFA(peaks, difc_local, t0_local, difa_local);
929 if (errsum > 0.) {
930 // normalize by degrees of freedom
931 errsum = errsum / static_cast<double>(numPeaks - numParams);
932 // save the best and forget the rest
933 // the selected (DIFC, DIFA, TZERO) correspond to those of the fit that
934 // minimizes errsum. It doesn't have to be the last fit.
935 if (errsum < best_errsum) {
936 if (difa_local > m_difaMax || difa_local < m_difaMin)
937 continue; // unphysical fit
938 if (t0_local > m_tzeroMax || t0_local < m_tzeroMin)
939 continue; // unphysical fit
940 best_errsum = errsum;
941 best_difc = difc_local;
942 best_t0 = t0_local;
943 best_difa = difa_local;
944 }
945 }
946 }
947
948 difc = best_difc;
949 // check that something actually fit and set to the best result
950 if (best_difc > 0. && best_errsum < std::numeric_limits<double>::max()) {
951 t0 = best_t0;
952 difa = best_difa;
953 }
954}
955
965vector<double> PDCalibration::dSpacingWindows(const std::vector<double> &centres,
966 const std::vector<double> &windows_in) {
967
968 const std::size_t numPeaks = centres.size();
969
970 // assumes distance between peaks can be used for window sizes
971 assert(numPeaks >= 2);
972
973 vector<double> windows_out(2 * numPeaks);
974 double left;
975 double right;
976 for (std::size_t i = 0; i < numPeaks; ++i) {
977 if (windows_in.size() == 1) {
978 left = centres[i] - windows_in[0];
979 right = centres[i] + windows_in[0];
980 } else {
981 left = windows_in[2 * i];
982 right = windows_in[2 * i + 1];
983 }
984 // check boundaries don't exceed half dist to adjacent peaks
985 if (i > 0) {
986 left = std::max(left, centres[i] - 0.5 * (centres[i] - centres[i - 1]));
987 }
988 if (i < numPeaks - 1) {
989 right = std::min(right, centres[i] + 0.5 * (centres[i + 1] - centres[i]));
990 }
991 // set the windows
992 windows_out[2 * i] = left;
993 windows_out[2 * i + 1] = right;
994 }
995 return windows_out;
996}
997
1005std::tuple<double, double, double> PDCalibration::getDSpacingToTof(const std::set<detid_t> &detIds) {
1006 // to start this is the old calibration values
1007 double difc = 0.;
1008 double difa = 0.;
1009 double tzero = 0.;
1010 for (auto detId : detIds) {
1011 auto rowNum = m_detidToRow[detId];
1012 difc += m_calibrationTable->getRef<double>("difc", rowNum);
1013 difa += m_calibrationTable->getRef<double>("difa", rowNum);
1014 tzero += m_calibrationTable->getRef<double>("tzero", rowNum);
1015 }
1016 if (detIds.size() > 1) {
1017 double norm = 1. / static_cast<double>(detIds.size());
1018 difc = norm * difc;
1019 difa = norm * difa;
1020 tzero = norm * tzero;
1021 }
1022
1023 return {difc, difa, tzero};
1024}
1025
1026void PDCalibration::setCalibrationValues(const detid_t detid, const double difc, const double difa,
1027 const double tzero) {
1028
1029 auto rowNum = m_detidToRow[detid];
1030
1031 // detid is already there
1032 m_calibrationTable->cell<double>(rowNum, 1) = difc;
1033 m_calibrationTable->cell<double>(rowNum, 2) = difa;
1034 m_calibrationTable->cell<double>(rowNum, 3) = tzero;
1035
1036 size_t hasDasIdsOffset = 0; // because it adds a column
1037 if (m_hasDasIds)
1038 hasDasIdsOffset++;
1039
1040 const auto tofMinMax = getTOFminmax(difc, difa, tzero);
1041 m_calibrationTable->cell<double>(rowNum, 4 + hasDasIdsOffset) = tofMinMax[0];
1042 m_calibrationTable->cell<double>(rowNum, 5 + hasDasIdsOffset) = tofMinMax[1];
1043}
1044
1053vector<double> PDCalibration::getTOFminmax(const double difc, const double difa, const double tzero) {
1054 vector<double> tofminmax(2);
1055
1056 Kernel::Units::dSpacing dSpacingUnit;
1057 tofminmax[0] = dSpacingUnit.calcTofMin(difc, difa, tzero, m_tofMin);
1058 tofminmax[1] = dSpacingUnit.calcTofMax(difc, difa, tzero, m_tofMax);
1059
1060 return tofminmax;
1061}
1062MatrixWorkspace_sptr PDCalibration::load(const std::string &filename) {
1063 // TODO this assumes that all files are event-based
1064 const double maxChunkSize = getProperty("MaxChunkSize");
1065 const double filterBadPulses = getProperty("FilterBadPulses");
1066
1067 auto alg = createChildAlgorithm("LoadEventAndCompress");
1068 alg->setLoggingOffset(1);
1069 alg->setProperty("Filename", filename);
1070 alg->setProperty("MaxChunkSize", maxChunkSize);
1071 alg->setProperty("FilterByTofMin", m_tofMin);
1072 alg->setProperty("FilterByTofMax", m_tofMax);
1073 alg->setProperty("FilterBadPulses", filterBadPulses);
1074 alg->setProperty("LoadMonitors", false);
1075 alg->executeAsChildAlg();
1076 API::Workspace_sptr workspace = alg->getProperty("OutputWorkspace");
1077
1078 return std::dynamic_pointer_cast<MatrixWorkspace>(workspace);
1079}
1080
1083 m_uncalibratedWS = getProperty("InputWorkspace");
1084 return rebin(m_uncalibratedWS);
1085}
1086
1088 g_log.information("Binning data in time-of-flight");
1089 auto rebin = createChildAlgorithm("Rebin");
1090 rebin->setLoggingOffset(1);
1091 rebin->setProperty("InputWorkspace", wksp);
1092 rebin->setProperty("OutputWorkspace", wksp);
1093 rebin->setProperty("Params", getPropertyValue("TofBinning"));
1094 rebin->setProperty("PreserveEvents", true);
1095 rebin->executeAsChildAlg();
1096 wksp = rebin->getProperty("OutputWorkspace");
1097
1098 return wksp;
1099}
1100
1115 API::ITableWorkspace_sptr calibrationTableOld = getProperty("PreviousCalibrationTable");
1116 if (calibrationTableOld == nullptr) {
1117 // load from file
1118 std::string filename = getProperty("PreviousCalibrationFile");
1119 auto alg = createChildAlgorithm("LoadDiffCal");
1120 alg->setLoggingOffset(1);
1121 alg->setProperty("Filename", filename);
1122 alg->setProperty("WorkspaceName", "NOMold"); // TODO
1123 alg->setProperty("MakeGroupingWorkspace", false);
1124 alg->setProperty("MakeMaskWorkspace", false);
1125 alg->setProperty("TofMin", m_tofMin);
1126 alg->setProperty("TofMax", m_tofMax);
1127 alg->executeAsChildAlg();
1128 calibrationTableOld = alg->getProperty("OutputCalWorkspace");
1129 }
1130
1131 m_hasDasIds = hasDasIDs(calibrationTableOld);
1132
1133 // generate the map of detid -> row
1134 API::ColumnVector<int> detIDs = calibrationTableOld->getVector("detid");
1135 const size_t numDets = detIDs.size();
1136 for (size_t i = 0; i < numDets; ++i) {
1137 m_detidToRow[static_cast<detid_t>(detIDs[i])] = i;
1138 }
1139
1140 // create a new workspace
1141 m_calibrationTable = std::make_shared<DataObjects::TableWorkspace>();
1142 // TODO m_calibrationTable->setTitle("");
1143 m_calibrationTable->addColumn("int", "detid");
1144 m_calibrationTable->addColumn("double", "difc");
1145 m_calibrationTable->addColumn("double", "difa");
1146 m_calibrationTable->addColumn("double", "tzero");
1147 if (m_hasDasIds)
1148 m_calibrationTable->addColumn("int", "dasid");
1149 m_calibrationTable->addColumn("double", "tofmin");
1150 m_calibrationTable->addColumn("double", "tofmax");
1151
1152 // copy over the values
1153 for (std::size_t rowNum = 0; rowNum < calibrationTableOld->rowCount(); ++rowNum) {
1154 API::TableRow newRow = m_calibrationTable->appendRow();
1155
1156 newRow << calibrationTableOld->getRef<int>("detid", rowNum);
1157 newRow << calibrationTableOld->getRef<double>("difc", rowNum);
1158 newRow << calibrationTableOld->getRef<double>("difa", rowNum);
1159 newRow << calibrationTableOld->getRef<double>("tzero", rowNum);
1160 if (m_hasDasIds)
1161 newRow << calibrationTableOld->getRef<int>("dasid", rowNum);
1162
1163 // adjust tofmin and tofmax for this pixel
1164 const auto tofMinMax = getTOFminmax(calibrationTableOld->getRef<double>("difc", rowNum),
1165 calibrationTableOld->getRef<double>("difa", rowNum),
1166 calibrationTableOld->getRef<double>("tzero", rowNum));
1167 newRow << tofMinMax[0]; // tofmin
1168 newRow << tofMinMax[1]; // tofmax
1169 }
1170}
1171
1179 // create new calibraion table for when an old one isn't loaded
1180 // using the signal workspace and CalculateDIFC
1181 auto alg = createChildAlgorithm("CalculateDIFC");
1182 alg->setLoggingOffset(1);
1183 alg->setProperty("InputWorkspace", m_uncalibratedWS);
1184 alg->executeAsChildAlg();
1185 API::MatrixWorkspace_const_sptr difcWS = alg->getProperty("OutputWorkspace");
1186
1187 // create a new workspace
1188 m_calibrationTable = std::make_shared<DataObjects::TableWorkspace>();
1189 // TODO m_calibrationTable->setTitle("");
1190 m_calibrationTable->addColumn("int", "detid");
1191 m_calibrationTable->addColumn("double", "difc");
1192 m_calibrationTable->addColumn("double", "difa");
1193 m_calibrationTable->addColumn("double", "tzero");
1194 m_hasDasIds = false;
1195 m_calibrationTable->addColumn("double", "tofmin");
1196 m_calibrationTable->addColumn("double", "tofmax");
1197 setProperty("OutputCalibrationTable", m_calibrationTable);
1198
1199 const detid2index_map allDetectors = difcWS->getDetectorIDToWorkspaceIndexMap(false);
1200
1201 // copy over the values
1202 auto it = allDetectors.begin();
1203 size_t i = 0;
1204 for (; it != allDetectors.end(); ++it) {
1205 const detid_t detID = it->first;
1206 m_detidToRow[detID] = i++;
1207 const size_t wi = it->second;
1208 API::TableRow newRow = m_calibrationTable->appendRow();
1209 newRow << detID;
1210 newRow << difcWS->y(wi)[0];
1211 newRow << 0.; // difa
1212 newRow << 0.; // tzero
1213 newRow << 0.; // tofmin
1214 newRow << DBL_MAX; // tofmax
1215 }
1216}
1217
1224 // table for the fitted location of the various peaks, in d-spacing units
1225 m_peakPositionTable = std::make_shared<DataObjects::TableWorkspace>();
1226 m_peakWidthTable = std::make_shared<DataObjects::TableWorkspace>();
1227 m_peakHeightTable = std::make_shared<DataObjects::TableWorkspace>();
1228
1229 m_peakPositionTable->addColumn("int", "detid");
1230 m_peakWidthTable->addColumn("int", "detid");
1231 m_peakHeightTable->addColumn("int", "detid");
1232
1233 for (double dSpacing : m_peaksInDspacing) {
1234 std::stringstream namess;
1235 namess << "@" << std::setprecision(5) << dSpacing;
1236 m_peakPositionTable->addColumn("double", namess.str());
1237 m_peakWidthTable->addColumn("double", namess.str());
1238 m_peakHeightTable->addColumn("double", namess.str());
1239 }
1240 m_peakPositionTable->addColumn("double", "chisq");
1241 m_peakPositionTable->addColumn("double", "normchisq");
1242 // residuals aren't needed for FWHM or height
1243
1244 // convert the map of m_detidToRow to be a vector of detector ids
1245 std::vector<detid_t> detIds(m_detidToRow.size());
1246 for (const auto &it : m_detidToRow) {
1247 detIds[it.second] = it.first;
1248 }
1249
1250 // copy the detector ids from the main table and add lots of NaNs
1251 for (const auto &detId : detIds) {
1252 API::TableRow newPosRow = m_peakPositionTable->appendRow();
1253 API::TableRow newWidthRow = m_peakWidthTable->appendRow();
1254 API::TableRow newHeightRow = m_peakHeightTable->appendRow();
1255
1256 newPosRow << detId;
1257 newWidthRow << detId;
1258 newHeightRow << detId;
1259
1260 for (double dSpacing : m_peaksInDspacing) {
1261 UNUSED_ARG(dSpacing);
1262 newPosRow << std::nan("");
1263 newWidthRow << std::nan("");
1264 newHeightRow << std::nan("");
1265 }
1266 }
1267}
1268
1271 std::make_shared<DataObjects::SpecialWorkspace2D>(m_uncalibratedWS->getInstrument());
1272 resolutionWksp->setTitle("average width/height");
1273
1274 // assume both tables have the same number of rows b/c the algorithm created
1275 // both
1276 // they are also in the same order
1277 // accessing cells is done by (row, col)
1278 const size_t numRows = m_peakPositionTable->rowCount();
1279 const size_t numPeaks = m_peaksInDspacing.size();
1280 std::vector<double> resolution; // vector of non-nan resolutions
1281 for (size_t rowIndex = 0; rowIndex < numRows; ++rowIndex) {
1282 resolution.clear();
1283 // first column is detid
1284 const auto detId = static_cast<detid_t>(m_peakPositionTable->Int(rowIndex, 0));
1285 for (size_t peakIndex = 1; peakIndex < numPeaks + 1; ++peakIndex) {
1286 const double pos = m_peakPositionTable->Double(rowIndex, peakIndex);
1287 if (std::isnormal(pos)) {
1288 resolution.emplace_back(m_peakWidthTable->Double(rowIndex, peakIndex) / pos);
1289 }
1290 }
1291 if (resolution.empty()) {
1292 resolutionWksp->setValue(detId, 0., 0.); // instview doesn't like nan
1293 } else {
1294 // calculate the mean
1295 const double mean =
1296 std::accumulate(resolution.begin(), resolution.end(), 0.) / static_cast<double>(resolution.size());
1297 double stddev = 0.;
1298 std::for_each(resolution.cbegin(), resolution.cend(),
1299 [&stddev, mean](const auto value) { stddev += (value - mean) * (value * mean); });
1300 stddev = std::sqrt(stddev / static_cast<double>(resolution.size() - 1));
1301 resolutionWksp->setValue(detId, mean, stddev);
1302 }
1303 }
1304
1305 return resolutionWksp;
1306}
1307
1310 auto alg = createChildAlgorithm("SortTableWorkspace");
1311 alg->setLoggingOffset(1);
1312 alg->setProperty("InputWorkspace", table);
1313 alg->setProperty("OutputWorkspace", table);
1314 alg->setProperty("Columns", "detid");
1315 alg->executeAsChildAlg();
1316 table = alg->getProperty("OutputWorkspace");
1317
1318 return table;
1319}
1320
1335std::pair<API::MatrixWorkspace_sptr, API::MatrixWorkspace_sptr>
1337 const std::vector<double> &peakWindow) {
1338
1339 // calculate fitting ranges to the left and right of each nominal peak
1340 // center, in d-spacing units
1341 const auto windowsInDSpacing = dSpacingWindows(m_peaksInDspacing, peakWindow);
1342
1343 for (std::size_t i = 0; i < m_peaksInDspacing.size(); ++i) {
1344 g_log.information() << "[" << i << "] " << windowsInDSpacing[2 * i] << " < " << m_peaksInDspacing[i] << " < "
1345 << windowsInDSpacing[2 * i + 1] << std::endl;
1346 }
1347
1348 // create workspaces for nominal peak centers and fit ranges
1349 size_t numspec = dataws->getNumberHistograms();
1350 size_t numpeaks = m_peaksInDspacing.size();
1351 MatrixWorkspace_sptr peak_pos_ws = create<Workspace2D>(numspec, Points(numpeaks));
1352 MatrixWorkspace_sptr peak_window_ws = create<Workspace2D>(numspec, Points(numpeaks * 2));
1353
1354 const auto NUM_HIST = static_cast<int64_t>(dataws->getNumberHistograms());
1355 API::Progress prog(this, 0., .2, NUM_HIST);
1356
1357 PRAGMA_OMP(parallel for schedule(dynamic, 1) )
1358 for (int64_t iws = 0; iws < static_cast<int64_t>(NUM_HIST); ++iws) {
1360 // calculatePositionWindowInTOF
1361 PDCalibration::FittedPeaks peaks(dataws, static_cast<size_t>(iws));
1362 // toTof is a function that converts from d-spacing to TOF for a particular
1363 // pixel
1364 auto [difc, difa, tzero] = getDSpacingToTof(peaks.detid);
1365 // setpositions initializes peaks.inTofPos and peaks.inTofWindows
1366 peaks.setPositions(m_peaksInDspacing, windowsInDSpacing, difa, difc, tzero);
1367 peak_pos_ws->setPoints(iws, peaks.inTofPos);
1368 peak_window_ws->setPoints(iws, peaks.inTofWindows);
1369 prog.report();
1370
1372 }
1374
1375 return std::make_pair(peak_pos_ws, peak_window_ws);
1376}
1377
1378} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double value
The value of the point.
Definition: FitMW.cpp:51
double height
Definition: GetAllEi.cpp:155
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
double left
Definition: LineProfile.cpp:80
double right
Definition: LineProfile.cpp:81
#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...
Definition: MultiThreaded.h:94
#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:64
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
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.
Definition: FileProperty.h:42
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Definition: FileProperty.h:53
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
Definition: PDCalibration.h:63
std::map< detid_t, size_t > m_detidToRow
Definition: PDCalibration.h:66
API::ITableWorkspace_sptr m_peakHeightTable
Definition: PDCalibration.h:64
void setCalibrationValues(const detid_t detid, const double difc, const double difa, const double tzero)
API::MatrixWorkspace_sptr m_uncalibratedWS
Definition: PDCalibration.h:60
API::MatrixWorkspace_sptr rebin(API::MatrixWorkspace_sptr wksp)
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
Definition: PDCalibration.h:61
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....
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,...
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
Definition: PDCalibration.h:62
std::vector< double > m_peaksInDspacing
Definition: PDCalibration.h:65
void exec() override
Execute the algorithm.
void createCalTableFromExisting()
Read a calibration table workspace provided by user, or load from a file provided by User.
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.
Definition: ArrayProperty.h:28
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...
Definition: ListValidator.h:29
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
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.
Definition: ProgressBase.h:51
Validator to check the format of a vector providing the rebin parameters to an algorithm.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
void toTOF(std::vector< double > &xdata, std::vector< double > &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:147
void initialize(const double &_l1, const int &_emode, const UnitParametersMap &params)
Initialize the unit to perform conversion using singleToTof() and singleFromTof()
Definition: Unit.cpp:132
d-Spacing in Angstrom
Definition: Unit.h:383
double calcTofMax(const double difc, const double difa, const double tzero, const double tofmax=0.)
Definition: Unit.cpp:761
double calcTofMin(const double difc, const double difa, const double tzero, const double tofmin=0.)
Definition: Unit.cpp:754
double singleFromTOF(const double tof) const override
DIFA * d^2 + DIFC * d + T0 - TOF = 0.
Definition: Unit.cpp:675
double singleToTOF(const double x) const override
Convert a single X value to TOF.
Definition: Unit.cpp:649
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
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< MaskWorkspace > MaskWorkspace_sptr
shared pointer to the MaskWorkspace class
Definition: MaskWorkspace.h:64
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
int32_t detid_t
Typedef for a detector ID.
Definition: SpectrumInfo.h:21
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:43
Describes the direction (within an algorithm) of a Property.
Definition: Property.h:50
@ InOut
Both an input & output workspace.
Definition: Property.h:55
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54