bloc.reactors#

Extension of Cantera Reactor classes to include new features.

Implementation is based on the examples given in https://cantera.org/3.1/examples/python/reactors/custom2.html

Classes#

CarbonBlackIdealGasReactor

IdealGasReactor with additional features for carbon quality calculations.

CarbonBlackIdealGasConstPressureReactor

IdealGasConstPressureReactor with additional features for carbon quality calculations.

CarbonBlackIdealGasMoleReactor

IdealGasMoleReactor with additional features for carbon quality calculations.

PSR

Perfectly Stirred Reactor (PSR) engine — well-mixed, constant-pressure.

PFR

Plug Flow Reactor (PFR) engine — closed Lagrangian parcel march.

PFRWallProfile

PFR engine with a prescribed wall-temperature profile.

PFRHomogeneousShell

PFR engine with homogeneous-shell radial heat loss.

PFRThinShell

PFR engine with thin-shell local-temperature radial heat loss.

PFRGasTemperatureProfile

PFR engine with a prescribed GAS temperature profile T_gas(x).

PFRNetwork

Unified network driver for any PFR subclass.

Functions#

build_closed_carrier_net(reactor, T, P, X, volume, *)

Build a fresh CLOSED carrier reactor (no flow devices) and a bare ReactorNet.

copy_state(src_phase, dst_reactor)

Copy outlet TPX from src_phase back onto the stage dst_reactor.

march_lagrangian_parcel(reactor, T_in, P_in, X_in, *, ...)

Single forward pass of a CLOSED Lagrangian parcel from inlet to outlet.

get_H2_yield(states)

Compute the H2 yield of the process from the states object.

get_carbon_yield(gas[, n_C_min])

Compute carbon yield (solid C mass / total C mass in feed).

find_temperature_from_enthalpy(h_mass, X, P_bar, mechanism)

Return the temperature in °C for a given enthalpy in J/kg and for a given gas composition and pressure.

switch_mechanism(gas, new_mechanism[, htol, Xtol, verbose])

Switch the mechanism of a gas object.

solve_isothermal_reactor(qm_kgs, T_reactor_C, t_res, ...)

Solve the kinetics in an isothermal isobaric reactor at T_reactor_C (°C) and P_bar (bar) for a residence time t_res (s).

solve_adiabatic_reactor(qm_kgs, P_in_kW, t_res, X0, ...)

Solve the kinetics in an adiabatic isobaric reactor at P_bar (bar) for a residence time t_res (s).

solve_fixed_position_temperature_profile_pfr(qm_kgs, ...)

Solve kinetics in a PFR with a prescribed temperature profile T(x).

solve_fixed_time_temperature_profile_pfr(...[, dt, ...])

Solve kinetics in a PFR with a prescribed temperature profile T(t).

mix_two_streams(gas_1, qm_1, gas_2, qm_2)

Mix two streams of gases and return the resulting cantera.Quantity object.

compute_reactor_length(qm_in, S_in, states)

qm_in = v * S_in * density <=> v = qm_in / (S_in*density).

compute_reactor_dimensions_with_form_factor(qm_in, ...)

get_reactor_mass(L_reactor, d_reactor, e_insul_layers, ...)

Compute the mass of the reactor based on its dimensions and the insulation layers.

compute_recovered_power(qm_torch, T_torch_input_C, ...)

Compute the power recovered from the preheating of the torch and the second injection.

compute_residual_heat(P_recovered, gas, qm_tot, T_amb_C)

Compute residual heat after recovery in the exchanger.

Module Contents#

class bloc.reactors.CarbonBlackIdealGasReactor(*args, **kwargs)#

Bases: cantera.ExtensibleIdealGasReactor

IdealGasReactor with additional features for carbon quality calculations.

This class extends the cantera.IdealGasReactor class to include additional features for carbon quality calculations. The class is intended to be used in Reactor Networks.

Examples

residence_time#

Residence time in (s). Residence time must be set by the user during simulation.

after_initialize(t0)#
guess_specific_surface(Model=KirkOthmer2004SurfaceArea)#

Evaluate the specific surface of the carbon based on temperature and residence time.

Note

The residence time must be calculated before calling this method; for instance with:

reactor.residence_time = reactor.volume / volumetric_flow_rate

Or:

for t in logspace(0.001, 1, 50):  # s
    sim.advance(t)
    reactor.residence_time = t
Parameters:

Model (class) – Surface area model to use. Default is KirkOthmer2004SurfaceArea.

Examples

class bloc.reactors.CarbonBlackIdealGasConstPressureReactor(*args, **kwargs)#

Bases: cantera.ExtensibleIdealGasConstPressureReactor

IdealGasConstPressureReactor with additional features for carbon quality calculations.

This class extends the cantera.IdealGasReactor class to include additional features for carbon quality calculations. The class is intended to be used in Reactor Networks.

Examples

Carbon Black Reactor.

Carbon Black Reactor.
residence_time#

Residence time in (s). Residence time must be set by the user during simulation.

after_initialize(t0)#
guess_specific_surface(Model=KirkOthmer2004SurfaceArea)#

Evaluate the specific surface of the carbon based on temperature and residence time.

Note

The residence time must be calculated before calling this method; for instance with:

reactor.residence_time = reactor.volume / flow_rate

Or:

for t in logspace(0.001, 1, 50):  # s
    sim.advance(t)
    reactor.residence_time = t
Parameters:

Model (class) – Surface area model to use. Default is KirkOthmer2004SurfaceArea.

Examples

Carbon Black Reactor.

Carbon Black Reactor.
class bloc.reactors.CarbonBlackIdealGasMoleReactor(*args, **kwargs)#

Bases: cantera.ExtensibleIdealGasMoleReactor

IdealGasMoleReactor with additional features for carbon quality calculations.

This class extends the cantera.IdealGasMoleReactor class to include additional features for carbon quality calculations. The class is intended to be used in Reactor Networks.

Examples

residence_time#

Residence time in (s). Residence time must be set by the user during simulation.

after_initialize(t0)#
guess_specific_surface(Model=KirkOthmer2004SurfaceArea)#

Evaluate the specific surface of the carbon based on temperature and residence time.

Note

The residence time must be calculated before calling this method; for instance with:

reactor.residence_time = reactor.volume / flow_rate

Or:

for t in logspace(0.001, 1, 50):  # s
    sim.advance(t)
    reactor.residence_time = t
Parameters:

Model (class) – Surface area model to use. Default is KirkOthmer2004SurfaceArea.

Examples

bloc.reactors.build_closed_carrier_net(reactor, T, P, X, volume, *, reactor_kwargs=None, use_preconditioner=True, max_steps=None)#

Build a fresh CLOSED carrier reactor (no flow devices) and a bare ReactorNet.

Both reactor families share this constructor so the closed-parcel guarantee (“no mass flow in/out during the inner integration”) has a single named home and cannot silently drift between the tube-furnace and PFRHomogeneousShell paths.

The fresh carrier is built by cloning the mechanism source from reactor._mech_source (present on both PFRWallProfile and DesignIdealGasConstPressureMoleReactor) and constructing a new instance of type(reactor). Because the carrier is a freshly created object it carries no process flow devices (MFC/PC), so CVODE only sees the closed-system ODEs.

Parameters:
  • reactor – Template reactor whose type and mechanism source are reused.

  • T – Inlet thermodynamic state (K, Pa, mole fractions).

  • P – Inlet thermodynamic state (K, Pa, mole fractions).

  • X – Inlet thermodynamic state (K, Pa, mole fractions).

  • volume – Initial reactor volume [m³].

  • reactor_kwargs – Extra keyword arguments passed to type(reactor)(gas, clone=False, **reactor_kwargs).

  • use_preconditioner – Attach a cantera.AdaptivePreconditioner to the network.

  • max_steps – If provided, set net.max_steps on the ReactorNet.

Returns:

carrier — the fresh closed reactor, gas — the cantera.Solution used to initialise it, net — the bare cantera.ReactorNet.

Return type:

(carrier, gas, net)

bloc.reactors.copy_state(src_phase, dst_reactor)#

Copy outlet TPX from src_phase back onto the stage dst_reactor.

Called after the inner closed-carrier integration completes so that the original stage reactor (which may carry process flow devices) reflects the converged outlet state. Both phase and the reactor’s internal CVODE state vector are updated.

bloc.reactors.march_lagrangian_parcel(reactor, T_in, P_in, X_in, *, length, diameter, mdot, heat_flux_factory=None, on_position=None, recorder=None, reactor_kwargs=None, n_points=200, max_steps=200000, use_preconditioner=True, rtol=None, atol=None, max_time_step=None, energy_off=False)#

Single forward pass of a CLOSED Lagrangian parcel from inlet to outlet.

This is the shared march engine used by both the tube-furnace path (PFRWallProfileNet) and the PFRHomogeneousShell / PFRThinShell family (_march_pfr_to_length()). It always builds a fresh closed carrier reactor (via build_closed_carrier_net()) so the parcel integrates independently of any process flow devices (MFC/PC) that may be attached to the stage reactor. The converged outlet state is copied back to reactor via copy_state().

Integration proceeds in n_points equal spatial checkpoints x = i * dx, dx = length / n_points. At each checkpoint:

  1. on_position(carrier, x_prev) is called (if provided) so the caller can update position-dependent state (e.g. T_wall(x)).

  2. The carrier is advanced to the next time point via net.advance(t + dt) where dt = dx / v and v = mdot / (rho * S).

  3. recorder(carrier, x, net.time) is called (if provided).

A ct.Wall(ambient, carrier) with area = 1 is built when a heat_flux_factory is provided. The factory receives carrier and must return a callable heat_flux(t) -> float [W/m²] (Cantera Wall convention: positive = heat from ambient to carrier).

The ambient reservoir uses a throwaway gri30 gas — it is never advanced, so its thermodynamic state is irrelevant.

Parameters:
  • reactor – Template / stage reactor. type(reactor) and _mech_source are used to build the fresh carrier; the outlet state is copied back here after the march.

  • T_in – Inlet state (K, Pa, mole fractions).

  • P_in – Inlet state (K, Pa, mole fractions).

  • X_in – Inlet state (K, Pa, mole fractions).

  • length – Tube length [m].

  • diameter – Tube inner diameter [m].

  • mdot – Mass flow rate [kg/s].

  • heat_flux_factoryfactory(carrier) -> callable that builds the Wall.heat_flux function. Pass None for adiabatic.

  • on_positionon_position(carrier, x) called before each checkpoint advance. Use to set carrier.T_wall_K, carrier.x_position, etc.

  • recorderrecorder(carrier, x, t) called after each checkpoint.

  • reactor_kwargs – Forwarded to build_closed_carrier_net().

  • n_points – Number of spatial checkpoints. Default 200.

  • max_steps – CVODE maximum steps per checkpoint. Default 200 000.

  • use_preconditioner – Attach cantera.AdaptivePreconditioner. Default True.

  • energy_off – When True the carrier’s energy equation is disabled (carrier.energy_enabled = False) so the gas temperature is held constant during each checkpoint advance. The temperature is then a Dirichlet condition imposed by on_position (see PFRGasTemperatureProfile). Default False.

Returns:

Spatial profile with extra columns t (residence time [s]) and x (axial position [m]). The outlet state is also copied back to reactor.

Return type:

ct.SolutionArray

class bloc.reactors.PSR(gas, *args, **kwargs)#

Bases: cantera.ExtensibleIdealGasConstPressureMoleReactor

Perfectly Stirred Reactor (PSR) engine — well-mixed, constant-pressure.

Physics#

The gas is assumed spatially uniform at every instant. The governing equations are the standard constant-pressure mole-balance ODEs:

\[\frac{dn_i}{dt} = V \dot{\omega}_i\]

where \(n_i\) is moles of species i, \(V\) is the reactor volume, and \(\dot{\omega}_i\) is the net molar production rate [mol/(m³·s)]. Energy is either solved (energy='on', the default) or held fixed (energy='off').

Use cases#

  • Torch Mixing Reactor (TMR): turbulent mixing of two streams at nearly constant residence time.

  • Any well-mixed stage where spatial gradients are negligible.

Network topology#

Stage YAML (SPRING ``ContinuousMixingReactor``): OPEN — inlet MassFlowController``(s), outlet ``PressureController / MassFlowController; continuous feed and discharge in the stage ReactorNet.

Inner physics: OPEN — CVODE integrates the stirred tank with inlet/outlet mass exchange. Volume is sized from t_res_s (\(V = \\tau \\dot m / \\rho\)) by Boulder at build time; use advance_to_steady_state on the stage.

Not intended for#

  • Plug-flow (axial gradients): use PFR or a subclass.

Why not ct.IdealGasConstPressureMoleReactor?#

This class inherits from the extensible variant so that subclasses can intercept Cantera’s ODE callbacks (before_update_state, after_eval, etc.) without modifying C++ code. Concrete subclasses such as ContinuousMixingReactor add industrial defaults and KPI helpers on top of this numerical base.

before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

Cantera’s Reactor::initialize() restores m_state (the state snapshot taken at construction time) via a direct C++ call that bypasses all Python delegates. The only reliable interception point is here: CVODE calls updateState(y) just before its first eval, and y is writable.

If _pending_init is set, this method overwrites y with values derived from the HP-corrected gas state and clears the flag. All subsequent updateState calls (driven by CVODE’s own stepping) are unaffected.

class bloc.reactors.PFR(gas, *args, **kwargs)#

Bases: cantera.ExtensibleIdealGasConstPressureMoleReactor

Plug Flow Reactor (PFR) engine — closed Lagrangian parcel march.

Physics#

A fixed mass of gas is tracked as it travels axially through a tube. The parcel is modelled as a closed constant-pressure system: no mass enters or leaves during the inner CVODE integration. At every spatial checkpoint the outlet state is written back to the stage reactor via copy_state().

The common invariant shared by all PFR subclasses is:

  1. A fresh carrier reactor is built by build_closed_carrier_net() from the stage reactor’s mechanism (_mech_source) and type.

  2. The carrier is marched from inlet to outlet.

  3. The outlet TPX is copied back to the stage reactor.

Subclasses override heat_flux_factory() and/or SOLVER_MODE to express the wall heat-transfer hypothesis:

  • PFRWallProfile — prescribed T_wall(x) with forced-convection + grey-gas radiation, adaptive net.step march.

  • PFRHomogeneousShell — radial insulation stack, spatial-mean temperature, Forward-Backward Sweep.

  • PFRThinShell — radial insulation stack, local T(z), single forward pass.

Why not ct.FlowReactor?#

Cantera’s FlowReactor is a steady-state plug-flow model that is integrated by ReactorNet over axial distance (DAE / IDAS), not over residence time (CVODES). It is adiabatic and frictionless by construction and has no hook for our wall heat-transfer laws. Cantera also enforces that a FlowReactor must run alone in its network — it cannot share a ReactorNet with time-dependent ODE reactors (MFC/PC stages, mixing vessels, recycle loops) because the two formulations use different independent variables and different integrators.

We therefore march a closed Lagrangian parcel on an extensible const-pressure mole reactor, which preserves wall heat injection via after_eval / heat_flux_factory and full compatibility with Boulder’s time-based stage networks.

See also: https://cantera.org/dev/python/zerodim.html#cantera.FlowReactor

param gas:

Cantera Solution at the initial thermodynamic state.

Notes

_meta must be populated by the network or builder before marching. Required keys depend on the subclass heat law; at minimum: length, diameter, mass_flow_rate.

SOLVER_MODE: ClassVar[str] = 'fixed_checkpoint'#
NETWORK_CLASS: ClassVar[type | None] = None#
before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

heat_flux_factory(carrier)#

Return a heat-flux callable Q(t) [W/m²] for this wall model.

Override in subclasses to provide the wall heat-transfer law. Return None for an adiabatic parcel.

Parameters:

carrier – The fresh closed-carrier instance created by build_closed_carrier_net(). Subclasses may read carrier attributes (diameter, phase, _meta, …) to build the closure.

class bloc.reactors.PFRWallProfile(gas, *args, clone=False, diameter=0.1, kappa_grey=0.0, mass_flow_rate=0.0, **kwargs)#

Bases: PFR

PFR engine with a prescribed wall-temperature profile.

Physics#

The tube wall temperature T_wall(x) is provided as a callable. At each spatial position the network sets reactor.T_wall_K and reactor.x_position; the after_eval hook then injects the combined forced-convection and grey-gas-radiation flux directly into the energy ODE RHS:

\[Q_{wall} = h_{conv}\,A_{int}\,(T_{wall} - T_{gas}) - q_{rad}\,A_{int}\]

where \(A_{int} = (4/D)\,V\) is the internal surface area and \(h_{conv}\) is computed from the Nusselt correlation (internal_convection_h(), Baehr-Stephan / Hausen / Churchill-Zajic depending on the flow regime).

The parcel is marched with an adaptive net.step() loop so the integrator controls step size according to local stiffness.

Use cases#

  • Electrically or externally heated tube furnace with a known wall temperature profile.

  • Any reactor where the wall temperature is prescribed rather than computed from an insulation model.

See also

PFRHomogeneousShell

thick-shell radial loss.

PFRThinShell

thin-shell local-T radial loss.

SOLVER_MODE: ClassVar[str] = 'adaptive'#
diameter = 0.1#
kappa_grey = 0.0#
mass_flow_rate = 0.0#
T_wall_K: float | None = None#
x_position: float = 0.0#
after_eval(t, LHS, RHS)#

Inject wall convection + radiation into the energy ODE RHS.

NETWORK_CLASS: ClassVar[type | None] = None#
before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

heat_flux_factory(carrier)#

Return a heat-flux callable Q(t) [W/m²] for this wall model.

Override in subclasses to provide the wall heat-transfer law. Return None for an adiabatic parcel.

Parameters:

carrier – The fresh closed-carrier instance created by build_closed_carrier_net(). Subclasses may read carrier attributes (diameter, phase, _meta, …) to build the closure.

class bloc.reactors.PFRHomogeneousShell(gas, *args, **kwargs)#

Bases: PFR

PFR engine with homogeneous-shell radial heat loss.

Physics#

Radial heat loss is modelled via a spatial-mean gas temperature and a lumped thermal resistance stack (insulation layers + external natural-convection / radiation). The total loss Phi_kW is converged by a Forward-Backward Sweep (FBS): an adiabatic predictor pass gives the mean T, which yields a first Phi_kW estimate; the corrector repeats with the updated wall flux until |ΔΦ| / Φ < 1 %.

The heat loss enters the Cantera ODE via a ct.Wall (area = 1 m²) whose heat_flux callable distributes Phi_kW uniformly in space (i.e. proportional to local velocity).

Use cases#

  • Insulated, thick-shell industrial vessels where axial wall conduction smears temperature gradients (e.g. the SPRING Carbon Growth Reactor).

  • Any reactor where the dominant resistance to heat loss is in the insulation layer rather than the gas film.

Not intended for#

  • Reactors with large axial temperature gradients and thin walls: use PFRThinShell instead.

See also

PFRWallProfile

prescribed wall-temperature profile.

PFRThinShell

thin-shell local-T radial loss.

SOLVER_MODE: ClassVar[str] = 'fixed_checkpoint'#
thermal_resistance_stack()#

Build the radial-conduction + external-convection resistance list.

Uses _meta geometry and insulation layers. Returns a list of thermal resistances [K/W] from inner wall to ambient, ordered from innermost insulation layer to external convection/radiation.

wall_thermal_report(T_mean_C)#

Return the wall heat-transfer breakdown at a mean gas temperature.

Single source of truth for the SPRING wall KPIs: the external film coefficients (h_CC, h_rad, h_eff [W/m²/K]), the external wall temperature (T_wall_ext [°C]) and the resulting wall heat loss (heat_loss_kW, including heat_loss_corr_factor). When _meta["adiabatic"] is set the loss is zero and the wall sits at ambient.

Parameters:

T_mean_C – Spatial-mean gas temperature in °C.

Returns:

{"h_CC", "h_rad", "h_eff", "T_wall_ext", "heat_loss_kW"}.

Return type:

dict

spatial_mean_temperature(states)#

Compute the spatial-mean gas temperature [°C] from a PFR profile.

Integrates T over the axial position recorded by _march_pfr_to_length in the x extra column, using the trapezoid rule (trapezoid(states.T, states.x) / states.x[-1]) so the FBS heat-loss estimate is identical between the staged and standalone paths.

Parameters:

states – SolutionArray with an x extra column (axial position [m]).

Returns:

Length-weighted mean temperature in °C. Falls back to the unweighted mean only if states.x is missing or zero.

Return type:

float

heat_loss(T_mean_C)#

Compute total wall heat loss [kW] for a given mean gas temperature.

Uses the linearized thermal resistance stack from thermal_resistance_stack() and multiplies by _meta["heat_loss_corr_factor"] (default 1.0) to account for thermal bridges and other unmodelled loss paths. Returns 0 when _meta["adiabatic"] is True.

Parameters:

T_mean_C – Spatial-mean gas temperature in °C.

Returns:

Heat loss in kW (positive = loss to ambient).

Return type:

float

solve_forward(Phi_kW)#

Integrate the PFR with a fixed heat-loss power and return the spatial profile.

Builds a PFRHomogeneousShellNet and delegates to its _spatial_ode_pass. This is the single-shot entry point for standalone use and tests; the FBS convergence loop in PFRHomogeneousShellNet.advance calls _spatial_ode_pass directly.

Parameters:

Phi_kW – Total wall heat loss to subtract from the inlet enthalpy [kW]. Positive value = heat removed from the gas.

Returns:

Spatial profile with extra columns t (residence time [s]) and x (axial position [m]).

Return type:

ct.SolutionArray

NETWORK_CLASS: ClassVar[type | None] = None#
before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

heat_flux_factory(carrier)#

Return a heat-flux callable Q(t) [W/m²] for this wall model.

Override in subclasses to provide the wall heat-transfer law. Return None for an adiabatic parcel.

Parameters:

carrier – The fresh closed-carrier instance created by build_closed_carrier_net(). Subclasses may read carrier attributes (diameter, phase, _meta, …) to build the closure.

class bloc.reactors.PFRThinShell(gas, *args, **kwargs)#

Bases: PFR

PFR engine with thin-shell local-temperature radial heat loss.

Physics#

Heat loss depends on the local gas temperature T(z) rather than the spatial mean. The local loss per unit length is:

\[q'(z) = -\frac{T_{gas}(z) - T_{amb}}{R_{tot} \cdot L}\quad [W/m]\]

Because the flux is evaluated at each checkpoint via a callable that reads reactor.phase.T, a single forward pass is sufficient — no Forward-Backward Sweep is needed.

Use cases#

  • Quartz-tube / lab-tube reactors where axial wall conduction is negligible (thin, low-conductivity walls).

  • Scenarios where the gas temperature profile is steep and a spatial-mean-T hypothesis would over-predict the loss in the hot inlet zone.

See also

PFRWallProfile

prescribed wall-temperature profile.

PFRHomogeneousShell

thick-shell homogeneous loss.

SOLVER_MODE: ClassVar[str] = 'fixed_checkpoint'#
thermal_resistance_stack#
wall_thermal_report#
spatial_mean_temperature#
heat_loss#
solve_forward()#

Integrate the PFR with the local-T heat loss and return the spatial profile.

Single-shot entry point for standalone use and tests. Builds a PFRThinShellNet and runs one advance pass.

Returns:

Spatial profile with extra columns t and x.

Return type:

ct.SolutionArray

NETWORK_CLASS: ClassVar[type | None] = None#
before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

heat_flux_factory(carrier)#

Return a heat-flux callable Q(t) [W/m²] for this wall model.

Override in subclasses to provide the wall heat-transfer law. Return None for an adiabatic parcel.

Parameters:

carrier – The fresh closed-carrier instance created by build_closed_carrier_net(). Subclasses may read carrier attributes (diameter, phase, _meta, …) to build the closure.

class bloc.reactors.PFRGasTemperatureProfile(gas, *args, clone=False, diameter=0.1, mass_flow_rate=0.0, **kwargs)#

Bases: PFR

PFR engine with a prescribed GAS temperature profile T_gas(x).

Physics#

This is the Dirichlet-on-gas boundary condition: the gas temperature itself is prescribed as a callable T_gas(x) and the energy equation is switched off. Only the kinetics evolve; at every spatial checkpoint the network resets the parcel temperature to T_gas(x):

\[T_{gas}(x) = T_{prescribed}(x), \qquad \frac{dY_k}{dt} = \frac{\dot\omega_k W_k}{\rho}\]

No wall film, no radiation and no resistance stack are involved — the temperature is imposed, not computed from a heat balance. Contrast with PFRWallProfile, which prescribes the wall temperature and lets the gas lag behind it through a convective + radiative film.

Numerically the parcel is marched by the shared march_lagrangian_parcel() with energy_off=True; the temperature is re-imposed before each checkpoint advance so the stepwise-constant history converges to the continuous profile as n_points grows. This is the closed-parcel / OO counterpart of the procedural solve_fixed_position_temperature_profile_pfr().

Use cases#

  • Kinetics validation against a measured in-stream gas temperature (thermocouple trace) where you want predicted species, not a predicted temperature.

  • Decoupling chemistry from the energy balance to isolate mechanism behaviour under a known thermal history.

See also

PFRWallProfile

prescribed wall temperature with convective + radiative coupling (gas temperature is free).

solve_fixed_position_temperature_profile_pfr()

procedural equivalent.

SOLVER_MODE: ClassVar[str] = 'fixed_checkpoint'#
IMPOSED_GAS_T: ClassVar[bool] = True#
diameter = 0.1#
mass_flow_rate = 0.0#
T_gas_K: float | None = None#
x_position: float = 0.0#
NETWORK_CLASS: ClassVar[type | None] = None#
before_update_state(y)#

Inject a pending initial state into CVODE’s y before the first eval.

heat_flux_factory(carrier)#

Return a heat-flux callable Q(t) [W/m²] for this wall model.

Override in subclasses to provide the wall heat-transfer law. Return None for an adiabatic parcel.

Parameters:

carrier – The fresh closed-carrier instance created by build_closed_carrier_net(). Subclasses may read carrier attributes (diameter, phase, _meta, …) to build the closure.

class bloc.reactors.PFRNetwork(reactor)#

Unified network driver for any PFR subclass.

A generic single-pass driver that runs the fixed-checkpoint _march_pfr_to_length() spatial march for any PFR subclass. Specialised stage networks (PFRWallProfileNet for the tube furnace, PFRHomogeneousShellNet / PFRThinShellNet for the shell models) wrap the same marcher with their wall-loss specifics; this class is the minimal fallback driver.

The network is Boulder-compatible: it exposes time and an advance() method, and wires into the NETWORK_CLASS attribute on each PFR subclass.

Parameters:

reactor – A PFR (or subclass) instance that has been fully configured (_meta populated by the STONE builder).

reactor#
property time: float#

Residence time [s] after the last advance.

advance(time=0.0)#

Run the spatial march and update the stage reactor outlet state.

The time argument is ignored (Boulder calls advance(1.0) by convention); the actual integration length is taken from reactor._meta['length'].

bloc.reactors.get_H2_yield(states)#

Compute the H2 yield of the process from the states object.

Parameters:
  • states (ct.SolutionArray) – Solution array with the states of the reactor at each time step. states[0] and states[-1] must be the initial and final states of the reactor. Mass is assumed to be conserved between states[0] and states[-1].

  • as (The H2 yield is defined)

  • subtracted (The initial amount of H2 in the feedstock must be)

  • produced. (because it is not)

  • Thus (H2_yield = (Y_H2_final - Y_H2_initial) / (Y_H_tot - Y_H2_initial))

  • math:: (..) – H2_yield = \frac{Y_{H2,final} - Y_{H2,initial}}{Y_{H,total} - Y_{H2,initial}}

bloc.reactors.get_carbon_yield(gas, n_C_min=300)#

Compute carbon yield (solid C mass / total C mass in feed).

Assumes no solid carbon in the input gas. Carbon yield = mass fraction of solid carbon / total mass fraction of carbon element in the input gas.

Parameters:
  • gas (cantera.Solution or cantera.SolutionArray) – Gas object with composition set.

  • n_C_min (int, optional) – Minimum number of carbon atoms to treat a species as solid carbon. Default 300. Use 24 for mechanisms that represent soot as large PAH only.

bloc.reactors.find_temperature_from_enthalpy(h_mass, X, P_bar, mechanism)#

Return the temperature in °C for a given enthalpy in J/kg and for a given gas composition and pressure.

Parameters:
  • h_mass (-) – Specific enthalpy target in J/kg

  • X (-) – Composition of the gas in mole fraction

  • P_bar (-) – Pressure in bar

  • mechanism (-) – Path to the mechanism file

bloc.reactors.switch_mechanism(gas, new_mechanism, htol=0.0001, Xtol=0.0001, verbose=False)#

Switch the mechanism of a gas object.

Useful to switch from plasma to reactor simulation for instance. As some species may not be present in the new mechanism, we compute the adjust the temperature of the new gas object to match the enthalpy of the old gas object, so that energy is conserved. Still, if the chemical enthalpy variation exceed htol, an error is raised. Similarly, if the mole fraction variation exceed Xtol, a error is raised.

Parameters:
  • gas (ct.Solution) – Gas object

  • new_mechanism (str) – Path to the new mechanism file

  • htol (float) – Tolerance for the chemical enthalpy variation between the two mechanisms. Default is 1e-4.

  • Xtol (float) – Tolerance for the mole fraction variation between the two mechanisms. Default is 1e-4.

  • verbose (bool) – If True, print information about the process. Default is False.

Returns:

gas_new – Gas object with the new mechanism

Return type:

ct.Solution

bloc.reactors.solve_isothermal_reactor(qm_kgs, T_reactor_C, t_res, X0, T0_C, P_bar, mechanism=None, dt_short=1e-09, rtolT_prevention=0.0001, rtolT_fatal=0.01, heat_recycling=True, conversion_target=None, H2_yield_target=None, name=None, verbose=2, termination='residence_time', L_r=None, S_r=None)#

Solve the kinetics in an isothermal isobaric reactor at T_reactor_C (°C) and P_bar (bar) for a residence time t_res (s).

Parameters:
  • qm_kgs (-) – Mass flow rate of the input gas in kg/s

  • T_reactor_C (-) – Temperature of the reactor in °C

  • t_res (-) – Residence time in the reactor in s

  • X0 (-) – Composition of the input gas in mole fraction

  • T0_C (-) – Temperature of the input gas in °C

  • P_bar (-) – Pressure in the reactor in bar

  • mechanism (-) – Path to the mechanism file

  • dt_short (-) – Short time step for the simulation. A short time step is used to prevent temperature variations.

  • rtolT_prevention (-) – Relative tolerance for temperature variations. If the relative variation of the temperature exceed rtolT_prevention, the time step is reduced.

  • rtolT_fatal (-) – Relative tolerance for temperature variations. If the relative variation of the temperature exceed rtolT_fatal, the simulation is stopped.

  • heat_recycling (-) – If True, the energy released in exothermic reactions is used in later endothermic reactions. Default is True.

  • conversion_target (-) – Target for the conversion of the input gas. Default is None.

  • H2_yield_target (-) – Target for the H2 yield. Default is None.

  • name (-) – Name of the reactor. Default is None.

  • verbose (int) – if >0, print infos. If >=2, add calculation progress bar.

Notes

Energy calculation: Two terms are computed: - Ecost_init: the energy required to heat the input gas from T0_C to T_reactor_C. Multiplying it by the mass flow

yields the heating power

  • delta_h: the energy required to compensate for the endothermic reactions and maintain the temperature constant in the reactor.

    If heat_recycling is True, the energy released in exothermic reactions is used in later endothermic reactions. Multiplying it by the mass flow yields the chemical power.

To compute delta_h, we use ‘energy = on’ in the reactor object definition, and evaluate the enthalpy variation at each time step. The time step is choosed so that the relative variation of the temperature is less than rtolT_prevention.

bloc.reactors.solve_adiabatic_reactor(qm_kgs, P_in_kW, t_res, X0, T0_C, P_bar, mechanism, dt_short=1e-09, conversion_target=None, H2_yield_target=None, termination='residence_time', L_reactor=0.0, S_reactor=1.0, P_in_kW_avg=0.0, name=None, verbose=2)#

Solve the kinetics in an adiabatic isobaric reactor at P_bar (bar) for a residence time t_res (s).

Parameters:
  • qm_kgs (-) – Mass flow rate of the input gas in kg/s

  • P_in_kW (-) – Instantaneous power input in kW. Applied once at t = 0 as an inlet specific-enthalpy pulse P_in_kW * 1e3 / qm_kgs (J/kg), before time integration starts.

  • t_res (-) – Residence time in the reactor in s

  • X0 (-) – Composition of the input gas in mole fraction

  • T0_C (-) – Temperature of the input gas in °C

  • P_bar (-) – Pressure in the reactor in bar

  • mechanism (-) – Name of the mechanism file

  • dt_short (-) – Short time step for the simulation

  • conversion_target (-) – Target for the conversion of the input gas. Default is None.

  • H2_yield_target (-) – Target for the H2 yield. Default is None.

  • termination (-) – Termination condition for the simulation. Default is ‘residence_time’. Can be ‘residence_time’ or ‘reactor_length’.

  • L_reactor (-) – Length of the reactor in m. Default is 0. If termination is ‘reactor_length’, L_reactor must be defined.

  • S_reactor (-) – Section of the reactor in m2. Default is 1. If termination is ‘reactor_length’, S_reactor must be defined.

  • P_in_kW_avg (-) – Average power input/removal in kW distributed uniformly over t_res. At each integration step dt, the specific enthalpy is adjusted by dh = P_in_kW_avg * 1e3 / qm_kgs * dt / t_res (J/kg). Positive values add heat; negative values remove heat.

  • name (-) – Name of the reactor. Default is None.

  • verbose (int) – if >0, print infos. If >=2, add calculation progress bar.

Returns:

  • “gas”: ct.Solution

    Gas object with the final state of the reactor

  • ”states”: ct.SolutionArray

    Solution array with the states of the reactor at each time step

Return type:

a dictionary

bloc.reactors.solve_fixed_position_temperature_profile_pfr(qm_kgs, inlet_composition, P_bar, mechanism, temperature_profile_position, dx=0.01, L_reactor=0.0, d_reactor=1.0, name=None, verbose=2)#

Solve kinetics in a PFR with a prescribed temperature profile T(x).

Integrates along the reactor axis with fixed spatial step dx. At each step, the reactor advances by dt = dx / velocity (from mass flow and cross-section), then temperature is set from the profile. No energy equation; T is imposed.

Parameters:
  • qm_kgs (float) – Mass flow rate (kg/s).

  • inlet_composition (str) – Inlet composition (Cantera format, mole fractions).

  • P_bar (float) – Pressure (bar).

  • mechanism (str, optional) – Mechanism name or path; must not be None.

  • temperature_profile_position (np.ndarray) – Shape (2, n): row 0 = axial positions (m), row 1 = temperature (K).

  • dx (float, optional) – Spatial integration step (m). Default 0.01.

  • L_reactor (float, optional) – Reactor length (m); used for reporting. Default 0.0.

  • d_reactor (float, optional) – Reactor diameter (m); used for velocity and cross-section. Default 1.0.

  • name (str, optional) – Reactor name for logging.

  • verbose (int, optional) – Logging level. Default 2.

Returns:

“gas”: Cantera Solution; “states”: SolutionArray with extra “t”, “x”. If mechanism is CRECK_Nobili2024.yaml, also “reaction_rates_by_class” and “mass_carbon_rates_by_class”.

Return type:

dict

bloc.reactors.solve_fixed_time_temperature_profile_pfr(inlet_composition, P_bar, mechanism, temperature_profile_time, dt=1e-06, name=None, verbose=2)#

Solve kinetics in a PFR with a prescribed temperature profile T(t).

Integrates in time with fixed step dt. At each step the reactor advances by dt, then temperature is set from the profile. No energy equation; T is imposed. No reactor geometry or velocity; purely time-residence integration.

Parameters:
  • inlet_composition (str) – Inlet composition (Cantera format, mole fractions).

  • P_bar (float) – Pressure (bar).

  • mechanism (str, optional) – Mechanism name or path; must not be None.

  • temperature_profile_time (np.ndarray) – Shape (2, n): row 0 = residence times (s), row 1 = temperature (K).

  • dt (float, optional) – Time integration step (s). Default 1e-6.

  • name (str, optional) – Reactor name for logging.

  • verbose (int, optional) – Logging level. Default 2.

Returns:

“gas”: Cantera Solution; “states”: SolutionArray with extra “t”. If mechanism is CRECK_Nobili2024.yaml, also “reaction_rates_by_class” and “mass_carbon_rates_by_class”.

Return type:

dict

bloc.reactors.mix_two_streams(gas_1, qm_1, gas_2, qm_2)#

Mix two streams of gases and return the resulting cantera.Quantity object.

bloc.reactors.compute_reactor_length(qm_in, S_in, states)#

qm_in = v * S_in * density <=> v = qm_in / (S_in*density).

qm_in: float

Mass flow rate of the input gas in kg/s

S_in: float

Section of the reactor in m2

states: ct.SolutionArray

contains the results of the kinetic simulation

bloc.reactors.compute_reactor_dimensions_with_form_factor(qm_in, f_factor, states)#
bloc.reactors.get_reactor_mass(L_reactor, d_reactor, e_insul_layers, rho_insul_layers, verbose=False)#

Compute the mass of the reactor based on its dimensions and the insulation layers.

Parameters:
  • L_reactor (float) – Length of the reactor in m.

  • d_reactor (float) – Diameter of the reactor in m.

  • e_insul_layers (list of float) – Thickness of the insulation layers in m.

  • rho_insul_layers (list of float) – Density of the insulation layers in kg/m3.

  • verbose (bool) – If True, print detailed information about the function call.

Returns:

Mass of the reactor in kg.

Return type:

float

bloc.reactors.compute_recovered_power(qm_torch, T_torch_input_C, X_torch_input, qm_2nd_inj, T_2nd_inj_C, X_2nd_inj, P_bar, T_amb, gas_reac, verbose=False)#

Compute the power recovered from the preheating of the torch and the second injection.

\[P_{\mathrm{recovered}} = P_{\mathrm{preheat,torch}} + P_{\mathrm{preheat,2nd}} P_{\mathrm{preheat}} = \dot{m} \, (h_{\mathrm{in}} - h_0)\]
bloc.reactors.compute_residual_heat(P_recovered, gas, qm_tot, T_amb_C, verbose=False)#

Compute residual heat after recovery in the exchanger.

\[P_{\mathrm{heat,tot}} = \dot{m}_{\mathrm{tot}} \, (h_{\mathrm{reactor\,out}} - h_{\mathrm{cold\,out}}) P_{\mathrm{residual}} = P_{\mathrm{heat,tot}} - P_{\mathrm{recovered}}\]