Selaa lähdekoodia

initial test of Riccati-Bessel funcitions ratio

Konstantin Ladutenko 4 vuotta sitten
vanhempi
commit
bbaa64edba

+ 1 - 0
src/CMakeLists.txt

@@ -5,6 +5,7 @@ set(_scattnlay_python_sources
         ${CMAKE_CURRENT_LIST_DIR}/nmie-pybind11.hpp
         ${CMAKE_CURRENT_LIST_DIR}/nmie-pybind11.cc
         ${CMAKE_CURRENT_LIST_DIR}/nmie-precision.hpp
+        ${CMAKE_CURRENT_LIST_DIR}/special-functions-impl.hpp
         ${CMAKE_CURRENT_LIST_DIR}/nmie-impl.hpp
         ${CMAKE_CURRENT_LIST_DIR}/pb11_wrapper.cc)
 

+ 15 - 35
src/nmie-impl.hpp

@@ -47,42 +47,16 @@
 //                                                                                  //
 // Hereinafter all equations numbers refer to [2]                                   //
 //**********************************************************************************//
-//<<<<<<< HEAD TODO
-//#include <array>
-//#include <algorithm>
-//#include <cstdio>
-//#include <cstdlib>
-//#include <cmath>
-//=======
-//>>>>>>> master
 #include <iostream>
 #include <iomanip>
 #include <stdexcept>
 #include <vector>
 
+#include "special-functions-impl.hpp"
 #include "nmie.hpp"
 #include "nmie-precision.hpp"
 
 namespace nmie {
-  //helper functions
-
-
-  template<class T> inline T pow2(const T value) {return value*value;}
-
-  template<class T> inline T cabs(const std::complex<T> value)
-  {return nmm::sqrt(pow2(value.real()) + pow2(value.imag()));}
-
-  template <typename FloatType>
-  int newround(FloatType x) {
-    return x >= 0 ? static_cast<int>(x + 0.5):static_cast<int>(x - 0.5);
-    //return x >= 0 ? (x + 0.5).convert_to<int>():(x - 0.5).convert_to<int>();
-  }
-  template<typename T>
-  inline std::complex<T> my_exp(const std::complex<T>& x) {
-    using std::exp; // use ADL
-    T const& r = exp(x.real());
-    return std::polar(r, x.imag());
-  }
 
 
   //class implementation
@@ -418,14 +392,20 @@ namespace nmie {
   void MultiLayerMie<FloatType>::calcD1D3(const std::complex<FloatType> z,
                                std::vector<std::complex<FloatType> >& D1,
                                std::vector<std::complex<FloatType> >& D3) {
-
-    // Downward recurrence for D1 - equations (16a) and (16b)
-    D1[nmax_] = std::complex<FloatType>(0.0, 0.0);
-    std::complex<FloatType> c_one(1.0, 0.0);
-    const std::complex<FloatType> zinv = std::complex<FloatType>(1.0, 0.0)/z;
-    for (int n = nmax_; n > 0; n--) {
-      D1[n - 1] = static_cast<FloatType>(n)*zinv - c_one/(D1[n] + static_cast<FloatType>(n)*zinv);
+    evalDownwardD1(z, D1);
+    int lnmx = evalKapteynNumberOfLostSignificantDigits(nmax_, z);
+    std::vector<std::complex<FloatType> > r;
+    if (lnmx < 4) {
+      r.resize(nmax_+1);
+      evalForwardR(z, r);
+    } else {
+      int valid_digits = 6;
+      int nstar = getNStar(nmax_, z, valid_digits);
+      r.resize(nstar);
+      evalBackwardR(z,r);
     }
+    convertRtoD1(z, r, D1);
+
     // TODO: Do we need this check?
     // if (cabs(D1[0]) > 1.0e15) {
     //   throw std::invalid_argument("Unstable D1! Please, try to change input parameters!\n");
@@ -436,7 +416,7 @@ namespace nmie {
     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()))
                  *static_cast<FloatType>(nmm::exp(-2.0*z.imag())));
     D3[0] = std::complex<FloatType>(0.0, 1.0);
-
+    const std::complex<FloatType> zinv = std::complex<FloatType>(1.0, 0.0)/z;
     for (int n = 1; n <= nmax_; n++) {
       PsiZeta_[n] = PsiZeta_[n - 1]*(static_cast<FloatType>(n)*zinv - D1[n - 1])
                                    *(static_cast<FloatType>(n)*zinv - D3[n - 1]);

+ 22 - 1
src/nmie.hpp

@@ -37,6 +37,7 @@
 #include <cstdlib>
 #include <iostream>
 #include <vector>
+#include "nmie-precision.hpp"
 #ifdef MULTI_PRECISION
 #include <boost/math/constants/constants.hpp>
 #endif
@@ -47,7 +48,27 @@ namespace nmie {
                 std::vector<std::vector<std::complex<double> > >& an, std::vector<std::vector<std::complex<double> > >& bn,
                 std::vector<std::vector<std::complex<double> > >& cn, std::vector<std::vector<std::complex<double> > >& dn);
 
-  // pl, nmax, mode_n, mode_type
+//helper functions
+
+
+template<class T> inline T pow2(const T value) {return value*value;}
+
+template<class T> inline T cabs(const std::complex<T> value)
+{return nmm::sqrt(pow2(value.real()) + pow2(value.imag()));}
+
+template <typename FloatType>
+int newround(FloatType x) {
+  return x >= 0 ? static_cast<int>(x + 0.5):static_cast<int>(x - 0.5);
+  //return x >= 0 ? (x + 0.5).convert_to<int>():(x - 0.5).convert_to<int>();
+}
+template<typename T>
+inline std::complex<T> my_exp(const std::complex<T>& x) {
+  using std::exp; // use ADL
+  T const& r = exp(x.real());
+  return std::polar(r, x.imag());
+}
+
+// pl, nmax, mode_n, mode_type
     int nMie(const unsigned int L,
            const int pl,
            std::vector<double>& x, std::vector<std::complex<double> >& m,

+ 315 - 0
src/special-functions-impl.hpp

@@ -0,0 +1,315 @@
+#ifndef SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
+#define SRC_SPECIAL_FUNCTIONS_IMPL_HPP_
+//**********************************************************************************//
+//    Copyright (C) 2009-2018  Ovidio Pena <ovidio@bytesfall.com>                   //
+//    Copyright (C) 2013-2018  Konstantin Ladutenko <kostyfisik@gmail.com>          //
+//                                                                                  //
+//    This file is part of scattnlay                                                //
+//                                                                                  //
+//    This program is free software: you can redistribute it and/or modify          //
+//    it under the terms of the GNU General Public License as published by          //
+//    the Free Software Foundation, either version 3 of the License, or             //
+//    (at your option) any later version.                                           //
+//                                                                                  //
+//    This program is distributed in the hope that it will be useful,               //
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of                //
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 //
+//    GNU General Public License for more details.                                  //
+//                                                                                  //
+//    The only additional remark is that we expect that all publications            //
+//    describing work using this software, or all commercial products               //
+//    using it, cite at least one of the following references:                      //
+//    [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by           //
+//        a multilayered sphere," Computer Physics Communications,                  //
+//        vol. 180, Nov. 2009, pp. 2348-2354.                                       //
+//    [2] K. Ladutenko, U. Pal, A. Rivera, and O. Pena-Rodriguez, "Mie              //
+//        calculation of electromagnetic near-field for a multilayered              //
+//        sphere," Computer Physics Communications, vol. 214, May 2017,             //
+//        pp. 225-230.                                                              //
+//                                                                                  //
+//    You should have received a copy of the GNU General Public License             //
+//    along with this program.  If not, see <http://www.gnu.org/licenses/>.         //
+//**********************************************************************************//
+
+//**********************************************************************************//
+// This class implements the algorithm for a multilayered sphere described by:      //
+//    [1] W. Yang, "Improved recursive algorithm for light scattering by a          //
+//        multilayered sphere,” Applied Optics, vol. 42, Mar. 2003, pp. 1710-1720.  //
+//                                                                                  //
+// You can find the description of all the used equations in:                       //
+//    [2] O. Pena and U. Pal, "Scattering of electromagnetic radiation by           //
+//        a multilayered sphere," Computer Physics Communications,                  //
+//        vol. 180, Nov. 2009, pp. 2348-2354.                                       //
+//    [3] K. Ladutenko, U. Pal, A. Rivera, and O. Pena-Rodriguez, "Mie              //
+//        calculation of electromagnetic near-field for a multilayered              //
+//        sphere," Computer Physics Communications, vol. 214, May 2017,             //
+//        pp. 225-230.                                                              //
+//                                                                                  //
+// Hereinafter all equations numbers refer to [2]                                   //
+//**********************************************************************************//
+#include <iostream>
+#include <iomanip>
+#include <stdexcept>
+#include <vector>
+
+#include "nmie.hpp"
+#include "nmie-precision.hpp"
+
+namespace nmie {
+////helper functions
+//template<class T> inline T pow2(const T value) {return value*value;}
+
+
+int evalKapteynNumberOfLostSignificantDigits(const int ni,
+                                             const std::complex<FloatType> z) {
+  using nmm::abs, nmm::imag, nmm::real, nmm::log, nmm::sqrt, nmm::round;
+  auto n = static_cast<FloatType>(ni);
+  auto one = std::complex<FloatType> (1, 0);
+  return round((
+      abs(imag(z)) - log(2) - n * ( real( log( z/n) + sqrt(one
+      - pow2(z/n)) - log (one + sqrt (one
+      - pow2(z/n)))
+      )))/ log(10));
+}
+
+int getNStar(int nmax, std::complex<FloatType> z, const int valid_digits) {
+  int nstar = nmax;
+  int forwardLoss = evalKapteynNumberOfLostSignificantDigits(nmax, z);
+  int increment = static_cast<int>(std::ceil(
+      std::max(4* std::pow(std::abs(z), 1/3.0), 5.0)
+      ));
+  int backwardLoss =evalKapteynNumberOfLostSignificantDigits(nstar, z);
+  while ( backwardLoss - forwardLoss < valid_digits) {
+    nstar += increment;
+    backwardLoss = evalKapteynNumberOfLostSignificantDigits(nstar,z);
+  };
+  return nstar;
+}
+
+// Forward iteration for evaluation of ratio of the Riccati–Bessel functions
+void evalForwardR (const std::complex<FloatType> z,
+                   std::vector<std::complex<FloatType> >& r) {
+  if (r.size() < 1) throw std::invalid_argument(
+      "We need non-zero evaluations of ratio of the Riccati–Bessel functions.\n");
+  // r0 = cot(z)
+  r[0] = nmm::cos(z)/nmm::sin(z);
+  for (unsigned int n = 0; n < r.size() - 1; n++) {
+    r[n+1] = static_cast<FloatType>(1)/( static_cast<FloatType>(2*n+1)/z - r[n] );
+  }
+}
+
+
+// Backward iteration for evaluation of ratio of the Riccati–Bessel functions
+void evalBackwardR (const std::complex<FloatType> z,
+                   std::vector<std::complex<FloatType> >& r) {
+  if (r.size() < 1) throw std::invalid_argument(
+        "We need non-zero evaluations of ratio of the Riccati–Bessel functions.\n");
+  int nmax = r.size()-1 ;
+  auto tmp = static_cast<FloatType>(2*nmax + 1)/z;
+  r[nmax] = tmp;
+  for (int n = nmax -1; n >= 0; n--) {
+    r[n] = -static_cast<FloatType>(1)/r[n+1] + static_cast<FloatType>(2*n+1)/z;
+  }
+}
+
+void convertRtoD1(const std::complex<FloatType> z,
+                  std::vector<std::complex<FloatType> >& r,
+                  std::vector<std::complex<FloatType> >& D1) {
+  if (D1.size() > r.size()) throw std::invalid_argument(
+      "Not enough elements in array of ratio of the Riccati–Bessel functions to convert it into logarithmic derivative array.\n");
+  std::complex<FloatType> Dold;
+  for (unsigned int n = 0; n < D1.size(); n++) {
+    Dold = D1[n];
+    D1[n] = r[n] - static_cast<FloatType>(n)/z;
+//    std::cout << "D1old - D1[n] :" << Dold-D1[n] << '\n';
+  }
+}
+
+// ********************************************************************** //
+void evalDownwardD1 (const std::complex<FloatType> z,
+               std::vector<std::complex<FloatType> >& D1) {
+  int nmax = D1.size() - 1;
+  // Downward recurrence for D1 - equations (16a) and (16b)
+  D1[nmax] = std::complex<FloatType>(0.0, 0.0);
+  std::complex<FloatType> c_one(1.0, 0.0);
+  const std::complex<FloatType> zinv = std::complex<FloatType>(1.0, 0.0)/z;
+  for (unsigned int n = nmax; n > 0; n--) {
+    D1[n - 1] = static_cast<FloatType>(n)*zinv - c_one/
+        (D1[n] + static_cast<FloatType>(n)*zinv);
+  }
+// //   r0 = cot(z)
+//  std::complex<FloatType> r0 = nmm::cos(z)/nmm::sin(z);
+//  D1[0] = r0; // - n/mx;
+}
+
+// ********************************************************************** //
+void evalForwardD (const std::complex<FloatType> z,
+                     std::vector<std::complex<FloatType> >& D) {
+  int nmax = D.size();
+  FloatType one = static_cast<FloatType >(1);
+  for (int n = 1; n < nmax; n++) {
+    FloatType nf = static_cast<FloatType > (n);
+    D[n] = one/ (nf/z  - D[n-1]) - nf/z;
+  }
+}
+
+// ********************************************************************** //
+void evalForwardD1 (const std::complex<FloatType> z,
+                   std::vector<std::complex<FloatType> >& D) {
+  if (D.size()<1) throw std::invalid_argument("Should have a leas one element!\n");
+  D[0] = std::cos(z)/std::sin(z);
+  evalForwardD(z,D);
+}
+
+  //**********************************************************************************//
+  // This function calculates the logarithmic derivatives of the Riccati-Bessel       //
+  // functions (D1 and D3) for a complex argument (z).                                //
+  // Equations (16a), (16b) and (18a) - (18d)                                         //
+  //                                                                                  //
+  // Input parameters:                                                                //
+  //   z: Complex argument to evaluate D1 and D3                                      //
+  //   nmax_: Maximum number of terms to calculate D1 and D3                          //
+  //                                                                                  //
+  // Output parameters:                                                               //
+  //   D1, D3: Logarithmic derivatives of the Riccati-Bessel functions                //
+  //**********************************************************************************//
+//  template <typename FloatType>
+//  void MultiLayerMie<FloatType>::calcD1D3(const std::complex<FloatType> z,
+//                               std::vector<std::complex<FloatType> >& D1,
+//                               std::vector<std::complex<FloatType> >& D3) {
+//
+//    // if (cabs(D1[0]) > 1.0e15) {
+//    //   throw std::invalid_argument("Unstable D1! Please, try to change input parameters!\n");
+//    // //printf("Warning: Potentially unstable D1! Please, try to change input parameters!\n");
+//    // }
+//
+//    // Upward recurrence for PsiZeta and D3 - equations (18a) - (18d)
+//    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()))
+//                 *static_cast<FloatType>(nmm::exp(-2.0*z.imag())));
+//    D3[0] = std::complex<FloatType>(0.0, 1.0);
+//
+//    for (int n = 1; n <= nmax_; n++) {
+//      PsiZeta_[n] = PsiZeta_[n - 1]*(static_cast<FloatType>(n)*zinv - D1[n - 1])
+//                                   *(static_cast<FloatType>(n)*zinv - D3[n - 1]);
+//      D3[n] = D1[n] + std::complex<FloatType>(0.0, 1.0)/PsiZeta_[n];
+//    }
+//  }
+//
+//
+  //**********************************************************************************//
+  // This function calculates the Riccati-Bessel functions (Psi and Zeta) for a       //
+  // complex argument (z).                                                            //
+  // Equations (20a) - (21b)                                                          //
+  //                                                                                  //
+  // Input parameters:                                                                //
+  //   z: Complex argument to evaluate Psi and Zeta                                   //
+  //   nmax: Maximum number of terms to calculate Psi and Zeta                        //
+  //                                                                                  //
+  // Output parameters:                                                               //
+  //   Psi, Zeta: Riccati-Bessel functions                                            //
+  //**********************************************************************************//
+//  template <typename FloatType>
+//  void MultiLayerMie<FloatType>::calcPsiZeta(std::complex<FloatType> z,
+//                                  std::vector<std::complex<FloatType> >& Psi,
+//                                  std::vector<std::complex<FloatType> >& Zeta) {
+//
+//    std::complex<FloatType> c_i(0.0, 1.0);
+//    std::vector<std::complex<FloatType> > D1(nmax_ + 1), D3(nmax_ + 1);
+//
+//    // First, calculate the logarithmic derivatives
+//    calcD1D3(z, D1, D3);
+//
+//    // Now, use the upward recurrence to calculate Psi and Zeta - equations (20a) - (21b)
+//    Psi[0] = std::sin(z);
+//    Zeta[0] = std::sin(z) - c_i*std::cos(z);
+//    for (int n = 1; n <= nmax_; n++) {
+//      Psi[n]  =  Psi[n - 1]*(std::complex<FloatType>(n,0.0)/z - D1[n - 1]);
+//      Zeta[n] = Zeta[n - 1]*(std::complex<FloatType>(n,0.0)/z - D3[n - 1]);
+//    }
+//  }
+
+
+  //**********************************************************************************//
+  // This function calculates Pi and Tau for a given value of cos(Theta).             //
+  // Equations (26a) - (26c)                                                          //
+  //                                                                                  //
+  // Input parameters:                                                                //
+  //   nmax_: Maximum number of terms to calculate Pi and Tau                         //
+  //   nTheta: Number of scattering angles                                            //
+  //   Theta: Array containing all the scattering angles where the scattering         //
+  //          amplitudes will be calculated                                           //
+  //                                                                                  //
+  // Output parameters:                                                               //
+  //   Pi, Tau: Angular functions Pi and Tau, as defined in equations (26a) - (26c)   //
+  //**********************************************************************************//
+//  template <typename FloatType>
+//  void MultiLayerMie<FloatType>::calcPiTau(const FloatType& costheta,
+//                                std::vector<FloatType>& Pi, std::vector<FloatType>& Tau) {
+//
+//    int i;
+//    //****************************************************//
+//    // Equations (26a) - (26c)                            //
+//    //****************************************************//
+//    // Initialize Pi and Tau
+//    Pi[0] = 1.0;  // n=1
+//    Tau[0] = costheta;
+//    // Calculate the actual values
+//    if (nmax_ > 1) {
+//      Pi[1] = 3*costheta*Pi[0]; //n=2
+//      Tau[1] = 2*costheta*Pi[1] - 3*Pi[0];
+//      for (i = 2; i < nmax_; i++) { //n=[3..nmax_]
+//        Pi[i] = ((i + i + 1)*costheta*Pi[i - 1] - (i + 1)*Pi[i - 2])/i;
+//        Tau[i] = (i + 1)*costheta*Pi[i] - (i + 2)*Pi[i - 1];
+//      }
+//    }
+//  }  // end of MultiLayerMie::calcPiTau(...)
+
+
+  //**********************************************************************************//
+  // This function calculates vector spherical harmonics (eq. 4.50, p. 95 BH),        //
+  // required to calculate the near-field parameters.                                 //
+  //                                                                                  //
+  // Input parameters:                                                                //
+  //   Rho: Radial distance                                                           //
+  //   Phi: Azimuthal angle                                                           //
+  //   Theta: Polar angle                                                             //
+  //   rn: Either the spherical Ricatti-Bessel function of first or third kind        //
+  //   Dn: Logarithmic derivative of rn                                               //
+  //   Pi, Tau: Angular functions Pi and Tau                                          //
+  //   n: Order of vector spherical harmonics                                         //
+  //                                                                                  //
+  // Output parameters:                                                               //
+  //   Mo1n, Me1n, No1n, Ne1n: Complex vector spherical harmonics                     //
+  //**********************************************************************************//
+//  template <typename FloatType>
+//  void MultiLayerMie<FloatType>::calcSpherHarm(const std::complex<FloatType> Rho, const FloatType Theta, const FloatType Phi,
+//                                    const std::complex<FloatType>& rn, const std::complex<FloatType>& Dn,
+//                                    const FloatType& Pi, const FloatType& Tau, const FloatType& n,
+//                                    std::vector<std::complex<FloatType> >& Mo1n, std::vector<std::complex<FloatType> >& Me1n,
+//                                    std::vector<std::complex<FloatType> >& No1n, std::vector<std::complex<FloatType> >& Ne1n) {
+//
+//    // using eq 4.50 in BH
+//    std::complex<FloatType> c_zero(0.0, 0.0);
+//
+//    using nmm::sin;
+//    using nmm::cos;
+//    Mo1n[0] = c_zero;
+//    Mo1n[1] = cos(Phi)*Pi*rn/Rho;
+//    Mo1n[2] = -sin(Phi)*Tau*rn/Rho;
+//
+//    Me1n[0] = c_zero;
+//    Me1n[1] = -sin(Phi)*Pi*rn/Rho;
+//    Me1n[2] = -cos(Phi)*Tau*rn/Rho;
+//
+//    No1n[0] = sin(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
+//    No1n[1] = sin(Phi)*Tau*Dn*rn/Rho;
+//    No1n[2] = cos(Phi)*Pi*Dn*rn/Rho;
+//
+//    Ne1n[0] = cos(Phi)*(n*n + n)*sin(Theta)*Pi*rn/Rho/Rho;
+//    Ne1n[1] = cos(Phi)*Tau*Dn*rn/Rho;
+//    Ne1n[2] = -sin(Phi)*Pi*Dn*rn/Rho;
+//  }  // end of MultiLayerMie::calcSpherHarm(...)
+//
+
+}  // end of namespace nmie
+#endif  // SRC_SPECIAL_FUNCTIONS_IMPL_HPP_

+ 6 - 6
tests/CMakeLists.txt

@@ -12,12 +12,12 @@ target_link_libraries("test_bulk_sphere" ${LIBS})
 add_test(NAME "test_bulk_sphere"
         COMMAND "test_bulk_sphere")
 
-add_executable("test_bulk_sphere_multi_precision" test_bulk_sphere.cc)
-target_compile_options("test_bulk_sphere_multi_precision"
-        PRIVATE -DMULTI_PRECISION=100)
-target_link_libraries("test_bulk_sphere_multi_precision" ${LIBS})
-add_test(NAME "test_bulk_sphere_multi_precision"
-        COMMAND "test_bulk_sphere_multi_precision")
+#add_executable("test_bulk_sphere_multi_precision" test_bulk_sphere.cc)
+#target_compile_options("test_bulk_sphere_multi_precision"
+#        PRIVATE -DMULTI_PRECISION=100)
+#target_link_libraries("test_bulk_sphere_multi_precision" ${LIBS})
+#add_test(NAME "test_bulk_sphere_multi_precision"
+#        COMMAND "test_bulk_sphere_multi_precision")
 
 
 add_executable("test_Riccati_Bessel_logarithmic_derivative"

+ 55 - 0
tests/special-functions-test-generator.py

@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+import mpmath as mp
+import numpy as np
+Du_test = [
+# // x, [Re(m), Im(m)], Qext, Qsca, test_name
+[0.099, [0.75,0], 7.417859e-06, 7.417859e-06, 'a'],
+# [0.101, [0.75,0], 8.033538e-06, 8.033538e-06, 'b'],
+# [10,    [0.75,0],     2.232265, 2.232265, 'c'],
+# [1000,  [0.75,0],     1.997908, 1.997908, 'd'],
+# [100,   [1.33,-1e-5], 2.101321, 2.096594, 'e'],
+# [10000, [1.33,-1e-5], 2.004089, 1.723857, 'f'],
+# [0.055, [1.5, -1],    0.101491, 1.131687e-05, 'g'],
+# [0.056, [1.5, -1],   0.1033467, 1.216311e-05, 'h'],
+# [100,   [1.5, -1],    2.097502, 1.283697, 'i'],
+# [10000, [1.5, -1],    2.004368, 1.236574, 'j'],
+# [1,     [10,  -10],   2.532993, 2.049405, 'k'],
+# [100,   [10,  -10,],  2.071124, 1.836785, 'l'],
+[10000, [10,  -10],   2.005914, 1.795393, 'm'],
+[80, [1.05,  1],   0, 0, 'du'],
+]
+# // Dtest refractive index is m={1.05,1}, the size parameter is x = 80
+n_list = [0,1,30,50,60,70,75,80,85,90,99,116,130];
+
+def get_z_values(du_list):
+    zlist = []
+    for record in du_list:
+        x = mp.mpf(str(record[0]))
+        m = mp.mpc(str(record[1][0]), str(record[1][1]))
+        z = x*m
+        zlist.append(z)
+    return zlist
+
+# Riccati-Bessel z*j_n(z)
+def psi(n,z):
+    return mp.sqrt( (mp.pi * z)/2 ) * mp.besselj(n+1/2,z)
+
+# to compare r(n,z) with Wolfram Alpha
+# n=49, z=1.3-2.1i,  SphericalBesselJ[n-1,z]/SphericalBesselJ[n,z]
+def r(n,z):
+    return psi(n-1,z)/psi(n,z)
+
+def D1(n,z):
+    return r(n,z) - n/z
+
+def main ():
+    mp.mp.dps = 62
+    output_dps = 10
+
+    zlist = get_z_values(Du_test)
+    for z in zlist:
+        print(z)
+        for n in n_list:
+            print(n, mp.nstr(D1(n,z), output_dps))
+
+main()

+ 97 - 3
tests/test_Riccati_Bessel_logarithmic_derivative.cc

@@ -1,9 +1,103 @@
 #include "gtest/gtest.h"
 #include "../src/nmie-impl.hpp"
-TEST(RiccatiBesselTest, HandlesInput) {
-  nmie::MultiLayerMie<double> nmie;
-  EXPECT_EQ(1, 1)<<"Should be equal";
+// From W. Yang APPLIED OPTICS Vol. 42, No. 9,  20 March 2003
+// Dtest refractive index is m={1.05,1}, the size parameter is x = 80
+std::vector<int> Dtest_n({0,1,30,50,60,70,75,80,85,90,99,116,130});
+std::vector< std::complex<double>>
+    Dtest_D1({{0.11449e-15 ,-0.10000e+01 },{0.74646e-04 ,-0.10000e+01 },
+              {0.34764e-01 ,-0.99870},{0.95292e-01 ,-0.99935},
+              {0.13645,-0.10019e+01 },{0.18439,-0.10070e+01 },
+              {0.21070,-0.10107e+01 },{0.23845,-0.10154e+01 },
+              {0.26752,-0.10210e+01 },{0.29777,-0.10278e+01 },
+              {0.35481,-0.10426e+01 },{0.46923,-0.10806e+01 },
+              {0.17656,-0.13895e+01 }});
+std::vector< std::complex<double>>
+    Dtest_D2({{0.64966e-69 ,-0.10000e+01 },{0.74646e-04 ,-0.10000e+01 },
+              {0.34764e-01 ,-0.99870},{0.95292e-01 ,-0.99935},
+              {0.13645,-0.10019e+01 },{0.17769,-0.10099e+01 },
+              {0.41264e-01 ,-0.21076e+01 },{-0.20190,0.10435e+01 },
+              {-0.26343,0.10223e+01 },{-0.29339,0.10291e+01 },
+              {-0.34969,0.10437e+01 },{-0.46296,0.10809e+01 },
+              {-0.56047,0.11206e+01 }});
+std::vector< std::complex<double>>
+    Dtest_D3({{0.00000,0.10000e+01 },{-0.73809e-04 ,0.10000e+01 },
+              {-0.34344e-01 ,0.99912},{-0.94022e-01 ,0.10004e+01 },
+              {-0.13455,0.10032e+01 },{-0.18172,0.10084e+01 },
+              {-0.20762,0.10122e+01 },{-0.23494,0.10169e+01 },
+              {-0.26357,0.10225e+01 },{-0.29339,0.10291e+01 },
+              {-0.34969,0.10437e+01 },{-0.46296,0.10809e+01 },
+              {-0.56047,0.11206e+01 }});
+
+
+//TEST(Dtest, DISABLED_WYang_data){
+TEST(Dtest, WYang_data){
+  double rel_tol = 1e-6;
+  int Nstop = 131;
+  std::vector<std::complex<nmie::FloatType>> Df(Nstop), Db(Nstop), r;
+  std::complex<nmie::FloatType> z(1.05,1);
+  z = z*80.0;
+  nmie::evalForwardD1(z, Df);
+  r.resize(Nstop+1);
+  nmie::evalForwardR(z, r);
+  nmie::convertRtoD1(z, r, Df);
+  int valid_digits = 6;
+  int nstar = nmie::getNStar(Nstop, z, valid_digits);
+  r.resize(nstar);
+  nmie::evalBackwardR(z,r);
+  nmie::convertRtoD1(z, r, Db);
+
+for (int i = 0; i < Dtest_n.size(); i++) {
+  int n = Dtest_n[i];
+//  EXPECT_FLOAT_EQ(std::real(Df[n]), std::real(Dtest_D1[i])) << "f at n=" << n;
+//  EXPECT_FLOAT_EQ(std::imag(Df[n]), std::imag(Dtest_D1[i])) << "f at n=" << n;
+  EXPECT_NEAR(std::real(Db[n]), std::real(Dtest_D1[i]),
+              rel_tol*std::real(Db[n])) << "b at n=" << n;
+  EXPECT_NEAR(std::imag(Db[n]), std::imag(Dtest_D1[i]),
+              rel_tol*std::imag(Db[n])) << "b at n=" << n;
+//  EXPECT_FLOAT_EQ(std::real(Df[n]), std::real(Db[n])) << "f-b at n=" << n;
+//  EXPECT_FLOAT_EQ(std::imag(Df[n]), std::imag(Db[n])) << "f-b at n=" << n;
+  }
 }
+
+TEST(ratio_funcion_forward_vs_backward_recurrence, compare){
+  int Nstop = 131;
+  std::vector<std::complex<nmie::FloatType>> rf(Nstop), rb;
+//  std::complex<nmie::FloatType> z(1.05,1);
+//  z = z*80.0;
+
+  std::complex<nmie::FloatType> z(1.3,-2.1);
+// rb[49] in Wolfram Alpha
+// n=49, z=1.3-2.1i,  SphericalBesselJ[n-1,z]/SphericalBesselJ[n,z]
+  nmie::evalForwardR(z, rf);
+  int valid_digits = 20;
+  int nstar = nmie::getNStar(Nstop, z, valid_digits);
+  rb.resize(nstar);
+  nmie::evalBackwardR(z,rb);
+
+  for (int i = 0; i < Dtest_n.size(); i++) {
+    int n = Dtest_n[i];
+    EXPECT_FLOAT_EQ(std::real(rf[n]), std::real(rb[i]))
+              << "at n=" << n
+              << " expected forward loss="<<nmie::evalKapteynNumberOfLostSignificantDigits(n,z);
+    EXPECT_FLOAT_EQ(std::imag(rf[n]), std::imag(rb[i]))
+              << "at n=" << n
+              << " expected forward loss="<<nmie::evalKapteynNumberOfLostSignificantDigits(n,z);
+  }
+}
+
+
+TEST(KaptyenTest, HandlesInput) {
+  // H.Du APPLIED OPTICS, Vol. 43, No. 9, 20 March 2004
+  double l = nmie::evalKapteynNumberOfLostSignificantDigits(80, std::complex<double>(100,100));
+  EXPECT_EQ(l, 7)<<"Should be equal";
+  std::complex<double> z(10000,0);
+  l = nmie::evalKapteynNumberOfLostSignificantDigits(5070, z);
+  EXPECT_EQ(l, 0)<<"Should be equal";
+  int NStar = nmie::getNStar(5070, z,6);
+  EXPECT_GE(NStar, 10130);
+}
+
+
 int main(int argc, char **argv) {
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();

+ 4 - 2
tests/test_bulk_sphere.cc

@@ -15,7 +15,7 @@ TEST(BulkSphere, HandlesInput) {
           {0.101, {0.75,0}, 8.033538e-06, 8.033538e-06, 'b'},
           {10,    {0.75,0},     2.232265, 2.232265, 'c'},
           {1000,  {0.75,0},     1.997908, 1.997908, 'd'},
-//          {100,   {1.33,-1e-5}, 2.101321, 2.096594, 'e'},
+          {100,   {1.33,-1e-5}, 2.101321, 2.096594, 'e'},
 //          {10000, {1.33,-1e-5}, 2.004089, 1.723857, 'f'},
 //          {0.055, {1.5, -1},    0.101491, 1.131687e-05, 'g'},
 //          {0.056, {1.5, -1},   0.1033467, 1.216311e-05, 'h'},
@@ -28,11 +28,13 @@ TEST(BulkSphere, HandlesInput) {
   for (const auto &data : parameters_and_results) {
     nmie.SetLayersSize({std::get<0>(data)});
     nmie.SetLayersIndex({std::get<1>(data)});
+//    nmie.SetMaxTerms(150);
     nmie.RunMieCalculation();
     double Qext = static_cast<double>(nmie.GetQext());
     double Qsca = static_cast<double>(nmie.GetQsca());
     EXPECT_FLOAT_EQ(std::get<2>(data), Qext)
-              << "Extinction of the bulk sphere, test case:" << std::get<4>(data);
+              << "Extinction of the bulk sphere, test case:" << std::get<4>(data)
+              << "\nnmax_ = " << nmie.GetMaxTerms();
     EXPECT_FLOAT_EQ(std::get<3>(data), Qsca)
               << "Scattering of the bulk sphere, test case:" << std::get<4>(data);
   }