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
6568def _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 )
530582class 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
0 commit comments