Skip to content

Commit ab37ecc

Browse files
authored
Merge pull request #17 from pathsim/feature/ikcape-correlations
ikcape correlations
2 parents 43fd9d3 + 0c3f907 commit ab37ecc

25 files changed

Lines changed: 5579 additions & 5 deletions

.github/workflows/tests.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Run tests
2+
on: [push, pull_request]
3+
jobs:
4+
test:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
- name: Set up Python
9+
uses: actions/setup-python@v5
10+
with:
11+
python-version: '3.x'
12+
- name: Install dependencies
13+
run: |
14+
python -m pip install --upgrade pip
15+
pip install -e .[test]
16+
- name: Test with pytest
17+
run: |
18+
pytest tests/ -v

README.md

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,98 @@
33
<img src="https://raw.githubusercontent.com/pathsim/pathsim-chem/main/docs/source/logos/chem_logo.png" width="300" alt="PathSim-Chem Logo" />
44
</p>
55

6-
------------
6+
<p align="center">
7+
<strong>Chemical engineering blocks for PathSim</strong>
8+
</p>
9+
10+
<p align="center">
11+
<a href="https://pypi.org/project/pathsim-chem/"><img src="https://img.shields.io/pypi/v/pathsim-chem" alt="PyPI"></a>
12+
<img src="https://img.shields.io/github/license/pathsim/pathsim-chem" alt="License">
13+
</p>
14+
15+
<p align="center">
16+
<a href="https://docs.pathsim.org/chem">Documentation</a> &bull;
17+
<a href="https://pathsim.org">PathSim Homepage</a> &bull;
18+
<a href="https://github.com/pathsim/pathsim-chem">GitHub</a>
19+
</p>
20+
21+
---
22+
23+
PathSim-Chem extends the [PathSim](https://github.com/pathsim/pathsim) simulation framework with blocks for chemical engineering and thermodynamic property calculations. All blocks follow the standard PathSim block interface and can be connected into simulation diagrams.
24+
25+
## Features
26+
27+
- **IK-CAPE Thermodynamics** — 50+ blocks implementing the DECHEMA IK-CAPE standard for thermodynamic property calculations
28+
- **Pure Component Correlations** — Antoine, Wagner, Kirchhoff, Rackett, Aly-Lee, DIPPR, and 10 more temperature-dependent property correlations
29+
- **Mixing Rules** — Linear, quadratic, Lorentz-Berthelot, and other calculation-of-averages rules for mixture properties
30+
- **Activity Coefficients** — NRTL, Wilson, UNIQUAC, and Flory-Huggins models for liquid-phase non-ideality
31+
- **Equations of State** — Peng-Robinson and Soave-Redlich-Kwong cubic EoS with mixture support
32+
- **Fugacity Coefficients** — EoS-based and virial equation fugacity calculations
33+
- **Excess Enthalpy & Departure** — NRTL, UNIQUAC, Wilson, Redlich-Kister excess enthalpy and EoS departure functions
34+
- **Chemical Reactions** — Equilibrium constants, kinetic rate constants, and power-law rate expressions
35+
- **Tritium Processing** — GLC columns, TCAP cascades, bubblers, and splitters for tritium separation
36+
37+
## Install
38+
39+
```bash
40+
pip install pathsim-chem
41+
```
42+
43+
## Quick Example
44+
45+
Compute the vapor pressure of water at 100 °C using the Antoine equation:
46+
47+
```python
48+
from pathsim_chem.thermodynamics import Antoine
49+
50+
# Antoine coefficients for water (NIST)
51+
antoine = Antoine(a0=23.2256, a1=3835.18, a2=-45.343)
52+
53+
# Evaluate at 373.15 K
54+
antoine.inputs[0] = 373.15
55+
antoine.update(None)
56+
P_sat = antoine.outputs[0] # ≈ 101325 Pa
57+
```
58+
59+
Use activity coefficients in a simulation:
60+
61+
```python
62+
from pathsim_chem.thermodynamics import NRTL
63+
64+
# Ethanol-water NRTL model
65+
nrtl = NRTL(
66+
x=[0.4, 0.6],
67+
a=[[0, -0.801], [3.458, 0]],
68+
c=[[0, 0.3], [0.3, 0]],
69+
)
70+
71+
# Evaluate at 350 K
72+
nrtl.inputs[0] = 350
73+
nrtl.update(None)
74+
gamma_ethanol = nrtl.outputs[0]
75+
gamma_water = nrtl.outputs[1]
76+
```
77+
78+
## Modules
79+
80+
| Module | Description |
81+
|---|---|
82+
| `pathsim_chem.thermodynamics.correlations` | 16 pure component property correlations (Antoine, Wagner, etc.) |
83+
| `pathsim_chem.thermodynamics.averages` | 10 mixing rules for calculation of averages |
84+
| `pathsim_chem.thermodynamics.activity_coefficients` | NRTL, Wilson, UNIQUAC, Flory-Huggins |
85+
| `pathsim_chem.thermodynamics.equations_of_state` | Peng-Robinson, Soave-Redlich-Kwong |
86+
| `pathsim_chem.thermodynamics.corrections` | Poynting correction, Henry's law |
87+
| `pathsim_chem.thermodynamics.fugacity_coefficients` | RKS, PR, and virial fugacity coefficients |
88+
| `pathsim_chem.thermodynamics.enthalpy` | Excess enthalpy and enthalpy departure functions |
89+
| `pathsim_chem.thermodynamics.reactions` | Equilibrium constants, rate constants, power-law rates |
90+
| `pathsim_chem.tritium` | GLC, TCAP, bubbler, splitter blocks for tritium processing |
91+
92+
## Learn More
93+
94+
- [Documentation](https://docs.pathsim.org/chem) — API reference and examples
95+
- [PathSim](https://github.com/pathsim/pathsim) — the core simulation framework
96+
- [Contributing](https://docs.pathsim.org/pathsim/latest/contributing) — how to contribute
797

8-
# PathSim Chemical Engineering Toolbox
98+
## License
999

10-
This toolbox extends the core pathsim package with domain specific component models for chemical engineering.
100+
MIT
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Cubic Equations of State\n",
8+
"\n",
9+
"Simulating the compressibility factor of methane across a pressure sweep using the Peng-Robinson and Soave-Redlich-Kwong equations of state wired into a PathSim simulation."
10+
]
11+
},
12+
{
13+
"cell_type": "markdown",
14+
"metadata": {},
15+
"source": [
16+
"Cubic equations of state compute the compressibility factor $Z = Pv/(RT)$ by solving a cubic polynomial at each $(T, P)$ condition. The EoS blocks take two inputs (temperature and pressure) and produce two outputs (molar volume and compressibility factor).\n",
17+
"\n",
18+
"At low pressure $Z \\to 1$ (ideal gas). At moderate pressure attractive forces cause $Z < 1$, and at high pressure excluded-volume effects push $Z > 1$."
19+
]
20+
},
21+
{
22+
"cell_type": "code",
23+
"execution_count": null,
24+
"metadata": {},
25+
"outputs": [],
26+
"source": [
27+
"import matplotlib.pyplot as plt\n",
28+
"\n",
29+
"from pathsim import Simulation, Connection\n",
30+
"from pathsim.blocks import Source, Constant, Scope\n",
31+
"\n",
32+
"from pathsim_chem.thermodynamics import PengRobinson, RedlichKwongSoave"
33+
]
34+
},
35+
{
36+
"cell_type": "markdown",
37+
"metadata": {},
38+
"source": [
39+
"## System Definition\n",
40+
"\n",
41+
"Create Peng-Robinson and SRK blocks for pure methane. A `Constant` block supplies a fixed temperature while a `Source` sweeps pressure from 0.1 MPa to 30 MPa."
42+
]
43+
},
44+
{
45+
"cell_type": "code",
46+
"execution_count": null,
47+
"metadata": {},
48+
"outputs": [],
49+
"source": [
50+
"# Critical properties of methane\n",
51+
"Tc, Pc, omega = 190.6, 4.6e6, 0.011\n",
52+
"\n",
53+
"# EoS blocks\n",
54+
"pr = PengRobinson(Tc=Tc, Pc=Pc, omega=omega)\n",
55+
"rks = RedlichKwongSoave(Tc=Tc, Pc=Pc, omega=omega)\n",
56+
"\n",
57+
"# Fixed temperature, logarithmic pressure sweep\n",
58+
"import numpy as np\n",
59+
"T_const = Constant(250) # 250 K (above Tc, supercritical)\n",
60+
"P_src = Source(func=lambda t: 10**(4 + t * 0.035)) # 10 kPa to ~30 MPa over 100s\n",
61+
"\n",
62+
"# Scopes: record Z from both EoS (output port 1)\n",
63+
"scp_pr = Scope(labels=[\"Z_PR\"])\n",
64+
"scp_rks = Scope(labels=[\"Z_RKS\"])"
65+
]
66+
},
67+
{
68+
"cell_type": "markdown",
69+
"metadata": {},
70+
"source": [
71+
"## Wiring\n",
72+
"\n",
73+
"Both EoS blocks receive the same $(T, P)$ inputs. We record the compressibility factor (output port 1) from each."
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"metadata": {},
80+
"outputs": [],
81+
"source": [
82+
"sim = Simulation(\n",
83+
" blocks=[T_const, P_src, pr, rks, scp_pr, scp_rks],\n",
84+
" connections=[\n",
85+
" # Temperature -> both EoS (input port 0)\n",
86+
" Connection(T_const, pr, rks),\n",
87+
" # Pressure -> both EoS (input port 1)\n",
88+
" Connection(P_src, pr[1], rks[1]),\n",
89+
" # Z output (port 1) -> scopes\n",
90+
" Connection(pr[1], scp_pr),\n",
91+
" Connection(rks[1], scp_rks),\n",
92+
" ],\n",
93+
" dt=1.0,\n",
94+
")\n",
95+
"\n",
96+
"sim.run(100)"
97+
]
98+
},
99+
{
100+
"cell_type": "code",
101+
"execution_count": null,
102+
"metadata": {},
103+
"outputs": [],
104+
"source": [
105+
"time, Z_pr = scp_pr.read()\n",
106+
"_, Z_rks = scp_rks.read()\n",
107+
"P_vals = 10**(4 + time * 0.035) / 1e6 # MPa\n",
108+
"\n",
109+
"fig, ax = plt.subplots(figsize=(7, 5))\n",
110+
"ax.semilogx(P_vals, Z_pr[0], label=\"Peng-Robinson\")\n",
111+
"ax.semilogx(P_vals, Z_rks[0], \"--\", label=\"Soave-Redlich-Kwong\")\n",
112+
"ax.axhline(1.0, color=\"gray\", linestyle=\"-.\", alpha=0.5, label=\"Ideal gas\")\n",
113+
"ax.set_xlabel(\"Pressure [MPa]\")\n",
114+
"ax.set_ylabel(\"Compressibility Factor Z\")\n",
115+
"ax.set_title(\"Methane at T = 250 K\")\n",
116+
"ax.legend()\n",
117+
"ax.grid(True, alpha=0.3)\n",
118+
"plt.tight_layout()\n",
119+
"plt.show()"
120+
]
121+
},
122+
{
123+
"cell_type": "markdown",
124+
"metadata": {},
125+
"source": [
126+
"Both EoS give very similar results. The characteristic dip below $Z = 1$ at moderate pressures reflects attractive intermolecular forces, while the rise above $Z = 1$ at high pressures is due to repulsive (excluded volume) effects."
127+
]
128+
},
129+
{
130+
"cell_type": "markdown",
131+
"metadata": {},
132+
"source": [
133+
"## Mixture\n",
134+
"\n",
135+
"The EoS blocks also support mixtures through van der Waals one-fluid mixing rules. Here we set up a methane-ethane mixture and sweep pressure."
136+
]
137+
},
138+
{
139+
"cell_type": "code",
140+
"execution_count": null,
141+
"metadata": {},
142+
"outputs": [],
143+
"source": [
144+
"pr_mix = PengRobinson(\n",
145+
" Tc=[190.6, 305.3],\n",
146+
" Pc=[4.6e6, 4.872e6],\n",
147+
" omega=[0.011, 0.099],\n",
148+
" x=[0.7, 0.3],\n",
149+
")\n",
150+
"\n",
151+
"T_const2 = Constant(300)\n",
152+
"P_src2 = Source(func=lambda t: 10**(4 + t * 0.035))\n",
153+
"scp_mix = Scope(labels=[\"Z_mixture\"])\n",
154+
"\n",
155+
"sim_mix = Simulation(\n",
156+
" blocks=[T_const2, P_src2, pr_mix, scp_mix],\n",
157+
" connections=[\n",
158+
" Connection(T_const2, pr_mix),\n",
159+
" Connection(P_src2, pr_mix[1]),\n",
160+
" Connection(pr_mix[1], scp_mix),\n",
161+
" ],\n",
162+
" dt=1.0,\n",
163+
")\n",
164+
"\n",
165+
"sim_mix.run(100)"
166+
]
167+
},
168+
{
169+
"cell_type": "code",
170+
"execution_count": null,
171+
"metadata": {},
172+
"outputs": [],
173+
"source": [
174+
"time_m, Z_mix = scp_mix.read()\n",
175+
"P_mix = 10**(4 + time_m * 0.035) / 1e6\n",
176+
"\n",
177+
"fig, ax = plt.subplots(figsize=(7, 5))\n",
178+
"ax.semilogx(P_vals, Z_pr[0], label=\"Pure CH₄ (250 K)\")\n",
179+
"ax.semilogx(P_mix, Z_mix[0], \"--\", label=\"70/30 CH₄-C₂H₆ (300 K)\")\n",
180+
"ax.axhline(1.0, color=\"gray\", linestyle=\"-.\", alpha=0.5)\n",
181+
"ax.set_xlabel(\"Pressure [MPa]\")\n",
182+
"ax.set_ylabel(\"Compressibility Factor Z\")\n",
183+
"ax.set_title(\"Peng-Robinson: Pure vs Mixture\")\n",
184+
"ax.legend()\n",
185+
"ax.grid(True, alpha=0.3)\n",
186+
"plt.tight_layout()\n",
187+
"plt.show()"
188+
]
189+
},
190+
{
191+
"cell_type": "markdown",
192+
"metadata": {},
193+
"source": [
194+
"The mixture shows a deeper dip because ethane ($T_c = 305.3$ K) is near its critical temperature at 300 K, leading to stronger non-ideal behavior."
195+
]
196+
}
197+
],
198+
"metadata": {
199+
"kernelspec": {
200+
"display_name": "Python 3",
201+
"language": "python",
202+
"name": "python3"
203+
},
204+
"language_info": {
205+
"name": "python",
206+
"version": "3.11.0"
207+
}
208+
},
209+
"nbformat": 4,
210+
"nbformat_minor": 4
211+
}

0 commit comments

Comments
 (0)