📄 legendre_poly.c
字号:
/* specfunc/legendre_poly.c * * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 Gerard Jungman * * 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 2 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* Author: G. Jungman */#include <config.h>#include <gsl/gsl_math.h>#include <gsl/gsl_errno.h>#include <gsl/gsl_sf_bessel.h>#include <gsl/gsl_sf_exp.h>#include <gsl/gsl_sf_gamma.h>#include <gsl/gsl_sf_log.h>#include <gsl/gsl_sf_pow_int.h>#include <gsl/gsl_sf_legendre.h>#include "error.h"/* Calculate P_m^m(x) from the analytic result: * P_m^m(x) = (-1)^m (2m-1)!! (1-x^2)^(m/2) , m > 0 * = 1 , m = 0 */static double legendre_Pmm(int m, double x){ if(m == 0) { return 1.0; } else { double p_mm = 1.0; double root_factor = sqrt(1.0-x)*sqrt(1.0+x); double fact_coeff = 1.0; int i; for(i=1; i<=m; i++) { p_mm *= -fact_coeff * root_factor; fact_coeff += 2.0; } return p_mm; }}/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/intgsl_sf_legendre_P1_e(double x, gsl_sf_result * result){ /* CHECK_POINTER(result) */ { result->val = x; result->err = 0.0; return GSL_SUCCESS; }}intgsl_sf_legendre_P2_e(double x, gsl_sf_result * result){ /* CHECK_POINTER(result) */ { result->val = 0.5*(3.0*x*x - 1.0); result->err = GSL_DBL_EPSILON * (fabs(3.0*x*x) + 1.0); return GSL_SUCCESS; }}intgsl_sf_legendre_P3_e(double x, gsl_sf_result * result){ /* CHECK_POINTER(result) */ { result->val = 0.5*x*(5.0*x*x - 3.0); result->err = GSL_DBL_EPSILON * (fabs(result->val) + 0.5 * fabs(x) * (fabs(5.0*x*x) + 3.0)); return GSL_SUCCESS; }}intgsl_sf_legendre_Pl_e(const int l, const double x, gsl_sf_result * result){ /* CHECK_POINTER(result) */ if(l < 0 || x < -1.0 || x > 1.0) { DOMAIN_ERROR(result); } else if(l == 0) { result->val = 1.0; result->err = 0.0; return GSL_SUCCESS; } else if(l == 1) { result->val = x; result->err = 0.0; return GSL_SUCCESS; } else if(l == 2) { result->val = 0.5 * (3.0*x*x - 1.0); result->err = 3.0 * GSL_DBL_EPSILON * fabs(result->val); return GSL_SUCCESS; } else if(x == 1.0) { result->val = 1.0; result->err = 0.0; return GSL_SUCCESS; } else if(x == -1.0) { result->val = ( GSL_IS_ODD(l) ? -1.0 : 1.0 ); result->err = 0.0; return GSL_SUCCESS; } else if(l < 100000) { /* upward recurrence: l P_l = (2l-1) z P_{l-1} - (l-1) P_{l-2} */ double p_ellm2 = 1.0; /* P_0(x) */ double p_ellm1 = x; /* P_1(x) */ double p_ell = p_ellm1; int ell; for(ell=2; ell <= l; ell++){ p_ell = (x*(2*ell-1)*p_ellm1 - (ell-1)*p_ellm2) / ell; p_ellm2 = p_ellm1; p_ellm1 = p_ell; } result->val = p_ell; result->err = (0.5 * ell + 1.0) * GSL_DBL_EPSILON * fabs(p_ell); return GSL_SUCCESS; } else { /* Asymptotic expansion. * [Olver, p. 473] */ double u = l + 0.5; double th = acos(x); gsl_sf_result J0; gsl_sf_result Jm1; int stat_J0 = gsl_sf_bessel_J0_e(u*th, &J0); int stat_Jm1 = gsl_sf_bessel_Jn_e(-1, u*th, &Jm1); double pre; double B00; double c1; /* B00 = 1/8 (1 - th cot(th) / th^2 * pre = sqrt(th/sin(th)) */ if(th < GSL_ROOT4_DBL_EPSILON) { B00 = (1.0 + th*th/15.0)/24.0; pre = 1.0 + th*th/12.0; } else { double sin_th = sqrt(1.0 - x*x); double cot_th = x / sin_th; B00 = 1.0/8.0 * (1.0 - th * cot_th) / (th*th); pre = sqrt(th/sin_th); } c1 = th/u * B00; result->val = pre * (J0.val + c1 * Jm1.val); result->err = pre * (J0.err + fabs(c1) * Jm1.err); result->err += GSL_SQRT_DBL_EPSILON * fabs(result->val); return GSL_ERROR_SELECT_2(stat_J0, stat_Jm1); }}intgsl_sf_legendre_Pl_array(const int lmax, const double x, double * result_array){ /* CHECK_POINTER(result_array) */ if(lmax < 0 || x < -1.0 || x > 1.0) { GSL_ERROR ("domain error", GSL_EDOM); } else if(lmax == 0) { result_array[0] = 1.0; return GSL_SUCCESS; } else if(lmax == 1) { result_array[0] = 1.0; result_array[1] = x; return GSL_SUCCESS; } else { /* upward recurrence: l P_l = (2l-1) z P_{l-1} - (l-1) P_{l-2} */ double p_ellm2 = 1.0; /* P_0(x) */ double p_ellm1 = x; /* P_1(x) */ double p_ell = p_ellm1; int ell; result_array[0] = 1.0; result_array[1] = x; for(ell=2; ell <= lmax; ell++){ p_ell = (x*(2*ell-1)*p_ellm1 - (ell-1)*p_ellm2) / ell; p_ellm2 = p_ellm1; p_ellm1 = p_ell; result_array[ell] = p_ell; } return GSL_SUCCESS; }}intgsl_sf_legendre_Pl_deriv_array(const int lmax, const double x, double * result_array, double * result_deriv_array){ int stat_array = gsl_sf_legendre_Pl_array(lmax, x, result_array); if(lmax >= 0) result_deriv_array[0] = 0.0; if(lmax >= 1) result_deriv_array[1] = 1.0; if(stat_array == GSL_SUCCESS) { int ell; if(fabs(x - 1.0)*(lmax+1.0)*(lmax+1.0) < GSL_SQRT_DBL_EPSILON) { /* x is near 1 */ for(ell = 2; ell <= lmax; ell++) { const double pre = 0.5 * ell * (ell+1.0); result_deriv_array[ell] = pre * (1.0 - 0.25 * (1.0-x) * (ell+2.0)*(ell-1.0)); } } else if(fabs(x + 1.0)*(lmax+1.0)*(lmax+1.0) < GSL_SQRT_DBL_EPSILON) { /* x is near -1 */ for(ell = 2; ell <= lmax; ell++) { const double sgn = ( GSL_IS_ODD(ell) ? 1.0 : -1.0 ); /* derivative is odd in x for even ell */ const double pre = sgn * 0.5 * ell * (ell+1.0); result_deriv_array[ell] = pre * (1.0 - 0.25 * (1.0+x) * (ell+2.0)*(ell-1.0)); } } else { const double diff_a = 1.0 + x; const double diff_b = 1.0 - x; for(ell = 2; ell <= lmax; ell++) { result_deriv_array[ell] = - ell * (x * result_array[ell] - result_array[ell-1]) / (diff_a * diff_b); } } return GSL_SUCCESS; } else { return stat_array; }}intgsl_sf_legendre_Plm_e(const int l, const int m, const double x, gsl_sf_result * result){ /* If l is large and m is large, then we have to worry * about overflow. Calculate an approximate exponent which * measures the normalization of this thing. */ double dif = l-m; double sum = l+m; double exp_check = 0.5 * log(2.0*l+1.0) + 0.5 * dif * (log(dif)-1.0) - 0.5 * sum * (log(sum)-1.0); /* CHECK_POINTER(result) */ if(m < 0 || l < m || x < -1.0 || x > 1.0) { DOMAIN_ERROR(result); } else if(exp_check < GSL_LOG_DBL_MIN + 10.0){ /* Bail out. */ OVERFLOW_ERROR(result); } else { /* Account for the error due to the * representation of 1-x. */ const double err_amp = 1.0 / (GSL_DBL_EPSILON + fabs(1.0-fabs(x))); /* P_m^m(x) and P_{m+1}^m(x) */ double p_mm = legendre_Pmm(m, x); double p_mmp1 = x * (2*m + 1) * p_mm; if(l == m){ result->val = p_mm; result->err = err_amp * 2.0 * GSL_DBL_EPSILON * fabs(p_mm); return GSL_SUCCESS; } else if(l == m + 1) { result->val = p_mmp1; result->err = err_amp * 2.0 * GSL_DBL_EPSILON * fabs(p_mmp1); return GSL_SUCCESS; } else{ /* upward recurrence: (l-m) P(l,m) = (2l-1) z P(l-1,m) - (l+m-1) P(l-2,m) * start at P(m,m), P(m+1,m) */ double p_ellm2 = p_mm; double p_ellm1 = p_mmp1; double p_ell = 0.0; int ell; for(ell=m+2; ell <= l; ell++){ p_ell = (x*(2*ell-1)*p_ellm1 - (ell+m-1)*p_ellm2) / (ell-m); p_ellm2 = p_ellm1; p_ellm1 = p_ell; } result->val = p_ell; result->err = err_amp * (0.5*(l-m) + 1.0) * GSL_DBL_EPSILON * fabs(p_ell); return GSL_SUCCESS; } }}intgsl_sf_legendre_Plm_array(const int lmax, const int m, const double x, double * result_array){ /* If l is large and m is large, then we have to worry * about overflow. Calculate an approximate exponent which * measures the normalization of this thing. */ double dif = lmax-m; double sum = lmax+m; double exp_check = 0.5 * log(2.0*lmax+1.0) + 0.5 * dif * (log(dif)-1.0) - 0.5 * sum * (log(sum)-1.0); /* CHECK_POINTER(result_array) */ if(m < 0 || lmax < m || x < -1.0 || x > 1.0) { GSL_ERROR ("domain error", GSL_EDOM); } else if(m > 0 && (x == 1.0 || x == -1.0)) { int ell; for(ell=m; ell<=lmax; ell++) result_array[ell-m] = 0.0; return GSL_SUCCESS; } else if(exp_check < GSL_LOG_DBL_MIN + 10.0){ /* Bail out. */ GSL_ERROR ("overflow", GSL_EOVRFLW); } else { double p_mm = legendre_Pmm(m, x); double p_mmp1 = x * (2.0*m + 1.0) * p_mm; if(lmax == m){ result_array[0] = p_mm; return GSL_SUCCESS; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -