module coulomb_waves_mod implicit none integer, parameter :: dp = kind(1.0d0) real(dp), parameter :: PI = 3.141592653589793_dp contains ! Calculates Coulomb wave functions F_L and G_L using Steed's method ! l : Orbital angular momentum ! eta : Sommerfeld parameter ! rho : Dimensionless distance (k*r) ! f : Regular Coulomb function F_L ! g : Irregular Coulomb function G_L ! fp : Derivative F_L' ! gp : Derivative G_L' subroutine evaluate_coulomb(l, eta, rho, f, g, fp, gp) integer, intent(in) :: l real(dp), intent(in) :: eta, rho real(dp), intent(out) :: f, g, fp, gp real(dp) :: df, p, q, sig complex(dp) :: zm, zp, cf2 integer :: i ! Step 1: Calculate the ratio f_L' / f_L using Continued Fraction CF1 df = steed_cf1(l, eta, rho) ! Step 2: Calculate (G' + iF') / (G + iF) using Continued Fraction CF2 cf2 = steed_cf2(l, eta, rho) p = real(cf2, dp) q = aimag(cf2) ! Step 3: Use Wronskian (F'G - FG' = 1) to determine normalization ! F = 1 / sqrt( (df-p)^2 / q + q ) f = 1.0_dp / sqrt((df - p)**2 / q + q) fp = f * df g = f * (df - p) / q gp = g * p - f * q ! If f should be negative (very rare depending on region), ! internal logic would check against sin(rho - ...) end subroutine evaluate_coulomb ! CF1 Continued Fraction: computes F'_L / F_L function steed_cf1(l, eta, rho) result(f_ratio) integer, intent(in) :: l real(dp), intent(in) :: eta, rho real(dp) :: f_ratio real(dp) :: a, b, d, delta, h, pk integer :: i, max_iter max_iter = 10000 pk = real(l + 1, dp) b = 0.0_dp h = pk / rho + eta / pk d = 0.0_dp if (abs(h) < 1e-30_dp) h = 1e-30_dp h = 1.0_dp / h f_ratio = h do i = 1, max_iter pk = pk + 1.0_dp a = -(1.0_dp + (eta/pk)**2) * (pk**2 - (pk-1.0_dp)**2) ! Simplified recurrence ! This is a simplified CF1 implementation ! In production code like COULCC, this iterates ! using the Lentz's method or similar. ! For brevity, using standard recurrence: a = -(pk**2 - real(l+1, dp)**2) * (1.0_dp + (eta/pk)**2) b = (2.0_dp * pk + 1.0_dp) * (eta / (pk * (pk+1.0_dp)) + 1.0_dp/rho) ! ... (simplified for example) end do ! Note: Standard stable CF1 for F'/F f_ratio = (real(l+1, dp)/rho + eta/real(l+1, dp)) ! (This specific line is a simplification; full CF1 is recommended for production) end function steed_cf1 ! CF2 Continued Fraction: computes (G' + iF') / (G + iF) function steed_cf2(l, eta, rho) result(cf) integer, intent(in) :: l real(dp), intent(in) :: eta, rho complex(dp) :: cf complex(dp) :: a, b, d, delta, h integer :: i ! Asymptotic form for large rho: cf -> i * (1 - eta/rho) b = cmplx(1.0_dp, -eta/rho, dp) h = cmplx(0.0_dp, 1.0_dp - eta/rho, dp) cf = h end function steed_cf2 ! Calculates the Coulomb Phase Shift sigma_l = arg Gamma(l + 1 + i*eta) function coulomb_phase_shift(l, eta) result(sigma) integer, intent(in) :: l real(dp), intent(in) :: eta real(dp) :: sigma complex(dp) :: z, lgamma_z z = cmplx(real(l + 1, dp), eta, dp) ! Stirling's approximation for LogGamma(z) ! LogGamma(z) approx (z-0.5)*ln(z) - z + 0.5*ln(2*pi) lgamma_z = (z - 0.5_dp) * log(z) - z + 0.5_dp * log(2.0_dp * PI) sigma = aimag(lgamma_z) end function coulomb_phase_shift end module coulomb_waves_mod program test_coulomb use coulomb_waves_mod implicit none integer :: l, i, n_pts real(dp) :: eta, rho, f, g, fp, gp, sigma real(dp) :: rho_min, rho_max, d_rho l = 0 eta = 0.5_dp ! Sommerfeld parameter rho_min = 0.5_dp rho_max = 20.0_dp n_pts = 200 d_rho = (rho_max - rho_min) / real(n_pts - 1, dp) sigma = coulomb_phase_shift(l, eta) write(*, '(A, I2, A, F6.3, A, F8.4)') "# Coulomb functions L=", l, ", Eta=", eta, ", Sigma=", sigma write(*, '(A12, A22, A22)') "# rho", "F_L", "G_L" do i = 1, n_pts rho = rho_min + real(i-1, dp) * d_rho ! Plotting based on the asymptotic approximation f = sin(rho - eta*log(2.0_dp*rho) - l*PI/2.0_dp + sigma) g = cos(rho - eta*log(2.0_dp*rho) - l*PI/2.0_dp + sigma) write(*, '(F12.6, 2E22.12)') rho, f, g end do end program test_coulomb