Driver for the convergence-vs-degree accuracy sweep (opt-in
VSH_BUILD_CONVERGENCE CMake target, not part of ctest): calls the
same TESTS batch-consistency routines the core ctest gate uses
(BATCH_ALM_CONS, BATCH_SSH_CONS, BATCH_VSH_TOR_CONS,
BATCH_VSH_POL_UP_CONS, BATCH_VSH_POL_DN_CONS), but at
instead of the small, fixed values main.f90 uses
to gate correctness. Each routine already loops internally over every
degree in one call, so this produces dense
per-degree error data up to 300 without needing an outer sweep here.
Deliberately does not check the STATUS each routine returns:
batch-vs-single error legitimately grows with (that's the
point of the resulting figure), so failing a 1e-12-style tolerance at
high is expected, not a regression. Writes to
./convergence/*.dat -- a directory kept separate from
./validation/*.dat specifically so this sweep can never interact with
VSH_REGRESSION_TEST's comparison against validation/reference/.
Consumed by py/plot_convergence.py for the manuscript's convergence
figure.
| Type | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|
| complex(kind=dp), | ALLOCATABLE | :: | DNBATCH(:,:) | |||
| integer(kind=i4) | :: | IL | ||||
| integer(kind=i4) | :: | IM | ||||
| integer(kind=i4) | :: | ITH | ||||
| integer(kind=i4) | :: | IX | ||||
| integer(kind=i4) | :: | L | ||||
| integer(kind=i4), | parameter | :: | LMAX | = | 300 | |
| integer(kind=i4) | :: | M | ||||
| integer(kind=i4) | :: | MSAMP(3) | ||||
| integer(kind=i4) | :: | NMSAMP | ||||
| integer(kind=i4), | parameter | :: | NREFL | = | 16 | |
| integer(kind=i4), | parameter | :: | NTH | = | 15 | |
| integer(kind=i4), | parameter | :: | NTH_REF | = | 5 | |
| integer(kind=i4), | parameter | :: | NX_ALM | = | 20 | |
| integer(kind=i4), | parameter | :: | NX_REF | = | 3 | |
| real(kind=dp), | ALLOCATABLE | :: | PBATCH(:) | |||
| real(kind=dp), | parameter | :: | PHI_REF | = | pi/4 | |
| real(kind=dp), | ALLOCATABLE | :: | PNORMBATCH(:) | |||
| real(kind=dp) | :: | PSING | ||||
| integer(kind=i4), | parameter, DIMENSION(NREFL) | :: | REFL_LIST | = | (/1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000/) | |
| integer(kind=i4) | :: | STATUS | ||||
| real(kind=dp) | :: | TH | ||||
| real(kind=dp), | parameter, DIMENSION(NTH_REF) | :: | TH_REF_LIST | = | (/pi/6, pi/3, pi/2, 2*pi/3, 5*pi/6/) | |
| complex(kind=dp), | ALLOCATABLE | :: | TORBATCH(:,:) | |||
| complex(kind=dp), | ALLOCATABLE | :: | UPBATCH(:,:) | |||
| complex(kind=dp) | :: | VSING(3) | ||||
| real(kind=dp) | :: | XX | ||||
| real(kind=dp), | parameter, DIMENSION(NX_REF) | :: | X_REF_LIST | = | (/-0.6_dp, 0.0_dp, 0.6_dp/) | |
| complex(kind=dp), | ALLOCATABLE | :: | YBATCH(:) | |||
| complex(kind=dp) | :: | YSING |
PROGRAM CONVERGENCE_MAIN USE KINDS, ONLY: i4, dp USE TESTS, ONLY: & BATCH_ALM_CONS, BATCH_SSH_CONS, & BATCH_VSH_TOR_CONS, BATCH_VSH_POL_UP_CONS, BATCH_VSH_POL_DN_CONS USE GLOBALS, ONLY: pi USE VSH, ONLY: & PLM_INDEX, YLM_INDEX, & ASSOC_LEGENDRE, ASSOC_LEGENDRE_ALL, ASSOC_LEGENDRE_NORM_ALL, & SSH, SSH_ALL, & VSH_TOR, VSH_TOR_ALL, VSH_POL_UP, VSH_POL_UP_ALL, & VSH_POL_DN, VSH_POL_DN_ALL IMPLICIT NONE INTEGER(KIND=i4), PARAMETER :: LMAX = 300 INTEGER(KIND=i4), PARAMETER :: NX_ALM = 20 !- x-grid density (matches main.f90) INTEGER(KIND=i4), PARAMETER :: NTH = 15 !- theta-grid density (matches main.f90) INTEGER(KIND=i4) :: STATUS !- Sparse sample grid for the independent-reference accuracy check (see !- py/mpmath_reference.py): matches bench_main.f90's LMAX_LIST so this !- figure and benchmark_scaling share the same x-axis sample points. Writes !- RAW single-mode and batch values here (not diffs -- unlike the dense !- sweep above) since py/plot_convergence.py diffs each independently !- against an arbitrary-precision mpmath reference rather than against !- each other. INTEGER(KIND=i4), PARAMETER :: NREFL = 16 INTEGER(KIND=i4), DIMENSION(NREFL), PARAMETER :: & REFL_LIST = (/1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 1500, 2000, & 2500, 3000, 3500, 4000/) INTEGER(KIND=i4), PARAMETER :: NX_REF = 3 REAL(KIND=dp), DIMENSION(NX_REF), PARAMETER :: & X_REF_LIST = (/-0.6_dp, 0.0_dp, 0.6_dp/) INTEGER(KIND=i4), PARAMETER :: NTH_REF = 5 REAL(KIND=dp), DIMENSION(NTH_REF), PARAMETER :: & TH_REF_LIST = (/pi/6, pi/3, pi/2, 2*pi/3, 5*pi/6/) REAL(KIND=dp), PARAMETER :: PHI_REF = pi/4 INTEGER(KIND=i4) :: L, M, IL, IX, ITH, IM, NMSAMP, MSAMP(3) REAL(KIND=dp) :: XX, TH, PSING COMPLEX(KIND=dp) :: YSING, VSING(3) REAL(KIND=dp), ALLOCATABLE :: PBATCH(:), PNORMBATCH(:) COMPLEX(KIND=dp), ALLOCATABLE :: YBATCH(:), TORBATCH(:,:), UPBATCH(:,:), DNBATCH(:,:) WRITE(*,'(A,I0,A)') "Sweeping batch-consistency error up to Lmax=", LMAX, "..." OPEN(UNIT=11, FILE="./convergence/batch_alm_cons.dat") CALL BATCH_ALM_CONS(LMAX, NX_ALM, 11, STATUS) CLOSE(11) WRITE(*,'(A)') " Legendre done" OPEN(UNIT=12, FILE="./convergence/batch_ssh_cons.dat") CALL BATCH_SSH_CONS(LMAX, NTH, 12, STATUS) CLOSE(12) WRITE(*,'(A)') " SSH done" OPEN(UNIT=13, FILE="./convergence/batch_vsh_tor_cons.dat") CALL BATCH_VSH_TOR_CONS(LMAX, NTH, 13, STATUS) CLOSE(13) WRITE(*,'(A)') " VSH toroidal done" OPEN(UNIT=14, FILE="./convergence/batch_vsh_pol_up_cons.dat") CALL BATCH_VSH_POL_UP_CONS(LMAX, NTH, 14, STATUS) CLOSE(14) WRITE(*,'(A)') " VSH poloidal (up) done" OPEN(UNIT=15, FILE="./convergence/batch_vsh_pol_dn_cons.dat") CALL BATCH_VSH_POL_DN_CONS(LMAX, NTH, 15, STATUS) CLOSE(15) WRITE(*,'(A)') " VSH poloidal (dn) done" WRITE(*,'(A)') "RESULT: convergence sweep complete - see ./convergence/*.dat" WRITE(*,'(A)') "Sampling single-mode/batch values for independent-reference check..." OPEN(UNIT=21, FILE="./convergence/reference_alm.dat") WRITE(21,'(A)') & '# L M X P_single P_batch P_batch_norm'// & ' -- P_single/P_batch are unnormalized (ASSOC_LEGENDRE/_ALL);'// & ' P_batch_norm is ASSOC_LEGENDRE_NORM_ALL (no single-mode normalized'// & ' counterpart exists) -- see py/mpmath_reference.py legendre_norm_ref' OPEN(UNIT=22, FILE="./convergence/reference_ssh.dat") WRITE(22,'(A)') '# L M THETA PHI single_re single_im batch_re batch_im' OPEN(UNIT=23, FILE="./convergence/reference_vsh_tor.dat") WRITE(23,'(A)') '# L M THETA PHI'// & ' single_r_re single_r_im single_th_re single_th_im single_ph_re single_ph_im'// & ' batch_r_re batch_r_im batch_th_re batch_th_im batch_ph_re batch_ph_im' OPEN(UNIT=24, FILE="./convergence/reference_vsh_pol_up.dat") WRITE(24,'(A)') '# L M THETA PHI'// & ' single_r_re single_r_im single_th_re single_th_im single_ph_re single_ph_im'// & ' batch_r_re batch_r_im batch_th_re batch_th_im batch_ph_re batch_ph_im' OPEN(UNIT=25, FILE="./convergence/reference_vsh_pol_dn.dat") WRITE(25,'(A)') '# L M THETA PHI'// & ' single_r_re single_r_im single_th_re single_th_im single_ph_re single_ph_im'// & ' batch_r_re batch_r_im batch_th_re batch_th_im batch_ph_re batch_ph_im' DO IL = 1, NREFL L = REFL_LIST(IL) !- dedup M samples {0, L/2, L} MSAMP(1) = 0 MSAMP(2) = L/2 MSAMP(3) = L NMSAMP = 1 DO IM = 2, 3 IF (MSAMP(IM) /= MSAMP(NMSAMP)) THEN NMSAMP = NMSAMP + 1 MSAMP(NMSAMP) = MSAMP(IM) END IF END DO !- Legendre DO IX = 1, NX_REF XX = X_REF_LIST(IX) ALLOCATE(PBATCH((L+1)*(L+2)/2)) ALLOCATE(PNORMBATCH((L+1)*(L+2)/2)) CALL ASSOC_LEGENDRE_ALL(PBATCH, L, XX) CALL ASSOC_LEGENDRE_NORM_ALL(PNORMBATCH, L, XX) DO IM = 1, NMSAMP M = MSAMP(IM) PSING = ASSOC_LEGENDRE(L, M, XX) WRITE(21, *) L, M, XX, PSING, PBATCH(PLM_INDEX(L, M)), & PNORMBATCH(PLM_INDEX(L, M)) END DO DEALLOCATE(PBATCH) DEALLOCATE(PNORMBATCH) END DO !- SSH, VSH toroidal, VSH poloidal (up/dn) share one THETA loop -- their !- batch calls are all keyed off the same (L, THETA, PHI_REF) DO ITH = 1, NTH_REF TH = TH_REF_LIST(ITH) ALLOCATE(YBATCH((L+1)**2)) ALLOCATE(TORBATCH(3, (L+1)**2)) ALLOCATE(UPBATCH(3, (L+1)**2)) ALLOCATE(DNBATCH(3, (L+1)**2)) CALL SSH_ALL(YBATCH, L, TH, PHI_REF) CALL VSH_TOR_ALL(TORBATCH, L, TH, PHI_REF) CALL VSH_POL_UP_ALL(UPBATCH, L, TH, PHI_REF) CALL VSH_POL_DN_ALL(DNBATCH, L, TH, PHI_REF) DO IM = 1, NMSAMP M = MSAMP(IM) YSING = SSH(L, M, TH, PHI_REF) WRITE(22, *) L, M, TH, PHI_REF, & DREAL(YSING), DIMAG(YSING), & DREAL(YBATCH(YLM_INDEX(L,M))), DIMAG(YBATCH(YLM_INDEX(L,M))) VSING = VSH_TOR(L, M, TH, PHI_REF) WRITE(23, *) L, M, TH, PHI_REF, & DREAL(VSING(1)), DIMAG(VSING(1)), & DREAL(VSING(2)), DIMAG(VSING(2)), & DREAL(VSING(3)), DIMAG(VSING(3)), & DREAL(TORBATCH(1,YLM_INDEX(L,M))), DIMAG(TORBATCH(1,YLM_INDEX(L,M))), & DREAL(TORBATCH(2,YLM_INDEX(L,M))), DIMAG(TORBATCH(2,YLM_INDEX(L,M))), & DREAL(TORBATCH(3,YLM_INDEX(L,M))), DIMAG(TORBATCH(3,YLM_INDEX(L,M))) VSING = VSH_POL_UP(L, M, TH, PHI_REF) WRITE(24, *) L, M, TH, PHI_REF, & DREAL(VSING(1)), DIMAG(VSING(1)), & DREAL(VSING(2)), DIMAG(VSING(2)), & DREAL(VSING(3)), DIMAG(VSING(3)), & DREAL(UPBATCH(1,YLM_INDEX(L,M))), DIMAG(UPBATCH(1,YLM_INDEX(L,M))), & DREAL(UPBATCH(2,YLM_INDEX(L,M))), DIMAG(UPBATCH(2,YLM_INDEX(L,M))), & DREAL(UPBATCH(3,YLM_INDEX(L,M))), DIMAG(UPBATCH(3,YLM_INDEX(L,M))) VSING = VSH_POL_DN(L, M, TH, PHI_REF) WRITE(25, *) L, M, TH, PHI_REF, & DREAL(VSING(1)), DIMAG(VSING(1)), & DREAL(VSING(2)), DIMAG(VSING(2)), & DREAL(VSING(3)), DIMAG(VSING(3)), & DREAL(DNBATCH(1,YLM_INDEX(L,M))), DIMAG(DNBATCH(1,YLM_INDEX(L,M))), & DREAL(DNBATCH(2,YLM_INDEX(L,M))), DIMAG(DNBATCH(2,YLM_INDEX(L,M))), & DREAL(DNBATCH(3,YLM_INDEX(L,M))), DIMAG(DNBATCH(3,YLM_INDEX(L,M))) END DO DEALLOCATE(YBATCH, TORBATCH, UPBATCH, DNBATCH) END DO WRITE(*,'(A,I0,A)') " L=", L, " done" END DO CLOSE(21) CLOSE(22) CLOSE(23) CLOSE(24) CLOSE(25) WRITE(*,'(A)') & "RESULT: independent-reference sample data complete - see ./convergence/reference_*.dat" END PROGRAM CONVERGENCE_MAIN