Reproduce an analysis on a foreign HPC system (reprex round-trip)
A reprex bundle is a render bundle upgraded into a round-trippable Workflow-Run-Crate: a second person, given only the bundle, can validate whether your analysis will run on their cluster and get an exact list of what they must supply or amend. This guide covers both ends: emitting a reprex bundle, and running the round-trip check on a foreign system.
Like publishing, this is opt-in — a reprex bundle is never emitted by analysis.run()
or submit_workflow().
Emit a reprex bundle
Emit from a completed, rendered analysis (the bundle harvests the sources declared
during render_report(), so run it first):
# Single analysis
bundle_dir = analysis.reprex_bundle()
# Sensitivity master (the primary reprex surface — the per-(sa_id, column)
# problem-pair report is intrinsically a sensitivity concept)
bundle_dir = sensitivity.reprex_bundle()
reprex_bundle() returns a directory (it emits the bundle zip, then extracts it to a
sibling directory) so the round-trip below can consume it directly. Pass
output_path=Path(...) to control where the bundle is written.
The emitted bundle root carries the minimal runnable set:
cfg_system.yaml+cfg_analysis.yaml— path-scrubbed configs (your machine-local paths are nulled; experiment fields are preserved verbatim).reprex_config.yaml— a template the reproducer fills with their values.hpc_system_config.template.yaml— a scrubbed HPC profile with{your-allocation}placeholders (never your real account).Snakefile.source— the generated workflow, typed as the crate'smainEntity.ro-crate-metadata.json— the Workflow-Run-Crate metadata; heavy inputs and (for a container run) the SIF are recorded by reference with theirsha256.
What the reproducer supplies
The reproducer fills two things with their own system's values:
- A
reprex_config— the minimal host-local field set:
from hhemt.config.reprex_config import reprex_config
my_reprex = reprex_config(
default_account="my-alloc", # your HPC allocation
sif_path="/scratch/my-alloc/tritonswmm.sif", # where YOU fetched the SIF
target_ensemble_partition="gpu-a100", # your partition for the ensemble sims
# login_node / scratch_dir / target_setup_and_analysis_processing_partition optional
)
- Their own
hpc_system_config(the target profile — partition caps, container spec). See HPC-profile setup.
Run the round-trip check
from hhemt.bundle import Bundle
result = Bundle.from_directory(bundle_dir).reprex(my_reprex, my_hpc_profile)
reprex() does three things, in order:
-
Verify the SIF. If the crate references a SIF (a container run), the digest is a mandatory, fail-closed
sha256match againstreprex_config.sif_path— a mismatch raisesProcessingErrorbefore any validation runs. A best-effortapptainer verifyPGP check runs too (result.sif_signature_okisNonewhenapptaineror the producer key is unavailable — a warning, not a failure). A native run records no SIF in its crate, soresult.sif_reference_presentisFalseand verification is a vacuous pass. -
Re-aim preflight at your profile. Validation is re-run with your partition selectors overlaid, so the report reflects your cluster's caps.
-
Report problems and amendments.
resultis aReprexResult:
| Field | Meaning |
|---|---|
runnable |
True when no sensitivity row exceeds a target partition cap |
problem_pairs |
one ValidationIssue per (sa_id, column) that exceeds a cap — the exact rows/resources to reduce |
amendments |
per-field experiment amendments, each labelled validated (a deterministic target-partition lookup pins the value) or advisory (you must decide, with a named reason) |
sif_reference_present / sif_verified / sif_signature_ok |
SIF verification outcome (see step 1) |
zero_user_info_leaks |
informational: producer tokens still present in the bundle (see below) |
if not result.runnable:
for issue in result.problem_pairs:
print(issue) # e.g. row[3].n_gpus requests 8, cap is 4
for a in result.amendments:
print(a.status, a.field_name, "->", a.to_value, f"({a.reason})")
Reconstitute a runnable system_config.yaml (bundle-relative paths resolved to the
inputs you fetched into the bundle) with
hhemt.bundle._emit.reconstitute_runnable_config(bundle_dir).
Zero-user-info status
The reprex design goal is that a shared bundle carries zero producer-specific
information — proven by a positive blocklist scan over the emitted tree. That gate
currently runs consume-side and informationally: reprex() populates
result.zero_user_info_leaks but does not fail. Hard emit-time enforcement is deferred
to an emit-hardening pass (the producer's absolute paths still leak through
bundle_manifest.json, harvested SWMM .inp FILE references, and
validation_report.json — surfaces the config-field scrub does not yet cover). Until
that lands, treat a reprex bundle as not-yet-guaranteed-private before sharing it widely.