Skip to content

Commit 7c46300

Browse files
authored
Merge pull request #73 from CalebBell/master
release 1.5.1
2 parents 4bc59cf + f7512b1 commit 7c46300

9 files changed

Lines changed: 278 additions & 40 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
fi
103103
shell: bash
104104
- name: Add numba
105-
if: ${{ !contains(fromJSON('["pypy3.11", "3.13t"]'), matrix.python-version) }}
105+
if: ${{ !contains(fromJSON('["pypy3.11", "3.13t"]'), matrix.python-version) && !contains(fromJSON('["macos-15-intel"]'), matrix.os) }}
106106
run: |
107107
uv pip install --system -e .[numba]
108108
- name: Add CoolProp

Justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ test-nuitka py="3.13":
130130
@echo ">>> Creating temporary virtual environment with Python {{py}}..."
131131
@uv venv .venv-nuitka-{{py}} --python {{py}}
132132
@echo "\n>>> Installing project and Nuitka in temporary environment..."
133-
@uv pip install --python .venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -e .[test,numba]
133+
@uv pip install --python .venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -e .[test]
134134
@uv pip install --python .venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} nuitka
135135
@echo "\n>>> Preparing build directory..."
136136
@mkdir -p dev/nuitka/build

changelog.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## [1.5.1] - 2026-02-08
44

55
### Added
66

7+
- New `COSTALD_mixture_compressed` function for compressed liquid mixture density using the COSTALD-Tait CSP method (API Procedure 6A3.4)
8+
- New `COSTALD_compressed` function for compressed liquid density of pure components
9+
710
### Changed
811

12+
- CI: Skip numba tests on macOS Intel (no llvmlite wheels available)
13+
- CI: Enable numba tests on macOS ARM
14+
- CI: Remove `zoneinfo` from cx_Freeze excludes (required by pandas)
15+
- CI: Remove unnecessary numba dependency from Nuitka test recipe
16+
917
### Removed
1018

1119
### Fixed
1220

21+
- Fixed E coefficient in heat capacity table from Perry 8E for 1-heptene
1322
- Corrected spelling of "Demmler" viscosity cups (previously misspelled as "Demmier"). Added citation to Hydraulic Institute's Fluid Properties new website which shows the correct spelling.
1423

1524
## [1.5.0] - 2025-10-26

chemicals/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"""
2323

2424

25-
__version__ = "1.5.0"
25+
__version__ = "1.5.1"
2626
from math import isnan
2727

2828
from . import (

chemicals/volume.py

Lines changed: 218 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
.. autofunction:: chemicals.volume.Amgat
4949
.. autofunction:: chemicals.volume.Rackett_mixture
5050
.. autofunction:: chemicals.volume.COSTALD_mixture
51+
.. autofunction:: chemicals.volume.COSTALD_mixture_compressed
5152
5253
Gas Correlations
5354
----------------
@@ -204,9 +205,10 @@
204205
"volume_VDI_PPDS",
205206
]
206207

208+
from math import log1p, log10
207209

208-
from fluids.constants import R, root_two
209-
from fluids.numerics import exp, implementation_optimize_tck, log, np, splev, sqrt
210+
from fluids.constants import R
211+
from fluids.numerics import cbrt, exp, implementation_optimize_tck, log, np, splev, sqrt
210212

211213
from chemicals.data_reader import data_source, register_df_source
212214
from chemicals.utils import mark_numba_incompatible, mixing_simple, os_path_join, source_path
@@ -862,13 +864,17 @@ def COSTALD(T: float, Tc: float, Vc: float, omega: float) -> float:
862864
Saturated Densities of Liquids and Their Mixtures." AIChE Journal
863865
25, no. 4 (1979): 653-663. doi:10.1002/aic.690250412
864866
"""
865-
if T > Tc:
866-
T = Tc
867867
Tr = T/Tc
868+
if Tr > 1.0:
869+
Tr = 1.0
870+
868871
tau = 1.0 - Tr
872+
if tau < 0.0:
873+
tau = 0.0
874+
869875
tau_cbrt = (tau)**(1.0/3.)
870-
V_delta = (-0.296123 + Tr*(Tr*(-0.0480645*Tr - 0.0427258) + 0.386914))/(Tr - 1.00001)
871-
V_0 = tau_cbrt*(tau_cbrt*(tau_cbrt*(0.190454*tau_cbrt - 0.81446) + 1.43907) - 1.52816) + 1.0
876+
V_delta = (-0.296123 + Tr*(0.386914 + Tr*(-0.0427258 - 0.0480645*Tr)))/(Tr - 1.00001)
877+
V_0 = 1.0 + tau_cbrt*(-1.52816 + tau_cbrt*(1.43907 + tau_cbrt*(-0.81446 + 0.190454*tau_cbrt)))
872878
return Vc*V_0*(1.0 - omega*V_delta)
873879

874880

@@ -1182,10 +1188,21 @@ def COSTALD_compressed(T: float, P: float, Psat: float, Tc: float, Pc: float, om
11821188
k = 0.0344483
11831189
e = exp(f + omega*(g + h*omega))
11841190
C = j + k*omega
1185-
tau = 1.0 - T/Tc
1191+
1192+
Tr = T/Tc
1193+
tau = 1.0 - Tr
1194+
if tau < 0.0:
1195+
tau = 0.0
1196+
11861197
tau13 = tau**(1.0/3.0)
1187-
B = Pc*(-1.0 + a*tau13 + b*tau13*tau13 + d*tau + e*tau*tau13)
1188-
return Vs*(1.0 - C*log((B + P)/(B + Psat)))
1198+
1199+
# Optimized calculation for B using Horner-like scheme where possible
1200+
# -1 + a*t + b*t^2 + d*t^3 + e*t^4 where t = tau^(1/3)
1201+
B = Pc*(-1.0 + tau13*(a + tau13*(b + tau13*(d + e*tau13))))
1202+
1203+
# Use log1p for better numerical stability when P is close to Psat
1204+
# log((B+P)/(B+Psat)) == log(1 + (P-Psat)/(B+Psat))
1205+
return Vs*(1.0 - C*log1p((P - Psat)/(B + Psat)))
11891206

11901207
def Tait(P, P_ref, rho_ref, B, C):
11911208
r"""Calculates compressed-liquid mass density using the Tait
@@ -1418,6 +1435,57 @@ def Rackett_mixture(T: float, xs: list[float], MWs: list[float], Tcs: list[float
14181435
return (R*bigsum*Zr**(1.0 + (1.0 - Tr)**(2.0/7.0)))*MW
14191436

14201437

1438+
def COSTALD_mixture_parameters(xs: list[float], Tcs: list[float], Vcs: list[float], omegas: list[float]) -> tuple[float, float, float]:
1439+
r"""Shared helper to calculate the COSTALD mixture parameters
1440+
:math:`T_{cm}`, :math:`V_m`, and :math:`\omega_m` using the
1441+
COSTALD standard mixing rules.
1442+
1443+
Parameters
1444+
----------
1445+
xs : list[float]
1446+
Mole fractions of each component, [-]
1447+
Tcs : list[float]
1448+
Critical temperatures of all fluids, [K]
1449+
Vcs : list[float]
1450+
Critical volumes of all fluids (characteristic COSTALD volumes), [m^3/mol]
1451+
omegas : list[float]
1452+
Acentric factors (ideally SRK) of all fluids, [-]
1453+
1454+
Returns
1455+
-------
1456+
Tcm : float
1457+
Mixture pseudo-critical temperature, [K]
1458+
Vm : float
1459+
Mixture characteristic volume, [m^3/mol]
1460+
omega : float
1461+
Mixture acentric factor, [-]
1462+
"""
1463+
sum_xV = 0.0
1464+
sum_xV_1_3 = 0.0
1465+
sum_xV_2_3 = 0.0
1466+
omega = 0.0
1467+
term_Tcm = 0.0
1468+
1469+
for i in range(len(xs)):
1470+
xi = xs[i]
1471+
Vci = Vcs[i]
1472+
1473+
V_1_3 = cbrt(Vci)
1474+
V_2_3 = V_1_3*V_1_3
1475+
1476+
sum_xV += xi * Vci
1477+
sum_xV_1_3 += xi * V_1_3
1478+
sum_xV_2_3 += xi * V_2_3
1479+
1480+
omega += xi * omegas[i]
1481+
1482+
term_Tcm += xi * sqrt(Vci * Tcs[i])
1483+
1484+
Vm = 0.25 * (sum_xV + 3.0 * sum_xV_2_3 * sum_xV_1_3)
1485+
Tcm = (term_Tcm * term_Tcm) / Vm
1486+
return Tcm, Vm, omega
1487+
1488+
14211489
def COSTALD_mixture(xs: list[float], T: float, Tcs: list[float], Vcs: list[float], omegas: list[float]) -> float:
14221490
r"""Calculate mixture liquid density using the COSTALD CSP method.
14231491
@@ -1468,38 +1536,157 @@ def COSTALD_mixture(xs: list[float], T: float, Tcs: list[float], Vcs: list[float
14681536
Examples
14691537
--------
14701538
>>> COSTALD_mixture([0.4576, 0.5424], 298., [512.58, 647.29], [0.000117, 5.6e-05], [0.559,0.344])
1471-
2.7065887732713534e-05
1539+
2.706589e-05
14721540
14731541
References
14741542
----------
14751543
.. [1] Hankinson, Risdon W., and George H. Thomson. "A New Correlation for
14761544
Saturated Densities of Liquids and Their Mixtures." AIChE Journal
14771545
25, no. 4 (1979): 653-663. doi:10.1002/aic.690250412
14781546
"""
1479-
N = len(xs)
1480-
sum1, sum2, sum3, omega = 0.0, 0.0, 0.0, 0.0
1481-
for i in range(N):
1482-
sum1 += xs[i]*Vcs[i]
1483-
p = Vcs[i]**(1.0/3.)
1484-
v = xs[i]*p
1485-
sum2 += v
1486-
sum3 += v*p
1487-
omega += xs[i]*omegas[i]
1488-
1489-
Vm = 0.25*(sum1 + 3.0*sum2*sum3)
1490-
Vm_inv_root = root_two*(Vm)**-0.5
1491-
vec = [0.0]*N
1492-
for i in range(N):
1493-
vec[i] = (Tcs[i]*Vcs[i])**0.5*xs[i]*Vm_inv_root
1494-
1495-
Tcm = 0.0
1496-
for i in range(N):
1497-
for j in range(i):
1498-
Tcm += vec[i]*vec[j]
1499-
Tcm += 0.5*vec[i]*vec[i]
1547+
Tcm, Vm, omega = COSTALD_mixture_parameters(xs, Tcs, Vcs, omegas)
15001548
return COSTALD(T, Tcm, Vm, omega)
15011549

15021550

1551+
def COSTALD_mixture_compressed(xs: list[float], T: float, Tcs: list[float], Vcs: list[float], omegas: list[float], P: float, Psat: float | None = None) -> float:
1552+
r"""Calculate compressed mixture liquid density using the COSTALD-Tait CSP method,
1553+
by Hankinson, Brobst, and Thomson [1]_, corresponding to API Technical Data Book Procedure 6A3.4 [2]_.
1554+
1555+
The molar volume of the compressed liquid mixture is calculated using the
1556+
Tait equation, where the saturation volume :math:`V_s` is obtained via the
1557+
standard COSTALD mixture rules (API Procedure 6A3.2).
1558+
1559+
.. math::
1560+
V = V_s \left( 1 - C \ln \frac{B + P}{B + P_{sat}}\right)
1561+
1562+
The parameters :math:`B` and :math:`C` are generalized functions of the
1563+
mixture acentric factor :math:`\omega_m` and reduced temperature :math:`T_r = T/T_{cm}`:
1564+
1565+
.. math::
1566+
\frac{B}{P_{cm}} = -1 + a(1-T_r)^{1/3} + b(1-T_r)^{2/3} + d(1-T_r) + e(1-T_r)^{4/3}
1567+
1568+
.. math::
1569+
e = \exp(f + g\omega_{m} + h \omega_{m}^2)
1570+
1571+
.. math::
1572+
C = j + k \omega_{m}
1573+
1574+
If the saturation pressure :math:`P_{sat}` is not provided, it is estimated
1575+
using the Generalized Riedel vapor pressure equation using mixture critical properties:
1576+
1577+
.. math::
1578+
\log_{10} \frac{P_{sat}}{P_{cm}} = P_{rm}^{(0)} + \omega_m P_{rm}^{(1)}
1579+
1580+
.. math::
1581+
P_{rm}^{(0)} = 5.8031817 \log_{10} T_r + 0.07608141 \alpha
1582+
1583+
.. math::
1584+
P_{rm}^{(1)} = 4.86601 \beta
1585+
1586+
.. math::
1587+
\alpha = 35.0 - \frac{36.0}{T_r} - 96.736 \log_{10} T_r + T_r^6
1588+
1589+
.. math::
1590+
\beta = \log_{10} T_r + 0.03721754 \alpha
1591+
1592+
The mixture critical properties :math:`T_{cm}`, :math:`V_{m}`, and :math:`\omega_m`
1593+
are calculated using the COSTALD mixing rules:
1594+
1595+
.. math::
1596+
V_m = 0.25\left[ \sum_i x_i V_i^* + 3\left(\sum_i x_i (V_i^*)^{2/3}\right)\left(\sum_i x_i (V_i^*)^{1/3}\right)\right]
1597+
1598+
.. math::
1599+
V_{ij}^* T_{cij} = \sqrt{V_i^* T_{ci} V_j^* T_{cj}}
1600+
1601+
.. math::
1602+
T_{cm} = \frac{\sum_i\sum_j x_i x_j (V_{ij}^* T_{cij})}{V_m}
1603+
1604+
.. math::
1605+
\omega_m = \sum_i x_i \omega_i
1606+
1607+
.. math::
1608+
P_{cm} = \frac{(0.291 - 0.080\omega_m)RT_{cm}}{V_m}
1609+
1610+
Parameters
1611+
----------
1612+
xs : list
1613+
Mole fractions of each component [-]
1614+
T : float
1615+
Temperature of fluid [K]
1616+
Tcs : list
1617+
Critical temperature of fluids [K]
1618+
Vcs : list
1619+
Critical volumes of fluids (characteristic COSTALD volume) [m^3/mol]
1620+
omegas : list
1621+
Acentric factor (ideally SRK) of all fluids [-]
1622+
P : float
1623+
Pressure of fluid [Pa]
1624+
Psat : float, optional
1625+
Saturation pressure of the mixture [Pa]. If not provided, it is estimated
1626+
using the Generalized Riedel equation.
1627+
1628+
Returns
1629+
-------
1630+
V_dense : float
1631+
Compressed liquid mixture molar volume [m^3/mol]
1632+
1633+
Notes
1634+
-----
1635+
The constants for the Tait parameter correlations are:
1636+
a = -9.070217, b = 62.45326, d = -135.1102, f = 4.79594,
1637+
g = 0.250047, h = 1.14188, j = 0.0861488, k = 0.0344483.
1638+
1639+
This method is generally accurate to within 1% for defined hydrocarbon mixtures
1640+
below Tr = 0.95.
1641+
1642+
Examples
1643+
--------
1644+
>>> Tcs = [305.3278, 617.594]
1645+
>>> Vcs = [0.00014576928794529767, 0.0006192229409547785]
1646+
>>> omegas = [0.0983, 0.4916]
1647+
>>> xs = [0.2, 0.8]
1648+
>>> T = 344.26111
1649+
>>> P = 20684271.8795
1650+
>>> COSTALD_mixture_compressed(xs, T, Tcs, Vcs, omegas, P)
1651+
0.0001716710
1652+
1653+
References
1654+
----------
1655+
.. [1] Thomson, G. H., K. R. Brobst, and R. W. Hankinson. "An Improved
1656+
Correlation for Densities of Compressed Liquids and Liquid Mixtures."
1657+
AIChE Journal 28, no. 4 (July 1, 1982): 671-76. doi:10.1002/aic.690280420
1658+
.. [2] API Technical Data Book, Procedure 6A3.4 "Computer Method for the
1659+
Liquid Densities of Compressed Hydrocarbon Mixtures of Defined Composition."
1660+
"""
1661+
Tcm, Vm, omega = COSTALD_mixture_parameters(xs, Tcs, Vcs, omegas)
1662+
Vs = COSTALD(T, Tcm, Vm, omega)
1663+
1664+
# Calculate Pcm and then Psat if needed
1665+
Zcm = 0.291 - 0.080 * omega
1666+
Pcm = Zcm * R * Tcm / Vm
1667+
1668+
if Psat is None:
1669+
Trm = T / Tcm
1670+
1671+
# Generalized Riedel Vapor Pressure Equation (API 6A3.4-12 to 16)
1672+
if Trm > 0:
1673+
log_Trm = log10(Trm)
1674+
alpha = 35.0 - 36.0 / Trm - 96.736 * log_Trm + Trm**6
1675+
beta = log_Trm + 0.03721754 * alpha
1676+
1677+
Prm_0 = 5.8031817 * log_Trm + 0.07608141 * alpha
1678+
Prm_1 = 4.86601 * beta
1679+
1680+
log_Prm = Prm_0 + omega * Prm_1
1681+
Prm = 10.0**log_Prm
1682+
1683+
Psat = Prm * Pcm
1684+
else:
1685+
Psat = 0.0
1686+
1687+
return COSTALD_compressed(T, P, Psat, Tcm, Pcm, omega, Vs)
1688+
1689+
15031690
### Gases
15041691

15051692

conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
is_graal = "Graal" in sys.version
88
is_free_threaded = hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled()
99
ver_tup = tuple(int(x) for x in platform.python_version_tuple()[:2])
10-
is_x86_or_x86_64 = platform.machine().lower() in ("i386", "i686", "x86", "x86_64", "amd64")
10+
is_mac = sys.platform == "darwin"
11+
has_numba_platform = (platform.machine().lower() in ("i386", "i686", "x86", "x86_64", "amd64") and not is_mac) or (is_mac and platform.machine() == "arm64")
1112

1213

1314
def pytest_ignore_collect( collection_path, config):
@@ -43,7 +44,7 @@ def pytest_ignore_collect( collection_path, config):
4344
is_pypy or
4445
is_graal or
4546
is_free_threaded or
46-
not is_x86_or_x86_64
47+
not has_numba_platform
4748
)
4849
if unsupported_for_numba:
4950
if "numba" in path:

dev/cx_freeze/cx_freeze_basic_standalone_check_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"matplotlib_inline", "ply", "pydoc_data", "pygtkcompat", "pyximport", "tblib",
1010
"typed_ast", "xmlrpc", "yapf", "zope", "asgiref", "blib2to3", "certifi", "cloudpickle",
1111
"dbm", "jupyter_core", "kiwisolver", "lz4", "ptyprocess", "PySide2", "snappy",
12-
"sortedcontainers", "toml", "tomli", "tomllib", "zoneinfo", "blosc", "ephem",
12+
"sortedcontainers", "toml", "tomli", "tomllib", "blosc", "ephem",
1313
"exceptiongroup", "gast", "http", "jacobi", "lazy_object_proxy", "llvmlite",
1414
"mpi4py", "mpl_toolkits", "msgpack", "OpenSSL", "past", "pydevd_plugins", "smmap",
1515
"wrapt", "wsgiref", "xml", "argcomplete", "bs4", "executing", "ipython_genutils",

0 commit comments

Comments
 (0)