All -normalized associated Legendre functions , (i.e. the real-valued radial part of SSH, for ), using the Holmes & Featherstone (2002) modified forward-column recurrence. The normalization is folded directly into the recurrence coefficients, keeping every intermediate value rather than letting itself grow/shrink combinatorially like the unnormalized ASSOC_LEGENDRE_ALL/Bonnet recurrence does. This is the recurrence SSH_ALL and every batch VSH routine build on.
Warning
Numerical stability: verified safe for
at any . Beyond that, safety depends on :
this recurrence has a distinct failure mode near the classical
turning point of the associated Legendre ODE () -- for within about 30-56 degrees of either
pole, values can blow up by 100+ orders of magnitude once
exceeds a -dependent onset (as low as in
the worst case, degrees). Near the poles
( degrees) or equator (
degrees) it remains accurate at least to (untested
beyond that). This supersedes an earlier, overly optimistic
"stable to " claim that didn't account for the
-dependence. Full methodology, per-degree onset table, and
a (\ell,\theta) heatmap: STABILITY_FINDINGS.md at the repo root
(regenerable via the opt-in VSH_BUILD_STABILITY CMake target).
PLM_INDEX(l,m).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=dp), | intent(out) | :: | PNORM((LMAX+1)*(LMAX+2)/2) | |||
| integer(kind=i4), | intent(in) | :: | LMAX |
Maximum degree, . |
||
| real(kind=dp), | intent(in) | :: | X |
Argument, (typically ). |
SUBROUTINE ASSOC_LEGENDRE_NORM_ALL_DP(PNORM, LMAX, X) IMPLICIT NONE INTEGER(KIND=i4), INTENT(IN) :: LMAX REAL(KIND=dp), INTENT(IN) :: X REAL(KIND=dp), INTENT(OUT) :: PNORM((LMAX+1)*(LMAX+2)/2) INTEGER(KIND=i4) :: L, M REAL(KIND=dp) :: SINTH SINTH = SQRT((1.0_dp - X) * (1.0_dp + X)) PNORM(PLM_INDEX(0, 0)) = 1.0_dp / DSQRT(4.0_dp*pi) DO M = 0, LMAX IF (M .GT. 0) & PNORM(PLM_INDEX(M, M)) = & -DSQRT((2*M+1.d0)/(2*M)) * SINTH * & PNORM(PLM_INDEX(M-1, M-1)) IF (M .LT. LMAX) & PNORM(PLM_INDEX(M+1, M)) = & DSQRT(2*M+3.d0) * X * PNORM(PLM_INDEX(M, M)) DO L = M+2, LMAX PNORM(PLM_INDEX(L, M)) = & DSQRT((4.d0*L**2-1.d0)/(L**2-M**2)) * X * & PNORM(PLM_INDEX(L-1, M)) - & DSQRT((2*L+1.d0)*(L-M-1.d0)*(L+M-1.d0)/ & ((2*L-3.d0)*(L**2-M**2))) * & PNORM(PLM_INDEX(L-2, M)) END DO END DO END SUBROUTINE ASSOC_LEGENDRE_NORM_ALL_DP