BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
Bessel.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Base/Math/Bessel.cpp
6 //! @brief Implements Bessel functions in namespace Math.
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 
15 #include "Base/Math/Bessel.h"
16 #include "Base/Math/Constants.h"
17 #include <gsl/gsl_sf_bessel.h>
18 
19 namespace {
20 
21 //! Computes the complex Bessel function J0(z), using power series and asymptotic expansion.
22 //!
23 //! Forked from unoptimized code at http://www.crbond.com/math.htm,
24 //! who refers to "Computation of Special Functions", Zhang and Jin, John Wiley and Sons, 1996.
25 
26 complex_t J0_PowSer(const complex_t z)
27 {
28  complex_t cj0;
29  static const double eps = 1e-15;
30  static double a[] = {-7.03125e-2, 0.112152099609375, -0.5725014209747314,
31  6.074042001273483, -1.100171402692467e2, 3.038090510922384e3,
32  -1.188384262567832e5, 6.252951493434797e6, -4.259392165047669e8,
33  3.646840080706556e10, -3.833534661393944e12, 4.854014686852901e14,
34  -7.286857349377656e16, 1.279721941975975e19};
35  static double b[] = {7.32421875e-2, -0.2271080017089844, 1.727727502584457,
36  -2.438052969955606e1, 5.513358961220206e2, -1.825775547429318e4,
37  8.328593040162893e5, -5.006958953198893e7, 3.836255180230433e9,
38  -3.649010818849833e11, 4.218971570284096e13, -5.827244631566907e15,
39  9.476288099260110e17, -1.792162323051699e20};
40 
41  double a0 = std::abs(z);
42  complex_t z1 = z;
43  if (a0 == 0.0)
44  return 1.0;
45  if (std::real(z) < 0.0)
46  z1 = -z;
47  if (a0 <= 12.0) {
48  // standard power series [http://dlmf.nist.gov/10.2 (10.2.2)]
49  complex_t z2 = 0.25 * z * z;
50  cj0 = 1.0;
51  complex_t cr = 1.0;
52  for (size_t k = 1; k <= 40; ++k) {
53  cr *= -z2 / (double)(k * k);
54  cj0 += cr;
55  if (std::abs(cr) < std::abs(cj0) * eps)
56  break;
57  }
58  } else {
59  // Hankel's asymptotic expansion [http://dlmf.nist.gov/10.17 (10.17.3)]
60  size_t kz;
61  if (a0 >= 50.0)
62  kz = 8; // can be changed to 10
63  else if (a0 >= 35.0)
64  kz = 10; // " " " 12
65  else
66  kz = 12; // " " " 14
67  complex_t ct1 = z1 - M_PI_4;
68  complex_t cp0 = 1.0;
69  complex_t cq0 = -0.125;
70  const complex_t z1m2 = 1. / (z1 * z1); // faster than std::pow(z1, -2.0) ??
71  complex_t ptmp = z1m2;
72  for (size_t k = 0; k < kz; ++k) {
73  cp0 += a[k] * ptmp;
74  cq0 += b[k] * ptmp;
75  ptmp *= z1m2;
76  }
77  cj0 = std::sqrt(M_2_PI / z1) * (cp0 * std::cos(ct1) - cq0 / z1 * std::sin(ct1));
78  }
79  return cj0;
80 }
81 
82 //! Computes the complex Bessel function J1(z), using power series and asymptotic expansion.
83 //!
84 //! Forked from same source as for J0_PowSer
85 
86 complex_t J1_PowSer(const complex_t z)
87 {
88  complex_t cj1;
89  static const double eps = 1e-15;
90 
91  static double a1[] = {0.1171875,
92  -0.1441955566406250,
93  0.6765925884246826,
94  -6.883914268109947,
95  1.215978918765359e2,
96  -3.302272294480852e3,
97  1.276412726461746e5,
98  -6.656367718817688e6,
99  4.502786003050393e8,
100  -3.833857520742790e10,
101  4.011838599133198e12,
102  -5.060568503314727e14,
103  7.572616461117958e16,
104  -1.326257285320556e19};
105  static double b1[] = {-0.1025390625, 0.2775764465332031, -1.993531733751297,
106  2.724882731126854e1, -6.038440767050702e2, 1.971837591223663e4,
107  -8.902978767070678e5, 5.310411010968522e7, -4.043620325107754e9,
108  3.827011346598605e11, -4.406481417852278e13, 6.065091351222699e15,
109  -9.833883876590679e17, 1.855045211579828e20};
110 
111  double a0 = std::abs(z);
112  if (a0 == 0.0)
113  return 0.0;
114 
115  complex_t z1 = z;
116  if (std::real(z) < 0.0)
117  z1 = -z;
118  if (a0 <= 12.0) {
119  // standard power series [http://dlmf.nist.gov/10.2 (10.2.2)]
120  const complex_t z2 = 0.25 * z * z;
121  cj1 = 1.0;
122  complex_t cr = 1.0; // powers will be computed recursively
123  for (int k = 1; k <= 40; ++k) {
124  cr *= -z2 / (double)(k * (k + 1));
125  cj1 += cr;
126  if (std::abs(cr) < std::abs(cj1) * eps)
127  break;
128  }
129  cj1 *= 0.5 * z1;
130  } else {
131  // Hankel's asymptotic expansion [http://dlmf.nist.gov/10.17 (10.17.3)]
132  size_t kz;
133  if (a0 >= 50.0)
134  kz = 8; // can be changed to 10
135  else if (a0 >= 35.0)
136  kz = 10; // " " " 12
137  else
138  kz = 12; // " " " 14
139  complex_t cp1 = 1.0;
140  complex_t cq1 = 0.375; // division by z1 postponed to final sum
141  const complex_t z1m2 = 1. / (z1 * z1); // faster than std::pow(z1, -2.0) ??
142  complex_t ptmp = z1m2; // powers will be computed recursively
143  for (size_t k = 0; k < kz; ++k) {
144  cp1 += a1[k] * ptmp;
145  cq1 += b1[k] * ptmp; // division by z1 postponed to final sum
146  ptmp *= z1m2;
147  }
148  const complex_t ct2 = z1 - 0.75 * M_PI;
149  cj1 = std::sqrt(M_2_PI / z1) * (cp1 * std::cos(ct2) - cq1 / z1 * std::sin(ct2));
150  }
151  if (std::real(z) < 0.0)
152  cj1 = -cj1;
153  return cj1;
154 }
155 
156 } // namespace
157 
158 // ************************************************************************************************
159 // Bessel functions
160 // ************************************************************************************************
161 
162 double Math::Bessel::J0(double x)
163 {
164  return gsl_sf_bessel_J0(x);
165 }
166 
167 double Math::Bessel::J1(double x)
168 {
169  return gsl_sf_bessel_J1(x);
170 }
171 
172 double Math::Bessel::J1c(double x)
173 {
174  return x == 0 ? 0.5 : gsl_sf_bessel_J1(x) / x;
175 }
176 
177 double Math::Bessel::I0(double x)
178 {
179  return gsl_sf_bessel_I0(x);
180 }
181 
183 {
184  if (std::imag(z) == 0)
185  return gsl_sf_bessel_J0(std::real(z));
186  return J0_PowSer(z);
187 }
188 
190 {
191  if (std::imag(z) == 0)
192  return gsl_sf_bessel_J1(std::real(z));
193  return J1_PowSer(z);
194 }
195 
197 {
198  if (std::imag(z) == 0) {
199  double xv = std::real(z);
200  return xv == 0 ? 0.5 : gsl_sf_bessel_J1(xv) / xv;
201  }
202  return z == 0. ? 0.5 : J1_PowSer(z) / z;
203 }
Defines Bessel functions in namespace Math.
std::complex< double > complex_t
Definition: Complex.h:20
Defines M_PI and some more mathematical constants.
#define M_2_PI
Definition: Constants.h:48
#define M_PI
Definition: Constants.h:44
#define M_PI_4
Definition: Constants.h:46
double J0(double x)
Bessel function of the first kind and order 0.
Definition: Bessel.cpp:162
double I0(double x)
Modified Bessel function of the first kind and order 0.
Definition: Bessel.cpp:177
double J1(double x)
Bessel function of the first kind and order 1.
Definition: Bessel.cpp:167
double J1c(double x)
Bessel function J1(x)/x.
Definition: Bessel.cpp:172