BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
IPeakShape.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Sample/Correlations/IPeakShape.cpp
6 //! @brief Implements the interface IPeakShape and subclasses.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************** //
14 
17 #include "Base/Utils/Integrator.h"
19 #include <limits>
20 
21 namespace
22 {
23 
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());
26 
27 double FisherDistribution(double x, double kappa)
28 {
29  if (kappa <= 0.0) {
30  return 1.0 / (4.0 * M_PI);
31  }
32  double prefactor = kappa / (4.0 * M_PI);
33  if (kappa > maxkappa) {
34  return 2.0 * prefactor * std::exp(kappa * (x - 1.0));
35  }
36  return prefactor * std::exp(kappa * x) / std::sinh(kappa);
37 }
38 
39 double FisherPrefactor(double kappa)
40 {
41  if (kappa <= 0.0) {
42  return 1.0 / (4.0 * M_PI);
43  }
44  if (kappa > maxkappa) {
45  return kappa / 2.0 / M_PI;
46  } else {
47  return kappa * std::exp(kappa) / 4.0 / M_PI / std::sinh(kappa);
48  }
49 }
50 
51 double MisesPrefactor(double kappa)
52 {
53  if (kappa <= 0.0) {
54  return 1.0 / (2.0 * M_PI);
55  }
56  if (kappa > maxkappa2) {
57  return std::sqrt(kappa / 2.0 / M_PI) / (1.0 + 1.0 / (8.0 * kappa));
58  } else {
59  return std::exp(kappa) / (2.0 * M_PI * MathFunctions::Bessel_I0(kappa));
60  }
61 }
62 
63 double Gauss3D(double q2, double domainsize)
64 {
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);
68 }
69 
70 double Cauchy3D(double q2, double domainsize)
71 {
72  double lorentz1 = domainsize / (1.0 + q2 * domainsize * domainsize) / M_PI;
73  return domainsize * lorentz1 * lorentz1;
74 }
75 
76 } // namespace
77 
78 // ************************************************************************** //
79 // interface IPeakShape
80 // ************************************************************************** //
81 
82 IPeakShape::IPeakShape(const NodeMeta& meta, const std::vector<double>& PValues)
83  : ISample(meta, PValues)
84 {
85 }
86 
87 IPeakShape::~IPeakShape() = default;
88 
89 // ************************************************************************** //
90 // class IsotropicGaussPeakShape
91 // ************************************************************************** //
92 
93 IsotropicGaussPeakShape::IsotropicGaussPeakShape(double max_intensity, double domainsize)
94  : m_max_intensity(max_intensity), m_domainsize(domainsize)
95 {
96 }
97 
99 
101 {
103 }
104 
106 {
107  double q_norm = q.mag2();
108  return m_max_intensity * Gauss3D(q_norm, m_domainsize);
109 }
110 
111 double IsotropicGaussPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
112 {
113  return evaluate(q - q_lattice_point);
114 }
115 
116 // ************************************************************************** //
117 // class IsotropicLorentzPeakShape
118 // ************************************************************************** //
119 
120 IsotropicLorentzPeakShape::IsotropicLorentzPeakShape(double max_intensity, double domainsize)
121  : m_max_intensity(max_intensity), m_domainsize(domainsize)
122 {
123 }
124 
126 
128 {
130 }
131 
133 {
134  double q_norm = q.mag2();
135  return m_max_intensity * Cauchy3D(q_norm, m_domainsize);
136 }
137 
138 double IsotropicLorentzPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
139 {
140  return evaluate(q - q_lattice_point);
141 }
142 
143 // ************************************************************************** //
144 // class GaussFisherPeakShape
145 // ************************************************************************** //
146 
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)
149 {
150 }
151 
153 
155 {
157 }
158 
159 double GaussFisherPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
160 {
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);
164  if (q_lat_r == 0.0)
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);
172  }
173  return m_max_intensity * radial_part * angular_part;
174 }
175 
176 // ************************************************************************** //
177 // class LorentzFisherPeakShape
178 // ************************************************************************** //
179 
180 LorentzFisherPeakShape::LorentzFisherPeakShape(double max_intensity, double radial_size,
181  double kappa)
182  : m_max_intensity(max_intensity), m_radial_size(radial_size), m_kappa(kappa)
183 {
184 }
185 
187 
189 {
191 }
192 
193 double LorentzFisherPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
194 {
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);
198  if (q_lat_r == 0.0)
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);
205  }
206  return m_max_intensity * radial_part * angular_part;
207 }
208 
209 // ************************************************************************** //
210 // class MisesFisherGaussPeakShape
211 // ************************************************************************** //
212 
213 MisesFisherGaussPeakShape::MisesFisherGaussPeakShape(double max_intensity, double radial_size,
214  kvector_t zenith, double kappa_1,
215  double kappa_2)
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)
218 {
219 }
220 
222 
224 {
226  m_kappa_2);
227 }
228 
229 double MisesFisherGaussPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
230 {
231  // radial part
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);
239  // angular part
240  m_uy = m_zenith.cross(q_lattice_point);
241  kvector_t zxq = m_zenith.cross(q);
242  m_up = q_lattice_point.unit();
243  if (m_uy.mag2() <= 0.0 || zxq.mag2() <= 0.0) {
244  double x = q.unit().dot(m_up);
245  double angular_part = FisherDistribution(x, m_kappa_1);
246  return m_max_intensity * radial_part * angular_part;
247  }
248  m_uy = m_uy.unit();
249  m_ux = m_uy.cross(m_zenith);
250  kvector_t q_ortho = q - q.dot(m_zenith) * 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);
255  double integral = RealIntegrator().integrate(
256  [&](double phi) -> double { return integrand(phi); }, 0.0, M_TWOPI);
257  return m_max_intensity * radial_part * pre_1 * pre_2 * integral;
258 }
259 
260 double MisesFisherGaussPeakShape::integrand(double phi) const
261 {
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;
267 }
268 
269 // ************************************************************************** //
270 // class MisesGaussPeakShape
271 // ************************************************************************** //
272 
273 MisesGaussPeakShape::MisesGaussPeakShape(double max_intensity, double radial_size, kvector_t zenith,
274  double kappa)
275  : m_max_intensity(max_intensity), m_radial_size(radial_size), m_zenith(zenith.unit()),
276  m_kappa(kappa)
277 {
278 }
279 
281 
283 {
285 }
286 
287 double MisesGaussPeakShape::evaluate(const kvector_t q, const kvector_t q_lattice_point) const
288 {
289  m_uy = m_zenith.cross(q_lattice_point);
290  kvector_t zxq = m_zenith.cross(q);
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);
294  }
295  m_qr = q.mag();
296  m_p = q_lattice_point;
297  m_uy = m_uy.unit();
298  m_ux = m_uy.cross(m_zenith);
299  kvector_t q_ortho = q - q.dot(m_zenith) * 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);
303  double integral = RealIntegrator().integrate(
304  [&](double phi) -> double { return integrand(phi); }, 0.0, M_TWOPI);
305  return m_max_intensity * pre * integral;
306 }
307 
308 double MisesGaussPeakShape::integrand(double phi) const
309 {
310  kvector_t q_rot = m_qr
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;
317 }
Defines the interface IPeakShape and subclasses.
Defines classes RealIntegrator, ComplexIntegrator.
Defines M_PI and some more mathematical constants.
#define M_TWOPI
Definition: MathConstants.h:49
#define M_PI
Definition: MathConstants.h:39
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...
Definition: IPeakShape.h:92
GaussFisherPeakShape(double max_intensity, double radial_size, double kappa)
Definition: IPeakShape.cpp:147
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.
Definition: IPeakShape.cpp:159
GaussFisherPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:154
~GaussFisherPeakShape() override
virtual ~IPeakShape()
IPeakShape()=default
Pure virtual base class for sample components and properties related to scattering.
Definition: ISample.h:28
Class that implements an isotropic Gaussian peak shape of a Bragg peak.
Definition: IPeakShape.h:47
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.
Definition: IPeakShape.cpp:111
IsotropicGaussPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:100
~IsotropicGaussPeakShape() override
IsotropicGaussPeakShape(double max_intensity, double domainsize)
Definition: IPeakShape.cpp:93
An isotropic Lorentzian peak shape of a Bragg peak.
Definition: IPeakShape.h:69
IsotropicLorentzPeakShape(double max_intensity, double domainsize)
Definition: IPeakShape.cpp:120
IsotropicLorentzPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:127
~IsotropicLorentzPeakShape() override
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.
Definition: IPeakShape.cpp:138
A peak shape that is Lorentzian in the radial direction and uses the Mises-Fisher distribution in the...
Definition: IPeakShape.h:117
LorentzFisherPeakShape(double max_intensity, double radial_size, double kappa)
Definition: IPeakShape.cpp:180
~LorentzFisherPeakShape() override
LorentzFisherPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:188
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.
Definition: IPeakShape.cpp:193
A peak shape that is Gaussian in the radial direction and a convolution of a Mises-Fisher distributio...
Definition: IPeakShape.h:142
MisesFisherGaussPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:223
double integrand(double phi) const
Definition: IPeakShape.cpp:260
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.
Definition: IPeakShape.cpp:229
MisesFisherGaussPeakShape(double max_intensity, double radial_size, kvector_t zenith, double kappa_1, double kappa_2)
Definition: IPeakShape.cpp:213
~MisesFisherGaussPeakShape() override
A peak shape that is a convolution of a Mises-Fisher distribution with a 3d Gaussian.
Definition: IPeakShape.h:172
~MisesGaussPeakShape() override
MisesGaussPeakShape(double max_intensity, double radial_size, kvector_t zenith, double kappa)
Definition: IPeakShape.cpp:273
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.
Definition: IPeakShape.cpp:287
MisesGaussPeakShape * clone() const override
Returns a clone of this ISample object.
Definition: IPeakShape.cpp:282
kvector_t m_zenith
Definition: IPeakShape.h:188
double integrand(double phi) const
Definition: IPeakShape.cpp:308
To integrate a real function of a real variable.
Definition: Integrator.h:24
double integrate(const std::function< double(double)> &f, double lmin, double lmax)
Definition: Integrator.cpp:27
double Bessel_I0(double x)
Modified Bessel function of the first kind and order 0.
static constexpr double gauss
Definition: Units.h:60
double Cauchy3D(double q2, double domainsize)
Definition: IPeakShape.cpp:70
double FisherPrefactor(double kappa)
Definition: IPeakShape.cpp:39
double Gauss3D(double q2, double domainsize)
Definition: IPeakShape.cpp:63
double MisesPrefactor(double kappa)
Definition: IPeakShape.cpp:51
double FisherDistribution(double x, double kappa)
Definition: IPeakShape.cpp:27
Metadata of one model node.
Definition: INode.h:37