hhemt 0.1.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.
Files changed (192) hide show
  1. hhemt/__init__.py +41 -0
  2. hhemt/__main__.py +10 -0
  3. hhemt/_filelock_compat.py +103 -0
  4. hhemt/analysis.py +5040 -0
  5. hhemt/analysis_validation.py +585 -0
  6. hhemt/bundle/__init__.py +406 -0
  7. hhemt/bundle/_combine.py +471 -0
  8. hhemt/bundle/_combine_merge.py +118 -0
  9. hhemt/bundle/_compatibility.py +264 -0
  10. hhemt/bundle/_emit.py +651 -0
  11. hhemt/bundle/_path_policy.py +129 -0
  12. hhemt/bundle/_protocol.py +26 -0
  13. hhemt/bundle/_reprex.py +326 -0
  14. hhemt/bundle/_reprex_gate.py +47 -0
  15. hhemt/bundle/snakefile_generator.py +302 -0
  16. hhemt/case_study_catalog.py +380 -0
  17. hhemt/cf_conventions.py +307 -0
  18. hhemt/cli.py +2124 -0
  19. hhemt/cli_utils.py +65 -0
  20. hhemt/config/__init__.py +9 -0
  21. hhemt/config/analysis.py +745 -0
  22. hhemt/config/base.py +139 -0
  23. hhemt/config/brand_theme.py +81 -0
  24. hhemt/config/case_manifest.py +67 -0
  25. hhemt/config/eda.py +93 -0
  26. hhemt/config/globus.py +274 -0
  27. hhemt/config/hpc_system.py +331 -0
  28. hhemt/config/invalidating_fixes.py +135 -0
  29. hhemt/config/loaders.py +51 -0
  30. hhemt/config/report.py +1072 -0
  31. hhemt/config/reprex_config.py +37 -0
  32. hhemt/config/reprex_taxonomy.py +210 -0
  33. hhemt/config/static_plots.py +403 -0
  34. hhemt/config/synthetic_experiment.py +226 -0
  35. hhemt/config/system.py +342 -0
  36. hhemt/config/viz_vocabulary.py +214 -0
  37. hhemt/consolidate_workflow.py +503 -0
  38. hhemt/constants.py +154 -0
  39. hhemt/delete_consolidation_runner.py +139 -0
  40. hhemt/delete_processed_runner.py +67 -0
  41. hhemt/delete_reprocess_zarr_runner.py +75 -0
  42. hhemt/delete_scenario_runner.py +105 -0
  43. hhemt/delete_subanalysis_reprocess_runner.py +77 -0
  44. hhemt/delete_subanalysis_runner.py +101 -0
  45. hhemt/du_sentinels.py +410 -0
  46. hhemt/eda/__init__.py +39 -0
  47. hhemt/eda/_config_diff.py +884 -0
  48. hhemt/eda/_context.py +128 -0
  49. hhemt/eda/_html_export.py +61 -0
  50. hhemt/eda/_local_surface.py +61 -0
  51. hhemt/eda/_notebook.py +172 -0
  52. hhemt/eda/_plotting.py +107 -0
  53. hhemt/eda/_promote.py +146 -0
  54. hhemt/eda/_report.py +126 -0
  55. hhemt/eda/_result.py +42 -0
  56. hhemt/eda/compute_sensitivity.py +640 -0
  57. hhemt/eda/cross_sim_identity.py +363 -0
  58. hhemt/examples.py +655 -0
  59. hhemt/exceptions.py +278 -0
  60. hhemt/execution.py +419 -0
  61. hhemt/export_scenario_status.py +455 -0
  62. hhemt/generate_case_manifest.py +56 -0
  63. hhemt/get_sim_folder.py +99 -0
  64. hhemt/globus_transfer.py +286 -0
  65. hhemt/gui.py +78 -0
  66. hhemt/invalidating_fixes.yaml +54 -0
  67. hhemt/invalidating_fixes_skew.py +189 -0
  68. hhemt/log.py +764 -0
  69. hhemt/log_utils.py +90 -0
  70. hhemt/metadata.py +443 -0
  71. hhemt/orchestration.py +368 -0
  72. hhemt/orchestrator_sentinels.py +121 -0
  73. hhemt/paths.py +140 -0
  74. hhemt/plot_analysis.py +37 -0
  75. hhemt/plot_system.py +129 -0
  76. hhemt/plot_utils.py +254 -0
  77. hhemt/prepare_scenario_runner.py +214 -0
  78. hhemt/process_simulation.py +2216 -0
  79. hhemt/process_timeseries_runner.py +428 -0
  80. hhemt/processing_analysis.py +521 -0
  81. hhemt/processing_system.py +143 -0
  82. hhemt/profile_catalog.py +235 -0
  83. hhemt/provenance.py +170 -0
  84. hhemt/publishing.py +380 -0
  85. hhemt/recompute.py +542 -0
  86. hhemt/render_report_runner.py +84 -0
  87. hhemt/report_plot_ids.py +120 -0
  88. hhemt/report_renderers/__init__.py +1 -0
  89. hhemt/report_renderers/_cli.py +174 -0
  90. hhemt/report_renderers/_figure_emission.py +712 -0
  91. hhemt/report_renderers/_hydrology_panel.py +174 -0
  92. hhemt/report_renderers/_map_bounds.py +59 -0
  93. hhemt/report_renderers/_plotly_theme.py +39 -0
  94. hhemt/report_renderers/_provenance.py +232 -0
  95. hhemt/report_renderers/_provenance_audit.py +293 -0
  96. hhemt/report_renderers/_react_surgery.py +270 -0
  97. hhemt/report_renderers/_reporting_sets.py +407 -0
  98. hhemt/report_renderers/_static_backend_warning.py +33 -0
  99. hhemt/report_renderers/_swmm_gis_layers.py +259 -0
  100. hhemt/report_renderers/_tabulator_defaults.py +1350 -0
  101. hhemt/report_renderers/cross_experiment_compatibility.py +92 -0
  102. hhemt/report_renderers/disk_utilization.py +98 -0
  103. hhemt/report_renderers/eda_compute_sensitivity.py +47 -0
  104. hhemt/report_renderers/errors_and_warnings.py +222 -0
  105. hhemt/report_renderers/metadata.py +758 -0
  106. hhemt/report_renderers/per_analysis_summary.py +271 -0
  107. hhemt/report_renderers/per_sim_conduit_flow.py +1097 -0
  108. hhemt/report_renderers/per_sim_peak_flood_depth.py +1600 -0
  109. hhemt/report_renderers/scenario_status_appendix.py +346 -0
  110. hhemt/report_renderers/sensitivity_benchmarking.py +1383 -0
  111. hhemt/report_renderers/system_overview.py +1669 -0
  112. hhemt/report_templates/captions/disk_utilization.rst +7 -0
  113. hhemt/report_templates/captions/eda_compute_sensitivity.rst +7 -0
  114. hhemt/report_templates/captions/errors_and_warnings.rst +5 -0
  115. hhemt/report_templates/captions/metadata.rst +20 -0
  116. hhemt/report_templates/captions/per_analysis_summary_table.rst +5 -0
  117. hhemt/report_templates/captions/per_sim_conduit_flow.rst +7 -0
  118. hhemt/report_templates/captions/per_sim_peak_flood_depth.rst +9 -0
  119. hhemt/report_templates/captions/scenario_status_appendix.rst +7 -0
  120. hhemt/report_templates/captions/sensitivity_benchmarking.rst +7 -0
  121. hhemt/report_templates/captions/system_map.rst +7 -0
  122. hhemt/report_templates/eda_report.html.j2 +39 -0
  123. hhemt/report_templates/report.css.j2 +215 -0
  124. hhemt/report_templates/workflow_description.rst.j2 +80 -0
  125. hhemt/reprocess_snakefile_generator.py +393 -0
  126. hhemt/resource_management.py +480 -0
  127. hhemt/run_simulation.py +1114 -0
  128. hhemt/run_simulation_runner.py +434 -0
  129. hhemt/scenario.py +1168 -0
  130. hhemt/scenario_inputs.py +395 -0
  131. hhemt/sensitivity_analysis.py +2676 -0
  132. hhemt/setup_workflow.py +380 -0
  133. hhemt/slurm_liveness.py +142 -0
  134. hhemt/snakemake_dry_run_report.py +245 -0
  135. hhemt/snakemake_snakefile_parsing.py +160 -0
  136. hhemt/static_snakefile_generator.py +283 -0
  137. hhemt/status_flags.py +129 -0
  138. hhemt/subprocess_utils.py +74 -0
  139. hhemt/summary_paths.py +91 -0
  140. hhemt/swmm_full_model.py +106 -0
  141. hhemt/swmm_output_parser.py +1365 -0
  142. hhemt/swmm_runoff_modeling.py +359 -0
  143. hhemt/swmm_schema.py +59 -0
  144. hhemt/swmm_utils.py +127 -0
  145. hhemt/synthetic_experiment.py +208 -0
  146. hhemt/synthetic_model/__init__.py +18 -0
  147. hhemt/synthetic_model/_build.py +153 -0
  148. hhemt/synthetic_model/geometry.py +173 -0
  149. hhemt/synthetic_model/landuse.py +55 -0
  150. hhemt/synthetic_model/swmm_template.py +828 -0
  151. hhemt/synthetic_model/triton_cfg.py +129 -0
  152. hhemt/synthetic_model/vectors.py +67 -0
  153. hhemt/synthetic_model/weather.py +120 -0
  154. hhemt/system.py +1704 -0
  155. hhemt/toolkit.py +444 -0
  156. hhemt/units.py +139 -0
  157. hhemt/utils.py +1085 -0
  158. hhemt/validation.py +1671 -0
  159. hhemt/version_migration/__init__.py +46 -0
  160. hhemt/version_migration/__main__.py +206 -0
  161. hhemt/version_migration/constants.py +50 -0
  162. hhemt/version_migration/context.py +948 -0
  163. hhemt/version_migration/exceptions.py +78 -0
  164. hhemt/version_migration/registry.py +160 -0
  165. hhemt/version_migration/runner.py +235 -0
  166. hhemt/version_migration/state.py +244 -0
  167. hhemt/version_migration/versions/V0001__rename_scenario_dirs.py +69 -0
  168. hhemt/version_migration/versions/V0002__snakemake_flag_rename.py +30 -0
  169. hhemt/version_migration/versions/V0003__datatree_consolidation.py +49 -0
  170. hhemt/version_migration/versions/V0004__cf_conventions_backfill.py +60 -0
  171. hhemt/version_migration/versions/V0005__inline_report_config.py +117 -0
  172. hhemt/version_migration/versions/V0006__fingerprint_schema_v3.py +198 -0
  173. hhemt/version_migration/versions/V0007__setup_target_flag_rename.py +73 -0
  174. hhemt/version_migration/versions/V0008__fix_per_rank_diff_aggregation.py +193 -0
  175. hhemt/version_migration/versions/V0009__crs_config_composite_submodel.py +118 -0
  176. hhemt/version_migration/versions/V0010__canonical_plot_id.py +114 -0
  177. hhemt/version_migration/versions/V0011__eda_subpackage.py +37 -0
  178. hhemt/version_migration/versions/V0012__eda_report_dir.py +30 -0
  179. hhemt/version_migration/versions/V0013__partition_as_sensitivity_axis.py +50 -0
  180. hhemt/version_migration/versions/V0014__static_plots_dir.py +22 -0
  181. hhemt/version_migration/versions/V0015__eda_public_notebook.py +23 -0
  182. hhemt/version_migration/versions/V0016__datatree_provenance_core.py +28 -0
  183. hhemt/version_migration/versions/V0017__version_provenance_stamp.py +28 -0
  184. hhemt/version_migration/versions/__init__.py +1 -0
  185. hhemt/wait_for_sentinel_runner.py +159 -0
  186. hhemt/workflow.py +8952 -0
  187. hhemt-0.1.0.dist-info/METADATA +125 -0
  188. hhemt-0.1.0.dist-info/RECORD +192 -0
  189. hhemt-0.1.0.dist-info/WHEEL +5 -0
  190. hhemt-0.1.0.dist-info/entry_points.txt +2 -0
  191. hhemt-0.1.0.dist-info/licenses/LICENSE +73 -0
  192. hhemt-0.1.0.dist-info/top_level.txt +1 -0
hhemt/__init__.py ADDED
@@ -0,0 +1,41 @@
1
+ """Top-level package for H&H Ensemble Modeling Toolkit."""
2
+
3
+ __author__ = """Daniel Lassiter"""
4
+ __email__ = "daniel.lassiter@outlook.com"
5
+
6
+ # Export high-level API
7
+ from .toolkit import Toolkit
8
+
9
+ # Export version_migration subpackage
10
+ from hhemt import version_migration # noqa: F401
11
+
12
+ # Export custom exceptions for convenient access
13
+ from .exceptions import (
14
+ TRITONSWMMError,
15
+ ConfigurationError,
16
+ CompilationError,
17
+ SimulationError,
18
+ ProcessingError,
19
+ WorkflowError,
20
+ SLURMError,
21
+ ResourceAllocationError,
22
+ CLIValidationError,
23
+ WorkflowPlanningError,
24
+ )
25
+
26
+ __all__ = [
27
+ "Toolkit",
28
+ "TRITONSWMMError",
29
+ "ConfigurationError",
30
+ "CompilationError",
31
+ "SimulationError",
32
+ "ProcessingError",
33
+ "WorkflowError",
34
+ "SLURMError",
35
+ "ResourceAllocationError",
36
+ "CLIValidationError",
37
+ "WorkflowPlanningError",
38
+ ]
39
+
40
+ # from .hhemt import run_model
41
+ # from .hhemt import examples
hhemt/__main__.py ADDED
@@ -0,0 +1,10 @@
1
+ import sys
2
+ from .cli import app
3
+ from .gui import launch_gui
4
+
5
+ if __name__ == "__main__":
6
+ # If no args, launch GUI
7
+ if len(sys.argv) == 1:
8
+ launch_gui()
9
+ else:
10
+ app()
@@ -0,0 +1,103 @@
1
+ """Filesystem-aware file locking for hhemt.
2
+
3
+ hhemt serializes concurrent JSON writes (run logs in ``log.py``, ``_version.json``
4
+ in ``version_migration``) and the synthetic-test build cache with ``filelock``.
5
+ ``filelock.FileLock`` uses ``fcntl.flock``, which several network/parallel
6
+ filesystems do NOT support: on Lustre (OLCF Frontier project / ``$MEMBERWORK``
7
+ space) ``flock`` raises ``OSError`` — the kernel leaks errno 524 (ENOTSUPP) — and
8
+ some NFS mounts raise errno 95 (EOPNOTSUPP). The hard-flock locks then abort every
9
+ log/version/cache write, which is what blocked the Frontier container-validation run.
10
+
11
+ ``resolve_filelock`` probes the lock's directory once and returns a hard
12
+ ``FileLock`` where ``flock`` works (keeping its automatic crash-release) or a
13
+ ``SoftFileLock`` (atomic ``O_CREAT | O_EXCL``, filesystem-agnostic) where it does
14
+ not. On filesystems that support ``flock`` the returned object is the SAME class
15
+ hhemt used before, so behavior off-Lustre is unchanged; only on flock-less
16
+ filesystems does the soft fallback engage. This is what makes hhemt runnable with
17
+ its working tree on Lustre (Frontier container validation, ADR-3).
18
+
19
+ Detection is by probe, not by an errno allow-list: the kernel's leaked 524 differs
20
+ from Python's ``errno.ENOTSUP`` (95), so attempting a real ``flock`` and catching
21
+ any ``OSError`` is the robust test.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ import uuid
28
+ from pathlib import Path
29
+
30
+ from filelock import BaseFileLock, FileLock, SoftFileLock
31
+
32
+ try:
33
+ import fcntl
34
+ except ImportError: # pragma: no cover - non-Unix (Windows uses msvcrt locking)
35
+ fcntl = None # type: ignore[assignment]
36
+
37
+
38
+ # flock support is a property of the FILESYSTEM, so cache the probe result by device
39
+ # id (st_dev). This makes the probe fire ONCE per filesystem (early, e.g. at the first
40
+ # cache-build lock) instead of on every lock — so render-time locks on a same-filesystem
41
+ # dir reuse the cache and create no `.flock_probe_` temp file, which otherwise trips the
42
+ # renderer-IO provenance audit (report_renderers/_provenance_audit.py) as an undeclared read.
43
+ _flock_support_by_dev: dict[int, bool] = {}
44
+
45
+
46
+ def flock_supported(directory: Path) -> bool:
47
+ """Return True iff ``fcntl.flock`` works on the filesystem hosting ``directory``.
48
+
49
+ Probes ONCE per filesystem (keyed by ``st_dev``): creates a temp file in
50
+ ``directory`` and takes + releases a non-blocking exclusive ``flock``. Any
51
+ ``OSError`` (ENOTSUPP/524 on Lustre, EOPNOTSUPP/95 on some NFS, EROFS, ...) means
52
+ ``flock`` is unavailable there. On non-Unix platforms (no ``fcntl``) ``filelock``
53
+ uses msvcrt locking, so we report supported.
54
+ """
55
+ if fcntl is None:
56
+ return True
57
+ directory = Path(directory)
58
+ try:
59
+ dev = directory.stat().st_dev
60
+ except OSError:
61
+ dev = None
62
+ if dev is not None and dev in _flock_support_by_dev:
63
+ return _flock_support_by_dev[dev]
64
+ # Probe via an EXPLICIT named probe file (O_CREAT|O_EXCL), never
65
+ # tempfile.NamedTemporaryFile: on CPython 3.12 the latter calls
66
+ # _io.open(dir, ..., opener=...), which fires an "open" audit event on the
67
+ # BARE DIRECTORY before the opener creates the named temp file. That bare-dir
68
+ # open escapes the renderer-IO provenance audit's ".flock_probe_" substring
69
+ # allowlist (the bare dir carries no such substring) and fails every per-sim
70
+ # plot rule whose render takes a cold-cache lock. The named probe path below
71
+ # carries ".flock_probe_" so the (single, named) open is allowlisted, and no
72
+ # bare-dir open is ever produced.
73
+ probe_path = directory / f".flock_probe_{os.getpid()}_{uuid.uuid4().hex}"
74
+ try:
75
+ fd = os.open(str(probe_path), os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o600)
76
+ try:
77
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
78
+ fcntl.flock(fd, fcntl.LOCK_UN)
79
+ result = True
80
+ finally:
81
+ os.close(fd)
82
+ try:
83
+ # EXEMPT-DU: transient-intermediate
84
+ probe_path.unlink()
85
+ except OSError:
86
+ pass
87
+ except OSError:
88
+ result = False
89
+ if dev is not None:
90
+ _flock_support_by_dev[dev] = result
91
+ return result
92
+
93
+
94
+ def resolve_filelock(lock_path: str | Path, timeout: float = -1) -> BaseFileLock:
95
+ """Return a ``FileLock`` (where ``flock`` works) or ``SoftFileLock`` (where it
96
+ does not) for ``lock_path``. Creates the parent directory if absent — a lock
97
+ file's directory must exist to host it. Drop-in for ``filelock.FileLock``;
98
+ the result supports the same ``with`` / ``timeout`` interface.
99
+ """
100
+ lock_path = Path(lock_path)
101
+ lock_path.parent.mkdir(parents=True, exist_ok=True)
102
+ cls = FileLock if flock_supported(lock_path.parent) else SoftFileLock
103
+ return cls(str(lock_path), timeout=timeout)