lh2poolx 0.1.0__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.
- lh2poolx-0.1.0/.gitignore +7 -0
- lh2poolx-0.1.0/CHANGELOG.md +9 -0
- lh2poolx-0.1.0/CITATION.cff +10 -0
- lh2poolx-0.1.0/LICENSE +21 -0
- lh2poolx-0.1.0/PKG-INFO +92 -0
- lh2poolx-0.1.0/README.md +51 -0
- lh2poolx-0.1.0/pyproject.toml +38 -0
- lh2poolx-0.1.0/src/lh2poolx/__init__.py +19 -0
- lh2poolx-0.1.0/src/lh2poolx/pool.py +148 -0
- lh2poolx-0.1.0/tests/test_pool.py +35 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-09-09
|
|
4
|
+
|
|
5
|
+
- First public release.
|
|
6
|
+
- Isenthalpic LH2 flash with CoolProp.
|
|
7
|
+
- Explicit deposition fraction and quasi-steady ground-conduction source term.
|
|
8
|
+
- Confined-pool accumulation status to prevent use as an unqualified steady source.
|
|
9
|
+
- Optional adapters supplied by SLABx and DEGADISx; no dispersion model is a dependency.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use LH2PoolX, please cite this software and the experimental sources underlying your analysis."
|
|
3
|
+
title: "LH2PoolX"
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
date-released: 2026-09-09
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: Lee
|
|
8
|
+
given-names: Ugwiyeon
|
|
9
|
+
license: MIT
|
|
10
|
+
repository-code: "https://github.com/lyullee/lh2poolx"
|
lh2poolx-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ugwiyeon Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lh2poolx-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lh2poolx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transparent liquid-hydrogen pool source terms for dispersion models
|
|
5
|
+
Project-URL: Source, https://github.com/lyullee/lh2poolx
|
|
6
|
+
Project-URL: Documentation, https://github.com/lyullee/lh2poolx#readme
|
|
7
|
+
Author: Ugwiyeon Lee
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Ugwiyeon Lee
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: LH2,liquid hydrogen,pool evaporation,process safety,source term
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Science/Research
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering
|
|
36
|
+
Requires-Python: >=3.10
|
|
37
|
+
Requires-Dist: coolprop>=6.5
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# LH2PoolX
|
|
43
|
+
|
|
44
|
+
LH2PoolX calculates an **evidence-qualified, quasi-steady liquid-hydrogen
|
|
45
|
+
pool source term** for a declared time window. It is an upstream input module
|
|
46
|
+
for dispersion models, not a dispersion model and not a general jet-impact or
|
|
47
|
+
pool-spreading solver.
|
|
48
|
+
|
|
49
|
+
## What it calculates
|
|
50
|
+
|
|
51
|
+
For a specified release rate, storage pressure, elapsed time, substrate and
|
|
52
|
+
ground-deposition fraction, LH2PoolX calculates:
|
|
53
|
+
|
|
54
|
+
1. isenthalpic flash fraction using CoolProp;
|
|
55
|
+
2. liquid mass reaching the ground;
|
|
56
|
+
3. heat-limited evaporation flux using a semi-infinite ground-conduction
|
|
57
|
+
relation with an LH2 critical-heat-flux cap; and
|
|
58
|
+
4. the unconfined equilibrium pool area and evaporation rate.
|
|
59
|
+
|
|
60
|
+
`deposition_fraction` is deliberately explicit. The package does **not**
|
|
61
|
+
predict impingement, splashing, droplet transport, drainage, barriers, or
|
|
62
|
+
transient pool spreading. A confined result reports liquid accumulation and
|
|
63
|
+
must not be used as a steady source without a separate inventory model.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install lh2poolx
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Minimal use
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from lh2poolx import LH2Release, evaluate_pool_source
|
|
75
|
+
|
|
76
|
+
release = LH2Release(rate_kg_s=0.1055, storage_pressure_barg=1.0)
|
|
77
|
+
source = evaluate_pool_source(release, elapsed_s=300.0)
|
|
78
|
+
print(source.area_m2, source.evaporation_rate_kg_s)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Evidence boundary
|
|
82
|
+
|
|
83
|
+
The concrete heat-transfer implementation is checked against the four
|
|
84
|
+
published PRESLHY E3.4 Concrete02 mass-loss windows. That comparison is a
|
|
85
|
+
restricted substrate-and-condition check, not validation for other substrates
|
|
86
|
+
or for jet-to-ground deposition. Literature supports the physical form of
|
|
87
|
+
the source chain; it does not turn unknown deposition into a fitted value.
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT. Cite the accompanying software record when one is released, plus the
|
|
92
|
+
underlying experimental and heat-transfer sources used in an analysis.
|
lh2poolx-0.1.0/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# LH2PoolX
|
|
2
|
+
|
|
3
|
+
LH2PoolX calculates an **evidence-qualified, quasi-steady liquid-hydrogen
|
|
4
|
+
pool source term** for a declared time window. It is an upstream input module
|
|
5
|
+
for dispersion models, not a dispersion model and not a general jet-impact or
|
|
6
|
+
pool-spreading solver.
|
|
7
|
+
|
|
8
|
+
## What it calculates
|
|
9
|
+
|
|
10
|
+
For a specified release rate, storage pressure, elapsed time, substrate and
|
|
11
|
+
ground-deposition fraction, LH2PoolX calculates:
|
|
12
|
+
|
|
13
|
+
1. isenthalpic flash fraction using CoolProp;
|
|
14
|
+
2. liquid mass reaching the ground;
|
|
15
|
+
3. heat-limited evaporation flux using a semi-infinite ground-conduction
|
|
16
|
+
relation with an LH2 critical-heat-flux cap; and
|
|
17
|
+
4. the unconfined equilibrium pool area and evaporation rate.
|
|
18
|
+
|
|
19
|
+
`deposition_fraction` is deliberately explicit. The package does **not**
|
|
20
|
+
predict impingement, splashing, droplet transport, drainage, barriers, or
|
|
21
|
+
transient pool spreading. A confined result reports liquid accumulation and
|
|
22
|
+
must not be used as a steady source without a separate inventory model.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install lh2poolx
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Minimal use
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from lh2poolx import LH2Release, evaluate_pool_source
|
|
34
|
+
|
|
35
|
+
release = LH2Release(rate_kg_s=0.1055, storage_pressure_barg=1.0)
|
|
36
|
+
source = evaluate_pool_source(release, elapsed_s=300.0)
|
|
37
|
+
print(source.area_m2, source.evaporation_rate_kg_s)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Evidence boundary
|
|
41
|
+
|
|
42
|
+
The concrete heat-transfer implementation is checked against the four
|
|
43
|
+
published PRESLHY E3.4 Concrete02 mass-loss windows. That comparison is a
|
|
44
|
+
restricted substrate-and-condition check, not validation for other substrates
|
|
45
|
+
or for jet-to-ground deposition. Literature supports the physical form of
|
|
46
|
+
the source chain; it does not turn unknown deposition into a fitted value.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT. Cite the accompanying software record when one is released, plus the
|
|
51
|
+
underlying experimental and heat-transfer sources used in an analysis.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lh2poolx"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Transparent liquid-hydrogen pool source terms for dispersion models"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [{ name = "Ugwiyeon Lee" }]
|
|
13
|
+
keywords = ["liquid hydrogen", "LH2", "pool evaporation", "source term", "process safety"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Scientific/Engineering",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["CoolProp>=6.5"]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
test = ["pytest>=7"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Source = "https://github.com/lyullee/lh2poolx"
|
|
28
|
+
Documentation = "https://github.com/lyullee/lh2poolx#readme"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.sdist]
|
|
31
|
+
include = ["src", "tests", "README.md", "LICENSE", "CHANGELOG.md", "CITATION.cff"]
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/lh2poolx"]
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Evidence-qualified LH2 pool source terms for dispersion-model inputs."""
|
|
2
|
+
|
|
3
|
+
from .pool import (
|
|
4
|
+
CONCRETE_CRYOGENIC,
|
|
5
|
+
CRITICAL_HEAT_FLUX_W_M2,
|
|
6
|
+
LH2Release,
|
|
7
|
+
PoolSource,
|
|
8
|
+
Substrate,
|
|
9
|
+
evaluate_pool_source,
|
|
10
|
+
flash_vapour_fraction,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"__version__", "CONCRETE_CRYOGENIC", "CRITICAL_HEAT_FLUX_W_M2",
|
|
17
|
+
"LH2Release", "PoolSource", "Substrate", "evaluate_pool_source",
|
|
18
|
+
"flash_vapour_fraction",
|
|
19
|
+
]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Quasi-steady LH2 pool source terms with explicit evidence boundaries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
import CoolProp.CoolProp as CP
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
CRITICAL_HEAT_FLUX_W_M2 = 120e3
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Substrate:
|
|
16
|
+
"""Effective cryogenic substrate properties for the stated evidence case."""
|
|
17
|
+
|
|
18
|
+
name: str
|
|
19
|
+
conductivity_W_mK: float
|
|
20
|
+
diffusivity_m2_s: float
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# PRESLHY E3.4 Concrete02 effective cryogenic properties. Do not use this as
|
|
24
|
+
# a generic concrete correlation without an explicit applicability decision.
|
|
25
|
+
CONCRETE_CRYOGENIC = Substrate("concrete_cryogenic_e34", 2.0, 2.5e-7)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class LH2Release:
|
|
30
|
+
"""Declared inputs for an LH2 release reaching a possible ground pool."""
|
|
31
|
+
|
|
32
|
+
rate_kg_s: float
|
|
33
|
+
storage_pressure_barg: float
|
|
34
|
+
deposition_fraction: float = 1.0
|
|
35
|
+
max_radius_m: float | None = None
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
if self.rate_kg_s <= 0.0:
|
|
39
|
+
raise ValueError("rate_kg_s must be > 0")
|
|
40
|
+
if self.storage_pressure_barg < 0.0:
|
|
41
|
+
raise ValueError("storage_pressure_barg must be >= 0")
|
|
42
|
+
if not 0.0 <= self.deposition_fraction <= 1.0:
|
|
43
|
+
raise ValueError("deposition_fraction must be in [0, 1]")
|
|
44
|
+
if self.max_radius_m is not None and self.max_radius_m <= 0.0:
|
|
45
|
+
raise ValueError("max_radius_m must be > 0 when supplied")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class PoolSource:
|
|
50
|
+
"""A source term for one declared time window, not a pool trajectory."""
|
|
51
|
+
|
|
52
|
+
elapsed_s: float
|
|
53
|
+
flash_vapour_fraction: float
|
|
54
|
+
deposition_fraction: float
|
|
55
|
+
liquid_to_ground_kg_s: float
|
|
56
|
+
pool_temperature_K: float
|
|
57
|
+
latent_heat_J_kg: float
|
|
58
|
+
evaporative_flux_kg_m2_s: float
|
|
59
|
+
unconfined_radius_m: float
|
|
60
|
+
radius_m: float
|
|
61
|
+
area_m2: float
|
|
62
|
+
evaporation_rate_kg_s: float
|
|
63
|
+
liquid_accumulation_rate_kg_s: float
|
|
64
|
+
confined: bool
|
|
65
|
+
source_status: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def flash_vapour_fraction(storage_pressure_barg: float, *,
|
|
69
|
+
ambient_pressure_Pa: float = 101325.0) -> float:
|
|
70
|
+
"""Isenthalpic saturated-liquid LH2 flash fraction at ambient pressure."""
|
|
71
|
+
if storage_pressure_barg < 0.0:
|
|
72
|
+
raise ValueError("storage_pressure_barg must be >= 0")
|
|
73
|
+
p0 = storage_pressure_barg * 1e5 + ambient_pressure_Pa
|
|
74
|
+
if p0 >= CP.PropsSI("Pcrit", "Hydrogen"):
|
|
75
|
+
raise ValueError("storage pressure is at or above H2 critical pressure")
|
|
76
|
+
h0 = CP.PropsSI("H", "P", p0, "Q", 0, "Hydrogen")
|
|
77
|
+
x = CP.PropsSI("Q", "P", ambient_pressure_Pa, "H", h0, "Hydrogen")
|
|
78
|
+
return min(max(float(x), 0.0), 1.0)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _ground_flux(*, substrate: Substrate, latent_heat_J_kg: float,
|
|
82
|
+
ambient_temperature_K: float, pool_temperature_K: float,
|
|
83
|
+
elapsed_s: float,
|
|
84
|
+
critical_heat_flux_W_m2: float | None) -> float:
|
|
85
|
+
if elapsed_s <= 0.0:
|
|
86
|
+
raise ValueError("elapsed_s must be > 0")
|
|
87
|
+
if latent_heat_J_kg <= 0.0:
|
|
88
|
+
raise ValueError("latent_heat_J_kg must be > 0")
|
|
89
|
+
if critical_heat_flux_W_m2 is not None and critical_heat_flux_W_m2 <= 0.0:
|
|
90
|
+
raise ValueError("critical_heat_flux_W_m2 must be > 0 or None")
|
|
91
|
+
delta_T = ambient_temperature_K - pool_temperature_K
|
|
92
|
+
if delta_T <= 0.0:
|
|
93
|
+
return 0.0
|
|
94
|
+
q = substrate.conductivity_W_mK * delta_T / math.sqrt(
|
|
95
|
+
math.pi * substrate.diffusivity_m2_s * elapsed_s
|
|
96
|
+
)
|
|
97
|
+
if critical_heat_flux_W_m2 is not None:
|
|
98
|
+
q = min(q, critical_heat_flux_W_m2)
|
|
99
|
+
return q / latent_heat_J_kg
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def evaluate_pool_source(release: LH2Release, *, elapsed_s: float,
|
|
103
|
+
substrate: Substrate = CONCRETE_CRYOGENIC,
|
|
104
|
+
ambient_temperature_K: float = 282.0,
|
|
105
|
+
ambient_pressure_Pa: float = 101325.0,
|
|
106
|
+
critical_heat_flux_W_m2: float | None =
|
|
107
|
+
CRITICAL_HEAT_FLUX_W_M2) -> PoolSource:
|
|
108
|
+
"""Calculate a quasi-steady pool source for one elapsed-time window.
|
|
109
|
+
|
|
110
|
+
The pool area instantaneously balances the declared liquid-to-ground rate
|
|
111
|
+
against heat-limited evaporation. This is appropriate only after a pool
|
|
112
|
+
exists and where a quasi-steady source approximation is justified.
|
|
113
|
+
"""
|
|
114
|
+
x = flash_vapour_fraction(release.storage_pressure_barg,
|
|
115
|
+
ambient_pressure_Pa=ambient_pressure_Pa)
|
|
116
|
+
T_pool = float(CP.PropsSI("T", "P", ambient_pressure_Pa, "Q", 0,
|
|
117
|
+
"Hydrogen"))
|
|
118
|
+
latent = float(CP.PropsSI("H", "P", ambient_pressure_Pa, "Q", 1,
|
|
119
|
+
"Hydrogen") - CP.PropsSI(
|
|
120
|
+
"H", "P", ambient_pressure_Pa, "Q", 0,
|
|
121
|
+
"Hydrogen"))
|
|
122
|
+
flux = _ground_flux(
|
|
123
|
+
substrate=substrate, latent_heat_J_kg=latent,
|
|
124
|
+
ambient_temperature_K=ambient_temperature_K,
|
|
125
|
+
pool_temperature_K=T_pool, elapsed_s=elapsed_s,
|
|
126
|
+
critical_heat_flux_W_m2=critical_heat_flux_W_m2,
|
|
127
|
+
)
|
|
128
|
+
liquid_to_ground = release.rate_kg_s * (1.0 - x) * release.deposition_fraction
|
|
129
|
+
radius_unconfined = (0.0 if liquid_to_ground == 0.0 else math.sqrt(
|
|
130
|
+
liquid_to_ground / (math.pi * flux)))
|
|
131
|
+
radius = radius_unconfined if release.max_radius_m is None else min(
|
|
132
|
+
radius_unconfined, release.max_radius_m)
|
|
133
|
+
area = math.pi * radius * radius
|
|
134
|
+
evaporation = area * flux
|
|
135
|
+
confined = (release.max_radius_m is not None and
|
|
136
|
+
radius_unconfined > release.max_radius_m)
|
|
137
|
+
return PoolSource(
|
|
138
|
+
elapsed_s=elapsed_s, flash_vapour_fraction=x,
|
|
139
|
+
deposition_fraction=release.deposition_fraction,
|
|
140
|
+
liquid_to_ground_kg_s=liquid_to_ground, pool_temperature_K=T_pool,
|
|
141
|
+
latent_heat_J_kg=latent, evaporative_flux_kg_m2_s=flux,
|
|
142
|
+
unconfined_radius_m=radius_unconfined, radius_m=radius, area_m2=area,
|
|
143
|
+
evaporation_rate_kg_s=evaporation,
|
|
144
|
+
liquid_accumulation_rate_kg_s=max(liquid_to_ground - evaporation, 0.0),
|
|
145
|
+
confined=confined,
|
|
146
|
+
source_status=("quasi_steady_equilibrium" if not confined else
|
|
147
|
+
"confined_pool_requires_inventory_model"),
|
|
148
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from lh2poolx import LH2Release, evaluate_pool_source, flash_vapour_fraction
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_flash_rises_with_storage_pressure():
|
|
9
|
+
values = [flash_vapour_fraction(p) for p in (0.8, 2.0, 6.0)]
|
|
10
|
+
assert values[0] < values[1] < values[2]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_unconfined_source_closes_the_mass_balance():
|
|
14
|
+
source = evaluate_pool_source(LH2Release(0.1055, 1.0), elapsed_s=300.0)
|
|
15
|
+
assert source.source_status == "quasi_steady_equilibrium"
|
|
16
|
+
assert source.evaporation_rate_kg_s == pytest.approx(
|
|
17
|
+
source.liquid_to_ground_kg_s)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_radius_scales_with_deposition_fraction():
|
|
21
|
+
full = evaluate_pool_source(LH2Release(0.1055, 1.0, 1.0), elapsed_s=300.0)
|
|
22
|
+
quarter = evaluate_pool_source(LH2Release(0.1055, 1.0, 0.25), elapsed_s=300.0)
|
|
23
|
+
assert quarter.radius_m / full.radius_m == pytest.approx(0.5)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_confinement_reports_accumulation():
|
|
27
|
+
source = evaluate_pool_source(LH2Release(9.5, 0.1, max_radius_m=0.5),
|
|
28
|
+
elapsed_s=300.0)
|
|
29
|
+
assert source.confined
|
|
30
|
+
assert source.liquid_accumulation_rate_kg_s > 0.0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_bad_deposition_fraction_is_rejected():
|
|
34
|
+
with pytest.raises(ValueError):
|
|
35
|
+
LH2Release(1.0, 1.0, 1.1)
|