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 muR = muRmin() + to<double>(i - 1) * dmuR;
120 xmur[i] = muR;
121
122 auto attenuation = calculateSelfAttenuation(muR);
123 const double absFactor = attenuation / (M_PI * muR * muR);
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, muR, 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.
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.
std::unique_ptr< Kernel::PseudoRandomNumberGenerator > m_rng
Random number generator.
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 the Mersenne Twister 19937 pseudo-random number generator algorithm as a specialz...
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
Definition: Statistics.cpp:167
STL namespace.
Stores parameters for a single calculation for a given angle and sample details.
size_t msNRuns
Number of runs to average ms correction over.
Controls the computation of statisical data.
Definition: Statistics.h:39