bloc.reactor_models#

Industrial reactor models and STONE YAML builders for Bloc.

This module connects Bloc’s domain-specific reactor formulations to the YAML with STONE standard used by Boulder. It registers custom reactor builders so that STONE component types like PlasmaTorchInstantaneousHeating, ContinuousMixingReactor, RefractoryReactor, QuartzTubeReactor and TubeFurnace declared in configuration files are instantiated as concrete Cantera reactors inside a ReactorNet.

Each industrial model subclasses a numerical engine defined in bloc.reactors (PFRHomogeneousShell, PFRThinShell, PFRWallProfile, PSR) and adds industrial defaults and KPI helpers.

Registration is handled by bloc.boulder_plugins.register.register_plugins() function called via entry point.

Key responsibilities#

  • Register custom STONE builders via register_reactor_builder() and install a post-build hook via register_post_build_hook(). Builders are invoked by CanteraConverter when converting a normalized STONE config to a running Cantera network.

  • Provide design-oriented reactor subclasses that expose convenient solver and utility methods (mixing, energy accounting, heat transfer estimates) used by higher-level workflows.

  • Interoperate with Bloc utilities and models (bloc.reactors, bloc.utils, bloc.models) while remaining fully compatible with Reactor and ReactorNet.

PFR thermal hypotheses#

Bloc ships two PFR variants which differ only in how they map gas temperature to wall heat loss; both attach a ct.Wall between the reactor and an ambient ct.Reservoir, and both surface that wall in the post-solve visualization network.

  • PFRHomogeneousShellhomogeneous shell hypothesis (industrial model: RefractoryReactor). The wall is assumed isothermal because axial conduction through a thick shell smears local gradients. Total radial loss is driven by the spatial-mean gas temperature, so the network class resolves it with a Forward-Backward Sweep (FBS) iteration.

  • PFRThinShellthin shell hypothesis (industrial model: QuartzTubeReactor). Axial conduction is negligible (e.g. quartz tubes used in lab tube furnaces), so heat loss is computed locally from T(z). No FBS is required; the network solves the spatial ODE in a single pass with a wall.heat_flux callable that closes over reactor.phase.T.

Use RefractoryReactor for insulated, thick-shell configurations where axial wall conduction smears local gradients. Use QuartzTubeReactor only when the reactor shell is genuinely thin and axially non-conducting.

Example

Given a STONE component

- id: torch
  PlasmaTorchInstantaneousHeating:
    t_res_s: 1.0e-4
    electric_power_kW: 55.0
    torch_eff: 0.8
    gen_eff: 0.8

the converter will call this module’s registered builder and inject an instance into the network under the torch id.

Attributes#

Classes#

TorchInstantaneousHeatingNet

ReactorNet subclass implementing torch adiabatic integration.

TorchInstantaneousHeating

Torch physics engine with instantaneous enthalpy jump (plasma / simplified).

DesignIdealGasConstPressureMoleReactor

Design-oriented constant-pressure mole reactor with helper APIs.

PFRWallProfileNet

Stage network driving a TubeFurnace through a prescribed wall profile.

PFRGasTemperatureProfileNet

Stage network driving a PFRGasTemperatureProfile.

PFRHomogeneousShellNet

ReactorNet subclass implementing Forward-Backward Sweep for a PFR stage.

PFRThinShellNet

Single-pass PFR network for the thin-shell hypothesis.

TubeFurnace

Industrial Tube Furnace model.

RefractoryReactor

Industrial Carbon Growth Reactor (CGR) / refractory-lined vessel.

QuartzTubeReactor

Industrial quartz-tube / lab-tube reactor.

ContinuousMixingReactor

Industrial Torch Mixing Reactor (TMR) / multi-inlet well-mixed vessel.

InstantaneousMixingNet

ReactorNet subclass for instantaneous-mix + closed-τ chemistry.

InstantaneousMixing

Engine: instantaneous HP mix of two inlet streams + closed-τ chemistry.

InstantaneousMixingReactor

Industrial model: instantaneous mix + closed chemical residence time.

PlasmaTorchInstantaneousHeating

Industrial Plasma Torch model — instantaneous-heating variant.

Functions#

residence_states_to_series(states)

Serialize a closed CSTR residence-time profile for the Plots tab.

states_to_series(states[, fbs_convergence])

Serialize a spatial SolutionArray profile to a JSON-compatible dict.

compute_tube_furnace_kpis(sim)

Compute tube-furnace engineering KPIs from a solved STONE Simulation.

Module Contents#

class bloc.reactor_models.TorchInstantaneousHeatingNet(reactors, meta=None)#

Bases: cantera.ReactorNet

ReactorNet subclass implementing torch adiabatic integration.

Mirrors the PFRHomogeneousShellNet pattern: overrides advance() so the torch stage runs the same physics as the legacy simulation_non_isothermal_reactors path — instantaneous enthalpy addition followed by adiabatic kinetics for t_res_s.

Parameters:
  • reactors – List of reactors for this stage. Must contain exactly one TorchInstantaneousHeating.

  • meta (dict, optional) – Parameters injected by _build_torch_instantaneous() and enriched by _post_build_design_volumes(): effective_power_kW, mass_flow_rate, t_res_s, mechanism.

advance_to_steady_state()#

Validate that precomputed torch outlet data is available.

This stage does not integrate dynamics here; the outlet profile is precomputed in _post_build_design_volumes().

solve_steady()#

Delegate steady solve to advance_to_steady_state().

Base ReactorNet would run on an empty reactor list and raise.

advance(time)#

Validate and expose the precomputed torch outlet.

_post_build_design_volumes() pre-computes a torch outlet profile and stores _states before this TorchInstantaneousHeatingNet is instantiated. Downstream extraction reads torch.phase after solve_adiabatic() writeback.

Parameters:

time – Nominal advance time [s], unused and kept for API compatibility.

property states: cantera.SolutionArray | None#

Adiabatic post-enthalpy profile (satisfies CustomStageNetwork).

property scalars: Dict[str, Any]#

Torch build parameters and outlet-state physics quantities.

class bloc.reactor_models.TorchInstantaneousHeating(gas, *args, **kwargs)#

Bases: cantera.IdealGasConstPressureMoleReactor

Torch physics engine with instantaneous enthalpy jump (plasma / simplified).

Boulder’s build_sub_network detects NETWORK_CLASS and instantiates TorchInstantaneousHeatingNet for the torch stage. That class uses the same physics as the legacy simulation_non_isothermal_reactors path: instantaneous enthalpy addition (gas.HP += Q/mdot) followed by adiabatic kinetics for t_res_s.

_meta#

Torch parameters populated by _build_torch_instantaneous() and enriched by _post_build_design_volumes().

Type:

dict

_states#

Residence-time profile written by solve_adiabatic() (extra t [s]).

Type:

ct.SolutionArray or None

NETWORK_CLASS: ClassVar[type | None]#
solve_adiabatic(mdot, power_kW, t_res_s, verbose=True)#

Solve adiabatic kinetics using this reactor’s current inlet state.

Adds torch power as an enthalpy jump and integrates one adiabatic pass for t_res_s using a fresh type(self)(gas, clone=False) carrier.

class bloc.reactor_models.DesignIdealGasConstPressureMoleReactor(gas, *args, **kwargs)#

Bases: cantera.ExtensibleIdealGasConstPressureMoleReactor

Design-oriented constant-pressure mole reactor with helper APIs.

Inherits from ExtensibleIdealGasConstPressureMoleReactor so that subclasses can intercept Cantera’s ODE callbacks without modifying C++ code.

The key hook used here is before_update_state(): CVODE calls updateState(y) before its very first eval, at which point y is still writable. By storing a pending initial condition in _pending_init and writing it into y on the first call, any subclass can override CVODE’s starting point — even though m_state (the C++ snapshot taken at construction) cannot be changed from Python.

Parameters:

gas – Cantera Solution object used to initialise the reactor.

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.

solve_adiabatic(mdot, power_kW, t_res_s, verbose=False)#

Solve an adiabatic constant-pressure reactor segment.

Uses this reactor as the ODE owner: applies an enthalpy jump from power_kW / mdot and integrates with a fresh type(self)(gas, clone=False) carrier for t_res_s.

switch_mechanism(gas, mechanism_path, htol, Xtol)#

Switch the active mechanism while conserving chemical enthalpy.

See switch_mechanism() for details.

external_coeffs(D_ext, L_reac, eps_wall, T_wall_hyp_C, T_amb_C)#

Return convective and radiative heat transfer coefficients at the wall.

heat_losses_linear(T_mean_C, T_amb_C, R_list)#

Compute linearized heat losses across a thermal resistance stack.

thermal_resistance(d_reac, L_reac, e_insul_list, cond_list, h_tot, S_ext)#

Build a radial-conduction plus external heat transfer resistance list.

mass_and_power(qm_torch, T_torch_input_C, X_torch, qm_2nd_inj, T_2nd_inj_C, X_2nd_inj, P_bar, T_amb_C, gas)#

Compute preheat recovery and residual heat based on reactor states.

mass_reactor(L_reac, d_reac, e_insul_list, density_list, verbose=False)#

Estimate reactor mass and volume from geometry and insulation layers.

class bloc.reactor_models.PFRWallProfileNet(reactors, meta=None, *, wall_T_fn=None, total_length=None, n_points=200, rtol=0.0001, atol=1e-12, cap_time_step=True)#

Stage network driving a TubeFurnace through a prescribed wall profile.

Replaces the legacy LagrangianPFRReactor + LagrangianPFRNetwork + DesignTubeFurnaceNet trio with a single driver built on the unified PFRWallProfile engine and the shared march_lagrangian_parcel() marcher.

Physics#

A closed Lagrangian gas parcel is marched from inlet to outlet. At each spatial checkpoint the wall temperature T_wall(x) and the entry-length position x_position = max(x - heating_start, 0) are pushed onto the carrier; the after_eval() hook then injects forced-convection + grey-gas-radiation flux into the energy ODE.

Numerics#

Unlike the retired adaptive net.step loop, this driver uses the fixed-checkpoint net.advance march (n_points default 200), which yields a uniform spatial grid and reproduces the experimentally-validated (Mei 2019) profiles within < 1 %.

The public surface is duck-typed to Boulder’s CustomStageNetwork (time, states, scalars, advance, advance_to_steady_state) and exposes the tube-furnace diagnostics (E_conv, E_rad, n_steps, h_conv_avg, Fo_D_min and the recorded *_arr profiles) consumed by compute_tube_furnace_kpis(), report_details and yaml_utils.

param reactors:

Iterable containing exactly one TubeFurnace (PFRWallProfile) reactor.

param meta:

Geometry / wall-profile parameters produced by _build_design_tube_furnace() (total_length, T_wall_K, T_ambient_K, entry_leg, exit_leg, entry_zone, plateau_zone, T_wall_profile, diameter, kappa_grey).

type meta:

dict, optional

NETWORK_DIAGRAM_NOTE: str = 'Node temperatures in this diagram reflect the outlet gas state of the Lagrangian simulation...#
reactors#
E_conv: float = 0.0#
E_rad: float = 0.0#
n_steps: int = 0#
h_conv_avg: float#
Fo_D_min: float#
x_arr: numpy.ndarray | None = None#
t_arr: numpy.ndarray | None = None#
T_wall_arr: numpy.ndarray | None = None#
T_gas_arr: numpy.ndarray | None = None#
Y_Cs_arr: numpy.ndarray | None = None#
X_arr: numpy.ndarray | None = None#
species_names: list[str] | None = None#
Fo_D_arr: numpy.ndarray | None = None#
h_conv_arr: numpy.ndarray | None = None#
alpha_arr: numpy.ndarray | None = None#
u_arr: numpy.ndarray | None = None#
k_arr: numpy.ndarray | None = None#
Re_arr: numpy.ndarray | None = None#
Pr_arr: numpy.ndarray | None = None#
property reactor: bloc.reactors.PFRWallProfile#

The single PFRWallProfile/TubeFurnace reactor (used by plot helpers).

property mass_flow_rate: float#

Mass flow rate [kg/s] of the driven reactor.

property network#

Self-reference kept for backward compatibility with diagnostics readers.

property time: float#

Residence time [s] of the Lagrangian march (0 before advance).

property preconditioner#

Preconditioner is managed inside march_lagrangian_parcel; exposed for symmetry.

solve_steady()#

Delegate to advance_to_steady_state() for Boulder solve_steady.

advance(t=1.0)#

Advance to t; for a Lagrangian tube this runs to the outlet.

advance_to_steady_state()#

Run the fixed-checkpoint parcel march from inlet to outlet.

The full axial cantera.SolutionArray is attached to the reactor as _states (consumed by Boulder’s stage-state collector and spatial_series_fn). Idempotent: a second call is a no-op.

property states: cantera.SolutionArray | None#

Axial Lagrangian profile (satisfies Boulder’s stage-state collector).

property scalars: Dict[str, Any]#

Tube-furnace scalars (wall energy budget, residence time, meta).

draw(title='Tube Furnace Wall Temperature Profile', n_points=200)#

Return a schematic matplotlib Figure of the tube-furnace geometry.

Upper panel: colour-coded tube rectangle (cold→hot→cold). Lower panel: wall (and, post-advance, gas) temperature profile. Does not require advance() — relies only on wall_T_fn/total_length.

plot_temperature_profile(title='Temperature Profile Tube Furnace')#

Plot gas + wall temperature vs position with a residence-time axis.

Requires advance_to_steady_state() to have been called.

plot_species_profiles(species=None, title='Species Profile Tube Furnace')#

Plot mole-fraction profiles vs position with a residence-time axis.

Requires advance_to_steady_state() to have been called. When species is None, the six species with the highest peak mole fraction (≥ 0.1 %) are selected automatically.

class bloc.reactor_models.PFRGasTemperatureProfileNet(reactors, meta=None, *, T_gas_fn=None, total_length=None, n_points=200, rtol=0.0001, atol=1e-12)#

Stage network driving a PFRGasTemperatureProfile.

Imposes a prescribed gas temperature profile T_gas(x) on a closed Lagrangian parcel: the energy equation is disabled and the temperature is reset to T_gas(x) at every spatial checkpoint, so only the kinetics evolve. This is the Dirichlet-on-gas counterpart of the wall-driven PFRWallProfileNet.

The public surface is duck-typed to Boulder’s CustomStageNetwork (time, states, scalars, advance, advance_to_steady_state) and records the axial temperature and species profiles consumed by the calculation note and the plot helpers.

Parameters:
  • reactors – Iterable containing exactly one PFRGasTemperatureProfile reactor.

  • meta – Geometry/profile meta dict (populated by _build_pfr_gas_temperature_profile()). Must contain total_length and the trapezoidal-profile keys (T_gas_K, T_ambient_K, entry_leg, exit_leg, entry_zone, plateau_zone) or an explicit T_gas_profile dict.

  • T_gas_fn – Standalone API: pass an explicit T_gas(x) callable and tube length instead of the STONE meta dict (used by examples and tests).

  • total_length – Standalone API: pass an explicit T_gas(x) callable and tube length instead of the STONE meta dict (used by examples and tests).

  • n_points – Standalone API: pass an explicit T_gas(x) callable and tube length instead of the STONE meta dict (used by examples and tests).

  • rtol – Integrator tolerances forwarded to march_lagrangian_parcel().

  • atol – Integrator tolerances forwarded to march_lagrangian_parcel().

reactors#
x_arr: numpy.ndarray | None = None#
t_arr: numpy.ndarray | None = None#
T_gas_arr: numpy.ndarray | None = None#
Y_Cs_arr: numpy.ndarray | None = None#
X_arr: numpy.ndarray | None = None#
species_names: list | None = None#
property reactor: bloc.reactors.PFRGasTemperatureProfile#

The single PFRGasTemperatureProfile reactor (used by plot helpers).

property mass_flow_rate: float#

Mass flow rate [kg/s] of the driven reactor.

property network#

Self-reference kept for symmetry with PFRWallProfileNet readers.

property time: float#

Residence time [s] of the Lagrangian march (0 before advance).

property preconditioner#

Preconditioner is managed inside march_lagrangian_parcel.

solve_steady()#

Delegate to advance_to_steady_state() for Boulder solve_steady.

advance(t=1.0)#

Advance to t; for a Lagrangian tube this runs to the outlet.

advance_to_steady_state()#

Run the fixed-checkpoint parcel march with the gas temperature imposed.

Idempotent: a second call is a no-op.

property states: cantera.SolutionArray | None#

Axial Lagrangian profile (satisfies Boulder’s stage-state collector).

property scalars: Dict[str, Any]#

Imposed-gas-T scalars (residence time + meta).

plot_temperature_profile(title='Imposed Gas Temperature Profile')#

Plot the imposed gas temperature T_gas(x) vs position [degC].

plot_species_profiles(species=None, title='Species Profiles (imposed gas T)')#

Plot mole-fraction profiles vs position with a residence-time axis.

When species is None, the six species with the highest peak mole fraction (≥ 0.1 %) are selected automatically.

bloc.reactor_models.HeatFluxFactory#
bloc.reactor_models.residence_states_to_series(states)#

Serialize a closed CSTR residence-time profile for the Plots tab.

Parameters:

states – Profile with extra t [s] (physical residence time, not axial position).

Returns:

Keys: t, T, P, X, Y, is_residence. Arrays are plain Python lists for JSON safety. is_spatial is intentionally absent so Boulder does not label the axis as position [m].

Return type:

dict

bloc.reactor_models.states_to_series(states, fbs_convergence=None)#

Serialize a spatial SolutionArray profile to a JSON-compatible dict.

Parameters:
  • states – Converged spatial profile with custom x (position, m) and t (residence time, s) extra columns set by _march_pfr_to_length().

  • fbs_convergence – Per-FBS-iteration list of Phi_kW values, or None.

Returns:

Keys: x, t, T, P, X, Y, is_spatial, fbs_convergence. All arrays are plain Python lists for JSON safety.

Return type:

dict

class bloc.reactor_models.PFRHomogeneousShellNet(reactors, meta=None)#

Bases: cantera.ReactorNet

ReactorNet subclass implementing Forward-Backward Sweep for a PFR stage.

Overrides advance() to run the FBS heat-loss convergence loop. The spatial integration is performed by _spatial_ode_pass(), which uses the PFRHomogeneousShell reactor directly as the ODE carrier — no auxiliary gas or reactor objects are created. For each pass, a fresh ReactorNet wraps the PFRHomogeneousShell so CVODE always starts from the correct (heat-loss-corrected) inlet state. The converged spatial profile is stored on the reactor as _states for downstream trajectory collection.

Parameters:
  • reactors – List of reactors to integrate. Must contain exactly one PFRHomogeneousShell.

  • meta (dict, optional) – Geometry and insulation parameters for the FBS loop, as produced by _build_pfr_homogeneous_shell().

MAX_ITER: int = 10#
CONVERGENCE_TOL: float = 0.01#
advance_to_steady_state()#

Advance to the tube outlet (one nominal time unit).

solve_steady()#

Delegate to advance_to_steady_state() for Boulder solve_steady kind.

Cantera’s default would use the empty reactor list from super().__init__ and fail.

advance(time)#

Run Forward-Backward Sweep and update the PFR reactor state.

Reads the inlet state from self._pfr_reactor.phase (set by the staged solver before this call), then iterates _spatial_ode_pass() with updated heat-loss estimates until convergence or MAX_ITER is reached. In the adiabatic branch a single pass with zero heat flux is performed.

After convergence the wall flux is pinned to the converged scalar value so a downstream visualization network sees the actual radial loss. The spatial profile is written to reactor._states and the scalar to reactor._heat_loss_kW.

Parameters:

time – Nominal advance time [s]. Not used by the length-terminated PFR integration; kept for API compatibility with ReactorNet.

property states: cantera.SolutionArray | None#

Converged FBS spatial profile (satisfies CustomStageNetwork).

property states_as_series: dict | None#

Return the spatial profile as a JSON-serializable dict for the frontend.

property scalars: Dict[str, Any]#

heat loss, geometry, and outlet-state physics quantities.

Outlet T/P/density are read from reactor.phase, which the integrator leaves at the converged outlet after advance().

Type:

FBS scalar outputs

class bloc.reactor_models.PFRThinShellNet(reactors, meta=None)#

Bases: cantera.ReactorNet

Single-pass PFR network for the thin-shell hypothesis.

Heat loss depends on the local gas temperature T(z) so a single forward pass is sufficient — there is no Forward-Backward Sweep. The radial loss enters the ODE through the build-time ct.Wall, whose heat_flux is wired to a callable that reads reactor.phase.T each CVODE eval.

After advance() the wall flux is pinned to the integrated scalar Phi_kW so the post-solve visualization network shows a Wall carrying the actual radial loss.

Parameters:
  • reactors – Stage reactors; must contain exactly one PFRThinShell.

  • meta (dict, optional) – Geometry and thermal parameters (length, diameter, insulation, T_amb …) as produced by _build_pfr_thin_shell().

advance_to_steady_state()#

Advance through the single-pass thin-shell solve.

solve_steady()#

Delegate to advance_to_steady_state() (same pattern as PFRHomogeneousShellNet).

advance(time)#

Run a single-pass thin-shell solve and update the reactor state.

Parameters:

time – Nominal advance time [s]. Not used by the length-terminated integration; kept for API compatibility with ReactorNet.

property states: cantera.SolutionArray | None#

Spatial profile (satisfies CustomStageNetwork).

property states_as_series: dict | None#

Return the spatial profile as a JSON-serializable dict for the frontend.

property scalars: Dict[str, Any]#

heat loss, geometry, and outlet-state physics quantities.

Outlet T/P/density are read from reactor.phase, which the integrator leaves at the outlet after advance().

Type:

Scalar outputs

bloc.reactor_models.compute_tube_furnace_kpis(sim)#

Compute tube-furnace engineering KPIs from a solved STONE Simulation.

Bridges the STONE/Boulder path to the same output key set as bloc.yaml_utils._tf_kpi_extractor() (the ctwrap path), so that run_yaml_scenarios.py and the legacy ctwrap runner produce the same Calculation Note Excel.

Looks for a PFRWallProfileNet in sim.network.networks (the stage solver mapping). All scalars are read from that network, which is populated during advance_to_steady_state.

Parameters:

sim (bloc.simulation_builder.Simulation) – Solved STONE simulation returned by bloc.simulation_builder.build_simulation_from_yaml().

Returns:

Flat scalar dict with keys matching bloc.yaml_utils.TF_OUTPUT_VARIABLE_MAP (T_outlet_C, E_conv_J, E_rad_J, P_conv_avg_W, P_rad_avg_W, SEO_MJ_kg, n_steps, h_conv_avg_W_m2_K, Fo_D_min, diameter_mm, t_res_s) plus per-species mole (X_*) and mass (Y_*) fractions. Returns an empty dict when no PFRWallProfileNet is found in sim.network.networks.

Return type:

dict

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

Bases: bloc.reactors.PFRWallProfile

Industrial Tube Furnace model.

A plug-flow reactor heated by an externally prescribed wall-temperature profile T_wall(x) using forced convection and grey-gas radiation (see PFRWallProfile).

STONE kind: TubeFurnace. For the STONE YAML schema see TubeFurnaceSchema.

Use this kind for any electrically or indirectly heated tube where the wall temperature profile is known or measured. The Carbon-Black tube furnace (Mei-2019 validation) is the canonical example.

See also

PFRWallProfile

underlying numerical engine.

RefractoryReactor

insulated thick-shell reactor (SPRING CGR).

NETWORK_CLASS: ClassVar[type | None] = None#
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.

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.reactor_models.RefractoryReactor(gas, *args, **kwargs)#

Bases: bloc.reactors.PFRHomogeneousShell

Industrial Carbon Growth Reactor (CGR) / refractory-lined vessel.

A plug-flow reactor with homogeneous-shell radial heat loss through an insulation resistance stack, converged by Forward-Backward Sweep (see PFRHomogeneousShell).

STONE kind: RefractoryReactor. Node id in SPRING YAML: cgr (the CGR in SPRING A3/A4).

Use this kind for thick-walled insulated industrial reactors where axial wall conduction smears temperature gradients.

See also

PFRHomogeneousShell

underlying numerical engine.

QuartzTubeReactor

thin-shell variant for lab reactors.

NETWORK_CLASS: ClassVar[type | None] = None#
mass_and_power(qm_torch, T_torch_input_C, X_torch, qm_2nd_inj, T_2nd_inj_C, X_2nd_inj, P_bar, T_amb_C, gas)#

Compute preheat recovery and residual heat based on reactor states.

Industrial-design helper for SPRING thermal-recovery KPIs (consumed by bloc.spring_kpi.compute_spring_kpis()).

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

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.reactor_models.QuartzTubeReactor(gas, *args, **kwargs)#

Bases: bloc.reactors.PFRThinShell

Industrial quartz-tube / lab-tube reactor.

A plug-flow reactor with thin-shell local-temperature radial heat loss (see PFRThinShell). Single forward pass; no Forward-Backward Sweep.

STONE kind: QuartzTubeReactor.

Use this kind for quartz, fused-silica, or other thin-walled lab reactors where axial wall conduction is negligible.

See also

PFRThinShell

underlying numerical engine.

RefractoryReactor

thick-shell variant for industrial vessels.

NETWORK_CLASS: ClassVar[type | None] = None#
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

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.reactor_models.ContinuousMixingReactor(gas, *args, **kwargs)#

Bases: bloc.reactors.PSR

Industrial Torch Mixing Reactor (TMR) / multi-inlet well-mixed vessel.

A perfectly stirred reactor (see PSR) used to model the turbulent mixing of two or more streams at the outlet of a plasma torch.

STONE kind: ContinuousMixingReactor. Node id in SPRING YAML: tmr (the TMR in SPRING A3/A4).

Use this kind for any well-mixed stage where axial gradients are negligible. Assume Da_mix ≫ 1: mixing inside the vessel is faster than chemistry so the CSTR approximation holds.

Network topology#

Stage YAML: OPEN — multi-inlet MFCs, outlet PC/MFC; solved with ReactorNet and advance_to_steady_state (default).

Inner physics: OPEN CSTR. t_res_s sizes volume via Bloc post-build (_post_build_design_volumes(), V = τ·ṁ/ρ); it is the design residence time, not the stage advance_time.

See also

PSR

underlying numerical engine.

InstantaneousMixingReactor

closed-parcel alternative when inlet streams are pre-mixed algebraically before chemistry.

NETWORK_CLASS = None#
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.reactor_models.InstantaneousMixingNet(reactors, meta=None)#

Bases: cantera.ReactorNet

ReactorNet subclass for instantaneous-mix + closed-τ chemistry.

Mirrors TorchInstantaneousHeatingNet: the stage network holds no live ODE reactors. advance() only validates that _post_build_design_volumes() has pre-computed the outlet profile.

Parameters:
  • reactors – Stage reactor list. Must contain exactly one InstantaneousMixing instance.

  • meta (dict, optional) – Parameters from the STONE builder, enriched by _post_build_design_volumes(): t_res_s, mass_flow_rate.

advance_to_steady_state()#

Validate precomputed outlet and delegate.

solve_steady()#

Delegate steady solve to advance_to_steady_state().

advance(time)#

Validate precomputed outlet profile from post-build.

Parameters:

time – Nominal advance time [s]; unused, kept for API compatibility.

property states: cantera.SolutionArray | None#

Closed-τ residence-time profile (satisfies CustomStageNetwork).

property scalars: Dict[str, Any]#

Build parameters and outlet-state scalars.

class bloc.reactor_models.InstantaneousMixing(gas, *args, **kwargs)#

Bases: bloc.reactors.PSR

Engine: instantaneous HP mix of two inlet streams + closed-τ chemistry.

Step 1 — inlet streams are combined with constant-HP algebraic mixing (mix_two_streams()), reproducing the Da_mix 1 assumption (turbulent mixing much faster than chemistry).

Step 2 — a closed constant-pressure parcel integrates the ODE from t = 0 to t = t_res_s with no inlet/outlet mass exchange.

This formalises the legacy pattern:

gas_mixed = mix_two_streams(gas_1, qm_1, gas_2, qm_2)
PSR_simu = solve_adiabatic_reactor(qm_total, 0, t_res_s, ...)
_states#

Residence-time profile recorded by solve_premixed().

Type:

ct.SolutionArray or None

NETWORK_CLASS: ClassVar[type | None] = None#
solve_premixed(gas_1, qm_1, gas_2, qm_2, t_res_s, *, verbose=True)#

HP-mix two inlet streams then integrate closed chemistry for t_res_s.

Parameters:
  • gas_1 – Cantera Solution objects for the two inlet streams (same or different mechanisms; both must be on the stage mechanism before calling — species mapping is the caller’s responsibility).

  • gas_2 – Cantera Solution objects for the two inlet streams (same or different mechanisms; both must be on the stage mechanism before calling — species mapping is the caller’s responsibility).

  • qm_1 – Mass flow rates [kg/s] of each stream.

  • qm_2 – Mass flow rates [kg/s] of each stream.

  • t_res_s – Chemical residence time [s]; CVODE integrates 0 → t_res_s on a closed parcel (no MFC flux during integration).

  • verbose – Print outlet state summary.

Returns:

  • dict with keys ````”gas”```` (Solution) and :py:class:``”states”:py:class:``

  • (:class:`~cantera.SolutionArray with extra column t [s]).`

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.reactor_models.InstantaneousMixingReactor(gas, *args, **kwargs)#

Bases: InstantaneousMixing

Industrial model: instantaneous mix + closed chemical residence time.

Two inlet streams (e.g. torch primary + secondary injection) are combined with constant-HP algebraic mixing (mix_two_streams()), then chemistry runs for t_res_s in a closed well-mixed parcel.

STONE kind: InstantaneousMixingReactor.

Use when Da_mix ≫ 1 (turbulent mixing time ≪ chemical timescale) and the workflow is a one-shot feedforward chain (not a continuously-fed vessel). For a continuously-fed open CSTR, use ContinuousMixingReactor instead.

Network topology#

Stage YAML: OPEN — two inlet MFCs (source reservoirs or upstream reactor outlets), outlet MFC/PressureController for downstream handoff.

Inner physics: CLOSED parcel. _post_build_design_volumes() gathers both inlet states, calls solve_premixed(), and writes back to reactor.phase + syncState(). No live ODE in the stage ReactorNet.

See also

ContinuousMixingReactor

open CSTR alternative (continuous feed/discharge).

mix_two_streams

algebraic HP mixer used in step 1.

NETWORK_CLASS: ClassVar[type | None] = None#
solve_premixed(gas_1, qm_1, gas_2, qm_2, t_res_s, *, verbose=True)#

HP-mix two inlet streams then integrate closed chemistry for t_res_s.

Parameters:
  • gas_1 – Cantera Solution objects for the two inlet streams (same or different mechanisms; both must be on the stage mechanism before calling — species mapping is the caller’s responsibility).

  • gas_2 – Cantera Solution objects for the two inlet streams (same or different mechanisms; both must be on the stage mechanism before calling — species mapping is the caller’s responsibility).

  • qm_1 – Mass flow rates [kg/s] of each stream.

  • qm_2 – Mass flow rates [kg/s] of each stream.

  • t_res_s – Chemical residence time [s]; CVODE integrates 0 → t_res_s on a closed parcel (no MFC flux during integration).

  • verbose – Print outlet state summary.

Returns:

  • dict with keys ````”gas”```` (Solution) and :py:class:``”states”:py:class:``

  • (:class:`~cantera.SolutionArray with extra column t [s]).`

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.reactor_models.PlasmaTorchInstantaneousHeating(gas, *args, **kwargs)#

Bases: TorchInstantaneousHeating

Industrial Plasma Torch model — instantaneous-heating variant.

Instantaneous enthalpy jump (plasma power input at eta * P_torch) followed by adiabatic constant-pressure kinetics for t_res_s (see TorchInstantaneousHeating).

STONE kind: PlasmaTorchInstantaneousHeating.

The InstantaneousHeating suffix distinguishes this model from future plasma-torch implementations that may use a different heating model (e.g., arc-discharge, spatially-resolved, or multi-temperature).

Use this kind for all plasma torch stages where the heating is quasi-instantaneous compared to the chemical relaxation time.

See also

TorchInstantaneousHeating

underlying physics class.

NETWORK_CLASS: ClassVar[type | None] = None#
solve_adiabatic(mdot, power_kW, t_res_s, verbose=True)#

Solve adiabatic kinetics using this reactor’s current inlet state.

Adds torch power as an enthalpy jump and integrates one adiabatic pass for t_res_s using a fresh type(self)(gas, clone=False) carrier.