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