Skip to content

Reactors & reactive separations

Equilibrium and kinetic reactors (Gibbs/stoichiometric equilibrium, CSTR, PFR) and reactive separations that solve phase and reaction equilibrium together (reactive flash and reactive distillation).

See the reactions & reactors guide for worked examples.

Reactors

reactors

Differentiable reactor unit operations with material and energy balances.

These blocks turn the reaction thermochemistry and kinetics in fugacio.thermo into flowsheet units that consume and produce Stream objects, just like the separation units in fugacio.sim.units. Two families are provided:

  • Equilibrium / stoichiometric reactors: the conversion is set by chemical equilibrium (fugacio.thermo.reaction_equilibrium.equilibrium) or by a specified extent/conversion; no rate law is needed.
  • Kinetic reactors: cstr, pfr, and batch_reactor integrate the rate laws of fugacio.thermo.kinetics over reactor volume (CSTR/PFR) or time (batch).

Every reactor supports an energy balance: run it isothermally at a specified temperature and the heat duty required to hold that temperature is returned (it carries the heat of reaction), or run it adiabatic=True and the outlet temperature is solved from an adiabatic enthalpy balance. The enthalpy bookkeeping in legacy calls is the ideal-gas absolute enthalpy Hf_i(298) + integral Cp_i dT that underlies fugacio.thermo.reactions.delta_h_rxn, so reaction heat and sensible heat are accounted for consistently. Kinetic-reactor concentrations use the ideal-gas relation c_i = y_i P / (R T) in legacy calls. Passing a property package or ReactionSet to equilibrium_reactor, cstr, or pfr selects the checked common-package implementation with phase-specific properties. Its ReactionResult also retains acceptance reports and profiles.

Because the underlying solves (equilibrium root-finds, the CSTR Newton system, the explicit RK4 marches) are differentiable, a reactor's conversion, outlet temperature, and duty are differentiable in the feed, the operating conditions, and the reaction/kinetic parameters, ready for gradient-based design.

Classes:

Name Description
ReactorResult

Outcome of a reactor calculation.

Functions:

Name Description
equilibrium_reactor

Reactor whose outlet is the chemical-equilibrium composition.

stoichiometric_reactor

Reactor with a specified extent or key-reactant conversion (no equilibrium).

cstr

Continuous stirred-tank reactor (perfectly mixed) at steady state.

pfr

Plug-flow reactor: integrate the species balances along the reactor volume.

batch_reactor

Constant-volume batch reactor: integrate the mole balances over time.

conversion

Fractional conversion of a feed component, (n_in - n_out) / n_in.

ReactorResult

Bases: NamedTuple

Outcome of a reactor calculation.

Attributes:

Name Type Description
outlet Stream

Product Stream (at the reactor outlet temperature, solved for adiabatic operation).

duty Array

Heat duty (W) to hold an isothermal reactor at temperature; positive means heat added. Zero for an adiabatic reactor.

extent Array

Extent of each reaction (mol/s for flow reactors, mol for batch), shape (n_reactions,).

equilibrium_reactor

equilibrium_reactor(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction] | ReactionSet,
    *,
    model: Model = None,
    phase: str = "vapor",
    rate_basis: str = "concentration",
    check: bool = True,
    t_out: ArrayLike | None = None,
    adiabatic: bool = False,
    basis: str = "ideal-gas",
    eos: CubicEOS = PR,
    kij: Array | None = None,
    tol: float = 1e-10,
    max_iter: int = 80,
) -> ReactorResult | ReactionResult

Reactor whose outlet is the chemical-equilibrium composition.

Isothermal (default, or with t_out): the equilibrium composition at the operating temperature is found and the duty to hold that temperature returned. Adiabatic (adiabatic=True): the extents and outlet temperature are solved together from the equilibrium conditions plus an adiabatic energy balance (ideal-gas basis).

Parameters:

Name Type Description Default
feed Stream

Inlet stream; reactions must be defined over feed.components.

required
reactions Reaction | Sequence[Reaction] | ReactionSet

One reaction or several sharing the feed's component ordering.

required
t_out ArrayLike | None

Isothermal operating temperature (K); defaults to feed.t.

None
adiabatic bool

Solve the outlet temperature from an adiabatic balance instead.

False
basis str

"ideal-gas" or "phi" (EOS fugacity coefficients) for the isothermal equilibrium; adiabatic operation uses the ideal-gas basis.

'ideal-gas'
eos CubicEOS

Cubic EOS used when basis="phi".

PR
kij Array | None

Optional binary interaction matrix for the EOS.

None
tol float

Convergence tolerance on the reaction extents.

1e-10
max_iter int

Maximum number of solver iterations.

80
model Model

Common property package; enables the checked reactor implementation.

None
phase str

Homogeneous reaction phase for a newly constructed reaction set.

'vapor'
rate_basis str

Kinetic input convention for a newly constructed reaction set.

'concentration'
check bool

Raise on failed concrete package-based results.

True

Returns:

Type Description
ReactorResult | ReactionResult

A ReactionResult with a package or ReactionSet, otherwise a legacy ReactorResult.

stoichiometric_reactor

stoichiometric_reactor(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction],
    *,
    extent: ArrayLike | None = None,
    conversion: ArrayLike | None = None,
    t_out: ArrayLike | None = None,
    adiabatic: bool = False,
    model: Model = None,
    t_lo: float = 200.0,
    t_hi: float = 6000.0,
) -> ReactorResult

Reactor with a specified extent or key-reactant conversion (no equilibrium).

Provide exactly one of extent (per reaction, mol/s) or conversion (a single-reaction fractional conversion of its limiting reactant). The outlet is n = n_feed + extent @ nu; the energy balance is the same isothermal-duty / adiabatic-temperature treatment as equilibrium_reactor.

Pass model to use the flowsheet package's sensible and residual enthalpies plus ideal-gas formation enthalpies. With no model, the historical ideal-gas energy basis is retained. Reference-fluid packages have a different energy reference and aren't supported for reaction thermochemistry.

Raises:

Type Description
ValueError

if not exactly one of extent / conversion is given, or conversion is used with more than one reaction.

cstr

cstr(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction] | ReactionSet,
    rate_laws: Any = None,
    volume: ArrayLike | None = None,
    *,
    model: Model = None,
    phase: str = "vapor",
    rate_basis: str = "concentration",
    check: bool = True,
    t_out: ArrayLike | None = None,
    adiabatic: bool = False,
    tol: float = 1e-10,
    max_iter: int = 100,
) -> ReactorResult | ReactionResult

Continuous stirred-tank reactor (perfectly mixed) at steady state.

Solves the steady-state mole balance F_out = F_in + V (r . Nu) with the outlet-condition rates r (one per reaction, from rate_laws) and ideal-gas concentrations. Isothermal by default (duty returned); with adiabatic=True the outlet temperature is solved jointly with the flows.

Parameters:

Name Type Description Default
feed Stream

Inlet stream (feed.n are molar flows, mol/s).

required
reactions Reaction | Sequence[Reaction] | ReactionSet

Reaction(s) over feed.components.

required
rate_laws Any

One rate law per reaction; omit when reactions is a ReactionSet.

None
volume ArrayLike | None

Reactor volume (m^3).

None
t_out ArrayLike | None

Isothermal temperature (K); defaults to feed.t.

None
adiabatic bool

Solve the outlet temperature from the energy balance.

False
tol float

Convergence tolerance on the steady-state mole balance.

1e-10
max_iter int

Maximum number of Newton iterations.

100
model Model

Common property package; enables the checked reactor implementation.

None
phase str

Homogeneous reaction phase for a newly constructed reaction set.

'vapor'
rate_basis str

Kinetic input convention for a newly constructed reaction set.

'concentration'
check bool

Raise on failed concrete package-based results.

True

pfr

pfr(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction] | ReactionSet,
    rate_laws: Any = None,
    volume: ArrayLike | None = None,
    *,
    model: Model = None,
    phase: str = "vapor",
    rate_basis: str = "concentration",
    check: bool = True,
    integration_rtol: float = 1e-05,
    integration_atol: float = 1e-08,
    t_out: ArrayLike | None = None,
    adiabatic: bool = False,
    steps: int = 200,
) -> ReactorResult | ReactionResult

Plug-flow reactor: integrate the species balances along the reactor volume.

Marches dF_i/dV = (r . Nu)_i (ideal-gas concentrations, isobaric) from the feed to volume with explicit RK4. Isothermal by default; with adiabatic=True the temperature is integrated alongside via dT/dV = -(sum_j r_j DH_rxn,j) / (sum_i F_i Cp_i).

A package or ReactionSet selects the checked common-package path, including step doubling and phase-specific caloric properties. With a ReactionSet, omit rate_laws and specify volume by keyword.

batch_reactor

batch_reactor(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction],
    rate_laws: Any,
    volume: ArrayLike,
    time: ArrayLike,
    *,
    t_out: ArrayLike | None = None,
    adiabatic: bool = False,
    steps: int = 200,
) -> ReactorResult

Constant-volume batch reactor: integrate the mole balances over time.

Here feed.n are the initial moles (mol). Marches dN_i/dt = V (r . Nu)_i with concentrations c_i = N_i / V by explicit RK4. Isothermal by default; with adiabatic=True the temperature is integrated via (sum_i N_i Cp_i) dT/dt = -V sum_j r_j DH_rxn,j. The returned extent is in moles and duty (isothermal) is the cumulative heat (J).

conversion

conversion(
    feed: Stream, outlet: Stream, component_index: int
) -> Array

Fractional conversion of a feed component, (n_in - n_out) / n_in.

Reactive separations

reactive

Reactive separations: simultaneous chemical reaction and phase equilibrium.

Real separation equipment often runs with a reaction happening inside it: the whole point of reactive distillation is to push a reaction past its equilibrium limit by continuously pulling products into a different phase. This module adds common-package units alongside the legacy gamma-phi approximation:

  • reactive_flash: an isothermal flash in which the liquid simultaneously reaches chemical equilibrium (one or more reactions) and phase equilibrium (vapour-liquid). The extents of reaction and the V/L split are solved together, reusing the validated gamma-phi flash and the ideal-gas reaction thermochemistry. Works for any net mole change.

  • reactive_column: energy-balanced MESH with a common property package and volumetric rates on a declared liquid or vapor phase.

  • reactive_distillation: a legacy multistage approximation (Wang-Henke bubble-point, constant molar overflow) with a rate-based reaction source on each reactive stage: S_{j,i} = H_j * sum_r nu_{r,i} * rate_r(T_j, a_j) with the liquid-phase activities a_i = x_i gamma_i and a per-stage molar holdup H_j. For an equimolar reaction (sum_i nu_i = 0, the dominant reactive distillation class: esterification, transesterification, metathesis, isomerisation) the source conserves total moles. Constant molar overflow remains a material-balance approximation; it does not establish energy closure.

The reaction equilibrium constant K(T) comes from the ideal-gas formation data in fugacio.thermo.reactions; at vapour-liquid equilibrium the component fugacities are equal across phases, so the ideal-gas-referenced equilibrium is written consistently in terms of the liquid activities a_i = x_i gamma_i f_i^{0,L}/P_ref. Every result is a differentiable Stream (or profile of them): conversions, product purities, and stage profiles carry gradients with respect to the feed, operating conditions, the activity-model parameters, and the kinetic/thermochemical parameters.

Classes:

Name Description
ReactiveFlashResult

Outcome of a simultaneous reaction + vapour-liquid flash.

ReactiveColumnResult

Converged profile and products of a reactive distillation column.

Functions:

Name Description
reactive_flash

Solve isothermal chemical equilibrium coupled to a package PT flash.

reactive_distillation

Rate-based reactive distillation by the gamma-phi Wang-Henke method (CMO).

reactive_column

Solve reactive MESH distillation with formation-consistent stage energy.

ReactiveFlashResult

Bases: NamedTuple

Outcome of a simultaneous reaction + vapour-liquid flash.

Attributes:

Name Type Description
vapor Stream

Vapour product Stream.

liquid Stream

Liquid product Stream.

beta Array

Vapour fraction (mol vapour / mol after reaction).

extent Array

Equilibrium extent of each reaction (mol/s), shape (n_reactions,).

duty Array

External heat input, including formation energy exactly once (W).

generation Array

Component generation (mol/s).

report SolveReport

Chemical solve and final phase-solve acceptance.

phase_report SolveReport

Actual final PT flash iteration report.

ReactiveColumnResult

Bases: NamedTuple

Converged profile and products of a reactive distillation column.

Attributes:

Name Type Description
t Array

Stage temperatures (K), top stage first, shape (n_stages,).

x Array

Liquid mole fractions, shape (n_stages, n_components).

y Array

Vapour mole fractions, shape (n_stages, n_components).

distillate Stream

Distillate product Stream.

bottoms Stream

Bottoms product Stream.

reflux Array

Reflux ratio used.

generation Array

Net mole generation by reaction on each stage (mol/s), shape (n_stages, n_components).

reactive_flash

reactive_flash(
    feed: Stream,
    reactions: Reaction | Sequence[Reaction] | ReactionSet,
    t: ArrayLike,
    p: ArrayLike,
    model: Model,
    *,
    tol: float = 1e-09,
    max_iter: int = 100,
    check: bool = True,
) -> ReactiveFlashResult

Solve isothermal chemical equilibrium coupled to a package PT flash.

All property and thermochemical parameters pass explicitly through the implicit solve, including under JIT, JVP, and VJP. The reaction quotient uses the liquid fugacity when liquid is present, otherwise the vapor fugacity. Absent phases never determine chemical equilibrium. Formation enthalpy is included once in the reported heat input (W).

Parameters:

Name Type Description Default
feed Stream

Inlet material and thermal state.

required
reactions Reaction | Sequence[Reaction] | ReactionSet

One reaction, a sequence, or a validated ReactionSet.

required
t ArrayLike

Drum temperature (K).

required
p ArrayLike

Drum pressure (Pa).

required
model Model

Common property package or compatible equilibrium model.

required
tol float

Chemical-equilibrium residual tolerance.

1e-09
max_iter int

Root iteration cap.

100
check bool

Raise for failed concrete solves; compiled callers inspect report.

True

Returns:

Type Description
ReactiveFlashResult

Products preserving phase inventories, reaction extents, heat input,

ReactiveFlashResult

generation, and the actual chemical solve report. Process-case audits

ReactiveFlashResult

independently assess product equilibrium, stability, and balances.

reactive_distillation

reactive_distillation(
    feed: Stream,
    model: GammaPhiModel,
    reactions: Reaction | Sequence[Reaction],
    rate_laws: Any,
    holdup: ArrayLike,
    n_stages: int,
    feed_stage: int,
    reflux: ArrayLike,
    distillate_rate: ArrayLike,
    *,
    reactive_stages: tuple[int, int] | None = None,
    q: ArrayLike = 1.0,
    t_top: ArrayLike | None = None,
    t_bottom: ArrayLike | None = None,
    t_min: float = 200.0,
    t_max: float = 700.0,
    tol: float = 1e-11,
    max_iter: int = 600,
) -> ReactiveColumnResult

Rate-based reactive distillation by the gamma-phi Wang-Henke method (CMO).

A total condenser sits above stage 1 and a partial reboiler is stage n_stages; one feed of quality q enters at feed_stage (1-indexed). Each stage equilibrates by the gamma-phi bubble-point method, and on every reactive stage a rate-based source H * sum_r nu_r rate_r(T, a) (liquid activities a_i = x_i gamma_i, molar holdup H) is added to the component balance. The whole profile is converged by the Wegstein tear solver, so the products and profiles are differentiable with respect to reflux, distillate_rate, holdup, the feed, and the model/kinetic parameters.

This legacy screening model has no stage energy equations. Equimolar stoichiometry does not establish energy closure. Use reactive_column for energy-balanced design and non-equimolar reactions.

Parameters:

Name Type Description Default
feed Stream

Feed stream.

required
model GammaPhiModel

Gamma-phi property model for the (non-ideal) liquid.

required
reactions Reaction | Sequence[Reaction]

One reaction or several over feed.components.

required
rate_laws Any

One rate law per reaction (rate(T, a); activities passed as the concentration argument for a pseudo-homogeneous, activity-based rate).

required
holdup ArrayLike

Liquid molar holdup H on each reactive stage (mol).

required
n_stages int

Number of equilibrium stages including the reboiler.

required
feed_stage int

1-indexed feed stage.

required
reflux ArrayLike

Reflux ratio L/D.

required
distillate_rate ArrayLike

Distillate molar flow (mol/s).

required
reactive_stages tuple[int, int] | None

Inclusive 1-indexed (first, last) reactive stage range; defaults to all interior stages (2, n_stages - 1).

None
q ArrayLike

Feed thermal quality (1 = saturated liquid).

1.0
t_top ArrayLike | None

Optional initial top-stage temperature (K).

None
t_bottom ArrayLike | None

Optional initial bottom-stage temperature (K).

None
t_min float

Lower per-stage temperature clamp (K).

200.0
t_max float

Upper per-stage temperature clamp (K).

700.0
tol float

Convergence tolerance for the outer fixed point.

1e-11
max_iter int

Maximum number of outer sweeps.

600

Returns:

Type Description
ReactiveColumnResult

A ReactiveColumnResult.

reactive_column

reactive_column(
    feeds: Sequence[ColumnFeed],
    n_stages: int,
    reactions: ReactionSet,
    reaction_volumes: ArrayLike,
    **kwargs: Any,
) -> RigorousColumnResult

Solve reactive MESH distillation with formation-consistent stage energy.

Arguments follow rigorous_column. Volumes are reacting-phase m^3; a scalar selects interior stages and a vector explicitly selects each stage. This rigorous entry point replaces CMO assumptions for process design. The older reactive_distillation function retains its historical molar-holdup screening convention and should only be used for comparisons with those old examples.

Checked common-package reactors

reaction_units

Checked, property-package-consistent steady-state reaction calculations.

Reacting units use one declared homogeneous phase. Their energy includes formation enthalpies exactly once. A phase change is a rejected homogeneous reactor calculation; a reactive flash or reactive MESH column handles coupled reaction and phase separation explicitly.

Classes:

Name Description
ReactionResult

Reaction products, actual numerical reports, balances, and retained profiles.

Functions:

Name Description
reaction_parameter_validity

Check finite reaction parameters and element conservation under JAX too.

reaction_reactor

Solve a homogeneous equilibrium reactor, CSTR, or PFR with a common package.

ReactionResult

Bases: NamedTuple

Reaction products, actual numerical reports, balances, and retained profiles.

Extents and generation are in mol/s; duty is W, positive into the fluid. integration_error is a dimensionless step-doubling error ratio for PFRs (accepted at <= 1), zero for algebraic reactors. Profiles use the refined axial grid, including the inlet. Acceptance checks homogeneous phase compatibility in addition to balance closure, but a finite PT flash isn't an independent global phase-stability proof. Case runs add the existing stream stability audit.

Methods:

Name Description
check

Raise with the retained report for a rejected concrete calculation.

Attributes:

Name Type Description
converged Array

Whether numerical, physical-domain, and integration checks passed.

converged property

converged: Array

Whether numerical, physical-domain, and integration checks passed.

check

check() -> None

Raise with the retained report for a rejected concrete calculation.

reaction_parameter_validity

reaction_parameter_validity(system: ReactionSet) -> Array

Check finite reaction parameters and element conservation under JAX too.

reaction_reactor

reaction_reactor(
    feed: Stream,
    system: ReactionSet,
    *,
    kind: str = "equilibrium",
    model: Model = None,
    volume: float | Array = 1.0,
    t_out: float | Array | None = None,
    duty: float | Array | None = None,
    dp: float | Array = 0.0,
    steps: int = 64,
    integration_rtol: float = 1e-05,
    integration_atol: float = 1e-08,
    tol: float = 1e-10,
    max_iter: int = 100,
    check: bool = True,
) -> ReactionResult

Solve a homogeneous equilibrium reactor, CSTR, or PFR with a common package.

Parameters:

Name Type Description Default
feed Stream

Inlet material and phase state.

required
system ReactionSet

Validated reactions, kinetic laws, phase, and rate basis.

required
kind str

equilibrium, cstr, or pfr.

'equilibrium'
model Model

Property package; defaults to Peng-Robinson.

None
volume float | Array

Reacting-phase volume (m^3), required positive for kinetic units.

1.0
t_out float | Array | None

Isothermal temperature. Omission retains inlet T if duty is omitted.

None
duty float | Array | None

Specified heat input (W); zero selects adiabatic operation. Mutually exclusive with t_out. PFR heat is distributed uniformly.

None
dp float | Array

Nonnegative pressure loss (Pa); PFR pressure decreases linearly.

0.0
steps int

Coarse PFR mesh size; a 2*steps mesh supplies the returned solution.

64
integration_rtol float

Relative step-doubling and energy error scale.

1e-05
integration_atol float

Absolute step-doubling scale in normalized coordinates.

1e-08
tol float

Scaled nonlinear residual tolerance.

1e-10
max_iter int

Newton iteration cap.

100
check bool

Raise on a rejected concrete result; traced callers inspect report.

True

Returns:

Type Description
ReactionResult

A ReactionResult retaining failed primals and nonfinite failed derivatives.

ReactionResult

PFRs compare coarse/refined extents and temperatures, independently audit

ReactionResult

energy, and reject negative inventory without clipping the final solution.