test_Riccati_Bessel_logarithmic_derivative.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. #include "gtest/gtest.h"
  2. #include "../src/nmie-impl.hpp"
  3. #include "test_spec_functions_data.hpp"
  4. // From W. Yang APPLIED OPTICS Vol. 42, No. 9, 20 March 2003
  5. // Dtest refractive index is m={1.05,1}, the size parameter is x = 80
  6. std::vector<int> Dtest_n({0,1,30,50,60,70,75,80,85,90,99,116,130});
  7. std::vector< std::complex<double>>
  8. Dtest_D1({
  9. //Orig
  10. // {0.11449e-15 ,-0.10000e+01 },{0.74646e-04 ,-0.10000e+01 },
  11. // {0.34764e-01 ,-0.99870},{0.95292e-01 ,-0.99935},
  12. // {0.13645,-0.10019e+01 },{0.18439,-0.10070e+01 },
  13. // {0.21070,-0.10107e+01 },{0.23845,-0.10154e+01 },
  14. // {0.26752,-0.10210e+01 },{0.29777,-0.10278e+01 },
  15. // {0.35481,-0.10426e+01 },{0.46923,-0.10806e+01 },
  16. // {0.17656,-0.13895e+01 }
  17. // mod (from Python mpmath)
  18. {0.0,-1.0}, {7.464603828e-5,-0.9999958865},
  19. {0.03476380918,-0.9986960672},{0.09529213152,-0.999347654},
  20. {0.1364513887,-1.001895883},{0.184388335,-1.006979164},
  21. {0.2107044267,-1.01072099},{0.2384524295,-1.015382914},
  22. {0.2675164524,-1.021040337},{0.2977711192,-1.027753418},
  23. {0.3548096904,-1.042622957},{0.4692294405,-1.080629479},
  24. {0.5673827836,-1.121108944},
  25. });
  26. std::vector< std::complex<double>>
  27. Dtest_D2({{0.64966e-69 ,-0.10000e+01 },{0.74646e-04 ,-0.10000e+01 },
  28. {0.34764e-01 ,-0.99870},{0.95292e-01 ,-0.99935},
  29. {0.13645,-0.10019e+01 },{0.17769,-0.10099e+01 },
  30. {0.41264e-01 ,-0.21076e+01 },{-0.20190,0.10435e+01 },
  31. {-0.26343,0.10223e+01 },{-0.29339,0.10291e+01 },
  32. {-0.34969,0.10437e+01 },{-0.46296,0.10809e+01 },
  33. {-0.56047,0.11206e+01 }});
  34. std::vector< std::complex<double>>
  35. Dtest_D3({{0.00000,0.10000e+01 },{-0.73809e-04 ,0.10000e+01 },
  36. {-0.34344e-01 ,0.99912},{-0.94022e-01 ,0.10004e+01 },
  37. {-0.13455,0.10032e+01 },{-0.18172,0.10084e+01 },
  38. {-0.20762,0.10122e+01 },{-0.23494,0.10169e+01 },
  39. {-0.26357,0.10225e+01 },{-0.29339,0.10291e+01 },
  40. {-0.34969,0.10437e+01 },{-0.46296,0.10809e+01 },
  41. {-0.56047,0.11206e+01 }});
  42. void parse_mpmath_data(const double min_abs_tol, const std::tuple< std::complex<double>, int, std::complex<double>, double, double > data,
  43. std::complex<double> &z, int &n, std::complex<double> &func_mp,
  44. double &re_abs_tol, double &im_abs_tol){
  45. z = std::get<0>(data);
  46. n = std::get<1>(data);
  47. func_mp = std::get<2>(data);
  48. re_abs_tol = ( std::get<3>(data) > min_abs_tol && std::real(func_mp) < min_abs_tol)
  49. ? std::get<3>(data) : min_abs_tol;
  50. im_abs_tol = ( std::get<4>(data) > min_abs_tol && std::imag(func_mp) < min_abs_tol)
  51. ? std::get<4>(data) : min_abs_tol;
  52. // if re(func_mp) < 0.5 then round will give 0. To avoid zero tolerance add one.
  53. re_abs_tol *= std::abs(std::round(std::real(func_mp))) + 1;
  54. im_abs_tol *= std::abs(std::round(std::imag(func_mp))) + 1;
  55. }
  56. void parse2_mpmath_data(const nmie::FloatType min_abs_tol,
  57. const std::tuple< nmie::FloatType, std::complex<nmie::FloatType>, int, std::complex<nmie::FloatType>, nmie::FloatType, nmie::FloatType > data,
  58. nmie::FloatType &x, std::complex<nmie::FloatType> &m, int &n, std::complex<nmie::FloatType> &func_mp,
  59. nmie::FloatType &re_abs_tol, nmie::FloatType &im_abs_tol){
  60. x = std::get<0>(data);
  61. m = std::get<1>(data);
  62. n = std::get<2>(data);
  63. func_mp = std::get<3>(data);
  64. re_abs_tol = ( std::get<4>(data) > min_abs_tol && std::real(func_mp) < min_abs_tol)
  65. ? std::get<4>(data) : min_abs_tol;
  66. im_abs_tol = ( std::get<5>(data) > min_abs_tol && std::imag(func_mp) < min_abs_tol)
  67. ? std::get<5>(data) : min_abs_tol;
  68. // if re(func_mp) < 0.5 then round will give 0. To avoid zero tolerance add one.
  69. re_abs_tol *= std::abs(std::round(std::real(func_mp))) + 1;
  70. im_abs_tol *= std::abs(std::round(std::imag(func_mp))) + 1;
  71. }
  72. template<class T> inline T pow2(const T value) {return value*value;}
  73. TEST(an_test, DISABLED_mpmath_generated_input) {
  74. //TEST(an_test, mpmath_generated_input) {
  75. double min_abs_tol = 3e-14, x;
  76. std::complex<double> m, an_mp;
  77. int n;
  78. double re_abs_tol, im_abs_tol;
  79. for (const auto &data : an_test_30digits) {
  80. parse2_mpmath_data(min_abs_tol, data, x, m, n, an_mp, re_abs_tol, im_abs_tol);
  81. auto Nstop = nmie::LeRu_cutoff(m*x)+1;
  82. nmie::MultiLayerMie<nmie::FloatType> ml_mie;
  83. ml_mie.SetLayersSize({x});
  84. ml_mie.SetLayersIndex({m});
  85. ml_mie.SetMaxTerms(Nstop);
  86. ml_mie.calcScattCoeffs();
  87. auto an = ml_mie.GetAn();
  88. // auto bn = ml_mie.GetBn();
  89. if (n > an.size()) continue;
  90. if (n == 0) continue;
  91. EXPECT_NEAR(std::real(an[n-1]), std::real(an_mp), re_abs_tol)
  92. << "Db at n=" << n << " Nstop="<< Nstop<<" m="<<m<<" x="<<x;
  93. EXPECT_NEAR(std::imag(an[n-1]), std::imag(an_mp), im_abs_tol)
  94. << "Db at n=" << n << " Nstop="<< Nstop<<" m="<<m<<" x="<<x;
  95. }
  96. }
  97. //TEST(bn_test, DISABLED_mpmath_generated_input) {
  98. TEST(bn_test, mpmath_generated_input) {
  99. double min_abs_tol = 3e-14, x;
  100. std::complex<double> m, bn_mp;
  101. int n;
  102. double re_abs_tol, im_abs_tol;
  103. for (const auto &data : bn_test_30digits) {
  104. parse2_mpmath_data(min_abs_tol, data, x, m, n, bn_mp, re_abs_tol, im_abs_tol);
  105. auto Nstop = nmie::LeRu_cutoff(m*x)+1;
  106. nmie::MultiLayerMie<nmie::FloatType> ml_mie;
  107. ml_mie.SetLayersSize({x});
  108. ml_mie.SetLayersIndex({m});
  109. ml_mie.SetMaxTerms(Nstop);
  110. ml_mie.calcScattCoeffs();
  111. // auto an = ml_mie.GetAn();
  112. auto bn = ml_mie.GetBn();
  113. if (n > bn.size()) continue;
  114. if (n == 0) continue;
  115. EXPECT_NEAR(std::real(bn[n-1]), std::real(bn_mp), re_abs_tol)
  116. << "Db at n=" << n << " Nstop="<< Nstop<<" m="<<m<<" x="<<x;
  117. EXPECT_NEAR(std::imag(bn[n-1]), std::imag(bn_mp), im_abs_tol)
  118. << "Db at n=" << n << " Nstop="<< Nstop<<" m="<<m<<" x="<<x;
  119. }
  120. }
  121. TEST(zeta_psizeta_test, DISABLED_mpmath_generated_input) {
  122. //TEST(zeta_psizeta_test, mpmath_generated_input) {
  123. double min_abs_tol = 2e-10;
  124. std::complex<double> z, zeta_mp;
  125. int n;
  126. double re_abs_tol, im_abs_tol;
  127. for (const auto &data : zeta_test_16digits) {
  128. parse_mpmath_data(min_abs_tol, data, z, n, zeta_mp, re_abs_tol, im_abs_tol);
  129. auto Nstop = nmie::LeRu_cutoff(z)+10000;
  130. if (n > Nstop) continue;
  131. std::vector<std::complex<nmie::FloatType>> D1dr(Nstop+135), D3(Nstop+135),
  132. PsiZeta(Nstop+135), Psi(Nstop);
  133. nmie::evalDownwardD1(z, D1dr);
  134. nmie::evalUpwardD3(z, D1dr, D3, PsiZeta);
  135. nmie::evalUpwardPsi(z, D1dr, Psi);
  136. auto a = std::real(PsiZeta[n]);
  137. auto b = std::imag(PsiZeta[n]);
  138. auto c = std::real(Psi[n]);
  139. auto d = std::imag(Psi[n]);
  140. auto c_one = std::complex<nmie::FloatType>(0, 1);
  141. auto zeta = (a*c + b*d)/(pow2(c) + pow2(d)) +
  142. c_one * ((b*c - a*d)/(pow2(c) + pow2(d)));
  143. // zeta = PsiZeta[n]/Psi[n];
  144. if (std::isnan(std::real(zeta)) || std::isnan(std::imag(zeta))) continue;
  145. // std::vector<std::complex<nmie::FloatType>> D1dr(Nstop+35), D3(Nstop+35), zeta(Nstop);
  146. // nmie::evalDownwardD1(z, D1dr);
  147. // nmie::evalUpwardD3(z, D1dr, D3);
  148. // nmie::evalUpwardZeta(z, D3, zeta);
  149. EXPECT_NEAR(std::real(zeta), std::real(zeta_mp), re_abs_tol)
  150. << "zeta at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  151. EXPECT_NEAR(std::imag(zeta), std::imag(zeta_mp), im_abs_tol)
  152. << "zeta at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  153. }
  154. }
  155. // Old way to evaluate Zeta
  156. TEST(zeta_test, DISABLED_mpmath_generated_input) {
  157. //TEST(zeta_test, mpmath_generated_input) {
  158. double min_abs_tol = 2e-5;
  159. std::complex<double> z, zeta_mp;
  160. int n;
  161. double re_abs_tol, im_abs_tol;
  162. for (const auto &data : zeta_test_16digits) {
  163. parse_mpmath_data(min_abs_tol, data, z, n, zeta_mp, re_abs_tol, im_abs_tol);
  164. auto Nstop = nmie::LeRu_cutoff(z)+10000;
  165. if (n > Nstop) continue;
  166. std::vector<std::complex<nmie::FloatType>> D1dr(Nstop), D3(Nstop),
  167. PsiZeta(Nstop), zeta(Nstop);
  168. nmie::evalDownwardD1(z, D1dr);
  169. nmie::evalUpwardD3(z, D1dr, D3, PsiZeta);
  170. nmie::evalUpwardZeta(z, D3, zeta);
  171. if (std::isnan(std::real(zeta[n])) || std::isnan(std::imag(zeta[n]))) continue;
  172. EXPECT_NEAR(std::real(zeta[n]), std::real(zeta_mp), re_abs_tol)
  173. << "zeta[n] at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  174. EXPECT_NEAR(std::imag(zeta[n]), std::imag(zeta_mp), im_abs_tol)
  175. << "zeta at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  176. }
  177. }
  178. TEST(psizeta_test, DISABLED_mpmath_generated_input) {
  179. //TEST(psizeta_test, mpmath_generated_input) {
  180. double min_abs_tol = 9e-11;
  181. std::complex<double> z, PsiZeta_mp;
  182. int n;
  183. double re_abs_tol, im_abs_tol;
  184. for (const auto &data : psi_mul_zeta_test_16digits) {
  185. parse_mpmath_data(min_abs_tol, data, z, n, PsiZeta_mp, re_abs_tol, im_abs_tol);
  186. auto Nstop = nmie::LeRu_cutoff(z)+10000;
  187. if (n > Nstop) continue;
  188. std::vector<std::complex<nmie::FloatType>> D1dr(Nstop), D3(Nstop), PsiZeta(Nstop);
  189. nmie::evalDownwardD1(z, D1dr);
  190. nmie::evalUpwardD3(z, D1dr, D3, PsiZeta);
  191. EXPECT_NEAR(std::real(PsiZeta[n]), std::real(PsiZeta_mp), re_abs_tol)
  192. << "PsiZeta at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  193. EXPECT_NEAR(std::imag(PsiZeta[n]), std::imag(PsiZeta_mp), im_abs_tol)
  194. << "PsiZeta at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  195. // std::vector<nmie::FloatType> PsiUp(Nstop);
  196. // nmie::evalPsi(std::real(z), PsiUp);
  197. // EXPECT_NEAR(((PsiUp[n])), std::real(PsiZeta_mp), re_abs_tol)
  198. // << "PsiZeta(up) at n=" << n << " z="<<z;
  199. }
  200. }
  201. TEST(psi_test, mpmath_generated_input) {
  202. double min_abs_tol = 1e-12;
  203. std::complex<double> z, Psi_mp;
  204. int n;
  205. double re_abs_tol, im_abs_tol;
  206. for (const auto &data : psi_test_16digits) {
  207. parse_mpmath_data(min_abs_tol, data, z, n, Psi_mp, re_abs_tol, im_abs_tol);
  208. auto Nstop = nmie::LeRu_cutoff(z)+10000;
  209. if (n > Nstop) continue;
  210. std::vector<std::complex<nmie::FloatType>> D1dr(Nstop+35), Psi(Nstop);
  211. nmie::evalDownwardD1(z, D1dr);
  212. nmie::evalUpwardPsi(z, D1dr, Psi);
  213. EXPECT_NEAR(std::real(Psi[n]), std::real(Psi_mp), re_abs_tol)
  214. << "Psi at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  215. EXPECT_NEAR(std::imag(Psi[n]), std::imag(Psi_mp), im_abs_tol)
  216. << "Psi at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  217. }
  218. }
  219. TEST(D3test, DISABLED_mpmath_generated_input) {
  220. //TEST(D3test, mpmath_generated_input) {
  221. double min_abs_tol = 2e-11;
  222. std::complex<double> z, D3_mp;
  223. int n;
  224. double re_abs_tol, im_abs_tol;
  225. for (const auto &data : D3_test_16digits) {
  226. parse_mpmath_data(min_abs_tol, data, z, n, D3_mp, re_abs_tol, im_abs_tol);
  227. auto Nstop = nmie::LeRu_cutoff(z)+35;
  228. std::vector<std::complex<nmie::FloatType>> D1dr(Nstop), D3(Nstop), PsiZeta(Nstop);
  229. nmie::evalDownwardD1(z, D1dr);
  230. nmie::evalUpwardD3(z, D1dr, D3, PsiZeta);
  231. EXPECT_NEAR(std::real(D3[n]), std::real(D3_mp), re_abs_tol)
  232. << "D3 at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  233. EXPECT_NEAR(std::imag(D3[n]), std::imag(D3_mp), im_abs_tol)
  234. << "D3 at n=" << n << " Nstop="<< Nstop<<" z="<<z;
  235. }
  236. }
  237. //TEST(D1test, DISABLED_mpmath_generated_input) {
  238. TEST(D1test, mpmath_generated_input) {
  239. double min_abs_tol = 2e-11, x;
  240. std::complex<double> m, z, D1_mp;
  241. int n;
  242. double re_abs_tol, im_abs_tol;
  243. for (const auto &data : D1_test_30digits) {
  244. parse2_mpmath_data(min_abs_tol, data, x, m, n, D1_mp, re_abs_tol, im_abs_tol);
  245. z = m*x;
  246. auto Nstop = nmie::LeRu_cutoff(z)+1;
  247. std::vector<std::complex<nmie::FloatType>> Db(Nstop),Dold(Nstop+135), r;
  248. int valid_digits = 14;
  249. int nstar = nmie::getNStar(Nstop, z, valid_digits);
  250. r.resize(nstar);
  251. nmie::evalBackwardR(z,r);
  252. nmie::convertRtoD1(z, r, Db);
  253. if (n > Db.size()) continue;
  254. EXPECT_NEAR(std::real(Db[n]), std::real(D1_mp), re_abs_tol)
  255. << "Db at n=" << n << " Nstop="<< Nstop<<" nstar="<<nstar<< " z="<<z;
  256. EXPECT_NEAR(std::imag(Db[n]), std::imag(D1_mp), im_abs_tol)
  257. << "Db at n=" << n << " Nstop="<< Nstop<<" nstar="<<nstar<< " z="<<z;
  258. nmie::evalDownwardD1(z, Dold);
  259. if (n > Dold.size()) continue;
  260. EXPECT_NEAR(std::real(Dold[n]), std::real(D1_mp), re_abs_tol)
  261. << "Dold at n=" << n << " Nstop="<< Nstop<< " z="<<z;
  262. EXPECT_NEAR(std::imag(Dold[n]), std::imag(D1_mp), im_abs_tol)
  263. << "Dold at n=" << n << " Nstop="<< Nstop<< " z="<<z;
  264. }
  265. }
  266. //TEST(D1test, DISABLED_WYang_data){
  267. TEST(D1test, WYang_data){
  268. double abs_tol = 1e-9;
  269. int test_loss_digits = std::round(15 - std::log10(1/abs_tol));
  270. int Nstop = 131;
  271. std::vector<std::complex<nmie::FloatType>> Df(Nstop), Db(Nstop),Dold(Nstop), r;
  272. std::complex<nmie::FloatType> z(1.05,1);
  273. z = z*80.0;
  274. // eval D1 directly from backward recurrence
  275. nmie::evalDownwardD1(z, Dold);
  276. // eval forward recurrence
  277. r.resize(Nstop+1);
  278. nmie::evalForwardR(z, r);
  279. nmie::convertRtoD1(z, r, Df);
  280. // eval backward recurrence
  281. int valid_digits = 6;
  282. int nstar = nmie::getNStar(Nstop, z, valid_digits);
  283. r.resize(nstar);
  284. nmie::evalBackwardR(z,r);
  285. nmie::convertRtoD1(z, r, Db);
  286. for (int i = 0; i < Dtest_n.size(); i++) {
  287. int n = Dtest_n[i];
  288. int forward_loss_digits = nmie::evalKapteynNumberOfLostSignificantDigits(n, z);
  289. forward_loss_digits += 3; // Kapteyn is too optimistic
  290. if (test_loss_digits > forward_loss_digits ) {
  291. EXPECT_NEAR(std::real(Df[n]), std::real(Dtest_D1[i]),
  292. abs_tol) << "f at n=" << n << " lost digits = " << forward_loss_digits;
  293. EXPECT_NEAR(std::imag(Df[n]), std::imag(Dtest_D1[i]),
  294. abs_tol) << "f at n=" << n << " lost digits = " << forward_loss_digits;
  295. }
  296. EXPECT_NEAR(std::real(Db[n]), std::real(Dtest_D1[i]),
  297. abs_tol) << "b at n=" << n;
  298. EXPECT_NEAR(std::imag(Db[n]), std::imag(Dtest_D1[i]),
  299. abs_tol) << "b at n=" << n;
  300. if (n < Dold.size()-15) {
  301. EXPECT_NEAR(std::real(Dold[n]), std::real(Dtest_D1[i]),
  302. abs_tol) << "old at n=" << n;
  303. EXPECT_NEAR(std::imag(Dold[n]), std::imag(Dtest_D1[i]),
  304. abs_tol) << "old at n=" << n;
  305. }
  306. }
  307. }
  308. TEST(KaptyenTest, HandlesInput) {
  309. // H.Du APPLIED OPTICS, Vol. 43, No. 9, 20 March 2004
  310. double l = nmie::evalKapteynNumberOfLostSignificantDigits(80, std::complex<double>(100,100));
  311. EXPECT_EQ(l, 7)<<"Should be equal";
  312. std::complex<double> z(10000,0);
  313. l = nmie::evalKapteynNumberOfLostSignificantDigits(5070, z);
  314. EXPECT_EQ(l, 0)<<"Should be equal";
  315. // find NStar such that l_nstar(z) - l_nmax(z) >= valid_digits
  316. int NStar = nmie::getNStar(5070, z,6);
  317. EXPECT_GE(NStar, 10130);
  318. // const double pi=3.14159265358979323846;
  319. // z = std::complex<double>(100,100);
  320. // l = nmie::evalKapteynNumberOfLostSignificantDigits(1, z);
  321. // EXPECT_EQ(l, 0)<<"Should be equal";
  322. }
  323. int main(int argc, char **argv) {
  324. testing::InitGoogleTest(&argc, argv);
  325. return RUN_ALL_TESTS();
  326. }