special-functions-impl.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. #ifndef SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
  2. #define SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
  3. //******************************************************************************
  4. // Copyright (C) 2009-2022 Ovidio Pena <ovidio@bytesfall.com>
  5. // Copyright (C) 2013-2022 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.
  37. // 1710-1720.
  38. //
  39. // You can find the description of all the used equations in:
  40. // [2] O. Pena and U. Pal, "Scattering of electromagnetic radiation by
  41. // a multilayered sphere," Computer Physics Communications,
  42. // vol. 180, Nov. 2009, pp. 2348-2354.
  43. // [3] K. Ladutenko, U. Pal, A. Rivera, and O. Pena-Rodriguez, "Mie
  44. // calculation of electromagnetic near-field for a multilayered
  45. // sphere," Computer Physics Communications, vol. 214, May 2017,
  46. // pp. 225-230.
  47. //
  48. // Hereinafter all equations numbers refer to [2]
  49. //******************************************************************************
  50. #include <iomanip>
  51. #include <iostream>
  52. #include <stdexcept>
  53. #include <vector>
  54. #include "nmie-precision.hpp"
  55. #include "nmie.hpp"
  56. namespace nmie {
  57. //******************************************************************************
  58. // Note, that Kapteyn seems to be too optimistic (at least by 3 digits
  59. // in some cases) for forward recurrence, see D1test with WYang_data
  60. //******************************************************************************
  61. template <typename FloatType>
  62. int evalKapteynNumberOfLostSignificantDigits(const int ni,
  63. const std::complex<FloatType> zz) {
  64. using std::abs;
  65. using std::imag;
  66. using std::log;
  67. using std::real;
  68. using std::round;
  69. using std::sqrt;
  70. std::complex<double> z = ConvertComplex<double>(zz);
  71. if (abs(z) == 0.)
  72. return 0;
  73. auto n = static_cast<double>(ni);
  74. auto one = std::complex<double>(1, 0);
  75. auto lost_digits = round( //
  76. ( //
  77. abs(imag(z)) - log(2.) -
  78. n * (real(log(z / n) + sqrt(one - pow2(z / n)) -
  79. log(one + sqrt(one - pow2(z / n))) //
  80. ) //
  81. ) //
  82. ) /
  83. log(10.) //
  84. );
  85. return lost_digits;
  86. }
  87. //******************************************************************************
  88. template <typename FloatType>
  89. int getNStar(int nmax, std::complex<FloatType> z, const int valid_digits) {
  90. if (nmax == 0)
  91. nmax = 1;
  92. int nstar = nmax;
  93. auto z_dp = ConvertComplex<double>(z);
  94. if (std::abs(z_dp) == 0.)
  95. return nstar;
  96. int forwardLoss = evalKapteynNumberOfLostSignificantDigits(nmax, z_dp);
  97. int increment = static_cast<int>(
  98. std::ceil(std::max(4 * std::pow(std::abs(z_dp), 1 / 3.0), 5.0)));
  99. int backwardLoss = evalKapteynNumberOfLostSignificantDigits(nstar, z_dp);
  100. while (backwardLoss - forwardLoss < valid_digits) {
  101. nstar += increment;
  102. backwardLoss = evalKapteynNumberOfLostSignificantDigits(nstar, z_dp);
  103. };
  104. return nstar;
  105. }
  106. //******************************************************************************
  107. // Custom implementation of complex cot function to avoid overflow
  108. // if Im(z) < 0, then it evaluates cot(z) as conj(cot(conj(z)))
  109. // see Eqs. 10-12 of [1] for details.
  110. // [1]H. Du, Mie-Scattering Calculation, Appl. Opt. 43, 1951 (2004).
  111. //******************************************************************************
  112. template <typename FloatType>
  113. std::complex<FloatType> complex_cot(const std::complex<FloatType> z) {
  114. auto Remx = z.real();
  115. auto Immx = z.imag();
  116. int sign =
  117. (Immx > 0) ? 1 : -1; // use complex conj if needed for exp and return
  118. auto exp = nmm::exp(-2 * sign * Immx);
  119. auto tan = nmm::tan(Remx);
  120. auto a = tan - exp * tan;
  121. auto b = 1 + exp;
  122. auto c = -1 + exp;
  123. auto d = tan + exp * tan;
  124. auto c_one = std::complex<FloatType>(0, 1);
  125. return (a * c + b * d) / (pow2(c) + pow2(d)) +
  126. c_one * (sign * (b * c - a * d) / (pow2(c) + pow2(d)));
  127. }
  128. //******************************************************************************
  129. // Forward iteration for evaluation of ratio of the Riccati–Bessel functions
  130. //******************************************************************************
  131. template <typename FloatType>
  132. void evalForwardR(const std::complex<FloatType> z,
  133. std::vector<std::complex<FloatType>>& r) {
  134. if (r.size() < 1)
  135. throw std::invalid_argument(
  136. "We need non-zero evaluations of ratio of the Riccati–Bessel "
  137. "functions.\n");
  138. // r0 = cot(z)
  139. // r[0] = nmm::cos(z)/nmm::sin(z);
  140. r[0] = complex_cot(z);
  141. for (unsigned int n = 0; n < r.size() - 1; n++) {
  142. r[n + 1] = static_cast<FloatType>(1) /
  143. ( //
  144. static_cast<FloatType>(2 * n + 1) / z //
  145. - r[n] //
  146. );
  147. }
  148. }
  149. //******************************************************************************
  150. // Backward iteration for evaluation of ratio of the Riccati–Bessel functions
  151. //******************************************************************************
  152. template <typename FloatType>
  153. void evalBackwardR(const std::complex<FloatType> z,
  154. std::vector<std::complex<FloatType>>& r) {
  155. if (r.size() < 1)
  156. throw std::invalid_argument(
  157. "We need non-zero evaluations of ratio of the Riccati-Bessel "
  158. "functions.\n");
  159. int nmax = r.size() - 1;
  160. auto tmp = static_cast<FloatType>(2 * nmax + 1) / z;
  161. r[nmax] = tmp;
  162. for (int n = nmax - 1; n >= 0; n--) {
  163. r[n] = -static_cast<FloatType>(1) / r[n + 1] +
  164. static_cast<FloatType>(2 * n + 1) / z;
  165. }
  166. r[0] = complex_cot(z);
  167. }
  168. //******************************************************************************
  169. template <typename FloatType>
  170. void convertRtoD1(const std::complex<FloatType> z,
  171. std::vector<std::complex<FloatType>>& r,
  172. std::vector<std::complex<FloatType>>& D1) {
  173. if (D1.size() > r.size())
  174. throw std::invalid_argument(
  175. "Not enough elements in array of ratio of the Riccati–Bessel functions "
  176. "to convert it into logarithmic derivative array.\n");
  177. std::complex<FloatType> D_old;
  178. for (unsigned int n = 0; n < D1.size(); n++) {
  179. D_old = D1[n];
  180. D1[n] = r[n] - static_cast<FloatType>(n) / z;
  181. // std::cout << "D1old - D1[n] :" << D_old-D1[n] << '\n';
  182. }
  183. }
  184. //******************************************************************************
  185. template <typename FloatType>
  186. void evalForwardD(const std::complex<FloatType> z,
  187. std::vector<std::complex<FloatType>>& D) {
  188. int nmax = D.size();
  189. FloatType one = static_cast<FloatType>(1);
  190. for (int n = 1; n < nmax; n++) {
  191. FloatType nf = static_cast<FloatType>(n);
  192. D[n] = one / (nf / z - D[n - 1]) - nf / z;
  193. }
  194. }
  195. //******************************************************************************
  196. template <typename FloatType>
  197. void evalForwardD1(const std::complex<FloatType> z,
  198. std::vector<std::complex<FloatType>>& D) {
  199. if (D.size() < 1)
  200. throw std::invalid_argument("Should have a leas one element!\n");
  201. D[0] = nmm::cos(z) / nmm::sin(z);
  202. evalForwardD(z, D);
  203. }
  204. // template <typename FloatType>
  205. // void MultiLayerMie<FloatType>::calcD1D3(const std::complex<FloatType> z,
  206. // std::vector<std::complex<FloatType> > &D1,
  207. // std::vector<std::complex<FloatType> > &D3) {
  208. //
  209. // // if (cabs(D1[0]) > 1.0e15) {
  210. // // throw std::invalid_argument("Unstable D1! Please, try to change input
  211. // parameters!\n");
  212. // // //printf("Warning: Potentially unstable D1! Please, try to change input
  213. // parameters!\n");
  214. // // }
  215. //
  216. // // Upward recurrence for PsiZeta and D3 - equations (18a) - (18d)
  217. // PsiZeta_[0] = static_cast<FloatType>(0.5)*(static_cast<FloatType>(1.0) -
  218. // std::complex<FloatType>(nmm::cos(2.0*z.real()), nmm::sin(2.0*z.real()))
  219. // *static_cast<FloatType>(nmm::exp(-2.0*z.imag())));
  220. // D3[0] = std::complex<FloatType>(0.0, 1.0);
  221. //
  222. // for (int n = 1; n <= nmax_; n++) {
  223. // PsiZeta_[n] = PsiZeta_[n - 1]*(static_cast<FloatType>(n)*z_inv - D1[n -
  224. // 1])
  225. // *(static_cast<FloatType>(n)*z_inv - D3[n -
  226. // 1]);
  227. // D3[n] = D1[n] + std::complex<FloatType>(0.0, 1.0)/PsiZeta_[n];
  228. // }
  229. // }
  230. //
  231. //
  232. //*******************************************************************************
  233. // This function calculates the Riccati-Bessel functions (Psi and Zeta) for a
  234. // complex argument (z).
  235. // Equations (20a) - (21b)
  236. //
  237. // Input parameters:
  238. // z: Complex argument to evaluate Psi and Zeta
  239. // nmax: Maximum number of terms to calculate Psi and Zeta
  240. //
  241. // Output parameters:
  242. // Psi, Zeta: Riccati-Bessel functions
  243. //*******************************************************************************
  244. // template <typename FloatType>
  245. // void MultiLayerMie<FloatType>::calcPsiZeta(std::complex<FloatType> z,
  246. // std::vector<std::complex<FloatType> > &Psi,
  247. // std::vector<std::complex<FloatType> > &Zeta)
  248. // {
  249. //
  250. // std::complex<FloatType> c_i(0.0, 1.0);
  251. // std::vector<std::complex<FloatType> > D1(nmax_ + 1), D3(nmax_ + 1);
  252. //
  253. // // First, calculate the logarithmic derivatives
  254. // calcD1D3(z, D1, D3);
  255. //
  256. // // Now, use the upward recurrence to calculate Psi and Zeta - equations
  257. // (20a) - (21b) Psi[0] = nmm::sin(z); Zeta[0] = nmm::sin(z) -
  258. // c_i*nmm::cos(z); for (int n = 1; n <= nmax_; n++) {
  259. // Psi[n] = Psi[n - 1]*(std::complex<FloatType>(n,0.0)/z - D1[n - 1]);
  260. // Zeta[n] = Zeta[n - 1]*(std::complex<FloatType>(n,0.0)/z - D3[n - 1]);
  261. // }
  262. // }
  263. //**********************************************************************************
  264. // This function calculates Pi and Tau for a given value of cos(Theta).
  265. // Equations (26a) - (26c)
  266. //
  267. // Input parameters:
  268. // nmax_: Maximum number of terms to calculate Pi and Tau
  269. // nTheta: Number of scattering angles
  270. // Theta: Array containing all the scattering angles where the scattering
  271. // amplitudes will be calculated
  272. //
  273. // Output parameters:
  274. // Pi, Tau: Angular functions Pi and Tau, as defined in equations (26a) -
  275. // (26c)
  276. //**********************************************************************************
  277. // template <typename FloatType>
  278. // void MultiLayerMie<FloatType>::calcPiTau(const FloatType &costheta,
  279. // std::vector<FloatType> &Pi,
  280. // std::vector<FloatType> &Tau) {
  281. //
  282. // int i;
  283. // //****************************************************//
  284. // // Equations (26a) - (26c) //
  285. // //****************************************************//
  286. // // Initialize Pi and Tau
  287. // Pi[0] = 1.0; // n=1
  288. // Tau[0] = costheta;
  289. // // Calculate the actual values
  290. // if (nmax_ > 1) {
  291. // Pi[1] = 3*costheta*Pi[0]; //n=2
  292. // Tau[1] = 2*costheta*Pi[1] - 3*Pi[0];
  293. // for (i = 2; i < nmax_; i++) { //n=[3..nmax_]
  294. // Pi[i] = ((i + i + 1)*costheta*Pi[i - 1] - (i + 1)*Pi[i - 2])/i;
  295. // Tau[i] = (i + 1)*costheta*Pi[i] - (i + 2)*Pi[i - 1];
  296. // }
  297. // }
  298. // } // end of MultiLayerMie::calcPiTau(...)
  299. //**********************************************************************************
  300. // This function calculates vector spherical harmonics (eq. 4.50, p. 95 BH),
  301. // required to calculate the near-field parameters.
  302. //
  303. // Input parameters:
  304. // Rho: Radial distance
  305. // Phi: Azimuthal angle
  306. // Theta: Polar angle
  307. // rn: Either the spherical Ricatti-Bessel function of first or third kind
  308. // Dn: Logarithmic derivative of rn
  309. // Pi, Tau: Angular functions Pi and Tau
  310. // n: Order of vector spherical harmonics
  311. //
  312. // Output parameters:
  313. // Mo1n, Me1n, No1n, Ne1n: Complex vector spherical harmonics
  314. //**********************************************************************************
  315. // template <typename FloatType>
  316. // void MultiLayerMie<FloatType>::calcSpherHarm(const std::complex<FloatType>
  317. // Rho, const FloatType Theta, const FloatType Phi,
  318. // const std::complex<FloatType> &rn, const
  319. // std::complex<FloatType> &Dn, const
  320. // FloatType &Pi, const FloatType &Tau, const
  321. // FloatType &n,
  322. // std::vector<std::complex<FloatType> >&
  323. // Mo1n, std::vector<std::complex<FloatType>
  324. // >& Me1n,
  325. // std::vector<std::complex<FloatType> >&
  326. // No1n, std::vector<std::complex<FloatType>
  327. // >& Ne1n) {
  328. //
  329. // // using eq 4.50 in BH
  330. // std::complex<FloatType> c_zero(0.0, 0.0);
  331. //
  332. // using nmm::sin;
  333. // using nmm::cos;
  334. // Mo1n[0] = c_zero;
  335. // Mo1n[1] = cos(Phi)*Pi*rn/Rho;
  336. // Mo1n[2] = -sin(Phi)*Tau*rn/Rho;
  337. //
  338. // Me1n[0] = c_zero;
  339. // Me1n[1] = -sin(Phi)*Pi*rn/Rho;
  340. // Me1n[2] = -cos(Phi)*Tau*rn/Rho;
  341. //
  342. // No1n[0] = sin(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
  343. // No1n[1] = sin(Phi)*Tau*Dn*rn/Rho;
  344. // No1n[2] = cos(Phi)*Pi*Dn*rn/Rho;
  345. //
  346. // Ne1n[0] = cos(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
  347. // Ne1n[1] = cos(Phi)*Tau*Dn*rn/Rho;
  348. // Ne1n[2] = -sin(Phi)*Pi*Dn*rn/Rho;
  349. // } // end of MultiLayerMie::calcSpherHarm(...)
  350. //
  351. //******************************************************************************
  352. // This functions calculate the logarithmic derivatives of the Riccati-Bessel
  353. // functions (D1 and D3) for a complex argument (z).
  354. // Equations (16a), (16b) and (18a) - (18d)
  355. //
  356. // Input parameters:
  357. // z: Complex argument to evaluate D1 and D3
  358. // nmax_: Maximum number of terms to calculate D1 and D3
  359. //
  360. // Output parameters:
  361. // D1, D3: Logarithmic derivatives of the Riccati-Bessel functions
  362. //******************************************************************************
  363. template <typename FloatType>
  364. void evalDownwardD1(const std::complex<FloatType> z,
  365. std::vector<std::complex<FloatType>>& D1) {
  366. int nmax = D1.size() - 1;
  367. int valid_digits = 16;
  368. #ifdef MULTI_PRECISION
  369. valid_digits += MULTI_PRECISION;
  370. #endif
  371. int nstar = nmie::getNStar(nmax, z, valid_digits);
  372. D1.resize(nstar + 1);
  373. // Downward recurrence for D1 - equations (16a) and (16b)
  374. D1[nstar] = std::complex<FloatType>(0.0, 0.0);
  375. std::complex<FloatType> c_one(1.0, 0.0);
  376. const std::complex<FloatType> z_inv = std::complex<FloatType>(1.0, 0.0) / z;
  377. for (unsigned int n = nstar; n > 0; n--) {
  378. D1[n - 1] = static_cast<FloatType>(n) * z_inv -
  379. c_one / (D1[n] + static_cast<FloatType>(n) * z_inv);
  380. }
  381. // Use D1[0] from upward recurrence
  382. D1[0] = complex_cot(z);
  383. D1.resize(nmax + 1);
  384. // printf("D1[0] = (%16.15g, %16.15g) z=(%16.15g,%16.15g)\n",
  385. // D1[0].real(),D1[0].imag(),
  386. // z.real(),z.imag());
  387. }
  388. //******************************************************************************
  389. template <typename FloatType>
  390. void evalUpwardD3(const std::complex<FloatType> z,
  391. const std::vector<std::complex<FloatType>>& D1,
  392. std::vector<std::complex<FloatType>>& D3,
  393. std::vector<std::complex<FloatType>>& PsiZeta) {
  394. int nmax = D1.size() - 1;
  395. // Upward recurrence for PsiZeta and D3 - equations (18a) - (18d)
  396. PsiZeta[0] = static_cast<FloatType>(0.5) *
  397. (static_cast<FloatType>(1.0) -
  398. std::complex<FloatType>(nmm::cos(2.0 * z.real()),
  399. nmm::sin(2.0 * z.real())) *
  400. static_cast<FloatType>(nmm::exp(-2.0 * z.imag())));
  401. D3[0] = std::complex<FloatType>(0.0, 1.0);
  402. const std::complex<FloatType> z_inv = std::complex<FloatType>(1.0, 0.0) / z;
  403. for (int n = 1; n <= nmax; n++) {
  404. PsiZeta[n] = PsiZeta[n - 1] *
  405. (static_cast<FloatType>(n) * z_inv - D1[n - 1]) *
  406. (static_cast<FloatType>(n) * z_inv - D3[n - 1]);
  407. D3[n] = D1[n] + std::complex<FloatType>(0.0, 1.0) / PsiZeta[n];
  408. }
  409. }
  410. //******************************************************************************
  411. template <typename FloatType>
  412. std::complex<FloatType> complex_sin(const std::complex<FloatType> z) {
  413. auto a = z.real();
  414. auto b = z.imag();
  415. auto i = std::complex<FloatType>(0, 1);
  416. using nmm::cos;
  417. using nmm::exp;
  418. using nmm::sin;
  419. return ((cos(a) + i * sin(a)) * exp(-b) - (cos(-a) + i * sin(-a)) * exp(b)) /
  420. (static_cast<FloatType>(2) * i);
  421. }
  422. //******************************************************************************
  423. template <typename FloatType>
  424. void evalUpwardPsi(const std::complex<FloatType> z,
  425. const std::vector<std::complex<FloatType>> D1,
  426. std::vector<std::complex<FloatType>>& Psi) {
  427. int nmax = Psi.size() - 1;
  428. // Now, use the upward recurrence to calculate Psi and Zeta - equations (20a)
  429. // - (21b)
  430. Psi[0] = complex_sin(z);
  431. for (int n = 1; n <= nmax; n++) {
  432. Psi[n] = Psi[n - 1] * (std::complex<FloatType>(n, 0.0) / z - D1[n - 1]);
  433. }
  434. }
  435. //******************************************************************************
  436. // Sometimes in literature Zeta is also denoted as Ksi, it is a Riccati-Bessel
  437. // function of third kind.
  438. //******************************************************************************
  439. template <typename FloatType>
  440. void evalUpwardZeta(const std::complex<FloatType> z,
  441. const std::vector<std::complex<FloatType>> D3,
  442. std::vector<std::complex<FloatType>>& Zeta) {
  443. int nmax = Zeta.size() - 1;
  444. std::complex<FloatType> c_i(0.0, 1.0);
  445. // Now, use the upward recurrence to calculate Zeta and Zeta - equations (20a)
  446. // - (21b)
  447. Zeta[0] = nmm::sin(z) - c_i * nmm::cos(z);
  448. for (int n = 1; n <= nmax; n++) {
  449. Zeta[n] = Zeta[n - 1] * (std::complex<FloatType>(n, 0.0) / z - D3[n - 1]);
  450. }
  451. }
  452. //******************************************************************************
  453. // void evalForwardRiccatiBessel(const FloatType x, const FloatType first, const
  454. // FloatType second,
  455. // std::vector<FloatType> &values) {
  456. // values[0] = first;
  457. // values[1] = second;
  458. // int nmax = values.size();
  459. // for (int i = 1; i < nmax-1; i++) {
  460. // values[i+1] = (1 + 2*i) * values[i]/x - values[i-1];
  461. // }
  462. // }
  463. //
  464. //******************************************************************************
  465. // void evalChi(const FloatType x, std::vector<FloatType> &Chi) {
  466. // auto first = nmm::cos(x);
  467. // auto second = first/x + nmm::sin(x);
  468. // evalForwardRiccatiBessel(x, first, second, Chi);
  469. // }
  470. //
  471. //******************************************************************************
  472. // void evalPsi(const FloatType x, std::vector<FloatType> &Psi) {
  473. // auto first = nmm::sin(x);
  474. // auto second = first/x - nmm::cos(x);
  475. // evalForwardRiccatiBessel(x, first, second, Psi);
  476. // }
  477. //
  478. //******************************************************************************
  479. // void composeZeta(const std::vector<FloatType> &Psi,
  480. // const std::vector<FloatType> &Chi,
  481. // std::vector< std::complex<FloatType>> &Zeta) {
  482. // int nmax = Zeta.size();
  483. // for (int i = 0; i < nmax; i++) {
  484. // Zeta[i] = std::complex<FloatType > (Psi[i], Chi[i]);
  485. // }
  486. // }
  487. //******************************************************************************
  488. template <typename FloatType>
  489. void evalPsiZetaD1D3(const std::complex<FloatType> cxd,
  490. std::vector<std::complex<FloatType>>& Psi,
  491. std::vector<std::complex<FloatType>>& Zeta,
  492. std::vector<std::complex<FloatType>>& D1,
  493. std::vector<std::complex<FloatType>>& D3) {
  494. int nmax = Psi.size() - 1;
  495. std::vector<std::complex<FloatType>> PsiZeta(nmax + 1);
  496. for (int n = 0; n <= nmax; n++) {
  497. D1[n] = std::complex<FloatType>(0.0, -1.0);
  498. D3[n] = std::complex<FloatType>(0.0, 1.0);
  499. }
  500. evalDownwardD1(cxd, D1);
  501. evalUpwardPsi(cxd, D1, Psi);
  502. evalUpwardD3(cxd, D1, D3, PsiZeta);
  503. for (unsigned int i = 0; i < Zeta.size(); i++) {
  504. Zeta[i] = PsiZeta[i] / Psi[i];
  505. }
  506. }
  507. } // end of namespace nmie
  508. #endif // SRC_SPECIAL_FUNCTIONS_IMPL_HPP_