BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
MathFunctions.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Base/Utils/MathFunctions.cpp
6 //! @brief Implements namespace MathFunctions.
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 <chrono>
18 #include <cstring>
19 #include <fftw3.h>
20 #include <gsl/gsl_sf_bessel.h>
21 #include <gsl/gsl_sf_erf.h>
22 #include <gsl/gsl_sf_expint.h>
23 #include <gsl/gsl_sf_trig.h>
24 #include <limits>
25 #include <random>
26 #include <stdexcept> // need overlooked by g++ 5.4
27 
28 // ************************************************************************** //
29 // Various functions
30 // ************************************************************************** //
31 
33 {
34  return std::exp(-x * x / 2.0) / std::sqrt(M_TWOPI);
35 }
36 
37 double MathFunctions::Gaussian(double x, double average, double std_dev)
38 {
39  return StandardNormal((x - average) / std_dev) / std_dev;
40 }
41 
42 double MathFunctions::IntegratedGaussian(double x, double average, double std_dev)
43 {
44  double normalized_x = (x - average) / std_dev;
45  static double root2 = std::sqrt(2.0);
46  return (gsl_sf_erf(normalized_x / root2) + 1.0) / 2.0;
47 }
48 
49 double MathFunctions::cot(double x)
50 {
51  return tan(M_PI_2 - x);
52 }
53 
54 double MathFunctions::Si(double x) // int_0^x du Sin(u)/u
55 {
56  return gsl_sf_Si(x);
57 }
58 
59 double MathFunctions::sinc(double x) // Sin(x)/x
60 {
61  return gsl_sf_sinc(x / M_PI);
62 }
63 
65 {
66  // This is an exception from the rule that we must not test floating-point numbers for equality.
67  // For small non-zero arguments, sin(z) returns quite accurately z or z-z^3/6.
68  // There is no loss of precision in computing sin(z)/z.
69  // Therefore there is no need for an expensive test like abs(z)<eps.
70  if (z == complex_t(0., 0.))
71  return 1.0;
72  return std::sin(z) / z;
73 }
74 
76 {
77  if (std::abs(z) < std::numeric_limits<double>::epsilon())
78  return 1.0;
79  return std::tanh(z) / z;
80 }
81 
82 double MathFunctions::Laue(const double x, size_t N)
83 {
84  static const double SQRT6DOUBLE_EPS = std::sqrt(6.0 * std::numeric_limits<double>::epsilon());
85  auto nd = static_cast<double>(N);
86  if (std::abs(nd * x) < SQRT6DOUBLE_EPS)
87  return nd;
88  double num = std::sin(nd * x);
89  double den = std::sin(x);
90  return num / den;
91 }
92 
93 double MathFunctions::erf(double arg)
94 {
95  if (arg < 0.0)
96  throw std::runtime_error("Error in MathFunctions::erf: negative argument is not allowed");
97  if (std::isinf(arg))
98  return 1.0;
99  return gsl_sf_erf(arg);
100 }
101 
102 // ************************************************************************** //
103 // Bessel functions
104 // ************************************************************************** //
105 
106 namespace MathFunctions
107 {
108 //! Computes complex Bessel function J0(z), using power series and asymptotic expansion
110 
111 //! Computes complex Bessel function J0(z), using power series and asymptotic expansion
113 } // namespace MathFunctions
114 
115 double MathFunctions::Bessel_J0(double x)
116 {
117  return gsl_sf_bessel_J0(x);
118 }
119 
120 double MathFunctions::Bessel_J1(double x)
121 {
122  return gsl_sf_bessel_J1(x);
123 }
124 
126 {
127  return x == 0 ? 0.5 : gsl_sf_bessel_J1(x) / x;
128 }
129 
130 double MathFunctions::Bessel_I0(double x)
131 {
132  return gsl_sf_bessel_I0(x);
133 }
134 
136 {
137  if (std::imag(z) == 0)
138  return gsl_sf_bessel_J0(std::real(z));
139  return Bessel_J0_PowSer(z);
140 }
141 
143 {
144  if (std::imag(z) == 0)
145  return gsl_sf_bessel_J1(std::real(z));
146  return Bessel_J1_PowSer(z);
147 }
148 
150 {
151  if (std::imag(z) == 0) {
152  double xv = std::real(z);
153  return xv == 0 ? 0.5 : gsl_sf_bessel_J1(xv) / xv;
154  }
155  return z == 0. ? 0.5 : MathFunctions::Bessel_J1_PowSer(z) / z;
156 }
157 
158 //! Computes the complex Bessel function J0(z), using power series and asymptotic expansion.
159 //!
160 //! Forked from unoptimized code at http://www.crbond.com/math.htm,
161 //! who refers to "Computation of Special Functions", Zhang and Jin, John Wiley and Sons, 1996.
162 
164 {
165  complex_t cj0;
166  static const double eps = 1e-15;
167  static double a[] = {-7.03125e-2, 0.112152099609375, -0.5725014209747314,
168  6.074042001273483, -1.100171402692467e2, 3.038090510922384e3,
169  -1.188384262567832e5, 6.252951493434797e6, -4.259392165047669e8,
170  3.646840080706556e10, -3.833534661393944e12, 4.854014686852901e14,
171  -7.286857349377656e16, 1.279721941975975e19};
172  static double b[] = {7.32421875e-2, -0.2271080017089844, 1.727727502584457,
173  -2.438052969955606e1, 5.513358961220206e2, -1.825775547429318e4,
174  8.328593040162893e5, -5.006958953198893e7, 3.836255180230433e9,
175  -3.649010818849833e11, 4.218971570284096e13, -5.827244631566907e15,
176  9.476288099260110e17, -1.792162323051699e20};
177 
178  double a0 = std::abs(z);
179  complex_t z1 = z;
180  if (a0 == 0.0)
181  return 1.0;
182  if (std::real(z) < 0.0)
183  z1 = -z;
184  if (a0 <= 12.0) {
185  // standard power series [http://dlmf.nist.gov/10.2 (10.2.2)]
186  complex_t z2 = 0.25 * z * z;
187  cj0 = 1.0;
188  complex_t cr = 1.0;
189  for (size_t k = 1; k <= 40; ++k) {
190  cr *= -z2 / (double)(k * k);
191  cj0 += cr;
192  if (std::abs(cr) < std::abs(cj0) * eps)
193  break;
194  }
195  } else {
196  // Hankel's asymptotic expansion [http://dlmf.nist.gov/10.17 (10.17.3)]
197  size_t kz;
198  if (a0 >= 50.0)
199  kz = 8; // can be changed to 10
200  else if (a0 >= 35.0)
201  kz = 10; // " " " 12
202  else
203  kz = 12; // " " " 14
204  complex_t ct1 = z1 - M_PI_4;
205  complex_t cp0 = 1.0;
206  complex_t cq0 = -0.125;
207  const complex_t z1m2 = 1. / (z1 * z1); // faster than std::pow(z1, -2.0) ??
208  complex_t ptmp = z1m2;
209  for (size_t k = 0; k < kz; ++k) {
210  cp0 += a[k] * ptmp;
211  cq0 += b[k] * ptmp;
212  ptmp *= z1m2;
213  }
214  cj0 = std::sqrt(M_2_PI / z1) * (cp0 * std::cos(ct1) - cq0 / z1 * std::sin(ct1));
215  }
216  return cj0;
217 }
218 
219 //! Computes the complex Bessel function J1(z), using power series and asymptotic expansion.
220 //!
221 //! Forked from same source as for Bessel_J0_PowSer
222 
224 {
225  complex_t cj1;
226  static const double eps = 1e-15;
227 
228  static double a1[] = {0.1171875,
229  -0.1441955566406250,
230  0.6765925884246826,
231  -6.883914268109947,
232  1.215978918765359e2,
233  -3.302272294480852e3,
234  1.276412726461746e5,
235  -6.656367718817688e6,
236  4.502786003050393e8,
237  -3.833857520742790e10,
238  4.011838599133198e12,
239  -5.060568503314727e14,
240  7.572616461117958e16,
241  -1.326257285320556e19};
242  static double b1[] = {-0.1025390625, 0.2775764465332031, -1.993531733751297,
243  2.724882731126854e1, -6.038440767050702e2, 1.971837591223663e4,
244  -8.902978767070678e5, 5.310411010968522e7, -4.043620325107754e9,
245  3.827011346598605e11, -4.406481417852278e13, 6.065091351222699e15,
246  -9.833883876590679e17, 1.855045211579828e20};
247 
248  double a0 = std::abs(z);
249  if (a0 == 0.0)
250  return 0.0;
251 
252  complex_t z1 = z;
253  if (std::real(z) < 0.0)
254  z1 = -z;
255  if (a0 <= 12.0) {
256  // standard power series [http://dlmf.nist.gov/10.2 (10.2.2)]
257  const complex_t z2 = 0.25 * z * z;
258  cj1 = 1.0;
259  complex_t cr = 1.0; // powers will be computed recursively
260  for (int k = 1; k <= 40; ++k) {
261  cr *= -z2 / (double)(k * (k + 1));
262  cj1 += cr;
263  if (std::abs(cr) < std::abs(cj1) * eps)
264  break;
265  }
266  cj1 *= 0.5 * z1;
267  } else {
268  // Hankel's asymptotic expansion [http://dlmf.nist.gov/10.17 (10.17.3)]
269  size_t kz;
270  if (a0 >= 50.0)
271  kz = 8; // can be changed to 10
272  else if (a0 >= 35.0)
273  kz = 10; // " " " 12
274  else
275  kz = 12; // " " " 14
276  complex_t cp1 = 1.0;
277  complex_t cq1 = 0.375; // division by z1 postponed to final sum
278  const complex_t z1m2 = 1. / (z1 * z1); // faster than std::pow(z1, -2.0) ??
279  complex_t ptmp = z1m2; // powers will be computed recursively
280  for (size_t k = 0; k < kz; ++k) {
281  cp1 += a1[k] * ptmp;
282  cq1 += b1[k] * ptmp; // division by z1 postponed to final sum
283  ptmp *= z1m2;
284  }
285  const complex_t ct2 = z1 - 0.75 * M_PI;
286  cj1 = std::sqrt(M_2_PI / z1) * (cp1 * std::cos(ct2) - cq1 / z1 * std::sin(ct2));
287  }
288  if (std::real(z) < 0.0)
289  cj1 = -cj1;
290  return cj1;
291 }
292 
293 // ************************************************************************** //
294 // Fourier transform and convolution
295 // ************************************************************************** //
296 
297 //! @brief simple (and unoptimized) wrapper function
298 //! for the discrete fast Fourier transformation library (fftw3)
299 
300 std::vector<complex_t> MathFunctions::FastFourierTransform(const std::vector<complex_t>& data,
302 {
303  double scale(1.);
304  size_t npx = data.size();
305 
306  fftw_complex* ftData = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * npx);
307  fftw_complex* ftResult = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * npx);
308  memset(ftData, 0, sizeof(fftw_complex) * npx);
309  memset(ftResult, 0, sizeof(fftw_complex) * npx);
310 
311  for (size_t i = 0; i < npx; i++) {
312  ftData[i][0] = data[i].real();
313  ftData[i][1] = data[i].imag();
314  }
315 
316  fftw_plan plan;
317  switch (ftCase) {
319  plan = fftw_plan_dft_1d((int)npx, ftData, ftResult, FFTW_FORWARD, FFTW_ESTIMATE);
320  break;
322  plan = fftw_plan_dft_1d((int)npx, ftData, ftResult, FFTW_BACKWARD, FFTW_ESTIMATE);
323  scale = 1. / double(npx);
324  break;
325  default:
326  throw std::runtime_error(
327  "MathFunctions::FastFourierTransform -> Panic! Unknown transform case.");
328  }
329 
330  fftw_execute(plan);
331 
332  // saving data for user
333  std::vector<complex_t> outData;
334  outData.resize(npx);
335  for (size_t i = 0; i < npx; i++)
336  outData[i] = scale * complex_t(ftResult[i][0], ftResult[i][1]);
337 
338  fftw_destroy_plan(plan);
339  fftw_free(ftData);
340  fftw_free(ftResult);
341 
342  return outData;
343 }
344 
345 //! @brief simple (and unoptimized) wrapper function
346 //! for the discrete fast Fourier transformation library (fftw3);
347 //! transforms real to complex
348 
349 std::vector<complex_t> MathFunctions::FastFourierTransform(const std::vector<double>& data,
351 {
352  std::vector<complex_t> cdata;
353  cdata.resize(data.size());
354  for (size_t i = 0; i < data.size(); i++)
355  cdata[i] = complex_t(data[i], 0);
356  return MathFunctions::FastFourierTransform(cdata, ftCase);
357 }
358 
359 //! convolution of two real vectors of equal size
360 
361 std::vector<complex_t> MathFunctions::ConvolveFFT(const std::vector<double>& signal,
362  const std::vector<double>& resfunc)
363 {
364  if (signal.size() != resfunc.size())
365  throw std::runtime_error("MathFunctions::ConvolveFFT() -> This convolution works only for "
366  "two vectors of equal size. Use Convolve class instead.");
367  std::vector<complex_t> fft_signal =
369  std::vector<complex_t> fft_resfunc =
371 
372  std::vector<complex_t> fft_prod;
373  fft_prod.resize(fft_signal.size());
374  for (size_t i = 0; i < fft_signal.size(); i++)
375  fft_prod[i] = fft_signal[i] * fft_resfunc[i];
376 
377  std::vector<complex_t> result =
379  return result;
380 }
381 
382 // ************************************************************************** //
383 // Random number generators
384 // ************************************************************************** //
385 
387 {
388  int random_int = std::rand();
389  return (double)random_int / RAND_MAX;
390 }
391 
392 double MathFunctions::GenerateStandardNormalRandom() // using c++11 standard library
393 {
394  unsigned seed =
395  static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
396  std::default_random_engine generator(seed);
397  std::normal_distribution<double> distribution(0.0, 1.0);
398  return distribution(generator);
399 }
400 
401 double MathFunctions::GenerateNormalRandom(double average, double std_dev)
402 {
403  return GenerateStandardNormalRandom() * std_dev + average;
404 }
405 
406 double MathFunctions::GeneratePoissonRandom(double average) // using c++11 standard library
407 {
408  unsigned seed =
409  static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
410  std::default_random_engine generator(seed);
411  if (average <= 0.0)
412  return 0.0;
413  if (average < 1000.0) { // Use std::poisson_distribution
414  std::poisson_distribution<int> distribution(average);
415  int sample = distribution(generator);
416  return (double)sample;
417  } else { // Use normal approximation
418  std::normal_distribution<double> distribution(average, std::sqrt(average));
419  double sample = distribution(generator);
420  return std::max(0.0, sample);
421  }
422 }
std::complex< double > complex_t
Definition: Complex.h:20
Defines M_PI and some more mathematical constants.
#define M_TWOPI
Definition: MathConstants.h:49
#define M_PI_2
Definition: MathConstants.h:40
#define M_2_PI
Definition: MathConstants.h:43
#define M_PI
Definition: MathConstants.h:39
#define M_PI_4
Definition: MathConstants.h:41
Defines namespace MathFunctions.
Various mathematical functions.
double GenerateUniformRandom()
double IntegratedGaussian(double x, double average, double std_dev)
complex_t Bessel_J0_PowSer(const complex_t z)
Computes complex Bessel function J0(z), using power series and asymptotic expansion.
double GenerateNormalRandom(double average, double std_dev)
double cot(double x)
cotangent function:
std::vector< complex_t > FastFourierTransform(const std::vector< complex_t > &data, EFFTDirection tcase)
simple (and unoptimized) wrapper function for the discrete fast Fourier transformation library (fftw3...
double Laue(const double x, size_t N)
Real Laue function: .
std::vector< complex_t > ConvolveFFT(const std::vector< double > &signal, const std::vector< double > &resfunc)
convolution of two real vectors of equal size
double sinc(double x)
sinc function:
double StandardNormal(double x)
complex_t tanhc(const complex_t z)
Complex tanhc function: .
double Bessel_J0(double x)
Bessel function of the first kind and order 0.
double Bessel_J1(double x)
Bessel function of the first kind and order 1.
double Bessel_J1c(double x)
Bessel function Bessel_J1(x)/x.
double Gaussian(double x, double average, double std_dev)
double erf(double arg)
Error function of real-valued argument.
double Si(double x)
Sine integral function: .
double GeneratePoissonRandom(double average)
double Bessel_I0(double x)
Modified Bessel function of the first kind and order 0.
complex_t Bessel_J1_PowSer(const complex_t z)
Computes complex Bessel function J0(z), using power series and asymptotic expansion.
double GenerateStandardNormalRandom()