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.
- hhemt/__init__.py +41 -0
- hhemt/__main__.py +10 -0
- hhemt/_filelock_compat.py +103 -0
- hhemt/analysis.py +5040 -0
- hhemt/analysis_validation.py +585 -0
- hhemt/bundle/__init__.py +406 -0
- hhemt/bundle/_combine.py +471 -0
- hhemt/bundle/_combine_merge.py +118 -0
- hhemt/bundle/_compatibility.py +264 -0
- hhemt/bundle/_emit.py +651 -0
- hhemt/bundle/_path_policy.py +129 -0
- hhemt/bundle/_protocol.py +26 -0
- hhemt/bundle/_reprex.py +326 -0
- hhemt/bundle/_reprex_gate.py +47 -0
- hhemt/bundle/snakefile_generator.py +302 -0
- hhemt/case_study_catalog.py +380 -0
- hhemt/cf_conventions.py +307 -0
- hhemt/cli.py +2124 -0
- hhemt/cli_utils.py +65 -0
- hhemt/config/__init__.py +9 -0
- hhemt/config/analysis.py +745 -0
- hhemt/config/base.py +139 -0
- hhemt/config/brand_theme.py +81 -0
- hhemt/config/case_manifest.py +67 -0
- hhemt/config/eda.py +93 -0
- hhemt/config/globus.py +274 -0
- hhemt/config/hpc_system.py +331 -0
- hhemt/config/invalidating_fixes.py +135 -0
- hhemt/config/loaders.py +51 -0
- hhemt/config/report.py +1072 -0
- hhemt/config/reprex_config.py +37 -0
- hhemt/config/reprex_taxonomy.py +210 -0
- hhemt/config/static_plots.py +403 -0
- hhemt/config/synthetic_experiment.py +226 -0
- hhemt/config/system.py +342 -0
- hhemt/config/viz_vocabulary.py +214 -0
- hhemt/consolidate_workflow.py +503 -0
- hhemt/constants.py +154 -0
- hhemt/delete_consolidation_runner.py +139 -0
- hhemt/delete_processed_runner.py +67 -0
- hhemt/delete_reprocess_zarr_runner.py +75 -0
- hhemt/delete_scenario_runner.py +105 -0
- hhemt/delete_subanalysis_reprocess_runner.py +77 -0
- hhemt/delete_subanalysis_runner.py +101 -0
- hhemt/du_sentinels.py +410 -0
- hhemt/eda/__init__.py +39 -0
- hhemt/eda/_config_diff.py +884 -0
- hhemt/eda/_context.py +128 -0
- hhemt/eda/_html_export.py +61 -0
- hhemt/eda/_local_surface.py +61 -0
- hhemt/eda/_notebook.py +172 -0
- hhemt/eda/_plotting.py +107 -0
- hhemt/eda/_promote.py +146 -0
- hhemt/eda/_report.py +126 -0
- hhemt/eda/_result.py +42 -0
- hhemt/eda/compute_sensitivity.py +640 -0
- hhemt/eda/cross_sim_identity.py +363 -0
- hhemt/examples.py +655 -0
- hhemt/exceptions.py +278 -0
- hhemt/execution.py +419 -0
- hhemt/export_scenario_status.py +455 -0
- hhemt/generate_case_manifest.py +56 -0
- hhemt/get_sim_folder.py +99 -0
- hhemt/globus_transfer.py +286 -0
- hhemt/gui.py +78 -0
- hhemt/invalidating_fixes.yaml +54 -0
- hhemt/invalidating_fixes_skew.py +189 -0
- hhemt/log.py +764 -0
- hhemt/log_utils.py +90 -0
- hhemt/metadata.py +443 -0
- hhemt/orchestration.py +368 -0
- hhemt/orchestrator_sentinels.py +121 -0
- hhemt/paths.py +140 -0
- hhemt/plot_analysis.py +37 -0
- hhemt/plot_system.py +129 -0
- hhemt/plot_utils.py +254 -0
- hhemt/prepare_scenario_runner.py +214 -0
- hhemt/process_simulation.py +2216 -0
- hhemt/process_timeseries_runner.py +428 -0
- hhemt/processing_analysis.py +521 -0
- hhemt/processing_system.py +143 -0
- hhemt/profile_catalog.py +235 -0
- hhemt/provenance.py +170 -0
- hhemt/publishing.py +380 -0
- hhemt/recompute.py +542 -0
- hhemt/render_report_runner.py +84 -0
- hhemt/report_plot_ids.py +120 -0
- hhemt/report_renderers/__init__.py +1 -0
- hhemt/report_renderers/_cli.py +174 -0
- hhemt/report_renderers/_figure_emission.py +712 -0
- hhemt/report_renderers/_hydrology_panel.py +174 -0
- hhemt/report_renderers/_map_bounds.py +59 -0
- hhemt/report_renderers/_plotly_theme.py +39 -0
- hhemt/report_renderers/_provenance.py +232 -0
- hhemt/report_renderers/_provenance_audit.py +293 -0
- hhemt/report_renderers/_react_surgery.py +270 -0
- hhemt/report_renderers/_reporting_sets.py +407 -0
- hhemt/report_renderers/_static_backend_warning.py +33 -0
- hhemt/report_renderers/_swmm_gis_layers.py +259 -0
- hhemt/report_renderers/_tabulator_defaults.py +1350 -0
- hhemt/report_renderers/cross_experiment_compatibility.py +92 -0
- hhemt/report_renderers/disk_utilization.py +98 -0
- hhemt/report_renderers/eda_compute_sensitivity.py +47 -0
- hhemt/report_renderers/errors_and_warnings.py +222 -0
- hhemt/report_renderers/metadata.py +758 -0
- hhemt/report_renderers/per_analysis_summary.py +271 -0
- hhemt/report_renderers/per_sim_conduit_flow.py +1097 -0
- hhemt/report_renderers/per_sim_peak_flood_depth.py +1600 -0
- hhemt/report_renderers/scenario_status_appendix.py +346 -0
- hhemt/report_renderers/sensitivity_benchmarking.py +1383 -0
- hhemt/report_renderers/system_overview.py +1669 -0
- hhemt/report_templates/captions/disk_utilization.rst +7 -0
- hhemt/report_templates/captions/eda_compute_sensitivity.rst +7 -0
- hhemt/report_templates/captions/errors_and_warnings.rst +5 -0
- hhemt/report_templates/captions/metadata.rst +20 -0
- hhemt/report_templates/captions/per_analysis_summary_table.rst +5 -0
- hhemt/report_templates/captions/per_sim_conduit_flow.rst +7 -0
- hhemt/report_templates/captions/per_sim_peak_flood_depth.rst +9 -0
- hhemt/report_templates/captions/scenario_status_appendix.rst +7 -0
- hhemt/report_templates/captions/sensitivity_benchmarking.rst +7 -0
- hhemt/report_templates/captions/system_map.rst +7 -0
- hhemt/report_templates/eda_report.html.j2 +39 -0
- hhemt/report_templates/report.css.j2 +215 -0
- hhemt/report_templates/workflow_description.rst.j2 +80 -0
- hhemt/reprocess_snakefile_generator.py +393 -0
- hhemt/resource_management.py +480 -0
- hhemt/run_simulation.py +1114 -0
- hhemt/run_simulation_runner.py +434 -0
- hhemt/scenario.py +1168 -0
- hhemt/scenario_inputs.py +395 -0
- hhemt/sensitivity_analysis.py +2676 -0
- hhemt/setup_workflow.py +380 -0
- hhemt/slurm_liveness.py +142 -0
- hhemt/snakemake_dry_run_report.py +245 -0
- hhemt/snakemake_snakefile_parsing.py +160 -0
- hhemt/static_snakefile_generator.py +283 -0
- hhemt/status_flags.py +129 -0
- hhemt/subprocess_utils.py +74 -0
- hhemt/summary_paths.py +91 -0
- hhemt/swmm_full_model.py +106 -0
- hhemt/swmm_output_parser.py +1365 -0
- hhemt/swmm_runoff_modeling.py +359 -0
- hhemt/swmm_schema.py +59 -0
- hhemt/swmm_utils.py +127 -0
- hhemt/synthetic_experiment.py +208 -0
- hhemt/synthetic_model/__init__.py +18 -0
- hhemt/synthetic_model/_build.py +153 -0
- hhemt/synthetic_model/geometry.py +173 -0
- hhemt/synthetic_model/landuse.py +55 -0
- hhemt/synthetic_model/swmm_template.py +828 -0
- hhemt/synthetic_model/triton_cfg.py +129 -0
- hhemt/synthetic_model/vectors.py +67 -0
- hhemt/synthetic_model/weather.py +120 -0
- hhemt/system.py +1704 -0
- hhemt/toolkit.py +444 -0
- hhemt/units.py +139 -0
- hhemt/utils.py +1085 -0
- hhemt/validation.py +1671 -0
- hhemt/version_migration/__init__.py +46 -0
- hhemt/version_migration/__main__.py +206 -0
- hhemt/version_migration/constants.py +50 -0
- hhemt/version_migration/context.py +948 -0
- hhemt/version_migration/exceptions.py +78 -0
- hhemt/version_migration/registry.py +160 -0
- hhemt/version_migration/runner.py +235 -0
- hhemt/version_migration/state.py +244 -0
- hhemt/version_migration/versions/V0001__rename_scenario_dirs.py +69 -0
- hhemt/version_migration/versions/V0002__snakemake_flag_rename.py +30 -0
- hhemt/version_migration/versions/V0003__datatree_consolidation.py +49 -0
- hhemt/version_migration/versions/V0004__cf_conventions_backfill.py +60 -0
- hhemt/version_migration/versions/V0005__inline_report_config.py +117 -0
- hhemt/version_migration/versions/V0006__fingerprint_schema_v3.py +198 -0
- hhemt/version_migration/versions/V0007__setup_target_flag_rename.py +73 -0
- hhemt/version_migration/versions/V0008__fix_per_rank_diff_aggregation.py +193 -0
- hhemt/version_migration/versions/V0009__crs_config_composite_submodel.py +118 -0
- hhemt/version_migration/versions/V0010__canonical_plot_id.py +114 -0
- hhemt/version_migration/versions/V0011__eda_subpackage.py +37 -0
- hhemt/version_migration/versions/V0012__eda_report_dir.py +30 -0
- hhemt/version_migration/versions/V0013__partition_as_sensitivity_axis.py +50 -0
- hhemt/version_migration/versions/V0014__static_plots_dir.py +22 -0
- hhemt/version_migration/versions/V0015__eda_public_notebook.py +23 -0
- hhemt/version_migration/versions/V0016__datatree_provenance_core.py +28 -0
- hhemt/version_migration/versions/V0017__version_provenance_stamp.py +28 -0
- hhemt/version_migration/versions/__init__.py +1 -0
- hhemt/wait_for_sentinel_runner.py +159 -0
- hhemt/workflow.py +8952 -0
- hhemt-0.1.0.dist-info/METADATA +125 -0
- hhemt-0.1.0.dist-info/RECORD +192 -0
- hhemt-0.1.0.dist-info/WHEEL +5 -0
- hhemt-0.1.0.dist-info/entry_points.txt +2 -0
- hhemt-0.1.0.dist-info/licenses/LICENSE +73 -0
- 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,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)
|