Skip to content

Unit operations

Energy-balanced unit operations (flash drum, heater, valve, pump, compressor, turbine, mixer, splitter, component separator) and the non-ideal separation units (two-phase flash, decanter, three-phase flash).

Core unit operations

units

Differentiable unit operations with rigorous material and energy balances.

Every block here is built on the equation-of-state phase equilibrium and the energy core in fugacio.thermo, so each one is differentiable end-to-end with respect to its operating conditions and feed. That is the headline Fugacio claim made concrete: you can take a gradient of a product purity, a duty, or a shaft power with respect to a drum temperature, an outlet pressure, a split fraction, or a feed flow: the basis for gradient-based flowsheet optimisation.

The library covers the staples of a process flowsheet:

  • flash_drum: isothermal-isobaric vapour/liquid separator;
  • heater: heater/cooler on either a temperature or a duty spec;
  • valve: isenthalpic (Joule-Thomson) pressure letdown;
  • pump: incompressible-liquid pump with an efficiency;
  • compressor / turbine: isentropic machines with an efficiency;
  • mix: adiabatic mixer (energy-balanced);
  • splitter: flow splitter;
  • component_separator: idealised component split.

Outlet temperatures of the energy-specified blocks are found with the differentiable flash_ph / flash_ps solves, whose temperatures carry implicit-function gradients.

Classes:

Name Description
HeaterResult

Outlet of a heater/cooler together with the heat duty.

PumpResult

Outlet of a pump together with the shaft work.

WorkResult

Outlet of a compressor/turbine with actual and ideal shaft work.

Functions:

Name Description
flash_drum

Flash a feed stream at temperature t and pressure p.

heater

Heat or cool a stream on either a temperature or a duty specification.

valve

Isenthalpic (Joule-Thomson) pressure letdown to p_out.

pump

Pump an (incompressible) liquid from feed.p to p_out.

compressor

Compress a stream to p_out with an isentropic efficiency (< 1).

turbine

Expand a stream to p_out with an isentropic efficiency (< 1).

splitter

Split feed into multiple outlets that share its composition and state.

component_separator

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

mix

Combine streams with an exact material balance and an adiabatic energy balance.

HeaterResult

Bases: NamedTuple

Outlet of a heater/cooler together with the heat duty.

Attributes:

Name Type Description
outlet Stream

Product Stream.

duty Array

Heat duty (W). Positive means heat added; negative means cooling.

PumpResult

Bases: NamedTuple

Outlet of a pump together with the shaft work.

Attributes:

Name Type Description
outlet Stream

Product Stream.

work Array

Shaft power delivered to the fluid (W).

WorkResult

Bases: NamedTuple

Outlet of a compressor/turbine with actual and ideal shaft work.

Attributes:

Name Type Description
outlet Stream

Product Stream.

work Array

Actual shaft power into the fluid (W); negative for a turbine (the fluid does work on the surroundings).

ideal_work Array

Reversible (isentropic) shaft power into the fluid (W).

flash_drum

flash_drum(
    feed: Stream,
    t: ArrayLike,
    p: ArrayLike,
    *,
    eos: CubicEOS = PR,
    kij: Array | None = None,
) -> tuple[Stream, Stream]

Flash a feed stream at temperature t and pressure p.

Parameters:

Name Type Description Default
feed Stream

Inlet Stream.

required
t ArrayLike

Drum temperature (K).

required
p ArrayLike

Drum pressure (Pa).

required
eos CubicEOS

Cubic equation of state to use (defaults to Peng-Robinson).

PR
kij Array | None

Optional binary interaction matrix.

None

Returns:

Type Description
Stream

(vapor, liquid) product streams. Their flows are differentiable with

Stream

respect to t, p and the feed.

heater

heater(
    feed: Stream,
    *,
    t_out: ArrayLike | None = None,
    duty: ArrayLike | None = None,
    dp: ArrayLike = 0.0,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> HeaterResult

Heat or cool a stream on either a temperature or a duty specification.

Provide exactly one of t_out (target outlet temperature) or duty (signed heat added, W). With t_out the duty is computed from the enthalpy change; with duty the outlet temperature is found by an isenthalpic solve, so the block correctly handles partial vaporisation/condensation. dp is the pressure drop across the block.

Raises:

Type Description
ValueError

if not exactly one of t_out / duty is given.

valve

valve(
    feed: Stream,
    p_out: ArrayLike,
    *,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> Stream

Isenthalpic (Joule-Thomson) pressure letdown to p_out.

Enthalpy is conserved, so the outlet temperature (and any flashing that results from the pressure drop) follows from an isenthalpic solve. The outlet may be two-phase; it is returned as a single bulk stream at the solved temperature.

pump

pump(
    feed: Stream,
    p_out: ArrayLike,
    *,
    efficiency: ArrayLike = 0.75,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> PumpResult

Pump an (incompressible) liquid from feed.p to p_out.

The reversible work is v_L (p_out - p_in) per mole using the liquid molar volume; the actual work is divided by efficiency and the inefficiency is deposited as heat, so the outlet temperature is found from an isenthalpic balance on H_out = H_in + W_actual.

compressor

compressor(
    feed: Stream,
    p_out: ArrayLike,
    *,
    efficiency: ArrayLike = 0.75,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> WorkResult

Compress a stream to p_out with an isentropic efficiency (< 1).

The reversible outlet is the isentropic state at p_out; the actual work is W_ideal / efficiency and the extra enthalpy sets the (higher) real outlet temperature via an isenthalpic solve.

turbine

turbine(
    feed: Stream,
    p_out: ArrayLike,
    *,
    efficiency: ArrayLike = 0.85,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> WorkResult

Expand a stream to p_out with an isentropic efficiency (< 1).

The fluid recovers efficiency of the reversible work; the returned work is negative (power delivered to the shaft) and the real outlet is warmer than the isentropic outlet because of the lost work.

splitter

splitter(
    feed: Stream, fractions: ArrayLike
) -> tuple[Stream, ...]

Split feed into multiple outlets that share its composition and state.

fractions is a sequence of split fractions (one per outlet); each outlet carries that fraction of every component flow. The fractions should sum to one for a conservative split. Temperature and pressure are passed through.

component_separator

component_separator(
    feed: Stream,
    split_to_top: ArrayLike,
    *,
    top_t: ArrayLike | None = None,
    top_p: ArrayLike | None = None,
    bottom_t: ArrayLike | None = None,
    bottom_p: ArrayLike | None = None,
) -> tuple[Stream, Stream]

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

split_to_top is a per-component fraction (aligned with feed.components) sent to the top outlet; the remainder leaves in the bottom. This is the workhorse "spec" separator for conceptual flowsheets, a stand-in for a column or absorber whose recoveries are known. Product temperatures and pressures default to the feed's.

mix

mix(
    streams: list[Stream],
    *,
    t: ArrayLike | None = None,
    p: ArrayLike | None = None,
    eos: CubicEOS = PR,
    kij: Array | None = None,
    t_init: float = 300.0,
) -> Stream

Combine streams with an exact material balance and an adiabatic energy balance.

Flows add component-by-component. By default the outlet temperature is found from an adiabatic energy balance (total enthalpy in equals total enthalpy out) via an isenthalpic solve, so heat of mixing and any phase change are accounted for; pass t to fix the outlet temperature instead. Outlet pressure defaults to the lowest inlet pressure.

Raises:

Type Description
ValueError

if the streams are empty or do not share a component list.

Non-ideal separations

separations

Non-ideal separation units: model-driven flash, decanter, three-phase flash.

These complement the equation-of-state blocks in fugacio.sim.units with the non-ideal phase behaviour the gamma-phi property system unlocks:

  • flash_vle: a two-phase V-L flash driven by any EquilibriumModel (EOS or gamma-phi), so the same drum works on Peng-Robinson or NRTL/UNIQUAC/UNIFAC;
  • decanter: a liquid-liquid separator (settling tank) that splits one feed into two conjugate liquid products via the isoactivity LLE flash; and
  • three_phase_flash: a vapour + two-liquid (V-L-L) separator for heterogeneous systems (water/organic decantation, heteroazeotropic columns).

Every product is a differentiable Stream; flows carry gradients with respect to the operating T, P, the feed, and (through the model object) the thermodynamic parameters themselves.

Functions:

Name Description
flash_vle

Two-phase vapour-liquid flash of feed at (T, P) using model.

decanter

Liquid-liquid settler: split feed into two conjugate liquid products.

three_phase_flash

Vapour-liquid-liquid (V-L-L) flash of feed at (T, P).

flash_vle

flash_vle(
    feed: Stream,
    t: ArrayLike,
    p: ArrayLike,
    model: _VLEModel,
) -> tuple[Stream, Stream]

Two-phase vapour-liquid flash of feed at (T, P) using model.

Works with any EquilibriumModel (an EOSModel for the phi-phi route or a GammaPhiModel for the activity-coefficient route) so the drum's thermodynamics are chosen by the model passed in.

Returns:

Type Description
tuple[Stream, Stream]

(vapor, liquid) product streams at (T, P).

decanter

decanter(
    feed: Stream,
    model: GammaPhiModel,
    *,
    t: ArrayLike | None = None,
    tol: float = 1e-12,
    max_iter: int = 400,
) -> tuple[Stream, Stream]

Liquid-liquid settler: split feed into two conjugate liquid products.

Solves the isoactivity LLE flash (fugacio.thermo.flash_lle) with the model's activity description at temperature t (default: the feed temperature) and the feed pressure. For a feed outside any miscibility gap the LLE flash collapses to the trivial split and one product carries essentially the whole feed; check with fugacio.thermo.liquid_stability upstream if that matters.

Returns:

Type Description
Stream

(liquid_I, liquid_II) product streams. The two isoactivity LLE roots

Stream

are symmetric, so which phase the solver labels I vs II is not

tuple[Stream, Stream]

stable across platforms/precision; the order here is made deterministic by

tuple[Stream, Stream]

returning the product richest in the first component (index 0) as

tuple[Stream, Stream]

liquid_I.

three_phase_flash

three_phase_flash(
    feed: Stream,
    t: ArrayLike,
    p: ArrayLike,
    model: GammaPhiModel,
    *,
    tol: float = 1e-11,
    max_iter: int = 300,
) -> tuple[Stream, Stream, Stream]

Vapour-liquid-liquid (V-L-L) flash of feed at (T, P).

Drives the three-phase flash (fugacio.thermo.flash_vlle) with the model's activity liquid and its EOS/ideal vapour. Use for heterogeneous systems (water/organic decantation and heteroazeotropic distillation) where a vapour coexists with two liquids.

Returns:

Type Description
Stream

(vapor, liquid_I, liquid_II) product streams. When the feed is not

Stream

genuinely three-phase one of the liquid flows collapses to (near) zero.