Mantid
Loading...
Searching...
No Matches
Bk2BkExpConvPV.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#include <cmath>
8
9#include "MantidAPI/Axis.h"
15
16#include <gsl/gsl_sf_erf.h>
17#include <gsl/gsl_sf_lambert.h>
18
19using namespace Mantid::Kernel;
20
21using namespace Mantid::API;
22
24
25using namespace CurveFitting;
26using namespace CurveFitting::SpecialFunctionSupport;
27namespace {
29Kernel::Logger g_log("Bk2BkExpConvPV");
30} // namespace
31
32DECLARE_FUNCTION(Bk2BkExpConvPV)
33
34
36void Bk2BkExpConvPV::init() {
37 declareParameter("X0", -0.0, "Location of the peak");
38 declareParameter("Intensity", 0.0, "Integrated intensity");
39 declareParameter("Alpha", 0.04, "Exponential rise");
40 declareParameter("Beta", 0.02, "Exponential decay");
41 declareParameter("Sigma2", 1.0, "Sigma squared");
42 declareParameter("Gamma", 0.0);
43}
44
47void Bk2BkExpConvPV::setHeight(const double h) {
48 setParameter("Intensity", 1);
49 double h0 = height();
50
51 // avoid divide by zero
52 double minCutOff = 100.0 * std::numeric_limits<double>::min();
53 if (h0 >= 0 && h0 < minCutOff)
54 h0 = minCutOff;
55 else if (h0 < 0 && h0 > -minCutOff)
56 h0 = -minCutOff;
57
58 setParameter("Intensity", h / h0);
59}
60
64double Bk2BkExpConvPV::height() const {
65 double height[1];
66 double peakCentre[1];
67 peakCentre[0] = this->getParameter("X0");
68 this->functionLocal(height, peakCentre, 1);
69 return height[0];
70}
71
75double Bk2BkExpConvPV::fwhm() const {
76 // get sigma of Gauss with same FWHM as voigt (H)
77 double H, eta;
78 calHandEta(getParameter("Sigma2"), getParameter("Gamma"), H, eta);
79 const auto s = H / (2 * sqrt(2 * M_LN2)); // FWHM = 2*sqrt(2*ln(2))*sigma
80 const auto w0 = expWidth();
81 // Gaussian and B2B exp widths don't add in quadrature. The following
82 // tends to gaussian at large S and at S=0 is equal to the intrinsic width of
83 // the B2B exp (good to <3% for typical params)
84 // (same Eq. used in BackToBackExponential)
85 return w0 * exp(-0.5 * M_LN2 * s / w0) + 2 * sqrt(2 * M_LN2) * s;
86}
87
93void Bk2BkExpConvPV::setFwhm(const double w) {
94 const auto h0 = height();
95 const auto w0 = expWidth();
96 if (w > w0) {
97 const auto a = 0.5 * M_LN2;
98 const auto b = 2 * sqrt(2 * M_LN2);
99 // Can calculate FWHM of voigt (from FWHM of Gauss componeont from Eq. in BackToBackExponential)
100 // From Eq. in calHandEta assuming FWHM of Gauss and Lorz are equal fwhm_pv = 1.6364*fwhm_gauss
101 const auto fwhm_gauss =
102 b * w0 * (gsl_sf_lambert_W0(-(a / b) * exp(-(a / b) * (w / w0))) / a + (w / w0) / b) / 1.6364;
103 setParameter("Sigma2", std::pow(fwhm_gauss / b, 2));
104 setParameter("Gamma", fwhm_gauss / 2);
105 } else {
106 // set to some small number relative to w0
107 setParameter("Sigma2", 1e-6);
108 setParameter("Gamma", 1e-6);
109 }
110 setHeight(h0);
111}
112
115void Bk2BkExpConvPV::setCentre(const double c) { setParameter("X0", c); }
116
119double Bk2BkExpConvPV::centre() const { return getParameter("X0"); }
120
121void Bk2BkExpConvPV::setMatrixWorkspace(std::shared_ptr<const API::MatrixWorkspace> workspace, size_t wi, double startX,
122 double endX) {
124}
125
128void Bk2BkExpConvPV::functionLocal(double *out, const double *xValues, const size_t nData) const {
129 // 1. Prepare constants
130 const double alpha = this->getParameter("Alpha");
131 const double beta = this->getParameter("Beta");
132 const double sigma2 = this->getParameter("Sigma2");
133 const double gamma = this->getParameter("Gamma");
134 const double intensity = this->getParameter("Intensity");
135 const double x0 = this->getParameter("X0");
136
137 double invert_sqrt2sigma = 1.0 / sqrt(2.0 * sigma2);
138 double N = alpha * beta * 0.5 / (alpha + beta);
139
140 double H, eta;
141 calHandEta(sigma2, gamma, H, eta);
142
143 g_log.debug() << "DB1143: nData = " << nData << " From " << xValues[0] << " To " << xValues[nData - 1]
144 << " X0 = " << x0 << " Intensity = " << intensity << " alpha = " << alpha << " beta = " << beta
145 << " H = " << H << " eta = " << eta << '\n';
146
147 // 2. Do calculation for each data point within extent of peak (avoid NaNs)
148 double extent = 10 * fwhm();
149 for (size_t id = 0; id < nData; ++id) {
150 double dT = xValues[id] - x0;
151 if (fabs(dT) < extent) {
152 double omega = calOmega(dT, eta, N, alpha, beta, H, sigma2, invert_sqrt2sigma);
153 out[id] = intensity * omega;
154 } else {
155 out[id] = 0.0;
156 }
157 }
158}
159
162void Bk2BkExpConvPV::functionDerivLocal(API::Jacobian * /*jacobian*/, const double * /*xValues*/,
163 const size_t /*nData*/) {
164 throw Mantid::Kernel::Exception::NotImplementedError("functionDerivLocal is not implemented for Bk2BkExpConvPV.");
165}
166
170 calNumericalDeriv(domain, jacobian);
171}
172
175double Bk2BkExpConvPV::calOmega(double x, double eta, double N, double alpha, double beta, double H, double sigma2,
176 double invert_sqrt2sigma) const {
177 // 1. Prepare
178 std::complex<double> p(alpha * x, alpha * H * 0.5);
179 std::complex<double> q(-beta * x, beta * H * 0.5);
180
181 double u = 0.5 * alpha * (alpha * sigma2 + 2 * x);
182 double y = (alpha * sigma2 + x) * invert_sqrt2sigma;
183
184 double v = 0.5 * beta * (beta * sigma2 - 2 * x);
185 double z = (beta * sigma2 - x) * invert_sqrt2sigma;
186
187 // 2. Calculate
188 double omega1 = (1 - eta) * N * (exp(u + gsl_sf_log_erfc(y)) + exp(v + gsl_sf_log_erfc(z)));
189 double omega2;
190 if (eta < 1.0E-8) {
191 omega2 = 0.0;
192 } else {
193 omega2 = 2 * N * eta / M_PI * (imag(exponentialIntegral(p)) + imag(exponentialIntegral(q)));
194 }
195 double omega = omega1 - omega2;
196
197 return omega;
198}
199
200void Bk2BkExpConvPV::geneatePeak(double *out, const double *xValues, const size_t nData) {
201 this->functionLocal(out, xValues, nData);
202}
203
204void Bk2BkExpConvPV::calHandEta(double sigma2, double gamma, double &H, double &eta) const {
205 // 1. Calculate H
206 double H_G = sqrt(8.0 * sigma2 * M_LN2); // FWHM Gauss
207 double H_L = 2 * gamma; // FWHM lorz
208
209 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) +
210 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);
211
212 H = std::pow(temp1, 0.2); // FWHM of PV
213
214 // 2. Calculate eta
215 double gam_pv = H_L / H;
216 eta = 1.36603 * gam_pv - 0.47719 * std::pow(gam_pv, 2) + 0.11116 * std::pow(gam_pv, 3);
217
218 if (eta > 1 || eta < 0) {
219 g_log.error() << "Bk2BkExpConvPV: Calculated eta = " << eta << " is out of range [0, 1].\n";
220 }
221}
222
227 const double a = getParameter("Alpha");
228 const double b = getParameter("Beta");
229 return M_LN2 * (a + b) / (a * b);
230}
231
232} // namespace Mantid::CurveFitting::Functions
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
IPeaksWorkspace_sptr workspace
#define fabs(x)
Definition Matrix.cpp:22
Base class that represents the domain of a function.
void setMatrixWorkspace(std::shared_ptr< const API::MatrixWorkspace > workspace, size_t wi, double startX, double endX) override
Set MatrixWorkspace.
virtual double getParameter(size_t i) const =0
Get i-th parameter.
void calNumericalDeriv(const FunctionDomain &domain, Jacobian &jacobian)
Calculate numerical derivatives.
virtual double intensity() const
Returns the integral intensity of the peak.
void setParameter(const std::string &name, const double &value, bool explicitlySet=true) override
Override parent so that we may bust the cache when a parameter is set.
Represents the Jacobian in IFitFunction::functionDeriv.
Definition Jacobian.h:22
Bk2BkExpConvPV : Peak profile as tback-to-back exponential convoluted with pseudo-Voigt.
void functionDeriv(const API::FunctionDomain &domain, API::Jacobian &jacobian) override
Numerical derivative.
double height() const override
Get approximate height (maximum value) of the peak (not at X0)
double centre() const override
overwrite IPeakFunction base class methods
void geneatePeak(double *out, const double *xValues, const size_t nData)
Set up the range of peak calculation for higher efficiency.
double fwhm() const override
Get approximate peak width.
void setHeight(const double h) override
Set peak height.
void setMatrixWorkspace(std::shared_ptr< const API::MatrixWorkspace > workspace, size_t wi, double startX, double endX) override
Set matrix workspace.
double calOmega(double x, double eta, double N, double alpha, double beta, double H, double sigma2, double invert_sqrt2sigma) const
Calculate Omega(x) = ... ...
void setFwhm(const double w) override
Set new peak width approximately using same mapping in BackToBackExponential assuming fwhm_gaus = fwh...
void setCentre(const double c) override
Set peak center.
void functionLocal(double *out, const double *xValues, const size_t nData) const override
Implement the peak calculating formula.
double expWidth() const
Calculate contribution to the width by the exponentials.
void calHandEta(double sigma2, double gamma, double &H, double &eta) const
void functionDerivLocal(API::Jacobian *out, const double *xValues, const size_t nData) override
Local derivative.
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:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::complex< double > MANTID_CURVEFITTING_DLL exponentialIntegral(const std::complex< double > &z)
Compute exp(z)*E1(z) where z is complex and E1(z) is the Exponential Integral.