Unit operations¶
Energy-balanced unit operations (flash drum, adiabatic flash, heater, valve, pump, compressor, turbine, mixer, splitter, component separator) and the non-ideal separation units (decanter, three-phase flash). Every unit is a compiled kernel with the property package and its operating values as dynamic arguments. An eager call raises on failure, and a traced call returns NaN outlets; see the reliability guide.
Core unit operations¶
units
¶
Differentiable unit operations with rigorous material and energy balances.
Every block here is built on a fugacio.thermo.PropertyPackage, the object that
owns both the phase equilibrium and the energy properties of the mixture, so
each unit is differentiable end-to-end with respect to its operating conditions,
its feed, and the thermodynamic parameters: 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, a feed flow, or an NRTL parameter.
The library covers the staples of a process flowsheet:
flash_drum/flash_drum_with_info: isothermal-isobaric separator;adiabatic_flash: pressure-and-duty separator (a PH flash), the right specification for a letdown into a drum or a pure fluid on its saturation line;heater: heater/cooler on a temperature, duty, or outlet vapor-fraction 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.
Contract. Every unit is one compiled kernel. The property package and every
specification are dynamic arguments, so a unit compiles once per package
structure and specification kind, then evaluates new operating points without
recompiling. Each kernel returns a fugacio.thermo.diagnostics.SolveReport
alongside its outlets, and a failed kernel's outlets are NaN. An eager call
raises: fugacio.thermo.diagnostics.ConvergenceError for a failed solve and
ValueError for a violated operating limit (fugacio.sim.unit_limits). A
traced call (inside jax.jit, a flowsheet, or an optimizer) can't raise and
instead returns NaN outlets with nonfinite derivatives.
The flashes here solve one liquid and one vapor. Whether a feed splits into two
liquids is a separate, more expensive question: check it with flash_drum_checked
(fugacio.sim.acceptance), a flowsheet's post-solve audit, or the package's
stability method.
Classes:
| Name | Description |
|---|---|
FlashDrumResult |
Vapor and liquid products of a separator, its heat duty, and its report. |
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_with_info |
Flash a feed at temperature |
flash_drum |
Flash a feed stream at temperature |
adiabatic_flash |
Separate a feed at pressure |
heater |
Heat or cool a stream on a temperature, duty, or vapor-fraction specification. |
valve |
Isenthalpic (Joule-Thomson) pressure letdown to |
pump |
Pump an (incompressible) liquid from |
compressor |
Compress a stream to |
turbine |
Expand a stream to |
splitter |
Split |
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. |
FlashDrumResult
¶
Bases: NamedTuple
Vapor and liquid products of a separator, its heat duty, and its report.
Attributes:
| Name | Type | Description |
|---|---|---|
vapor |
Stream
|
Vapor product |
liquid |
Stream
|
Liquid product |
duty |
Array
|
Heat added to hold the drum at its specification (W). |
report |
SolveReport
|
Solve and specification report. |
HeaterResult
¶
Bases: NamedTuple
Outlet of a heater/cooler together with the heat duty.
Attributes:
| Name | Type | Description |
|---|---|---|
outlet |
Stream
|
Product |
duty |
Array
|
Heat duty (W). Positive means heat added; negative means cooling. |
report |
SolveReport
|
Solve and specification report. |
PumpResult
¶
Bases: NamedTuple
Outlet of a pump together with the shaft work.
Attributes:
| Name | Type | Description |
|---|---|---|
outlet |
Stream
|
Product |
work |
Array
|
Shaft power delivered to the fluid (W). |
report |
SolveReport
|
Solve and specification report. |
WorkResult
¶
Bases: NamedTuple
Outlet of a compressor/turbine with actual and ideal shaft work.
Attributes:
| Name | Type | Description |
|---|---|---|
outlet |
Stream
|
Product |
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). |
report |
SolveReport
|
Solve and specification report. |
flash_drum_with_info
¶
flash_drum_with_info(
feed: Stream,
t: ArrayLike,
p: ArrayLike,
*,
model: Model = None,
) -> FlashDrumResult
Flash a feed at temperature t and pressure p, with duty and report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feed
|
Stream
|
Inlet |
required |
t
|
ArrayLike
|
Drum temperature (K). |
required |
p
|
ArrayLike
|
Drum pressure (Pa). |
required |
model
|
Model
|
Property package; defaults to Peng-Robinson over the feed. |
None
|
Returns:
| Type | Description |
|---|---|
FlashDrumResult
|
The vapor and liquid products, the duty to hold the drum at |
FlashDrumResult
|
the report. A pure fluid specified at its own resolved saturation state |
FlashDrumResult
|
keeps its phase inventory. |
Raises:
| Type | Description |
|---|---|
ConvergenceError
|
If an eager flash fails. |
ValueError
|
If |
flash_drum
¶
flash_drum(
feed: Stream,
t: ArrayLike,
p: ArrayLike,
*,
model: Model = None,
) -> tuple[Stream, Stream]
Flash a feed stream at temperature t and pressure p.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feed
|
Stream
|
Inlet |
required |
t
|
ArrayLike
|
Drum temperature (K). |
required |
p
|
ArrayLike
|
Drum pressure (Pa). |
required |
model
|
Model
|
Property package; defaults to Peng-Robinson over the feed. |
None
|
Returns:
| Type | Description |
|---|---|
Stream
|
|
Stream
|
respect to |
Raises:
| Type | Description |
|---|---|
ConvergenceError
|
If an eager flash fails (NaN products when traced). |
ValueError
|
If |
Warns:
| Type | Description |
|---|---|
PhysicalAcceptanceWarning
|
If an eager call's outlet phase is unstable (the feed forms more phases than a vapor-liquid flash represents). |
adiabatic_flash
¶
adiabatic_flash(
feed: Stream,
p: ArrayLike,
*,
duty: ArrayLike = 0.0,
model: Model = None,
t_init: ArrayLike = 300.0,
allow_extrapolation: bool = False,
) -> FlashDrumResult
Separate a feed at pressure p with heat duty added (a PH flash drum).
The drum temperature follows from the energy balance, so this is the separator for a letdown into a drum, a partial condenser on a duty, or a pure fluid on its saturation line (where a temperature can't fix the phase amounts).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feed
|
Stream
|
Inlet |
required |
p
|
ArrayLike
|
Drum pressure (Pa). |
required |
duty
|
ArrayLike
|
Heat added (W); zero for an adiabatic drum. |
0.0
|
model
|
Model
|
Property package; defaults to Peng-Robinson over the feed. |
None
|
t_init
|
ArrayLike
|
Initial temperature guess (K) for the energy solve. |
300.0
|
allow_extrapolation
|
bool
|
Accept states outside the parameters' observed bounds. |
False
|
Returns:
| Type | Description |
|---|---|
FlashDrumResult
|
The vapor and liquid products, the duty, and the report. |
Raises:
| Type | Description |
|---|---|
ConvergenceError
|
If an eager solve fails. |
ValueError
|
If |
heater
¶
heater(
feed: Stream,
*,
t_out: ArrayLike | None = None,
duty: ArrayLike | None = None,
vapor_fraction: ArrayLike | None = None,
dp: ArrayLike = 0.0,
model: Model = None,
t_init: ArrayLike = 300.0,
allow_extrapolation: bool = False,
) -> HeaterResult
Heat or cool a stream on a temperature, duty, or vapor-fraction specification.
Provide exactly one of:
t_out: outlet temperature (K); the duty follows from the enthalpy change;duty: signed heat added (W); the outlet temperature follows from an isenthalpic solve, so partial vaporization/condensation is handled;vapor_fraction: outlet molar vapor fraction at its saturation temperature, any value in[0, 1]for a pure fluid (a condenser to saturated liquid is0) and0(bubble point) or1(dew point) for a mixture.
dp is the (non-negative) pressure drop across the block. A pure fluid
specified by t_out exactly at its saturation temperature is rejected:
its quality is undetermined, so use vapor_fraction or duty.
Raises:
| Type | Description |
|---|---|
ValueError
|
If not exactly one specification is given, or |
ConvergenceError
|
If an eager solve fails. |
valve
¶
valve(
feed: Stream,
p_out: ArrayLike,
*,
model: Model = None,
t_init: ArrayLike = 300.0,
allow_extrapolation: bool = False,
) -> 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; its phase inventory is retained on the stream.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
ConvergenceError
|
If an eager solve fails. |
pump
¶
pump(
feed: Stream,
p_out: ArrayLike,
*,
efficiency: ArrayLike = 0.75,
model: Model = None,
t_init: ArrayLike = 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.
Raises:
| Type | Description |
|---|---|
ValueError
|
For an efficiency outside |
ConvergenceError
|
If an eager solve fails. |
compressor
¶
compressor(
feed: Stream,
p_out: ArrayLike,
*,
efficiency: ArrayLike = 0.75,
model: Model = None,
t_init: ArrayLike = 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.
Raises:
| Type | Description |
|---|---|
ValueError
|
For an efficiency outside |
ConvergenceError
|
If an eager solve fails. |
turbine
¶
turbine(
feed: Stream,
p_out: ArrayLike,
*,
efficiency: ArrayLike = 0.85,
model: Model = None,
t_init: ArrayLike = 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.
Raises:
| Type | Description |
|---|---|
ValueError
|
For an efficiency outside |
ConvergenceError
|
If an eager solve fails. |
splitter
¶
Split feed into outlets that share its composition and state.
fractions holds one split fraction per outlet; each outlet carries that
fraction of every component flow (and of the vapor inventory). The fractions
must each lie in [0, 1] and sum to one, so the split conserves matter.
Raises:
| Type | Description |
|---|---|
ValueError
|
For invalid fractions (NaN outlets when traced). |
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 in [0, 1] (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; product pressures can't
exceed it.
Raises:
| Type | Description |
|---|---|
ValueError
|
For recoveries outside |
mix
¶
mix(
streams: list[Stream],
*,
t: ArrayLike | None = None,
p: ArrayLike | None = None,
model: Model = None,
t_init: ArrayLike = 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 (its heat
is then the enthalpy change). Outlet pressure defaults to the lowest inlet
pressure and can't exceed it.
Raises:
| Type | Description |
|---|---|
ValueError
|
if the streams are empty, don't share a component list, or
|
ConvergenceError
|
If an eager energy solve fails. |
Operating limits¶
The predicates that every unit and the saved-case audit enforce.
unit_limits
¶
Operating limits shared by the unit kernels and the process-case audit.
Each rule is a traceable predicate, so a unit evaluated inside a compiled
flowsheet reports a violated limit (and returns NaN outputs) while an eager
call raises a ValueError naming the rule. The saved-case audit
(fugacio.sim.cases.results) evaluates the same predicates on solved
streams, so a limit can't be enforced in one place and forgotten in another.
Functions:
| Name | Description |
|---|---|
pressure_not_raised |
Valves, expanders, separators, and mixers can't raise pressure. |
pressure_not_lowered |
Pumps and compressors can't lower pressure. |
split_fractions_valid |
Conservative split: fractions in |
recoveries_valid |
Per-component recoveries in |
liquid_inlet |
A liquid-only machine's inlet is (within roundoff) all liquid. |
require |
Raise |
pressure_not_raised
¶
Valves, expanders, separators, and mixers can't raise pressure.
pressure_not_lowered
¶
Pumps and compressors can't lower pressure.
split_fractions_valid
¶
Conservative split: fractions in [0, 1] summing to one.
recoveries_valid
¶
Per-component recoveries in [0, 1] (the complement leaves the other outlet).
liquid_inlet
¶
A liquid-only machine's inlet is (within roundoff) all liquid.
require
¶
require(ok: Array, message: str) -> Array
Raise ValueError for a concrete violated rule; return the flag for tracing.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Persistent compilation cache¶
compilation
¶
Opt-in persistent compilation cache for repeated process studies.
Every Fugacio kernel compiles once per process (see fugacio.sim.units), but a
fresh process compiles again. enable_compilation_cache points JAX's
persistent compilation cache at a directory so a second process (a CLI run, a
worker in a sweep, a notebook restart) loads the compiled executables instead.
The cache is keyed by the JAX version, the backend, and the exact program, so a stale entry is never reused for a different computation. It's opt-in because the directory grows with every distinct program compiled.
Functions:
| Name | Description |
|---|---|
enable_compilation_cache |
Persist compiled JAX programs in |
enable_compilation_cache
¶
Persist compiled JAX programs in directory for reuse across processes.
Call it before the first computation when possible. Calling it later also works: the cache is re-initialized so the next compilation uses it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str | Path
|
Cache directory. It's created if missing. |
required |
min_compile_time_s
|
float
|
Only programs that took at least this long to compile are stored. The default, zero, stores every program, since process kernels are small individually but numerous. |
0.0
|
Returns:
| Type | Description |
|---|---|
Path
|
The resolved cache directory. |
Non-ideal separations¶
separations
¶
Liquid-liquid and three-phase separators on an activity-coefficient package.
These complement the vapor-liquid blocks in fugacio.sim.units with the
non-ideal phase behavior an activity-coefficient (gamma-phi) package describes:
decanter: a liquid-liquid separator (settling tank) that splits one feed into two conjugate liquid products via the stability-first isoactivity flash;three_phase_flash: a vapor + two-liquid (V-L-L) separator for heterogeneous systems (water/organic decantation, heteroazeotropic columns).
Both follow the unit contract: an eager call raises on failure (with a hint
naming the separator that does apply), and a traced call returns NaN products.
Every product is a differentiable Stream; flows carry gradients with respect
to the operating T, P, the feed, and the package's activity parameters.
Functions:
| Name | Description |
|---|---|
decanter |
Liquid-liquid settler: split |
three_phase_flash |
Vapor-liquid-liquid (V-L-L) flash of |
decanter
¶
decanter(
feed: Stream,
model: GammaPhiPackage,
*,
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 stability-first isoactivity LLE flash
(fugacio.thermo.lle.flash_lle_with_info) with the package's activity model
at temperature t (default: the feed temperature) and the feed pressure.
A feed outside any miscibility gap is one liquid: liquid_I carries it all
and liquid_II is empty.
Returns:
| Type | Description |
|---|---|
Stream
|
|
Stream
|
symmetric, so the order is made deterministic by returning the product |
tuple[Stream, Stream]
|
richest in the first component (index 0) as |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ConvergenceError
|
If an eager split fails. |
three_phase_flash
¶
three_phase_flash(
feed: Stream,
t: ArrayLike,
p: ArrayLike,
model: GammaPhiPackage,
*,
tol: float = 1e-11,
max_iter: int = 300,
) -> tuple[Stream, Stream, Stream]
Vapor-liquid-liquid (V-L-L) flash of feed at (T, P).
Drives the three-phase flash (fugacio.thermo.vlle.flash_vlle_with_info)
with the package's activity liquid and its EOS or ideal vapor. Use it where a
vapor coexists with two liquids (water/organic decantation, heteroazeotropic
distillation).
Returns:
| Type | Description |
|---|---|
tuple[Stream, Stream, Stream]
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ConvergenceError
|
If an eager flash fails. A feed that isn't three-phase
at |