nmie-applied.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. ///
  2. /// @file nmie.cc
  3. /// @author Ladutenko Konstantin <kostyfisik at gmail (.) com>
  4. /// @date Tue Sep 3 00:38:27 2013
  5. /// @copyright 2013,2014,2015 Ladutenko Konstantin
  6. ///
  7. /// nmie 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. /// nmie-wrapper 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. /// You should have received a copy of the GNU General Public License
  18. /// along with nmie-wrapper. If not, see <http://www.gnu.org/licenses/>.
  19. ///
  20. /// nmie uses nmie.c from scattnlay by Ovidio Pena
  21. /// <ovidio@bytesfall.com> . He has an additional condition to
  22. /// his library:
  23. // The only additional condition is that we expect that all publications //
  24. // describing work using this software , or all commercial products //
  25. // using it, cite the following reference: //
  26. // [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by //
  27. // a multilayered sphere," Computer Physics Communications, //
  28. // vol. 180, Nov. 2009, pp. 2348-2354. //
  29. ///
  30. /// @brief Wrapper class around nMie function for ease of use
  31. ///
  32. #include "nmie-applied.h"
  33. #include <array>
  34. #include <algorithm>
  35. #include <cstdio>
  36. #include <cstdlib>
  37. #include <stdexcept>
  38. #include <vector>
  39. namespace nmie {
  40. //**********************************************************************************//
  41. // This function emulates a C call to calculate the actual scattering parameters //
  42. // and amplitudes. //
  43. // //
  44. // Input parameters: //
  45. // L: Number of layers //
  46. // pl: Index of PEC layer. If there is none just send -1 //
  47. // x: Array containing the size parameters of the layers [0..L-1] //
  48. // m: Array containing the relative refractive indexes of the layers [0..L-1] //
  49. // nTheta: Number of scattering angles //
  50. // Theta: Array containing all the scattering angles where the scattering //
  51. // amplitudes will be calculated //
  52. // nmax: Maximum number of multipolar expansion terms to be used for the //
  53. // calculations. Only use it if you know what you are doing, otherwise //
  54. // set this parameter to -1 and the function will calculate it //
  55. // //
  56. // Output parameters: //
  57. // Qext: Efficiency factor for extinction //
  58. // Qsca: Efficiency factor for scattering //
  59. // Qabs: Efficiency factor for absorption (Qabs = Qext - Qsca) //
  60. // Qbk: Efficiency factor for backscattering //
  61. // Qpr: Efficiency factor for the radiation pressure //
  62. // g: Asymmetry factor (g = (Qext-Qpr)/Qsca) //
  63. // Albedo: Single scattering albedo (Albedo = Qsca/Qext) //
  64. // S1, S2: Complex scattering amplitudes //
  65. // //
  66. // Return value: //
  67. // Number of multipolar expansion terms used for the calculations //
  68. //**********************************************************************************//
  69. int nMieApplied(const unsigned int L, const int pl, std::vector<double>& x, std::vector<std::complex<double> >& m, const unsigned int nTheta, std::vector<double>& Theta, const int nmax, double *Qext, double *Qsca, double *Qabs, double *Qbk, double *Qpr, double *g, double *Albedo, std::vector<std::complex<double> >& S1, std::vector<std::complex<double> >& S2) {
  70. if (x.size() != L || m.size() != L)
  71. throw std::invalid_argument("Declared number of layers do not fit x and m!");
  72. if (Theta.size() != nTheta)
  73. throw std::invalid_argument("Declared number of sample for Theta is not correct!");
  74. try {
  75. MultiLayerMieApplied ml_mie;
  76. ml_mie.SetAllLayersSize(x);
  77. ml_mie.SetAllLayersIndex(m);
  78. ml_mie.SetAngles(Theta);
  79. ml_mie.SetPECLayer(pl);
  80. ml_mie.SetMaxTerms(nmax);
  81. ml_mie.RunMieCalculation();
  82. *Qext = ml_mie.GetQext();
  83. *Qsca = ml_mie.GetQsca();
  84. *Qabs = ml_mie.GetQabs();
  85. *Qbk = ml_mie.GetQbk();
  86. *Qpr = ml_mie.GetQpr();
  87. *g = ml_mie.GetAsymmetryFactor();
  88. *Albedo = ml_mie.GetAlbedo();
  89. S1 = ml_mie.GetS1();
  90. S2 = ml_mie.GetS2();
  91. return ml_mie.GetMaxTerms();
  92. } catch(const std::invalid_argument& ia) {
  93. // Will catch if ml_mie fails or other errors.
  94. std::cerr << "Invalid argument: " << ia.what() << std::endl;
  95. throw std::invalid_argument(ia);
  96. return -1;
  97. }
  98. return 0;
  99. }
  100. int nMieApplied(const unsigned int L, std::vector<double>& x, std::vector<std::complex<double> >& m, const unsigned int nTheta, std::vector<double>& Theta, double *Qext, double *Qsca, double *Qabs, double *Qbk, double *Qpr, double *g, double *Albedo, std::vector<std::complex<double> >& S1, std::vector<std::complex<double> >& S2) {
  101. return nmie::nMieApplied(L, -1, x, m, nTheta, Theta, -1, Qext, Qsca, Qabs, Qbk, Qpr, g, Albedo, S1, S2);
  102. }
  103. int nMieApplied(const unsigned int L, const int pl, std::vector<double>& x, std::vector<std::complex<double> >& m, const unsigned int nTheta, std::vector<double>& Theta, double *Qext, double *Qsca, double *Qabs, double *Qbk, double *Qpr, double *g, double *Albedo, std::vector<std::complex<double> >& S1, std::vector<std::complex<double> >& S2) {
  104. return nmie::nMieApplied(L, pl, x, m, nTheta, Theta, -1, Qext, Qsca, Qabs, Qbk, Qpr, g, Albedo, S1, S2);
  105. }
  106. int nMieApplied(const unsigned int L, std::vector<double>& x, std::vector<std::complex<double> >& m, const unsigned int nTheta, std::vector<double>& Theta, const int nmax, double *Qext, double *Qsca, double *Qabs, double *Qbk, double *Qpr, double *g, double *Albedo, std::vector<std::complex<double> >& S1, std::vector<std::complex<double> >& S2) {
  107. return nmie::nMieApplied(L, -1, x, m, nTheta, Theta, nmax, Qext, Qsca, Qabs, Qbk, Qpr, g, Albedo, S1, S2);
  108. }
  109. // ********************************************************************** //
  110. // ********************************************************************** //
  111. // ********************************************************************** //
  112. void MultiLayerMieApplied::GetFailed() {
  113. double faild_x = 9.42477796076938;
  114. //double faild_x = 9.42477796076937;
  115. std::complex<double> z(faild_x, 0.0);
  116. std::vector<int> nmax_local_array = {20, 100, 500, 2500};
  117. for (auto nmax_local : nmax_local_array) {
  118. std::vector<std::complex<double> > D1_failed(nmax_local + 1);
  119. // Downward recurrence for D1 - equations (16a) and (16b)
  120. D1_failed[nmax_local] = std::complex<double>(0.0, 0.0);
  121. const std::complex<double> zinv = std::complex<double>(1.0, 0.0)/z;
  122. for (int n = nmax_local; n > 0; n--) {
  123. D1_failed[n - 1] = double(n)*zinv - 1.0/(D1_failed[n] + double(n)*zinv);
  124. }
  125. printf("Faild D1[0] from reccurence (z = %16.14f, nmax = %d): %g\n",
  126. faild_x, nmax_local, D1_failed[0].real());
  127. }
  128. printf("Faild D1[0] from continued fraction (z = %16.14f): %g\n", faild_x,
  129. calcD1confra(0,z).real());
  130. //D1[nmax_] = calcD1confra(nmax_, z);
  131. }
  132. // ********************************************************************** //
  133. // ********************************************************************** //
  134. // ********************************************************************** //
  135. void MultiLayerMieApplied::AddTargetLayer(double width, std::complex<double> layer_index) {
  136. isMieCalculated_ = false;
  137. if (width <= 0)
  138. throw std::invalid_argument("Layer width should be positive!");
  139. target_width_.push_back(width);
  140. target_index_.push_back(layer_index);
  141. } // end of void MultiLayerMieApplied::AddTargetLayer(...)
  142. // ********************************************************************** //
  143. // ********************************************************************** //
  144. // ********************************************************************** //
  145. void MultiLayerMieApplied::SetTargetPEC(double radius) {
  146. isMieCalculated_ = false;
  147. if (target_width_.size() != 0 || target_index_.size() != 0)
  148. throw std::invalid_argument("Error! Define PEC target radius before any other layers!");
  149. // Add layer of any index...
  150. AddTargetLayer(radius, std::complex<double>(0.0, 0.0));
  151. // ... and mark it as PEC
  152. SetPEC(0.0);
  153. }
  154. // ********************************************************************** //
  155. // ********************************************************************** //
  156. // ********************************************************************** //
  157. void MultiLayerMieApplied::SetCoatingIndex(std::vector<std::complex<double> > index) {
  158. isMieCalculated_ = false;
  159. coating_index_.clear();
  160. for (auto value : index) coating_index_.push_back(value);
  161. } // end of void MultiLayerMieApplied::SetCoatingIndex(std::vector<complex> index);
  162. // ********************************************************************** //
  163. // ********************************************************************** //
  164. // ********************************************************************** //
  165. void MultiLayerMieApplied::SetAngles(const std::vector<double>& angles) {
  166. isMieCalculated_ = false;
  167. theta_ = angles;
  168. // theta_.clear();
  169. // for (auto value : angles) theta_.push_back(value);
  170. } // end of SetAngles()
  171. // ********************************************************************** //
  172. // ********************************************************************** //
  173. // ********************************************************************** //
  174. void MultiLayerMieApplied::SetCoatingWidth(std::vector<double> width) {
  175. isMieCalculated_ = false;
  176. coating_width_.clear();
  177. for (auto w : width)
  178. if (w <= 0)
  179. throw std::invalid_argument("Coating width should be positive!");
  180. else coating_width_.push_back(w);
  181. }
  182. // end of void MultiLayerMieApplied::SetCoatingWidth(...);
  183. // ********************************************************************** //
  184. // ********************************************************************** //
  185. // ********************************************************************** //
  186. void MultiLayerMieApplied::SetWidthSP(const std::vector<double>& size_parameter) {
  187. isMieCalculated_ = false;
  188. size_param_.clear();
  189. double prev_size_parameter = 0.0;
  190. for (auto layer_size_parameter : size_parameter) {
  191. if (layer_size_parameter <= 0.0)
  192. throw std::invalid_argument("Size parameter should be positive!");
  193. if (prev_size_parameter > layer_size_parameter)
  194. throw std::invalid_argument
  195. ("Size parameter for next layer should be larger than the previous one!");
  196. prev_size_parameter = layer_size_parameter;
  197. size_param_.push_back(layer_size_parameter);
  198. }
  199. }
  200. // end of void MultiLayerMieApplied::SetWidthSP(...);
  201. // ********************************************************************** //
  202. // ********************************************************************** //
  203. // ********************************************************************** //
  204. void MultiLayerMieApplied::SetIndexSP(const std::vector< std::complex<double> >& index) {
  205. isMieCalculated_ = false;
  206. //refractive_index_.clear();
  207. refractive_index_ = index;
  208. // for (auto value : index) refractive_index_.push_back(value);
  209. } // end of void MultiLayerMieApplied::SetIndexSP(...);
  210. // ********************************************************************** //
  211. // ********************************************************************** //
  212. // ********************************************************************** //
  213. void MultiLayerMieApplied::SetFieldPointsSP(const std::vector< std::vector<double> >& coords_sp) {
  214. if (coords_sp.size() != 3)
  215. throw std::invalid_argument("Error! Wrong dimension of field monitor points!");
  216. if (coords_sp[0].size() != coords_sp[1].size() || coords_sp[0].size() != coords_sp[2].size())
  217. throw std::invalid_argument("Error! Missing coordinates for field monitor points!");
  218. coords_sp_ = coords_sp;
  219. // for (int i = 0; i < coords_sp_[0].size(); ++i) {
  220. // printf("%g, %g, %g\n", coords_sp_[0][i], coords_sp_[1][i], coords_sp_[2][i]);
  221. // }
  222. } // end of void MultiLayerMieApplied::SetFieldPointsSP(...)
  223. // ********************************************************************** //
  224. // ********************************************************************** //
  225. // ********************************************************************** //
  226. void MultiLayerMieApplied::SetPEC(int layer_position) {
  227. isMieCalculated_ = false;
  228. if (layer_position < 0)
  229. throw std::invalid_argument("Error! Layers are numbered from 0!");
  230. PEC_layer_position_ = layer_position;
  231. }
  232. // ********************************************************************** //
  233. // ********************************************************************** //
  234. // ********************************************************************** //
  235. void MultiLayerMieApplied::GenerateSizeParameter() {
  236. isMieCalculated_ = false;
  237. size_param_.clear();
  238. double radius = 0.0;
  239. for (auto width : target_width_) {
  240. radius += width;
  241. size_param_.push_back(2*PI_*radius/wavelength_);
  242. }
  243. for (auto width : coating_width_) {
  244. radius += width;
  245. size_param_.push_back(2*PI_*radius/wavelength_);
  246. }
  247. total_radius_ = radius;
  248. } // end of void MultiLayerMieApplied::GenerateSizeParameter();
  249. // ********************************************************************** //
  250. // ********************************************************************** //
  251. // ********************************************************************** //
  252. void MultiLayerMieApplied::GenerateIndex() {
  253. isMieCalculated_ = false;
  254. refractive_index_.clear();
  255. for (auto index : target_index_) refractive_index_.push_back(index);
  256. for (auto index : coating_index_) refractive_index_.push_back(index);
  257. } // end of void MultiLayerMieApplied::GenerateIndex();
  258. // ********************************************************************** //
  259. // ********************************************************************** //
  260. // ********************************************************************** //
  261. double MultiLayerMieApplied::GetTotalRadius() {
  262. if (!isMieCalculated_)
  263. throw std::invalid_argument("You should run calculations before result request!");
  264. if (total_radius_ == 0) GenerateSizeParameter();
  265. return total_radius_;
  266. } // end of double MultiLayerMieApplied::GetTotalRadius();
  267. // ********************************************************************** //
  268. // ********************************************************************** //
  269. // ********************************************************************** //
  270. std::vector< std::vector<double> >
  271. MultiLayerMieApplied::GetSpectra(double from_WL, double to_WL, int samples) {
  272. if (!isMieCalculated_)
  273. throw std::invalid_argument("You should run calculations before result request!");
  274. std::vector< std::vector<double> > spectra;
  275. double step_WL = (to_WL - from_WL)/static_cast<double>(samples);
  276. double wavelength_backup = wavelength_;
  277. long fails = 0;
  278. for (double WL = from_WL; WL < to_WL; WL += step_WL) {
  279. wavelength_ = WL;
  280. try {
  281. RunMieCalculation();
  282. } catch(const std::invalid_argument& ia) {
  283. fails++;
  284. continue;
  285. }
  286. //printf("%3.1f ",WL);
  287. spectra.push_back(std::vector<double>({wavelength_, Qext_, Qsca_, Qabs_, Qbk_}));
  288. } // end of for each WL in spectra
  289. printf("Spectrum has %li fails\n",fails);
  290. wavelength_ = wavelength_backup;
  291. return spectra;
  292. }
  293. // ********************************************************************** //
  294. // ********************************************************************** //
  295. // ********************************************************************** //
  296. void MultiLayerMieApplied::ClearTarget() {
  297. isMieCalculated_ = false;
  298. target_width_.clear();
  299. target_index_.clear();
  300. }
  301. // ********************************************************************** //
  302. // ********************************************************************** //
  303. // ********************************************************************** //
  304. void MultiLayerMieApplied::ClearCoating() {
  305. isMieCalculated_ = false;
  306. coating_width_.clear();
  307. coating_index_.clear();
  308. }
  309. // ********************************************************************** //
  310. // ********************************************************************** //
  311. // ********************************************************************** //
  312. void MultiLayerMieApplied::ClearLayers() {
  313. isMieCalculated_ = false;
  314. ClearTarget();
  315. ClearCoating();
  316. }
  317. // ********************************************************************** //
  318. // ********************************************************************** //
  319. // ********************************************************************** //
  320. void MultiLayerMieApplied::ClearAllDesign() {
  321. isMieCalculated_ = false;
  322. ClearLayers();
  323. size_param_.clear();
  324. refractive_index_.clear();
  325. }
  326. // ********************************************************************** //
  327. // ********************************************************************** //
  328. // ********************************************************************** //
  329. // Computational core
  330. // ********************************************************************** //
  331. // ********************************************************************** //
  332. // ********************************************************************** //
  333. //**********************************************************************************//
  334. // Function CONFRA ported from MIEV0.f (Wiscombe,1979)
  335. // Ref. to NCAR Technical Notes, Wiscombe, 1979
  336. /*
  337. c Compute Bessel function ratio A-sub-N from its
  338. c continued fraction using Lentz method
  339. c ZINV = Reciprocal of argument of A
  340. c I N T E R N A L V A R I A B L E S
  341. c ------------------------------------
  342. c CAK Term in continued fraction expansion of A (Eq. R25)
  343. c a_k
  344. c CAPT Factor used in Lentz iteration for A (Eq. R27)
  345. c T_k
  346. c CNUMER Numerator in capT (Eq. R28A)
  347. c N_k
  348. c CDENOM Denominator in capT (Eq. R28B)
  349. c D_k
  350. c CDTD Product of two successive denominators of capT factors
  351. c (Eq. R34C)
  352. c xi_1
  353. c CNTN Product of two successive numerators of capT factors
  354. c (Eq. R34B)
  355. c xi_2
  356. c EPS1 Ill-conditioning criterion
  357. c EPS2 Convergence criterion
  358. c KK Subscript k of cAk (Eq. R25B)
  359. c k
  360. c KOUNT Iteration counter (used to prevent infinite looping)
  361. c MAXIT Max. allowed no. of iterations
  362. c MM + 1 and - 1, alternately
  363. */
  364. std::complex<double> MultiLayerMieApplied::calcD1confra(const int N, const std::complex<double> z) {
  365. // NTMR -> nmax_ - 1 \\TODO nmax_ ?
  366. //int N = nmax_ - 1;
  367. int KK, KOUNT, MAXIT = 10000, MM;
  368. // double EPS1=1.0e-2;
  369. double EPS2=1.0e-8;
  370. std::complex<double> CAK, CAPT, CDENOM, CDTD, CNTN, CNUMER;
  371. std::complex<double> one = std::complex<double>(1.0,0.0);
  372. std::complex<double> ZINV = one/z;
  373. // c ** Eq. R25a
  374. std::complex<double> CONFRA = static_cast<std::complex<double> >(N + 1)*ZINV; //debug ZINV
  375. MM = - 1;
  376. KK = 2*N +3; //debug 3
  377. // c ** Eq. R25b, k=2
  378. CAK = static_cast<std::complex<double> >(MM*KK)*ZINV; //debug -3 ZINV
  379. CDENOM = CAK;
  380. CNUMER = CDENOM + one/CONFRA; //-3zinv+z
  381. KOUNT = 1;
  382. //10 CONTINUE
  383. do { ++KOUNT;
  384. if (KOUNT > MAXIT) {
  385. printf("re(%g):im(%g)\t\n", CONFRA.real(), CONFRA.imag());
  386. throw std::invalid_argument("ConFra--Iteration failed to converge!\n");
  387. }
  388. MM *= - 1; KK += 2; //debug mm=1 kk=5
  389. CAK = static_cast<std::complex<double> >(MM*KK)*ZINV; // ** Eq. R25b //debug 5zinv
  390. // //c ** Eq. R32 Ill-conditioned case -- stride two terms instead of one
  391. // if (std::abs(CNUMER/CAK) >= EPS1 || std::abs(CDENOM/CAK) >= EPS1) {
  392. // //c ** Eq. R34
  393. // CNTN = CAK*CNUMER + 1.0;
  394. // CDTD = CAK*CDENOM + 1.0;
  395. // CONFRA = (CNTN/CDTD)*CONFRA; // ** Eq. R33
  396. // MM *= - 1; KK += 2;
  397. // CAK = static_cast<std::complex<double> >(MM*KK)*ZINV; // ** Eq. R25b
  398. // //c ** Eq. R35
  399. // CNUMER = CAK + CNUMER/CNTN;
  400. // CDENOM = CAK + CDENOM/CDTD;
  401. // ++KOUNT;
  402. // //GO TO 10
  403. // continue;
  404. // } else { //c *** Well-conditioned case
  405. {
  406. CAPT = CNUMER/CDENOM; // ** Eq. R27 //debug (-3zinv + z)/(-3zinv)
  407. // printf("re(%g):im(%g)**\t", CAPT.real(), CAPT.imag());
  408. CONFRA = CAPT*CONFRA; // ** Eq. R26
  409. //if (N == 0) {output=true;printf(" re:");prn(CONFRA.real());printf(" im:"); prn(CONFRA.imag());output=false;};
  410. //c ** Check for convergence; Eq. R31
  411. if (std::abs(CAPT.real() - 1.0) >= EPS2 || std::abs(CAPT.imag()) >= EPS2) {
  412. //c ** Eq. R30
  413. CNUMER = CAK + one/CNUMER;
  414. CDENOM = CAK + one/CDENOM;
  415. continue;
  416. //GO TO 10
  417. } // end of if < eps2
  418. }
  419. break;
  420. } while(1);
  421. //if (N == 0) printf(" return confra for z=(%g,%g)\n", ZINV.real(), ZINV.imag());
  422. return CONFRA;
  423. }
  424. // ********************************************************************** //
  425. // ********************************************************************** //
  426. // ********************************************************************** //
  427. void MultiLayerMieApplied::ConvertToSP() {
  428. isMieCalculated_ = false;
  429. if (target_width_.size() + coating_width_.size() == 0)
  430. return; // Nothing to convert, we suppose that SP was set directly
  431. GenerateSizeParameter();
  432. GenerateIndex();
  433. if (size_param_.size() != refractive_index_.size())
  434. throw std::invalid_argument("Ivalid conversion of width to size parameter units!/n");
  435. }
  436. // ********************************************************************** //
  437. // ********************************************************************** //
  438. // ********************************************************************** //
  439. } // end of namespace nmie