efit_to_boozer derives the field-line helicity from a hard-coded poloidal-field rule, so it is wrong for half of all valid GEQDSK inputs, and it names the two handedness output files accordingly.
What happens
src/magfie/field_divB0.f90:245-246 forms the poloidal field as
Brad = -dpsidz/rrr
Bzet = dpsidr/rrr
That is one specific choice of the Sauter–Medvedev sigma_Bp (Computer Physics Communications 184 (2013) 293, Table I). A GEQDSK does not carry a COCOS label, and both signs of sigma_Bp occur in files that are otherwise perfectly valid. When the input uses the other one, every poloidal-field component libneo derives from it is reversed.
That reversal reaches the output through src/efit_to_boozer/field_line_integration_for_Boozer.f90:108:
sigma = sign(1.d0, Bz*Bp)
sigma is the field-line screw direction, and app/efit_to_boozer.f90:96-97,202-207,214-220 uses it to decide which of fromefit_neo_lhs.bc and fromefit_neo_rhs.bc receives the flipped sine coefficients, the flipped iota and the flipped Jpol. With the poloidal field reversed, sigma comes out with the wrong sign and the two named output files are exchanged.
Evidence
An ITER CHEASE equilibrium (nw=nh=513) declaring current = +1.5563913e7 A, bcentr = +5.3 T, fpol > 0, with psi increasing outward. Ampère's law on the file's own psi, integrated on closed psi contours, under the two candidate rules:
psi_n |
Bzet = -dpsidr/rrr |
Bzet = +dpsidr/rrr (libneo) |
| 0.60 |
+1.0208e7 A |
-1.0208e7 A |
| 0.90 |
+1.3470e7 A |
-1.3470e7 A |
| 0.98 |
+1.5113e7 A |
-1.5113e7 A |
Only the first reproduces the declared +1.5564e7 A, closing to 97.1% at psi_n = 0.98 and rising. The magnitude agreement also confirms psi is per radian. So this file needs the rule libneo does not use.
Measuring the emitted charts against a field line traced from the same EQDSK — sigma_theta from the signed poloidal area the file's own rmnc/rmns/zmnc/zmns sweep, sigma_phi from sign(iota_file/iota_traced):
| file |
name claims |
measured sigma_theta |
measured sigma_phi |
measured handedness |
fromefit_neo_lhs.bc |
left-handed (s,theta,phi) |
+1 |
-1 |
right |
fromefit_neo_rhs.bc |
right-handed (s,theta,phi) |
-1 |
-1 |
left |
The traced |iota| matches the printed |iota| to 1.2e-4, so the geometry and the transform itself are fine. Only the handedness bookkeeping is wrong.
Why it matters
Consumers act on the file name and on the sign of iota. NEO-RT, for instance, fixes sign_theta = -1.0 at compile time for a left-handed (s,theta,phi) and applies no check, so it will silently consume the file that is labelled left-handed but is not.
Suggested fix
Do not assume the rule. A GEQDSK carries current and bcentr redundantly with psi, and that redundancy determines the rule uniquely: integrate B_pol . dl on a closed psi contour under both signs and keep the one whose enclosed current matches the declared current in sign. It costs one contour integral at startup, and it converts a silent wrong answer into a derived one. Refusing outright when the two disagree would also be an improvement over the present behaviour.
Reproducer
diagnostics/python/verify_eqdsk_boozer_chart_helicity.py in https://gitlab.tugraz.at/plasma/proj/plasma-sign-conventions (private), with a manufactured large-aspect-ratio oracle in tests/test_eqdsk_boozer_chart_helicity.py. Happy to extract a standalone script if that is useful.
efit_to_boozerderives the field-line helicity from a hard-coded poloidal-field rule, so it is wrong for half of all valid GEQDSK inputs, and it names the two handedness output files accordingly.What happens
src/magfie/field_divB0.f90:245-246forms the poloidal field asThat is one specific choice of the Sauter–Medvedev
sigma_Bp(Computer Physics Communications 184 (2013) 293, Table I). A GEQDSK does not carry a COCOS label, and both signs ofsigma_Bpoccur in files that are otherwise perfectly valid. When the input uses the other one, every poloidal-field component libneo derives from it is reversed.That reversal reaches the output through
src/efit_to_boozer/field_line_integration_for_Boozer.f90:108:sigmais the field-line screw direction, andapp/efit_to_boozer.f90:96-97,202-207,214-220uses it to decide which offromefit_neo_lhs.bcandfromefit_neo_rhs.bcreceives the flipped sine coefficients, the flippediotaand the flippedJpol. With the poloidal field reversed,sigmacomes out with the wrong sign and the two named output files are exchanged.Evidence
An ITER CHEASE equilibrium (
nw=nh=513) declaringcurrent = +1.5563913e7 A,bcentr = +5.3 T,fpol > 0, withpsiincreasing outward. Ampère's law on the file's ownpsi, integrated on closedpsicontours, under the two candidate rules:psi_nBzet = -dpsidr/rrrBzet = +dpsidr/rrr(libneo)+1.0208e7 A-1.0208e7 A+1.3470e7 A-1.3470e7 A+1.5113e7 A-1.5113e7 AOnly the first reproduces the declared
+1.5564e7 A, closing to 97.1% atpsi_n = 0.98and rising. The magnitude agreement also confirmspsiis per radian. So this file needs the rule libneo does not use.Measuring the emitted charts against a field line traced from the same EQDSK —
sigma_thetafrom the signed poloidal area the file's ownrmnc/rmns/zmnc/zmnssweep,sigma_phifromsign(iota_file/iota_traced):sigma_thetasigma_phifromefit_neo_lhs.bc(s,theta,phi)+1-1fromefit_neo_rhs.bc(s,theta,phi)-1-1The traced
|iota|matches the printed|iota|to1.2e-4, so the geometry and the transform itself are fine. Only the handedness bookkeeping is wrong.Why it matters
Consumers act on the file name and on the sign of
iota. NEO-RT, for instance, fixessign_theta = -1.0at compile time for a left-handed(s,theta,phi)and applies no check, so it will silently consume the file that is labelled left-handed but is not.Suggested fix
Do not assume the rule. A GEQDSK carries
currentandbcentrredundantly withpsi, and that redundancy determines the rule uniquely: integrateB_pol . dlon a closedpsicontour under both signs and keep the one whose enclosed current matches the declaredcurrentin sign. It costs one contour integral at startup, and it converts a silent wrong answer into a derived one. Refusing outright when the two disagree would also be an improvement over the present behaviour.Reproducer
diagnostics/python/verify_eqdsk_boozer_chart_helicity.pyin https://gitlab.tugraz.at/plasma/proj/plasma-sign-conventions (private), with a manufactured large-aspect-ratio oracle intests/test_eqdsk_boozer_chart_helicity.py. Happy to extract a standalone script if that is useful.