co2-eos 0.1.0__py3-none-any.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.
- co2_eos/__init__.py +265 -0
- co2_eos/data/saturation_table.npz +0 -0
- co2_eos/inversions.py +695 -0
- co2_eos/saturation.py +156 -0
- co2_eos/span_wagner.py +501 -0
- co2_eos/transport.py +286 -0
- co2_eos-0.1.0.dist-info/METADATA +193 -0
- co2_eos-0.1.0.dist-info/RECORD +11 -0
- co2_eos-0.1.0.dist-info/WHEEL +5 -0
- co2_eos-0.1.0.dist-info/licenses/LICENSE +201 -0
- co2_eos-0.1.0.dist-info/top_level.txt +1 -0
co2_eos/__init__.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""CO2-EOS: Differentiable CO₂ thermodynamic properties in JAX.
|
|
2
|
+
|
|
3
|
+
Public API
|
|
4
|
+
==========
|
|
5
|
+
|
|
6
|
+
State functions (return a dict of thermodynamic + transport properties):
|
|
7
|
+
|
|
8
|
+
properties(T, rho) — full state from (T, ρ)
|
|
9
|
+
state_from_PT(P, T) — full state from (P, T)
|
|
10
|
+
state_from_Ph(P, h) — full state from (P, h)
|
|
11
|
+
state_from_Du(rho, u) — full state from (ρ, u)
|
|
12
|
+
|
|
13
|
+
Single-property inversion:
|
|
14
|
+
|
|
15
|
+
density_from_PT(P, T) — ρ from (P, T)
|
|
16
|
+
|
|
17
|
+
Saturation curve (cubic-spline interpolation of a precomputed Maxwell table):
|
|
18
|
+
|
|
19
|
+
saturation_pressure(T) → P_sat
|
|
20
|
+
saturation_temperature(P) → T_sat
|
|
21
|
+
saturation_densities(T) → (ρ_l, ρ_v)
|
|
22
|
+
saturation_densities_P(P) → (ρ_l, ρ_v)
|
|
23
|
+
saturation_enthalpies(T) → (h_l, h_v)
|
|
24
|
+
saturation_enthalpies_P(P) → (h_l, h_v)
|
|
25
|
+
saturation_entropies(T) → (s_l, s_v)
|
|
26
|
+
saturation_entropies_P(P) → (s_l, s_v)
|
|
27
|
+
|
|
28
|
+
Transport:
|
|
29
|
+
|
|
30
|
+
viscosity(T, rho) → μ [Pa·s]
|
|
31
|
+
thermal_conductivity(T, rho) → λ [W/(m·K)]
|
|
32
|
+
|
|
33
|
+
Phase hints (for state_from_PT, state_from_Ph, state_from_Du, density_from_PT):
|
|
34
|
+
|
|
35
|
+
LIQUID = 0, VAPOR = 1, SUPERCRITICAL = 2 (alias: AUTO)
|
|
36
|
+
|
|
37
|
+
Convention
|
|
38
|
+
----------
|
|
39
|
+
Public state functions take their arguments in the order suggested by the
|
|
40
|
+
function name: ``state_from_PT(P, T)``, ``state_from_Ph(P, h)``,
|
|
41
|
+
``state_from_Du(ρ, u)``. ``properties`` and the transport correlations follow
|
|
42
|
+
the natural Helmholtz form ``(T, ρ)``.
|
|
43
|
+
|
|
44
|
+
All public functions are scalar in the inputs they accept (0-d arrays or
|
|
45
|
+
Python floats). For batches, wrap with ``jax.vmap``::
|
|
46
|
+
|
|
47
|
+
states = jax.vmap(lambda T: co2.state_from_PT(P=8e6, T=T))(T_array)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import jax
|
|
51
|
+
import jax.numpy as jnp
|
|
52
|
+
|
|
53
|
+
# Enable float64 for the entire library (also done by submodules).
|
|
54
|
+
jax.config.update("jax_enable_x64", True)
|
|
55
|
+
|
|
56
|
+
__version__ = "0.1.0"
|
|
57
|
+
|
|
58
|
+
# ── Phase hint constants ────────────────────────────────────────────────────
|
|
59
|
+
LIQUID = 0
|
|
60
|
+
VAPOR = 1
|
|
61
|
+
SUPERCRITICAL = 2
|
|
62
|
+
AUTO = SUPERCRITICAL # alias: auto-pick in the dome / supercritical-style guess
|
|
63
|
+
|
|
64
|
+
# ── Internal modules ────────────────────────────────────────────────────────
|
|
65
|
+
from co2_eos import span_wagner as _sw
|
|
66
|
+
from co2_eos import transport as _tr
|
|
67
|
+
from co2_eos import saturation as _sat
|
|
68
|
+
from co2_eos import inversions as _inv
|
|
69
|
+
|
|
70
|
+
# ── Re-exports: saturation curve ────────────────────────────────────────────
|
|
71
|
+
# Naming convention: the T-input form is the unsuffixed name; the P-input
|
|
72
|
+
# form has a "_P" suffix. The internal saturation module uses inconsistent
|
|
73
|
+
# names — these aliases give the public surface a single coherent rule.
|
|
74
|
+
saturation_pressure = _sat.saturation_pressure
|
|
75
|
+
saturation_temperature = _sat.saturation_temperature
|
|
76
|
+
saturation_densities = _sat.saturation_densities
|
|
77
|
+
saturation_densities_P = _sat.saturation_densities_P
|
|
78
|
+
saturation_enthalpies = _sat.saturation_enthalpies_T
|
|
79
|
+
saturation_enthalpies_P = _sat.saturation_enthalpies
|
|
80
|
+
saturation_entropies = _sat.saturation_entropies
|
|
81
|
+
saturation_entropies_P = _sat.saturation_entropies_P
|
|
82
|
+
|
|
83
|
+
# ── Re-exports: transport ───────────────────────────────────────────────────
|
|
84
|
+
# Re-bound as scalar-input functions so they fit the rest of the public API.
|
|
85
|
+
# The internal transport module's `viscosity`/`thermal_conductivity` are
|
|
86
|
+
# pre-vmapped and would reject scalar inputs; here we expose the scalar form.
|
|
87
|
+
_scalar_viscosity = _tr._scalar_viscosity
|
|
88
|
+
_scalar_thermal_conductivity = _tr._scalar_thermal_conductivity
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@jax.jit
|
|
92
|
+
def viscosity(T, rho):
|
|
93
|
+
"""Dynamic viscosity μ [Pa·s] at scalar (T, ρ).
|
|
94
|
+
|
|
95
|
+
For batches: ``jax.vmap(viscosity)(T_array, rho_array)``.
|
|
96
|
+
"""
|
|
97
|
+
T = jnp.asarray(T, dtype=jnp.float64)
|
|
98
|
+
rho = jnp.asarray(rho, dtype=jnp.float64)
|
|
99
|
+
return _scalar_viscosity(T, rho)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@jax.jit
|
|
103
|
+
def thermal_conductivity(T, rho):
|
|
104
|
+
"""Thermal conductivity λ [W/(m·K)] at scalar (T, ρ).
|
|
105
|
+
|
|
106
|
+
For batches: ``jax.vmap(thermal_conductivity)(T_array, rho_array)``.
|
|
107
|
+
"""
|
|
108
|
+
T = jnp.asarray(T, dtype=jnp.float64)
|
|
109
|
+
rho = jnp.asarray(rho, dtype=jnp.float64)
|
|
110
|
+
return _scalar_thermal_conductivity(T, rho)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ── State dict builder (shared helper) ──────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
def _state_dict(T, rho):
|
|
116
|
+
"""Compute every thermo + transport property at scalar (T, ρ).
|
|
117
|
+
|
|
118
|
+
Reuses the shared-derivative bundle in span_wagner so the eight
|
|
119
|
+
Helmholtz-derived properties cost a single autodiff pass.
|
|
120
|
+
"""
|
|
121
|
+
P, Cv, Cp, w, h, u, s, g = _sw._scalar_all_properties_shared(T, rho)
|
|
122
|
+
mu = _scalar_viscosity(T, rho)
|
|
123
|
+
lam = _scalar_thermal_conductivity(T, rho)
|
|
124
|
+
return {
|
|
125
|
+
"temperature": T,
|
|
126
|
+
"density": rho,
|
|
127
|
+
"pressure": P,
|
|
128
|
+
"cv": Cv,
|
|
129
|
+
"cp": Cp,
|
|
130
|
+
"speed_of_sound": w,
|
|
131
|
+
"enthalpy": h,
|
|
132
|
+
"internal_energy": u,
|
|
133
|
+
"entropy": s,
|
|
134
|
+
"gibbs_energy": g,
|
|
135
|
+
"viscosity": mu,
|
|
136
|
+
"thermal_conductivity": lam,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ── Public state functions ──────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
@jax.jit
|
|
143
|
+
def properties(T, rho):
|
|
144
|
+
"""Full thermodynamic + transport state at (T, ρ).
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
T: Temperature [K] (scalar)
|
|
148
|
+
rho: Density [kg/m³] (scalar)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
dict with keys ``temperature``, ``density``, ``pressure``, ``cv``,
|
|
152
|
+
``cp``, ``speed_of_sound``, ``enthalpy``, ``internal_energy``,
|
|
153
|
+
``entropy``, ``gibbs_energy``, ``viscosity``, ``thermal_conductivity``.
|
|
154
|
+
"""
|
|
155
|
+
T = jnp.asarray(T, dtype=jnp.float64)
|
|
156
|
+
rho = jnp.asarray(rho, dtype=jnp.float64)
|
|
157
|
+
return _state_dict(T, rho)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@jax.jit
|
|
161
|
+
def state_from_PT(P, T, phase_hint=AUTO):
|
|
162
|
+
"""Full state at (P, T). Phase-aware Halley solve for ρ, then derive all.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
P: Pressure [Pa] (scalar)
|
|
166
|
+
T: Temperature [K] (scalar)
|
|
167
|
+
phase_hint: LIQUID, VAPOR, or SUPERCRITICAL/AUTO (default AUTO).
|
|
168
|
+
AUTO is an alias for SUPERCRITICAL. For points clearly in the
|
|
169
|
+
subcritical liquid or vapor region, passing an explicit
|
|
170
|
+
``LIQUID`` or ``VAPOR`` hint gives a better initial guess and
|
|
171
|
+
more robust convergence.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
State dict (see ``properties``). ``pressure`` echoes the input ``P``.
|
|
175
|
+
"""
|
|
176
|
+
P = jnp.asarray(P, dtype=jnp.float64)
|
|
177
|
+
T = jnp.asarray(T, dtype=jnp.float64)
|
|
178
|
+
phase = jnp.asarray(phase_hint, dtype=jnp.int32)
|
|
179
|
+
rho = _inv._density_from_PT(T, P, phase)
|
|
180
|
+
state = _state_dict(T, rho)
|
|
181
|
+
state["pressure"] = P # use the constraint exactly, regardless of solver residual
|
|
182
|
+
return state
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@jax.jit
|
|
186
|
+
def density_from_PT(P, T, phase_hint=AUTO):
|
|
187
|
+
"""Density [kg/m³] at (P, T). Phase-aware Halley solve.
|
|
188
|
+
|
|
189
|
+
``phase_hint`` defaults to AUTO (alias for SUPERCRITICAL). For points
|
|
190
|
+
clearly in subcritical liquid or vapor regions, an explicit ``LIQUID``
|
|
191
|
+
or ``VAPOR`` hint gives a better initial guess and more robust
|
|
192
|
+
convergence.
|
|
193
|
+
"""
|
|
194
|
+
P = jnp.asarray(P, dtype=jnp.float64)
|
|
195
|
+
T = jnp.asarray(T, dtype=jnp.float64)
|
|
196
|
+
phase = jnp.asarray(phase_hint, dtype=jnp.int32)
|
|
197
|
+
return _inv._density_from_PT(T, P, phase)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@jax.jit
|
|
201
|
+
def state_from_Ph(P, h, phase_hint=AUTO):
|
|
202
|
+
"""Full state at (P, h). Detects the two-phase dome at subcritical P
|
|
203
|
+
and returns saturation-boundary properties; otherwise 2-D Newton on (T, ρ).
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
P: Pressure [Pa] (scalar)
|
|
207
|
+
h: Specific enthalpy [J/kg] (scalar)
|
|
208
|
+
phase_hint: LIQUID, VAPOR, or SUPERCRITICAL/AUTO (default AUTO).
|
|
209
|
+
AUTO is an alias for SUPERCRITICAL; in the dome it picks ρ_l
|
|
210
|
+
vs ρ_v by which side of the mean enthalpy ``h`` falls. For
|
|
211
|
+
points clearly in the subcritical liquid or vapor region,
|
|
212
|
+
passing an explicit ``LIQUID`` or ``VAPOR`` hint gives a
|
|
213
|
+
better initial guess and more robust convergence.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
State dict (see ``properties``). ``pressure`` and ``enthalpy``
|
|
217
|
+
echo the inputs.
|
|
218
|
+
"""
|
|
219
|
+
P = jnp.asarray(P, dtype=jnp.float64)
|
|
220
|
+
h = jnp.asarray(h, dtype=jnp.float64)
|
|
221
|
+
phase = jnp.asarray(phase_hint, dtype=jnp.int32)
|
|
222
|
+
T, rho = _inv._state_from_Ph(P, h, phase)
|
|
223
|
+
state = _state_dict(T, rho)
|
|
224
|
+
state["pressure"] = P
|
|
225
|
+
state["enthalpy"] = h
|
|
226
|
+
return state
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@jax.jit
|
|
230
|
+
def state_from_Du(rho, u, phase_hint=AUTO):
|
|
231
|
+
"""Full state at (ρ, u). 1-D Newton on u(T, ρ) at fixed ρ.
|
|
232
|
+
|
|
233
|
+
Intended for simulation codes carrying (ρ, u) as conserved variables.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
rho: Density [kg/m³] (scalar)
|
|
237
|
+
u: Specific internal energy [J/kg] (scalar)
|
|
238
|
+
phase_hint: accepted for API symmetry; not used by the (ρ, u) inversion.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
State dict (see ``properties``). ``density`` and ``internal_energy``
|
|
242
|
+
echo the inputs.
|
|
243
|
+
"""
|
|
244
|
+
rho = jnp.asarray(rho, dtype=jnp.float64)
|
|
245
|
+
u = jnp.asarray(u, dtype=jnp.float64)
|
|
246
|
+
phase = jnp.asarray(phase_hint, dtype=jnp.int32)
|
|
247
|
+
T = _inv._temperature_from_Du(rho, u, phase)
|
|
248
|
+
state = _state_dict(T, rho)
|
|
249
|
+
state["density"] = rho
|
|
250
|
+
state["internal_energy"] = u
|
|
251
|
+
return state
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
__all__ = [
|
|
255
|
+
"__version__",
|
|
256
|
+
"LIQUID", "VAPOR", "SUPERCRITICAL", "AUTO",
|
|
257
|
+
"properties",
|
|
258
|
+
"state_from_PT", "state_from_Ph", "state_from_Du",
|
|
259
|
+
"density_from_PT",
|
|
260
|
+
"saturation_pressure", "saturation_temperature",
|
|
261
|
+
"saturation_densities", "saturation_densities_P",
|
|
262
|
+
"saturation_enthalpies", "saturation_enthalpies_P",
|
|
263
|
+
"saturation_entropies", "saturation_entropies_P",
|
|
264
|
+
"viscosity", "thermal_conductivity",
|
|
265
|
+
]
|
|
Binary file
|