yaeos 3.1.0__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ """Solvers.
2
+
3
+ Module that contains different solvers for specific porpouses.
4
+ """
5
+
6
+ import numpy as np
7
+
8
+
9
+ def binary_isofugacity_x1y1pt(x, p, t, model):
10
+ """Isofugacity evaluation at a given P and T."""
11
+ y1, x1 = x
12
+
13
+ x = np.array([x1, 1 - x1])
14
+ y = np.array([y1, 1 - y1])
15
+
16
+ lnphi_x = model.lnphi_pt(x, pressure=p, temperature=t, root="stable")
17
+ lnphi_y = model.lnphi_pt(y, pressure=p, temperature=t, root="stable")
18
+
19
+ isofug = np.log(x) + lnphi_x - (np.log(y) + lnphi_y)
20
+
21
+ return isofug**2
22
+
23
+
24
+ def solve_pt(model, p, t, kind):
25
+ """Solve a point at a given P and T."""
26
+ try:
27
+ x10, y10 = find_init_binary_ll(model, p, t, kind)
28
+ except ValueError:
29
+ x10, y10 = 0.1, 0.9
30
+
31
+ mean = (x10 + y10) / 2
32
+
33
+ z = [mean, 1 - mean]
34
+ y0 = np.array([y10, 1 - y10])
35
+ x0 = np.array([x10, 1 - x10])
36
+
37
+ flash = model.flash_pt(z, pressure=p, temperature=t, k0=y0 / x0)
38
+
39
+ x1 = flash["x"][0]
40
+ y1 = flash["y"][0]
41
+
42
+ return x1, y1
43
+
44
+
45
+ def find_init_binary_ll(model, pressure, temperature, kind):
46
+ """Find initial guess for a binary liquid-liquid system."""
47
+ from scipy.signal import argrelmin, argrelmax
48
+
49
+ (
50
+ p,
51
+ t,
52
+ ) = (
53
+ pressure,
54
+ temperature,
55
+ )
56
+
57
+ if kind == "liquid-liquid":
58
+ root = "liquid"
59
+ else:
60
+ root = "stable"
61
+
62
+ zs = np.linspace(1e-15, 1 - 1e-15, 100)
63
+
64
+ phis = np.array(
65
+ [
66
+ model.lnphi_pt([z, 1 - z], temperature=t, pressure=p, root=root)
67
+ for z in zs
68
+ ]
69
+ )
70
+ phis = np.exp(phis)
71
+ fug_1 = zs * phis[:, 0] * p
72
+
73
+ argmin = argrelmin(zs * phis[:, 0] * p)[-1] + 1
74
+ argmax = argrelmax(zs * phis[:, 0] * p)[0] - 1
75
+
76
+ fug = np.mean([fug_1[argmin], fug_1[argmax]])
77
+
78
+ if fug > fug_1[-1]:
79
+ fug = np.mean([fug_1[argmin[0]], fug_1[-1]])
80
+
81
+ msk = zs < zs[argmax]
82
+ x1 = zs[msk][np.argmin(np.abs(fug - fug_1[msk]))]
83
+
84
+ msk = zs > zs[argmin]
85
+ y1 = zs[msk][np.argmin(np.abs(fug - fug_1[msk]))]
86
+
87
+ return x1, y1
yaeos/gpec.py ADDED
@@ -0,0 +1,104 @@
1
+ """Global Phase Equilibria Calculations.
2
+
3
+ Module that implements the GPEC algorithm for calculation of GPEDs and its
4
+ derivatives to obtain isopleths, isotherms and isobars.
5
+ """
6
+
7
+ import matplotlib.pyplot as plt
8
+
9
+ import numpy as np
10
+
11
+ from yaeos.core import ArModel
12
+
13
+
14
+ class GPEC:
15
+
16
+ def __init__(
17
+ self,
18
+ model: ArModel,
19
+ max_pressure=2500,
20
+ max_points=10000,
21
+ stability_analysis=True,
22
+ step_21=1e-2,
23
+ step_12=1e-5,
24
+ ):
25
+ self._z0 = [0, 1]
26
+ self._zi = [1, 0]
27
+ self._model = model
28
+
29
+ psats = [model.pure_saturation_pressures(i) for i in [1, 2]]
30
+
31
+ self._pures = psats
32
+
33
+ diff = 1e-3
34
+ cl, cep = model.critical_line(
35
+ z0=self._z0,
36
+ zi=self._zi,
37
+ ns=1,
38
+ s=diff,
39
+ a0=diff,
40
+ ds0=step_21,
41
+ stop_pressure=max_pressure,
42
+ max_points=max_points,
43
+ stability_analysis=stability_analysis,
44
+ )
45
+
46
+ self._cl21 = cl
47
+ self._cep21 = cep
48
+
49
+ if (
50
+ not np.isnan(cep["T"])
51
+ or (
52
+ abs(cl["T"][-1] - psats[0]["T"][-1]) > 10
53
+ and abs(cl["P"][-1] - psats[0]["P"][-1] > 10)
54
+ )
55
+ ):
56
+ cl, cep = model.critical_line(
57
+ z0=self._z0,
58
+ zi=self._zi,
59
+ ns=1,
60
+ s=1 - diff / 10,
61
+ a0=1 - diff / 10,
62
+ ds0=-step_12,
63
+ stop_pressure=max_pressure,
64
+ max_points=max_points,
65
+ stability_analysis=stability_analysis,
66
+ )
67
+
68
+ self._cl12 = cl
69
+ self._cep12 = cep
70
+ else:
71
+ self._cl12 = None
72
+ self._cep12 = None
73
+
74
+ a, T, V = model.critical_line_liquid_liquid(
75
+ z0=self._z0, zi=self._zi, pressure=max_pressure, t0=500
76
+ )
77
+
78
+ cl, cep = model.critical_line(
79
+ z0=self._z0,
80
+ zi=self._zi,
81
+ ns=4,
82
+ s=np.log(max_pressure),
83
+ a0=a,
84
+ v0=V,
85
+ t0=T,
86
+ p0=max_pressure,
87
+ ds0=-1e-1,
88
+ stop_pressure=max_pressure * 1.1,
89
+ max_points=max_points,
90
+ stability_analysis=stability_analysis,
91
+ )
92
+
93
+ self._cl_ll = cl
94
+ self._cep_ll = cep
95
+
96
+ def plot_gped(self):
97
+ for pure in self._pures:
98
+ plt.plot(pure["T"], pure["P"], color="green")
99
+
100
+ plt.plot(self._cl21["T"], self._cl21["P"], color="black")
101
+ if self._cl12:
102
+ plt.plot(self._cl12["T"], self.cl12["P"], color="black")
103
+ if self._cl_ll:
104
+ plt.plot(self._cl_ll["T"], self._cl_ll["P"], color="black")
yaeos/lib/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """yaeos_c module.
2
+
3
+ This module provides the yaeos_c module, which is a Python interface to the
4
+ yaeos C API compiled with f2py.
5
+ """
6
+
7
+ from yaeos.lib.yaeos_python import yaeos_c
8
+
9
+ __all__ = ["yaeos_c"]
@@ -0,0 +1,24 @@
1
+ """Models module.
2
+
3
+ Yaeos models module. This module provides the following submodules:
4
+
5
+ - excess_gibbs: Excess Gibbs energy models
6
+ - NRTL: non-random two-liquid model
7
+ - UNIFACVLE: Original UNIFAC VLE model
8
+ - UNIQUAC: UNIversal QUAsiChemical Excess Gibbs free energy model
9
+
10
+ - residual_helmholtz: Residual Helmholtz energy models
11
+ - Cubic EoS:
12
+ - PengRobinson76: Peng-Robinson model (1976)
13
+ - PengRobinson78: Peng-Robinson model (1978)
14
+ - SoaveRedlichKwong: Soave-Redlich-Kwong model
15
+ - RKPR: RKPR model
16
+ - Mixing rules: mixing rules for cubic EoS
17
+ - QMR: cuadratic mixing rule
18
+ - MHV: modified Huron-Vidal mixing rule
19
+ """
20
+
21
+ from . import excess_gibbs, residual_helmholtz
22
+
23
+
24
+ __all__ = ["excess_gibbs", "residual_helmholtz"]
@@ -0,0 +1,20 @@
1
+ """Gibbs Excess Models module.
2
+
3
+ Yaeos Gibbs excess module. This module provides the following submodules:
4
+
5
+ - excess_gibbs: Excess Gibbs energy models
6
+ - NRTL: non-random two-liquid model
7
+ - UNIFACVLE: Original UNIFAC VLE model
8
+ - UNIFACPSRK: UNIFAC-PSRK model (Predictive Soave-Redlich-Kwong)
9
+ - UNIFACDortmund: UNIFAC Dortmund model
10
+ - UNIQUAC: UNIversal QUAsiChemical Excess Gibbs free energy model
11
+ """
12
+
13
+ from .dortmund import UNIFACDortmund
14
+ from .nrtl import NRTL
15
+ from .psrk_unifac import UNIFACPSRK
16
+ from .unifac import UNIFACVLE
17
+ from .uniquac import UNIQUAC
18
+
19
+
20
+ __all__ = ["NRTL", "UNIFACVLE", "UNIFACPSRK", "UNIFACDortmund", "UNIQUAC"]
@@ -0,0 +1,45 @@
1
+ """UNIFAC Dortmund Module."""
2
+
3
+ from typing import List
4
+
5
+ from yaeos.core import GeModel
6
+ from yaeos.lib import yaeos_c
7
+ from yaeos.models.groups import groups_from_dicts
8
+
9
+
10
+ class UNIFACDortmund(GeModel):
11
+ """UNIFAC Dortmund model.
12
+
13
+ Please refer to the `yaeos` user documentation for an in-depth look at the
14
+ model's information: https://ipqa-research.github.io/yaeos/page/index.html
15
+
16
+ Parameters
17
+ ----------
18
+ molecules : list of dict
19
+ List of dicts with the groups and their amounts for each molecule.
20
+
21
+ Example
22
+ -------
23
+ .. code-block:: python
24
+
25
+ from yaeos import UNIFACDortmund
26
+
27
+ # Groups for water and ethanol
28
+ water = {16: 1}
29
+ ethanol = {1: 1, 2: 1, 14: 1}
30
+
31
+ groups = [water, ethanol]
32
+
33
+ model = UNIFACDortmund(groups)
34
+
35
+ model.ln_gamma([0.5, 0.5], 298.15)
36
+ """
37
+
38
+ def __init__(self, molecules: List[dict]) -> None:
39
+
40
+ (number_of_groups, groups_ids, groups_ammounts) = groups_from_dicts(
41
+ molecules
42
+ )
43
+ self.id = yaeos_c.unifac_dortmund(
44
+ ngs=number_of_groups, g_ids=groups_ids, g_v=groups_ammounts
45
+ )
@@ -0,0 +1,49 @@
1
+ """Non-random two-liquid model (NRTL) module."""
2
+
3
+ from yaeos.core import GeModel
4
+ from yaeos.lib import yaeos_c
5
+
6
+
7
+ class NRTL(GeModel):
8
+ """Non-random two-liquid model (NRTL) class.
9
+
10
+ Parameters
11
+ ----------
12
+ a : array_like
13
+ NRTL aij parameters matrix
14
+ b : array_like
15
+ NRTL bij parameters matrix
16
+ c : array_like
17
+ NRTL cij parameters matrix
18
+
19
+ Attributes
20
+ ----------
21
+ a : array_like
22
+ NRTL aij parameters matrix
23
+ b : array_like
24
+ NRTL bij parameters matrix
25
+ c : array_like
26
+ NRTL cij parameters matrix
27
+ id : int
28
+ NRTL model ID
29
+
30
+ Example
31
+ -------
32
+ .. code-block:: python
33
+
34
+ import numpy as np
35
+
36
+ from yaeos import NRTL
37
+
38
+ a = np.array([[0, 0.3], [0.3, 0]])
39
+ b = np.array([[0, 0.4], [0.4, 0]])
40
+ c = np.array([[0, 0.5], [0.5, 0]])
41
+
42
+ nrtl = NRTL(a, b, c)
43
+ """
44
+
45
+ def __init__(self, a, b, c) -> None:
46
+ self.a = a
47
+ self.b = b
48
+ self.c = c
49
+ self.id = yaeos_c.nrtl(a, b, c)
@@ -0,0 +1,45 @@
1
+ """PSRK-UNIFAC Module."""
2
+
3
+ from typing import List
4
+
5
+ from yaeos.core import GeModel
6
+ from yaeos.lib import yaeos_c
7
+ from yaeos.models.groups import groups_from_dicts
8
+
9
+
10
+ class UNIFACPSRK(GeModel):
11
+ """PSRK-UNIFAC model.
12
+
13
+ Please refer to the `yaeos` user documentation for an in-depth look at the
14
+ model's information: https://ipqa-research.github.io/yaeos/page/index.html
15
+
16
+ Parameters
17
+ ----------
18
+ molecules : list of dict
19
+ List of dicts with the groups and their amounts for each molecule.
20
+
21
+ Example
22
+ -------
23
+ .. code-block:: python
24
+
25
+ from yaeos import UNIFACPSRK
26
+
27
+ # Groups for water and ethanol
28
+ water = {16: 1}
29
+ ethanol = {1: 1, 2: 1, 14: 1}
30
+
31
+ groups = [water, ethanol]
32
+
33
+ model = UNIFACPSRK(groups)
34
+
35
+ model.ln_gamma([0.5, 0.5], 298.15)
36
+ """
37
+
38
+ def __init__(self, molecules: List[dict]) -> None:
39
+
40
+ (number_of_groups, groups_ids, groups_ammounts) = groups_from_dicts(
41
+ molecules
42
+ )
43
+ self.id = yaeos_c.unifac_psrk(
44
+ ngs=number_of_groups, g_ids=groups_ids, g_v=groups_ammounts
45
+ )
@@ -0,0 +1,45 @@
1
+ """UNIFAC Module."""
2
+
3
+ from typing import List
4
+
5
+ from yaeos.core import GeModel
6
+ from yaeos.lib import yaeos_c
7
+ from yaeos.models.groups import groups_from_dicts
8
+
9
+
10
+ class UNIFACVLE(GeModel):
11
+ """UNIFAC VLE model.
12
+
13
+ Please refer to the `yaeos` user documentation for an in-depth look at the
14
+ model's information: https://ipqa-research.github.io/yaeos/page/index.html
15
+
16
+ Parameters
17
+ ----------
18
+ molecules : list of dict
19
+ List of dicts with the groups and their amounts for each molecule.
20
+
21
+ Example
22
+ -------
23
+ .. code-block:: python
24
+
25
+ from yaeos import UNIFACVLE
26
+
27
+ # Groups for water and ethanol
28
+ water = {16: 1}
29
+ ethanol = {1: 1, 2: 1, 14: 1}
30
+
31
+ groups = [water, ethanol]
32
+
33
+ model = UNIFAVLE(groups)
34
+
35
+ model.ln_gamma([0.5, 0.5], 298.15)
36
+ """
37
+
38
+ def __init__(self, molecules: List[dict]) -> None:
39
+
40
+ (number_of_groups, groups_ids, groups_ammounts) = groups_from_dicts(
41
+ molecules
42
+ )
43
+ self.id = yaeos_c.unifac_vle(
44
+ ngs=number_of_groups, g_ids=groups_ids, g_v=groups_ammounts
45
+ )
@@ -0,0 +1,117 @@
1
+ """UNIQUAC (UNIversal QUAsiChemical) Excess Gibbs free energy model."""
2
+
3
+ import numpy as np
4
+
5
+ from yaeos.core import GeModel
6
+ from yaeos.lib import yaeos_c
7
+
8
+
9
+ class UNIQUAC(GeModel):
10
+ """UNIQUAC (UNIversal QUAsiChemical) Excess Gibbs free energy model.
11
+
12
+ Please refer to the `yaeos` user documentation for an in-depth look at the
13
+ model's information: https://ipqa-research.github.io/yaeos/page/index.html
14
+
15
+ Parameters
16
+ ----------
17
+ qs : array_like
18
+ Molecule's relative areas :math:`Q_i`
19
+ rs : array_like
20
+ Molecule's relative volumes :math:`R_i`
21
+ aij : array_like
22
+ Interaction parameters matrix :math:`a_{ij}` zero matrix if no
23
+ provided, by default None
24
+ bij : array_like
25
+ Interaction parameters matrix :math:`b_{ij}` zero matrix if no
26
+ provided, by default None
27
+ cij : array_like
28
+ Interaction parameters matrix :math:`c_{ij}` zero matrix if no
29
+ provided, by default None
30
+ dij : array_like
31
+ Interaction parameters matrix :math:`d_{ij}` zero matrix if no
32
+ provided, by default None
33
+ eij : array_like
34
+ Interaction parameters matrix :math:`e_{ij}` zero matrix if no
35
+ provided, by default None
36
+
37
+ Attributes
38
+ ----------
39
+ qs : array_like
40
+ Molecule's relative areas :math:`Q_i`
41
+ rs : array_like
42
+ Molecule's relative volumes :math:`R_i`
43
+ aij : array_like
44
+ Interaction parameters matrix :math:`a_{ij}`
45
+ bij : array_like
46
+ Interaction parameters matrix :math:`b_{ij}`
47
+ cij : array_like
48
+ Interaction parameters matrix :math:`c_{ij}`
49
+ dij : array_like
50
+ Interaction parameters matrix :math:`d_{ij}`
51
+ eij : array_like
52
+ Interaction parameters matrix :math:`e_{ij}`
53
+
54
+ Example
55
+ -------
56
+ .. code-block:: python
57
+
58
+ import numpy as np
59
+
60
+ from yaeos import UNIQUAC
61
+
62
+ b = np.array(
63
+ [
64
+ [0.0, -526.02, -309.64],
65
+ [318.06, 0.0, 91.532],
66
+ [-1325.1, -302.57, 0.0],
67
+ ]
68
+ )
69
+
70
+ rs = np.array([0.92, 2.1055, 3.1878])
71
+ qs = np.array([1.4, 1.972, 2.4])
72
+
73
+ t = 298.15
74
+
75
+ model = UNIQUAC(qs, rs, bij=b)
76
+
77
+ n = np.array([2.0, 2.0, 8.0])
78
+
79
+ gammas = np.exp(model.ln_gamma(n, t)) # [8.856, 0.860, 1.425]
80
+ """
81
+
82
+ def __init__(
83
+ self, qs, rs, aij=None, bij=None, cij=None, dij=None, eij=None
84
+ ) -> None:
85
+ self.qs = qs
86
+ self.rs = rs
87
+
88
+ nc = len(qs)
89
+
90
+ if aij is not None:
91
+ self.aij = np.array(aij, order="F")
92
+ else:
93
+ self.aij = np.zeros((nc, nc), order="F")
94
+
95
+ if bij is not None:
96
+ self.bij = np.array(bij, order="F")
97
+ else:
98
+ self.bij = np.zeros((nc, nc), order="F")
99
+
100
+ if cij is not None:
101
+ self.cij = np.array(cij, order="F")
102
+ else:
103
+ self.cij = np.zeros((nc, nc), order="F")
104
+
105
+ if dij is not None:
106
+ self.dij = np.array(dij, order="F")
107
+ else:
108
+ self.dij = np.zeros((nc, nc), order="F")
109
+
110
+ if eij is not None:
111
+ self.eij = np.array(eij, order="F")
112
+ else:
113
+ self.eij = np.zeros((nc, nc), order="F")
114
+
115
+ self.id = yaeos_c.uniquac(
116
+ qs, rs, self.aij, self.bij, self.cij, self.dij, self.eij
117
+ )
yaeos/models/groups.py ADDED
@@ -0,0 +1,49 @@
1
+ """Handling groups for Group Contribution Models."""
2
+
3
+ from typing import List
4
+
5
+ import numpy as np
6
+
7
+
8
+ def groups_from_dicts(molecules: List[dict]):
9
+ """Convert list of dicts with groups and their to C-API format.
10
+
11
+ Convert list of dicts with groups and their amounts to the format required
12
+ by the C-API.
13
+
14
+ Parameters
15
+ ----------
16
+ molecules : list of dict
17
+
18
+ Returns
19
+ -------
20
+ number_of_groups : list of int
21
+ Number of groups for each molecule.
22
+ groups_ids : np.ndarray
23
+ Groups ids for each molecule.
24
+ groups_ammounts : np.ndarray
25
+ Groups amounts for each molecule.
26
+ """
27
+ nc = len(molecules)
28
+ max_ng = max([len(groups) for groups in molecules])
29
+
30
+ # The C-API expects two 2D arrays with the groups and their amounts
31
+ # for each molecule. Each row in the arrays corresponds to a molecule
32
+ # and the columns to the groups. The arrays are padded with zeros.
33
+ groups_ids = np.zeros((nc, max_ng), dtype=np.int32, order="F")
34
+ groups_ammounts = np.zeros((nc, max_ng), dtype=np.int32, order="F")
35
+
36
+ # Construction of the arrays from the input dictionary
37
+ number_of_groups = []
38
+ for i, molecule_groups in enumerate(molecules):
39
+ ids = []
40
+ ammount = []
41
+ for group_id, group_count in molecule_groups.items():
42
+ ids.append(int(group_id))
43
+ ammount.append(int(group_count))
44
+
45
+ number_of_groups.append(len(ids))
46
+ groups_ids[i, : len(ids)] = ids
47
+ groups_ammounts[i, : len(ids)] = ammount
48
+
49
+ return number_of_groups, groups_ids, groups_ammounts
@@ -0,0 +1,21 @@
1
+ """Residual Helmholtz energy models module.
2
+
3
+ Yaeos Residual Helmholtz module. This module provides the following submodules:
4
+
5
+ - residual_helmholtz: Residual Helmholtz energy models
6
+ - Cubic EoS:
7
+ - PengRobinson76: Peng-Robinson model (1976)
8
+ - PengRobinson78: Peng-Robinson model (1978)
9
+ - SoaveRedlichKwong: Soave-Redlich-Kwong model
10
+ - RKPR: RKPR model
11
+ - PSRK: Predictive-Soave-Redlich-Kwong model
12
+ - Mixing rules: mixing rules for cubic EoS
13
+ - QMR: cuadratic mixing rule
14
+ - HV: Huron-Vidal mixing rule
15
+ - MHV: Michelsen's modified Huron-Vidal mixing rule
16
+ """
17
+
18
+ from . import cubic_eos
19
+
20
+
21
+ __all__ = ["cubic_eos"]
@@ -0,0 +1,38 @@
1
+ """Cubic EoS module.
2
+
3
+ Implemented models:
4
+
5
+ - Cubic EoS:
6
+ - PengRobinson76: Peng-Robinson model (1976)
7
+ - PengRobinson78: Peng-Robinson model (1978)
8
+ - SoaveRedlichKwong: Soave-Redlich-Kwong model
9
+ - RKPR: RKPR model
10
+ - Mixing rules: mixing rules for cubic EoS
11
+ - QMR: cuadratic mixing rule
12
+ - MHV: modified Huron-Vidal mixing rule
13
+ """
14
+
15
+ from .cubic_eos import (
16
+ CubicEoS,
17
+ PSRK,
18
+ PengRobinson76,
19
+ PengRobinson78,
20
+ RKPR,
21
+ SoaveRedlichKwong,
22
+ )
23
+ from .mixing_rules import CubicMixRule, HV, MHV, QMR, QMRTD
24
+
25
+
26
+ __all__ = [
27
+ "CubicEoS",
28
+ "PengRobinson76",
29
+ "PengRobinson78",
30
+ "SoaveRedlichKwong",
31
+ "RKPR",
32
+ "PSRK",
33
+ "CubicMixRule",
34
+ "QMR",
35
+ "QMRTD",
36
+ "MHV",
37
+ "HV",
38
+ ]