|
@@ -24,38 +24,355 @@
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
|
//**********************************************************************************//
|
|
|
+#include "bessel.h"
|
|
|
+#include <algorithm>
|
|
|
#include <complex>
|
|
|
#include <cmath>
|
|
|
+#include <limits>
|
|
|
#include <stdexcept>
|
|
|
#include <vector>
|
|
|
|
|
|
namespace nmie {
|
|
|
- // Implementation of Bessel functions from Bohren and Huffman book, pp. 86-87, eq 4.11
|
|
|
- // Calculate all orders of function from 0 to nmax (included) for argument rho
|
|
|
- std::vector< std::complex<double> > bessel_j(int nmax, std::complex<double> rho) {
|
|
|
- if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
|
|
|
- std::vector< std::complex<double> > j(nmax+1);
|
|
|
- j[0] = std::sin(rho)/rho;
|
|
|
- if (nmax == 0) return j;
|
|
|
- j[1] = std::sin(rho)/(rho*rho) - std::cos(rho)/rho;
|
|
|
- if (nmax == 1) return j;
|
|
|
- for (int i = 2; i < n+1; ++i) {
|
|
|
- int n = i - 1;
|
|
|
- j[n+1] = static_cast<double>(2*n+1)/rho*j[n] - j[n-1];
|
|
|
+ namespace bessel {
|
|
|
+ // Implementation of spherical Bessel functions from
|
|
|
+ // L.-W. Cai / Computer Physics Communications 182 (2011) 663–668
|
|
|
+ std::vector< std::complex<double> > bessel_j(int nmax, std::complex<double> rho) {
|
|
|
+ if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
|
|
|
+ std::complex<double> c_zero(0.0,0.0);
|
|
|
+ std::vector< std::complex<double> > j(nmax+1, c_zero);
|
|
|
+ // Select normalization amplitude
|
|
|
+ int norm_n = 0;
|
|
|
+ std::complex<double> norm_value = std::sin(rho)/rho,
|
|
|
+ tmp = std::sin(rho)/(rho*rho) - std::cos(rho)/rho;
|
|
|
+ if (std::abs(tmp) > std::abs(norm_value)) {
|
|
|
+ norm_value = tmp;
|
|
|
+ norm_n = 1;
|
|
|
+ }
|
|
|
+ // calculate number of terms for backward recursion Cai eq(11)
|
|
|
+ double theta = std::arg(rho), r = std::abs(rho);
|
|
|
+ double M = (1.83 + 4.1* std::pow(std::sin(theta), 0.36)) *
|
|
|
+ std::pow(r, (0.91 - 0.43*std::pow(std::sin(theta),0.33)))
|
|
|
+ + 9.0*(1.0-std::sqrt(std::sin(theta)));
|
|
|
+ int terms = std::max(nmax+2, static_cast<int>(std::ceil(M))+2);
|
|
|
+ printf("terms = %d\n", terms);
|
|
|
+ // Seed for eq(2)
|
|
|
+ std::complex<double> b_np1(0.0, 0.0), b_nm1;
|
|
|
+ double eps = std::numeric_limits<double>::epsilon();
|
|
|
+ std::complex<double> b_n(eps, 0.0);
|
|
|
+ //recurence
|
|
|
+ for (int n = terms*2; n > 0; --n) {
|
|
|
+ b_nm1 = (2.0*static_cast<double>(n)+1.0)/rho*b_n - b_np1;
|
|
|
+ if (n-1 < nmax+1) j[n-1] = b_nm1;
|
|
|
+ }
|
|
|
+ // normalize
|
|
|
+ norm_value /= j[norm_n];
|
|
|
+ for (auto& value : j) value *=norm_value;
|
|
|
+
|
|
|
+ return j;
|
|
|
}
|
|
|
- }
|
|
|
- // Implementation of Bessel functions from Bohren and Huffman book, pp. 86-87, eq 4.11
|
|
|
- // Calculate all orders of function from 0 to nmax (included) for argument rho
|
|
|
- std::vector< std::complex<double> > bessel_y(int nmax, std::complex<double> rho) {
|
|
|
- if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
|
|
|
- std::vector< std::complex<double> > j(nmax+1);
|
|
|
- j[0] = -std::cos(rho)/rho;
|
|
|
- if (nmax == 0) return j;
|
|
|
- j[1] = -std::cos(rho)/(rho*rho) - std::sin(rho)/rho;
|
|
|
- if (nmax == 1) return j;
|
|
|
- for (int i = 2; i < n+1; ++i) {
|
|
|
- int n = i - 1;
|
|
|
- j[n+1] = static_cast<double>(2*n+1)/rho*j[n] - j[n-1];
|
|
|
+ // Implementation of Bessel functions from Bohren and Huffman book, pp. 86-87, eq 4.11
|
|
|
+ // Calculate all orders of function from 0 to nmax (included) for argument rho
|
|
|
+ std::vector< std::complex<double> > bh_bessel_j(int nmax, std::complex<double> rho) {
|
|
|
+ if (nmax < 0) throw std::invalid_argument("Bessel order should be >= 0 (nmie::bessel_j)\n");
|
|
|
+ std::vector< std::complex<double> > j(nmax+1);
|
|
|
+ j[0] = std::sin(rho)/rho;
|
|
|
+ if (nmax == 0) return j;
|
|
|
+ j[1] = std::sin(rho)/(rho*rho) - std::cos(rho)/rho;
|
|
|
+ if (nmax == 1) return j;
|
|
|
+ for (int i = 2; i < nmax+1; ++i) {
|
|
|
+ int n = i - 1;
|
|
|
+ j[n+1] = static_cast<double>(2*n+1)/rho*j[n] - j[n-1];
|
|
|
+ }
|
|
|
+ return j;
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+
|
|
|
+// !*****************************************************************************80
|
|
|
+//
|
|
|
+// C++ port of fortran code
|
|
|
+//
|
|
|
+// !! CSPHJY: spherical Bessel functions jn(z) and yn(z) for complex argument.
|
|
|
+// !
|
|
|
+// ! Discussion:
|
|
|
+// !
|
|
|
+// ! This procedure computes spherical Bessel functions jn(z) and yn(z)
|
|
|
+// ! and their derivatives for a complex argument.
|
|
|
+// !
|
|
|
+// ! Licensing:
|
|
|
+// !
|
|
|
+// ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
|
|
|
+// ! they give permission to incorporate this routine into a user program
|
|
|
+// ! provided that the copyright is acknowledged.
|
|
|
+// !
|
|
|
+// ! Modified:
|
|
|
+// !
|
|
|
+// ! 01 August 2012
|
|
|
+// !
|
|
|
+// ! Author:
|
|
|
+// !
|
|
|
+// ! Shanjie Zhang, Jianming Jin
|
|
|
+// !
|
|
|
+// ! Reference:
|
|
|
+// !
|
|
|
+// ! Shanjie Zhang, Jianming Jin,
|
|
|
+// ! Computation of Special Functions,
|
|
|
+// ! Wiley, 1996,
|
|
|
+// ! ISBN: 0-471-11963-6,
|
|
|
+// ! LC: QA351.C45.
|
|
|
+// !
|
|
|
+// ! Parameters:
|
|
|
+// !
|
|
|
+// ! Input, integer ( kind = 4 ) N, the order of jn(z) and yn(z).
|
|
|
+// !
|
|
|
+// ! Input, complex ( kind = 8 ) Z, the argument.
|
|
|
+// !
|
|
|
+// ! Output, integer ( kind = 4 ) NM, the highest order computed.
|
|
|
+// !
|
|
|
+// ! Output, complex ( kind = 8 ) CSJ(0:N0, CDJ(0:N), CSY(0:N), CDY(0:N),
|
|
|
+// ! the values of jn(z), jn'(z), yn(z), yn'(z).
|
|
|
+// !
|
|
|
+ void csphjy (int n, std::complex<double>z, int& nm,
|
|
|
+ std::vector< std::complex<double> >& csj,
|
|
|
+ std::vector< std::complex<double> >& cdj,
|
|
|
+ std::vector< std::complex<double> >& csy,
|
|
|
+ std::vector< std::complex<double> >& cdy ) {
|
|
|
+ double a0;
|
|
|
+ csj.resize(n+1);
|
|
|
+ cdj.resize(n+1);
|
|
|
+ csy.resize(n+1);
|
|
|
+ cdy.resize(n+1);
|
|
|
+ std::complex<double> cf, cf0, cf1, cs, csa, csb;
|
|
|
+ int k, m;
|
|
|
+ a0 = std::abs(z);
|
|
|
+ nm = n;
|
|
|
+ if (a0 < 1.0e-60) {
|
|
|
+ for (int k = 0; k < n+1; ++k) {
|
|
|
+ csj[k] = 0.0;
|
|
|
+ cdj[k] = 0.0;
|
|
|
+ csy[k] = -1.0e+300;
|
|
|
+ cdy[k] = 1.0e+300;
|
|
|
+ }
|
|
|
+ csj[0] = std::complex<double>( 1.0, 0.0);
|
|
|
+ cdj[1] = std::complex<double>( 0.333333333333333, 0.0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ csj[0] = std::sin ( z ) / z;
|
|
|
+ csj[1] = ( csj[0] - std::cos ( z ) ) / z;
|
|
|
+
|
|
|
+ if ( 2 <= n ) {
|
|
|
+ csa = csj[0];
|
|
|
+ csb = csj[1];
|
|
|
+ m = msta1 ( a0, 200 );
|
|
|
+ if ( m < n ) nm = m;
|
|
|
+ else m = msta2 ( a0, n, 15 );
|
|
|
+ cf0 = 0.0;
|
|
|
+ cf1 = 1.0e-100;
|
|
|
+ for (int k = m; k>=0; --k) {
|
|
|
+ cf = ( 2.0 * k + 3.0 ) * cf1 / z - cf0;
|
|
|
+ if ( k <= nm ) csj[k] = cf;
|
|
|
+ cf0 = cf1;
|
|
|
+ cf1 = cf;
|
|
|
+ }
|
|
|
+ if ( std::abs ( csa ) <= std::abs ( csb ) ) cs = csb / cf0;
|
|
|
+ else cs = csa / cf;
|
|
|
+ for (int k = 0; k <= nm; ++k) {
|
|
|
+ csj[k] = cs * csj[k];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cdj[0] = ( std::cos ( z ) - std::sin ( z ) / z ) / z;
|
|
|
+ for (int k = 1; k <=nm; ++k) {
|
|
|
+ cdj[k] = csj[k-1] - ( k + 1.0 ) * csj[k] / z;
|
|
|
+ }
|
|
|
+ csy[0] = - std::cos ( z ) / z;
|
|
|
+ csy[1] = ( csy[0] - std::sin ( z ) ) / z;
|
|
|
+ cdy[0] = ( std::sin ( z ) + std::cos ( z ) / z ) / z;
|
|
|
+ cdy[1] = ( 2.0 * cdy[0] - std::cos ( z ) ) / z;
|
|
|
+ for (int k = 2; k<=nm; ++k) {
|
|
|
+ if ( std::abs ( csj[k-2] ) < std::abs ( csj[k-1] ) ) {
|
|
|
+ csy[k] = ( csj[k] * csy[k-1] - 1.0 / ( z * z ) ) / csj[k-1];
|
|
|
+ } else {
|
|
|
+ csy[k] = ( csj[k] * csy[k-2] - ( 2.0 * k - 1.0 ) / std::pow(z,3) )
|
|
|
+ / csj[k-2];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int k = 2; k<=nm; ++k) {
|
|
|
+ cdy[k] = csy[k-1] - ( k + 1.0 ) * csy[k] / z;
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // function msta2 ( x, n, mp )
|
|
|
+
|
|
|
+ // !*****************************************************************************80
|
|
|
+ // !
|
|
|
+ // !! MSTA2 determines a backward recurrence starting point for Jn(x).
|
|
|
+ // !
|
|
|
+ // ! Discussion:
|
|
|
+ // !
|
|
|
+ // ! This procedure determines the starting point for a backward
|
|
|
+ // ! recurrence such that all Jn(x) has MP significant digits.
|
|
|
+ // !
|
|
|
+ // ! Licensing:
|
|
|
+ // !
|
|
|
+ // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
|
|
|
+ // ! they give permission to incorporate this routine into a user program
|
|
|
+ // ! provided that the copyright is acknowledged.
|
|
|
+ // !
|
|
|
+ // ! Modified:
|
|
|
+ // !
|
|
|
+ // ! 08 July 2012
|
|
|
+ // !
|
|
|
+ // ! Author:
|
|
|
+ // !
|
|
|
+ // ! Shanjie Zhang, Jianming Jin
|
|
|
+ // !
|
|
|
+ // ! Reference:
|
|
|
+ // !
|
|
|
+ // ! Shanjie Zhang, Jianming Jin,
|
|
|
+ // ! Computation of Special Functions,
|
|
|
+ // ! Wiley, 1996,
|
|
|
+ // ! ISBN: 0-471-11963-6,
|
|
|
+ // ! LC: QA351.C45.
|
|
|
+ // !
|
|
|
+ // ! Parameters:
|
|
|
+ // !
|
|
|
+ // ! Input, real ( kind = 8 ) X, the argument of Jn(x).
|
|
|
+ // !
|
|
|
+ // ! Input, integer ( kind = 4 ) N, the order of Jn(x).
|
|
|
+ // !
|
|
|
+ // ! Input, integer ( kind = 4 ) MP, the number of significant digits.
|
|
|
+ // !
|
|
|
+ // ! Output, integer ( kind = 4 ) MSTA2, the starting point.
|
|
|
+ // !
|
|
|
+ int msta2 ( double x, int n, int mp ) {
|
|
|
+ double a0, ejn, f, f0, f1, hmp;
|
|
|
+ int it, n0, n1, nn;
|
|
|
+ double obj;
|
|
|
+ a0 = std::abs ( x );
|
|
|
+ hmp = 0.5 * mp;
|
|
|
+ ejn = envj ( n, a0 );
|
|
|
+ if ( ejn <= hmp ) {
|
|
|
+ obj = mp;
|
|
|
+ n0 = static_cast<int> ( 1.1 * a0 );
|
|
|
+ } else {
|
|
|
+ obj = hmp + ejn;
|
|
|
+ n0 = n;
|
|
|
+ }
|
|
|
+ f0 = envj ( n0, a0 ) - obj;
|
|
|
+ n1 = n0 + 5;
|
|
|
+ f1 = envj ( n1, a0 ) - obj;
|
|
|
+ for (int it = 1; it < 21; ++it) {
|
|
|
+ nn = n1 - ( n1 - n0 ) / ( 1.0 - f0 / f1 );
|
|
|
+ f = envj ( nn, a0 ) - obj;
|
|
|
+ if ( std::abs ( nn - n1 ) < 1 ) break;
|
|
|
+ n0 = n1;
|
|
|
+ f0 = f1;
|
|
|
+ n1 = nn;
|
|
|
+ f1 = f;
|
|
|
+ }
|
|
|
+ return nn + 10;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// !*****************************************************************************80
|
|
|
+// !
|
|
|
+// !! MSTA1 determines a backward recurrence starting point for Jn(x).
|
|
|
+// !
|
|
|
+// ! Discussion:
|
|
|
+// !
|
|
|
+// ! This procedure determines the starting point for backward
|
|
|
+// ! recurrence such that the magnitude of
|
|
|
+// ! Jn(x) at that point is about 10^(-MP).
|
|
|
+// !
|
|
|
+// ! Licensing:
|
|
|
+// !
|
|
|
+// ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
|
|
|
+// ! they give permission to incorporate this routine into a user program
|
|
|
+// ! provided that the copyright is acknowledged.
|
|
|
+// !
|
|
|
+// ! Modified:
|
|
|
+// !
|
|
|
+// ! 08 July 2012
|
|
|
+// !
|
|
|
+// ! Author:
|
|
|
+// !
|
|
|
+// ! Shanjie Zhang, Jianming Jin
|
|
|
+// !
|
|
|
+// ! Reference:
|
|
|
+// !
|
|
|
+// ! Shanjie Zhang, Jianming Jin,
|
|
|
+// ! Computation of Special Functions,
|
|
|
+// ! Wiley, 1996,
|
|
|
+// ! ISBN: 0-471-11963-6,
|
|
|
+// ! LC: QA351.C45.
|
|
|
+// !
|
|
|
+// ! Parameters:
|
|
|
+// !
|
|
|
+// ! Input, real ( kind = 8 ) X, the argument.
|
|
|
+// !
|
|
|
+// ! Input, integer ( kind = 4 ) MP, the negative logarithm of the
|
|
|
+// ! desired magnitude.
|
|
|
+// !
|
|
|
+// ! Output, integer ( kind = 4 ) MSTA1, the starting point.
|
|
|
+// !
|
|
|
+ int msta1 ( double x, int mp ) {
|
|
|
+ double a0, f, f0, f1;
|
|
|
+ int it, n0, n1, nn;
|
|
|
+ a0 = std::abs ( x );
|
|
|
+ n0 = static_cast<int> (1.1 * a0 ) + 1;
|
|
|
+ f0 = envj ( n0, a0 ) - mp;
|
|
|
+ n1 = n0 + 5;
|
|
|
+ f1 = envj ( n1, a0 ) - mp;
|
|
|
+ for (int it = 1; it <= 20; ++it) {
|
|
|
+ nn = n1 - ( n1 - n0 ) / ( 1.0 - f0 / f1 );
|
|
|
+ f = envj ( nn, a0 ) - mp;
|
|
|
+ if ( abs ( nn - n1 ) < 1 ) break;
|
|
|
+ n0 = n1;
|
|
|
+ f0 = f1;
|
|
|
+ n1 = nn;
|
|
|
+ f1 = f;
|
|
|
+ }
|
|
|
+ return nn;
|
|
|
+ }
|
|
|
+ // function envj ( n, x )
|
|
|
+
|
|
|
+ // !*****************************************************************************80
|
|
|
+ // !
|
|
|
+ // !! ENVJ is a utility function used by MSTA1 and MSTA2.
|
|
|
+ // !
|
|
|
+ // ! Licensing:
|
|
|
+ // !
|
|
|
+ // ! This routine is copyrighted by Shanjie Zhang and Jianming Jin. However,
|
|
|
+ // ! they give permission to incorporate this routine into a user program
|
|
|
+ // ! provided that the copyright is acknowledged.
|
|
|
+ // !
|
|
|
+ // ! Modified:
|
|
|
+ // !
|
|
|
+ // ! 14 March 2012
|
|
|
+ // !
|
|
|
+ // ! Author:
|
|
|
+ // !
|
|
|
+ // ! Shanjie Zhang, Jianming Jin
|
|
|
+ // !
|
|
|
+ // ! Reference:
|
|
|
+ // !
|
|
|
+ // ! Shanjie Zhang, Jianming Jin,
|
|
|
+ // ! Computation of Special Functions,
|
|
|
+ // ! Wiley, 1996,
|
|
|
+ // ! ISBN: 0-471-11963-6,
|
|
|
+ // ! LC: QA351.C45.
|
|
|
+ // !
|
|
|
+ // ! Parameters:
|
|
|
+ // !
|
|
|
+ // ! Input, integer ( kind = 4 ) N, ?
|
|
|
+ // !
|
|
|
+ // ! Input, real ( kind = 8 ) X, ?
|
|
|
+ // !
|
|
|
+ // ! Output, real ( kind = 8 ) ENVJ, ?
|
|
|
+ // !
|
|
|
+ double envj (int n, double x ) {
|
|
|
+ return 0.5 * std::log10(6.28 * n) - n * std::log10(1.36 * x / static_cast<double>(n) );
|
|
|
+ }
|
|
|
+ } // end of namespace bessel
|
|
|
} // end of namespace nmie
|