100 config: CombustionEfficiencyConfig,
101 cooling_efficiency: float,
102 advanced_params: Dict[str, Any],
105 """c* efficiency, eta_vap * eta_mix * eta_HL.
107 eta_vap and eta_mix come from combustion_physics. eta_HL is the heat lost through the wall,
108 taken from the cooling results the chamber solver leaves in the spray diagnostics
109 (``spray_diagnostics["cooling"]``); ``cooling_efficiency`` is used only when the call carries
110 none. The breakdown, with its inputs and assumptions, is written back to
111 ``spray_diagnostics["cstar_efficiency"]`` so it reaches the reported diagnostics.
113 advanced_params: Pc, Tc (CEA, adiabatic), cstar_ideal, gamma, R, MR, Ac, At, m_dot_total,
114 u_fuel, u_lox, spray_diagnostics, fuel_props, and optionally ox_props; cea_cache (the design's
115 CEACache, whose propellant pair selects the wide c*(O/F) table) or cstar_fn for the mixing and
116 vaporized-gas c* ratios.
118 from .combustion_physics
import calculate_combustion_efficiency_advanced
120 Pc = advanced_params.get(
"Pc", DEFAULT_CHAMBER_PRESS_PA)
121 Tc = advanced_params.get(
"Tc", DEFAULT_CHAMBER_TEMP_K)
122 cstar_ideal = advanced_params.get(
"cstar_ideal", DEFAULT_CSTAR_IDEAL_M_S)
123 gamma = advanced_params.get(
"gamma", DEFAULT_GAMMA_ND)
124 R = advanced_params.get(
"R", DEFAULT_GAS_CONST_J_KG_K)
125 MR = advanced_params.get(
"MR", DEFAULT_MIXTURE_RATIO_ND)
126 Ac = advanced_params.get(
"Ac",
None)
127 At = advanced_params.get(
"At",
None)
128 m_dot_total = advanced_params.get(
"m_dot_total",
None)
129 spray_diagnostics = advanced_params.get(
"spray_diagnostics",
None)
130 if Ac
is None or m_dot_total
is None:
131 raise ValueError(
"Ac and m_dot_total are required for combustion efficiency calculation")
133 raise ValueError(
"At is required for combustion efficiency calculation")
135 results = calculate_combustion_efficiency_advanced(
136 Lstar, Pc, Tc, cstar_ideal, gamma, R, MR, config, Ac, At, m_dot_total,
137 u_fuel=advanced_params.get(
"u_fuel",
None),
138 u_lox=advanced_params.get(
"u_lox",
None),
139 spray_diagnostics=spray_diagnostics,
140 fuel_props=advanced_params.get(
"fuel_props",
None),
141 ox_props=advanced_params.get(
"ox_props",
None),
143 cea_cache=advanced_params.get(
"cea_cache",
None),
144 cstar_fn=advanced_params.get(
"cstar_fn",
None),
147 cooling_results = spray_diagnostics.get(
"cooling")
if isinstance(spray_diagnostics, dict)
else None
149 if not config.use_cooling_coupling:
151 elif isinstance(cooling_results, dict):
154 if not (np.isfinite(cooling_efficiency)
and 0.0 <= cooling_efficiency <= 1.0):
155 raise ValueError(f
"Invalid cooling_efficiency: {cooling_efficiency}. Must be in [0, 1].")
156 eta_HL = float(cooling_efficiency)
157 if isinstance(cooling_results, dict)
and cooling_results.get(
"film", {}).get(
"enabled"):
158 results[
"assumptions"].append({
159 "name":
"combustion.film_cooling_cstar_loss",
"value":
None,
"unit":
"",
160 "reason":
"film coolant stays in the flow; its c* cost (a fuel-rich wall layer) is not modelled"})
162 eta = results[
"eta_total"] * eta_HL
165 if not (np.isfinite(eta)
and eta > 0.0):
167 f
"Invalid combustion efficiency {eta}: L*={Lstar:.4f} m, eta_vap={results['eta_vaporization']:.4f}, "
168 f
"eta_mix={results['eta_mixing']:.4f}, eta_HL={eta_HL:.4f}."
171 if isinstance(spray_diagnostics, dict):
172 spray_diagnostics[
"cstar_efficiency"] = {
174 "eta_heat_loss": float(eta_HL),
175 "heat_lost_W": float(Q_lost),
176 "heat_lost_by_source_W": Q_by_source,
177 "eta_cstar": float(eta),
180 logging.getLogger(
"evaluate").
info(
181 f
"[ETA] eta_c*={eta:.4f} = vap {results['eta_vaporization']:.4f} x mix "
182 f
"{results['eta_mixing']:.4f} x HL {eta_HL:.4f} (Q_lost {Q_lost/1e3:.1f} kW)"