Skip to content

Equation-oriented flowsheeting

The equation-oriented (EO) engine assembles every unit's equations, the stream connectivity, the recycles, and any design specs into one residual system and solves it simultaneously by Newton's method, with the Jacobian supplied exactly by JAX autodiff and the converged solution differentiable by the implicit function theorem.

See the equation-oriented flowsheeting guide for worked examples. Import the public surface from the subpackage root:

from fugacio.sim.eo import EOFlowsheet, Flash, Heater, Mixer, optimize_flowsheet_eo

Flowsheet, solution & DOF analysis

flowsheet

Simultaneous execution of physical units and custom residual blocks.

Topology uses ProcessGraph validation. ResidualGraph assembles independently compiled blocks into sparse Newton and implicit derivative systems. Built-in blocks use the common physical unit kernels and stream coordinates; custom blocks can introduce residual equations and auxiliary unknowns. Design specifications enter as additional equations and manipulated variables.

Classes:

Name Description
DOFReport

Degrees-of-freedom analysis of an EO flowsheet.

EOSolution

Converged equation-oriented flowsheet solution.

EOFlowsheet

Declarative equation-oriented flowsheet.

DOFReport

Bases: NamedTuple

Degrees-of-freedom analysis of an EO flowsheet.

Attributes:

Name Type Description
n_unknowns int

Total scalar unknowns (internal stream variables + block auxiliaries + freed design-spec variables).

n_equations int

Total scalar equations (block residuals + design specs).

degrees_of_freedom int

n_unknowns - n_equations. Zero means the equation count is balanced; rank and convergence still need checking; positive means under-specified (add specs); negative means over-specified.

per_block dict[str, int]

Equation count contributed by each block (keyed by the block's first outlet name).

EOSolution dataclass

EOSolution(
    streams: dict[str, Stream],
    aux: dict[str, Array],
    specs: dict[str, Array],
    residual_norm: Array,
    n_unknowns: int,
    n_equations: int,
    report: SolveReport,
)

Converged equation-oriented flowsheet solution.

Attributes:

Name Type Description
streams dict[str, Stream]

All named streams (feeds plus solved internal streams).

aux dict[str, Array]

Solved auxiliary variables declared by custom blocks.

specs dict[str, Array]

Solved values of any freed design-spec manipulated variables.

residual_norm Array

Max-norm of the (scaled) residual at the solution.

n_unknowns int

Number of scalar unknowns solved.

n_equations int

Number of scalar equations.

Methods:

Name Description
check

Raise for a concrete failed simultaneous solve.

converged property

converged: Array

Whether the full scaled equation system converged.

check

check() -> None

Raise for a concrete failed simultaneous solve.

EOFlowsheet dataclass

EOFlowsheet(
    scales: Scales | None = None,
    model: Model = None,
    feeds: dict[str, Stream] = dict(),
    blocks: list[Block] = list(),
    specs: list[_DesignSpec] = list(),
    linear_solver: str = "sparse",
)

Declarative equation-oriented flowsheet.

Register feeds and blocks, optionally add design specs, then call solve. Streams are referenced by name; a block's outlet names become the system unknowns and recycles need no special handling (just reuse a downstream stream name as an upstream block's inlet).

Example::

fs = EOFlowsheet()
fs.feed("fresh", fresh_stream)
fs.add(Mixer(inlets=("fresh", "recycle"), outlets=("mixed",)))
fs.add(Flash(inlets=("mixed",), outlets=("vapor", "liquid"), t="T", p="P"))
fs.add(Splitter(inlets=("liquid",), outlets=("recycle", "purge"),
                fractions=("r_recycle",)))
sol = fs.solve({"T": 320.0, "P": 20e5, "r_recycle": [0.5, 0.5]})
product = sol["vapor"]

Attributes:

Name Type Description
scales Scales | None

Residual/variable scales (auto-derived from the feeds by solve when left at the default).

model Model

Property package (see fugacio.sim.models.package_for) used by every block; None selects Peng-Robinson over the feeds' components. Any method class (cubic, gamma-phi, PC-SAFT, reference fluid) can drive the whole simultaneous solve.

Methods:

Name Description
feed

Register a fresh feed stream by name. Returns self for chaining.

add

Register a unit block. Returns self for chaining.

spec

Add a design spec: free parameter manipulated to hit measure = target.

diagnose_structure

Inspect declared incidence without compiling or solving the flowsheet.

degrees_of_freedom

Report the unknown/equation balance for the flowsheet (see DOFReport).

equation_labels

Stable block and equation names in residual assembly order.

diagnose

Inspect the scaled Jacobian at an initial or user-supplied state.

solve_path

Continue operating conditions, reusing only accepted stream guesses.

solve

Solve the flowsheet simultaneously and return all streams.

feed

feed(name: str, stream: Stream) -> EOFlowsheet

Register a fresh feed stream by name. Returns self for chaining.

add

add(block: Block) -> EOFlowsheet

Register a unit block. Returns self for chaining.

spec

spec(
    manipulated: str,
    measure: Measure,
    target: float | Array,
    *,
    init: float | Array,
) -> EOFlowsheet

Add a design spec: free parameter manipulated to hit measure = target.

In EO form a design spec is simply one more equation (measure(streams) - target = 0) and one more unknown (the freed value of manipulated, seeded at init), solved simultaneously with the flowsheet, so coupled specs converge together with no nested loop.

Parameters:

Name Type Description Default
manipulated str

A parameter key read by some block (the degree of freedom). Its value becomes an unknown; the value passed in params for this key, if any, is ignored.

required
measure Measure

Reads the controlled variable from the solved streams.

required
target float | Array

Desired value of the controlled variable.

required
init float | Array

Initial guess for the manipulated variable.

required

Returns:

Type Description
EOFlowsheet

self for chaining.

diagnose_structure

diagnose_structure() -> dict[str, Any]

Inspect declared incidence without compiling or solving the flowsheet.

This identifies disconnected equation/variable counts and reports the coloring cost. Matching is an upper bound on rank. Use diagnose afterward for state-dependent rank and conditioning.

degrees_of_freedom

degrees_of_freedom() -> DOFReport

Report the unknown/equation balance for the flowsheet (see DOFReport).

equation_labels

equation_labels(
    ctx: Context | None = None,
) -> tuple[str, ...]

Stable block and equation names in residual assembly order.

diagnose

diagnose(
    params: Mapping[str, Any] | None = None,
    *,
    guess: Mapping[str, Stream] | None = None,
    rank_tol: float | None = None,
) -> dict[str, Any]

Inspect the scaled Jacobian at an initial or user-supplied state.

Reports numerical rank, conditioning, zero rows and columns, and the corresponding unit/variable labels. Rank is local to the supplied state; a square equation count alone doesn't establish a solvable specification. This diagnostic runs on the host and doesn't mutate the warm-start cache.

solve_path

solve_path(
    start: Any,
    target: Any,
    *,
    solve_options: Mapping[str, Any] | None = None,
    **options: Any,
) -> Any

Continue operating conditions, reusing only accepted stream guesses.

solve

solve(
    params: Mapping[str, Any] | None = None,
    *,
    guess: Mapping[str, Stream] | None = None,
    sweeps: int = 6,
    tol: float = 1e-10,
    max_iter: int = 60,
    check_dof: bool = True,
    check: bool = True,
    warm_start: bool = True,
) -> EOSolution

Solve the flowsheet simultaneously and return all streams.

Parameters:

Name Type Description Default
params Mapping[str, Any] | None

Parameter mapping read by blocks (operating conditions, split fractions, ...). Gradients with respect to these (and the feeds) flow through the converged solution by implicit differentiation.

None
guess Mapping[str, Stream] | None

Optional initial guesses for specific internal streams (useful to seed a recycle); other streams get a default interior seed.

None
sweeps int

Forward sweeps used to build the initial guess.

6
tol float

Maximum accepted scaled equation residual.

1e-10
max_iter int

Newton iteration cap.

60
check bool

Raise for concrete failures; mask compiled failed values with NaNs.

True
warm_start bool

Reuse the most recent converged state for this structure.

True
check_dof bool

If True, raise when the flowsheet is not square (degrees_of_freedom != 0).

True

Returns:

Type Description
EOSolution

An EOSolution with every solved stream, differentiable in params

EOSolution

and the feeds.

Raises:

Type Description
ValueError

if check_dof and the flowsheet is under/over-specified.

Unit blocks

blocks

Physical unit declarations for simultaneous process equations.

Built-in blocks call the same unit kernels as sequential execution. Their connection equations include material, temperature, pressure, and resolved vapor inventory. Custom blocks can supply explicit residual equations and auxiliary variables through the Block protocol.

Classes:

Name Description
Scales

Characteristic scales that non-dimensionalise the EO residual system.

Context

Static (non-differentiated) data shared by every block during a solve.

Block

Base class for an equation-oriented unit block.

Mixer

Adiabatic (or isothermal) mixer: combine inlet streams into one outlet.

Splitter

Flow splitter: one inlet to several outlets sharing composition and state.

Heater

Heater/cooler on a temperature or a duty specification.

Valve

Isenthalpic (Joule-Thomson) pressure letdown to p_out.

Pump

Incompressible-liquid pump to p_out with an isentropic-equivalent efficiency.

Compressor

Isentropic compressor to p_out with an isentropic efficiency (< 1).

Turbine

Isentropic turbine (expander) to p_out with an isentropic efficiency (< 1).

Flash

Isothermal-isobaric two-phase flash: one inlet to vapour and liquid outlets.

ComponentSeparator

Idealised separator with a per-component recovery to the top product.

HeatExchanger

Two-sided exchanger: a hot and a cold stream coupled by one duty.

StoichiometricReactor

Fixed-conversion reactor with a formation-enthalpy energy balance.

Column

Rigorous MESH column embedded as a procedural block.

Functions:

Name Description
resolve

Resolve a Spec to a JAX array.

Scales dataclass

Scales(
    flow: float = 1.0,
    temperature: float = 100.0,
    pressure: float = 100000.0,
    enthalpy_flow: float = 10000.0,
    enthalpy_molar: float = 10000.0,
    entropy_molar: float = 10.0,
)

Characteristic scales that non-dimensionalise the EO residual system.

The unknowns and residuals are divided by these so the Newton system is well conditioned: molar flows and material balances by flow, temperatures and temperature specs by temperature, pressures and pressure specs by pressure, total-enthalpy balances by enthalpy_flow, molar-enthalpy balances by enthalpy_molar, and molar-entropy relations by entropy_molar. Phase-equilibrium (equifugacity) residuals are already dimensionless and are left unscaled.

Attributes:

Name Type Description
flow float

Characteristic molar flow (mol/s).

temperature float

Characteristic temperature (K).

pressure float

Characteristic pressure (Pa).

enthalpy_flow float

Characteristic total enthalpy flow (W).

enthalpy_molar float

Characteristic molar enthalpy (J/mol).

entropy_molar float

Characteristic molar entropy (J/mol/K).

Context dataclass

Context(
    components: tuple[str, ...],
    scales: Scales = Scales(),
    model: Model = None,
)

Static (non-differentiated) data shared by every block during a solve.

Attributes:

Name Type Description
components tuple[str, ...]

The flowsheet's component names (shared by all streams).

scales Scales

Residual / variable scales (see Scales).

model Model

Property package used for every equilibrium / property call; None selects Peng-Robinson over components.

n_components property

n_components: int

Number of components (the per-stream material-balance count).

package property

package: PropertyPackage

The resolved property package every block evaluates properties with.

Block dataclass

Block(inlets: tuple[str, ...], outlets: tuple[str, ...])

Base class for an equation-oriented unit block.

A block names its inlet and outlet streams (by the keys used in the flowsheet) and supplies the equations relating them. Outlet streams are unknowns of the global EO system; each block "defines" its outlet streams by contributing exactly enough residual equations.

Attributes:

Name Type Description
inlets tuple[str, ...]

Inlet stream names (must already exist as feeds or other blocks' outlets).

outlets tuple[str, ...]

Outlet stream names defined by this block.

Methods:

Name Description
residual_dependencies

Declare stream and auxiliary dependencies, or return None for all unknowns.

aux_scales

Auxiliary unknowns introduced by the block, mapped to their scale.

n_residuals

Number of residual equations this block contributes.

aux_init

Initial values for the block's auxiliary unknowns (unscaled).

forward

Evaluate the unit explicitly, returning its outlet streams.

residuals

Compare shared stream coordinates against the physical unit kernel.

feasible

Whether a solved block state is physically realizable (checked after solving).

residual_dependencies

residual_dependencies(
    ctx: Context,
) -> tuple[tuple[str, ...], tuple[str, ...]] | None

Declare stream and auxiliary dependencies, or return None for all unknowns.

This is a structural contract for every equation and operating point, not an observation of zeros at one state. Built-in blocks depend on their ports and owned auxiliaries. Custom classes, including subclasses of built-ins, default to all unknowns unless they override this method. Freed operating parameters are always included by the assembler.

aux_scales

aux_scales(ctx: Context) -> dict[str, float]

Auxiliary unknowns introduced by the block, mapped to their scale.

Built-in blocks keep their internal solves in the common physical kernels. A custom block can declare an additional variable here so the flowsheet allocates an unknown, then add its defining equation in residuals and include it in n_residuals.

n_residuals

n_residuals(ctx: Context) -> int

Number of residual equations this block contributes.

aux_init

aux_init(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Array]

Initial values for the block's auxiliary unknowns (unscaled).

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate the unit explicitly, returning its outlet streams.

Reuses the sequential-modular unit operation, so it is the reference the EO residuals are built to reproduce. Used to seed the EO solve.

residuals

residuals(
    streams: Mapping[str, Stream],
    aux: Mapping[str, Array],
    params: Mapping[str, Any],
    ctx: Context,
) -> Array

Compare shared stream coordinates against the physical unit kernel.

Custom residual units can override this and n_residuals. Built-in units have one implementation of their physics, shared with ordinary Python functions and saved process cases.

feasible

feasible(
    streams: Mapping[str, Stream],
    aux: Mapping[str, Array],
    params: Mapping[str, Any],
    ctx: Context,
) -> Array

Whether a solved block state is physically realizable (checked after solving).

The equations can have solutions no equipment achieves (a heat exchanger with a temperature cross). A block that can tell overrides this; the flowsheet reports an infeasible solution as not converged.

Mixer dataclass

Mixer(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    t: Spec | None = None,
    p: Spec | None = None,
)

Bases: Block

Adiabatic (or isothermal) mixer: combine inlet streams into one outlet.

Flows add component-by-component. With t unset the outlet temperature is set by an adiabatic energy balance (total enthalpy in equals total enthalpy out); set t to fix the outlet temperature. The outlet pressure defaults to the lowest inlet pressure, or is fixed by p.

Attributes:

Name Type Description
t Spec | None

Optional outlet temperature spec (literal or parameter key); None selects the adiabatic energy balance.

p Spec | None

Optional outlet pressure spec; None uses the minimum inlet pressure.

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.mix.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.mix.

Splitter dataclass

Splitter(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    fractions: Spec = 1.0,
)

Bases: Block

Flow splitter: one inlet to several outlets sharing composition and state.

Attributes:

Name Type Description
fractions Spec

Per-outlet split fractions (literal sequence/array or a parameter key), one per name in outlets.

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.splitter.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.splitter.

Heater dataclass

Heater(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    t_out: Spec | None = None,
    duty: Spec | None = None,
    dp: Spec = 0.0,
)

Bases: Block

Heater/cooler on a temperature or a duty specification.

Provide exactly one of t_out (outlet temperature) or duty (signed heat added, W). dp is the pressure drop across the block.

Attributes:

Name Type Description
t_out Spec | None

Outlet temperature spec, or None if a duty is given.

duty Spec | None

Heat-duty spec (W), or None if an outlet temperature is given.

dp Spec

Pressure drop (Pa).

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.heater.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.heater.

Valve dataclass

Valve(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    p_out: Spec = 100000.0,
)

Bases: Block

Isenthalpic (Joule-Thomson) pressure letdown to p_out.

Attributes:

Name Type Description
p_out Spec

Outlet pressure spec (literal or parameter key).

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.valve.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.valve.

Pump dataclass

Pump(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    p_out: Spec = 100000.0,
    efficiency: Spec = 0.75,
)

Bases: Block

Incompressible-liquid pump to p_out with an isentropic-equivalent efficiency.

Attributes:

Name Type Description
p_out Spec

Outlet pressure spec.

efficiency Spec

Pump efficiency in (0, 1]; the lost work heats the outlet.

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.pump.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.pump.

Compressor dataclass

Compressor(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    p_out: Spec = 100000.0,
    efficiency: Spec = 0.75,
    _is_turbine: bool = False,
)

Bases: _Machine

Isentropic compressor to p_out with an isentropic efficiency (< 1).

Turbine dataclass

Turbine(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    p_out: Spec = 100000.0,
    efficiency: Spec = 0.75,
    _is_turbine: bool = True,
)

Bases: _Machine

Isentropic turbine (expander) to p_out with an isentropic efficiency (< 1).

Flash dataclass

Flash(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    t: Spec = 298.15,
    p: Spec = 100000.0,
)

Bases: Block

Isothermal-isobaric two-phase flash: one inlet to vapour and liquid outlets.

outlets must be (vapor_name, liquid_name). The common flash kernel determines the phase split and the connection equations retain both products' resolved inventories, temperatures, and pressures.

Attributes:

Name Type Description
t Spec

Drum temperature spec (literal or parameter key).

p Spec

Drum pressure spec (literal or parameter key).

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.flash_drum.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.flash_drum.

ComponentSeparator dataclass

ComponentSeparator(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    split_to_top: Spec = 0.5,
    top_t: Spec | None = None,
    top_p: Spec | None = None,
    bottom_t: Spec | None = None,
    bottom_p: Spec | None = None,
)

Bases: Block

Idealised separator with a per-component recovery to the top product.

outlets must be (top_name, bottom_name). split_to_top is a per-component fraction sent to the top; the remainder leaves in the bottom.

Attributes:

Name Type Description
split_to_top Spec

Per-component recovery to the top (sequence/array or key).

top_t Spec | None

Optional top-product temperature (defaults to the feed's).

top_p Spec | None

Optional top-product pressure (defaults to the feed's).

bottom_t Spec | None

Optional bottom-product temperature (defaults to the feed's).

bottom_p Spec | None

Optional bottom-product pressure (defaults to the feed's).

Methods:

Name Description
forward

Evaluate via fugacio.sim.units.component_separator.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.units.component_separator.

HeatExchanger dataclass

HeatExchanger(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    duty: Spec | None = None,
    t_hot_out: Spec | None = None,
    t_cold_out: Spec | None = None,
    ua: Spec | None = None,
    dp_hot: Spec = 0.0,
    dp_cold: Spec = 0.0,
    flow: str = "counter",
)

Bases: Block

Two-sided exchanger: a hot and a cold stream coupled by one duty.

inlets must be (hot_in, cold_in) and outlets (hot_out, cold_out). The common segmented exchanger kernel determines duty and phase inventories from exactly one closing specification.

Attributes:

Name Type Description
duty Spec | None

Duty spec (W), or None.

t_hot_out Spec | None

Hot outlet temperature spec (K), or None.

t_cold_out Spec | None

Cold outlet temperature spec (K), or None.

ua Spec | None

Size spec UA (W/K), or None.

dp_hot Spec

Hot-side pressure drop (Pa).

dp_cold Spec

Cold-side pressure drop (Pa).

flow str

"counter" or "parallel" (used by the ua relation and the forward seed).

Methods:

Name Description
forward

Evaluate via fugacio.sim.heat_exchanger.heat_exchanger.

feasible

Reject a failed unit solve even if its best outlet state matches the graph.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.heat_exchanger.heat_exchanger.

feasible

feasible(
    streams: Mapping[str, Stream],
    aux: Mapping[str, Array],
    params: Mapping[str, Any],
    ctx: Context,
) -> Array

Reject a failed unit solve even if its best outlet state matches the graph.

StoichiometricReactor dataclass

StoichiometricReactor(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    nu: Any = (),
    key: tuple[int, ...] = (),
    conversion: Any = 1.0,
    t_out: Spec | None = None,
    duty: Spec | None = 0.0,
    dp: Spec = 0.0,
)

Bases: Block

Fixed-conversion reactor with a formation-enthalpy energy balance.

The reactions are given as a stoichiometric matrix nu of shape (n_reactions, n_components) (negative for reactants). Each reaction's extent is set by the fractional conversion of its key reactant, based on the inlet flow of that reactant, so the outlet is n_out = n_in + extent @ nu. The energy balance uses the package's bulk enthalpy plus the ideal-gas standard enthalpies of formation, which carry the heat of reaction: the same basis as fugacio.sim.reactors.stoichiometric_reactor. Specify either the outlet temperature t_out or the duty (0.0 for adiabatic).

Attributes:

Name Type Description
nu Any

Stoichiometric matrix (a sequence of rows or an array).

key tuple[int, ...]

Key-reactant index of every reaction.

conversion Any

Fractional conversion spec(s) of the key reactants (a scalar for one reaction, else a sequence aligned with nu).

t_out Spec | None

Outlet temperature spec, or None when duty is given.

duty Spec | None

Heat-duty spec (W, positive when added), or None.

dp Spec

Pressure drop (Pa).

Methods:

Name Description
forward

Evaluate via fugacio.sim.reactors.stoichiometric_reactor (same energy basis).

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.reactors.stoichiometric_reactor (same energy basis).

Column dataclass

Column(
    inlets: tuple[str, ...],
    outlets: tuple[str, ...],
    feed_stages: tuple[int, ...] = (),
    n_stages: int = 10,
    p: Spec = 100000.0,
    specs: tuple[tuple[str, Spec], ...] = (),
    condenser: str | None = "total",
    reboiler: str | None = "kettle",
    efficiency: Spec = 1.0,
    options: Mapping[str, Any] = dict(),
)

Bases: Block

Rigorous MESH column embedded as a procedural block.

outlets must be (distillate, bottoms); every inlet is a column feed whose stage is given by feed_stages (aligned with inlets). The block's equations state that the outlet streams equal the products of fugacio.sim.distillation.rigorous_column at the current inlets and parameters. The column is converged inside every residual evaluation and differentiated implicitly, so the global Newton system stays small (the products only) while the column itself keeps its own well-conditioned simultaneous-correction solve.

Attributes:

Name Type Description
feed_stages tuple[int, ...]

Feed stage (1 = top) of every inlet.

n_stages int

Number of equilibrium stages including condenser and reboiler.

p Spec

Column pressure (Pa; literal or parameter key).

specs tuple[tuple[str, Spec], ...]

Column specifications as (kind, value) pairs, where kind is one of "reflux_ratio", "distillate_rate", "bottoms_rate", "boilup_ratio", "reflux_rate", "condenser_duty", "reboiler_duty" and value is a Spec.

condenser str | None

"total", "partial", or None.

reboiler str | None

"kettle" or None.

efficiency Spec

Murphree vapour efficiency.

options Mapping[str, Any]

Extra keyword arguments forwarded to rigorous_column.

Methods:

Name Description
forward

Evaluate via fugacio.sim.distillation.rigorous_column.

forward

forward(
    streams: Mapping[str, Stream],
    params: Mapping[str, Any],
    ctx: Context,
) -> dict[str, Stream]

Evaluate via fugacio.sim.distillation.rigorous_column.

resolve

resolve(spec: Spec, params: Mapping[str, Any]) -> Array

Resolve a Spec to a JAX array.

A string is looked up in params (so the value can be a differentiable parameter or a design-spec unknown); anything else is treated as a literal.

Parameters:

Name Type Description Default
spec Spec

A literal value or a key into params.

required
params Mapping[str, Any]

The parameter mapping passed to the solver.

required

Returns:

Type Description
Array

The resolved value as a float array.

Raises:

Type Description
KeyError

if spec is a string with no entry in params.

Optimization

optimize

Optimization over an equation-oriented flowsheet.

An EO flowsheet is differentiable end to end, so a design-optimization problem ("minimize the cost / maximize the yield over these operating variables") is a smooth program the gradient-based optimizers in fugacio.sim.optimize solve directly. Two equivalent formulations are offered:

  • Nested (simultaneous=False, the robust default): the decision variables parameterize the flowsheet, each objective evaluation solves the flowsheet to convergence (one Newton solve), and the optimizer descends the resulting reduced objective. The flowsheet solve is differentiable, so the reduced gradient is exact.
  • Simultaneous / full-space (simultaneous=True): the decision variables and the flowsheet state are optimized together, with the flowsheet equations imposed as equality constraints (an augmented-Lagrangian solve). No inner loop converges the flowsheet; feasibility and optimality are reached at once, the classic equation-oriented optimization paradigm.

Both return the optimal decision and the converged flowsheet at that optimum.

Classes:

Name Description
EOOptResult

Outcome of an equation-oriented flowsheet optimization.

Functions:

Name Description
optimize_flowsheet_eo

Optimize an EO flowsheet over named decision variables.

EOOptResult dataclass

EOOptResult(
    decision: dict[str, Array],
    objective: Array,
    solution: EOSolution,
    converged: Array,
    constraint_violation: Array,
)

Outcome of an equation-oriented flowsheet optimization.

Attributes:

Name Type Description
decision dict[str, Array]

Optimal value of each decision variable (keyed by name).

objective Array

Objective value at the optimum.

solution EOSolution

The converged EOSolution at the optimum.

converged Array

Whether the optimizer met its tolerances.

constraint_violation Array

Max flowsheet-equation violation (0 for the nested formulation; the feasibility residual for the simultaneous one).

optimize_flowsheet_eo

optimize_flowsheet_eo(
    flowsheet: EOFlowsheet,
    objective: Objective,
    decisions: Mapping[str, Decision],
    *,
    params: Mapping[str, Any] | None = None,
    simultaneous: bool = False,
    tol: float = 1e-06,
    max_iter: int = 200,
    solve_kwargs: Mapping[str, Any] | None = None,
) -> EOOptResult

Optimize an EO flowsheet over named decision variables.

Parameters:

Name Type Description Default
flowsheet EOFlowsheet

The EOFlowsheet to optimize.

required
objective Objective

Scalar objective objective(streams) -> () to minimize, read off the solved streams.

required
decisions Mapping[str, Decision]

Map of parameter key -> (init, lower, upper). Each key is read by some block; the optimizer varies it within its bounds.

required
params Mapping[str, Any] | None

Fixed parameters merged under the decisions for every solve.

None
simultaneous bool

Use the full-space formulation (flowsheet equations as equality constraints) instead of the nested one.

False
tol float

Optimizer tolerance.

1e-06
max_iter int

Optimizer outer-iteration cap.

200
solve_kwargs Mapping[str, Any] | None

Extra keyword arguments forwarded to EOFlowsheet.solve (e.g. sweeps, tol, max_iter) in the nested formulation.

None

Returns:

Type Description
EOOptResult

An EOOptResult with the optimal decision and the converged flowsheet.