44 """Mirrors native_injector._fill_discharge.
46 When the config declares an orifice INLET geometry, resolve it here and hand the kernel
47 the finished Cd_inf with the diameter scaling switched OFF. The kernel has no notion of
48 inlet treatment, so passing the raw Cd_inf would leave it on the old diameter path while
49 Python used the inlet value -- a fresh Python/kernel divergence of exactly the kind that
50 the x_star evaporation split already cost this codebase 9.7% of thrust.
53 _cd_inlet = cd_inf_from_inlet_geometry(c)
54 _lich = lichtarowicz_re_inputs(c)
55 _lod, _beta, _cdu = (0.0, 0.0, 0.0)
if _lich
is None else (
56 _lich[1], float(_lich[2]
or 0.0), _lich[0])
57 _cd_inf = float(_cd_inlet)
if _cd_inlet
is not None else float(c.Cd_inf)
58 _use_geom = 0
if _cd_inlet
is not None else int(bool(c.use_geometry_cd))
60 Cd_inf=_cd_inf, a_Re=float(c.a_Re), Cd_min=float(c.Cd_min),
61 use_geometry_cd=_use_geom,
62 d_ref_m=float(c.d_ref_m), d_min_m=float(c.d_min_m),
63 cd_small_hole_exponent=float(c.cd_small_hole_exponent),
64 cd_large_hole_log_gain=float(c.cd_large_hole_log_gain),
65 cd_inf_max=float(c.cd_inf_max), cd_inf_min_geom=float(c.cd_inf_min_geom),
66 use_pressure_correction=int(bool(c.use_pressure_correction)),
67 P_ref=float(c.P_ref), a_P=float(c.a_P),
68 use_temperature_correction=int(bool(c.use_temperature_correction)),
69 T_ref=float(c.T_ref), a_T=float(c.a_T),
70 lich_l_over_d=_lod, lich_beta=_beta, lich_cd_u=_cdu,
124 """Mirrors native_injector._fill_comb."""
125 floor = getattr(eff,
"tau_Tc_floor_K",
None)
126 ropt = getattr(eff,
"R_opt",
None)
128 model=_EFF_MODEL.get(eff.model, 2),
129 C=float(eff.C), K=float(eff.K),
130 cooling_efficiency_floor=float(eff.cooling_efficiency_floor),
131 use_cooling_coupling=int(bool(eff.use_cooling_coupling)),
132 tau_ref=float(eff.tau_ref), tau_ref_P=float(eff.tau_ref_P),
133 tau_ref_T=float(eff.tau_ref_T), n_pressure=float(eff.n_pressure),
134 T_star_fuel_cap_K=float(getattr(eff,
"T_star_fuel_cap_K", 1000.0)),
135 has_tau_Tc_floor=int(floor
is not None),
136 tau_Tc_floor=float(floor
or 0.0),
138 Em_peak=float(getattr(eff,
"Em_peak", 0.96)),
139 mixing_sigma=float(getattr(eff,
"mixing_sigma", 1.5)),
140 R_opt=float(ropt)
if ropt
is not None else 0.0,
145 """Mirrors native_injector._fill_cooling.
147 The C struct is zero-initialised, so fields guarded by `if rg is not None` /
148 `if ab is not None` stay 0.0 when that config block is absent. The defaults
149 below reproduce that -- do not "improve" them into schema defaults.
151 rg, ab = cfg.regen_cooling, cfg.ablative_cooling
152 fc = getattr(cfg,
"film_cooling",
None)
153 eff = cfg.combustion.efficiency
155 regen_enabled=int(bool(rg
and rg.enabled)),
156 film_enabled=int(bool(fc
and fc.enabled)),
157 ablative_enabled=int(bool(ab
and ab.enabled)),
158 graphite_enabled=int(bool(getattr(cfg,
"graphite_insert",
None)
159 and cfg.graphite_insert.enabled)),
160 use_cooling_coupling=int(bool(eff.use_cooling_coupling)),
161 cooling_efficiency_floor=float(eff.cooling_efficiency_floor),
163 hot_gas_viscosity=0.0, hot_gas_thermal_conductivity=0.0,
164 hot_gas_prandtl=0.0, gas_turbulence_intensity=0.0, recovery_factor=0.0,
165 radiation_emissivity_hot=0.0, radiation_view_factor=0.0,
166 regen_chamber_inner_diameter=0.0,
167 ablative_coverage_fraction=0.0, ablative_surface_temperature_limit=0.0,
168 ablative_material_density=0.0, ablative_heat_of_ablation=0.0,
169 ablative_specific_heat=0.0, ablative_pyrolysis_temperature=0.0,
170 ablative_use_physics_based_blowing=0, ablative_blowing_efficiency=0.0,
171 ablative_blowing_coefficient=0.0, ablative_blowing_min_reduction_factor=0.0,
172 ablative_turbulence_reference_intensity=0.0, ablative_turbulence_sensitivity=0.0,
173 ablative_turbulence_exponent=0.0, ablative_turbulence_max_multiplier=0.0,
174 ablative_surface_emissivity=0.0, ablative_ambient_temperature=0.0,
175 ablative_radiative_sink_minimum_threshold=0.0,
176 ablative_radiative_sink_fallback_temperature=0.0,
179 c.hot_gas_viscosity = float(rg.hot_gas_viscosity)
180 c.hot_gas_thermal_conductivity = float(rg.hot_gas_thermal_conductivity)
181 c.hot_gas_prandtl = float(rg.hot_gas_prandtl)
182 c.gas_turbulence_intensity = float(rg.gas_turbulence_intensity)
183 c.recovery_factor = float(rg.recovery_factor)
if rg.recovery_factor
is not None else 0.94
184 c.radiation_emissivity_hot = float(rg.radiation_emissivity_hot)
185 c.radiation_view_factor = float(rg.radiation_view_factor)
186 c.regen_chamber_inner_diameter = float(rg.chamber_inner_diameter
or 0.0)
188 c.ablative_coverage_fraction = float(ab.coverage_fraction)
189 c.ablative_surface_temperature_limit = float(ab.surface_temperature_limit)
190 c.ablative_material_density = float(ab.material_density)
191 c.ablative_heat_of_ablation = float(ab.heat_of_ablation)
192 c.ablative_specific_heat = float(ab.specific_heat)
193 c.ablative_pyrolysis_temperature = float(ab.pyrolysis_temperature)
194 c.ablative_use_physics_based_blowing = int(bool(ab.use_physics_based_blowing))
195 c.ablative_blowing_efficiency = float(ab.blowing_efficiency)
196 c.ablative_blowing_coefficient = float(ab.blowing_coefficient)
197 c.ablative_blowing_min_reduction_factor = float(ab.blowing_min_reduction_factor)
198 c.ablative_turbulence_reference_intensity = float(ab.turbulence_reference_intensity)
199 c.ablative_turbulence_sensitivity = float(ab.turbulence_sensitivity)
200 c.ablative_turbulence_exponent = float(ab.turbulence_exponent)
201 c.ablative_turbulence_max_multiplier = float(ab.turbulence_max_multiplier)
202 c.ablative_surface_emissivity = float(ab.surface_emissivity)
203 c.ablative_ambient_temperature = float(ab.ambient_temperature)
204 c.ablative_radiative_sink_minimum_threshold = float(ab.radiative_sink_minimum_threshold)
205 c.ablative_radiative_sink_fallback_temperature = float(ab.radiative_sink_fallback_temperature)
210 """Mirrors native_injector._fill_geom. `cg` must come from ensure_chamber_geometry."""
212 A_throat=float(cg.A_throat), A_exit=float(cg.A_exit), volume=float(cg.volume),
213 Lstar=float(cg.Lstar)
if cg.Lstar
else 0.0,
214 length=float(cg.length),
215 length_cylindrical=float(cg.length_cylindrical
or 0.0),
216 length_contraction=float(cg.length_contraction
or 0.0),
217 chamber_diameter=float(cg.chamber_diameter
or 0.0),
218 exit_diameter=float(cg.exit_diameter
or 0.0),
219 expansion_ratio=float(cg.expansion_ratio),
220 nozzle_efficiency=float(cg.nozzle_efficiency),
221 Cf=float(cg.Cf
or 0.0),
222 design_pressure=float(cg.design_pressure
or 0.0),
227 """Pintle geometry, flattened.
229 The area/hydraulic-diameter maths lives in engine.core.geometry and is called
230 HERE, at the boundary, so the kernel sees scalars and the two implementations
231 cannot drift on geometry.
234 A_O, A_F = get_effective_areas(geom)
235 dh_O, dh_F = get_hydraulic_diameters(geom)
236 return _ns(A_O=float(A_O), A_F=float(A_F),
237 d_hyd_O=float(dh_O), d_hyd_F=float(dh_F),
238 d_orifice=float(geom.lox.d_orifice),
239 h_gap=float(geom.fuel.h_gap),
240 d_pintle_tip=float(geom.fuel.d_pintle_tip))
261 """The stream's back-channel ring (impinging._RingManifold), read off the very object the
262 Python injector builds, so the kernel marches the same branch; zeros for a plenum."""
264 fl = config.fluids[side]
265 b = getattr(config.injector.geometry, side)
266 net = _ring_manifold_for(config, side, float(fl.density), float(fl.viscosity),
267 b.n_elements, b.d_jet)
270 return _ns(enabled=1, n_ports=int(net.n_ports), h=int(net.h), A_ch=float(net.A_ch),
271 D_h=float(net.D_h), s=float(net.s), eps=float(net.eps), scale=float(net.scale),
272 A_hole=float(net.A_hole), d_hole=float(net.d_hole), K_ent=float(net.K_ent),
277 """Pure-Python equivalent of native_injector.build_state.
279 Returns a namespace with EdEngineState's attribute paths. Raises the same way
280 build_state does on a config it cannot map (e.g. a pintle config, whose
281 injector geometry has no .oxidizer/.fuel impinging branches).
285 g = config.injector.geometry
287 inj_type = _INJ[config.injector.type]
291 if inj_type == _INJ[
"impinging"]:
292 imp_O, imp_F, pin =
_imp(g.oxidizer),
_imp(g.fuel), _PIN_ZERO
293 net_O, net_F =
_net(config,
"oxidizer"),
_net(config,
"fuel")
294 elif inj_type == _INJ[
"pintle"]:
295 _z =
_ns(n_elements=0, d_jet=0.0, impingement_angle=0.0, spacing=0.0)
296 imp_O, imp_F, pin = _z, _z,
_pintle(g)
297 net_O = net_F = _NET_ZERO
299 raise NotImplementedError(f
"injector type {config.injector.type!r} not ported")
306 smd_C=float(sp.smd.C), smd_m=float(sp.smd.m), smd_p=float(sp.smd.p),
307 smd_scale=float(getattr(sp.smd,
"smd_scale", 1.0)
or 1.0),
308 smd_property_scaling=float(bool(getattr(sp.smd,
"smd_property_scaling",
True))),
310 smd_we_corr_max=float(getattr(sp.smd,
"we_corr_max",
None))
311 if getattr(sp.smd,
"we_corr_max",
None)
else 0.0,
312 use_turbulence_corrections=int(bool(getattr(sp,
"use_turbulence_corrections",
False))),
313 turbulence_penetration_gain=float(getattr(sp,
"turbulence_penetration_gain", 0.0)
or 0.0),
314 pintle_C=float(sp.pintle.C), pintle_B=float(sp.pintle.B),
315 pintle_n=float(sp.pintle.n), pintle_p=float(sp.pintle.p),
316 chamber_gas_R=float(sp.smd.chamber_gas_R),
317 chamber_gas_T=float(sp.smd.chamber_gas_T),
318 spray_angle_model=0
if sp.spray_angle.model ==
"J" else 1,
319 spray_angle_k=float(sp.spray_angle.k), spray_angle_n=float(sp.spray_angle.n),
320 we_min=float(sp.weber.get(
"We_min", 15.0)),
321 evap_K=float(sp.evaporation.K),
322 evap_x_star_limit=float(sp.evaporation.x_star_limit),
323 evap_use_constraint=int(bool(sp.evaporation.use_constraint)),
324 evap_model_code=1
if getattr(sp.evaporation,
"model",
"derived") ==
"derived" else 0,
325 evap_C_evap=float(getattr(sp.evaporation,
"C_evap", 1.562)),
326 evap_cp_gas=float(getattr(sp.evaporation,
"cp_gas", 2200.0)),
327 evap_apply_tau_res=1.0
if getattr(sp.evaporation,
"apply_tau_res_correction",
False)
else 0.0,
329 d32_measured_O=float(getattr(sp.smd,
"d32_measured_O",
None)
or 0.0),
330 d32_measured_F=float(getattr(sp.smd,
"d32_measured_F",
None)
or 0.0),
334 closure_max_iterations=int(config.solver.closure.max_iterations),
335 closure_Cd_reduction_factor=float(config.solver.closure.Cd_reduction_factor),
336 Pc_min_bound=float(config.solver.Pc_bounds[0]),
337 Pc_max_bound=float(config.solver.Pc_bounds[1]),
338 tolerance=float(config.solver.tolerance),
339 max_iterations=int(config.solver.max_iterations),
343 injector=
_ns(type=inj_type, imp_O=imp_O, imp_F=imp_F),
345 net_O=net_O, net_F=net_F,
347 discharge_O=
_discharge(effective_discharge(config,
"oxidizer")),
348 discharge_F=
_discharge(effective_discharge(config,
"fuel")),
350 feed_O=
_feed(config.feed_system[
"oxidizer"]),
351 feed_F=
_feed(config.feed_system[
"fuel"]),
352 fluid_O=
_fluid(config.fluids[
"oxidizer"]),
353 fluid_F=
_fluid(config.fluids[
"fuel"]),
356 comb=
_comb(config.combustion.efficiency),
358 geom=
_geom(ensure_chamber_geometry(config)),