lmhdx 1.5.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.
- lmhdx/__init__.py +178 -0
- lmhdx/__main__.py +6 -0
- lmhdx/_fringing_common.py +1278 -0
- lmhdx/_fringing_duct.py +1534 -0
- lmhdx/_fringing_pipe.py +1452 -0
- lmhdx/advect.py +186 -0
- lmhdx/bc.py +98 -0
- lmhdx/cases.py +1709 -0
- lmhdx/cli.py +586 -0
- lmhdx/core3d.py +652 -0
- lmhdx/coreflow.py +356 -0
- lmhdx/data/benchmarks/references/alex-b1-pipe.csv +17 -0
- lmhdx/data/benchmarks/references/alex-b2-square.csv +19 -0
- lmhdx/data/benchmarks/references/samper-table-i.toml +79 -0
- lmhdx/data/benchmarks/specs/alex-b1-pipe.toml +241 -0
- lmhdx/data/benchmarks/specs/alex-b2-square.toml +292 -0
- lmhdx/data/benchmarks/specs/hunt-ha20.toml +71 -0
- lmhdx/data/benchmarks/specs/shercliff-ha20.toml +69 -0
- lmhdx/design.py +200 -0
- lmhdx/em.py +314 -0
- lmhdx/fringing.py +1738 -0
- lmhdx/grid.py +373 -0
- lmhdx/io.py +1098 -0
- lmhdx/mesh.py +1099 -0
- lmhdx/ops.py +397 -0
- lmhdx/physics.py +483 -0
- lmhdx/pipe.py +247 -0
- lmhdx/poisson.py +1026 -0
- lmhdx/py.typed +1 -0
- lmhdx/q2d.py +410 -0
- lmhdx/solvers.py +1461 -0
- lmhdx/specs.py +891 -0
- lmhdx/steady.py +580 -0
- lmhdx/timeloop.py +245 -0
- lmhdx/validation.py +1367 -0
- lmhdx-1.5.0.dist-info/METADATA +308 -0
- lmhdx-1.5.0.dist-info/RECORD +41 -0
- lmhdx-1.5.0.dist-info/WHEEL +5 -0
- lmhdx-1.5.0.dist-info/entry_points.txt +2 -0
- lmhdx-1.5.0.dist-info/licenses/LICENSE +21 -0
- lmhdx-1.5.0.dist-info/top_level.txt +1 -0
lmhdx/__init__.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Small, lazy convenience API for LMhdX.
|
|
2
|
+
|
|
3
|
+
Research and advanced APIs live in their named modules, for example
|
|
4
|
+
``lmhdx.fringing`` and ``lmhdx.cases``. Keeping the package root deliberately
|
|
5
|
+
small makes supported concepts discoverable and avoids importing JAX-heavy
|
|
6
|
+
solver modules until a symbol is used.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from importlib import import_module
|
|
13
|
+
|
|
14
|
+
# Cold runs first: cuBLAS instead of Triton GEMMs halves the GPU compile of a steady
|
|
15
|
+
# solve (9.4 s to 4.2 s on an RTX A4000) at about 10 % warm cost. XLA reads the flag
|
|
16
|
+
# when the GPU backend starts, so it is set on import; an explicit user flag wins.
|
|
17
|
+
if (
|
|
18
|
+
"xla_gpu_enable_triton_gemm" not in os.environ.get("XLA_FLAGS", "")
|
|
19
|
+
and os.environ.get("LMHDX_XLA_DEFAULTS") != "0"
|
|
20
|
+
):
|
|
21
|
+
os.environ["XLA_FLAGS"] = (
|
|
22
|
+
os.environ.get("XLA_FLAGS", "") + " --xla_gpu_enable_triton_gemm=false"
|
|
23
|
+
).strip()
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"enable_x64",
|
|
27
|
+
"enable_compilation_cache",
|
|
28
|
+
"make_hartmann_case",
|
|
29
|
+
"make_shercliff_case",
|
|
30
|
+
"make_hunt_case",
|
|
31
|
+
"make_q2d_case",
|
|
32
|
+
"evolve_q2d",
|
|
33
|
+
"solve_fully_developed_fields",
|
|
34
|
+
"Q2DProblem",
|
|
35
|
+
"solve",
|
|
36
|
+
"ChannelProblem",
|
|
37
|
+
"duct_problem",
|
|
38
|
+
"solve_steady_state",
|
|
39
|
+
"advance",
|
|
40
|
+
"generate_rect_duct_mesh",
|
|
41
|
+
"generate_rect_duct_mesh_from_faces",
|
|
42
|
+
"generate_layered_duct_mesh",
|
|
43
|
+
"generate_layered_duct_mesh_from_fluid_faces",
|
|
44
|
+
"generate_multilayer_duct_mesh",
|
|
45
|
+
"WallLayer",
|
|
46
|
+
"dynamic_to_kinematic_viscosity",
|
|
47
|
+
"kinematic_to_dynamic_viscosity",
|
|
48
|
+
"hartmann_number",
|
|
49
|
+
"reynolds_number",
|
|
50
|
+
"interaction_parameter",
|
|
51
|
+
"magnetic_reynolds_number",
|
|
52
|
+
"magnetic_field_from_hartmann",
|
|
53
|
+
"wall_conductance_ratio",
|
|
54
|
+
"effective_pinhole_conductance_ratio",
|
|
55
|
+
"tangential_stack_conductance_ratio",
|
|
56
|
+
"normal_stack_leakage_ratio",
|
|
57
|
+
"equivalent_single_layer",
|
|
58
|
+
"nested_wall_layer_resolution_summary",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def enable_x64() -> None:
|
|
63
|
+
"""Enable float64 arrays process-wide; call before constructing meshes or tracing.
|
|
64
|
+
|
|
65
|
+
This also sets JAX's ``jax_default_matmul_precision`` to ``'highest'`` when
|
|
66
|
+
that option is unset. ``'highest'`` is the same level as ``'float32'``.
|
|
67
|
+
Unset, JAX lets Ampere and newer GPUs run float32 matrix products and
|
|
68
|
+
convolutions in TensorFloat-32. On an RTX A4000, float32 contractions then
|
|
69
|
+
differed from float64 by 3e-4 relative. With the pinned precision the
|
|
70
|
+
difference was 2.6-6.1e-7. CPUs and float64 arrays are unaffected.
|
|
71
|
+
|
|
72
|
+
A value the user already chose is kept, whether set through
|
|
73
|
+
``jax.config.update`` or the ``JAX_DEFAULT_MATMUL_PRECISION`` environment
|
|
74
|
+
variable. Constructing a case with a ``dtype``, a ``ChannelProblem`` or a
|
|
75
|
+
``Q2DProblem`` applies the same rule.
|
|
76
|
+
"""
|
|
77
|
+
from jax import config
|
|
78
|
+
|
|
79
|
+
config.update("jax_enable_x64", True)
|
|
80
|
+
_pin_matmul_precision()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _pin_matmul_precision() -> str:
|
|
84
|
+
"""Set ``jax_default_matmul_precision`` to ``'highest'`` if unset; return the value in effect.
|
|
85
|
+
|
|
86
|
+
The option is part of JAX's compilation key, so functions traced before the
|
|
87
|
+
pin are traced again afterwards rather than silently keeping TensorFloat-32.
|
|
88
|
+
"""
|
|
89
|
+
from jax import config
|
|
90
|
+
|
|
91
|
+
if config.jax_default_matmul_precision is None:
|
|
92
|
+
config.update("jax_default_matmul_precision", "highest")
|
|
93
|
+
_enable_default_cache(config)
|
|
94
|
+
return config.jax_default_matmul_precision
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _enable_default_cache(config) -> None:
|
|
98
|
+
"""Keep compiled solves on disk unless the user chose otherwise, so a new process starts warm.
|
|
99
|
+
|
|
100
|
+
Programs are shared across field values, so a new Hartmann number on the same
|
|
101
|
+
mesh reuses them (compile 4.4 s to 0.45 s on an A4000) at a 35-60 % warm cost.
|
|
102
|
+
Only compiles over one second are kept, in at most 2 GiB. ``LMHDX_COMPILATION_CACHE=0``
|
|
103
|
+
disables it; a path in it sets the directory. It stays off on macOS with jaxlib
|
|
104
|
+
older than 0.10, which can crash reading back a large cached CPU program.
|
|
105
|
+
"""
|
|
106
|
+
choice = os.environ.get("LMHDX_COMPILATION_CACHE", "")
|
|
107
|
+
if choice == "0" or config.jax_compilation_cache_dir or _cache_read_unsafe():
|
|
108
|
+
return
|
|
109
|
+
from .io import enable_compilation_cache
|
|
110
|
+
|
|
111
|
+
enable_compilation_cache(choice or None, min_compile_time_secs=1.0, share_across_values=True)
|
|
112
|
+
config.update("jax_compilation_cache_max_size", 2**31)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _cache_read_unsafe() -> bool:
|
|
116
|
+
import platform
|
|
117
|
+
|
|
118
|
+
import jaxlib
|
|
119
|
+
|
|
120
|
+
major, minor = (int(part) for part in jaxlib.__version__.split(".")[:2])
|
|
121
|
+
return platform.system() == "Darwin" and (major, minor) < (0, 10)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_EXPORTS = {
|
|
125
|
+
"enable_compilation_cache": ("lmhdx.io", "enable_compilation_cache"),
|
|
126
|
+
"make_hartmann_case": ("lmhdx.cases", "make_hartmann_case"),
|
|
127
|
+
"make_shercliff_case": ("lmhdx.cases", "make_shercliff_case"),
|
|
128
|
+
"make_hunt_case": ("lmhdx.cases", "make_hunt_case"),
|
|
129
|
+
"make_q2d_case": ("lmhdx.q2d", "make_q2d_case"),
|
|
130
|
+
"evolve_q2d": ("lmhdx.q2d", "evolve_q2d"),
|
|
131
|
+
"solve_fully_developed_fields": ("lmhdx.cases", "solve_fully_developed_fields"),
|
|
132
|
+
"ChannelProblem": ("lmhdx.core3d", "ChannelProblem"),
|
|
133
|
+
"duct_problem": ("lmhdx.core3d", "duct_problem"),
|
|
134
|
+
"solve_steady_state": ("lmhdx.steady", "solve_steady_state"),
|
|
135
|
+
"advance": ("lmhdx.timeloop", "advance"),
|
|
136
|
+
"Q2DProblem": ("lmhdx.q2d", "Q2DProblem"),
|
|
137
|
+
"solve": ("lmhdx.cases", "solve"),
|
|
138
|
+
"generate_rect_duct_mesh": ("lmhdx.mesh", "generate_rect_duct_mesh"),
|
|
139
|
+
"generate_rect_duct_mesh_from_faces": ("lmhdx.mesh", "generate_rect_duct_mesh_from_faces"),
|
|
140
|
+
"generate_layered_duct_mesh": ("lmhdx.mesh", "generate_layered_duct_mesh"),
|
|
141
|
+
"generate_layered_duct_mesh_from_fluid_faces": (
|
|
142
|
+
"lmhdx.mesh",
|
|
143
|
+
"generate_layered_duct_mesh_from_fluid_faces",
|
|
144
|
+
),
|
|
145
|
+
"generate_multilayer_duct_mesh": ("lmhdx.mesh", "generate_multilayer_duct_mesh"),
|
|
146
|
+
"WallLayer": ("lmhdx.physics", "WallLayer"),
|
|
147
|
+
"dynamic_to_kinematic_viscosity": ("lmhdx.physics", "dynamic_to_kinematic_viscosity"),
|
|
148
|
+
"kinematic_to_dynamic_viscosity": ("lmhdx.physics", "kinematic_to_dynamic_viscosity"),
|
|
149
|
+
"hartmann_number": ("lmhdx.physics", "hartmann_number"),
|
|
150
|
+
"reynolds_number": ("lmhdx.physics", "reynolds_number"),
|
|
151
|
+
"interaction_parameter": ("lmhdx.physics", "interaction_parameter"),
|
|
152
|
+
"magnetic_reynolds_number": ("lmhdx.physics", "magnetic_reynolds_number"),
|
|
153
|
+
"magnetic_field_from_hartmann": ("lmhdx.physics", "magnetic_field_from_hartmann"),
|
|
154
|
+
"wall_conductance_ratio": ("lmhdx.physics", "wall_conductance_ratio"),
|
|
155
|
+
"effective_pinhole_conductance_ratio": ("lmhdx.physics", "effective_pinhole_conductance_ratio"),
|
|
156
|
+
"tangential_stack_conductance_ratio": ("lmhdx.physics", "tangential_stack_conductance_ratio"),
|
|
157
|
+
"normal_stack_leakage_ratio": ("lmhdx.physics", "normal_stack_leakage_ratio"),
|
|
158
|
+
"equivalent_single_layer": ("lmhdx.physics", "equivalent_single_layer"),
|
|
159
|
+
"nested_wall_layer_resolution_summary": ("lmhdx.physics", "nested_wall_layer_resolution_summary"),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def __getattr__(name: str):
|
|
164
|
+
"""Load a documented root export on first access."""
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
module_name, attr_name = _EXPORTS[name]
|
|
168
|
+
except KeyError as exc:
|
|
169
|
+
raise AttributeError(f"module 'lmhdx' has no attribute {name!r}") from exc
|
|
170
|
+
value = getattr(import_module(module_name), attr_name)
|
|
171
|
+
globals()[name] = value
|
|
172
|
+
return value
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def __dir__() -> list[str]:
|
|
176
|
+
"""Return module globals plus the lazy public surface for discovery."""
|
|
177
|
+
|
|
178
|
+
return sorted(set(globals()) | set(__all__))
|