bessel.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. //**********************************************************************************//
  2. // Copyright (C) 2009-2015 Ovidio Pena <ovidio@bytesfall.com> //
  3. // Copyright (C) 2013-2015 Konstantin Ladutenko <kostyfisik@gmail.com> //
  4. // //
  5. // This file is part of scattnlay //
  6. // //
  7. // This program is free software: you can redistribute it and/or modify //
  8. // it under the terms of the GNU General Public License as published by //
  9. // the Free Software Foundation, either version 3 of the License, or //
  10. // (at your option) any later version. //
  11. // //
  12. // This program is distributed in the hope that it will be useful, //
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of //
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
  15. // GNU General Public License for more details. //
  16. // //
  17. // The only additional remark is that we expect that all publications //
  18. // describing work using this software, or all commercial products //
  19. // using it, cite the following reference: //
  20. // [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by //
  21. // a multilayered sphere," Computer Physics Communications, //
  22. // vol. 180, Nov. 2009, pp. 2348-2354. //
  23. // //
  24. // You should have received a copy of the GNU General Public License //
  25. // along with this program. If not, see <http://www.gnu.org/licenses/>. //
  26. //**********************************************************************************//
  27. #include "bessel.h"
  28. #include <algorithm>
  29. #include <complex>
  30. #include <cmath>
  31. #include <limits>
  32. #include <stdexcept>
  33. #include <vector>
  34. namespace nmie {
  35. namespace bessel {
  36. // Implementation of spherical Bessel functions from
  37. // L.-W. Cai / Computer Physics Communications 182 (2011) 663–668
  38. std::vector< std::complex<double> > bessel_j(int nmax, std::complex<double> rho) {
  39. if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
  40. std::complex<double> c_zero(0.0,0.0);
  41. std::vector< std::complex<double> > j(nmax+1, c_zero);
  42. // Select normalization amplitude
  43. int norm_n = 0;
  44. std::complex<double> norm_value = std::sin(rho)/rho,
  45. tmp = std::sin(rho)/(rho*rho) - std::cos(rho)/rho;
  46. if (std::abs(tmp) > std::abs(norm_value)) {
  47. norm_value = tmp;
  48. norm_n = 1;
  49. }
  50. // calculate number of terms for backward recursion Cai eq(11)
  51. double theta = std::arg(rho), r = std::abs(rho);
  52. double M = (1.83 + 4.1* std::pow(std::sin(theta), 0.36)) *
  53. std::pow(r, (0.91 - 0.43*std::pow(std::sin(theta),0.33)))
  54. + 9.0*(1.0-std::sqrt(std::sin(theta)));
  55. int terms = std::max(nmax+2, static_cast<int>(std::ceil(M))+2);
  56. printf("terms = %d\n", terms);
  57. // Seed for eq(2)
  58. std::complex<double> b_np1(0.0, 0.0), b_nm1;
  59. double eps = std::numeric_limits<double>::epsilon();
  60. std::complex<double> b_n(eps, 0.0);
  61. //recurence
  62. for (int n = terms*2; n > 0; --n) {
  63. b_nm1 = (2.0*static_cast<double>(n)+1.0)/rho*b_n - b_np1;
  64. if (n-1 < nmax+1) j[n-1] = b_nm1;
  65. }
  66. // normalize
  67. norm_value /= j[norm_n];
  68. for (auto& value : j) value *=norm_value;
  69. return j;
  70. }
  71. // Implementation of Bessel functions from Bohren and Huffman book, pp. 86-87, eq 4.11
  72. // Calculate all orders of function from 0 to nmax (included) for argument rho
  73. std::vector< std::complex<double> > bh_bessel_j(int nmax, std::complex<double> rho) {
  74. if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
  75. std::vector< std::complex<double> > j(nmax+1);
  76. j[0] = std::sin(rho)/rho;
  77. if (nmax == 0) return j;
  78. j[1] = std::sin(rho)/(rho*rho) - std::cos(rho)/rho;
  79. if (nmax == 1) return j;
  80. for (int i = 2; i < nmax+1; ++i) {
  81. int n = i - 1;
  82. j[n+1] = static_cast<double>(2*n+1)/rho*j[n] - j[n-1];
  83. }
  84. return j;
  85. }
  86. // !*****************************************************************************80
  87. //
  88. // C++ port of fortran code
  89. //
  90. // !! CSPHJY: spherical Bessel functions jn(z) and yn(z) for complex argument.
  91. // !
  92. // ! Discussion:
  93. // !
  94. // ! This procedure computes spherical Bessel functions jn(z) and yn(z)
  95. // ! and their derivatives for a complex argument.
  96. // !
  97. // ! Licensing:
  98. // !
  99. // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
  100. // ! they give permission to incorporate this routine into a user program
  101. // ! provided that the copyright is acknowledged.
  102. // !
  103. // ! Modified:
  104. // !
  105. // ! 01 August 2012
  106. // !
  107. // ! Author:
  108. // !
  109. // ! Shanjie Zhang, Jianming Jin
  110. // !
  111. // ! Reference:
  112. // !
  113. // ! Shanjie Zhang, Jianming Jin,
  114. // ! Computation of Special Functions,
  115. // ! Wiley, 1996,
  116. // ! ISBN: 0-471-11963-6,
  117. // ! LC: QA351.C45.
  118. // !
  119. // ! Parameters:
  120. // !
  121. // ! Input, integer ( kind = 4 ) N, the order of jn(z) and yn(z).
  122. // !
  123. // ! Input, complex ( kind = 8 ) Z, the argument.
  124. // !
  125. // ! Output, integer ( kind = 4 ) NM, the highest order computed.
  126. // !
  127. // ! Output, complex ( kind = 8 ) CSJ(0:N0, CDJ(0:N), CSY(0:N), CDY(0:N),
  128. // ! the values of jn(z), jn'(z), yn(z), yn'(z).
  129. // !
  130. void csphjy (int n, std::complex<double>z, int& nm,
  131. std::vector< std::complex<double> >& csj,
  132. std::vector< std::complex<double> >& cdj,
  133. std::vector< std::complex<double> >& csy,
  134. std::vector< std::complex<double> >& cdy ) {
  135. double a0;
  136. csj.resize(n+1);
  137. cdj.resize(n+1);
  138. csy.resize(n+1);
  139. cdy.resize(n+1);
  140. std::complex<double> cf, cf0, cf1, cs, csa, csb;
  141. int k, m;
  142. a0 = std::abs(z);
  143. nm = n;
  144. if (a0 < 1.0e-60) {
  145. for (int k = 0; k < n+1; ++k) {
  146. csj[k] = 0.0;
  147. cdj[k] = 0.0;
  148. csy[k] = -1.0e+300;
  149. cdy[k] = 1.0e+300;
  150. }
  151. csj[0] = std::complex<double>( 1.0, 0.0);
  152. cdj[1] = std::complex<double>( 0.333333333333333, 0.0);
  153. return;
  154. }
  155. csj[0] = std::sin ( z ) / z;
  156. csj[1] = ( csj[0] - std::cos ( z ) ) / z;
  157. if ( 2 <= n ) {
  158. csa = csj[0];
  159. csb = csj[1];
  160. m = msta1 ( a0, 200 );
  161. if ( m < n ) nm = m;
  162. else m = msta2 ( a0, n, 15 );
  163. cf0 = 0.0;
  164. cf1 = 1.0e-100;
  165. for (int k = m; k>=0; --k) {
  166. cf = ( 2.0 * k + 3.0 ) * cf1 / z - cf0;
  167. if ( k <= nm ) csj[k] = cf;
  168. cf0 = cf1;
  169. cf1 = cf;
  170. }
  171. if ( std::abs ( csa ) <= std::abs ( csb ) ) cs = csb / cf0;
  172. else cs = csa / cf;
  173. for (int k = 0; k <= nm; ++k) {
  174. csj[k] = cs * csj[k];
  175. }
  176. }
  177. cdj[0] = ( std::cos ( z ) - std::sin ( z ) / z ) / z;
  178. for (int k = 1; k <=nm; ++k) {
  179. cdj[k] = csj[k-1] - ( k + 1.0 ) * csj[k] / z;
  180. }
  181. csy[0] = - std::cos ( z ) / z;
  182. csy[1] = ( csy[0] - std::sin ( z ) ) / z;
  183. cdy[0] = ( std::sin ( z ) + std::cos ( z ) / z ) / z;
  184. cdy[1] = ( 2.0 * cdy[0] - std::cos ( z ) ) / z;
  185. for (int k = 2; k<=nm; ++k) {
  186. if ( std::abs ( csj[k-2] ) < std::abs ( csj[k-1] ) ) {
  187. csy[k] = ( csj[k] * csy[k-1] - 1.0 / ( z * z ) ) / csj[k-1];
  188. } else {
  189. csy[k] = ( csj[k] * csy[k-2] - ( 2.0 * k - 1.0 ) / std::pow(z,3) )
  190. / csj[k-2];
  191. }
  192. }
  193. for (int k = 2; k<=nm; ++k) {
  194. cdy[k] = csy[k-1] - ( k + 1.0 ) * csy[k] / z;
  195. }
  196. return;
  197. }
  198. // function msta2 ( x, n, mp )
  199. // !*****************************************************************************80
  200. // !
  201. // !! MSTA2 determines a backward recurrence starting point for Jn(x).
  202. // !
  203. // ! Discussion:
  204. // !
  205. // ! This procedure determines the starting point for a backward
  206. // ! recurrence such that all Jn(x) has MP significant digits.
  207. // !
  208. // ! Licensing:
  209. // !
  210. // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
  211. // ! they give permission to incorporate this routine into a user program
  212. // ! provided that the copyright is acknowledged.
  213. // !
  214. // ! Modified:
  215. // !
  216. // ! 08 July 2012
  217. // !
  218. // ! Author:
  219. // !
  220. // ! Shanjie Zhang, Jianming Jin
  221. // !
  222. // ! Reference:
  223. // !
  224. // ! Shanjie Zhang, Jianming Jin,
  225. // ! Computation of Special Functions,
  226. // ! Wiley, 1996,
  227. // ! ISBN: 0-471-11963-6,
  228. // ! LC: QA351.C45.
  229. // !
  230. // ! Parameters:
  231. // !
  232. // ! Input, real ( kind = 8 ) X, the argument of Jn(x).
  233. // !
  234. // ! Input, integer ( kind = 4 ) N, the order of Jn(x).
  235. // !
  236. // ! Input, integer ( kind = 4 ) MP, the number of significant digits.
  237. // !
  238. // ! Output, integer ( kind = 4 ) MSTA2, the starting point.
  239. // !
  240. int msta2 ( double x, int n, int mp ) {
  241. double a0, ejn, f, f0, f1, hmp;
  242. int it, n0, n1, nn;
  243. double obj;
  244. a0 = std::abs ( x );
  245. hmp = 0.5 * mp;
  246. ejn = envj ( n, a0 );
  247. if ( ejn <= hmp ) {
  248. obj = mp;
  249. n0 = static_cast<int> ( 1.1 * a0 );
  250. } else {
  251. obj = hmp + ejn;
  252. n0 = n;
  253. }
  254. f0 = envj ( n0, a0 ) - obj;
  255. n1 = n0 + 5;
  256. f1 = envj ( n1, a0 ) - obj;
  257. for (int it = 1; it < 21; ++it) {
  258. nn = n1 - ( n1 - n0 ) / ( 1.0 - f0 / f1 );
  259. f = envj ( nn, a0 ) - obj;
  260. if ( std::abs ( nn - n1 ) < 1 ) break;
  261. n0 = n1;
  262. f0 = f1;
  263. n1 = nn;
  264. f1 = f;
  265. }
  266. return nn + 10;
  267. }
  268. // !*****************************************************************************80
  269. // !
  270. // !! MSTA1 determines a backward recurrence starting point for Jn(x).
  271. // !
  272. // ! Discussion:
  273. // !
  274. // ! This procedure determines the starting point for backward
  275. // ! recurrence such that the magnitude of
  276. // ! Jn(x) at that point is about 10^(-MP).
  277. // !
  278. // ! Licensing:
  279. // !
  280. // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
  281. // ! they give permission to incorporate this routine into a user program
  282. // ! provided that the copyright is acknowledged.
  283. // !
  284. // ! Modified:
  285. // !
  286. // ! 08 July 2012
  287. // !
  288. // ! Author:
  289. // !
  290. // ! Shanjie Zhang, Jianming Jin
  291. // !
  292. // ! Reference:
  293. // !
  294. // ! Shanjie Zhang, Jianming Jin,
  295. // ! Computation of Special Functions,
  296. // ! Wiley, 1996,
  297. // ! ISBN: 0-471-11963-6,
  298. // ! LC: QA351.C45.
  299. // !
  300. // ! Parameters:
  301. // !
  302. // ! Input, real ( kind = 8 ) X, the argument.
  303. // !
  304. // ! Input, integer ( kind = 4 ) MP, the negative logarithm of the
  305. // ! desired magnitude.
  306. // !
  307. // ! Output, integer ( kind = 4 ) MSTA1, the starting point.
  308. // !
  309. int msta1 ( double x, int mp ) {
  310. double a0, f, f0, f1;
  311. int it, n0, n1, nn;
  312. a0 = std::abs ( x );
  313. n0 = static_cast<int> (1.1 * a0 ) + 1;
  314. f0 = envj ( n0, a0 ) - mp;
  315. n1 = n0 + 5;
  316. f1 = envj ( n1, a0 ) - mp;
  317. for (int it = 1; it <= 20; ++it) {
  318. nn = n1 - ( n1 - n0 ) / ( 1.0 - f0 / f1 );
  319. f = envj ( nn, a0 ) - mp;
  320. if ( abs ( nn - n1 ) < 1 ) break;
  321. n0 = n1;
  322. f0 = f1;
  323. n1 = nn;
  324. f1 = f;
  325. }
  326. return nn;
  327. }
  328. // function envj ( n, x )
  329. // !*****************************************************************************80
  330. // !
  331. // !! ENVJ is a utility function used by MSTA1 and MSTA2.
  332. // !
  333. // ! Licensing:
  334. // !
  335. // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
  336. // ! they give permission to incorporate this routine into a user program
  337. // ! provided that the copyright is acknowledged.
  338. // !
  339. // ! Modified:
  340. // !
  341. // ! 14 March 2012
  342. // !
  343. // ! Author:
  344. // !
  345. // ! Shanjie Zhang, Jianming Jin
  346. // !
  347. // ! Reference:
  348. // !
  349. // ! Shanjie Zhang, Jianming Jin,
  350. // ! Computation of Special Functions,
  351. // ! Wiley, 1996,
  352. // ! ISBN: 0-471-11963-6,
  353. // ! LC: QA351.C45.
  354. // !
  355. // ! Parameters:
  356. // !
  357. // ! Input, integer ( kind = 4 ) N, ?
  358. // !
  359. // ! Input, real ( kind = 8 ) X, ?
  360. // !
  361. // ! Output, real ( kind = 8 ) ENVJ, ?
  362. // !
  363. double envj (int n, double x ) {
  364. return 0.5 * std::log10(6.28 * n) - n * std::log10(1.36 * x / static_cast<double>(n) );
  365. }
  366. } // end of namespace bessel
  367. } // end of namespace nmie