Mantid
Loading...
Searching...
No Matches
MayersSampleCorrectionStrategy.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 +
7//-----------------------------------------------------------------------------
8// Includes
9//-----------------------------------------------------------------------------
15#include <cassert>
16#include <cmath>
17
23using std::pow;
24
25namespace {
26
27// The constants were set as default in the original fortran
29size_t N_MUR_PTS = 21;
31size_t N_RAD = 29;
33size_t N_THETA = 29;
35size_t N_POLY_ORDER = 4;
37double TWOPI = 2.0 * M_PI;
38
39// Avoid typing static_casts everywhere
40template <typename R, typename T> inline R to(const T val) { return static_cast<R>(val); }
41
42//-----------------------------------------------------------------------------
43// Utility functions
44//-----------------------------------------------------------------------------
45
52double integrate(const std::vector<double> &y, const double dx) {
53 assert(y.size() > 3);
54 // strictly Simpson's rule says that n should be even but the old fortran
55 // didn't...
56 // sum even and odd terms excluding the front/back
57 double sumEven(0.0), sumOdd(0.0);
58 auto itend = y.end() - 1;
59 for (auto it = y.begin() + 1; it != itend;) {
60 sumOdd += *(it++);
61 if (it == itend)
62 break;
63 sumEven += *(it++);
64 }
65 return dx * (y.front() + 4.0 * sumOdd + 2.0 * sumEven + y.back()) / 3.0;
66}
67} // namespace
68
69namespace Mantid::Algorithms {
70
71//-----------------------------------------------------------------------------
72// Public methods
73//-----------------------------------------------------------------------------
81 HistogramData::Histogram inputHist)
82 : m_pars(std::move(params)), m_histogram(std::move(inputHist)), m_tofVals(m_histogram.points()),
83 m_histoYSize(m_histogram.size()), m_muRrange(calculateMuRange()), m_rng(std::make_unique<MersenneTwister>(1)) {
84
85 const auto &xVals = m_histogram.x();
86 if (!(xVals.front() < xVals.back())) {
87 throw std::invalid_argument("TOF values are expected to be monotonically increasing");
88 }
89}
90
95
103Mantid::HistogramData::Histogram MayersSampleCorrectionStrategy::getCorrectedHisto() {
104
105 // Temporary storage
106 std::vector<double> xmur(N_MUR_PTS + 1, 0.0), yabs(N_MUR_PTS + 1, 1.0), // absorption signals
107 wabs(N_MUR_PTS + 1, 1.0), // absorption weights
108 yms(0), // multiple scattering signals
109 wms(0); // multiple scattering weights
110 if (m_pars.mscat) {
111 yms.resize(N_MUR_PTS + 1, 0.0);
112 wms.resize(N_MUR_PTS + 1, 100.0);
113 }
114
115 // Main loop over mur. Limit is nrpts but vectors are nrpts+1. First value set
116 // by initial values above
117 const double dmuR = (muRmax() - muRmin()) / to<double>(N_MUR_PTS - 1);
118 for (size_t i = 1; i < N_MUR_PTS + 1; ++i) {
119 const double muRValue = muRmin() + to<double>(i - 1) * dmuR;
120 xmur[i] = muRValue;
121
122 auto attenuation = calculateSelfAttenuation(muRValue);
123 const double absFactor = attenuation / (M_PI * muRValue * muRValue);
124 // track these
125 yabs[i] = 1. / absFactor;
126 wabs[i] = absFactor;
127 if (m_pars.mscat) {
128 // ratio of second/first scatter
129 auto mscat = calculateMS(i, muRValue, attenuation);
130 yms[i] = mscat.first;
131 wms[i] = mscat.second;
132 }
133 }
134
135 // Fit polynomials to absorption values to interpolate to input data range
136 ChebyshevPolyFit polyfit(N_POLY_ORDER);
137 auto absCoeffs = polyfit(xmur, yabs, wabs);
138 decltype(absCoeffs) msCoeffs(0);
139 if (m_pars.mscat)
140 msCoeffs = polyfit(xmur, yms, wms);
141
142 // corrections to input
143 const double muMin(xmur.front()), muMax(xmur.back()), flightPath(m_pars.l1 + m_pars.l2);
144 ChebyshevSeries chebyPoly(N_POLY_ORDER);
145
146 auto outputHistogram = m_histogram;
147
148 auto &sigOut = outputHistogram.mutableY();
149 auto &errOut = outputHistogram.mutableE();
150
151 for (size_t i = 0; i < m_histoYSize; ++i) {
152 const double yin(m_histogram.y()[i]), ein(m_histogram.e()[i]);
153 if (yin == 0) {
154 // Detector with 0 signal received - skip this bin
155 continue;
156 }
157
158 const double sigt = sigmaTotal(flightPath, m_tofVals[i]);
159 const double rmu = muR(sigt);
160 // Varies between [-1,+1]
161 const double xcap = ((rmu - muMin) - (muMax - rmu)) / (muMax - muMin);
162 double corrfact = chebyPoly(absCoeffs, xcap);
163 if (m_pars.mscat) {
164 const double msVal = chebyPoly(msCoeffs, xcap);
165 const double beta = m_pars.sigmaSc * msVal / sigt;
166 corrfact *= (1.0 - beta);
167 }
168 // apply correction
169
170 sigOut[i] = yin * corrfact;
171 errOut[i] = sigOut[i] * ein / yin;
172 }
173 return outputHistogram;
174}
175
182 // Integrate over the cylindrical coordinates
183 // Constants for calculation
184 const double dyr = muR / to<double>(N_RAD - 1);
185 const double dyth = TWOPI / to<double>(N_THETA - 1);
186 const double muRSq = muR * muR;
187 const double cosaz = cos(m_pars.azimuth);
188
189 // Store values at each point
190 std::vector<double> yr(N_RAD), yth(N_THETA);
191 for (size_t i = 0; i < N_RAD; ++i) {
192 const double r0 = to<double>(i) * dyr;
193
194 for (size_t j = 0; j < N_THETA; ++j) {
195 const double theta = to<double>(j) * dyth;
196 // distance to vertical axis
197 double fact1 = muRSq - std::pow(r0 * sin(theta), 2);
198 if (fact1 < 0.0)
199 fact1 = 0.0;
200 // + final distance to scatter point
201 const double mul1 = sqrt(fact1) + r0 * cos(theta);
202 // exit distance after scatter
203 double fact2 = muRSq - std::pow(r0 * sin(m_pars.twoTheta - theta), 2);
204 if (fact2 < 0.0)
205 fact2 = 0.0;
206 const double mul2 = (sqrt(fact2) - r0 * cos(m_pars.twoTheta - theta)) / cosaz;
207 yth[j] = exp(-mul1 - mul2);
208 }
209
210 yr[i] = r0 * integrate(yth, dyth);
211 }
212 return integrate(yr, dyr);
213}
214
223std::pair<double, double> MayersSampleCorrectionStrategy::calculateMS(const size_t irp, const double muR,
224 const double abs) {
225 // Radial coordinate raised to power 1/3 to ensure uniform density of points
226 // across circle following discussion with W.G.Marshall (ISIS)
227 const double radDistPower = 1. / 3.;
228 const double muH = muR * (m_pars.cylHeight / m_pars.cylRadius);
229 const double cosaz = cos(m_pars.azimuth);
230 seedRNG(irp);
231
232 // Take an average over a number of sets of second scatters
233 std::vector<double> deltas(m_pars.msNRuns, 0.0);
234 for (size_t j = 0; j < m_pars.msNRuns; ++j) {
235 double sum = 0.0;
236 for (size_t i = 0; i < m_pars.msNEvents; ++i) {
237 // Random (r,theta,z)
238 const double r1 = pow(m_rng->nextValue(), radDistPower) * muR;
239 const double r2 = pow(m_rng->nextValue(), radDistPower) * muR;
240 const double z1 = m_rng->nextValue() * muH;
241 const double z2 = m_rng->nextValue() * muH;
242 const double th1 = m_rng->nextValue() * TWOPI;
243 const double th2 = m_rng->nextValue() * TWOPI;
244 double fact1 = pow(muR, 2) - std::pow(r1 * sin(th1), 2);
245 if (fact1 < 0.0)
246 fact1 = 0.0;
247 // Path into first point
248 const double mul1 = sqrt(fact1) + r1 * cos(th1);
249 double fact2 = pow(muR, 2) - pow(r2 * sin(m_pars.twoTheta - th2), 2);
250 if (fact2 < 0.0)
251 fact2 = 0.0;
252 // Path out from final point
253 const double mul2 = (sqrt(fact2) - r2 * cos(m_pars.twoTheta - th2)) / cosaz;
254 // Path between point 1 & 2
255 const double mul12 =
256 sqrt(pow(r1 * cos(th1) - r2 * cos(th2), 2) + pow(r1 * sin(th1) - r2 * sin(th2), 2) + pow(z1 - z2, 2));
257 if (mul12 < 0.01)
258 continue;
259 sum += exp(-(mul1 + mul2 + mul12)) / pow(mul12, 2);
260 }
261 const double beta = pow(M_PI * muR * muR * muH, 2) * sum / to<double>(m_pars.msNEvents);
262 const double delta = 0.25 * beta / (M_PI * abs * muH);
263 deltas[j] = delta;
264 }
266 return std::make_pair(stats.mean, stats.mean / stats.standard_deviation);
267}
268
269//-----------------------------------------------------------------------------
270// Private methods
271//-----------------------------------------------------------------------------
277std::pair<double, double> MayersSampleCorrectionStrategy::calculateMuRange() const {
278 const double flightPath(m_pars.l1 + m_pars.l2);
279 const double tmin(m_tofVals[0]), tmax(m_tofVals[m_histoYSize - 1]);
280 return std::make_pair(muR(flightPath, tmin), muR(flightPath, tmax));
281}
282
289double MayersSampleCorrectionStrategy::muR(const double flightPath, const double tof) const {
290 return muR(sigmaTotal(flightPath, tof));
291}
292
298double MayersSampleCorrectionStrategy::muR(const double sigt) const {
299 // Dimensionless number - rho in (1/Angtroms^3), sigt in barns
300 // (1/Angstrom = 1e8/cm) * (barn = 1e-24cm) --> factors cancel out
301 return m_pars.rho * sigt * (m_pars.cylRadius * 1e2);
302}
303
311double MayersSampleCorrectionStrategy::sigmaTotal(const double flightPath, const double tof) const {
312 // sigabs = sigabs(@2200(m/s)^-1)*2200 * velocity;
313 const double sigabs = m_pars.sigmaAbs * 2200.0 * tof * 1e-6 / flightPath;
314 return sigabs + m_pars.sigmaSc;
315}
316
321void MayersSampleCorrectionStrategy::seedRNG(const size_t seed) { m_rng->setSeed(seed); }
322
323} // namespace Mantid::Algorithms
const Parameters m_pars
A copy of the correction parameters.
std::pair< double, double > calculateMS(const size_t irp, const double muR, const double abs)
Calculate the multiple scattering factor for a single mu*r value & absorption value.
Mantid::HistogramData::Histogram getCorrectedHisto()
Return the correction factors.
std::unique_ptr< Kernel::MersenneTwister > m_rng
Random number generator.
double calculateSelfAttenuation(const double muR)
Calculate the self-attenuation factor for a single mu*r value.
std::pair< double, double > calculateMuRange() const
Calculate the mu*r range required to cover the given tof range.
MayersSampleCorrectionStrategy(MayersSampleCorrectionStrategy::Parameters params, HistogramData::Histogram inputHist)
Constructor.
double muR(const double flightPath, const double tof) const
Calculate the value of mu*r for the given flightPath and time of flight.
double sigmaTotal(const double flightPath, const double tof) const
Calculate the value of the total scattering cross section for the given flightPath and time of flight...
~MayersSampleCorrectionStrategy()
Destructor - defined in cpp file to use forward declaration with unique_ptr.
const size_t m_histoYSize
Holds the number of Y vals to process.
void seedRNG(const size_t seed)
(Re-)seed the random number generator
Compute a weighted least-squares polynomial approximations to an arbitrary set of data points.
Evaluate an approximation to a nth order polynomial using a Chebyshev series through Crenshaw's algor...
This implements the Mersenne Twister 19937 pseudo-random number generator algorithm as a specialzatio...
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
STL namespace.
Stores parameters for a single calculation for a given angle and sample details.
Controls the computation of statisical data.
Definition Statistics.h:52