56 ) -> Tuple[float, float, Dict[str, Any]]:
60 discharge_O = config.discharge[
"oxidizer"]
61 discharge_F = config.discharge[
"fuel"]
62 feed_O = config.feed_system[
"oxidizer"]
63 feed_F = config.feed_system[
"fuel"]
64 spray_cfg = config.spray
65 fluids = config.fluids
67 rho_O = fluids[
"oxidizer"].density
68 mu_O = fluids[
"oxidizer"].viscosity
69 sigma_O = fluids[
"oxidizer"].surface_tension
71 rho_F = fluids[
"fuel"].density
72 mu_F = fluids[
"fuel"].viscosity
73 sigma_F = fluids[
"fuel"].surface_tension
76 A_core = geometry.core.n_ports * np.pi * (geometry.core.d_port / 2.0) ** 2
77 d_hyd_core = geometry.core.d_port
79 A_annulus =
_annulus_area(geometry.annulus.inner_diameter, geometry.annulus.gap_thickness)
85 max_iter = config.solver.closure.max_iterations
86 Cd_reduction = config.solver.closure.Cd_reduction_factor
87 Cd_O_eff = discharge_O.Cd_inf
88 Cd_F_eff = discharge_F.Cd_inf
92 "constraints_satisfied":
False,
102 "injector_type":
"coaxial",
105 for iteration
in range(max_iter):
107 for feed_iter
in range(3):
108 delta_p_feed_O = delta_p_feed(mdot_O, rho_O, feed_O, P_tank_O, mu=mu_O)
109 delta_p_feed_F_base = delta_p_feed(mdot_F, rho_F, feed_F, P_tank_F, mu=mu_F)
110 if config.regen_cooling
is not None and config.regen_cooling.enabled:
111 delta_p_regen = delta_p_regen_channels(
115 config.regen_cooling,
118 delta_p_feed_F = delta_p_feed_F_base + delta_p_regen
120 delta_p_feed_F = delta_p_feed_F_base
123 P_inj_O = P_tank_O - delta_p_feed_O
124 P_inj_F = P_tank_F - delta_p_feed_F
127 delta_p_inj_O =
max(0.0, P_inj_O - Pc)
128 delta_p_inj_F =
max(0.0, P_inj_F - Pc)
130 u_O_quick = np.sqrt(2 * delta_p_inj_O / rho_O)
if delta_p_inj_O > 0
else 0.0
131 u_F_quick = np.sqrt(2 * delta_p_inj_F / rho_F)
if delta_p_inj_F > 0
else 0.0
133 Re_O_quick = calculate_reynolds_number(rho_O, u_O_quick, d_hyd_core, mu_O)
134 Re_F_quick = calculate_reynolds_number(rho_F, u_F_quick, d_hyd_annulus, mu_F)
138 Cd_O_quick_base = cd_from_re(Re_O_quick, discharge_O, P_inlet=P_inj_O, T_inlet=T_tank_O)
139 Cd_F_quick_base = cd_from_re(Re_F_quick, discharge_F, P_inlet=P_inj_F, T_inlet=T_tank_F)
140 Cd_O_quick =
min(Cd_O_quick_base, Cd_O_eff)
141 Cd_F_quick =
min(Cd_F_quick_base, Cd_F_eff)
143 mdot_O = Cd_O_quick * A_core * np.sqrt(2 * rho_O * delta_p_inj_O)
144 mdot_F = Cd_F_quick * A_annulus * np.sqrt(2 * rho_F * delta_p_inj_F)
146 delta_p_inj_O =
max(0.0, P_inj_O - Pc)
147 delta_p_inj_F =
max(0.0, P_inj_F - Pc)
155 u_O = mdot_O / (rho_O * A_core)
if A_core > 0
else 0.0
156 u_F_axial = mdot_F / (rho_F * A_annulus)
if A_annulus > 0
else 0.0
157 swirl_angle = np.deg2rad(geometry.annulus.swirl_angle)
158 u_F = u_F_axial / np.cos(swirl_angle)
if np.cos(swirl_angle) > 1e-6
else u_F_axial
160 Re_O = calculate_reynolds_number(rho_O, u_O, d_hyd_core, mu_O)
161 Re_F = calculate_reynolds_number(rho_F, u_F, d_hyd_annulus, mu_F)
163 Cd_O_base = cd_from_re(Re_O, discharge_O, P_inlet=P_inj_O, T_inlet=90.0)
164 Cd_F_base = cd_from_re(Re_F, discharge_F, P_inlet=P_inj_F, T_inlet=300.0)
165 Cd_O =
min(Cd_O_base, Cd_O_eff)
166 Cd_F =
min(Cd_F_base, Cd_F_eff)
168 if delta_p_inj_O > 0:
169 mdot_O = Cd_O * A_core * np.sqrt(2 * rho_O * delta_p_inj_O)
173 if delta_p_inj_F > 0:
174 mdot_F = Cd_F * A_annulus * np.sqrt(2 * rho_F * delta_p_inj_F)
178 u_O = mdot_O / (rho_O * A_core)
if A_core > 0
else 0.0
179 u_F_axial = mdot_F / (rho_F * A_annulus)
if A_annulus > 0
else 0.0
180 u_F = u_F_axial / np.cos(swirl_angle)
if np.cos(swirl_angle) > 1e-6
else u_F_axial
182 J = momentum_flux_ratio(rho_O, u_O, rho_F, u_F_axial)
183 MR = mdot_O / mdot_F
if mdot_F > 0
else np.inf
184 TMR = thrust_momentum_ratio(J, MR)
186 if spray_cfg.spray_angle.model ==
"J":
187 theta = spray_angle_from_J(J, spray_cfg.spray_angle.k, spray_cfg.spray_angle.n)
189 theta = spray_angle_from_TMR(TMR)
191 We_O = weber_number(rho_O, u_O, geometry.core.d_port, sigma_O)
192 We_F = weber_number(rho_F, u_F, d_hyd_annulus, sigma_F)
194 Oh_O = ohnesorge_number(mu_O, rho_O, sigma_O, geometry.core.d_port)
195 Oh_F = ohnesorge_number(mu_F, rho_F, sigma_F, d_hyd_annulus)
197 D32_O = smd_lefebvre(
198 geometry.core.d_port,
205 D32_F = smd_lefebvre(
214 U_rel = np.sqrt(u_O ** 2 + u_F_axial ** 2)
215 tau_evap_O = tau_evap(D32_O, spray_cfg.evaporation.K)
216 x_star_O = xstar(U_rel, tau_evap_O)
217 x_star_combined =
max(x_star_O, xstar(U_rel, tau_evap(D32_F, spray_cfg.evaporation.K)))
219 constraints_ok, violations = check_spray_constraints(We_O, We_F, x_star_combined, spray_cfg)
223 "iterations": iteration + 1,
224 "constraints_satisfied": constraints_ok,
225 "violations": violations,
233 "x_star": x_star_combined,
234 "swirl_angle_deg": geometry.annulus.swirl_angle,
244 Cd_O_eff *= Cd_reduction
245 Cd_O_eff =
max(Cd_O_eff, discharge_O.Cd_min)
246 Cd_F_eff *= Cd_reduction
247 Cd_F_eff =
max(Cd_F_eff, discharge_F.Cd_min)
249 return mdot_O, mdot_F, diagnostics