24 const double maxkappa = std::log(1.0 / std::numeric_limits<double>::epsilon()) / 2.0;
25 const double maxkappa2 = std::log(std::numeric_limits<double>::max());
27 double FisherDistribution(
double x,
double kappa)
30 return 1.0 / (4.0 * M_PI);
32 double prefactor = kappa / (4.0 * M_PI);
33 if (kappa > maxkappa) {
34 return 2.0 * prefactor * std::exp(kappa * (x - 1.0));
36 return prefactor * std::exp(kappa * x) / std::sinh(kappa);
39 double FisherPrefactor(
double kappa)
42 return 1.0 / (4.0 * M_PI);
44 if (kappa > maxkappa) {
45 return kappa / 2.0 / M_PI;
47 return kappa * std::exp(kappa) / 4.0 / M_PI / std::sinh(kappa);
51 double MisesPrefactor(
double kappa)
54 return 1.0 / (2.0 * M_PI);
56 if (kappa > maxkappa2) {
57 return std::sqrt(kappa / 2.0 / M_PI) / (1.0 + 1.0 / (8.0 * kappa));
63 double Gauss3D(
double q2,
double domainsize)
65 double norm_factor = std::pow(domainsize / std::sqrt(M_TWOPI), 3.0);
66 double exponent = -q2 * domainsize * domainsize / 2.0;
67 return norm_factor * std::exp(exponent);
70 double Cauchy3D(
double q2,
double domainsize)
72 double lorentz1 = domainsize / (1.0 + q2 * domainsize * domainsize) / M_PI;
73 return domainsize * lorentz1 * lorentz1;
82 IPeakShape::IPeakShape(
const NodeMeta& meta,
const std::vector<double>& PValues)
87 IPeakShape::~IPeakShape() =
default;
93 IsotropicGaussPeakShape::IsotropicGaussPeakShape(
double max_intensity,
double domainsize)
94 : m_max_intensity(max_intensity), m_domainsize(domainsize)
98 IsotropicGaussPeakShape::~IsotropicGaussPeakShape() =
default;
107 double q_norm = q.
mag2();
108 return m_max_intensity * Gauss3D(q_norm, m_domainsize);
113 return evaluate(q - q_lattice_point);
120 IsotropicLorentzPeakShape::IsotropicLorentzPeakShape(
double max_intensity,
double domainsize)
121 : m_max_intensity(max_intensity), m_domainsize(domainsize)
125 IsotropicLorentzPeakShape::~IsotropicLorentzPeakShape() =
default;
134 double q_norm = q.
mag2();
135 return m_max_intensity * Cauchy3D(q_norm, m_domainsize);
140 return evaluate(q - q_lattice_point);
147 GaussFisherPeakShape::GaussFisherPeakShape(
double max_intensity,
double radial_size,
double kappa)
148 : m_max_intensity(max_intensity), m_radial_size(radial_size), m_kappa(kappa)
152 GaussFisherPeakShape::~GaussFisherPeakShape() =
default;
161 double q_r = q.
mag();
162 double q_lat_r = q_lattice_point.
mag();
163 double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
165 return m_max_intensity * Gauss3D(dq2, m_radial_size);
166 double norm_factor = m_radial_size / std::sqrt(M_TWOPI);
167 double radial_part = norm_factor * std::exp(-dq2 * m_radial_size * m_radial_size / 2.0);
168 double angular_part = 1.0;
169 if (q_r * q_lat_r > 0.0) {
170 double dot_norm = q.
dot(q_lattice_point) / q_r / q_lat_r;
171 angular_part = FisherDistribution(dot_norm, m_kappa) / (q_r * q_r);
173 return m_max_intensity * radial_part * angular_part;
180 LorentzFisherPeakShape::LorentzFisherPeakShape(
double max_intensity,
double radial_size,
182 : m_max_intensity(max_intensity), m_radial_size(radial_size), m_kappa(kappa)
186 LorentzFisherPeakShape::~LorentzFisherPeakShape() =
default;
195 double q_r = q.
mag();
196 double q_lat_r = q_lattice_point.
mag();
197 double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
199 return m_max_intensity * Cauchy3D(dq2, m_radial_size);
200 double radial_part = m_radial_size / (1.0 + dq2 * m_radial_size * m_radial_size) / M_PI;
201 double angular_part = 1.0;
202 if (q_r * q_lat_r > 0.0) {
203 double dot_norm = q.
dot(q_lattice_point) / q_r / q_lat_r;
204 angular_part = FisherDistribution(dot_norm, m_kappa) / (q_r * q_r);
206 return m_max_intensity * radial_part * angular_part;
213 MisesFisherGaussPeakShape::MisesFisherGaussPeakShape(
double max_intensity,
double radial_size,
216 : m_max_intensity(max_intensity), m_radial_size(radial_size), m_zenith(zenith.unit()),
217 m_kappa_1(kappa_1), m_kappa_2(kappa_2)
221 MisesFisherGaussPeakShape::~MisesFisherGaussPeakShape() =
default;
232 double q_r = q.
mag();
233 double q_lat_r = q_lattice_point.
mag();
234 double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
235 if (q_lat_r == 0.0 || q_r == 0.0)
236 return m_max_intensity * Gauss3D(dq2, m_radial_size);
237 double norm_factor = m_radial_size / std::sqrt(M_TWOPI);
238 double radial_part = norm_factor * std::exp(-dq2 * m_radial_size * m_radial_size / 2.0);
240 m_uy = m_zenith.
cross(q_lattice_point);
242 m_up = q_lattice_point.
unit();
243 if (m_uy.
mag2() <= 0.0 || zxq.
mag2() <= 0.0) {
245 double angular_part = FisherDistribution(x, m_kappa_1);
246 return m_max_intensity * radial_part * angular_part;
249 m_ux = m_uy.
cross(m_zenith);
251 m_phi = std::acos(q_ortho.
unit().
dot(m_ux));
252 m_theta = std::acos(q.
unit().
dot(m_zenith));
253 double pre_1 = FisherPrefactor(m_kappa_1);
254 double pre_2 = MisesPrefactor(m_kappa_2);
256 [&](
double phi) ->
double {
return integrand(phi); }, 0.0, M_TWOPI);
257 return m_max_intensity * radial_part * pre_1 * pre_2 * integral;
260 double MisesFisherGaussPeakShape::integrand(
double phi)
const
262 kvector_t u_q = std::sin(m_theta) * std::cos(phi) * m_ux
263 + std::sin(m_theta) * std::sin(phi) * m_uy + std::cos(m_theta) * m_zenith;
264 double fisher = std::exp(m_kappa_1 * (u_q.
dot(m_up) - 1.0));
265 double vonmises = std::exp(m_kappa_2 * (std::cos(m_phi - phi) - 1.0));
266 return fisher * vonmises;
273 MisesGaussPeakShape::MisesGaussPeakShape(
double max_intensity,
double radial_size,
kvector_t zenith,
275 : m_max_intensity(max_intensity), m_radial_size(radial_size), m_zenith(zenith.unit()),
280 MisesGaussPeakShape::~MisesGaussPeakShape() =
default;
289 m_uy = m_zenith.
cross(q_lattice_point);
291 if (m_uy.
mag2() <= 0.0 || zxq.
mag2() <= 0.0) {
292 double dq2 = (q - q_lattice_point).mag2();
293 return m_max_intensity * Gauss3D(dq2, m_radial_size);
296 m_p = q_lattice_point;
298 m_ux = m_uy.
cross(m_zenith);
300 m_phi = std::acos(q_ortho.
unit().
dot(m_ux));
301 m_theta = std::acos(q.
unit().
dot(m_zenith));
302 double pre = MisesPrefactor(m_kappa);
304 [&](
double phi) ->
double {
return integrand(phi); }, 0.0, M_TWOPI);
305 return m_max_intensity * pre * integral;
308 double MisesGaussPeakShape::integrand(
double phi)
const
311 * (std::sin(m_theta) * std::cos(phi) * m_ux
312 + std::sin(m_theta) * std::sin(phi) * m_uy + std::cos(m_theta) * m_zenith);
313 double dq2 = (q_rot - m_p).mag2();
314 double gauss = Gauss3D(dq2, m_radial_size);
315 double vonmises = std::exp(m_kappa * (std::cos(m_phi - phi) - 1.0));
316 return gauss * vonmises;
Defines the interface IPeakShape and subclasses.
Defines classes RealIntegrator, ComplexIntegrator.
Defines M_PI and some more mathematical constants.
Defines namespace MathFunctions.
double mag2() const
Returns magnitude squared of the vector.
auto dot(const BasicVector3D< U > &v) const
Returns dot product of vectors (antilinear in the first [=self] argument).
BasicVector3D< T > unit() const
Returns unit vector in direction of this. Throws for null vector.
double mag() const
Returns magnitude of the vector.
auto cross(const BasicVector3D< U > &v) const
Returns cross product of vectors (linear in both arguments).
A peak shape that is Gaussian in the radial direction and uses the Mises-Fisher distribution in the a...
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
GaussFisherPeakShape * clone() const override
Returns a clone of this ISample object.
Pure virtual base class for sample components and properties related to scattering.
Class that implements an isotropic Gaussian peak shape of a Bragg peak.
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
IsotropicGaussPeakShape * clone() const override
Returns a clone of this ISample object.
An isotropic Lorentzian peak shape of a Bragg peak.
IsotropicLorentzPeakShape * clone() const override
Returns a clone of this ISample object.
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
A peak shape that is Lorentzian in the radial direction and uses the Mises-Fisher distribution in the...
LorentzFisherPeakShape * clone() const override
Returns a clone of this ISample object.
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
A peak shape that is Gaussian in the radial direction and a convolution of a Mises-Fisher distributio...
MisesFisherGaussPeakShape * clone() const override
Returns a clone of this ISample object.
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
A peak shape that is a convolution of a Mises-Fisher distribution with a 3d Gaussian.
double evaluate(const kvector_t q, const kvector_t q_lattice_point) const override
Evaluates the peak shape at q from a reciprocal lattice point at q_lattice_point.
MisesGaussPeakShape * clone() const override
Returns a clone of this ISample object.
To integrate a real function of a real variable.
double Bessel_I0(double x)
Modified Bessel function of the first kind and order 0.