special-functions-impl.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. #ifndef SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
  2. #define SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
  3. //**********************************************************************************//
  4. // Copyright (C) 2009-2018 Ovidio Pena <ovidio@bytesfall.com> //
  5. // Copyright (C) 2013-2018 Konstantin Ladutenko <kostyfisik@gmail.com> //
  6. // //
  7. // This file is part of scattnlay //
  8. // //
  9. // This program is free software: you can redistribute it and/or modify //
  10. // it under the terms of the GNU General Public License as published by //
  11. // the Free Software Foundation, either version 3 of the License, or //
  12. // (at your option) any later version. //
  13. // //
  14. // This program is distributed in the hope that it will be useful, //
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of //
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
  17. // GNU General Public License for more details. //
  18. // //
  19. // The only additional remark is that we expect that all publications //
  20. // describing work using this software, or all commercial products //
  21. // using it, cite at least one of the following references: //
  22. // [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by //
  23. // a multilayered sphere," Computer Physics Communications, //
  24. // vol. 180, Nov. 2009, pp. 2348-2354. //
  25. // [2] K. Ladutenko, U. Pal, A. Rivera, and O. Pena-Rodriguez, "Mie //
  26. // calculation of electromagnetic near-field for a multilayered //
  27. // sphere," Computer Physics Communications, vol. 214, May 2017, //
  28. // pp. 225-230. //
  29. // //
  30. // You should have received a copy of the GNU General Public License //
  31. // along with this program. If not, see <http://www.gnu.org/licenses/>. //
  32. //**********************************************************************************//
  33. //**********************************************************************************//
  34. // This class implements the algorithm for a multilayered sphere described by: //
  35. // [1] W. Yang, "Improved recursive algorithm for light scattering by a //
  36. // multilayered sphere,” Applied Optics, vol. 42, Mar. 2003, pp. 1710-1720. //
  37. // //
  38. // You can find the description of all the used equations in: //
  39. // [2] O. Pena and U. Pal, "Scattering of electromagnetic radiation by //
  40. // a multilayered sphere," Computer Physics Communications, //
  41. // vol. 180, Nov. 2009, pp. 2348-2354. //
  42. // [3] K. Ladutenko, U. Pal, A. Rivera, and O. Pena-Rodriguez, "Mie //
  43. // calculation of electromagnetic near-field for a multilayered //
  44. // sphere," Computer Physics Communications, vol. 214, May 2017, //
  45. // pp. 225-230. //
  46. // //
  47. // Hereinafter all equations numbers refer to [2] //
  48. //**********************************************************************************//
  49. #include <iostream>
  50. #include <iomanip>
  51. #include <stdexcept>
  52. #include <vector>
  53. #include "nmie.hpp"
  54. #include "nmie-precision.hpp"
  55. namespace nmie {
  56. // Note, that Kapteyn seems to be too optimistic (at least by 3 digits
  57. // in some cases) for forward recurrence, see D1test with WYang_data
  58. template <typename FloatType>
  59. int evalKapteynNumberOfLostSignificantDigits(const int ni,
  60. const std::complex<FloatType> zz) {
  61. using std::abs; using std::imag; using std::real; using std::log; using std::sqrt; using std::round;
  62. std::complex<double> z = ConvertComplex<double>(zz);
  63. if (abs(z) == 0.) return 0;
  64. auto n = static_cast<double>(ni);
  65. auto one = std::complex<double> (1, 0);
  66. auto lost_digits = round((
  67. abs(imag(z)) - log(2.) - n * ( real( log( z/n) + sqrt(one
  68. - pow2(z/n)) - log (one + sqrt (one
  69. - pow2(z/n)))
  70. )))/ log(10.));
  71. return lost_digits;
  72. }
  73. template <typename FloatType>
  74. int getNStar(int nmax, std::complex<FloatType> z, const int valid_digits) {
  75. if (nmax == 0) nmax = 1;
  76. int nstar = nmax;
  77. auto z_dp = ConvertComplex<double>(z);
  78. if (std::abs(z_dp) == 0.) return nstar;
  79. int forwardLoss = evalKapteynNumberOfLostSignificantDigits(nmax, z_dp);
  80. int increment = static_cast<int>(std::ceil(
  81. std::max(4* std::pow(std::abs( z_dp), 1/3.0), 5.0)
  82. ));
  83. int backwardLoss =evalKapteynNumberOfLostSignificantDigits(nstar, z_dp);
  84. while ( backwardLoss - forwardLoss < valid_digits) {
  85. nstar += increment;
  86. backwardLoss = evalKapteynNumberOfLostSignificantDigits(nstar,z_dp);
  87. };
  88. return nstar;
  89. }
  90. // Custom implementation of complex cot function to avoid overflow
  91. // if Im(z) < 0, then it evaluates cot(z) as conj(cot(conj(z)))
  92. // see Eqs. 10-12 of [1] for details.
  93. // [1]H. Du, Mie-Scattering Calculation, Appl. Opt. 43, 1951 (2004).
  94. template <typename FloatType>
  95. std::complex<FloatType> complex_cot(const std::complex<FloatType> z) {
  96. auto Remx = z.real();
  97. auto Immx = z.imag();
  98. int sign = (Immx>0) ? 1: -1; // use complex conj if needed for exp and return
  99. auto exp = nmm::exp(-2*sign*Immx);
  100. auto tan = nmm::tan(Remx);
  101. auto a = tan - exp*tan;
  102. auto b = 1 + exp;
  103. auto c = -1 + exp;
  104. auto d = tan + exp* tan;
  105. auto c_one = std::complex<FloatType>(0, 1);
  106. return (a*c + b*d)/(pow2(c) + pow2(d)) +
  107. c_one * (sign* (b*c - a*d)/(pow2(c) + pow2(d)));
  108. }
  109. // Forward iteration for evaluation of ratio of the Riccati–Bessel functions
  110. template <typename FloatType>
  111. void evalForwardR (const std::complex<FloatType> z,
  112. std::vector<std::complex<FloatType> > &r) {
  113. if (r.size() < 1) throw std::invalid_argument(
  114. "We need non-zero evaluations of ratio of the Riccati–Bessel functions.\n");
  115. // r0 = cot(z)
  116. // r[0] = nmm::cos(z)/nmm::sin(z);
  117. r[0] = complex_cot(z);
  118. for (unsigned int n = 0; n < r.size() - 1; n++) {
  119. r[n+1] = static_cast<FloatType>(1)/( static_cast<FloatType>(2*n+1)/z - r[n] );
  120. }
  121. }
  122. // Backward iteration for evaluation of ratio of the Riccati–Bessel functions
  123. template <typename FloatType>
  124. void evalBackwardR (const std::complex<FloatType> z,
  125. std::vector<std::complex<FloatType> > &r) {
  126. if (r.size() < 1) throw std::invalid_argument(
  127. "We need non-zero evaluations of ratio of the Riccati–Bessel functions.\n");
  128. int nmax = r.size()-1 ;
  129. auto tmp = static_cast<FloatType>(2*nmax + 1)/z;
  130. r[nmax] = tmp;
  131. for (int n = nmax -1; n >= 0; n--) {
  132. r[n] = -static_cast<FloatType>(1)/r[n+1] + static_cast<FloatType>(2*n+1)/z;
  133. }
  134. r[0] = complex_cot(z);
  135. }
  136. template <typename FloatType>
  137. void convertRtoD1(const std::complex<FloatType> z,
  138. std::vector<std::complex<FloatType> > &r,
  139. std::vector<std::complex<FloatType> > &D1) {
  140. if (D1.size() > r.size()) throw std::invalid_argument(
  141. "Not enough elements in array of ratio of the Riccati–Bessel functions to convert it into logarithmic derivative array.\n");
  142. std::complex<FloatType> Dold;
  143. for (unsigned int n = 0; n < D1.size(); n++) {
  144. Dold = D1[n];
  145. D1[n] = r[n] - static_cast<FloatType>(n)/z;
  146. // std::cout << "D1old - D1[n] :" << Dold-D1[n] << '\n';
  147. }
  148. }
  149. // ********************************************************************** //
  150. template <typename FloatType>
  151. void evalForwardD (const std::complex<FloatType> z,
  152. std::vector<std::complex<FloatType> > &D) {
  153. int nmax = D.size();
  154. FloatType one = static_cast<FloatType >(1);
  155. for (int n = 1; n < nmax; n++) {
  156. FloatType nf = static_cast<FloatType > (n);
  157. D[n] = one/ (nf/z - D[n-1]) - nf/z;
  158. }
  159. }
  160. // ********************************************************************** //
  161. template <typename FloatType>
  162. void evalForwardD1 (const std::complex<FloatType> z,
  163. std::vector<std::complex<FloatType> > &D) {
  164. if (D.size()<1) throw std::invalid_argument("Should have a leas one element!\n");
  165. D[0] = nmm::cos(z)/nmm::sin(z);
  166. evalForwardD(z,D);
  167. }
  168. // template <typename FloatType>
  169. // void MultiLayerMie<FloatType>::calcD1D3(const std::complex<FloatType> z,
  170. // std::vector<std::complex<FloatType> > &D1,
  171. // std::vector<std::complex<FloatType> > &D3) {
  172. //
  173. // // if (cabs(D1[0]) > 1.0e15) {
  174. // // throw std::invalid_argument("Unstable D1! Please, try to change input parameters!\n");
  175. // // //printf("Warning: Potentially unstable D1! Please, try to change input parameters!\n");
  176. // // }
  177. //
  178. // // Upward recurrence for PsiZeta and D3 - equations (18a) - (18d)
  179. // PsiZeta_[0] = static_cast<FloatType>(0.5)*(static_cast<FloatType>(1.0) - std::complex<FloatType>(nmm::cos(2.0*z.real()), nmm::sin(2.0*z.real()))
  180. // *static_cast<FloatType>(nmm::exp(-2.0*z.imag())));
  181. // D3[0] = std::complex<FloatType>(0.0, 1.0);
  182. //
  183. // for (int n = 1; n <= nmax_; n++) {
  184. // PsiZeta_[n] = PsiZeta_[n - 1]*(static_cast<FloatType>(n)*zinv - D1[n - 1])
  185. // *(static_cast<FloatType>(n)*zinv - D3[n - 1]);
  186. // D3[n] = D1[n] + std::complex<FloatType>(0.0, 1.0)/PsiZeta_[n];
  187. // }
  188. // }
  189. //
  190. //
  191. //**********************************************************************************//
  192. // This function calculates the Riccati-Bessel functions (Psi and Zeta) for a //
  193. // complex argument (z). //
  194. // Equations (20a) - (21b) //
  195. // //
  196. // Input parameters: //
  197. // z: Complex argument to evaluate Psi and Zeta //
  198. // nmax: Maximum number of terms to calculate Psi and Zeta //
  199. // //
  200. // Output parameters: //
  201. // Psi, Zeta: Riccati-Bessel functions //
  202. //**********************************************************************************//
  203. // template <typename FloatType>
  204. // void MultiLayerMie<FloatType>::calcPsiZeta(std::complex<FloatType> z,
  205. // std::vector<std::complex<FloatType> > &Psi,
  206. // std::vector<std::complex<FloatType> > &Zeta) {
  207. //
  208. // std::complex<FloatType> c_i(0.0, 1.0);
  209. // std::vector<std::complex<FloatType> > D1(nmax_ + 1), D3(nmax_ + 1);
  210. //
  211. // // First, calculate the logarithmic derivatives
  212. // calcD1D3(z, D1, D3);
  213. //
  214. // // Now, use the upward recurrence to calculate Psi and Zeta - equations (20a) - (21b)
  215. // Psi[0] = nmm::sin(z);
  216. // Zeta[0] = nmm::sin(z) - c_i*nmm::cos(z);
  217. // for (int n = 1; n <= nmax_; n++) {
  218. // Psi[n] = Psi[n - 1]*(std::complex<FloatType>(n,0.0)/z - D1[n - 1]);
  219. // Zeta[n] = Zeta[n - 1]*(std::complex<FloatType>(n,0.0)/z - D3[n - 1]);
  220. // }
  221. // }
  222. //**********************************************************************************//
  223. // This function calculates Pi and Tau for a given value of cos(Theta). //
  224. // Equations (26a) - (26c) //
  225. // //
  226. // Input parameters: //
  227. // nmax_: Maximum number of terms to calculate Pi and Tau //
  228. // nTheta: Number of scattering angles //
  229. // Theta: Array containing all the scattering angles where the scattering //
  230. // amplitudes will be calculated //
  231. // //
  232. // Output parameters: //
  233. // Pi, Tau: Angular functions Pi and Tau, as defined in equations (26a) - (26c) //
  234. //**********************************************************************************//
  235. // template <typename FloatType>
  236. // void MultiLayerMie<FloatType>::calcPiTau(const FloatType &costheta,
  237. // std::vector<FloatType> &Pi, std::vector<FloatType> &Tau) {
  238. //
  239. // int i;
  240. // //****************************************************//
  241. // // Equations (26a) - (26c) //
  242. // //****************************************************//
  243. // // Initialize Pi and Tau
  244. // Pi[0] = 1.0; // n=1
  245. // Tau[0] = costheta;
  246. // // Calculate the actual values
  247. // if (nmax_ > 1) {
  248. // Pi[1] = 3*costheta*Pi[0]; //n=2
  249. // Tau[1] = 2*costheta*Pi[1] - 3*Pi[0];
  250. // for (i = 2; i < nmax_; i++) { //n=[3..nmax_]
  251. // Pi[i] = ((i + i + 1)*costheta*Pi[i - 1] - (i + 1)*Pi[i - 2])/i;
  252. // Tau[i] = (i + 1)*costheta*Pi[i] - (i + 2)*Pi[i - 1];
  253. // }
  254. // }
  255. // } // end of MultiLayerMie::calcPiTau(...)
  256. //**********************************************************************************//
  257. // This function calculates vector spherical harmonics (eq. 4.50, p. 95 BH), //
  258. // required to calculate the near-field parameters. //
  259. // //
  260. // Input parameters: //
  261. // Rho: Radial distance //
  262. // Phi: Azimuthal angle //
  263. // Theta: Polar angle //
  264. // rn: Either the spherical Ricatti-Bessel function of first or third kind //
  265. // Dn: Logarithmic derivative of rn //
  266. // Pi, Tau: Angular functions Pi and Tau //
  267. // n: Order of vector spherical harmonics //
  268. // //
  269. // Output parameters: //
  270. // Mo1n, Me1n, No1n, Ne1n: Complex vector spherical harmonics //
  271. //**********************************************************************************//
  272. // template <typename FloatType>
  273. // void MultiLayerMie<FloatType>::calcSpherHarm(const std::complex<FloatType> Rho, const FloatType Theta, const FloatType Phi,
  274. // const std::complex<FloatType> &rn, const std::complex<FloatType> &Dn,
  275. // const FloatType &Pi, const FloatType &Tau, const FloatType &n,
  276. // std::vector<std::complex<FloatType> >& Mo1n, std::vector<std::complex<FloatType> >& Me1n,
  277. // std::vector<std::complex<FloatType> >& No1n, std::vector<std::complex<FloatType> >& Ne1n) {
  278. //
  279. // // using eq 4.50 in BH
  280. // std::complex<FloatType> c_zero(0.0, 0.0);
  281. //
  282. // using nmm::sin;
  283. // using nmm::cos;
  284. // Mo1n[0] = c_zero;
  285. // Mo1n[1] = cos(Phi)*Pi*rn/Rho;
  286. // Mo1n[2] = -sin(Phi)*Tau*rn/Rho;
  287. //
  288. // Me1n[0] = c_zero;
  289. // Me1n[1] = -sin(Phi)*Pi*rn/Rho;
  290. // Me1n[2] = -cos(Phi)*Tau*rn/Rho;
  291. //
  292. // No1n[0] = sin(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
  293. // No1n[1] = sin(Phi)*Tau*Dn*rn/Rho;
  294. // No1n[2] = cos(Phi)*Pi*Dn*rn/Rho;
  295. //
  296. // Ne1n[0] = cos(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
  297. // Ne1n[1] = cos(Phi)*Tau*Dn*rn/Rho;
  298. // Ne1n[2] = -sin(Phi)*Pi*Dn*rn/Rho;
  299. // } // end of MultiLayerMie::calcSpherHarm(...)
  300. //
  301. //**********************************************************************************//
  302. // This functions calculate the logarithmic derivatives of the Riccati-Bessel //
  303. // functions (D1 and D3) for a complex argument (z). //
  304. // Equations (16a), (16b) and (18a) - (18d) //
  305. // //
  306. // Input parameters: //
  307. // z: Complex argument to evaluate D1 and D3 //
  308. // nmax_: Maximum number of terms to calculate D1 and D3 //
  309. // //
  310. // Output parameters: //
  311. // D1, D3: Logarithmic derivatives of the Riccati-Bessel functions //
  312. //**********************************************************************************//
  313. template <typename FloatType>
  314. void evalDownwardD1 (const std::complex<FloatType> z,
  315. std::vector<std::complex<FloatType> >& D1) {
  316. int nmax = D1.size() - 1;
  317. int valid_digits = 16;
  318. #ifdef MULTI_PRECISION
  319. valid_digits += MULTI_PRECISION;
  320. #endif
  321. int nstar = nmie::getNStar(nmax, z, valid_digits);
  322. D1.resize(nstar+1);
  323. // Downward recurrence for D1 - equations (16a) and (16b)
  324. D1[nstar] = std::complex<FloatType>(0.0, 0.0);
  325. std::complex<FloatType> c_one(1.0, 0.0);
  326. const std::complex<FloatType> zinv = std::complex<FloatType>(1.0, 0.0)/z;
  327. for (unsigned int n = nstar; n > 0; n--) {
  328. D1[n - 1] = static_cast<FloatType>(n)*zinv - c_one/
  329. (D1[n] + static_cast<FloatType>(n)*zinv);
  330. }
  331. // Use D1[0] from upward recurrence
  332. D1[0] = complex_cot(z);
  333. D1.resize(nmax+1);
  334. // printf("D1[0] = (%16.15g, %16.15g) z=(%16.15g,%16.15g)\n", D1[0].real(),D1[0].imag(),
  335. // z.real(),z.imag());
  336. }
  337. template <typename FloatType>
  338. void evalUpwardD3 (const std::complex<FloatType> z,
  339. const std::vector<std::complex<FloatType> >& D1,
  340. std::vector<std::complex<FloatType> >& D3,
  341. std::vector<std::complex<FloatType> >& PsiZeta) {
  342. int nmax = D1.size()-1;
  343. // Upward recurrence for PsiZeta and D3 - equations (18a) - (18d)
  344. PsiZeta[0] = static_cast<FloatType>(0.5)*(static_cast<FloatType>(1.0) - std::complex<FloatType>(nmm::cos(2.0*z.real()), nmm::sin(2.0*z.real()))
  345. *static_cast<FloatType>(nmm::exp(-2.0*z.imag())));
  346. D3[0] = std::complex<FloatType>(0.0, 1.0);
  347. const std::complex<FloatType> zinv = std::complex<FloatType>(1.0, 0.0)/z;
  348. for (int n = 1; n <= nmax; n++) {
  349. PsiZeta[n] = PsiZeta[n - 1]*(static_cast<FloatType>(n)*zinv - D1[n - 1])
  350. *(static_cast<FloatType>(n)*zinv - D3[n - 1]);
  351. D3[n] = D1[n] + std::complex<FloatType>(0.0, 1.0)/PsiZeta[n];
  352. }
  353. }
  354. template <typename FloatType>
  355. std::complex<FloatType> complex_sin (const std::complex<FloatType> z){
  356. auto a = z.real();
  357. auto b = z.imag();
  358. auto i = std::complex<FloatType>(0,1);
  359. using nmm::sin; using nmm::cos; using nmm::exp;
  360. return ((cos(a)+i*sin(a))*exp(-b)
  361. - (cos(-a)+i*sin(-a))*exp(b)
  362. )/(static_cast<FloatType>(2)*i);
  363. }
  364. template <typename FloatType>
  365. void evalUpwardPsi (const std::complex<FloatType> z,
  366. const std::vector<std::complex<FloatType> > D1,
  367. std::vector<std::complex<FloatType> >& Psi) {
  368. int nmax = Psi.size() - 1;
  369. // Now, use the upward recurrence to calculate Psi and Zeta - equations (20a) - (21b)
  370. Psi[0] = complex_sin(z);
  371. for (int n = 1; n <= nmax; n++) {
  372. Psi[n] = Psi[n - 1]*(std::complex<FloatType>(n,0.0)/z - D1[n - 1]);
  373. }
  374. }
  375. // Sometimes in literature Zeta is also denoted as Ksi, it is a Riccati-Bessel function of third kind.
  376. template <typename FloatType>
  377. void evalUpwardZeta (const std::complex<FloatType> z,
  378. const std::vector<std::complex<FloatType> > D3,
  379. std::vector<std::complex<FloatType> >& Zeta) {
  380. int nmax = Zeta.size() - 1;
  381. std::complex<FloatType> c_i(0.0, 1.0);
  382. // Now, use the upward recurrence to calculate Zeta and Zeta - equations (20a) - (21b)
  383. Zeta[0] = nmm::sin(z) - c_i*nmm::cos(z);
  384. for (int n = 1; n <= nmax; n++) {
  385. Zeta[n] = Zeta[n - 1]*(std::complex<FloatType>(n, 0.0)/z - D3[n - 1]);
  386. }
  387. }
  388. //void evalForwardRiccatiBessel(const FloatType x, const FloatType first, const FloatType second,
  389. // std::vector<FloatType> &values) {
  390. // values[0] = first;
  391. // values[1] = second;
  392. // int nmax = values.size();
  393. // for (int i = 1; i < nmax-1; i++) {
  394. // values[i+1] = (1 + 2*i) * values[i]/x - values[i-1];
  395. // }
  396. //}
  397. //
  398. //void evalChi(const FloatType x, std::vector<FloatType> &Chi) {
  399. // auto first = nmm::cos(x);
  400. // auto second = first/x + nmm::sin(x);
  401. // evalForwardRiccatiBessel(x, first, second, Chi);
  402. //}
  403. //
  404. //void evalPsi(const FloatType x, std::vector<FloatType> &Psi) {
  405. // auto first = nmm::sin(x);
  406. // auto second = first/x - nmm::cos(x);
  407. // evalForwardRiccatiBessel(x, first, second, Psi);
  408. //}
  409. //
  410. //void composeZeta(const std::vector<FloatType> &Psi,
  411. // const std::vector<FloatType> &Chi,
  412. // std::vector< std::complex<FloatType>> &Zeta) {
  413. // int nmax = Zeta.size();
  414. // for (int i = 0; i < nmax; i++) {
  415. // Zeta[i] = std::complex<FloatType > (Psi[i], Chi[i]);
  416. // }
  417. //}
  418. } // end of namespace nmie
  419. #endif // SRC_SPECIAL_FUNCTIONS_IMPL_HPP_