/*---------------------------------------------------------------------- eri_sf.c Special functions used in LIBERI. This is based on Spherical_Bessel.c and openmx_common.c in OpenMX 3.4 package. 22 Nov. 2001, opnemx_common.c by T. Ozaki 08 Nov. 2005 Spherical_Bessel.c by T.Ozaki 16 Feb. 2009, M. Toyoda ----------------------------------------------------------------------*/ #include #include #include #include #include #include "eri_sf.h" #include "eri_def.h" #define LMAX_MAX 512 #define MAX_ITR 50 #define ERR_MAX 1e-12 #define CG_JMAX 30 static void sph_bessel_asym(double x, int l, double *sb, double *dsb); static void sph_bessel_drec(double x, int l, double *sb, double *dsb); static void sph_bessel_arec(double x, int l, double *sb, double *dsb); static double CG(int j1, int m1, int j2, int m2, int j, int m); /*---------------------------------------------------------------------- rotation matrix for Complex -> Real conversion of SH Z(l, m) = a(m)*Y(l, m) + b(m)*Y(l, -m) where a(m) = 1/sqrt(2) (m>0), 1 (m=0), and i(-1)^m/sqrt(2) (m<0) b(m) = (-1)^m/sqrt(2) (m>0), 0 (m=0), and -i/sqrt(2) (m<0) a(m) = (-1)^m/sqrt(2) (m>0), 1 (m=0), and i/sqrt(2) (m<0) b(m) = 1/sqrt(2) (m>0), 0 (m=0), and -i(-1)^m/sqrt(2) (m<0) ----------------------------------------------------------------------*/ static void ERI_RCSH_Coeff(int m, double a[2], double b[2]) { double oost; oost = 1.0/sqrt(2.0); if (m>0) { if (m%2==0) { a[0] = oost; a[1] = 0.0; b[0] = oost; b[1] = 0.0; } else { a[0] = -oost; a[1] = 0.0; b[0] = oost; b[1] = 0.0; } } else if (m<0) { if (abs(m)%2==0) { a[0] = 0.0; a[1] = oost; b[0] = 0.0; b[1] = -oost; } else { a[0] = 0.0; a[1] = oost; b[0] = 0.0; b[1] = oost; } } else { /* m == 0 */ a[0] = 1.0; a[1] = 0.0; b[0] = 0.0; b[1] = 0.0; } #if 0 if (m>0) { if (m%2==0) { a[0] = oost; a[1] = 0.0; b[0] = oost; b[1] = 0.0; } else { a[0] = oost; a[1] = 0.0; b[0] = -oost; b[1] = 0.0; } } else if (m<0) { if (m%2==0) { a[0] = 0.0; a[1] = oost; b[0] = 0.0; b[1] = -oost; } else { a[0] = 0.0; a[1] = -oost; b[0] = 0.0; b[1] = -oost; } } else { /* m == 0 */ a[0] = 1.0; a[1] = 0.0; b[0] = 0.0; b[1] = 0.0; } #endif } /*---------------------------------------------------------------------- rotation matrix for Real -> Complex conversion of SH Y(l, m) = c(m)*Z(l, m) + d(m)*Z(l, -m) where c(m) = 1/sqrt(2) (m>0), 1 (m=0), and -i(-1)^m/sqrt(2) (m<0) f(m) = i/sqrt(2) (m>0), 0 (m=0), and (-1)^m/sqrt(2) (m<0) ----------------------------------------------------------------------*/ static void ERI_RCSH_Coeff_Inverse(int m, double c[2], double d[2]) { double na, nb, a[2], b[2]; if (0==m) { c[0] = 1.0; c[1] = 0.0; d[0] = 0.0; d[1] = 0.0; } else { ERI_RCSH_Coeff(m, a, b); na = a[0]*a[0] + a[1]*a[1]; nb = b[0]*b[0] + b[1]*b[1]; c[0] = 0.5*a[0]/na; c[1] = -0.5*a[1]/na; d[0] = 0.5*b[0]/nb; d[1] = -0.5*b[1]/nb; } } /*---------------------------------------------------------------------- ERI_Spherical_Bessel Routine to calculate the spherical Bessel function of the first kind j_l(x) of the order from 0 to l and their derivatives. Descending recurrence algoritm is used for small x in order to avoid accumulation of rounding errors, whereas ascending recurrence is used for large x because rounding error can be negligible and the descending recurrence becomes too slow for large x. Reference: A. jablonski, "Numerical Evaluation of Spherical Bessel Functions of the First Kind", Journal of Computational Physics Vol. 111, pp. 256 (1994). ----------------------------------------------------------------------*/ void ERI_Spherical_Bessel( double x, /* (IN) argument x >= 0*/ int l, /* (IN) positive integer */ double *sb, /* (OUT) function values of order from 0 to l */ double *dsb /* (OUT) derivatives of order from 0 to l */ ) { const double xmin = 1e-10; const double xthresh = 100.0; /* negative x value is invalid */ assert( x >= 0.0 ); assert( l >= 0 ); if (x= 0 && m >= 0 && m <= l ); if ((1.0-cut0)0){ f = 1.0; tmp = -sqrt((1.0-x)*(1.0+x)); for (i=1; i<=m; i++){ Pm *= f*tmp; f += 2.0; } } if (l==m){ /* m = l */ p0 = Pm; p1 = 0.0; } else{ /* m < l */ Pm1 = x*(2.0*(double)m + 1.0)*Pm; if (m= 0 ); if (n==0) { *L = 1.0; *dL = 0.0; return; } else if (n==1) { *L = (1.0 + alpha)-x; *dL = -1.0; return; } /* n > 1 */ L1 = 1.0; /* L_0 */ L2 = (1.0+alpha)-x; /* L_1 */ /* recurrence */ for (i=1; i = delta(m,m1+m2)*sqrt(2j+1) *sqrt{ (j1+j2-j)! (j+j1-j2)! (j+j2-j1)! / (j+j1+j2+1)! } *sqrt{ (j1+m1)! (j1-m1)! (j2+m2)! (j2-m2)! (j+m)! (j-m!) } *sum_k (-1)^k / ( k! (j1+j2-j-k)! (j1-m1-k)! (j2+m2-k)! (j-j2+m1+k)! (j-j1-m2+k)! ) See M. Avbramowitz and I.A. Stegun (eds.), "Handbook of Mathematical Functions with Formulas, Graphs, and Tables," Dover Publications, Inc. (Mineola, NY), 1972; an electronic copy of this book is available at http://www.math.sfu.ca/~cbm/aands/. ----------------------------------------------------------------------*/ static double CG(int j1, int m1, int j2, int m2, int j, int m) { int kmin, kmax, k; double sgn, cg; const int fmax = 3*CG_JMAX; double f[3*CG_JMAX]; /* this routine safely calculates when j1, j2, j < CG_JMAX */ if (j1>CG_JMAX || j2>CG_JMAX || j>CG_JMAX) { fprintf(stderr, "*** out of bound (%s, %d)\n", __FILE__, __LINE__); abort(); } /* factorials */ f[0] = f[1] = 1.0; for (k=2; kj1 || abs(m2)>j2 || abs(m)>j) return 0.0; /* determin the range of k max(0, -j+j2-m1, -j+j1+m2) <= k <= min(j1+j2-j, j1-m1, j2+m2) */ kmin = 0; if (kmin < -j+j2-m1) kmin = -j+j2-m1; if (kmin < -j+j1+m2) kmin = -j+j1+m2; kmax = j1+j2-j; if (kmax > j1-m1) kmax = j1-m1; if (kmax > j2+m2) kmax = j2+m2; if (kmin>kmax) return 0.0; cg = 0.0; sgn = kmin%2==0 ? 1.0 : -1.0; for (k=kmin; k<=kmax; k++) { cg += sgn/(f[k]*f[j1+j2-j-k]*f[j1-m1-k]*f[j2+m2-k] *f[j-j2+m1+k]*f[j-j1-m2+k]); sgn = -sgn; } cg *= sqrt( (double)(2*j+1)*f[j1+j2-j]*f[j+j1-j2]*f[j+j2-j1] *f[j1+m1]*f[j1-m1]*f[j2+m2]*f[j2-m2]*f[j+m]*f[j-m] /f[j+j1+j2+1] ); return cg; } void ERI_GLQ(double *x, double *w, int n) { int i, itr; double err, xi, i1, L, dL; for (i=0; iERR_MAX); itr++) { ERI_Associated_Laguerre_Polynomial(xi, n, 0.0, &L, &dL); err = L/dL; xi -= err; } #ifndef NDEBUG if (fabs(err)>ERR_MAX) { fprintf(stderr, "*** error in ERI_GLQ: no convergence\n"); fprintf(stderr, " err = %12.4e\n", err); } #endif x[i] = xi; w[i] = 1.0/dL/dL/xi; } /* end loop of i */ }