BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
IPeakShape.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
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 
16 #include "Base/Math/Bessel.h"
17 #include "Base/Math/Constants.h"
18 #include "Base/Math/IntegratorGK.h"
19 #include <limits>
20 
21 namespace {
22 
23 const double maxkappa = std::log(1.0 / std::numeric_limits<double>::epsilon()) / 2.0;
24 const double maxkappa2 = std::log(std::numeric_limits<double>::max());
25 
26 double FisherDistribution(double x, double kappa)
27 {
28  if (kappa <= 0.0)
29  return 1.0 / (4.0 * M_PI);
30  double prefactor = kappa / (4.0 * M_PI);
31  if (kappa > maxkappa)
32  return 2.0 * prefactor * std::exp(kappa * (x - 1.0));
33  return prefactor * std::exp(kappa * x) / std::sinh(kappa);
34 }
35 
36 double FisherPrefactor(double kappa)
37 {
38  if (kappa <= 0.0)
39  return 1.0 / (4.0 * M_PI);
40  if (kappa > maxkappa)
41  return kappa / 2.0 / M_PI;
42  return kappa * std::exp(kappa) / 4.0 / M_PI / std::sinh(kappa);
43 }
44 
45 double MisesPrefactor(double kappa)
46 {
47  if (kappa <= 0.0)
48  return 1.0 / (2.0 * M_PI);
49  if (kappa > maxkappa2)
50  return std::sqrt(kappa / 2.0 / M_PI) / (1.0 + 1.0 / (8.0 * kappa));
51  return std::exp(kappa) / (2.0 * M_PI * Math::Bessel::I0(kappa));
52 }
53 
54 double Gauss3D(double q2, double domainsize)
55 {
56  double norm_factor = std::pow(domainsize / std::sqrt(M_TWOPI), 3.0);
57  double exponent = -q2 * domainsize * domainsize / 2.0;
58  return norm_factor * std::exp(exponent);
59 }
60 
61 double Cauchy3D(double q2, double domainsize)
62 {
63  double lorentz1 = domainsize / (1.0 + q2 * domainsize * domainsize) / M_PI;
64  return domainsize * lorentz1 * lorentz1;
65 }
66 
67 } // namespace
68 
69 // ************************************************************************************************
70 // interface IPeakShape
71 // ************************************************************************************************
72 
73 IPeakShape::IPeakShape(const std::vector<double>& PValues)
74  : INode(PValues)
75 {
76 }
77 
78 IPeakShape::~IPeakShape() = default;
79 
80 // ************************************************************************************************
81 // class IsotropicGaussPeakShape
82 // ************************************************************************************************
83 
84 IsotropicGaussPeakShape::IsotropicGaussPeakShape(double max_intensity, double domainsize)
85  : m_max_intensity(max_intensity)
86  , m_domainsize(domainsize)
87 {
88 }
89 
91 
93 {
95 }
96 
98 {
99  double q_norm = q.mag2();
100  return m_max_intensity * Gauss3D(q_norm, m_domainsize);
101 }
102 
103 double IsotropicGaussPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
104 {
105  return peakDistribution(q - q_lattice_point);
106 }
107 
108 // ************************************************************************************************
109 // class IsotropicLorentzPeakShape
110 // ************************************************************************************************
111 
112 IsotropicLorentzPeakShape::IsotropicLorentzPeakShape(double max_intensity, double domainsize)
113  : m_max_intensity(max_intensity)
114  , m_domainsize(domainsize)
115 {
116 }
117 
119 
121 {
123 }
124 
126 {
127  double q_norm = q.mag2();
128  return m_max_intensity * Cauchy3D(q_norm, m_domainsize);
129 }
130 
131 double IsotropicLorentzPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
132 {
133  return peakDistribution(q - q_lattice_point);
134 }
135 
136 // ************************************************************************************************
137 // class GaussFisherPeakShape
138 // ************************************************************************************************
139 
140 GaussFisherPeakShape::GaussFisherPeakShape(double max_intensity, double radial_size, double kappa)
141  : m_max_intensity(max_intensity)
142  , m_radial_size(radial_size)
143  , m_kappa(kappa)
144 {
145 }
146 
148 
150 {
152 }
153 
154 double GaussFisherPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
155 {
156  const double q_r = q.mag();
157  const double q_lat_r = q_lattice_point.mag();
158  const double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
159  if (q_lat_r == 0.0)
160  return m_max_intensity * Gauss3D(dq2, m_radial_size);
161  const double norm_factor = m_radial_size / std::sqrt(M_TWOPI);
162  const double radial_part = norm_factor * std::exp(-dq2 * m_radial_size * m_radial_size / 2.0);
163  double angular_part = 1.0;
164  if (q_r * q_lat_r > 0.0) {
165  const double dot_norm = q.dot(q_lattice_point) / q_r / q_lat_r;
166  angular_part = FisherDistribution(dot_norm, m_kappa) / (q_r * q_r);
167  }
168  return m_max_intensity * radial_part * angular_part;
169 }
170 
171 // ************************************************************************************************
172 // class LorentzFisherPeakShape
173 // ************************************************************************************************
174 
175 LorentzFisherPeakShape::LorentzFisherPeakShape(double max_intensity, double radial_size,
176  double kappa)
177  : m_max_intensity(max_intensity)
178  , m_radial_size(radial_size)
179  , m_kappa(kappa)
180 {
181 }
182 
184 
186 {
188 }
189 
190 double LorentzFisherPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
191 {
192  const double q_r = q.mag();
193  const double q_lat_r = q_lattice_point.mag();
194  const double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
195  if (q_lat_r == 0.0)
196  return m_max_intensity * Cauchy3D(dq2, m_radial_size);
197  const double radial_part = m_radial_size / (1.0 + dq2 * m_radial_size * m_radial_size) / M_PI;
198  double angular_part = 1.0;
199  if (q_r * q_lat_r > 0.0) {
200  const double dot_norm = q.dot(q_lattice_point) / q_r / q_lat_r;
201  angular_part = FisherDistribution(dot_norm, m_kappa) / (q_r * q_r);
202  }
203  return m_max_intensity * radial_part * angular_part;
204 }
205 
206 // ************************************************************************************************
207 // class MisesFisherGaussPeakShape
208 // ************************************************************************************************
209 
210 MisesFisherGaussPeakShape::MisesFisherGaussPeakShape(double max_intensity, double radial_size,
211  R3 zenith, double kappa_1, double kappa_2)
212  : m_max_intensity(max_intensity)
213  , m_radial_size(radial_size)
214  , m_zenith(zenith.unit())
215  , m_kappa_1(kappa_1)
216  , m_kappa_2(kappa_2)
217 {
218 }
219 
221 
223 {
225  m_kappa_2);
226 }
227 
228 double MisesFisherGaussPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
229 {
230  // radial part
231  const double q_r = q.mag();
232  const double q_lat_r = q_lattice_point.mag();
233  const double dq2 = (q_r - q_lat_r) * (q_r - q_lat_r);
234  if (q_lat_r == 0.0 || q_r == 0.0)
235  return m_max_intensity * Gauss3D(dq2, m_radial_size);
236  const double norm_factor = m_radial_size / std::sqrt(M_TWOPI);
237  const double radial_part = norm_factor * std::exp(-dq2 * m_radial_size * m_radial_size / 2.0);
238  // angular part
239  const R3 vy = m_zenith.cross(q_lattice_point);
240  const R3 zxq = m_zenith.cross(q);
241  const R3 up = q_lattice_point.unit();
242  if (vy.mag2() <= 0.0 || zxq.mag2() <= 0.0) {
243  const double x = q.unit().dot(up);
244  const double angular_part = FisherDistribution(x, m_kappa_1);
245  return m_max_intensity * radial_part * angular_part;
246  }
247  const R3 uy = vy.unit();
248  const R3 ux = uy.cross(m_zenith);
249  const R3 q_ortho = q - q.dot(m_zenith) * m_zenith;
250  const double phi0 = std::acos(q_ortho.unit().dot(ux));
251  const double theta = std::acos(q.unit().dot(m_zenith));
252  const double pre_1 = FisherPrefactor(m_kappa_1);
253  const double pre_2 = MisesPrefactor(m_kappa_2);
254  const double integral = RealIntegrator().integrate(
255  [=](double phi) -> double {
256  const R3 u_q = std::sin(theta) * std::cos(phi) * ux
257  + std::sin(theta) * std::sin(phi) * uy + std::cos(theta) * m_zenith;
258  const double fisher = std::exp(m_kappa_1 * (u_q.dot(up) - 1.0));
259  const double mises = std::exp(m_kappa_2 * (std::cos(phi0 - phi) - 1.0));
260  return fisher * mises;
261  },
262  0.0, M_TWOPI);
263  return m_max_intensity * radial_part * pre_1 * pre_2 * integral;
264 }
265 
266 // ************************************************************************************************
267 // class MisesGaussPeakShape
268 // ************************************************************************************************
269 
270 MisesGaussPeakShape::MisesGaussPeakShape(double max_intensity, double radial_size, R3 zenith,
271  double kappa)
272  : m_max_intensity(max_intensity)
273  , m_radial_size(radial_size)
274  , m_zenith(zenith.unit())
275  , m_kappa(kappa)
276 {
277 }
278 
280 
282 {
284 }
285 
286 double MisesGaussPeakShape::peakDistribution(const R3 q, const R3 q_lattice_point) const
287 {
288  const R3 vy = m_zenith.cross(q_lattice_point);
289  const R3 zxq = m_zenith.cross(q);
290  if (vy.mag2() <= 0.0 || zxq.mag2() <= 0.0) {
291  const double dq2 = (q - q_lattice_point).mag2();
292  return m_max_intensity * Gauss3D(dq2, m_radial_size);
293  }
294  const double m_qr = q.mag();
295  const R3 m_p = q_lattice_point;
296  const R3 uy = vy.unit();
297  const R3 ux = uy.cross(m_zenith);
298  const R3 q_ortho = q - q.dot(m_zenith) * m_zenith;
299  const double phi0 = std::acos(q_ortho.unit().dot(ux));
300  const double theta = std::acos(q.unit().dot(m_zenith));
301  const double pre = MisesPrefactor(m_kappa);
302  const double integral = RealIntegrator().integrate(
303  [&](double phi) -> double {
304  R3 q_rot = m_qr
305  * (std::sin(theta) * std::cos(phi) * ux
306  + std::sin(theta) * std::sin(phi) * uy + std::cos(theta) * m_zenith);
307  const double dq2 = (q_rot - m_p).mag2();
308  const double gauss = Gauss3D(dq2, m_radial_size);
309  const double mises = std::exp(m_kappa * (std::cos(phi0 - phi) - 1.0));
310  return gauss * mises;
311  },
312  0.0, M_TWOPI);
313  return m_max_intensity * pre * integral;
314 }
Defines Bessel functions in namespace Math.
Defines M_PI and some more mathematical constants.
#define M_TWOPI
Definition: Constants.h:54
#define M_PI
Definition: Constants.h:44
Defines the interface IPeakShape and subclasses.
Defines classes RealIntegrator, ComplexIntegrator.
A peak shape that is Gaussian in the radial direction and uses the Mises-Fisher distribution in the a...
Definition: IPeakShape.h:91
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:154
GaussFisherPeakShape(double max_intensity, double radial_size, double kappa)
Definition: IPeakShape.cpp:140
GaussFisherPeakShape * clone() const override
Definition: IPeakShape.cpp:149
~GaussFisherPeakShape() override
Base class for tree-like structures containing parameterized objects.
Definition: INode.h:40
~IPeakShape() override
IPeakShape()=default
Class that implements an isotropic Gaussian peak shape of a Bragg peak.
Definition: IPeakShape.h:44
IsotropicGaussPeakShape * clone() const override
Definition: IPeakShape.cpp:92
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:103
~IsotropicGaussPeakShape() override
IsotropicGaussPeakShape(double max_intensity, double domainsize)
Definition: IPeakShape.cpp:84
An isotropic Lorentzian peak shape of a Bragg peak.
Definition: IPeakShape.h:67
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:131
IsotropicLorentzPeakShape(double max_intensity, double domainsize)
Definition: IPeakShape.cpp:112
IsotropicLorentzPeakShape * clone() const override
Definition: IPeakShape.cpp:120
~IsotropicLorentzPeakShape() override
A peak shape that is Lorentzian in the radial direction and uses the Mises-Fisher distribution in the...
Definition: IPeakShape.h:118
LorentzFisherPeakShape(double max_intensity, double radial_size, double kappa)
Definition: IPeakShape.cpp:175
~LorentzFisherPeakShape() override
LorentzFisherPeakShape * clone() const override
Definition: IPeakShape.cpp:185
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:190
A peak shape that is Gaussian in the radial direction and a convolution of a Mises-Fisher distributio...
Definition: IPeakShape.h:145
MisesFisherGaussPeakShape(double max_intensity, double radial_size, R3 zenith, double kappa_1, double kappa_2)
Definition: IPeakShape.cpp:210
MisesFisherGaussPeakShape * clone() const override
Definition: IPeakShape.cpp:222
~MisesFisherGaussPeakShape() override
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:228
A peak shape that is a convolution of a Mises-Fisher distribution with a 3d Gaussian.
Definition: IPeakShape.h:174
~MisesGaussPeakShape() override
double peakDistribution(R3 q, R3 q_lattice_point) const override
Peak shape at q from a reciprocal lattice point at q_lattice_point.
Definition: IPeakShape.cpp:286
MisesGaussPeakShape(double max_intensity, double radial_size, R3 zenith, double kappa)
Definition: IPeakShape.cpp:270
MisesGaussPeakShape * clone() const override
Definition: IPeakShape.cpp:281
To integrate a real function of a real variable.
Definition: IntegratorGK.h:28
double integrate(const std::function< double(double)> &f, double lmin, double lmax)
double I0(double x)
Modified Bessel function of the first kind and order 0.
Definition: Bessel.cpp:177
static constexpr double gauss
Definition: Units.h:50