Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions notebooks/mhd_equilibrium_analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@
"q95 8.671 - [linear interpolation]\n",
"q_edge 11.73 - [profile endpoint]\n",
"beta_t 0.001274 - [volume and pressure integration]\n",
"beta_n 0.0702 % m T / MA [derived]\n",
"beta_n 0.0702 - [derived]\n",
"li_virial 0.6876 - [Lao virial closure]\n",
"shafranov_shift 0.0284 m [difference]\n",
"magnetic_shear 129-point profile - [finite differences]\n"
Expand All @@ -221,7 +221,7 @@
" (\"elongation\", 1.0, \"-\"), (\"triangularity_upper\", 1.0, \"-\"),\n",
" (\"triangularity_lower\", 1.0, \"-\"), (\"volume\", 1.0, \"m^3\"),\n",
" (\"cross_section_area\", 1.0, \"m^2\"), (\"q0\", 1.0, \"-\"), (\"q95\", 1.0, \"-\"),\n",
" (\"q_edge\", 1.0, \"-\"), (\"beta_t\", 1.0, \"-\"), (\"beta_n\", 1.0, \"% m T / MA\"),\n",
" (\"q_edge\", 1.0, \"-\"), (\"beta_t\", 1.0, \"-\"), (\"beta_n\", 1.0, \"-\"),\n",
" (\"li_virial\", 1.0, \"-\"), (\"shafranov_shift\", 1.0, \"m\"),\n",
" (\"magnetic_shear\", 1.0, \"-\"),\n",
")\n",
Expand Down
11 changes: 11 additions & 0 deletions test/test_parametric_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ def test_global_descriptors_and_radial_coordinates_have_definitions():
assert descriptors["volume"].value == pytest.approx(2 * np.pi**2 * 1.0 * 0.35 * 0.5, rel=2e-3)
assert descriptors["thermal_energy"].available
assert descriptors["beta_t"].available
assert descriptors["beta_n"].available
assert descriptors["beta_n"].unit == "1"
assert descriptors["beta_n"].quality.get("scaling_convention") == "% m T / MA"
assert len(descriptors.rational_surfaces[1.5]) == 1
radial = derive_radial_coordinates(eq)
np.testing.assert_allclose(radial["psi_n"].value[[0, -1]], [0, 1])
Expand Down Expand Up @@ -576,3 +579,11 @@ def test_explicit_flux_tolerance_overrides_the_derived_window():
strict = derive_boundary_representation(eq, flux_tolerance=0.0)
assert strict.provenance.tolerances["xpoint_flux_psi_n"] == 0.0
assert not strict.topology.is_diverted


def test_unavailable_beta_n_has_dimensionless_unit():
eq = _analytic_equilibrium()
eq_no_p = EquilibriumData(**{**eq.__dict__, "pressure": None})
descriptors = derive_global_descriptors(eq_no_p)
assert not descriptors["beta_n"].available
assert descriptors["beta_n"].unit == "1"
20 changes: 14 additions & 6 deletions vaft/process/_equilibrium_parametric.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,12 @@ def derive_global_descriptors(
centimetres apart. The poloidal field is computed through the equilibrium's
own declared COCOS and per-radian flag, so a wrongly declared convention
propagates into every field-derived quantity here. The magnetic shear is
taken against the poloidal radius.
taken against the poloidal radius. The normalized beta follows the Troyon
convention ``beta_N = 100 * beta_t * a * |B_t0| / |I_p|`` (with ``a`` in
metres, ``B_t0`` in tesla, and ``I_p`` in megamperes); its published unit
is ``"1"`` (dimensionless) conforming to IMAS ``global_quantities.beta_normal``,
with the conventional ``% * m * T / MA`` scaling factor folded into the value
and recorded in the derivation quality metadata.

Applicability
-------------
Expand All @@ -790,9 +795,8 @@ def derive_global_descriptors(
Every quantity degrades independently: a missing pressure profile costs the
energies and the betas but not the geometry. A failure inside the pressure
integration is captured as a warning on the returned report rather than
raised. The normalized beta carries a non-SI unit string where the rest of
the module is SI, tracked in #607. The virial internal inductance is
ill-conditioned as its coefficient approaches one.
raised. The virial internal inductance is ill-conditioned as its coefficient
approaches one.

Provenance
----------
Expand Down Expand Up @@ -916,11 +920,15 @@ def derive_global_descriptors(
values["beta_t"] = _derived(eq, beta_t, "1", "2*mu0*<p>/Bt0^2", "volume and pressure integration", ("pressure", "bt0"))
if geometry and eq.ip not in (None, 0):
beta_n = 100 * beta_t * values["minor_radius"].value * abs(eq.bt0) / (abs(eq.ip) / 1e6)
values["beta_n"] = _derived(eq, beta_n, "% m T / MA", "100*beta_t*a*abs(Bt0)/abs(Ip_MA)", "derived", ("beta_t", "minor_radius", "bt0", "ip"))
values["beta_n"] = _derived(
eq, beta_n, "1", "100*beta_t*a*abs(Bt0)/abs(Ip_MA)", "derived",
("beta_t", "minor_radius", "bt0", "ip"),
{"scaling_convention": "% m T / MA"},
)
if average_pressure is not None and bpa not in (None, 0):
values["beta_p_boundary_average"] = _derived(eq, 2 * MU0 * average_pressure / bpa**2, "1", "2*mu0*<p>/<Bp>_boundary^2", "boundary-field average", ("pressure", "psi", "lcfs"))
for name, definition in (("beta_t", "2*mu0*<p>/Bt0^2"), ("beta_n", "100*beta_t*a*abs(Bt0)/abs(Ip_MA)"), ("beta_p_boundary_average", "2*mu0*<p>/<Bp>_boundary^2")):
values.setdefault(name, _unavailable(eq, "1" if name != "beta_n" else "% m T / MA", definition, "required pressure, geometry, current, or magnetic field is unavailable"))
values.setdefault(name, _unavailable(eq, "1", definition, "required pressure, geometry, current, or magnetic field is unavailable"))
for name in ("s1", "s2", "s3", "alpha"):
values[name] = _derived(eq, virial[name], "1", f"Shafranov boundary integral {name}", "shafranov_integrals", ("psi", "lcfs")) if name in virial else _unavailable(eq, "1", f"Shafranov {name}", "valid grid, LCFS, and boundary poloidal field are required")
values["li_virial"] = _derived(eq, virial["li"], "1", "[S1/2+S2/2*(1-RT/R0)-S3]/(alpha-1)", "Lao virial closure", ("psi", "f", "pressure", "lcfs")) if "li" in virial else _unavailable(eq, "1", "Lao virial internal inductance", "F, pressure, field grid, and a well-conditioned alpha are required")
Expand Down
Loading