Liquid-liquid & vapour-liquid-liquid equilibrium¶
Rigorous two-liquid (LLE) and three-phase (VLLE) flashes, heterogeneous azeotropes, and the tangent-plane machinery that decides when a second liquid appears.
Liquid-liquid equilibrium¶
lle
¶
Liquid-liquid equilibrium (LLE): two-liquid flash, tie-lines, and binodals.
Partially miscible liquids split into two phases I and II whose component
fugacities are equal. With an activity-coefficient model that equality is the
isoactivity condition
x_i^I gamma_i(x^I, T) = x_i^II gamma_i(x^II, T),
i.e. K_i = x_i^II / x_i^I = gamma_i^I / gamma_i^II,
closed by the same Rachford-Rice material balance as a vapour-liquid flash, with
the phase fraction psi now the mole fraction in phase II:
sum_i z_i (K_i - 1) / (1 + psi (K_i - 1)) = 0,
x_i^I = z_i / (1 + psi (K_i - 1)), x_i^II = K_i x_i^I.
The flash is stability-first. The tangent-plane search of
fugacio.thermo.stability.liquid_stability decides whether a split exists: an
established stable liquid is returned as one phase (psi = 0), and an unstable
one seeds the isoactivity iteration from its most negative trial phase, which
avoids the ever-present trivial solution x^I = x^II = z. An iteration that
still collapses onto it is reported as TRIVIAL. The converged split is
differentiable in temperature, feed, and the model parameters via implicit
differentiation of the fixed point, so tie-lines move smoothly under a gradient,
which matters for solvent-selection optimisation and parameter fitting to
mutual-solubility data.
Only an activity model can describe an LLE; Wilson's model is excluded by construction (it has no miscibility gap), so use NRTL, UNIQUAC, or UNIFAC.
Classes:
| Name | Description |
|---|---|
LLEResult |
Result of a two-liquid (LLE) flash. |
LLESolveResult |
Two-liquid flash and its report. |
Functions:
| Name | Description |
|---|---|
flash_lle_with_info |
Split feed |
flash_lle |
Two-liquid flash value; NaN when |
binary_binodal |
Mutual-solubility (binodal) compositions of a binary at temperature |
tie_line |
One ternary tie-line through feed |
binodal_curve |
Binary binodal branches over a temperature range. |
LLEResult
¶
Bases: NamedTuple
Result of a two-liquid (LLE) flash.
Attributes:
| Name | Type | Description |
|---|---|---|
psi |
Array
|
Mole fraction of the feed in liquid phase |
x_i |
Array
|
Phase |
x_ii |
Array
|
Phase |
k |
Array
|
Distribution ratios |
LLESolveResult
¶
flash_lle_with_info
¶
flash_lle_with_info(
model: ActivityModel,
t: ArrayLike,
z: Array,
*,
k_guess: Array | None = None,
tol: float = 1e-12,
max_iter: int = 400,
) -> LLESolveResult
Split feed z into two liquids at temperature t (isoactivity flash).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
ActivityModel
|
Liquid activity-coefficient model (must admit a miscibility gap). |
required |
t
|
ArrayLike
|
Temperature (K). |
required |
z
|
Array
|
Overall (feed) mole fractions; absent components stay absent. |
required |
k_guess
|
Array | None
|
Optional initial distribution ratios |
None
|
tol
|
float
|
Fixed-point convergence tolerance. |
1e-12
|
max_iter
|
int
|
Maximum number of fixed-point iterations. |
400
|
Returns:
| Type | Description |
|---|---|
LLESolveResult
|
The split and its report. An established stable liquid converges to one |
LLESolveResult
|
phase ( |
LLESolveResult
|
trivial solution from an unstable feed is |
flash_lle
¶
flash_lle(
model: ActivityModel,
t: ArrayLike,
z: Array,
*,
k_guess: Array | None = None,
tol: float = 1e-12,
max_iter: int = 400,
) -> LLEResult
Two-liquid flash value; NaN when flash_lle_with_info reports failure.
binary_binodal
¶
binary_binodal(
model: ActivityModel,
t: ArrayLike,
*,
feed: ArrayLike = 0.5,
tol: float = 1e-12,
max_iter: int = 400,
) -> tuple[Array, Array]
Mutual-solubility (binodal) compositions of a binary at temperature t.
Returns (x1_phase_I, x1_phase_II), the mole fraction of component 1 in
each conjugate liquid (the tie-line ends). Any feed inside the gap gives the
same pair; the default 50/50 feed sits squarely in a symmetric gap. Outside
the gap (no split) both are NaN.
tie_line
¶
tie_line(
model: ActivityModel,
t: ArrayLike,
z: Array,
*,
tol: float = 1e-12,
max_iter: int = 400,
) -> tuple[Array, Array, Array]
One ternary tie-line through feed z: (x_raffinate, x_extract, psi).
A thin wrapper over flash_lle returning the two conjugate-phase
compositions and the phase fraction, the unit of a ternary LLE diagram.
binodal_curve
¶
binodal_curve(
model: ActivityModel,
temperatures: Array,
*,
feed: ArrayLike = 0.5,
tol: float = 1e-12,
max_iter: int = 400,
) -> tuple[Array, Array]
Binary binodal branches over a temperature range.
Maps binary_binodal across temperatures and returns
(x1_phase_I, x1_phase_II) arrays aligned with the input, the two branches
of the solubility envelope that meet at the upper (or lower) critical solution
temperature (NaN beyond it).
Vapour-liquid-liquid equilibrium¶
vlle
¶
Three-phase vapour-liquid-liquid equilibrium (VLLE).
When a vapour coexists with two partially miscible liquids (the heterogeneous
azeotropes that make water/organic distillation and decantation work), neither a
two-phase VLE flash nor an LLE flash alone suffices. VLLE couples them: a vapour
V and two liquids I and II all at equal temperature, pressure, and
component fugacity.
Following Michelsen, both non-reference phases are referred to liquid I through
two sets of K-values,
K_i^V = y_i / x_i^I = gamma_i^I f_i^{0,L} / (phi_i^V P),
K_i^L = x_i^II / x_i^I = gamma_i^I / gamma_i^II,
so the material balance gives x_i^I = z_i / D_i with
D_i = 1 + beta_V (K_i^V - 1) + beta_II (K_i^L - 1). The two phase fractions
(beta_V, beta_II) solve the pair of Rachford-Rice equations
sum_i z_i (K_i^V - 1)/D_i = 0 and sum_i z_i (K_i^L - 1)/D_i = 0 (a small
2x2 Newton with an analytic Jacobian), and the K-values are updated from the
fugacity equalities until consistent.
The flash is seeded from a VLE flash followed by a stability-driven split of its
liquid. When no three-phase solution exists (the Rachford-Rice root leaves the
phase-fraction simplex) the flash reports INFEASIBLE rather than clipping
phase fractions away from their compositions: use the two-phase VLE or LLE
flash there. A converged three-phase state is differentiable through the fixed
point.
Classes:
| Name | Description |
|---|---|
HeterogeneousAzeotrope |
A binary heterogeneous (two-liquid) azeotrope at fixed pressure. |
VLLEResult |
Result of a three-phase vapour-liquid-liquid flash. |
VLLESolveResult |
Three-phase flash and its report. |
Functions:
| Name | Description |
|---|---|
flash_vlle_with_info |
Isothermal-isobaric three-phase (V-L-L) flash of feed |
flash_vlle |
Three-phase flash value; NaN when |
heterogeneous_azeotrope |
Binary heterogeneous azeotrope: the boiling temperature of two conjugate liquids. |
HeterogeneousAzeotrope
¶
Bases: NamedTuple
A binary heterogeneous (two-liquid) azeotrope at fixed pressure.
Attributes:
| Name | Type | Description |
|---|---|---|
t |
Array
|
Azeotrope temperature (K). |
x_i |
Array
|
Composition of liquid |
x_ii |
Array
|
Composition of liquid |
y |
Array
|
Common vapour composition in equilibrium with both liquids. |
VLLEResult
¶
Bases: NamedTuple
Result of a three-phase vapour-liquid-liquid flash.
Attributes:
| Name | Type | Description |
|---|---|---|
beta_v |
Array
|
Vapour mole fraction of the feed. |
beta_l1 |
Array
|
Mole fraction in liquid |
beta_l2 |
Array
|
Mole fraction in liquid |
y |
Array
|
Vapour composition. |
x_i |
Array
|
Liquid |
x_ii |
Array
|
Liquid |
three_phase |
Array
|
|
VLLESolveResult
¶
flash_vlle_with_info
¶
flash_vlle_with_info(
model: ActivityModel,
t: ArrayLike,
p: ArrayLike,
z: Array,
tc: Array,
pc: Array,
omega: Array,
*,
eos: CubicEOS = PR,
kij: Array | None = None,
vapor: str = "ideal",
poynting: bool = False,
phi_saturation: bool = False,
tol: float = 1e-11,
max_iter: int = 300,
) -> VLLESolveResult
Isothermal-isobaric three-phase (V-L-L) flash of feed z at (T, P).
Seeds a vapour/liquid split from the gamma-phi VLE flash and a
liquid/liquid split of its liquid from fugacio.thermo.lle.flash_lle_with_info,
then drives both K-value sets to consistency with a 2x2 Rachford-Rice inner
solve. Phase fractions and compositions come from the same root, so the
returned state always closes the material balance.
Returns:
| Type | Description |
|---|---|
VLLESolveResult
|
The state and its report. The report is |
VLLESolveResult
|
must not be used) when the converged root has a phase fraction outside |
VLLESolveResult
|
|
flash_vlle
¶
flash_vlle(
model: ActivityModel,
t: ArrayLike,
p: ArrayLike,
z: Array,
tc: Array,
pc: Array,
omega: Array,
**options: Any,
) -> VLLEResult
Three-phase flash value; NaN when flash_vlle_with_info reports failure.
heterogeneous_azeotrope
¶
heterogeneous_azeotrope(
model: ActivityModel,
p: ArrayLike,
tc: Array,
pc: Array,
omega: Array,
*,
eos: CubicEOS = PR,
feed: ArrayLike = 0.5,
t_min: float = 250.0,
t_max: float = 500.0,
tol: float = 1e-09,
max_iter: int = 200,
) -> HeterogeneousAzeotrope
Binary heterogeneous azeotrope: the boiling temperature of two conjugate liquids.
Inside a miscibility gap the two conjugate liquids have equal component
activities (a_i = x_i gamma_i matches across the LLE tie-line), so they
necessarily boil to the same vapour y_i = a_i Psat_i / P. The
heterogeneous azeotrope is the temperature at which that shared vapour's total
pressure sum_i a_i Psat_i reaches the system pressure P, a single,
well-posed scalar root, located on a scanned bracket and
differentiable in P and the model parameters.
Returns:
| Type | Description |
|---|---|
HeterogeneousAzeotrope
|
A |
HeterogeneousAzeotrope
|
compositions, and the common vapour), or NaN when no two-liquid boiling |
HeterogeneousAzeotrope
|
point exists on |
HeterogeneousAzeotrope
|
component 1) that lies inside the miscibility gap. |