1818
1919import chex
2020import jax
21+ import jax .numpy as jnp
2122from torax ._src import array_typing
23+ from torax ._src import math_utils
2224from torax ._src import state
2325from torax ._src .config import runtime_params as runtime_params_lib
2426from torax ._src .geometry import geometry
4244class RuntimeParams (sources_runtime_params_lib .RuntimeParams ):
4345 puff_decay_length : array_typing .FloatScalar
4446 S_total : array_typing .FloatScalar
47+ target_line_average_n_e : array_typing .FloatScalar
48+ feedback_gain : array_typing .FloatScalar
4549
4650
4751# Default formula: exponential
@@ -66,6 +70,35 @@ def calc_puff_source(
6670 )
6771
6872
73+ # Gas puff with feedback on line averaged density
74+ def calc_puff_feedback_source (
75+ runtime_params : runtime_params_lib .RuntimeParams ,
76+ geo : geometry .Geometry ,
77+ source_name : str ,
78+ core_profiles : state .CoreProfiles ,
79+ unused_calculated_source_profiles : source_profiles .SourceProfiles | None ,
80+ unused_conductivity : conductivity_base .Conductivity | None ,
81+ ) -> tuple [array_typing .FloatVectorCell , ...]:
82+ """Calculates external source term for n from puffs with feedback."""
83+ source_params = runtime_params .sources [source_name ]
84+ assert isinstance (source_params , RuntimeParams )
85+
86+ current_line_avg_n_e = math_utils .line_average (core_profiles .n_e .value , geo )
87+ error = source_params .target_line_average_n_e - current_line_avg_n_e
88+
89+ S_total = source_params .feedback_gain * error
90+ S_total = jnp .clip (S_total , 0.0 , jnp .inf )
91+
92+ return (
93+ formulas .exponential_profile (
94+ decay_start = 1.0 ,
95+ width = source_params .puff_decay_length ,
96+ total = S_total ,
97+ geo = geo ,
98+ ),
99+ )
100+
101+
69102@dataclasses .dataclass (kw_only = True , frozen = True , eq = False )
70103class GasPuffSource (source .Source ):
71104 """Gas puff source for the n_e equation."""
@@ -86,35 +119,50 @@ class GasPuffSourceConfig(base.SourceModelBase):
86119 S_total: total gas puff particles/s
87120 """
88121
89- model_name : Annotated [Literal [ 'exponential' ], torax_pydantic . JAX_STATIC ] = (
90- 'exponential'
91- )
122+ model_name : Annotated [
123+ Literal [ 'exponential' , 'feedback' ], torax_pydantic . JAX_STATIC
124+ ] = 'exponential'
92125 puff_decay_length : torax_pydantic .TimeVaryingScalar = (
93126 torax_pydantic .ValidatedDefault (0.05 )
94127 )
95128 S_total : torax_pydantic .TimeVaryingScalar = torax_pydantic .ValidatedDefault (
96129 1e22
97130 )
131+ target_line_average_n_e : torax_pydantic .TimeVaryingScalar = (
132+ torax_pydantic .ValidatedDefault (0.0 )
133+ )
134+ feedback_gain : torax_pydantic .TimeVaryingScalar = (
135+ torax_pydantic .ValidatedDefault (0.0 )
136+ )
98137 mode : Annotated [
99138 sources_runtime_params_lib .Mode , torax_pydantic .JAX_STATIC
100139 ] = sources_runtime_params_lib .Mode .MODEL_BASED
101140
102141 @property
103142 def model_func (self ) -> source .SourceProfileFunction :
143+ if self .model_name == 'feedback' :
144+ return calc_puff_feedback_source
104145 return calc_puff_source
105146
106147 def build_runtime_params (
107148 self ,
108149 t : chex .Numeric ,
109150 ) -> RuntimeParams :
151+ if self .model_name == 'feedback' :
152+ is_explicit = True
153+ else :
154+ is_explicit = self .is_explicit
155+
110156 return RuntimeParams (
111157 prescribed_values = tuple (
112158 [v .get_value (t ) for v in self .prescribed_values ]
113159 ),
114160 mode = self .mode ,
115- is_explicit = self . is_explicit ,
161+ is_explicit = is_explicit ,
116162 puff_decay_length = self .puff_decay_length .get_value (t ),
117163 S_total = self .S_total .get_value (t ),
164+ target_line_average_n_e = self .target_line_average_n_e .get_value (t ),
165+ feedback_gain = self .feedback_gain .get_value (t ),
118166 )
119167
120168 def build_source (self ) -> GasPuffSource :
0 commit comments