Mantid
Loading...
Searching...
No Matches
ThermalNeutronBk2BkExpConvPVoigt.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 +
11#include "MantidKernel/Logger.h"
12
14
15#include <boost/lexical_cast.hpp>
16#include <cmath>
17#include <gsl/gsl_sf_erf.h>
18
19const double PEAKRANGE = 5.0;
20const double NEG_DBL_MAX = -1. * DBL_MAX;
21
22using namespace std;
23
24using namespace Mantid;
25
26using namespace Mantid::API;
27
29
30using namespace CurveFitting;
31namespace {
33Kernel::Logger g_log("ThermalNeutronBk2BkExpConvPV");
34} // namespace
35
36DECLARE_FUNCTION(ThermalNeutronBk2BkExpConvPVoigt)
37
38//----------------------------------------------------------------------------------------------
42 : IPowderDiffPeakFunction(), m_Alpha(0.), m_Beta(0.), m_Sigma2(0.), m_Gamma(0.), m_eta(0.), m_N(0.),
43 m_cancel(false), m_parallelException(false), m_dspaceCalculated(false) {
44 mHKLSet = false;
45}
46
47//----------------------------------------------------------------------------------------------
53 // Peak height (0)
54 declareParameter("Height", 1.0, "Intensity of peak");
55
56 // Instrument geometry related (1 ~ 8)
57 declareParameter("Dtt1", 1.0, "coefficient 1 for d-spacing calculation for epithermal neutron part");
58 declareParameter("Dtt2", 1.0, "coefficient 2 for d-spacing calculation for epithermal neutron part");
59 declareParameter("Dtt1t", 1.0, "coefficient 1 for d-spacing calculation for thermal neutron part");
60 declareParameter("Dtt2t", 1.0, "coefficient 2 for d-spacing calculation for thermal neutron part");
61 declareParameter("Zero", 0.0, "Zero shift for epithermal neutron");
62 declareParameter("Zerot", 0.0, "Zero shift for thermal neutron");
63 declareParameter("Width", 1.0, "width of the crossover region");
64 declareParameter("Tcross", 1.0, "position of the centre of the crossover region");
65
66 // Peak profile related (9 ~ 16) Back to back Expoential
67 declareParameter("Alph0", 1.6, "exponential constant for rising part of epithermal neutron pulse");
68 declareParameter("Alph1", 1.5, "exponential constant for rising part of expithermal neutron pulse");
69 declareParameter("Beta0", 1.6, "exponential constant of decaying part of epithermal neutron pulse");
70 declareParameter("Beta1", 1.5, "exponential constant of decaying part of epithermal neutron pulse");
71 declareParameter("Alph0t", 1.6, "exponential constant for rising part of thermal neutron pulse");
72 declareParameter("Alph1t", 1.5, "exponential constant for rising part of thermal neutron pulse");
73 declareParameter("Beta0t", 1.6, "exponential constant of decaying part of thermal neutron pulse");
74 declareParameter("Beta1t", 1.5, "exponential constant of decaying part of thermal neutron pulse");
75
76 // Pseudo-Voigt (17 ~ 22)
77 declareParameter("Sig0", 1.0,
78 "variance parameter 1 of the Gaussian "
79 "component of the psuedovoigt function");
80 declareParameter("Sig1", 1.0,
81 "variance parameter 2 of the Gaussian "
82 "component of the psuedovoigt function");
83 declareParameter("Sig2", 1.0,
84 "variance parameter 3 of the Gaussian "
85 "component of the psuedovoigt function");
86
87 declareParameter("Gam0", 0.0,
88 "FWHM parameter 1 of the Lorentzian component "
89 "of the psuedovoigt function");
90 declareParameter("Gam1", 0.0,
91 "FWHM parameter 2 of the Lorentzian component "
92 "of the psuedovoigt function");
93 declareParameter("Gam2", 0.0,
94 "FWHM parameter 3 of the Lorentzian component "
95 "of the psuedovoigt function");
96
97 // Lattice parameter (23)
98 declareParameter("LatticeConstant", 10.0, "lattice constant for the sample");
99
100 LATTICEINDEX = 23;
101 HEIGHTINDEX = 0;
102
103 // Unit cell
104 m_unitCellSize = 10.0;
105
106 // Set flag
108}
109
110//------------- Public Functions (Overwrite) Set/Get
111//---------------------------------------------
112
113//------------- Public Functions (New) Set/Get
114//-------------------------------------------------
115
116//----------------------------------------------------------------------------------------------
153//----------------------------------------------------------------------------------------------
165//----------------------------------------------------------------------------------------------
172double ThermalNeutronBk2BkExpConvPVoigt::getPeakParameter(const std::string &paramname) {
173 // 1. Calculate peak parameters if required
175 calculateParameters(false);
176 }
177
178 // 2. Get value
179 double paramvalue;
180
181 if (paramname == "Alpha")
182 paramvalue = m_Alpha;
183 else if (paramname == "Beta")
184 paramvalue = m_Beta;
185 else if (paramname == "Sigma2")
186 paramvalue = m_Sigma2;
187 else if (paramname == "Gamma")
188 paramvalue = m_Gamma;
189 else if (paramname == "d_h")
190 paramvalue = m_dcentre;
191 else if (paramname == "Eta")
192 paramvalue = m_eta;
193 else if (paramname == "TOF_h")
194 paramvalue = m_centre;
195 else if (paramname == "FWHM")
196 paramvalue = m_fwhm;
197 else {
198 stringstream errss;
199 errss << "Parameter " << paramname << " does not exist in peak function " << this->name()
200 << "'s calculated parameters. "
201 << "Candidates are Alpha, Beta, Sigma2, Gamma d_h and Eta. ";
202 throw runtime_error(errss.str());
203 }
204
205 return paramvalue;
206}
207
208//------------- Public Functions (Overwrite) Calculation
209//---------------------------------------------
214 // Obtain parameters (class) with pre-set order
215 double dtt1 = getParameter(1);
216 double dtt1t = getParameter(3);
217 double dtt2t = getParameter(4);
218 double zero = getParameter(5);
219 double zerot = getParameter(6);
220 double wcross = getParameter(7);
221 double Tcross = getParameter(8);
222
223 double alph0 = getParameter(9);
224 double alph1 = getParameter(10);
225 double beta0 = getParameter(11);
226 double beta1 = getParameter(12);
227 double alph0t = getParameter(13);
228 double alph1t = getParameter(14);
229 double beta0t = getParameter(15);
230 double beta1t = getParameter(16);
231
232 double sig0 = getParameter(17);
233 double sig1 = getParameter(18);
234 double sig2 = getParameter(19);
235 double gam0 = getParameter(20);
236 double gam1 = getParameter(21);
237 double gam2 = getParameter(22);
238
239 double latticeconstant = getParameter(LATTICEINDEX);
240
241 double dh, tof_h, eta, alpha, beta, H, sigma2, gamma, N;
242
243 // Calcualte Peak Position d-spacing and TOF
245 m_unitCell.set(latticeconstant, latticeconstant, latticeconstant, 90.0, 90.0, 90.0);
246 dh = m_unitCell.d(mH, mK, mL);
247 m_dcentre = dh;
249 } else {
250 dh = m_dcentre;
251 }
252
253 // Calculate all the parameters
254 // - Start to calculate alpha, beta, sigma2, gamma,
255 double n = 0.5 * gsl_sf_erfc(wcross * (Tcross - 1 / dh));
256
257 double alpha_e = alph0 + alph1 * dh;
258 double alpha_t = alph0t - alph1t / dh;
259 alpha = 1 / (n * alpha_e + (1 - n) * alpha_t);
260
261 double beta_e = beta0 + beta1 * dh;
262 double beta_t = beta0t - beta1t / dh;
263 beta = 1 / (n * beta_e + (1 - n) * beta_t);
264
265 double Th_e = zero + dtt1 * dh;
266 double Th_t = zerot + dtt1t * dh - dtt2t / dh;
267 tof_h = n * Th_e + (1 - n) * Th_t;
268
269 sigma2 = sig0 * sig0 + sig1 * sig1 * std::pow(dh, 2) + sig2 * sig2 * std::pow(dh, 4);
270 gamma = gam0 + gam1 * dh + gam2 * std::pow(dh, 2);
271
272 // - Calcualte H for the peak
273 calHandEta(sigma2, gamma, H, eta);
274
275 N = alpha * beta * 0.5 / (alpha + beta);
276
277 // Record recent value
278 m_Alpha = alpha;
279 m_Beta = beta;
280 m_Sigma2 = sigma2;
281 m_Gamma = gamma;
282 m_fwhm = H;
283 m_centre = tof_h;
284 m_N = N;
285 m_eta = eta;
286
287 // Check whether all the parameters are physical
288 if (alpha != alpha || beta != beta || sigma2 != sigma2 || gamma != gamma || H != H || H <= 0.) {
289 m_parameterValid = false;
290 } else {
291 m_parameterValid = true;
292 }
293
294 // 5.Debug output
295 if (explicitoutput) {
296 stringstream errss;
297 errss << "alpha = " << alpha << ", beta = " << beta << ", N = " << N << "\n";
298 errss << " n = " << n << ", alpha_e = " << alpha_e << ", alpha_t = " << alpha_t << "\n";
299 errss << " dh = " << dh << ", alph0t = " << alph0t << ", alph1t = " << alph1t << ", alph0 = " << alph0
300 << ", alph1 = " << alph1 << "\n";
301 errss << " n = " << n << ", beta_e = " << beta_e << ", beta_t = " << beta_t << "\n";
302 errss << " dh = " << dh << ", beta0t = " << beta0t << ", beta1t = " << beta1t << "\n";
303 g_log.information(errss.str());
304 }
305
306 // Reset the flag
308}
309
310//------------- Private Functions (Overwrite) Calculation
311//--------------------------------------
312
313//----------------------------------------------------------------------------------------------
316void ThermalNeutronBk2BkExpConvPVoigt::functionLocal(double *out, const double *xValues, size_t nData) const {
317 // 1. Calculate peak parameters
318 double height = getParameter(0);
319
320 // double d_h, tof_h, alpha, beta, H, sigma2, eta, N, gamma;
321 // d_h, tof_h, eta, alpha, beta, H, sigma2, gamma, N,
322
324 calculateParameters(false);
325
326 double peakrange = m_fwhm * PEAKRANGE;
327
328 // cout << "DBx212: eta = " << eta << ", gamma = " << gamma << '\n';
329
330 double invert_sqrt2sigma = 1.0 / sqrt(2.0 * m_Sigma2);
331
332 // PRAGMA_OMP(parallel for schedule(dynamic, 10))
333
334 // PARALLEL_SET_NUM_THREADS(8);
335 // PARALLEL_FOR_NO_WSP_CHECK()
336 for (size_t id = 0; id < nData; ++id) {
337 // PARALLEL_START_INTERRUPT_REGION
338
339 // a) Caclualte peak intensity
340 double dT = xValues[id] - m_centre;
341
342 double omega;
343 if (fabs(dT) < peakrange) {
344 omega = calOmega(dT, m_eta, m_N, m_Alpha, m_Beta, m_fwhm, m_Sigma2, invert_sqrt2sigma);
345 omega *= height;
346 } else {
347 omega = 0.0;
348 }
349
350 out[id] = omega;
351
352 /* Disabled for parallel checking
353 if (!(omega > -DBL_MAX && omega < DBL_MAX))
354 {
355 // Output with error
356 g_log.error() << "Calcuate Peak " << mH << ", " << mK << ", " << mL << "
357 wrong!\n";
358 bool explicitoutput = true;
359 calculateParameters(d_h, tof_h, eta, alpha, beta, H, sigma2, gamma, N,
360 explicitoutput);
361 calOmega(dT, eta, N, alpha, beta, H, sigma2, invert_sqrt2sigma,
362 explicitoutput);
363
364 out[id] = DBL_MAX;
365 }
366 else
367 {
368 out[id] = height*omega;
369 }
370 */
371
372 // PARALLEL_END_INTERRUPT_REGION
373 } // ENDFOR data points
374 // PARALLEL_CHECK_INTERRUPT_REGION
375}
376
377//----------------------------------------------------------------------------------------------
384void ThermalNeutronBk2BkExpConvPVoigt::function(vector<double> &out, const vector<double> &xValues) const {
385 // calculate peak parameters
386 const double HEIGHT = getParameter(0);
387 const double INVERT_SQRT2SIGMA = 1.0 / sqrt(2.0 * m_Sigma2);
388
389#if 0
390 g_log.notice() << "HEIGHT = " << HEIGHT << ".\n";
391
392#endif
394 calculateParameters(false);
395
396 const double RANGE = m_fwhm * PEAKRANGE;
397
398 // calculate where to start calculating
399 const double LEFT_VALUE = m_centre - RANGE;
400 auto iter = std::lower_bound(xValues.begin(), xValues.end(), LEFT_VALUE);
401
402 const double RIGHT_VALUE = m_centre + RANGE;
403 auto iter_end = std::lower_bound(iter, xValues.end(), RIGHT_VALUE);
404
405 // 2. Calcualte
406 std::size_t pos(std::distance(xValues.begin(), iter)); // second loop variable
407 for (; iter != iter_end; ++iter) {
408 out[pos] = HEIGHT * calOmega(*iter - m_centre, m_eta, m_N, m_Alpha, m_Beta, m_fwhm, m_Sigma2, INVERT_SQRT2SIGMA);
409 pos++;
410 } // ENDFOR data points
411}
412
413//----------------------------------------------------------------------------------------------
416void ThermalNeutronBk2BkExpConvPVoigt::functionDerivLocal(API::Jacobian * /*unused*/, const double * /*unused*/,
417 const size_t /*unused*/) {
418 throw Mantid::Kernel::Exception::NotImplementedError("functionDerivLocal is not implemented for IkedaCarpenterPV.");
419}
420
424 calNumericalDeriv(domain, jacobian);
425}
426
467//------------- Private Function To Calculate Peak Profile
468//--------------------------------------------
471void ThermalNeutronBk2BkExpConvPVoigt::calHandEta(double sigma2, double gamma, double &H, double &eta) const {
472 // 1. Calculate H
473 double H_G = sqrt(8.0 * sigma2 * M_LN2);
474 double H_L = gamma;
475
476 double temp1 = std::pow(H_L, 5) + 0.07842 * H_G * std::pow(H_L, 4) + 4.47163 * std::pow(H_G, 2) * std::pow(H_L, 3) +
477 2.42843 * std::pow(H_G, 3) * std::pow(H_L, 2) + 2.69269 * std::pow(H_G, 4) * H_L + std::pow(H_G, 5);
478
479 H = std::pow(temp1, 0.2);
480
481 // 2. Calculate eta
482 double gam_pv = H_L / H;
483 eta = 1.36603 * gam_pv - 0.47719 * std::pow(gam_pv, 2) + 0.11116 * std::pow(gam_pv, 3);
484
485 if (eta > 1 || eta < 0) {
486 g_log.warning() << "Calculated eta = " << eta << " is out of range [0, 1].\n";
487 }
488}
489
490//----------------------------------------------------------------------------------------------
494double ThermalNeutronBk2BkExpConvPVoigt::calOmega(const double x, const double eta, const double N, const double alpha,
495 const double beta, const double H, const double sigma2,
496 const double invert_sqrt2sigma, const bool explicitoutput) const {
497 const double u = 0.5 * alpha * (alpha * sigma2 + 2. * x);
498 const double y = (alpha * sigma2 + x) * invert_sqrt2sigma;
499
500 const double v = 0.5 * beta * (beta * sigma2 - 2. * x);
501 const double z = (beta * sigma2 - x) * invert_sqrt2sigma;
502
503 // 2. Calculate
504
505 const double erfcy = gsl_sf_erfc(y);
506 double part1(0.);
507 if (fabs(erfcy) > DBL_MIN)
508 part1 = exp(u) * erfcy;
509
510 const double erfcz = gsl_sf_erfc(z);
511 double part2(0.);
512 if (fabs(erfcz) > DBL_MIN)
513 part2 = exp(v) * erfcz;
514
515 const double omega1 = (1. - eta) * N * (part1 + part2);
516 double omega2(0.);
517 if (eta >= 1.0E-8) {
518 const double SQRT_H_5 = sqrt(H) * .5;
519 std::complex<double> p(alpha * x, alpha * SQRT_H_5);
520 std::complex<double> q(-beta * x, beta * SQRT_H_5);
521 double omega2a = imag(exp(p) * Mantid::API::E1(p));
522 double omega2b = imag(exp(q) * Mantid::API::E1(q));
523 omega2 = -1.0 * N * eta * (omega2a + omega2b) * M_2_PI;
524 }
525 const double omega = omega1 + omega2;
526
527 if (explicitoutput) {
528 if (omega <= NEG_DBL_MAX || omega >= DBL_MAX) {
529 stringstream errss;
530 errss << "Find omega = " << omega << " is infinity! omega1 = " << omega1 << ", omega2 = " << omega2 << "\n";
531 errss << " u = " << u << ", v = " << v << ", erfc(y) = " << gsl_sf_erfc(y) << ", erfc(z) = " << gsl_sf_erfc(z)
532 << "\n";
533 errss << " alpha = " << alpha << ", x = " << x << " sigma2 = " << sigma2 << ", N = " << N << "\n";
534 g_log.warning(errss.str());
535 }
536 }
537
538 // cout << "[DB] Final Value = " << omega << '\n';
539 return omega;
540}
541
542//----------------------------------------------------------------------------------------------
545void ThermalNeutronBk2BkExpConvPVoigt::setParameter(size_t i, const double &value, bool explicitlySet) {
546 if (i == LATTICEINDEX) {
547 // Lattice parameter
548 if (fabs(m_unitCellSize - value) > 1.0E-8) {
549 // If change in value is non-trivial
551 ParamFunction::setParameter(i, value, explicitlySet);
554 }
555 } else {
556 // Non lattice parameter
557 ParamFunction::setParameter(i, value, explicitlySet);
559 }
560}
561
562//----------------------------------------------------------------------------------------------
565void ThermalNeutronBk2BkExpConvPVoigt::setParameter(const std::string &name, const double &value, bool explicitlySet) {
566 if (name == "LatticeConstant") {
567 // Lattice parameter
568 if (fabs(m_unitCellSize - value) > 1.0E-8) {
569 // If change in value is non-trivial
574 }
575 } else {
576 ParamFunction::setParameter(name, value, explicitlySet);
578 }
579}
580
581//----------------------------------------------------------------------------------------------
597//------------------------- External Functions
598//---------------------------------------------------
662//----------------------------------------------------------------------------------------------
673void ThermalNeutronBk2BkExpConvPVoigt::function1D(double *out, const double *xValues, const size_t nData) const {
674 double c = centre();
675 double dx = fabs(s_peakRadius * this->fwhm());
676 int i0 = -1;
677 int n = 0;
678 for (size_t i = 0; i < nData; ++i) {
679 if (fabs(xValues[i] - c) < dx) {
680 if (i0 < 0)
681 i0 = static_cast<int>(i);
682 ++n;
683 } else {
684 out[i] = 0.0;
685 }
686 }
687 if (i0 < 0 || n == 0)
688 return;
689 this->functionLocal(out + i0, xValues + i0, n);
690}
691
694
695//----------------------------------------------------------------------------------------------
709} // namespace Mantid::CurveFitting::Functions
double value
The value of the point.
Definition: FitMW.cpp:51
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
#define fabs(x)
Definition: Matrix.cpp:22
const double PEAKRANGE
const double PEAKRANGE
const double NEG_DBL_MAX
Base class that represents the domain of a function.
static Kernel::Logger g_log
Logger instance.
Definition: IFunction1D.h:74
void calNumericalDeriv(const FunctionDomain &domain, Jacobian &jacobian)
Calculate numerical derivatives.
Definition: IFunction.cpp:1031
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
bool m_cellParamValueChanged
An indicator to re-calculate peak d-space position.
bool m_parameterValid
Flag to indicate whether peaks' parameters value can generate a valid peak.
virtual double height() const
Get peak's intensity.
bool m_hasNewParameterValue
Flag if any parameter value changed.
virtual double fwhm() const
Get peakl's FWHM.
double m_dcentre
Centre of the peak in d-space.
virtual double centre() const
Overwrite IFunction base class methods.
Represents the Jacobian in IFitFunction::functionDeriv.
Definition: Jacobian.h:22
void setParameter(size_t, const double &value, bool explicitlySet=true) override
Set i-th parameter.
void declareParameter(const std::string &name, double initValue=0, const std::string &description="") override
Declare a new parameter.
double getParameter(size_t i) const override
Get i-th parameter.
ThermalNeutronBk2BkExpConvPVoigt : Back-to-back exponential convoluted with pseudo Voigt for thermal ...
std::string name() const override
Overwrite IFunction base class methods.
void calHandEta(double sigma2, double gamma, double &H, double &eta) const
Calcualte H and Eta.
double calOmega(const double x, const double eta, const double N, const double alpha, const double beta, const double H, const double sigma2, const double invert_sqrt2sigma, const bool explicitoutput=false) const
Calculate peak profile I(TOF) = Omega(TOF)
void functionDeriv(const API::FunctionDomain &domain, API::Jacobian &jacobian) override
Derivative.
double getPeakParameter(const std::string &) override
Overwrite IPeakFunction base class methods.
void function1D(double *out, const double *xValues, const size_t nData) const override
Function you want to fit to.
void init() override
Overwrite IFunction base class method, which declare function parameters.
void setParameter(size_t i, const double &value, bool explicitlySet=true) override
Core function to calcualte peak values for whole region.
void functionLocal(double *out, const double *xValues, const size_t nData) const
Fuction local.
void function(std::vector< double > &out, const std::vector< double > &xValues) const override
Function (local) of the vector version.
virtual void functionDerivLocal(API::Jacobian *out, const double *xValues, const size_t nData)
Derivative.
void calculateParameters(bool explicitoutput) const override
Calculate peak parameters (alpha, beta, sigma2..)
void set(double _a, double _b, double _c, double _alpha, double _beta, double _gamma, const int angleunit=angDegrees)
Set lattice parameters.
Definition: UnitCell.cpp:303
double d(double h, double k, double l) const
Return d-spacing ( ) for a given h,k,l coordinate.
Definition: UnitCell.cpp:700
Marks code as not implemented yet.
Definition: Exception.h:138
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
std::complex< double > MANTID_API_DLL E1(std::complex< double > z)
Integral for Gamma.
Kernel::Logger g_log("ExperimentInfo")
static logger object
Helper class which provides the Collimation Length for SANS instruments.
STL namespace.