Skip to content

Commit a28d02e

Browse files
authored
Keep the default near-axis regularization radius on rho-native charts (#616)
## Cause Since b4778e4 the defaults of `orbit_timestep_axis` (`axis_floor` 1e-8, `near_axis_limit` 0.01) are read in the native radial unit of the chart. For flux-native charts this is a regularization disc of Cartesian radius 1e-4 and a near-axis zone of radius 0.1, but on rho-native REFCOORDS charts the same numbers shrank to 1e-8 and 0.01. An orbit crossing the axis at Cartesian radius of about 1e-6 then meets the raw polar-to-Cartesian pullback outside the regularized disc, and the RK45 integration exhausts its step budget (`ierr = 2`, libneo odeint max steps). ## Fix The defaults are now defined in normalized toroidal flux s (1e-8, 0.01) and converted to the native coordinate (rho = sqrt(s)) when `axis_radial_coordinate` reports a rho chart, so both chart types keep the same Cartesian disc. Explicit `axis_floor` / `near_axis_limit` arguments stay in native units. The radial-coordinate query moves before the argument validation, nothing else changes. ## Test `check_default_axis_disc` in `test_magfie_refcoords_splines` (NCSX rho chart): six orbits starting at rho 1e-8 and 1e-6 with three pitches, one step through the axis with default arguments, compared with the same orbit in 40 short steps with explicit native limits (1e-4, 0.1). Without the fix all six default-argument steps return `ierr = 2`; with it they agree to about 1e-13. Verified on macOS (Release, gfortran 16): `test_magfie_refcoords_splines`, `test_axis_regular_rhs` and `ctest -R 'refcoords|axis|chartmap|orbit'` pass except four Python tests that need numpy/netCDF4 missing on that host. The golden record has not been run. Chris&AI
1 parent b09d279 commit a28d02e

2 files changed

Lines changed: 60 additions & 2 deletions

File tree

‎src/sub_alpha_lifetime_can.f90‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,8 +901,18 @@ subroutine orbit_timestep_axis(z, dtau, dtaumin, relerr, ierr, step_limit, &
901901
end if
902902
!
903903
ierr = 0
904+
call axis_radial_coordinate(radial_is_rho, ierr)
905+
if (ierr /= 0) return
906+
! Defaults are given in normalized toroidal flux s and converted to
907+
! the native chart coordinate (rho = sqrt(s)), so that the
908+
! regularization disc has the same Cartesian radius for flux- and
909+
! rho-native charts. Explicit arguments stay in native units.
904910
axis_floor_local = 1.d-8
905911
snear_axis = 0.01d0
912+
if (radial_is_rho) then
913+
axis_floor_local = sqrt(axis_floor_local)
914+
snear_axis = sqrt(snear_axis)
915+
end if
906916
if (present(axis_floor)) axis_floor_local = axis_floor
907917
if (present(near_axis_limit)) snear_axis = near_axis_limit
908918
if (.not. is_finite(axis_floor_local) .or. &
@@ -912,8 +922,6 @@ subroutine orbit_timestep_axis(z, dtau, dtaumin, relerr, ierr, step_limit, &
912922
ierr = 2
913923
return
914924
end if
915-
call axis_radial_coordinate(radial_is_rho, ierr)
916-
if (ierr /= 0) return
917925
near_radius_squared = axis_native_radius_squared(snear_axis, radial_is_rho)
918926
z_initial = z
919927
y(1) = z(1)

‎test/tests/magfie/test_magfie_refcoords_splines.f90‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ program test_magfie_refcoords_splines
7676
error stop 'inactive reference field changed the RK axis chart'
7777
call init_magfie(REFCOORDS)
7878
call check_active_rho_rhs(n_failed)
79+
call check_default_axis_disc(n_failed)
7980

8081
phi_period = twopi/real(nper, dp)
8182

@@ -231,6 +232,55 @@ subroutine check_active_rho_rhs(n_failed)
231232
end if
232233
end subroutine check_active_rho_rhs
233234

235+
subroutine check_default_axis_disc(n_failed)
236+
!> The default regularization disc and near-axis zone are defined in flux
237+
!> units (1e-8, 0.01) and must keep their Cartesian size (1e-4, 0.1) on a
238+
!> rho-native chart. A step that starts within 1e-6 of the axis crosses it
239+
!> at Cartesian radius ~1e-6 and used to exhaust the RK step budget with
240+
!> the unconverted defaults (ierr = 2). The reference integrates the same
241+
!> orbit in many short steps with the equivalent explicit native limits.
242+
integer, intent(inout) :: n_failed
243+
type(tracer_t) :: orbit
244+
real(dp), parameter :: rho0(2) = [1.0e-8_dp, 1.0e-6_dp]
245+
real(dp), parameter :: pitch(3) = [0.3_dp, -0.6_dp, 0.05_dp]
246+
real(dp), parameter :: tol = 1.0e-9_dp
247+
real(dp) :: z(5), zref(5), dist, dtau
248+
integer :: irho, ipitch, istep, status, status_ref
249+
integer, parameter :: n_sub = 40
250+
251+
call init_params(orbit, 2, 4, 3.5e6_dp, 256, 1, 1.0e-10_dp)
252+
dtau = 0.2_dp*orbit%dtau
253+
do irho = 1, size(rho0)
254+
do ipitch = 1, size(pitch)
255+
z = [rho0(irho), 0.4_dp, 0.1_dp, 1.0_dp, pitch(ipitch)]
256+
zref = z
257+
call orbit_timestep_axis(z, dtau, dtau, 1.0e-10_dp, status)
258+
status_ref = 0
259+
do istep = 1, n_sub
260+
if (status_ref == 0) &
261+
call orbit_timestep_axis(zref, dtau/n_sub, dtau/n_sub, &
262+
1.0e-10_dp, status_ref, &
263+
axis_floor=1.0e-4_dp, &
264+
near_axis_limit=0.1_dp)
265+
end do
266+
if (status /= 0 .or. status_ref /= 0 .or. &
267+
.not. all(is_finite([z, zref]))) then
268+
print *, 'FAIL: default axis disc orbit failed', irho, ipitch, &
269+
status, status_ref
270+
n_failed = n_failed + 1
271+
cycle
272+
end if
273+
dist = hypot(z(1)*cos(z(2)) - zref(1)*cos(zref(2)), &
274+
z(1)*sin(z(2)) - zref(1)*sin(zref(2)))
275+
if (dist > tol .or. maxval(abs(z(3:5) - zref(3:5))) > tol) then
276+
print *, 'FAIL: default axis disc differs from explicit disc', &
277+
irho, ipitch, dist
278+
n_failed = n_failed + 1
279+
end if
280+
end do
281+
end do
282+
end subroutine check_default_axis_disc
283+
234284
subroutine check_point(i, phi_period, f, n_failed)
235285
integer, intent(in) :: i
236286
real(dp), intent(in) :: phi_period

0 commit comments

Comments
 (0)