Rigorous distillation¶
The simultaneous-correction (Naphtali-Sandholm) MESH column: multiple feeds, side draws, stage duties, condenser and reboiler types, design specifications, and the absorber and stripper wrappers.
See the rigorous distillation guide for worked examples.
distillation
¶
Rigorous equilibrium-stage columns: simultaneous-correction MESH solve.
The shortcut and constant-molar-overflow columns in fugacio.sim.column are
design-screening tools. This module is the rigorous counterpart a process
simulator is judged by: every stage carries its own Material balances,
Equilibrium relations, Summation constraints, and energy (Heat)
balance, and the whole set is solved simultaneously by Newton's method in the
manner of Naphtali and Sandholm (1971), with the Jacobian supplied exactly by JAX
autodiff. Because the stage energy balances are included, the internal vapour
and liquid traffic varies up the column as the real heats of vaporisation and
mixing dictate, which is what the constant-molar-overflow assumption discards.
The column is described generically enough to cover the separations that make up a plant:
- any number of feeds, each on its own stage (
ColumnFeed), entering at whatever thermal condition the feed stream carries; - liquid or vapour side draws (
SideDraw) as a fraction of the phase leaving a stage, and per-stage heat duties (StageDuty) for intercoolers and interreboilers; - a total or partial condenser (or none, for an absorber), a kettle reboiler (or none, for a stripper), and an optional subcooled-reflux temperature;
- Murphree vapour efficiencies per stage or for the whole column;
- a linear pressure profile between the top and bottom stages;
- any two column specifications (
ColumnSpec) to close the condenser and reboiler degrees of freedom: reflux ratio, distillate or bottoms rate, boilup ratio, a duty, a product purity, a component recovery, or a stage temperature; - any property package (
fugacio.sim.models.package_for), so an ethanol/water column runs on NRTL and a demethaniser on Peng-Robinson with the same code.
Unknowns are the logarithms of the component liquid and vapour flows leaving
every stage (so flows stay positive through the Newton iteration) plus the stage
temperatures and the condenser/reboiler duties; the equilibrium relations are
written in logarithmic form so trace components are as well conditioned as bulk
ones. A bubble-point (Wang-Henke) sweep seeds the iteration, then
fugacio.thermo.implicit.newton_system converges it with a backtracking line
search. The converged column is differentiable with respect to the feeds, the
pressure profile, the specifications, the side-draw fractions, the duties, and the
property package by the implicit function theorem, so column design variables
can be optimised by gradient descent through a fully rigorous model.
absorber and stripper are thin wrappers for the two-feed, no-condenser,
no-reboiler configurations.
Classes:
| Name | Description |
|---|---|
ColumnFeed |
A feed stream entering the column on a given stage. |
SideDraw |
A side product withdrawn from a stage. |
StageDuty |
An external heat duty on an intermediate stage (intercooler / interreboiler). |
ColumnSpec |
A column specification closing one condenser/reboiler degree of freedom. |
RigorousColumnResult |
Converged rigorous column. |
Functions:
| Name | Description |
|---|---|
reflux_ratio |
Spec the molar reflux ratio |
reflux_rate |
Spec the molar reflux flow |
distillate_rate |
Spec the total distillate flow |
bottoms_rate |
Spec the total bottoms flow |
boilup_ratio |
Spec the boilup ratio |
condenser_duty |
Spec the condenser duty (W, negative for heat removed). |
reboiler_duty |
Spec the reboiler duty (W, positive for heat added). |
purity |
Spec the mole fraction of |
recovery |
Spec the fraction of the fed |
component_flow |
Spec the molar flow of |
stage_temperature |
Spec the temperature of stage |
rigorous_column |
Solve a rigorous multistage column by simultaneous correction (MESH). |
absorber |
Countercurrent absorber: lean solvent to the top stage, rich gas to the bottom. |
stripper |
Countercurrent stripper: rich liquid to the top stage, stripping gas to the bottom. |
ColumnFeed
dataclass
¶
A feed stream entering the column on a given stage.
Attributes:
| Name | Type | Description |
|---|---|---|
stream |
Stream
|
The feed |
stage |
int
|
Stage number, counted from the top with the condenser (when
present) as stage 1 and the reboiler (when present) as stage
|
SideDraw
dataclass
¶
A side product withdrawn from a stage.
Attributes:
| Name | Type | Description |
|---|---|---|
stage |
int
|
Stage number (1 = top). |
phase |
str
|
|
fraction |
ArrayLike
|
Fraction of that phase leaving the stage that is withdrawn (a ratio keeps the material balance well posed for any column traffic; differentiable). |
StageDuty
dataclass
¶
StageDuty(stage: int, duty: ArrayLike)
An external heat duty on an intermediate stage (intercooler / interreboiler).
Attributes:
| Name | Type | Description |
|---|---|---|
stage |
int
|
Stage number (1 = top). |
duty |
ArrayLike
|
Heat added to the stage (W); negative removes heat. |
ColumnSpec
dataclass
¶
ColumnSpec(
kind: str,
value: ArrayLike,
component: int | None = None,
product: str = "distillate",
stage: int | None = None,
)
A column specification closing one condenser/reboiler degree of freedom.
Build these with the helper constructors (reflux_ratio, distillate_rate,
purity, ...) rather than by hand.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
One of |
value |
ArrayLike
|
Target value (differentiable). |
component |
int | None
|
Component index for composition / recovery / flow specs. |
product |
str
|
|
stage |
int | None
|
Stage number for |
RigorousColumnResult
¶
Bases: NamedTuple
Converged rigorous column.
Attributes:
| Name | Type | Description |
|---|---|---|
distillate |
Stream
|
Top product (liquid for a total condenser, vapour for a partial condenser, the overhead vapour for an absorber). |
bottoms |
Stream
|
Bottom liquid product. |
side_draws |
tuple[Stream, ...]
|
Side products in the order the |
condenser_duty |
Array
|
Condenser duty (W, negative = heat removed); zero if none. |
reboiler_duty |
Array
|
Reboiler duty (W); zero if none. |
t |
Array
|
Stage temperatures (K), top to bottom. |
p |
Array
|
Stage pressures (Pa). |
x |
Array
|
Liquid mole fractions per stage, shape |
y |
Array
|
Vapour mole fractions per stage. |
k |
Array
|
K-values per stage. |
liquid_flow |
Array
|
Total liquid leaving each stage (mol/s), including any draw. |
vapor_flow |
Array
|
Total vapour leaving each stage (mol/s), including any draw. |
reflux_ratio |
Array
|
|
boilup_ratio |
Array
|
|
residual_norm |
Array
|
Max-norm of the scaled MESH residual at the solution. |
report |
SolveReport
|
Numerical convergence report in the original equation order. |
stage_liquid |
Array
|
Internal liquid component flows, excluding side products. |
stage_vapor |
Array
|
Internal vapor component flows, excluding side products. |
solver_code |
Array
|
Array code for the selected linear strategy, 1 for block and 0 for dense. |
jacobian_size |
Array
|
Scalar number of simultaneous unknowns. |
generation |
Array
|
Per-stage component generation (mol/s), zero without reactions. |
reaction_rates |
Array
|
Per-stage intensive reaction rates (mol/(m^3 s)). |
reaction_heat |
Array
|
Formation-energy source (W); already included in stage balances. |
reaction_volumes |
Array
|
Reacting-phase volume on each stage (m^3). |
Methods:
| Name | Description |
|---|---|
warm_start |
Full stage state for |
solver_info |
Describe the selected Jacobian strategy for this concrete column. |
warm_start
¶
Full stage state for rigorous_column(..., guess=result.warm_start()).
distillate_rate
¶
distillate_rate(value: ArrayLike) -> ColumnSpec
Spec the total distillate flow D (mol/s).
condenser_duty
¶
condenser_duty(value: ArrayLike) -> ColumnSpec
Spec the condenser duty (W, negative for heat removed).
reboiler_duty
¶
reboiler_duty(value: ArrayLike) -> ColumnSpec
Spec the reboiler duty (W, positive for heat added).
purity
¶
purity(
product: str, component: int, value: ArrayLike
) -> ColumnSpec
Spec the mole fraction of component in product.
recovery
¶
recovery(
component: int, product: str, value: ArrayLike
) -> ColumnSpec
Spec the fraction of the fed component that leaves in product.
component_flow
¶
component_flow(
product: str, component: int, value: ArrayLike
) -> ColumnSpec
Spec the molar flow of component in product (mol/s).
stage_temperature
¶
stage_temperature(
stage: int, value: ArrayLike
) -> ColumnSpec
Spec the temperature of stage stage (K).
rigorous_column
¶
rigorous_column(
feeds: Sequence[ColumnFeed],
n_stages: int,
*,
p: ArrayLike | None = None,
p_top: ArrayLike | None = None,
p_bottom: ArrayLike | None = None,
condenser: str | None = "total",
reboiler: str | None = "kettle",
specs: Sequence[ColumnSpec] = (),
side_draws: Sequence[SideDraw] = (),
stage_duties: Sequence[StageDuty] = (),
efficiency: ArrayLike = 1.0,
reflux_temperature: ArrayLike | None = None,
reactions: ReactionSet | None = None,
reaction_volumes: ArrayLike = 0.0,
model: Model = None,
eos: CubicEOS = PR,
kij: Array | None = None,
guess: dict[str, Any] | None = None,
sweeps: int = 4,
tol: float = 1e-09,
max_iter: int = 80,
check: bool = True,
homotopy: bool = True,
linear_solver: str = "block",
) -> RigorousColumnResult
Solve a rigorous multistage column by simultaneous correction (MESH).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feeds
|
Sequence[ColumnFeed]
|
One or more |
required |
n_stages
|
int
|
Total number of equilibrium stages including the condenser and reboiler when present (stage 1 is the top). |
required |
p
|
ArrayLike | None
|
Uniform column pressure (Pa); or give |
None
|
p_top
|
ArrayLike | None
|
Top-stage pressure (Pa) when a profile is wanted. |
None
|
p_bottom
|
ArrayLike | None
|
Bottom-stage pressure (Pa) when a profile is wanted. |
None
|
condenser
|
str | None
|
|
'total'
|
reboiler
|
str | None
|
|
'kettle'
|
specs
|
Sequence[ColumnSpec]
|
Column specifications; exactly one per condenser/reboiler present (two for a conventional column, one for a reboiled absorber, none for an absorber or stripper). |
()
|
side_draws
|
Sequence[SideDraw]
|
Side products as fractions of the phase leaving a stage. |
()
|
stage_duties
|
Sequence[StageDuty]
|
Intermediate heat duties (W). |
()
|
efficiency
|
ArrayLike
|
Murphree vapour efficiency, a scalar or one value per stage. |
1.0
|
reflux_temperature
|
ArrayLike | None
|
Subcooled reflux temperature (K) for a total
condenser; |
None
|
reactions
|
ReactionSet | None
|
Optional validated kinetic reaction set. Local reaction sources enter material and energy equations without changing MESH unknowns. |
None
|
reaction_volumes
|
ArrayLike
|
Reacting-phase volumes (m^3). A scalar applies to interior stages; an n_stages vector explicitly selects every stage. Vapor-phase reaction in a total condenser is rejected. |
0.0
|
model
|
Model
|
Property package (see |
None
|
eos
|
CubicEOS
|
Cubic EOS for the default package. |
PR
|
kij
|
Array | None
|
Binary interaction matrix for the default package. |
None
|
guess
|
dict[str, Any] | None
|
Optional seeding hints: |
None
|
sweeps
|
int
|
Bubble-point sweeps used to seed the Newton solve. |
4
|
check
|
bool
|
Raise for a failed concrete solve; compiled failures return NaNs. |
True
|
tol
|
float
|
Maximum accepted scaled equation residual. |
1e-09
|
max_iter
|
int
|
Iteration cap for each Newton solve, including homotopy increments. |
80
|
homotopy
|
bool
|
Retry by gradually restoring NRTL activity effects and reaction volumes. The final report checks the original full model. |
True
|
linear_solver
|
str
|
|
'block'
|
Returns:
| Type | Description |
|---|---|
RigorousColumnResult
|
A |
RigorousColumnResult
|
differentiable with respect to the feeds, pressures, specifications, |
RigorousColumnResult
|
draw fractions, duties, and the package parameters. |
Raises:
| Type | Description |
|---|---|
ValueError
|
for an inconsistent description (bad stage numbers, wrong number of specs, unknown condenser/reboiler type, mismatched components). |
absorber
¶
absorber(
gas: Stream,
liquid: Stream,
n_stages: int,
*,
p: ArrayLike | None = None,
p_top: ArrayLike | None = None,
p_bottom: ArrayLike | None = None,
efficiency: ArrayLike = 1.0,
model: Model = None,
eos: CubicEOS = PR,
kij: Array | None = None,
**kwargs: Any,
) -> RigorousColumnResult
Countercurrent absorber: lean solvent to the top stage, rich gas to the bottom.
No condenser, no reboiler, no specifications: the column is fully determined
by its two feeds. distillate in the result is the treated gas leaving the
top and bottoms the rich solvent leaving the bottom.
stripper
¶
stripper(
liquid: Stream,
gas: Stream,
n_stages: int,
*,
p: ArrayLike | None = None,
p_top: ArrayLike | None = None,
p_bottom: ArrayLike | None = None,
efficiency: ArrayLike = 1.0,
model: Model = None,
eos: CubicEOS = PR,
kij: Array | None = None,
**kwargs: Any,
) -> RigorousColumnResult
Countercurrent stripper: rich liquid to the top stage, stripping gas to the bottom.
The same topology as absorber with the roles of the feeds swapped in the
signature; distillate is the loaded stripping gas and bottoms the
stripped liquid.