Mantid
Loading...
Searching...
No Matches
InelasticDiffSphere.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
10#include "MantidAPI/IFunction.h"
16#include "MantidKernel/Logger.h"
18#include "MantidTypes/SpectrumDefinition.h"
19
20#include <boost/math/special_functions/bessel.hpp>
21
22#include <cmath>
23#include <limits>
24
25namespace {
26Mantid::Kernel::Logger g_log("InelasticDiffSphere");
27}
28
30
31DECLARE_FUNCTION(InelasticDiffSphere)
32
33
36InelasticDiffSphere::InelasticDiffSphere() : m_lmax(24), m_divZone(0.1), m_hbar(0.658211626) {
37 this->declareParameter("Intensity", 1.0, "scaling factor");
38 this->declareParameter("Radius", 2.0, "Sphere radius, in Angstroms");
39 this->declareParameter("Diffusion", 0.05,
40 "Diffusion coefficient, in units of "
41 "A^2*THz, if energy in meV, or A^2*PHz "
42 "if energy in ueV");
43 this->declareParameter("Shift", 0.0, "Shift in domain");
44
45 declareAttributes();
46}
47
53 /* List of 98 coefficients sorted by increasing value (F.Volino,Mol. Phys.
54 * 41,271-279,1980)
55 * For each coefficient, the triad (coeff, l, n) is defined
56 */
57 size_t ncoeff = 98;
58
59 double const xvalues[] = {
60 2.081576, 3.342094, 4.493409, 4.514100, 5.646704, 5.940370, 6.756456, 7.289932, 7.725252, 7.851078,
61 8.583755, 8.934839, 9.205840, 9.840446, 10.010371, 10.613855, 10.904122, 11.070207, 11.079418, 11.972730,
62 12.143204, 12.279334, 12.404445, 13.202620, 13.295564, 13.472030, 13.846112, 14.066194, 14.258341, 14.590552,
63 14.651263, 15.244514, 15.310887, 15.579236, 15.819216, 15.863222, 16.360674, 16.609346, 16.977550, 17.042902,
64 17.117506, 17.220755, 17.408034, 17.947180, 18.127564, 18.356318, 18.453241, 18.468148, 18.742646, 19.262710,
65 19.270294, 19.496524, 19.581889, 19.862424, 20.221857, 20.371303, 20.406581, 20.538074, 20.559428, 20.795967,
66 21.231068, 21.537120, 21.578053, 21.666607, 21.840012, 21.899697, 21.999955, 22.578058, 22.616601, 22.662493,
67 23.082796, 23.106568, 23.194996, 23.390490, 23.519453, 23.653839, 23.783192, 23.906450, 24.360789, 24.382038,
68 24.474825, 24.689873, 24.850085, 24.899636, 25.052825, 25.218652, 25.561873, 25.604057, 25.724794, 25.846084,
69 26.012188, 26.283265, 26.516603, 26.552589, 26.666054, 26.735177, 26.758685, 26.837518};
70
71 size_t const lvalues[] = {1, 2, 0, 3, 4, 1, 5, 2, 0, 6, 3, 7, 1, 4, 8, 2, 0, 5, 9, 3,
72 10, 6, 1, 11, 4, 7, 2, 0, 12, 5, 8, 3, 13, 1, 9, 6, 14, 4, 10, 2,
73 7, 0, 15, 5, 11, 8, 16, 3, 1, 6, 12, 17, 9, 4, 2, 0, 13, 18, 7, 10,
74 5, 14, 19, 3, 8, 1, 11, 6, 20, 15, 4, 9, 12, 2, 0, 21, 16, 7, 10, 13,
75 5, 22, 3, 17, 1, 8, 14, 11, 23, 6, 18, 4, 9, 2, 0, 15, 24, 12};
76
77 size_t const nvalues[] = {0, 0, 1, 0, 0, 1, 0, 1, 2, 0, 1, 0, 2, 1, 0, 2, 3, 1, 0, 2, 0, 1, 3, 0, 2,
78 1, 3, 4, 0, 2, 1, 3, 0, 4, 1, 2, 0, 3, 1, 4, 2, 5, 0, 3, 1, 2, 0, 4, 5, 3,
79 1, 0, 2, 4, 5, 6, 1, 0, 3, 2, 4, 1, 0, 5, 3, 6, 2, 4, 0, 1, 5, 3, 2, 6, 7,
80 0, 1, 4, 3, 2, 5, 0, 6, 1, 7, 4, 2, 3, 0, 5, 1, 6, 4, 7, 8, 2, 0, 3};
81
82 for (size_t i = 0; i < ncoeff; i++) {
83 xnlc coeff;
84 coeff.x = xvalues[i];
85 coeff.l = lvalues[i];
86 coeff.n = nvalues[i];
87 m_xnl.emplace_back(coeff);
88 }
89}
90
96 for (std::vector<xnlc>::const_iterator it = m_xnl.begin(); it != m_xnl.end(); ++it) {
97 double x = it->x; // eigenvalue for a (n, l) pair
98 auto l = static_cast<double>(it->l);
99 m_alpha.emplace_back((2.0 * l + 1) * 6.0 * x * x / (x * x - l * (l + 1)));
100 }
101}
102
109 for (auto const &coeff : m_xnl) {
110 linearJ abJ;
111 auto x = coeff.x; // eigenvalue for a (n, l) pair
112 auto l = static_cast<unsigned int>(coeff.l);
113 double Qa = x - m_divZone; // left of the numerical divergence point
114 double J0 = (Qa * boost::math::sph_bessel(l + 1, Qa) - l * boost::math::sph_bessel(l, Qa)) / (Qa * Qa - x * x);
115 Qa = x + m_divZone; // right of the numerical divergence point
116 double J1 = (Qa * boost::math::sph_bessel(l + 1, Qa) - l * boost::math::sph_bessel(l, Qa)) / (Qa * Qa - x * x);
117 abJ.slope = (J1 - J0) / (2 * m_divZone); // slope of the linear
118 // interpolation
119 abJ.intercept = J0 - abJ.slope * (x - m_divZone); // intercept of the linear interpolation
120 m_linearJlist.emplace_back(abJ); // store the parameters of the linear interpolation for this it->x
121 }
122}
123
128 this->initXnlCoeff(); // initialize m_xnl with the list of coefficients xnlist
129 this->initAlphaCoeff(); // initialize m_alpha, certain factors constant over
130 // the fit
131 this->initLinJlist(); // initialize m_linearJlist, linear interpolation around
132 // numerical divergence
133}
134
138std::vector<double> InelasticDiffSphere::LorentzianCoefficients(double a) const {
139 // precompute the 2+m_lmax spherical bessel functions (26 in total)
140 std::vector<double> jl(2 + m_lmax);
141 for (size_t l = 0; l < 2 + m_lmax; l++) {
142 jl[l] = boost::math::sph_bessel(static_cast<unsigned int>(l), a);
143 }
144
145 // store the coefficient of each Lorentzian in vector YJ(a,w)
146 size_t ncoeff = m_xnl.size();
147 std::vector<double> YJ(ncoeff);
148
149 for (size_t i = 0; i < ncoeff; i++) {
150 auto x = m_xnl[i].x;
151 auto l = static_cast<unsigned int>(m_xnl[i].l);
152
153 double J;
154 if (fabs(a - x) > m_divZone) {
155 J = (a * jl[l + 1] - l * jl[l]) / (a * a - x * x);
156 } else {
157 J = m_linearJlist[i].slope * a + m_linearJlist[i].intercept; // linear interpolation instead
158 }
159
160 YJ[i] = m_alpha[i] * (J * J);
161 }
162
163 return YJ;
164} // end of LorentzianCoefficients
165
173void InelasticDiffSphere::function1D(double *out, const double *xValues, const size_t nData) const {
174 auto I = this->getParameter("Intensity");
175 auto R = this->getParameter("Radius");
176 auto D = this->getParameter("Diffusion");
177 auto S = this->getParameter("Shift");
178
179 double Q;
180 if (this->getAttribute("Q").asDouble() == EMPTY_DBL()) {
181 if (m_qValueCache.empty()) {
182 throw std::runtime_error("No Q attribute provided and cannot retrieve from worksapce.");
183 }
184
185 auto specIdx = this->getAttribute("WorkspaceIndex").asInt();
186 Q = m_qValueCache[specIdx];
187
188 g_log.debug() << "Get Q value for workspace index " << specIdx << ": " << Q << '\n';
189 } else {
190 Q = this->getAttribute("Q").asDouble();
191
192 g_log.debug() << "Using Q attribute: " << Q << '\n';
193 }
194
195 // // Penalize negative parameters
196 if (I < std::numeric_limits<double>::epsilon() || R < std::numeric_limits<double>::epsilon() ||
197 D < std::numeric_limits<double>::epsilon()) {
198 for (size_t i = 0; i < nData; i++) {
199 out[i] = std::numeric_limits<double>::infinity();
200 }
201 return;
202 }
203
204 // List of Lorentzian HWHM
205 std::vector<double> HWHM;
206 size_t ncoeff = m_xnl.size();
207 for (size_t n = 0; n < ncoeff; n++) {
208 auto x = m_xnl[n].x; // eigenvalue
209 HWHM.emplace_back(m_hbar * x * x * D / (R * R));
210 }
211
212 std::vector<double> YJ;
213 YJ = this->LorentzianCoefficients(Q * R); // The (2l+1)*A_{n,l}
214 for (size_t i = 0; i < nData; i++) {
215 double energy = xValues[i] - S; // from meV to THz (or from micro-eV to PHz)
216 out[i] = 0.0;
217 for (size_t n = 0; n < ncoeff; n++) {
218 double L = (1.0 / M_PI) * HWHM[n] / (HWHM[n] * HWHM[n] + energy * energy); // Lorentzian
219 out[i] += I * YJ[n] * L;
220 }
221 }
222}
223
232void InelasticDiffSphere::setWorkspace(std::shared_ptr<const API::Workspace> ws) {
233 m_qValueCache.clear();
234
235 auto workspace = std::dynamic_pointer_cast<const API::MatrixWorkspace>(ws);
236 if (!workspace)
237 return;
238
239 const auto &spectrumInfo = workspace->spectrumInfo();
240 const auto &detectorIDs = workspace->detectorInfo().detectorIDs();
241 for (size_t idx = 0; idx < spectrumInfo.size(); idx++) {
242 if (!spectrumInfo.hasDetectors(idx)) {
243 m_qValueCache.clear();
244 g_log.information("Cannot populate Q values from workspace - no detectors set.");
245 break;
246 }
247
248 const auto detectorIndex = spectrumInfo.spectrumDefinition(idx)[0].first;
249
250 try {
251 double efixed = workspace->getEFixed(detectorIDs[detectorIndex]);
252 double usingTheta = 0.5 * spectrumInfo.twoTheta(idx);
253
255
256 m_qValueCache.emplace_back(q);
257 } catch (std::runtime_error &) {
258 m_qValueCache.clear();
259 g_log.information("Cannot populate Q values from workspace - could not "
260 "find EFixed value.");
261 return;
262 }
263 }
264}
265
266} // namespace Mantid::CurveFitting::Functions
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
double energy
Definition GetAllEi.cpp:157
IPeaksWorkspace_sptr workspace
IntArray detectorIndex
#define fabs(x)
Definition Matrix.cpp:22
int asInt() const
Returns int value if attribute is a int, throws exception otherwise.
double asDouble() const
Returns double value if attribute is a double, throws exception otherwise.
virtual Attribute getAttribute(const std::string &name) const
Return a value of attribute attName.
double getParameter(size_t i) const override
Get i-th parameter.
Inelastic part of the DiffSphere function.
void function1D(double *out, const double *xValues, const size_t nData) const override
Calculate function values on an energy domain.
double m_divZone
linear interpolation zone around the numerical divergence of factor J
std::vector< double > LorentzianCoefficients(double a) const
Calculate the (2l+1)*A_{n,l} coefficients for each Lorentzian.
double m_hbar
Plank's constant divided by , in units of meV*THz.
void initLinJlist()
initialize the list of parameters for A_{n,l} linear interpolation around the indeterminacy point
void initAlphaCoeff()
initialize the m_alpha coefficients
std::vector< double > m_alpha
certain coefficients invariant during fitting
void setWorkspace(std::shared_ptr< const API::Workspace > ws) override
Cache Q values from the workspace.
void init() override
overwrite IFunction base class methods
std::vector< linearJ > m_linearJlist
list of linearized J values
std::vector< double > m_qValueCache
list of calculated Q values
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
static double convertToElasticQ(const double theta, const double efixed)
Convert to ElasticQ from Energy.
Kernel::Logger g_log("ExperimentInfo")
static logger object
Kernel::Logger g_log("DetermineSpinStateOrder")
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
simple structure to hold a linear interpolation of factor J around its numerical divergence point
structure to hold info on Volino's coefficients