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.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .venv/
6
+ build/
7
+ dist/
@@ -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.
@@ -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.
@@ -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)