We separate run state (files needed to resume/monitor a run) from results artifacts (PVD/XDMF/NPY and post-processing outputs), and keeps everything under one case-root directory.
Use one root for each case, for example:
/data/output/cases/<case_id>/
Where <case_id> is unique and sortable, e.g.:
20260222_1535_penaltydebug9case_0142
Within each case:
/data/output/cases/<case_id>/
├── run/
│ ├── config_snapshot/
│ │ ├── CONFIGPenalty.py
│ │ └── runtime_env.txt
│ ├── logs/
│ │ ├── run.log
│ │ └── progress.log
│ ├── checkpoint/
│ │ └── restart_state
│ └── exchange/
│ ├── p_init.npy
│ ├── xi_rot_prev.npy
│ ├── coupling_error.txt
│ ├── load_balance_err.txt
│ ├── d_coupling_errs.txt
│ └── d_load_balance_err.txt
├── results/
│ ├── steady/
│ │ ├── macroscale/
│ │ └── microscale/
│ ├── transient/
│ │ ├── t_0000p00/
│ │ ├── t_0000p05/
│ │ └── ...
│ ├── viz/
│ │ ├── pvd/
│ │ ├── xdmf/
│ │ └── vtu/
│ └── derived/
│ ├── forces.txt
│ ├── friction.txt
│ └── summary.csv
└── meta/
├── case_manifest.yaml
└── tags.txt
- Single case root: all files for one run stay in one tree.
- Run-state vs results split:
run/= restart + orchestration I/Oresults/= simulation outputs + post-processing
- Immutable result artifacts:
- avoid overwriting old time-step files
- append/write new files by timestep or iteration
- Config snapshot per run:
- copy
CONFIGPenalty.py(and environment summary) intorun/config_snapshot/
- copy
- Optional convenience symlink in repo root:
latest_case -> /data/output/cases/<case_id>
- Keep
run/andmeta/always. - Keep full
results/for important runs. - For routine runs, prune heavy
results/viz/vtu/but retainresults/viz/pvd,results/viz/xdmf, andresults/derived.
- Add a
CASE_ROOTvariable (derived once) and use it everywhere. - Set:
OUTPUT_DIR="$CASE_ROOT/run/exchange"for run-time exchange files.- writer paths for visualization/results under
"$CASE_ROOT/results/...".
- Keep backward compatibility for one transition period by mirroring key files or maintaining a symlink.
- Add one small cleanup/archive script to:
- compress old cases
- prune transient VTU files by policy.
Without changing solver internals yet:
- Define
CASE_ROOT=/data/output/cases/<case_id>in launcher scripts. - Point existing
OUTPUT_DIRat"$CASE_ROOT/run/exchange". - Move
run.log,progress.log, andrestart_stateinto"$CASE_ROOT/run/...". - Keep current solver outputs, but route any explicit output paths into
"$CASE_ROOT/results"where possible.
This gives immediate directory hygiene while minimizing code churn.
A first implementation can be done without touching all solver internals at once:
- In
RUN_PENALTY.sh, derive and create:CASE_ROOT=/data/output/cases/<case_id>RUN_ROOT=$CASE_ROOT/runEXCHANGE_DIR=$RUN_ROOT/exchangeLOG_DIR=$RUN_ROOT/logsCHECKPOINT_DIR=$RUN_ROOT/checkpointRESULTS_DIR=$CASE_ROOT/results
- Keep passing
--output_dir "$EXCHANGE_DIR"to existing Python scripts. - Export environment variables for solvers:
HMM_CASE_ID,HMM_CASE_ROOT,HMM_RESULTS_DIR
- In Python, infer the case id from
--output_dirand send it to the macroscale writer layer. - In the macroscale writer layer, resolve all
data/output/...writes throughHMM_RESULTS_DIR.
This keeps exchange/restart files in run/ while moving result files under results/ with minimal coupling-risk.