Skip to content

Commit 412b9be

Browse files
hamelphiTorax team
authored andcommitted
This is a no-op refactor: only He3 gets non-zero values from the model.
The uniform species output simplifies downstream code and prepares for future prescribed fast ion overrides. Key changes: - Add _FAST_ION_SPECIES constant listing all supported species. - Extract _build_fast_ions helper for constructing FastIon tuples. - Update icrh_model_func and zero_fast_ions to use _build_fast_ions. - Update test assertions for 5 species. PiperOrigin-RevId: 892467105
1 parent 9885e92 commit 412b9be

2 files changed

Lines changed: 89 additions & 50 deletions

File tree

torax/_src/sources/ion_cyclotron_source.py

Lines changed: 78 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
_TRITIUM_SECOND_HARMONIC_ID = '2T'
6262
_ELECTRON_ID = 'e'
6363

64+
# All fast ion species supported by the ICRH source.
65+
_FAST_ION_SPECIES: Final[tuple[str, ...]] = ('H', 'D', 'T', 'He3', 'He4')
66+
6467

6568
def _from_json(json_file) -> dict[str, Any]:
6669
"""Load the model config and weights from a JSON file."""
@@ -466,12 +469,8 @@ def icrh_model_func(
466469
power_deposition_2T /= total_power_deposition
467470

468471
# Computing fast ion density and temperatures for He3.
469-
# Only He3 is supported for now.
470-
# TODO(b/483988192): Add support for other species.
471-
472-
species = 'He3'
473-
atomic_mass = 3.016
474-
charge_number = 2
472+
he3_atomic_mass = 3.016
473+
he3_charge_number = 2
475474

476475
n_tail, T_tail = fast_ion_utils.bimaxwellian_split(
477476
power_deposition=power_deposition_he3,
@@ -481,37 +480,43 @@ def icrh_model_func(
481480
n_i=core_profiles.n_i.value,
482481
minority_concentration=minority_concentration_profile,
483482
P_total_W=source_params.P_total,
484-
charge_number=charge_number,
485-
mass_number=atomic_mass,
483+
charge_number=he3_charge_number,
484+
mass_number=he3_atomic_mass,
486485
bulk_ion_mass=core_profiles.A_i,
487486
Z_i=core_profiles.Z_i,
488487
n_impurity=core_profiles.n_impurity.value,
489488
Z_impurity=core_profiles.Z_impurity,
490489
A_impurity=core_profiles.A_impurity,
491490
)
492491

493-
fast_ions = (
494-
fast_ion_lib.FastIon(
495-
species=species,
496-
source=source_name,
497-
n=cell_variable.CellVariable(
498-
value=n_tail,
499-
face_centers=geo.rho_face_norm,
500-
right_face_grad_constraint=None,
501-
right_face_constraint=jnp.zeros(()),
502-
),
503-
T=cell_variable.CellVariable(
504-
value=T_tail,
505-
face_centers=geo.rho_face_norm,
506-
right_face_grad_constraint=None,
507-
right_face_constraint=core_profiles.T_i.right_face_constraint,
508-
),
492+
# Build fast ion output for all supported species.
493+
# Only He3 has non-zero values from the model; others are zeroed.
494+
he3_fast_ion = fast_ion_lib.FastIon(
495+
species='He3',
496+
source=source_name,
497+
n=cell_variable.CellVariable(
498+
value=n_tail,
499+
face_centers=geo.rho_face_norm,
500+
right_face_grad_constraint=None,
501+
right_face_constraint=jnp.zeros(()),
502+
),
503+
T=cell_variable.CellVariable(
504+
value=T_tail,
505+
face_centers=geo.rho_face_norm,
506+
right_face_grad_constraint=None,
507+
right_face_constraint=core_profiles.T_i.right_face_constraint,
509508
),
510509
)
510+
fast_ions = _build_fast_ions(
511+
source_name=source_name,
512+
geo=geo,
513+
fast_ions=[he3_fast_ion],
514+
)
515+
511516
frac_ion_heating = collisions.fast_ion_fractional_heating_formula(
512517
T_tail,
513518
core_profiles.T_e.value,
514-
atomic_mass,
519+
he3_atomic_mass,
515520
)
516521
absorbed_power = source_params.P_total * source_params.absorption_fraction
517522
source_ion = power_deposition_he3 * frac_ion_heating * absorbed_power
@@ -526,6 +531,53 @@ def icrh_model_func(
526531
return (source_ion, source_el, fast_ions)
527532

528533

534+
def _build_fast_ions(
535+
source_name: str,
536+
geo: geometry.Geometry,
537+
fast_ions: Sequence[fast_ion_lib.FastIon] = (),
538+
) -> tuple[fast_ion_lib.FastIon, ...]:
539+
"""Builds a complete FastIon tuple for all supported species.
540+
541+
Takes a list of computed FastIon objects (for a subset of species) and
542+
produces a full tuple covering all species in _FAST_ION_SPECIES. Species
543+
not present in the input list are filled with zero density and temperature.
544+
545+
Args:
546+
source_name: The name of the source.
547+
geo: Geometry.
548+
fast_ions: Computed FastIon objects for a subset of species.
549+
550+
Returns:
551+
Tuple of FastIon objects, one per species in _FAST_ION_SPECIES, in order.
552+
"""
553+
computed = {fi.species: fi for fi in fast_ions}
554+
zeros = jnp.zeros_like(geo.rho)
555+
result = []
556+
for species in _FAST_ION_SPECIES:
557+
if species in computed:
558+
result.append(computed[species])
559+
else:
560+
result.append(
561+
fast_ion_lib.FastIon(
562+
species=species,
563+
source=source_name,
564+
n=cell_variable.CellVariable(
565+
value=zeros,
566+
face_centers=geo.rho_face_norm,
567+
right_face_grad_constraint=None,
568+
right_face_constraint=jnp.zeros(()),
569+
),
570+
T=cell_variable.CellVariable(
571+
value=zeros,
572+
face_centers=geo.rho_face_norm,
573+
right_face_grad_constraint=None,
574+
right_face_constraint=jnp.zeros(()),
575+
),
576+
)
577+
)
578+
return tuple(result)
579+
580+
529581
@dataclasses.dataclass(kw_only=True, frozen=True, eq=False)
530582
class IonCyclotronSource(source.Source):
531583
"""Ion cyclotron source with surrogate model."""
@@ -542,25 +594,7 @@ def zero_fast_ions(
542594
cls,
543595
geo: geometry.Geometry,
544596
) -> tuple[fast_ion_lib.FastIon, ...]:
545-
# TODO(b/483988192): Add support for other species.
546-
return (
547-
fast_ion_lib.FastIon(
548-
species='He3',
549-
source=cls.SOURCE_NAME,
550-
n=cell_variable.CellVariable(
551-
value=jnp.zeros_like(geo.rho),
552-
face_centers=geo.rho_face_norm,
553-
right_face_grad_constraint=None,
554-
right_face_constraint=jnp.zeros(()),
555-
),
556-
T=cell_variable.CellVariable(
557-
value=jnp.zeros_like(geo.rho),
558-
face_centers=geo.rho_face_norm,
559-
right_face_grad_constraint=None,
560-
right_face_constraint=jnp.zeros(()),
561-
),
562-
),
563-
)
597+
return _build_fast_ions(source_name=cls.SOURCE_NAME, geo=geo)
564598

565599

566600
# Cache the result of this function to avoid re-creating the partial function

torax/_src/sources/tests/ion_cyclotron_source_test.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
# Internal import.
3838
# Internal import.
3939

40+
# Index of He3 in _FAST_ION_SPECIES.
41+
_HE3_INDEX = 3
42+
4043

4144
_DUMMY_MODEL_PATH = "/tmp/toricnn.json"
4245

@@ -383,8 +386,8 @@ def test_icrh_returns_fast_ion_data(self):
383386
self.assertLen(output, 3)
384387
fast_ion_data = output[2]
385388
self.assertIsInstance(fast_ion_data, tuple)
386-
self.assertLen(fast_ion_data, 1)
387-
fi = fast_ion_data[0]
389+
self.assertLen(fast_ion_data, 5)
390+
fi = fast_ion_data[_HE3_INDEX]
388391
self.assertIsInstance(fi, fast_ion_lib.FastIon)
389392
self.assertEqual(fi.species, "He3")
390393
self.assertEqual(
@@ -428,7 +431,7 @@ def test_fast_ion_density_conservation(self):
428431
conductivity=None,
429432
)
430433
fast_ion_data = output[2]
431-
n_tail = fast_ion_data[0].n.value
434+
n_tail = fast_ion_data[_HE3_INDEX].n.value
432435
n_total = core_profiles.n_e.value * minority_conc
433436
self.assertTrue(jnp.all(n_tail >= 0))
434437
self.assertTrue(jnp.all(n_tail <= n_total))
@@ -463,7 +466,7 @@ def test_fast_ion_tail_temperature_above_electron_temp(self):
463466
conductivity=None,
464467
)
465468
fast_ion_data = output[2]
466-
temperature_tail = fast_ion_data[0].T.value
469+
temperature_tail = fast_ion_data[_HE3_INDEX].T.value
467470
self.assertTrue(jnp.all(temperature_tail >= core_profiles.T_e.value))
468471

469472
def test_zero_power_produces_zero_fast_ions(self):
@@ -496,9 +499,11 @@ def test_zero_power_produces_zero_fast_ions(self):
496499
conductivity=None,
497500
)
498501
fast_ion_data = output[2]
499-
np.testing.assert_allclose(fast_ion_data[0].n.value, 0.0, atol=1e-9)
500502
np.testing.assert_allclose(
501-
fast_ion_data[0].T.value, core_profiles.T_i.value
503+
fast_ion_data[_HE3_INDEX].n.value, 0.0, atol=1e-9
504+
)
505+
np.testing.assert_allclose(
506+
fast_ion_data[_HE3_INDEX].T.value, core_profiles.T_i.value
502507
)
503508

504509

0 commit comments

Comments
 (0)