co2-eos 0.3.0__tar.gz → 0.3.1__tar.gz
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-0.3.0 → co2_eos-0.3.1}/PKG-INFO +1 -1
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/__init__.py +1 -1
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/inversions.py +87 -27
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos.egg-info/PKG-INFO +1 -1
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos.egg-info/SOURCES.txt +1 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/pyproject.toml +1 -1
- co2_eos-0.3.1/tests/test_density_from_PT_branch.py +113 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/LICENSE +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/README.md +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/core.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/data/chi_ref_cheb.npz +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/data/saturation_table.npz +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/data/seed_table.npz +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/helmholtz.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/saturation.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/span_wagner.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos/transport.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos.egg-info/dependency_links.txt +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos.egg-info/requires.txt +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/co2_eos.egg-info/top_level.txt +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/setup.cfg +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_accuracy_vs_coolprop.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_analytic_derivs.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_dome_detection.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_fastpath_validation.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_inversion_gradients.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_map_fresh.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_no_coolprop_runtime.py +0 -0
- {co2_eos-0.3.0 → co2_eos-0.3.1}/tests/test_rho_u_inversion.py +0 -0
|
@@ -52,7 +52,7 @@ import jax.numpy as jnp
|
|
|
52
52
|
# Enable float64 for the entire library (also done by submodules).
|
|
53
53
|
jax.config.update("jax_enable_x64", True)
|
|
54
54
|
|
|
55
|
-
__version__ = "0.3.
|
|
55
|
+
__version__ = "0.3.1"
|
|
56
56
|
|
|
57
57
|
# ── Phase hint constants ────────────────────────────────────────────────────
|
|
58
58
|
LIQUID = 0
|
|
@@ -54,6 +54,23 @@ _du_drho = jax.grad(sw._scalar_internal_energy, argnums=1)
|
|
|
54
54
|
|
|
55
55
|
# ── Phase-aware initial guess ─────────────────────────────────────────────
|
|
56
56
|
|
|
57
|
+
def _subcritical_branch(T, P):
|
|
58
|
+
"""Which stable branch a (T, P) state below TC lies on, and its bracket.
|
|
59
|
+
|
|
60
|
+
Below TC the Span-Wagner isotherm is an analytic continuation through the
|
|
61
|
+
dome: it loops between the spinodals and can swing far above P_sat (at
|
|
62
|
+
294 K it reaches 12 MPa near 550 kg/m³), so P(T, ρ) = P_target has spurious
|
|
63
|
+
roots inside the dome for any P_target below that swing. The only way to
|
|
64
|
+
pick the physical root is by P against P_sat(T): P ≥ P_sat is compressed
|
|
65
|
+
liquid, ρ ≥ ρ_l(T); P < P_sat is vapor, ρ ≤ ρ_v(T). Returns
|
|
66
|
+
(liquid_side, rho_l_T, rho_v_T); the saturation table is used only where
|
|
67
|
+
T < TC, so its behaviour above TC does not matter here.
|
|
68
|
+
"""
|
|
69
|
+
rho_l_T, rho_v_T = sat.saturation_densities(T)
|
|
70
|
+
liquid_side = P >= sat.saturation_pressure(T)
|
|
71
|
+
return liquid_side, rho_l_T, rho_v_T
|
|
72
|
+
|
|
73
|
+
|
|
57
74
|
def _initial_guess(T, P, phase_hint):
|
|
58
75
|
"""Return an initial density guess based on phase_hint.
|
|
59
76
|
|
|
@@ -61,10 +78,7 @@ def _initial_guess(T, P, phase_hint):
|
|
|
61
78
|
Uses jnp.where for JIT traceability.
|
|
62
79
|
"""
|
|
63
80
|
subcritical_T = T < sw.TC
|
|
64
|
-
|
|
65
|
-
# T-based saturation densities — good for compressed liquid (rho_l at T)
|
|
66
|
-
# and low-pressure vapor (rho_v at T)
|
|
67
|
-
rho_l_T, rho_v_T = sat.saturation_densities(T)
|
|
81
|
+
liquid_side, rho_l_T, rho_v_T = _subcritical_branch(T, P)
|
|
68
82
|
|
|
69
83
|
# Liquid: saturation liquid density at T (good for compressed liquid),
|
|
70
84
|
# fall back to 2*RHOC for supercritical T
|
|
@@ -72,23 +86,22 @@ def _initial_guess(T, P, phase_hint):
|
|
|
72
86
|
# Vapor: saturation vapor density at T, fall back to ideal gas
|
|
73
87
|
ideal_gas_rho = P / (sw.R * T)
|
|
74
88
|
vapor_guess = jnp.where(subcritical_T, rho_v_T, ideal_gas_rho)
|
|
75
|
-
#
|
|
76
|
-
# Near PC and TC
|
|
77
|
-
#
|
|
78
|
-
# When T is well below TC (liquid-like regardless of pressure), use
|
|
79
|
-
# saturated liquid density at T — same logic as the LIQUID branch.
|
|
89
|
+
# Auto, T >= TC: the isotherm is monotone, so any pressure-aware seed
|
|
90
|
+
# converges. Near PC and TC use RHOC; liquid-like (P > 1.5*PC) 2*RHOC;
|
|
91
|
+
# otherwise ideal gas.
|
|
80
92
|
near_critical = jnp.logical_and(
|
|
81
93
|
jnp.abs(P - sw.PC) < 0.2 * sw.PC,
|
|
82
94
|
jnp.abs(T - sw.TC) < 15.0,
|
|
83
95
|
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
jnp.where(
|
|
88
|
-
near_critical, sw.RHOC,
|
|
89
|
-
jnp.where(P > 1.5 * sw.PC, 2.0 * sw.RHOC, ideal_gas_rho)
|
|
90
|
-
)
|
|
96
|
+
sc_guess_above_TC = jnp.where(
|
|
97
|
+
near_critical, sw.RHOC,
|
|
98
|
+
jnp.where(P > 1.5 * sw.PC, 2.0 * sw.RHOC, ideal_gas_rho)
|
|
91
99
|
)
|
|
100
|
+
# Auto, T < TC: seed on the stable branch P_sat(T) selects. A seed inside
|
|
101
|
+
# the dome (RHOC, or the ideal-gas density) lands Halley on a spurious
|
|
102
|
+
# root of the isotherm's loop, with dP/dρ > 0 and no sign of trouble.
|
|
103
|
+
sc_guess_below_TC = jnp.where(liquid_side, rho_l_T, rho_v_T)
|
|
104
|
+
sc_guess = jnp.where(subcritical_T, sc_guess_below_TC, sc_guess_above_TC)
|
|
92
105
|
|
|
93
106
|
rho0 = jnp.where(
|
|
94
107
|
phase_hint == LIQUID, liquid_guess,
|
|
@@ -144,16 +157,16 @@ def _halley_cond(state):
|
|
|
144
157
|
_BISECT_LO = 1.0 # kg/m³ — lower bound
|
|
145
158
|
_BISECT_HI = 1200.0 # kg/m³ — upper bound
|
|
146
159
|
_BISECT_ITERS = 60 # ~10⁻¹⁸ relative precision
|
|
160
|
+
_BRANCH_TOL = 1e-4 # relative slack around ρ_l(T), ρ_v(T) for the branch tests
|
|
147
161
|
|
|
148
162
|
|
|
149
163
|
def _bisection_body(state):
|
|
150
164
|
"""One bisection step on f(ρ) = P(T,ρ) − P_target.
|
|
151
165
|
|
|
152
|
-
Monotonicity assumption: P(T,ρ) is monotonically increasing in ρ
|
|
153
|
-
single-phase stable
|
|
154
|
-
spinodal
|
|
155
|
-
|
|
156
|
-
for subcritical dome-crossing (handled by dome detection in state_from_Ph).
|
|
166
|
+
Monotonicity assumption: P(T,ρ) is monotonically increasing in ρ on the
|
|
167
|
+
bracket. That holds on any single-phase stable branch (∂P/∂ρ > 0) and
|
|
168
|
+
fails inside the spinodal loop, so below TC the caller brackets the
|
|
169
|
+
stable branch (`_bisection_bracket`) instead of the whole [1, 1200] range.
|
|
157
170
|
"""
|
|
158
171
|
lo, hi, T, P_target, i = state
|
|
159
172
|
mid = 0.5 * (lo + hi)
|
|
@@ -170,16 +183,61 @@ def _bisection_cond(state):
|
|
|
170
183
|
return jnp.logical_and(i < _BISECT_ITERS, (hi - lo) > 1e-14 * hi)
|
|
171
184
|
|
|
172
185
|
|
|
173
|
-
def
|
|
174
|
-
"""
|
|
175
|
-
|
|
186
|
+
def _bisection_bracket(T, P, phase_hint):
|
|
187
|
+
"""Density bracket [lo, hi] on which P(T, ρ) is monotone for this state.
|
|
188
|
+
|
|
189
|
+
Above TC the whole range. Below TC the stable branch: [ρ_l(T), 1200] on
|
|
190
|
+
the liquid side, [1, ρ_v(T)] on the vapor side, the side taken from the
|
|
191
|
+
explicit hint or, under auto, from P against P_sat(T).
|
|
192
|
+
"""
|
|
193
|
+
subcritical_T = T < sw.TC
|
|
194
|
+
liquid_side, rho_l_T, rho_v_T = _subcritical_branch(T, P)
|
|
195
|
+
liquid_side = jnp.where(
|
|
196
|
+
phase_hint == LIQUID, True,
|
|
197
|
+
jnp.where(phase_hint == VAPOR, False, liquid_side)
|
|
198
|
+
)
|
|
199
|
+
# A slack of _BRANCH_TOL keeps the saturation point itself inside the
|
|
200
|
+
# bracket (the spline carries ~6e-8 relative error) while excluding the
|
|
201
|
+
# dome interior, whose roots sit tens of percent away.
|
|
202
|
+
lo = jnp.where(jnp.logical_and(subcritical_T, liquid_side),
|
|
203
|
+
rho_l_T * (1.0 - _BRANCH_TOL), _BISECT_LO)
|
|
204
|
+
hi = jnp.where(jnp.logical_and(subcritical_T, jnp.logical_not(liquid_side)),
|
|
205
|
+
rho_v_T * (1.0 + _BRANCH_TOL), _BISECT_HI)
|
|
206
|
+
return lo, hi
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _bisection_solve(T, P, lo=_BISECT_LO, hi=_BISECT_HI):
|
|
210
|
+
"""Bisection solve for ρ given (T, P) on [lo, hi]. Scalar inputs."""
|
|
211
|
+
init_state = (jnp.asarray(lo, dtype=jnp.float64), jnp.asarray(hi, dtype=jnp.float64),
|
|
212
|
+
T, P, jnp.int32(0))
|
|
176
213
|
final_state = jax.lax.while_loop(_bisection_cond, _bisection_body, init_state)
|
|
177
214
|
lo, hi, _, _, _ = final_state
|
|
178
215
|
return 0.5 * (lo + hi)
|
|
179
216
|
|
|
180
217
|
|
|
218
|
+
def _on_requested_branch(T, P, rho, phase_hint):
|
|
219
|
+
"""Under the auto hint below TC, is the converged root on the stable branch?
|
|
220
|
+
|
|
221
|
+
A root inside the dome satisfies P(T, ρ) = P_target with ∂P/∂ρ > 0 and
|
|
222
|
+
passes every residual test, so the only check that catches it is its
|
|
223
|
+
position relative to the saturation densities. Explicit LIQUID/VAPOR hints
|
|
224
|
+
are the caller's assertion (a metastable request is legitimate) and are
|
|
225
|
+
not checked.
|
|
226
|
+
"""
|
|
227
|
+
subcritical_T = T < sw.TC
|
|
228
|
+
liquid_side, rho_l_T, rho_v_T = _subcritical_branch(T, P)
|
|
229
|
+
on_branch = jnp.where(liquid_side, rho >= rho_l_T * (1.0 - _BRANCH_TOL),
|
|
230
|
+
rho <= rho_v_T * (1.0 + _BRANCH_TOL))
|
|
231
|
+
checked = jnp.logical_and(subcritical_T, phase_hint == SUPERCRITICAL)
|
|
232
|
+
return jnp.logical_or(jnp.logical_not(checked), on_branch)
|
|
233
|
+
|
|
234
|
+
|
|
181
235
|
def _solve_density(T, P, phase_hint):
|
|
182
|
-
"""Halley solve for ρ given (T, P, phase_hint), with bisection fallback. Scalar inputs.
|
|
236
|
+
"""Halley solve for ρ given (T, P, phase_hint), with bisection fallback. Scalar inputs.
|
|
237
|
+
|
|
238
|
+
A root on the wrong side of the dome under the auto hint is returned as
|
|
239
|
+
NaN, the package's failure signal, never as a number.
|
|
240
|
+
"""
|
|
183
241
|
rho0 = _initial_guess(T, P, phase_hint)
|
|
184
242
|
init_state = (rho0, T, P, jnp.int32(0), jnp.bool_(False))
|
|
185
243
|
final_state = jax.lax.while_loop(_halley_cond, _halley_body, init_state)
|
|
@@ -187,17 +245,19 @@ def _solve_density(T, P, phase_hint):
|
|
|
187
245
|
|
|
188
246
|
# Bisection safety net — only runs when Halley did not converge
|
|
189
247
|
def _bisect_branch(_):
|
|
190
|
-
|
|
248
|
+
lo, hi = _bisection_bracket(T, P, phase_hint)
|
|
249
|
+
rho_bisect = _bisection_solve(T, P, lo, hi)
|
|
191
250
|
bisect_residual = jnp.abs(sw._scalar_pressure(T, rho_bisect) - P)
|
|
192
251
|
bisect_ok = bisect_residual < 1e-3 * jnp.maximum(jnp.abs(P), 1.0)
|
|
193
252
|
return jnp.where(bisect_ok, rho_bisect, jnp.nan)
|
|
194
253
|
|
|
195
|
-
|
|
254
|
+
rho = jax.lax.cond(
|
|
196
255
|
converged,
|
|
197
256
|
lambda _: rho_halley,
|
|
198
257
|
_bisect_branch,
|
|
199
258
|
None,
|
|
200
259
|
)
|
|
260
|
+
return jnp.where(_on_requested_branch(T, P, rho, phase_hint), rho, jnp.nan)
|
|
201
261
|
|
|
202
262
|
|
|
203
263
|
# ── Implicit differentiation via custom_jvp ───────────────────────────────
|
|
@@ -18,6 +18,7 @@ co2_eos/data/saturation_table.npz
|
|
|
18
18
|
co2_eos/data/seed_table.npz
|
|
19
19
|
tests/test_accuracy_vs_coolprop.py
|
|
20
20
|
tests/test_analytic_derivs.py
|
|
21
|
+
tests/test_density_from_PT_branch.py
|
|
21
22
|
tests/test_dome_detection.py
|
|
22
23
|
tests/test_fastpath_validation.py
|
|
23
24
|
tests/test_inversion_gradients.py
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
density_from_PT below TC must land on the stable branch under the auto hint.
|
|
3
|
+
|
|
4
|
+
Below TC the Span-Wagner isotherm is an analytic continuation through the dome
|
|
5
|
+
and loops between the spinodals; at 294 K it swings up to about 12 MPa near
|
|
6
|
+
550 kg/m³, so P(T, ρ) = P_target has roots inside the dome for any target below
|
|
7
|
+
that swing, each with ∂P/∂ρ > 0 and a zero residual. A solve seeded inside the
|
|
8
|
+
dome converges to one of them and reports nothing wrong. The regression that
|
|
9
|
+
exposed this: T = 294.15 K (0.02 K above the old `T < TC - 10` liquid guard),
|
|
10
|
+
P = 8.12 and 9.04 MPa, auto hint, returned 499 and 506 kg/m³ against the
|
|
11
|
+
compressed-liquid 821 and 836.
|
|
12
|
+
|
|
13
|
+
The contract tested here: under the auto hint the seed, the bisection bracket
|
|
14
|
+
and the branch guard all follow P against P_sat(T), so the result matches
|
|
15
|
+
CoolProp on the liquid side (P ≥ P_sat) and on the vapor side (P < P_sat) at
|
|
16
|
+
every subcritical temperature, through jit and vmap, and a root on the wrong
|
|
17
|
+
side of the dome is never returned as a number.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import pytest
|
|
22
|
+
import jax
|
|
23
|
+
import jax.numpy as jnp
|
|
24
|
+
|
|
25
|
+
jax.config.update("jax_enable_x64", True)
|
|
26
|
+
|
|
27
|
+
import co2_eos
|
|
28
|
+
from co2_eos import span_wagner as sw
|
|
29
|
+
from co2_eos import saturation as sat
|
|
30
|
+
from co2_eos.inversions import _on_requested_branch
|
|
31
|
+
|
|
32
|
+
CP = pytest.importorskip("CoolProp.CoolProp")
|
|
33
|
+
|
|
34
|
+
RTOL = 1e-8
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _ref(T, P):
|
|
38
|
+
return CP.PropsSI("D", "T", float(T), "P", float(P), "CO2")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ── The regression points ───────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("T,P", [
|
|
44
|
+
(294.15, 8.12e6),
|
|
45
|
+
(294.15, 9.04e6),
|
|
46
|
+
(294.15, 6.5e6),
|
|
47
|
+
(296.15, 6.5e6),
|
|
48
|
+
])
|
|
49
|
+
def test_regression_points_match_liquid_hint_and_coolprop(T, P):
|
|
50
|
+
rho_auto = float(co2_eos.density_from_PT(P, T, co2_eos.AUTO))
|
|
51
|
+
rho_liq = float(co2_eos.density_from_PT(P, T, co2_eos.LIQUID))
|
|
52
|
+
ref = _ref(T, P)
|
|
53
|
+
assert rho_auto == pytest.approx(ref, rel=RTOL)
|
|
54
|
+
assert rho_liq == pytest.approx(ref, rel=RTOL)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ── Both sides of the dome, every subcritical temperature ───────────────────
|
|
58
|
+
|
|
59
|
+
def _subcritical_grid():
|
|
60
|
+
for T in np.linspace(220.0, 304.0, 22):
|
|
61
|
+
P_sat = CP.PropsSI("P", "T", T, "Q", 0, "CO2")
|
|
62
|
+
for P in np.geomspace(P_sat * 1.0005, 30e6, 8):
|
|
63
|
+
yield T, P, "liquid"
|
|
64
|
+
for P in np.geomspace(0.1e6, P_sat * 0.9995, 5):
|
|
65
|
+
yield T, P, "vapor"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("T,P,side", list(_subcritical_grid()))
|
|
69
|
+
def test_auto_hint_follows_P_sat_below_TC(T, P, side):
|
|
70
|
+
rho = float(co2_eos.density_from_PT(P, T, co2_eos.AUTO))
|
|
71
|
+
ref = _ref(T, P)
|
|
72
|
+
assert np.isfinite(rho), f"NaN at T={T}, P={P} ({side})"
|
|
73
|
+
assert rho == pytest.approx(ref, rel=RTOL)
|
|
74
|
+
rho_l, rho_v = sat.saturation_densities(jnp.float64(T))
|
|
75
|
+
if side == "liquid":
|
|
76
|
+
assert rho >= float(rho_l) * (1.0 - 1e-4)
|
|
77
|
+
else:
|
|
78
|
+
assert rho <= float(rho_v) * (1.0 + 1e-4)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ── jit and vmap through the public surface, mixed hints and sides ──────────
|
|
82
|
+
|
|
83
|
+
def test_jit_vmap_mixed_hints():
|
|
84
|
+
P = jnp.array([8.12e6, 9.04e6, 6.5e6, 9.04e6, 12e6, 3.0e6])
|
|
85
|
+
T = jnp.array([294.15, 294.15, 296.15, 320.0, 310.0, 290.0])
|
|
86
|
+
hint = jnp.array([co2_eos.AUTO, co2_eos.AUTO, co2_eos.AUTO,
|
|
87
|
+
co2_eos.AUTO, co2_eos.LIQUID, co2_eos.AUTO])
|
|
88
|
+
f = jax.jit(jax.vmap(co2_eos.density_from_PT, in_axes=(0, 0, 0)))
|
|
89
|
+
rho = np.asarray(f(P, T, hint))
|
|
90
|
+
ref = np.array([_ref(t, p) for p, t in zip(np.asarray(P), np.asarray(T))])
|
|
91
|
+
np.testing.assert_allclose(rho, ref, rtol=RTOL)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_gradient_on_the_liquid_branch_near_the_old_hole():
|
|
95
|
+
T, P = 294.15, 9.04e6
|
|
96
|
+
g = float(jax.grad(lambda t: co2_eos.density_from_PT(P, t, co2_eos.AUTO))(T))
|
|
97
|
+
ref = CP.PropsSI("d(D)/d(T)|P", "T", T, "P", P, "CO2")
|
|
98
|
+
assert g == pytest.approx(ref, rel=1e-6)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ── The branch guard itself ────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
def test_branch_guard_rejects_a_dome_root_under_auto_only():
|
|
104
|
+
T, P = jnp.float64(294.15), jnp.float64(8.12e6)
|
|
105
|
+
dome_root = jnp.float64(499.33) # the old spurious answer
|
|
106
|
+
liquid_root = jnp.float64(820.72)
|
|
107
|
+
assert not bool(_on_requested_branch(T, P, dome_root, jnp.int32(co2_eos.AUTO)))
|
|
108
|
+
assert bool(_on_requested_branch(T, P, liquid_root, jnp.int32(co2_eos.AUTO)))
|
|
109
|
+
# explicit hints are the caller's assertion and are not checked
|
|
110
|
+
assert bool(_on_requested_branch(T, P, dome_root, jnp.int32(co2_eos.LIQUID)))
|
|
111
|
+
# above TC nothing is checked
|
|
112
|
+
assert bool(_on_requested_branch(jnp.float64(sw.TC + 5.0), P, dome_root,
|
|
113
|
+
jnp.int32(co2_eos.AUTO)))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|