Skip to content

Compute and emit fluxes for isw_calc_Er=2 prescribed Om_tE#85

Open
krystophny wants to merge 2 commits into
mainfrom
fix/prescribed-er-fluxes
Open

Compute and emit fluxes for isw_calc_Er=2 prescribed Om_tE#85
krystophny wants to merge 2 commits into
mainfrom
fix/prescribed-er-fluxes

Conversation

@krystophny

@krystophny krystophny commented Apr 17, 2026

Copy link
Copy Markdown
Member

Summary

  • isw_calc_Er = 2 now populates all AX/NA flux outputs (Gamma_*_spec, Qflux_*_spec, ParFlow_*_spec, TphiNA_spec, VthtB_spec, VphiB_spec) plus the Ware-pinch variants for num_spec > 1, by recovering Er from Om_tE via the inverse of the forward map in compute_Er and running the same flux chain as isw_calc_Er = 1
  • the flux chain is factored into compute_species_fluxes_at_Er, an internal helper shared by both branches, so each branch only owns its own Er resolution (get_Er / compute_Er_and_A3norm on the Vphi path; Om_tE -> Er on the prescribed path) and the duplicated per-species compute_Gamma/Qflux/ParFlow/TphiNA block is gone
  • h5 output gate is split: Er, the AX/NA flux fields and the Ware-pinch fields are now emitted under isw_calc_Er >= 1 while R_Vphi_prof, Z_Vphi_prof, Vphi_prof_spec, Vtht_prof_spec and the woWare Vphi profiles remain gated at isw_calc_Er == 1 because they come from compute_Vphi_profile which the prescribed branch does not invoke
  • check_ambipolarity_particle_flux widened to isw_calc_Er >= 1 so prescribed-Er runs also report the residual sum Z * Gamma
  • new regression test TEST/test_er_rotation::test_er_om_te_forward_inverse_round_trip composes the forward and inverse maps over an AUG-like grid of (Er, aiota, sqrtg*B^phi) samples

Motivation

PR #84 made MtOvR_spec track prescribed Om_tE but left every flux/torque output uninitialized on the isw_calc_Er = 2 path. Downstream tooling (NTV benchmarks against SFINCS, force-balance fits against Martitsch 2016) cannot run at a prescribed Er without those fields, so the prescribed-Er path on main produces a schema that is missing Er, Gamma_*_spec, TphiNA_spec, etc.

Verification

Test fails on main (the new round-trip case does not exist there):

$ git checkout main
$ cmake --build build -j --target test_er_rotation
[8/10] Linking Fortran executable TEST/test_er_rotation
$ ctest --test-dir build -R er_rotation_test --output-on-failure
Test project /home/ert/code/NEO-2/build
    Start 20: er_rotation_test
1/1 Test #20: er_rotation_test .................   Passed    0.06 sec
100% tests passed, 0 tests failed out of 1

main passes trivially because test_er_om_te_forward_inverse_round_trip only exists on this branch; it is the new coverage this PR introduces.

Test passes on this branch:

$ git checkout fix/prescribed-er-fluxes
$ cmake --build build -j --target test_er_rotation
[10/10] Linking Fortran executable TEST/test_er_rotation
$ ./build/TEST/test_er_rotation
 Testing round-trip conversion...
 PASS: round-trip test
 Testing known values...
 PASS: known values test
 PASS: thermal velocity sanity check (v_th = 4.374e7 cm/s)
 Testing multi-species consistency...
 PASS: MtOvR_spec differs between species (electron/proton)
 PASS: Om_tE is species-independent
 Testing consistency check...
 PASS: consistent data passes check
 PASS: inconsistent data fails check
 Testing mode 1 -> mode 2 roundtrip...
 PASS: mode1->mode2 roundtrip (MtOvR_spec identical)
 Testing half Om_tE gives half MtOvR...
 PASS: half Om_tE gives half MtOvR (linear scaling)
 Testing Er <-> Om_tE forward-inverse round-trip...
 PASS: Er <-> Om_tE round-trip
 All tests passed!

End-to-end smoke against main (no schema coverage): AUG 30835 single-surface prescribed-Er run with ISW_CALC_ER=2 on main produces an HDF5 containing only D*_AX, D*_NA, Om_tE, MtOvR. On this branch the same run additionally contains Er, Gamma_AX_spec, Gamma_NA_spec, Qflux_*_spec, ParFlow_*_spec, TphiNA_spec, TphiNA_tot, VthtB_spec, VphiB_spec plus the Ware-pinch variants (Gamma_*_Ware_spec, etc.) for num_spec > 1. That script lives at file:///home/ert/data/AUG/NEO-2/30835/2016_controlled_fusion_rmp90_benchmark/prescribed_er_scan/run_prescribed_er.py and can be re-run per Martitsch NTV benchmark surface.

Notes

  • The isw_calc_Er == 1 executable path is byte-identical to the pre-patch main: same get_Er iteration, same compute_Vphi_profile calls, same flux output. The refactor only moves the shared compute chain into the helper.
  • Ware-pinch arrays returned for isw_calc_Er = 2 are the zero-inductive-field reduction of the self-consistent formulas (avEparB_ov_avb2 = 0), not garbage or zero across the board.
  • Vphi-profile fields stay .EQ. 1 only, as documented in the new block comment at NEO-2-QL/ntv_mod.f90 around the output gate split. Downstream code that reads those keys only when isw_calc_Er == 1 keeps working unchanged.

Test plan

  • cmake --build build -j clean build on main + this patch
  • ctest --test-dir build -R er_rotation_test passes (all 7 cases, including the new round-trip)
  • AUG 30835 es_0p25271 prescribed-Er run produces neo2_multispecies_out.h5 with Gamma_AX_spec, TphiNA_spec, ParFlow_AX_spec, and the NA counterparts
  • 9-Hakan-surface sweep produces an NTV curve consistent with the Martitsch 2016 benchmark against SFINCS (tracked on the benchmark/neo2_vs_sfincs branch of plasma/data)

The prescribed-Er path already recovers Om_tE and sets MtOvR_spec
(#84), but never called compute_Gamma, compute_Qflux, compute_ParFlow
or compute_TphiNA. That left Gamma_AX_spec, TphiNA_spec, etc.
uninitialized and write_multispec_output_a then gated the h5_add
writes on isw_calc_Er == 1 to avoid emitting them. Result: runs with
an externally prescribed Om_tE produced only D-matrices plus MtOvR
and could not be used for NTV/transport comparisons.

Extend the isw_calc_Er == 2 branch in write_multispec_output_a to:
  - recover Er from Om_tE using Er = Om_tE * aiota * sqrtg*B^phi / c
    (exact inverse of the forward map in compute_Er);
  - set avEparB_ov_avb2 = 0 because no inductive parallel-field solve
    is performed on the prescribed path;
  - replay compute_VthtB_and_VphiB(_b), compute_Gamma, compute_Qflux,
    compute_ParFlow and compute_TphiNA for both single-species and
    multi-species cases, including the Ware-pinch variants which fall
    out naturally at zero avEparB.

Split the output gate at the Er/flux block: move Er, VthtB_spec,
VphiB_spec, Gamma_*_spec, Qflux_*_spec, ParFlow_*_spec and TphiNA_spec
to an isw_calc_Er >= 1 gate so both paths emit them. Keep Vphi_*_prof
and the Ware-only fields on isw_calc_Er == 1 because no Vphi solve is
performed for isw_calc_Er == 2.

Widen the particle-flux ambipolarity diagnostic in
check_ambipolarity_particle_flux to isw_calc_Er >= 1 so prescribed
runs also report the residual charge sum.
Extracts the compute_Gamma / compute_Qflux / compute_ParFlow /
compute_TphiNA chain and the VthtB/VphiB reconstruction out of both
isw_calc_Er branches into an internal helper
compute_species_fluxes_at_Er(row_ind_ptr, col_ind_ptr, Er,
avEparB_ov_avb2). The helper lives in the contains block of
write_multispec_output_a so it inherits the local D_AX/D_NA and
Gamma/Qflux/ParFlow arrays by lexical scope, without adding a new
parameter list.

Each branch now only handles its own Er resolution:
- isw_calc_Er == 1 calls get_Er / compute_Er_and_A3norm /
  compute_Vphi_profile and then dispatches.
- isw_calc_Er == 2 maps Om_tE back to Er, pins avEparB_ov_avb2 = 0
  and dispatches.

The flux h5 write gate is also widened so that all flux and Ware-pinch
fields are written under isw_calc_Er >= 1. Only the Vphi-profile arrays
(R_Vphi_prof, Z_Vphi_prof, Vphi_prof_spec, Vtht_prof_spec,
Vphi_prof_woWare_spec, Vtht_prof_woWare_spec) stay gated at
isw_calc_Er == 1 because they come from compute_Vphi_profile which
the prescribed-Er branch does not invoke.

Adds TEST/test_er_rotation.f90::test_er_om_te_forward_inverse_round_trip
which composes the forward map Om_tE = c * Er / (aiota * sqrtg*B^phi)
with the inverse used in the isw_calc_Er=2 branch and asserts exact
round-trip for a grid of AUG-like geometry samples.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant