Mantid
Loading...
Searching...
No Matches
GetEi2.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 +
8
12#include "MantidAPI/Run.h"
23
24#include <algorithm>
25#include <boost/lexical_cast.hpp>
26#include <cmath>
27#include <sstream>
28#include <utility>
29
30using namespace Mantid::Kernel;
31using namespace Mantid::API;
32using namespace Mantid::Geometry;
33
34namespace Mantid::Algorithms {
35
36// Register the algorithm into the algorithm factory
38
39
43 : Algorithm(), m_input_ws(), m_peak1_pos(0, 0.0), m_fixedei(false), m_tof_window(0.1), m_peak_signif(2.0),
44 m_peak_deriv(1.0), m_binwidth_frac(1.0 / 12.0), m_bkgd_frac(0.5) {
45 // Conversion factor common for converting between micro seconds and energy in
46 // meV
48}
49
51
52{ // Declare required input parameters for algorithm and do some validation here
53 auto validator = std::make_shared<CompositeValidator>();
54 validator->add<WorkspaceUnitValidator>("TOF");
55 validator->add<HistogramValidator>();
56 validator->add<InstrumentValidator>();
57
58 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::InOut, validator),
59 "The X units of this workspace must be time of flight with times in\n"
60 "microseconds");
61 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
62 mustBePositive->setLower(0);
63 declareProperty("Monitor1Spec", EMPTY_INT(), mustBePositive,
64 "The spectrum number of the output of the first monitor, "
65 "e.g. MAPS 41474, MARI 2, MERLIN 69634.\n If empty, it will "
66 "be read from the instrument file.\n");
67 declareProperty("Monitor2Spec", EMPTY_INT(), mustBePositive,
68 "The spectrum number of the output of the second monitor "
69 "e.g. MAPS 41475, MARI 3, MERLIN 69638.\n If empty, it will "
70 "be read from the instrument file.\n");
71 auto positiveDouble = std::make_shared<BoundedValidator<double>>();
72 positiveDouble->setLower(0.0);
73 declareProperty("EnergyEstimate", EMPTY_DBL(), positiveDouble,
74 "An approximate value for the typical incident energy, energy of\n"
75 "neutrons leaving the source (meV)\n Can be empty if there is a Sample "
76 "Log called EnergyRequest. Otherwise it is mandatory.");
77 declareProperty("FixEi", false,
78 "If true, the incident energy will be set to the value of the \n"
79 "EnergyEstimate property.");
80
81 declareProperty("IncidentEnergy", -1.0, "The energy of neutron in meV, it is also printed to the Mantid's log",
83
84 declareProperty("FirstMonitorPeak", -1.0,
85 "The time in :math:`\\rm{\\mu s}` when the count rate of the "
86 "first monitor, which defaults to the last monitor the beam "
87 "hits before the sample, is greatest. It is the mean X value "
88 "for the bin with the highest number of counts per second "
89 "and is also writen to Mantid's log.",
91
92 declareProperty("FirstMonitorIndex", 0, "The workspace index of the first monitor in the input workspace.",
94
95 declareProperty("Tzero", 0.0, "", Direction::Output);
96
97 auto inRange0toOne = std::make_shared<BoundedValidator<double>>();
98 inRange0toOne->setLower(0.0);
99 inRange0toOne->setUpper(1.0);
100 declareProperty("PeakSearchRange", 0.1, inRange0toOne,
101 "Specifies the relative TOF range where the algorithm tries "
102 "to find the monitor peak. Search occurs within "
103 "PEAK_TOF_Guess * (1 +/- PeakSearchRange) ranges.\n"
104 "Defaults are almost always sufficient but decrease this "
105 "value for very narrow peaks and increase for wide.",
107}
108
119 m_input_ws = getProperty("InputWorkspace");
120 m_fixedei = getProperty("FixEi");
121 m_tof_window = getProperty("PeakSearchRange");
122
123 double initial_guess = getProperty("EnergyEstimate");
124 // check if incident energy guess is left empty, and try to find it as
125 // EnergyRequest parameter
126 if (initial_guess == EMPTY_DBL()) {
127 if (m_input_ws->run().hasProperty("EnergyRequest")) {
128 initial_guess = m_input_ws->getLogAsSingleValue("EnergyRequest");
129 } else {
130 throw std::invalid_argument("Could not find an energy guess");
131 }
132 }
133 double incident_energy = calculateEi(initial_guess);
134
135 storeEi(incident_energy);
136
137 setProperty("InputWorkspace", m_input_ws);
138 // Output properties
139 setProperty("IncidentEnergy", incident_energy);
140 setProperty("FirstMonitorIndex", m_peak1_pos.first);
141 setProperty("FirstMonitorPeak", m_peak1_pos.second);
142}
143
149double GetEi2::calculateEi(const double initial_guess) {
150 const specnum_t monitor1_spec = getProperty("Monitor1Spec");
151 const specnum_t monitor2_spec = getProperty("Monitor2Spec");
152 specnum_t mon1 = monitor1_spec, mon2 = monitor2_spec;
153
154 const auto &componentInfo = m_input_ws->componentInfo();
155 // Searched from the instrument's first component, so that parameters on it or on the instrument are found
156 const auto firstComponent = componentInfo.children(componentInfo.root()).front();
157
158 // If we have a t0 formula, calculate t0 and return the initial guess
159 if (componentInfo.hasParameter(firstComponent, "t0_formula")) {
160 std::string formula = componentInfo.getParameterAsString(firstComponent, "t0_formula");
161 std::stringstream guess;
162 guess << initial_guess;
163
164 while (formula.find("incidentEnergy") != std::string::npos) {
165 // check if more than one 'value' in m_eq
166 size_t found = formula.find("incidentEnergy");
167 formula.replace(found, 14, guess.str());
168 }
169
170 try {
171 mu::Parser p;
172 p.SetExpr(formula);
173 double tzero = p.Eval();
174 setProperty("Tzero", tzero);
175
176 g_log.debug() << "T0 = " << tzero << '\n';
177 return initial_guess;
178 } catch (mu::Parser::exception_type &e) {
180 "Equation attribute for t0 formula. Muparser error message is: " + e.GetMsg());
181 }
182 }
183
184 if (mon1 == EMPTY_INT() && componentInfo.hasParameter(firstComponent, "ei-mon1-spec")) {
185 mon1 = boost::lexical_cast<specnum_t>(componentInfo.getParameterAsString(firstComponent, "ei-mon1-spec"));
186 }
187 if (mon2 == EMPTY_INT() && componentInfo.hasParameter(firstComponent, "ei-mon2-spec")) {
188 mon2 = boost::lexical_cast<specnum_t>(componentInfo.getParameterAsString(firstComponent, "ei-mon2-spec"));
189 }
190 if ((mon1 == EMPTY_INT()) || (mon2 == EMPTY_INT())) {
191 throw std::invalid_argument("Could not determine spectrum number to use. Try to set it explicitly");
192 }
193 // Covert spectrum numbers to workspace indices
194 std::vector<specnum_t> spec_nums(2, mon1);
195 spec_nums[1] = mon2;
196 // get the index number of the histogram for the first monitor
197 auto mon_indices = m_input_ws->getIndicesFromSpectra(spec_nums);
198
199 if (mon_indices.size() != 2) {
200 g_log.error() << "Error retrieving monitor spectra from input workspace. "
201 "Check input properties.\n";
202 throw std::runtime_error("Error retrieving monitor spectra spectra from input workspace.");
203 }
204
205 // Calculate actual peak postion for each monitor peak
206 double peak_times[2] = {0.0, 0.0};
207 double det_distances[2] = {0.0, 0.0};
208 const auto &spectrumInfo = m_input_ws->spectrumInfo();
209 for (unsigned int i = 0; i < 2; ++i) {
210 size_t ws_index = mon_indices[i];
211 det_distances[i] = getDistanceFromSource(ws_index, spectrumInfo);
212 const double peak_guess = det_distances[i] * std::sqrt(m_t_to_mev / initial_guess);
213 const double t_min = (1.0 - m_tof_window) * peak_guess;
214 const double t_max = (1.0 + m_tof_window) * peak_guess;
215 g_log.information() << "Time-of-flight window for peak " << (i + 1) << ": tmin = " << t_min
216 << " microseconds, tmax = " << t_max << " microseconds\n";
217 try {
218 peak_times[i] = calculatePeakPosition(ws_index, t_min, t_max);
219 g_log.information() << "Peak for monitor " << (i + 1) << " (at " << det_distances[i]
220 << " metres) = " << peak_times[i] << " microseconds\n";
221 } catch (std::invalid_argument &) {
222
223 if (!m_fixedei) {
224 throw std::invalid_argument("No peak found for the monitor with spectra num: " + std::to_string(spec_nums[i]) +
225 " (at " + boost::lexical_cast<std::string>(det_distances[i]) +
226 " metres from source).\n");
227 } else {
228 peak_times[i] = peak_guess;
229 g_log.warning() << "No peak found for monitor with spectra num " << spec_nums[i] << " (at " << det_distances[i]
230 << " metres).\n";
231 g_log.warning() << "Using guess time found from energy estimate of Peak = " << peak_times[i]
232 << " microseconds\n";
233 }
234 }
235 if (i == 0) {
236 m_peak1_pos = std::make_pair(static_cast<int>(ws_index), peak_times[i]);
237 if (m_fixedei)
238 break;
239 }
240 }
241
242 if (m_fixedei) {
243 g_log.notice() << "Incident energy fixed at Ei=" << initial_guess << " meV\n";
244 return initial_guess;
245 } else {
246 double mean_speed = (det_distances[1] - det_distances[0]) / (peak_times[1] - peak_times[0]);
247 double tzero = peak_times[1] - ((1.0 / mean_speed) * det_distances[1]);
248 g_log.debug() << "T0 = " << tzero << '\n';
249 g_log.debug() << "Mean Speed = " << mean_speed << '\n';
250 setProperty("Tzero", tzero);
251
252 const double energy = mean_speed * mean_speed * m_t_to_mev;
253 g_log.notice() << "Incident energy calculated at Ei= " << energy << " meV from initial guess = " << initial_guess
254 << " meV\n";
255 return energy;
256 }
257}
258
268double GetEi2::getDistanceFromSource(size_t ws_index, const SpectrumInfo &spectrumInfo) const {
269 g_log.debug() << "Computing distance between spectrum at index '" << ws_index << "' and the source\n";
270
271 const auto &detector = spectrumInfo.detector(ws_index);
272 const IComponent_const_sptr source = m_input_ws->getInstrument()->getSource();
273 if (!spectrumInfo.hasDetectors(ws_index)) {
274 std::ostringstream msg;
275 msg << "A detector for monitor at workspace index " << ws_index << " cannot be found. ";
276 throw std::runtime_error(msg.str());
277 }
278 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
279 g_log.debug() << "Detector position = " << spectrumInfo.position(ws_index)
280 << ", Source position = " << source->getPos() << "\n";
281 }
282 const double dist = detector.getDistance(*source);
283 g_log.debug() << "Distance = " << dist << " metres\n";
284 return dist;
285}
286
294double GetEi2::calculatePeakPosition(size_t ws_index, double t_min, double t_max) {
295 // Crop out the current monitor workspace to the min/max times defined
296 MatrixWorkspace_sptr monitor_ws = extractSpectrum(ws_index, t_min, t_max);
297 // Workspace needs to be a count rate for the fitting algorithm
299
300 const double prominence(4.0);
301 std::vector<double> dummyX, dummyY, dummyE;
302 double peak_width = calculatePeakWidthAtHalfHeight(monitor_ws, prominence, dummyX, dummyY, dummyE);
303 monitor_ws = rebin(monitor_ws, t_min, peak_width * m_binwidth_frac, t_max);
304
305 double t_mean(0.0);
306 try {
307 t_mean = calculateFirstMoment(monitor_ws, prominence);
308 } catch (std::runtime_error &) {
309 // Retry with smaller prominence factor
310 t_mean = calculateFirstMoment(monitor_ws, 2.0);
311 }
312
313 return t_mean;
314}
315
329MatrixWorkspace_sptr GetEi2::extractSpectrum(size_t ws_index, const double start, const double end) {
330 auto childAlg = createChildAlgorithm("CropWorkspace");
331 childAlg->setProperty("InputWorkspace", m_input_ws);
332 childAlg->setProperty<int>("StartWorkspaceIndex", static_cast<int>(ws_index));
333 childAlg->setProperty<int>("EndWorkspaceIndex", static_cast<int>(ws_index));
334 childAlg->setProperty("XMin", start);
335 childAlg->setProperty("XMax", end);
336 childAlg->executeAsChildAlg();
337 MatrixWorkspace_sptr monitors = childAlg->getProperty("OutputWorkspace");
338 if (std::dynamic_pointer_cast<API::IEventWorkspace>(m_input_ws)) {
339 // Convert to a MatrixWorkspace representation
340 childAlg = createChildAlgorithm("ConvertToMatrixWorkspace");
341 childAlg->setProperty("InputWorkspace", monitors);
342 childAlg->setProperty("OutputWorkspace", monitors);
343 childAlg->executeAsChildAlg();
344 monitors = childAlg->getProperty("OutputWorkspace");
345 }
346
347 return monitors;
348}
349
363double GetEi2::calculatePeakWidthAtHalfHeight(const API::MatrixWorkspace_sptr &data_ws, const double prominence,
364 std::vector<double> &peak_x, std::vector<double> &peak_y,
365 std::vector<double> &peak_e) const {
366 // Use WS->points() to create a temporary vector of bin_centre values to work
367 // with
368 auto Xs = data_ws->points(0);
369 const auto &Ys = data_ws->y(0);
370 const auto &Es = data_ws->e(0);
371
372 auto peakIt = std::max_element(Ys.cbegin(), Ys.cend());
373 double bkg_val = *std::min_element(Ys.begin(), Ys.end());
374 if (*peakIt == bkg_val) {
375 throw std::invalid_argument("No peak in the range specified as minimal and "
376 "maximal values of the function are equal ");
377 }
378 auto iPeak = peakIt - Ys.begin();
379 double peakY = Ys[iPeak] - bkg_val;
380 double peakE = Es[iPeak];
381
382 const std::vector<double>::size_type nxvals = Xs.size();
383
385 // nearest points that satisfy this
386 auto im = static_cast<int64_t>(iPeak - 1);
387 for (; im >= 0; --im) {
388 const double ratio = (Ys[im] - bkg_val) / peakY;
389 const double ratio_err = std::sqrt(std::pow(Es[im], 2) + std::pow(ratio * peakE, 2)) / peakY;
390 if (ratio < (1.0 / prominence - m_peak_signif * ratio_err)) {
391 break;
392 }
393 }
394
395 std::vector<double>::size_type ip = iPeak + 1;
396 for (; ip < nxvals; ip++) {
397 const double ratio = (Ys[ip] - bkg_val) / peakY;
398 const double ratio_err = std::sqrt(std::pow(Es[ip], 2) + std::pow(ratio * peakE, 2)) / peakY;
399 if (ratio < (1.0 / prominence - m_peak_signif * ratio_err)) {
400 break;
401 }
402 }
403
404 if (ip == nxvals || im < 0) {
405 throw std::invalid_argument("No peak found in data that satisfies prominence criterion");
406 }
407
408 // We now have a peak, so can start filling output arguments
409 // Determine extent of peak using derivatives
410 // At this point 1 =< im < ipk < ip =< size(x)
411 // After this section, new values will be given to im, ip that still satisfy
412 // these inequalities.
413 //
414 // The algorithm for negative side skipped if im=1; positive side skipped if
415 // ip=size(x);
416 // if fails derivative criterion -> ip=size(x) (+ve) im=1 (-ve)
417 // In either case, we deem that the peak has a tail(s) that extend outside the
418 // range of x
419
420 if (ip < nxvals) {
421 double deriv = -1000.0;
422 double error = 0.0;
423 while ((ip < nxvals - 1) && (deriv < -m_peak_deriv * error)) {
424 double dtp = Xs[ip + 1] - Xs[ip];
425 double dtm = Xs[ip] - Xs[ip - 1];
426 deriv = 0.5 * (((Ys[ip + 1] - Ys[ip]) / dtp) + ((Ys[ip] - Ys[ip - 1]) / dtm));
427 error = 0.5 * std::sqrt(((std::pow(Es[ip + 1], 2) + std::pow(Es[ip], 2)) / std::pow(dtp, 2)) +
428 ((std::pow(Es[ip], 2) + std::pow(Es[ip - 1], 2)) / std::pow(dtm, 2)) -
429 2.0 * (std::pow(Es[ip], 2) / (dtp * dtm)));
430 ip++;
431 }
432 ip--;
433
434 if (deriv < -error) {
435 ip = nxvals - 1; // ! derivative criterion not met
436 }
437 }
438
439 if (im > 0) {
440 double deriv = 1000.0;
441 double error = 0.0;
442 while ((im > 0) && (deriv > m_peak_deriv * error)) {
443 double dtp = Xs[im + 1] - Xs[im];
444 double dtm = Xs[im] - Xs[im - 1];
445 deriv = 0.5 * (((Ys[im + 1] - Ys[im]) / dtp) + ((Ys[im] - Ys[im - 1]) / dtm));
446 error = 0.5 * std::sqrt(((std::pow(Es[im + 1], 2) + std::pow(Es[im], 2)) / std::pow(dtp, 2)) +
447 ((std::pow(Es[im], 2) + std::pow(Es[im - 1], 2)) / std::pow(dtm, 2)) -
448 2.0 * std::pow(Es[im], 2) / (dtp * dtm));
449 im--;
450 }
451 im++;
452 if (deriv > error)
453 im = 0; // derivative criterion not met
454 }
455 double pk_min = Xs[im];
456 double pk_max = Xs[ip];
457 double pk_width = Xs[ip] - Xs[im];
458
459 // Determine background from either side of peak.
460 // At this point, im and ip define the extreme points of the peak
461 // Assume flat background
462 double bkgd = 0.0;
463 double bkgd_range = 0.0;
464 double bkgd_min = std::max(Xs.front(), pk_min - m_bkgd_frac * pk_width);
465 double bkgd_max = std::min(Xs.back(), pk_max + m_bkgd_frac * pk_width);
466
467 if (im > 0) {
468 double bkgd_m, bkgd_err_m;
469 integrate(bkgd_m, bkgd_err_m, Xs.rawData(), Ys, Es, bkgd_min, pk_min);
470 bkgd = bkgd + bkgd_m;
471 bkgd_range = bkgd_range + (pk_min - bkgd_min);
472 }
473
474 if (ip < nxvals - 1) {
475 double bkgd_p, bkgd_err_p;
476 integrate(bkgd_p, bkgd_err_p, Xs.rawData(), Ys, Es, pk_max, bkgd_max);
477 bkgd = bkgd + bkgd_p;
478 bkgd_range = bkgd_range + (bkgd_max - pk_max);
479 }
480
481 if (im > 0 || ip < nxvals - 1) // background from at least one side
482 {
483 bkgd = bkgd / bkgd_range;
484 }
485 // Perform moment analysis on the peak after subtracting the background
486 // Fill arrays with peak only:
487 const std::vector<double>::size_type nvalues = ip - im + 1;
488 peak_x.resize(nvalues);
489 std::copy(Xs.begin() + im, Xs.begin() + ip + 1, peak_x.begin());
490 peak_y.resize(nvalues);
491 using std::placeholders::_1;
492 std::transform(Ys.begin() + im, Ys.begin() + ip + 1, peak_y.begin(), std::bind(std::minus<double>(), _1, bkgd));
493 peak_e.resize(nvalues);
494 std::copy(Es.begin() + im, Es.begin() + ip + 1, peak_e.begin());
495
496 // FWHH:
497 int64_t ipk_int = static_cast<int64_t>(iPeak) - im; // ! peak position in internal array
498 double hby2 = 0.5 * peak_y[ipk_int];
499 double xp_hh(0);
500
501 auto nyvals = static_cast<int64_t>(peak_y.size());
502 if (peak_y[nyvals - 1] < hby2) {
503 int64_t ip1(0), ip2(0);
504 // first index after ipk_int where intensity drops below half-height
505 auto it1 = std::find_if(peak_y.cbegin() + ipk_int, peak_y.cend(), [hby2](double const y) { return y < hby2; });
506 if (it1 != peak_y.cend())
507 ip1 = static_cast<int64_t>(std::distance(peak_y.cbegin(), it1)) - 1;
508 // last index (searching backward from end) where intensity is above half-height
509 int64_t const rend2 = nyvals - ipk_int;
510 auto it2 = std::find_if(peak_y.crbegin(), peak_y.crbegin() + rend2, [hby2](double const y) { return y > hby2; });
511 if (it2 != peak_y.crbegin() + rend2)
512 ip2 = nyvals - static_cast<int64_t>(std::distance(peak_y.crbegin(), it2));
513 // A broad peak with many local maxima on the side can cause the algorithm to give the same indices for the two
514 // points (ip1 and ip2) either side of the half-width point. Noise may also result in equal y values for different
515 // ip1 and ip2 points which would cause a divide-by-zero error in the xp_hh calculation below. We know the
516 // algorithm isn't perfect so move one index until the y values are differnt.
517 if (peak_y[ip1] == peak_y[ip2]) {
518 g_log.warning() << "A peak with a local maxima on the trailing edge has "
519 "been found. The estimation of the "
520 << "half-height point will not be as accurate.\n";
521 // Shift the index ip1 until the heights are no longer equal. This avoids the divide-by-zero.
522 while (peak_y[ip1] == peak_y[ip2]) {
523 ip1--;
524 if (ip1 < ipk_int)
525 throw std::invalid_argument("Trailing edge values are equal until up to the peak centre.");
526 }
527 }
528
529 xp_hh = peak_x[ip2] + (peak_x[ip1] - peak_x[ip2]) * ((hby2 - peak_y[ip2]) / (peak_y[ip1] - peak_y[ip2]));
530 } else {
531 xp_hh = peak_x[nyvals - 1];
532 }
533
534 double xm_hh(0);
535 if (peak_y[0] < hby2) {
536 int64_t im1(0), im2(0);
537 // last index (searching backward from ipk_int) where intensity drops below half-height
538 int64_t const rstart3 = nyvals - 1 - ipk_int;
539 auto it3 = std::find_if(peak_y.crbegin() + rstart3, peak_y.crend(), [hby2](double const y) { return y < hby2; });
540 if (it3 != peak_y.crend())
541 im1 = nyvals - static_cast<int64_t>(std::distance(peak_y.crbegin(), it3));
542 // first index from 0 to ipk_int where intensity rises above half-height
543 auto it4 =
544 std::find_if(peak_y.cbegin(), peak_y.cbegin() + ipk_int + 1, [hby2](double const y) { return y > hby2; });
545 if (it4 != peak_y.cbegin() + ipk_int + 1)
546 im2 = static_cast<int64_t>(std::distance(peak_y.cbegin(), it4)) - 1;
547 // A broad peak with many local maxima on the side can cause the algorithm to give the same indices for the two
548 // points (im1 and im2) either side of the half-width point. Noise may also result in equal y values for different
549 // im1 and im2 points which would cause a divide-by-zero error in the xp_hh calculation below. We know the
550 // algorithm isn't perfect so move one index until the y values are differnt.
551 if (peak_y[im1] == peak_y[im2]) {
552 g_log.warning() << "A peak with a local maxima on the rising edge has "
553 "been found. The estimation of the "
554 << "half-height point will not be as accurate.\n";
555 // Shift the index ip1 until the heights are no longer equal. This avoids the divide-by-zero.
556 while (peak_y[im1] == peak_y[im2]) {
557 im1++;
558 if (im1 >= ipk_int)
559 throw std::invalid_argument("The rising edge values are equal up to the peak centre.");
560 }
561 }
562
563 xm_hh = peak_x[im2] + (peak_x[im1] - peak_x[im2]) * ((hby2 - peak_y[im2]) / (peak_y[im1] - peak_y[im2]));
564 } else {
565 xm_hh = peak_x.front();
566 }
567 return (xp_hh - xm_hh);
568}
569
576double GetEi2::calculateFirstMoment(const API::MatrixWorkspace_sptr &monitor_ws, const double prominence) {
577 std::vector<double> peak_x, peak_y, peak_e;
578 calculatePeakWidthAtHalfHeight(monitor_ws, prominence, peak_x, peak_y, peak_e);
579
580 // Area
581 double area(0.0), dummy(0.0);
582 double pk_xmin = peak_x.front();
583 double pk_xmax = peak_x.back();
584 integrate(area, dummy, peak_x, peak_y, peak_e, pk_xmin, pk_xmax);
585 // First moment
586 std::transform(peak_y.begin(), peak_y.end(), peak_x.begin(), peak_y.begin(), std::multiplies<double>());
587 double xbar(0.0);
588 integrate(xbar, dummy, peak_x, peak_y, peak_e, pk_xmin, pk_xmax);
589 return xbar / area;
590}
591
601 const double width, const double end) {
602 auto childAlg = createChildAlgorithm("Rebin");
603 childAlg->setProperty("InputWorkspace", monitor_ws);
604 std::ostringstream binParams;
605 binParams << first << "," << width << "," << end;
606 childAlg->setPropertyValue("Params", binParams.str());
607 childAlg->setProperty("IgnoreBinErrors", true);
608 childAlg->executeAsChildAlg();
609 return childAlg->getProperty("OutputWorkspace");
610}
611
622void GetEi2::integrate(double &integral_val, double &integral_err, const HistogramData::HistogramX &x,
623 const HistogramData::HistogramY &s, const HistogramData::HistogramE &e, const double xmin,
624 const double xmax) const {
625 // MG: Note that this is integration of a point data set from libisis
626 // @todo: Move to Kernel::VectorHelper and improve performance
627
628 auto lowit = std::lower_bound(x.cbegin(), x.cend(), xmin);
629 auto ml = std::distance(x.cbegin(), lowit);
630 auto highit = std::upper_bound(lowit, x.cend(), xmax);
631 auto mu = std::distance(x.cbegin(), highit);
632 if (mu > 0)
633 --mu;
634
635 auto nx(x.size());
636 if (mu < ml) {
637 // special case of no data points in the integration range
638 unsigned int ilo = std::max<unsigned int>(static_cast<unsigned int>(ml) - 1, 0);
639 unsigned int ihi = std::min<unsigned int>(static_cast<unsigned int>(mu) + 1, static_cast<unsigned int>(nx));
640 double fraction = (xmax - xmin) / (x[ihi] - x[ilo]);
641 integral_val =
642 0.5 * fraction * (s[ihi] * ((xmax - x[ilo]) + (xmin - x[ilo])) + s[ilo] * ((x[ihi] - xmax) + (x[ihi] - xmin)));
643 double err_hi = e[ihi] * ((xmax - x[ilo]) + (xmin - x[ilo]));
644 double err_lo = e[ilo] * ((x[ihi] - xmax) + (x[ihi] - xmin));
645 integral_err = 0.5 * fraction * std::sqrt(err_hi * err_hi + err_lo * err_lo);
646 return;
647 }
648
649 double x1eff(0.0), s1eff(0.0), e1eff(0.0);
650 if (ml > 0) {
651 x1eff = (xmin * (xmin - x[ml - 1]) + x[ml - 1] * (x[ml] - xmin)) / (x[ml] - x[ml - 1]);
652 double fraction = (x[ml] - xmin) / ((x[ml] - x[ml - 1]) + (xmin - x[ml - 1]));
653 s1eff = s[ml - 1] * fraction;
654 e1eff = e[ml - 1] * fraction;
655 } else {
656 x1eff = x[ml];
657 s1eff = 0.0;
658 }
659
660 double xneff(0.0), sneff(0.0), eneff;
661 if (mu < static_cast<int>(nx - 1)) {
662 xneff = (xmax * (x[mu + 1] - xmax) + x[mu + 1] * (xmax - x[mu])) / (x[mu + 1] - x[mu]);
663 const double fraction = (xmax - x[mu]) / ((x[mu + 1] - x[mu]) + (x[mu + 1] - xmax));
664 sneff = s[mu + 1] * fraction;
665 eneff = e[mu + 1] * fraction;
666 } else {
667 xneff = x.back();
668 sneff = 0.0;
669 eneff = 0.0; // Make sure to initialize to a value.
670 }
671
672 // xmin -> x[ml]
673 integral_val = (x[ml] - x1eff) * (s[ml] + s1eff);
674 integral_err = e1eff * (x[ml] - x1eff);
675 integral_err *= integral_err;
676
677 if (mu == ml) {
678 double ierr = e[ml] * (xneff - x1eff);
679 integral_err += ierr * ierr;
680 } else if (mu == ml + 1) {
681 integral_val += (s[mu] + s[ml]) * (x[mu] - x[ml]);
682 double err_lo = e[ml] * (x[ml + 1] - x1eff);
683 double err_hi = e[mu] * (x[mu - 1] - xneff);
684 integral_err += err_lo * err_lo + err_hi * err_hi;
685 } else {
686 for (auto i = static_cast<int>(ml); i < mu; ++i) {
687 integral_val += (s[i + 1] + s[i]) * (x[i + 1] - x[i]);
688 if (i < mu - 1) {
689 double ierr = e[i + 1] * (x[i + 2] - x[i]);
690 integral_err += ierr * ierr;
691 }
692 }
693 }
694
695 // x[mu] -> xmax
696 integral_val += (xneff - x[mu]) * (s[mu] + sneff);
697 double err_tmp = eneff * (xneff - x[mu]);
698 integral_err += err_tmp * err_tmp;
699
700 integral_val *= 0.5;
701 integral_err = 0.5 * sqrt(integral_err);
702}
703
708void GetEi2::storeEi(const double ei) const {
709 Property *incident_energy = new PropertyWithValue<double>("Ei", ei, Direction::Input);
710 m_input_ws->mutableRun().addProperty(incident_energy, true);
711}
712} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double energy
Definition GetAllEi.cpp:157
double error
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
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:426
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace has a valid instrument.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
bool hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
Kernel::V3D position(const size_t index) const
Returns the position of the spectrum with given index.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
Requires an estimate for the initial neutron energy which it uses to search for monitor peaks and fro...
Definition GetEi2.h:48
double m_tof_window
The percentage deviation from the estimated peak time that defines the peak region.
Definition GetEi2.h:107
const double m_bkgd_frac
The fraction of the peakwidth to use in calculating background points.
Definition GetEi2.h:115
void integrate(double &integral_val, double &integral_err, const HistogramData::HistogramX &x, const HistogramData::HistogramY &s, const HistogramData::HistogramE &e, const double xmin, const double xmax) const
Integrate the point data.
Definition GetEi2.cpp:622
double getDistanceFromSource(const size_t ws_index, const API::SpectrumInfo &spectrumInfo) const
Get the distance from the source of the detector at the workspace index given.
Definition GetEi2.cpp:268
double m_t_to_mev
Conversion factor between time and energy.
Definition GetEi2.h:104
API::MatrixWorkspace_sptr rebin(const API::MatrixWorkspace_sptr &monitor_ws, const double first, const double width, const double end)
Rebin the given workspace using the given parameters.
Definition GetEi2.cpp:600
void storeEi(const double ei) const
Store the incident energy within the sample object.
Definition GetEi2.cpp:708
double calculateFirstMoment(const API::MatrixWorkspace_sptr &monitor_ws, const double prominence)
Calculate the value of the first moment of the given spectrum.
Definition GetEi2.cpp:576
void exec() override
Execute the algorithm.
Definition GetEi2.cpp:118
double calculatePeakWidthAtHalfHeight(const API::MatrixWorkspace_sptr &data_ws, const double prominence, std::vector< double > &peak_x, std::vector< double > &peak_y, std::vector< double > &peak_e) const
Calculate peak width.
Definition GetEi2.cpp:363
API::MatrixWorkspace_sptr m_input_ws
The input workspace.
Definition GetEi2.h:98
double calculateEi(const double initial_guess)
Calculate Ei from the initial guess given.
Definition GetEi2.cpp:149
void init() override
Initialize the algorithm.
Definition GetEi2.cpp:50
const double m_peak_signif
Number of std deviations to consider a peak.
Definition GetEi2.h:109
std::pair< int, double > m_peak1_pos
The calculated position of the first peak.
Definition GetEi2.h:100
bool m_fixedei
True if the Ei should be fixed at the guess energy.
Definition GetEi2.h:102
const double m_peak_deriv
Number of std deviations to consider a peak for the derivative.
Definition GetEi2.h:111
double calculatePeakPosition(const size_t ws_index, const double t_min, const double t_max)
Calculate the peak position within the given window.
Definition GetEi2.cpp:294
const double m_binwidth_frac
The fraction of the peak width for the new bins.
Definition GetEi2.h:113
API::MatrixWorkspace_sptr extractSpectrum(const size_t ws_index, const double start, const double end)
Extract the required spectrum from the input workspace.
Definition GetEi2.cpp:329
Exception for errors associated with the instrument definition.
Definition Exception.h:220
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
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
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
bool is(int level) const
Returns true if at least the given log level is set.
Definition Logger.cpp:177
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
The concrete, templated class for properties.
Base class for properties.
Definition Property.h:95
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:165
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double meV
1 meV in Joules.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
int32_t specnum_t
Typedef for a spectrum Number.
Definition IDTypes.h:14
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
std::string to_string(const wide_integer< Bits, Signed > &n)
static void makeDistribution(const MatrixWorkspace_sptr &workspace, const bool forwards=true)
Divides the data in a workspace by the bin width to make it a distribution.
@ InOut
Both an input & output workspace.
Definition Property.h:56
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55