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 xvalues[] = {2.081576, 3.342094, 4.493409, 4.514100, 5.646704, 5.940370, 6.756456, 7.289932, 7.725252,
59 7.851078, 8.583755, 8.934839, 9.205840, 9.840446, 10.010371, 10.613855, 10.904122, 11.070207,
60 11.079418, 11.972730, 12.143204, 12.279334, 12.404445, 13.202620, 13.295564, 13.472030, 13.846112,
61 14.066194, 14.258341, 14.590552, 14.651263, 15.244514, 15.310887, 15.579236, 15.819216, 15.863222,
62 16.360674, 16.609346, 16.977550, 17.042902, 17.117506, 17.220755, 17.408034, 17.947180, 18.127564,
63 18.356318, 18.453241, 18.468148, 18.742646, 19.262710, 19.270294, 19.496524, 19.581889, 19.862424,
64 20.221857, 20.371303, 20.406581, 20.538074, 20.559428, 20.795967, 21.231068, 21.537120, 21.578053,
65 21.666607, 21.840012, 21.899697, 21.999955, 22.578058, 22.616601, 22.662493, 23.082796, 23.106568,
66 23.194996, 23.390490, 23.519453, 23.653839, 23.783192, 23.906450, 24.360789, 24.382038, 24.474825,
67 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 lvalues[] = {1, 2, 0, 3, 4, 1, 5, 2, 0, 6, 3, 7, 1, 4, 8, 2, 0, 5, 9, 3, 10, 6, 1, 11, 4,
71 7, 2, 0, 12, 5, 8, 3, 13, 1, 9, 6, 14, 4, 10, 2, 7, 0, 15, 5, 11, 8, 16, 3, 1, 6,
72 12, 17, 9, 4, 2, 0, 13, 18, 7, 10, 5, 14, 19, 3, 8, 1, 11, 6, 20, 15, 4, 9, 12, 2, 0,
73 21, 16, 7, 10, 13, 5, 22, 3, 17, 1, 8, 14, 11, 23, 6, 18, 4, 9, 2, 0, 15, 24, 12};
74
75 size_t 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, 1, 3, 4, 0, 2, 1, 3, 0,
76 4, 1, 2, 0, 3, 1, 4, 2, 5, 0, 3, 1, 2, 0, 4, 5, 3, 1, 0, 2, 4, 5, 6, 1, 0, 3, 2, 4, 1, 0, 5, 3, 6,
77 2, 4, 0, 1, 5, 3, 2, 6, 7, 0, 1, 4, 3, 2, 5, 0, 6, 1, 7, 4, 2, 3, 0, 5, 1, 6, 4, 7, 8, 2, 0, 3};
78
79 for (size_t i = 0; i < ncoeff; i++) {
80 xnlc coeff;
81 coeff.x = xvalues[i];
82 coeff.l = lvalues[i];
83 coeff.n = nvalues[i];
84 m_xnl.emplace_back(coeff);
85 }
86}
87
93 for (std::vector<xnlc>::const_iterator it = m_xnl.begin(); it != m_xnl.end(); ++it) {
94 double x = it->x; // eigenvalue for a (n, l) pair
95 auto l = static_cast<double>(it->l);
96 m_alpha.emplace_back((2.0 * l + 1) * 6.0 * x * x / (x * x - l * (l + 1)));
97 }
98}
99
106 for (auto &coeff : m_xnl) {
107 linearJ abJ;
108 auto x = coeff.x; // eigenvalue for a (n, l) pair
109 auto l = static_cast<unsigned int>(coeff.l);
110 double Qa = x - m_divZone; // left of the numerical divergence point
111 double J0 = (Qa * boost::math::sph_bessel(l + 1, Qa) - l * boost::math::sph_bessel(l, Qa)) / (Qa * Qa - x * x);
112 Qa = x + m_divZone; // right of the numerical divergence point
113 double J1 = (Qa * boost::math::sph_bessel(l + 1, Qa) - l * boost::math::sph_bessel(l, Qa)) / (Qa * Qa - x * x);
114 abJ.slope = (J1 - J0) / (2 * m_divZone); // slope of the linear
115 // interpolation
116 abJ.intercept = J0 - abJ.slope * (x - m_divZone); // intercept of the linear interpolation
117 m_linearJlist.emplace_back(abJ); // store the parameters of the linear interpolation for this it->x
118 }
119}
120
125 this->initXnlCoeff(); // initialize m_xnl with the list of coefficients xnlist
126 this->initAlphaCoeff(); // initialize m_alpha, certain factors constant over
127 // the fit
128 this->initLinJlist(); // initialize m_linearJlist, linear interpolation around
129 // numerical divergence
130}
131
135std::vector<double> InelasticDiffSphere::LorentzianCoefficients(double a) const {
136 // precompute the 2+m_lmax spherical bessel functions (26 in total)
137 std::vector<double> jl(2 + m_lmax);
138 for (size_t l = 0; l < 2 + m_lmax; l++) {
139 jl[l] = boost::math::sph_bessel(static_cast<unsigned int>(l), a);
140 }
141
142 // store the coefficient of each Lorentzian in vector YJ(a,w)
143 size_t ncoeff = m_xnl.size();
144 std::vector<double> YJ(ncoeff);
145
146 for (size_t i = 0; i < ncoeff; i++) {
147 auto x = m_xnl[i].x;
148 auto l = static_cast<unsigned int>(m_xnl[i].l);
149
150 double J;
151 if (fabs(a - x) > m_divZone) {
152 J = (a * jl[l + 1] - l * jl[l]) / (a * a - x * x);
153 } else {
154 J = m_linearJlist[i].slope * a + m_linearJlist[i].intercept; // linear interpolation instead
155 }
156
157 YJ[i] = m_alpha[i] * (J * J);
158 }
159
160 return YJ;
161} // end of LorentzianCoefficients
162
170void InelasticDiffSphere::function1D(double *out, const double *xValues, const size_t nData) const {
171 auto I = this->getParameter("Intensity");
172 auto R = this->getParameter("Radius");
173 auto D = this->getParameter("Diffusion");
174 auto S = this->getParameter("Shift");
175
176 double Q;
177 if (this->getAttribute("Q").asDouble() == EMPTY_DBL()) {
178 if (m_qValueCache.empty()) {
179 throw std::runtime_error("No Q attribute provided and cannot retrieve from worksapce.");
180 }
181
182 auto specIdx = this->getAttribute("WorkspaceIndex").asInt();
183 Q = m_qValueCache[specIdx];
184
185 g_log.debug() << "Get Q value for workspace index " << specIdx << ": " << Q << '\n';
186 } else {
187 Q = this->getAttribute("Q").asDouble();
188
189 g_log.debug() << "Using Q attribute: " << Q << '\n';
190 }
191
192 // // Penalize negative parameters
193 if (I < std::numeric_limits<double>::epsilon() || R < std::numeric_limits<double>::epsilon() ||
194 D < std::numeric_limits<double>::epsilon()) {
195 for (size_t i = 0; i < nData; i++) {
196 out[i] = std::numeric_limits<double>::infinity();
197 }
198 return;
199 }
200
201 // List of Lorentzian HWHM
202 std::vector<double> HWHM;
203 size_t ncoeff = m_xnl.size();
204 for (size_t n = 0; n < ncoeff; n++) {
205 auto x = m_xnl[n].x; // eigenvalue
206 HWHM.emplace_back(m_hbar * x * x * D / (R * R));
207 }
208
209 std::vector<double> YJ;
210 YJ = this->LorentzianCoefficients(Q * R); // The (2l+1)*A_{n,l}
211 for (size_t i = 0; i < nData; i++) {
212 double energy = xValues[i] - S; // from meV to THz (or from micro-eV to PHz)
213 out[i] = 0.0;
214 for (size_t n = 0; n < ncoeff; n++) {
215 double L = (1.0 / M_PI) * HWHM[n] / (HWHM[n] * HWHM[n] + energy * energy); // Lorentzian
216 out[i] += I * YJ[n] * L;
217 }
218 }
219}
220
229void InelasticDiffSphere::setWorkspace(std::shared_ptr<const API::Workspace> ws) {
230 m_qValueCache.clear();
231
232 auto workspace = std::dynamic_pointer_cast<const API::MatrixWorkspace>(ws);
233 if (!workspace)
234 return;
235
236 const auto &spectrumInfo = workspace->spectrumInfo();
237 const auto &detectorIDs = workspace->detectorInfo().detectorIDs();
238 for (size_t idx = 0; idx < spectrumInfo.size(); idx++) {
239 if (!spectrumInfo.hasDetectors(idx)) {
240 m_qValueCache.clear();
241 g_log.information("Cannot populate Q values from workspace - no detectors set.");
242 break;
243 }
244
245 const auto detectorIndex = spectrumInfo.spectrumDefinition(idx)[0].first;
246
247 try {
248 double efixed = workspace->getEFixed(detectorIDs[detectorIndex]);
249 double usingTheta = 0.5 * spectrumInfo.twoTheta(idx);
250
252
253 m_qValueCache.emplace_back(q);
254 } catch (std::runtime_error &) {
255 m_qValueCache.clear();
256 g_log.information("Cannot populate Q values from workspace - could not "
257 "find EFixed value.");
258 return;
259 }
260 }
261}
262
263} // 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
Definition: IndexPeaks.cpp:114
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.
Definition: IFunction.cpp:726
double asDouble() const
Returns double value if attribute is a double, throws exception otherwise.
Definition: IFunction.cpp:739
virtual Attribute getAttribute(const std::string &name) const
Return a value of attribute attName.
Definition: IFunction.cpp:1394
double getParameter(size_t i) const override
Get i-th parameter.
Inelastic part of the DiffSphere function.
void initXnlCoeff()
initialize the Xnl coefficients
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:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
static double convertToElasticQ(const double theta, const double efixed)
Convert to ElasticQ from Energy.
Kernel::Logger g_log("ExperimentInfo")
static logger object
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
simple structure to hold a linear interpolation of factor J around its numerical divergence point
structure to hold info on Volino's coefficients