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 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__))
lmhdx/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())