Regenerative Cooling¶
This page explains the regenerative cooling model implemented in pyskyfire.regen. It traces the path through coupled_solver.py, shows how the solver calls the equations in physics.py, and explains how the hot-gas aerothermodynamic properties are prepared by pyskyfire.skycea.aerothermodynamics.
The model is a quasi-one-dimensional steady heat-exchanger calculation. At each axial station, it balances three heat-transfer paths:
heat transfer from the combustion gas to the hot wall,
conduction through one or more wall layers, and
heat transfer from the cold wall into the coolant flowing in the cooling channels.
The result is a streamwise solution for wall temperatures, coolant bulk temperature, coolant pressure, heat flux, heat-transfer coefficients, coolant velocity, and residuals.
1. Code path overview¶
The public entry point is:
coupled_steady_heating_analysis(
thrust_chamber,
boundary_conditions,
nodes=100,
circuit_index=0,
film="auto",
solver="newton",
output=True,
)
For the current implementation, the only accepted solver name is "newton". This dispatches to:
solve_coupled_heat_exchanger(
thrust_chamber,
boundary_conditions,
nodes,
circuit_index,
output,
)
The coolant temperature and pressure are marched explicitly, but the two wall temperatures are obtained from a local nonlinear heat-balance solve using scipy.optimize.least_squares.
nodes may be an integer or three explicit, potentially ragged grids. An
integer constructs identical uniform grids. Explicit grids use
[wall_nodes, heat_flux_nodes, coolant_nodes]; the equivalent named form is:
nodes = {
"wall": [...],
"heat_flux": [...],
"coolant": [...],
}
Every coolant interval must contain at least one wall node. Multiple wall nodes are attached to the same lumped coolant state, and their heat rates are integrated before the next coolant state is evaluated. This is useful when a detailed wall/heat-flux distribution is needed but coolant property flashes dominate runtime.
The helper class:
CoupledHeatExchangerPhysics(thrust_chamber, boundary_conditions, circuit_index)
collects the local physics calculations used by the marching solver:
hot_side_coefficients(x, T_hw)cold_side_coefficients(x, T_cw, T_cool)dQ_hot_dx(x, T_hw)dQ_cond_dx(x, T_hw, T_cw)dQ_cold_dx(x, T_cw, T_cool)coolant_temperature_rate(T_cool, p_cool, dQ_cold_dx)coolant_friction_rate(x, T_cool, p_cool)bulk_velocity(x, T_cool, p_cool)interface_temperatures(x, T_hw, T_cw)
These methods call lower-level correlations from physics.py, mainly Bartz-type hot-gas heat transfer, Colburn coolant-side heat transfer, Reynolds number, Darcy friction factor, coolant velocity, curvature factor, and adiabatic-wall temperature.
The hot-gas property calls such as:
combustion_transport.get_T(x)
combustion_transport.get_p(x)
combustion_transport.get_h(x)
combustion_transport.get_cp(x)
combustion_transport.get_mu(x)
combustion_transport.get_k(x)
combustion_transport.get_Pr(x)
combustion_transport.get_M(x)
combustion_transport.get_a(x)
combustion_transport.get_gamma(x)
are normally supplied by skycea.aerothermodynamics.Aerothermodynamics, which precomputes CEA-based equilibrium and temperature-pressure maps along the chamber/nozzle contour.
2. Symbols and sign conventions¶
The most important local variables are:
Symbol |
Meaning |
Units |
|---|---|---|
|
axial coordinate |
m |
|
axial node spacing used by the marching solver |
m |
|
hot-side wall temperature |
K |
|
coolant-side wall temperature |
K |
|
coolant bulk static temperature |
K |
|
coolant static pressure |
Pa |
|
coolant stagnation pressure |
Pa |
|
hot-side thermal area per unit axial length |
m |
|
flow area of one coolant channel |
m² |
|
coolant hydraulic diameter |
m |
|
hot-gas-side hydraulic diameter approximation |
m |
|
coolant mass flow through one channel |
kg/s |
|
hot-gas mass flow |
kg/s |
|
effective temperature-based hot-side coefficient |
W/m²/K |
|
enthalpy-based hot-side coefficient |
kg/m²/s |
|
coolant-side heat-transfer coefficient |
W/m²/K |
|
hot-side wall heat flux |
W/m² |
|
hot-side heat input per unit axial length |
W/m |
|
wall conduction per unit axial length |
W/m |
|
coolant heat pickup per unit axial length |
W/m |
Heat flow is positive from the combustion gas into the wall and then into the coolant.
3. Boundary conditions¶
The BoundaryConditions class stores the inlet state of one cooling circuit:
BoundaryConditions(T_coolant_in, p_coolant_in, mdot_coolant)
where:
The mass flow supplied here is the total mass flow through the selected cooling circuit. The solver divides it evenly across all geometric channel instances:
This single-channel mass flow is used for local velocity, Reynolds number, heat-transfer coefficient, coolant temperature rise, and pressure drop.
4. Aerothermodynamics from skycea.aerothermodynamics¶
The regenerative cooling solver needs the hot-gas state along the contour. In the CEA-based workflow, this is provided by the Aerothermodynamics class.
4.1 Initialization from thrust, area ratio, and characteristic length¶
One constructor is:
Aerothermodynamics.from_F_eps_Lstar(
fu, ox, MR, p_c, F, eps, L_star,
T_fu_in=298.15,
T_ox_in=298.15,
p_amb=1.013e5,
)
The oxidizer-to-fuel mixture ratio is:
The method builds CEA fuel and oxidizer objects at the specified inlet temperatures and runs a CEA_Wrap.RocketProblem at chamber pressure and exit area ratio. Internally, chamber pressure is converted from pascal to psi using:
CEA returns design-point quantities such as characteristic velocity, vacuum specific impulse, chamber density, chamber temperature, throat temperature, and throat pressure. These are then used to derive engine-level quantities.
The total mass flow is calculated from the requested thrust and vacuum specific impulse:
using:
The fuel and oxidizer mass flows are:
The throat area follows from the definition of characteristic velocity:
so that:
The throat radius is:
The exit area and exit radius are:
The chamber residence time is computed from the characteristic length, throat area, chamber density, and total mass flow:
The chamber volume estimate is then:
which reduces to:
when substituting the previous expression.
The vacuum thrust coefficient is derived from vacuum specific impulse:
Ambient thrust coefficient is modeled by subtracting the pressure-thrust penalty:
and the corresponding ambient specific impulse is:
The sea-level thrust coefficient and sea-level specific impulse use:
4.2 Initialization from thrust and exit pressure¶
The alternative constructor is:
Aerothermodynamics.from_F_pe_Lstar(
fu, ox, MR, p_c, F, p_e, L_star,
T_fu_in=298.15,
T_ox_in=298.15,
p_amb=1.013e5,
)
Instead of specifying exit area ratio directly, it specifies exit pressure. CEA is called with the pressure ratio:
The area ratio is then obtained from the CEA result:
After that, the same mass-flow, area, radius, residence-time, and thrust-coefficient equations are used.
4.3 Attaching the contour¶
After construction, the method:
compute_aerothermodynamics(contour)
stores the contour on the object and clears the station cache. No property
table is built here: every gas state is solved live by CEA when a getter asks
for it, so there is no resolution to choose at this point. attach_contour is
an alias for the same method.
4.4 Station states along the contour¶
With no temperature or enthalpy argument, a getter such as get_T(x) solves
the rocket problem at the local area ratio:
The station is passed to CEA as a subsonic or supersonic area ratio depending
on the sign of x:
Stations inside the tolerance band \(\varepsilon(x) \le 1 + 10^{-10}\) are solved as the chamber state. CEA returns the state in its own units, which the code converts to base SI: bar to pascal, kJ to J, millipoise to Pa·s, and mW/(cm·K) to W/(m·K).
Because a station state depends only on x, it is cached on the object and
reused by every later getter at the same coordinate. Temperature- and
enthalpy-conditioned states are not cached: they are Newton trial points that
almost never recur, and caching them made the cache grow without bound during
a coupled solve. The coupled solver clears the cache when a simulation
finishes.
The cache is what makes post-processing cheap. A report tab that plots eleven
properties over the same axial grid pays for one CEA solve per station in the
first plot; the remaining ten read cached states. Visualization therefore
chooses its own resolution, and PlotTransportProperty accepts exactly one of:
PlotTransportProperty(transport, prop="T", results=cooling_data) # run grid
PlotTransportProperty(transport, prop="T", nodes=200) # uniform
PlotTransportProperty(transport, prop="T", x=my_x) # explicit
Passing results reuses the axial stations of the solved run, which keeps the
plots on the same grid as the simulation and hits the cache the run already
populated.
4.5 Imposed temperature and imposed enthalpy¶
When a temperature is supplied, for example:
get_h(x, T=T_wall)
the local static pressure is taken from the station state at x, and CEA is
solved as a TP problem at \((T,\ p(x))\). An imposed enthalpy is solved the same
way as an HP problem at \((h,\ p(x))\).
Pressure is special: get_p(x, T=..., h=...) ignores T and h and always
returns the station pressure \(p(x)\).
Composition is returned directly from the solve as a species mole-fraction dictionary; no interpolation between stations is involved.
4.6 Continuation below the CEA temperature boundary¶
CEA becomes unreliable at low temperature, so states below
minimum_cea_temperature (200 K by default, configurable on the constructor)
are not solved directly. Instead, the properties are continued along their
right-hand tangent at the boundary \(T_b\). Three TP solves are taken at
\(T_b,\ T_b + \Delta T,\ T_b + 2\Delta T\), and a second-order one-sided
derivative is formed:
Strictly positive properties (\(\rho\), \(c_p\), \(\gamma\), \(a\), \(\mu\), \(k\), \(Pr\), \(MW\)) are continued in log space, which keeps them positive:
Enthalpy is continued linearly:
This is \(C^1\) at the boundary, so a Newton solve that crosses \(T_b\) sees no kink in the properties or their first derivatives. An HP query that fails, or that lands below \(T_b\), inverts the enthalpy tangent to recover the temperature and then evaluates the same continuation. The tangents are cached per (station, pressure) pair and cleared together with the station cache.
5. Marching grid and unknowns in solver.py¶
The cooling solver reads the selected cooling circuit:
circuit = thrust_chamber.cooling_circuits[circuit_index]
and builds a marching grid from circuit.x_domain. If the circuit direction is positive:
If the circuit direction is negative, the order is reversed:
The axial spacing used in the solver is:
The stored solution arrays are:
T_hw_arr: hot-side wall temperature,T_cw_arr: coolant-side wall temperature,T_cool_arr: coolant bulk static temperature,p_static_arr: coolant static pressure,p_stagnation_arr: coolant stagnation pressure,dQ_dA_arr: local heat flux,velocity_arr: coolant velocity,T_stagnation_arr: coolant stagnation temperature.
The inlet values are:
The initial guesses for both wall temperatures at the first station are:
At each station, the unknowns solved by least squares are:
and:
so that:
The lower bound enforces:
and:
so the wall ordering remains:
The hot-wall upper bound is based on the local gas temperature:
6. Hot-gas-side heat transfer¶
The hot-side calculation is performed by:
CoupledHeatExchangerPhysics.hot_side_coefficients(x, T_hw)
6.1 Local gas-side geometry and state¶
The hot-gas-side hydraulic diameter is approximated as twice the local chamber radius:
The local hot-gas flow area is:
The hot-gas mass flow is:
The local gas static temperature is:
The temperature used in the current Bartz-property correction is the arithmetic mean of wall and gas temperature:
The gas enthalpy at the equilibrium state is:
The hot-wall enthalpy is requested from the combustion-transport model at the wall temperature:
If this lookup fails, the implementation falls back to:
The local Mach number and speed of sound are:
A reference enthalpy is computed:
Implementation note:
Currently H_gris calculated, but not used to query reference condition gas properties. The actual properties used in the Bartz correlation are currently retrieved at x as shown below. The reference-condition properties should ideally be evaluated at H_gr, but the current aerothermodynamics module does not yet support that workflow robustly. This is not trivial to solve, as it generally requires us to move away from NASA CEA as a reference lookup. There are currently no other programs that support as wide a library of propellants, so ditching it would hurt propellant compatibility strongly.
6.2 Bartz-type enthalpy-driven coefficient¶
The hot-side coefficient is calculated by physics.h_gas_bartz_enthalpy_driven:
The solver multiplies this by a user-supplied hot-side correction factor:
where:
The enthalpy-based heat-transfer coefficient used in the heat-flux equation is:
Because h_gr has units of W/m²/K and c_p has units of J/kg/K, h_g has units of kg/m²/s. This is why the hot-side heat flux is written in terms of enthalpy difference rather than temperature difference.
6.3 Adiabatic-wall temperature and adiabatic-wall enthalpy¶
Adiabatic-wall temperature is computed using physics.T_aw:
In the hot-side call, this becomes:
The heat flux itself is driven by adiabatic-wall enthalpy, not directly by T_aw. We compute:
6.4 Hot-side heat flux and heat per unit length¶
The hot-side wall heat flux is:
The effective temperature-based hot-side coefficient reported for plotting and diagnostics is:
The heat transfer per unit axial length is then:
where dA_hot/dx comes from:
cooling_circuit.dA_dx_thermal_exhaust(x)
7. Wall conduction¶
Wall conduction is calculated by:
dQ_cond_dx(x, T_hw, T_cw)
The solver supports a stack of wall layers. Each wall layer has local thickness:
and thermal conductivity evaluated at the mean wall temperature:
The hot-side thermal area per unit axial length is:
The thermal resistance per unit axial length of wall layer j is:
The total wall resistance per unit length is the series sum:
The conduction heat flow per unit axial length is:
Interface temperatures are reconstructed after solving. Starting with:
and using:
for each wall layer:
The resulting list is:
The output array reverses this order so that the saved temperature vector at each station is:
8. Coolant-side heat transfer¶
The coolant-side coefficient is calculated by:
cold_side_coefficients(x, T_cw, T_cool)
8.1 Coolant film and bulk properties¶
The film temperature is:
The current implementation obtains a pressure for these property calls from the combustion-transport model:
and then evaluates:
The coolant bulk density and viscosity are evaluated at the coolant bulk temperature:
Implementation note: this pressure choice is specific to cold_side_coefficients. The coolant temperature and pressure marching functions use the coolant pressure array. If coolant properties are strongly pressure-dependent in a particular case, this distinction should be checked.
8.2 Coolant velocity and Reynolds number¶
The local single-channel flow area is:
The local hydraulic diameter is:
Coolant velocity is computed by physics.u_coolant:
The Reynolds number is computed by physics.reynolds:
8.3 Curvature factor¶
The local channel radius of curvature is:
The curvature factor computed by physics.phi_curv is:
For a straight section:
Implementation note: the current cold_side_coefficients method computes phi_curv, but then calls the Colburn correlation with phi_curv=1. Therefore, curvature is reported in the returned dictionary but is not currently applied to the coolant-side heat-transfer coefficient.
8.4 Colburn coolant-side heat-transfer coefficient¶
The coolant-side heat-transfer coefficient is calculated by physics.h_coolant_colburn:
In the current solver call:
The result is multiplied by a user-supplied coolant-side correction factor:
where:
8.5 Coolant heat pickup per unit length¶
The coolant-side thermal resistance per unit length is delegated to the cooling-circuit geometry object:
R_coolant_per_len(x, h_c=h_c, T_wall_rep=T_rep)
with representative wall/coolant temperature:
The heat pickup per unit axial length is:
This lets the geometry object decide the effective coolant-side area and fin efficiency details, instead of hard-coding them directly in solver.py.
9. Local wall-temperature solve¶
At each axial station i, the solver forms three per-cell heat flows:
The local steady heat balance is:
The nonlinear residual vector is:
The residuals are scaled by:
so the least-squares residual vector is:
The solver uses least_squares with:
trust-region reflective method,
method="trf",robust
soft_l1loss,bounds enforcing physical wall-temperature ordering,
xtol = ftol = gtol = 1e-10,max_nfev = 200.
After convergence, the solved values are:
The converged wall temperatures become the initial guesses for the next axial station.
10. Coolant temperature marching¶
After the wall temperatures are solved at station i, the coolant temperature at station i+1 is updated using the heat absorbed by the coolant in the current cell.
The continuous energy equation is:
In the implemented marching step, the solver first computes the per-cell coolant heat pickup:
and then applies:
so:
In the source, the function name and argument name still refer to dQ_cold_dx, but in the marching call the quantity passed has already been multiplied by dx. The implemented update is therefore a finite-volume energy update using heat per cell.
11. Coolant pressure marching¶
The solver marches static pressure, because static pressure is what the momentum equation governs and what test data and the reference RL10 models report. Stagnation pressure is carried alongside as \(p_0 = p + \tfrac{1}{2}\rho_c u_c^2\).
The irreversible part of the update is calculated by:
coolant_friction_rate(x, T_cool, p_cool)
At the current station, the coolant density is:
The single-channel velocity is:
The Reynolds number is:
The Darcy friction factor is then computed by physics.f_darcy.
11.1 Darcy friction factor¶
The following laminar threshold is used:
and turbulent threshold:
For laminar flow:
For turbulent smooth-wall flow, a Petukhov-type expression is used:
If roughness is supplied, the Colebrook-White equation is solved iteratively:
where:
In the transitional regime, the solver linearly blends the laminar and turbulent friction factors:
11.2 Friction gradient¶
A geometric path-length factor is computed:
circuit.ds_dx(x)
The friction gradient, evaluated at the segment midpoint on the upstream state, is:
where \(f\) already carries the Ito curvature multiplier when
pressure_curvature_correction is enabled.
11.3 Acceleration term¶
The full one-dimensional momentum equation is:
The second term is not a purely local quantity: the coolant accelerates both because the channel tapers and because heating lowers its density. It is therefore applied segment to segment, trapezoidally in the mass flux \(G = \rho_c u_c\):
This form is exact in both limits it has to span. At constant density it reduces to Bernoulli, \(\tfrac{1}{2}\rho_c(u_{c,i+1}^2 - u_{c,i}^2)\); at constant area it reduces to \(G^2\left(1/\rho_{i+1} - 1/\rho_i\right)\), which for a heated duct is twice the change in dynamic head. Reconstructing static pressure as \(p_0 - \tfrac{1}{2}\rho_c u_c^2\) from a friction-only stagnation march therefore counts the heating-driven acceleration at half strength. Equivalently, a friction-only \(dp_0/dx\) omits the heat-addition (Rayleigh) stagnation loss \(+\tfrac{1}{2}u_c^2\,d\rho_c/dx\).
11.4 Segment update¶
The static update is:
The downstream density, velocity and pressure are mutually dependent through
the equation of state, so each segment is swept to a fixed point starting from
the friction-only guess. PRESSURE_SWEEPS caps the sweeps and
PRESSURE_SWEEP_TOL sets the convergence tolerance in pascals; a segment that
fails to settle raises a RuntimeWarning. The downstream enthalpy is fixed by
the segment’s heat load and so does not participate in the sweep.
The inlet boundary condition p_coolant_in is interpreted as a stagnation
pressure, so node 0 starts at \(p_0 = p_{coolant,in}\) and
\(p = p_{coolant,in} - \tfrac{1}{2}\rho_c u_c^2\).
12. Derived outputs¶
After all nodes have been marched, the solver calculates several reporting quantities.
12.1 Heat flux¶
At each station:
The reported heat flux is:
If A_hot is zero, the code stores zero to avoid division by zero.
12.2 Coolant velocity¶
The velocity reported at each node is:
where density is evaluated using the solved coolant temperature and static pressure.
12.3 Stagnation temperature¶
The stagnation temperature is computed from the static coolant temperature and kinetic-energy term:
where:
12.4 Wall-interface temperature array¶
The returned T array has shape:
The first column is coolant bulk temperature. The remaining columns are wall-interface temperatures ordered from cold side to hot side:
12.5 Heat-transfer coefficients and adiabatic-wall temperature¶
For each node, the solver recomputes and stores:
These are returned as:
"h_hot"
"h_hot_enthalpy"
"h_cold"
"T_aw_hot"
13. Residual logging¶
When residual logging is enabled, every local wall-temperature solve records:
The residual magnitude is:
The global residual history is aggregated iteration-by-iteration. For finite p, the implemented norm is:
For p = \infty, it is:
The final per-cell residual is the last recorded residual magnitude for each cell.
14. Assumptions and implementation details¶
The current regenerative cooling solver is an engineering heat-exchanger model. Its main assumptions are:
The solution is steady in time.
The solver marches along one spatial coordinate.
Axial conduction in the wall is neglected.
Wall conduction is treated as one-dimensional through the wall stack.
Each coolant channel in the selected circuit receives an equal share of total circuit mass flow.
Hot-gas heat transfer is modeled by an enthalpy-driven Bartz-style correlation.
Coolant-side heat transfer is modeled by a Colburn-style turbulent internal-flow correlation.
Coolant pressure drop is modeled with Darcy friction and a separate area-change correction.
Hot-gas properties are solved live by CEA at the requested station and cached per axial coordinate.
There are also a few important implementation details to keep in mind:
The Bartz gas properties are evaluated at the computed reference enthalpy
H_gr.phi_curvis computed in the coolant-side model, but the current Colburn call passesphi_curv=1, so curvature does not currently modifyh_cold.Gas states below
minimum_cea_temperatureare continued along a \(C^1\) tangent instead of being solved by CEA.get_p(x, T=..., h=...)always returns the station pressure and ignores the supplied temperature or enthalpy.In
cold_side_coefficients, coolant properties are evaluated using the marched coolant pressure.
These are limitations in the current implementation that is being worked on for future versions. Nevertheless, the exact behavior is reported here so results using the program are interpreted correctly.
15. Returned data structure¶
coupled_steady_heating_analysis returns a RegenResult produced by solve_coupled_heat_exchanger. Its core fields are:
{
"x": x_wall,
"x_wall": x_wall,
"x_heat_flux": x_heat_flux,
"x_coolant": x_coolant,
"T": T_full,
"T_static": T_cool_arr,
"T_stagnation": T_stagnation_arr,
"p_static": p_static_arr,
"p_stagnation": p_stagnation_arr,
"dQ_dA": dQ_dA_arr,
"velocity": velocity_arr,
"h_hot": h_hot_arr,
"h_hot_enthalpy": h_hot_enthalpy_arr,
"h_cold": h_cold_arr,
"T_aw_hot": T_aw_hot_arr,
"residuals": (global_R, final_R),
"wall_residual_scaled": wall_residual_scaled,
"wall_converged": wall_residual_scaled <= RESIDUAL_TOL,
"film_regime": film_regime,
"liquid_film": liquid_film_or_none,
"gaseous_film": gaseous_film_or_none,
}
x remains an alias for x_wall. T is defined on x_wall; dQ_dA,
qpp_hot, h_hot, h_hot_enthalpy, T_aw_hot, and T_drive are defined on
x_heat_flux; coolant temperature, pressure, velocity, phase, quality, and
h_cold are defined on x_coolant.
PlotWallTemperature displays a red x at every wall node where
wall_converged is false. Pass mark_nonconverged=False to suppress these
markers. Their hover labels report the final scaled residual.
16. Summary¶
The regenerative cooling solver couples a CEA-based hot-gas property model to a one-dimensional cooling-channel heat-exchanger calculation. The solver walks along the selected cooling circuit, solves a local nonlinear balance for the hot- and cold-side wall temperatures, and then marches coolant temperature and pressure downstream.
At each station, the central balance is:
with:
The hot side is enthalpy-driven, the wall is modeled as a stack of thermal resistances, and the coolant side is temperature-driven. This makes the solver fast enough for design iteration while retaining the main physical couplings needed for regenerative cooling analysis.