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 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