1"""Argument parser construction and command dispatch for PICurv."""
12 @brief Attach `run` parser with staged execution and dry-run support.
13 @param[in] subparsers Argument passed to `_add_run_parser()`.
14 @return Value returned by `_add_run_parser()`.
16 p_run = subparsers.add_parser(
18 help=
"Execute a simulation workflow (solve and/or post-process).",
19 formatter_class=argparse.RawTextHelpFormatter,
21 "Execute solver and/or post-processing stages.\n\n"
23 " - --num-procs applies to solver stage launches.\n"
24 " - Post-processing defaults to a single MPI rank/task.\n"
25 " - With --solve, --continue resumes the existing run directory in-place.\n"
26 " - With --post-process, --continue resumes the same recipe from the first unfinished step\n"
27 " and caps the launch to the highest fully available contiguous source frontier.\n\n"
29 " - PETSc and runtime memory diagnostics live under monitor.yml -> diagnostics.\n"
30 " - Use --dry-run to inspect resolved PETSc flags and expected log destinations.\n\n"
32 " picurv run --solve -n 8 --case case.yml --solver solver.yml --monitor monitor.yml\n"
33 " picurv run --solve --restart-from runs/old_run --case case.yml --solver solver.yml --monitor monitor.yml\n"
34 " picurv run --solve --continue --run-dir runs/my_run --case case.yml --solver solver.yml --monitor monitor.yml\n"
35 " picurv run --post-process --run-dir runs/my_run --post post.yml\n"
36 " picurv run --post-process --continue --run-dir runs/my_run --post post.yml\n"
37 " picurv run --solve --case case.yml --solver solver.yml --monitor monitor.yml --dry-run"
39 epilog=
"Next: run `picurv validate ...` first for config-only checks.",
41 run_group = p_run.add_argument_group(
"stages")
42 run_group.add_argument(
"--solve", action=
"store_true", help=
"Execute the solver stage (creates a new run directory).")
43 run_group.add_argument(
"--post-process", action=
"store_true", help=
"Execute the post-processing stage on a run directory.")
45 solver_group = p_run.add_argument_group(
"solver inputs (required for --solve)")
46 solver_group.add_argument(
"--case", help=
"Path to the case definition file (e.g., case.yml).")
47 solver_group.add_argument(
"--solver", help=
"Path to the solver settings profile (e.g., solver.yml).")
48 solver_group.add_argument(
"--monitor", help=
"Path to the monitoring, diagnostics, and I/O profile (e.g., monitor.yml).")
49 solver_group.add_argument(
51 help=
"Path to an existing run directory to restart from.\n"
52 "Creates a new run directory and copies/references checkpoint data from the source.",
54 run_group.add_argument(
58 help=
"Resume an existing run directory in-place. Requires --run-dir.\n"
59 "With --solve, appends to existing solver output/logs.\n"
60 "With --post-process, resumes the same recipe from the first unfinished step\n"
61 "and skips already-complete work inside the current live source frontier.",
64 post_group = p_run.add_argument_group(
"post-processor inputs (required for --post-process)")
65 post_group.add_argument(
"--run-dir", help=
"Path to an existing run directory.\n(Used with --post-process or --continue).")
66 post_group.add_argument(
"--post", help=
"Path to the post-processing recipe file (e.g., post.yml).")
73 help=
"Number of MPI processes for the solver stage. Post-processing defaults to 1 rank.",
75 p_run.add_argument(
"--cluster", help=
"Path to cluster.yml for Slurm execution mode.")
76 p_run.add_argument(
"--scheduler", help=
"Explicit scheduler selector (currently 'slurm').")
77 p_run.add_argument(
"--no-submit", action=
"store_true", help=
"Stage run artifacts without starting local execution or Slurm submission.")
81 help=
"Resolve and print planned commands/artifacts, including diagnostic flags and log paths, without writing files.",
86 choices=[
"text",
"json"],
88 help=
"Output format for --dry-run (default: text).",
95 @brief Perform add sweep parser.
96 @param[in] subparsers Argument passed to `_add_sweep_parser()`.
97 @return Value returned by `_add_sweep_parser()`.
99 p_sweep = subparsers.add_parser(
101 help=
"Launch or continue a Slurm-based parameter sweep/study.",
102 formatter_class=argparse.RawTextHelpFormatter,
104 "Launch studies from study.yml + cluster.yml, whether the study uses\n"
105 "cross-product parameter combinations or explicit coupled parameter_sets, continue\n"
106 "partially-completed studies, or re-aggregate metrics.\n\n"
108 " picurv sweep --study study.yml --cluster cluster.yml\n"
109 " picurv sweep --study study.yml --cluster cluster.yml --no-submit\n"
110 " picurv sweep --continue --study-dir studies/<id>\n"
111 " picurv sweep --continue --study-dir studies/<id> --cluster cluster_more_time.yml\n"
112 " picurv sweep --reaggregate --study-dir studies/<id>"
115 "Study files support either `parameters` cross-product expansion or explicit `parameter_sets`.\n"
116 "Next: inspect studies/<study_id>/results/metrics_table.csv for aggregated metrics."
119 p_sweep.add_argument(
"--study", help=
"Path to study.yml defining either a `parameters` cross-product expansion or explicit parameter_sets, plus metrics.")
120 p_sweep.add_argument(
"--cluster", help=
"Path to cluster.yml defining Slurm resources.")
121 p_sweep.add_argument(
"--no-submit", action=
"store_true", help=
"Generate all study artifacts without submitting jobs.")
122 p_sweep.add_argument(
"--study-dir", help=
"Path to an existing study directory (for --continue/--reaggregate).")
123 p_sweep.add_argument(
"--continue", action=
"store_true", dest=
"continue_study",
124 help=
"Continue a partially-completed study. Requires --study-dir.")
125 p_sweep.add_argument(
"--reaggregate", action=
"store_true",
126 help=
"Re-run metrics aggregation on a completed study. Requires --study-dir.")
132 @brief Perform add validate parser.
133 @param[in] subparsers Argument passed to `_add_validate_parser()`.
134 @return Value returned by `_add_validate_parser()`.
136 p_validate = subparsers.add_parser(
138 help=
"Validate config files without launching solver/post.",
139 formatter_class=argparse.RawTextHelpFormatter,
141 "Validate one or more config roles. No solver/post execution and no run/study artifact writes.\n\n"
143 " picurv validate --case case.yml --solver solver.yml --monitor monitor.yml\n"
144 " picurv validate --post post.yml --cluster cluster.yml\n"
145 " picurv validate --study study.yml --cluster cluster.yml --strict"
147 epilog=
"Next: run `picurv run --dry-run ...` to inspect resolved commands/artifacts.",
149 p_validate.add_argument(
"--case", help=
"Path to case.yml")
150 p_validate.add_argument(
"--solver", help=
"Path to solver.yml")
151 p_validate.add_argument(
"--monitor", help=
"Path to monitor.yml (logging, diagnostics, profiling, and I/O)")
152 p_validate.add_argument(
"--restart-from", help=
"Path to a run directory to validate restart from.")
153 p_validate.add_argument(
"--continue", action=
"store_true", dest=
"continue_run", help=
"Validate continue-in-place mode. Requires --run-dir.")
154 p_validate.add_argument(
"--run-dir", help=
"Path to an existing run directory (for --continue validation).")
155 p_validate.add_argument(
"--post", help=
"Path to post.yml")
156 p_validate.add_argument(
"--cluster", help=
"Path to cluster.yml")
157 p_validate.add_argument(
"--study", help=
"Path to study.yml")
158 p_validate.add_argument(
"--strict", action=
"store_true", help=
"Enable additional strict checks for selected roles.")
163 @brief Attach `precompute` parser for deterministic artifact generation.
164 @param[in] subparsers Argument passed to `_add_precompute_parser()`.
165 @return Configured parser.
167 p_precompute = subparsers.add_parser(
169 help=
"Generate deterministic case artifacts without launching the solver.",
170 formatter_class=argparse.RawTextHelpFormatter,
172 "Generate configured deterministic artifacts, such as grid_gen grids,\n"
173 "generated prescribed-flow inlet profiles, and ic_gen initial conditions,\n"
174 "into a run-like config layout.\n\n"
176 " picurv precompute --case case.yml\n"
177 " picurv precompute --case case.yml --output-dir precomputed/channel"
179 epilog=
"Next: inspect generated artifacts or run solve with the configured generated modes directly.",
181 p_precompute.add_argument(
"--case", required=
True, help=
"Path to case.yml containing grid/profile/IC generator settings.")
182 p_precompute.add_argument(
184 help=
"Directory where precomputed artifacts should be written. Defaults to precomputed/<case-name>.",
191 @brief Attach `summarize` parser for read-only run-health views.
192 @param[in] subparsers Argument passed to `_add_summarize_parser()`.
193 @return Value returned by `_add_summarize_parser()`.
195 p_summarize = subparsers.add_parser(
197 help=
"Summarize run configs/health and plot scalar log histories.",
198 formatter_class=argparse.RawTextHelpFormatter,
200 "Build read-only configuration overviews, run-health summaries, and scalar time-history plots.\n"
201 "It does not modify solver output and works for active or completed runs.\n\n"
203 " picurv summarize --run-dir runs/my_run --overview\n"
204 " picurv summarize --run-dir runs/my_run --case --solver\n"
205 " picurv summarize --run-dir runs/my_run --latest\n"
206 " picurv summarize --run-dir runs/my_run --monitor --step 500\n"
207 " picurv summarize --run-dir runs/my_run --list-plot-series\n"
208 " picurv summarize --run-dir runs/my_run --plot momentum.residual_norm --last 100\n"
209 " picurv summarize --run-dir runs/my_run --latest --format json"
211 epilog=
"Next: use `picurv run ...` to create runs or `picurv sweep ...` for multi-case studies.",
213 p_summarize.add_argument(
"--run-dir", required=
True, help=
"Path to the run directory to inspect.")
214 p_summarize.add_argument(
217 help=
"Summarize run metadata plus copied case, solver, and monitor configs without implicitly requesting health.",
219 p_summarize.add_argument(
"--case", action=
"store_true", help=
"Summarize the copied run-local case.yml.")
220 p_summarize.add_argument(
"--solver", action=
"store_true", help=
"Summarize the copied run-local solver.yml.")
221 p_summarize.add_argument(
"--monitor", action=
"store_true", help=
"Summarize the copied run-local monitor.yml.")
222 plot_group = p_summarize.add_mutually_exclusive_group()
223 plot_group.add_argument(
224 "--list-plot-series",
225 dest=
"list_plot_series",
227 help=
"List scalar time histories available to --plot.",
229 plot_group.add_argument(
231 dest=
"list_plot_series",
233 help=argparse.SUPPRESS,
235 plot_group.add_argument(
238 help=
"Plot one qualified scalar history, such as momentum.residual_norm (requires matplotlib).",
240 p_summarize.add_argument(
"--last", dest=
"last_n", type=int, help=
"Plot only the last N chronological records per line.")
241 p_summarize.add_argument(
"--plot-output", help=
"Save the plot to this path instead of opening an interactive window.")
242 p_summarize.add_argument(
"--linear-y", action=
"store_true", help=
"Force linear y-axis scaling instead of automatic residual/norm log scaling.")
243 step_group = p_summarize.add_mutually_exclusive_group()
244 step_group.add_argument(
"--step", type=int, help=
"Specific completed timestep to summarize.")
245 step_group.add_argument(
248 help=
"Summarize the most recently appended completed step found in available artifacts (default behavior).",
250 step_group.add_argument(
253 help=
"Summarize the numerically largest timestep found in available artifacts.",
255 p_summarize.add_argument(
259 help=
"Number of sampled particle snapshot rows to preview when solver stream output contains them.",
261 p_summarize.add_argument(
263 dest=
"output_format",
264 choices=[
"text",
"json"],
266 help=
"Output format (default: text).",
273 @brief Perform add cancel parser.
274 @param[in] subparsers Argument passed to `_add_cancel_parser()`.
275 @return Value returned by `_add_cancel_parser()`.
277 p_cancel = subparsers.add_parser(
279 help=
"Cancel Slurm-submitted jobs for an existing run directory.",
280 formatter_class=argparse.RawTextHelpFormatter,
282 "Look up scheduler/submission.json inside an existing run directory and cancel\n"
283 "the recorded Slurm job(s) without requiring manual job-id lookup.\n\n"
285 " picurv cancel --run-dir runs/my_run\n"
286 " picurv cancel --run-dir runs/my_run --stage solve\n"
287 " picurv cancel --run-dir runs/my_run --stage solve --graceful\n"
288 " picurv cancel --run-dir runs/my_run --dry-run\n\n"
289 "Default cancellation is a hard Slurm cancel (`scancel <job_id>`). With\n"
290 "`--graceful`, solver jobs receive SIGUSR1 so PICurv can write the latest\n"
291 "safe off-cadence step at the next runtime checkpoint before exiting.\n"
292 "Post-process jobs still use ordinary hard cancellation."
294 epilog=
"Next: use `picurv summarize --run-dir ...` to inspect whatever output the run already produced.",
296 p_cancel.add_argument(
"--run-dir", required=
True, help=
"Path to the run directory whose Slurm job(s) should be canceled.")
297 p_cancel.add_argument(
299 choices=[
"all",
"solve",
"post-process"],
301 help=
"Which recorded stage job(s) to cancel (default: all).",
303 p_cancel.add_argument(
306 help=
"Show which `scancel` command(s) would run without actually canceling anything.",
308 p_cancel.add_argument(
312 "For solver jobs, request a clean runtime shutdown by sending SIGUSR1 instead of "
313 "hard-canceling immediately. The solver writes the latest safe off-cadence step at "
314 "the next checkpoint. Non-solver stages still use ordinary scancel."
322 @brief Perform add submit parser.
323 @param[in] subparsers Argument passed to `_add_submit_parser()`.
324 @return Value returned by `_add_submit_parser()`.
326 p_submit = subparsers.add_parser(
328 help=
"Execute or submit previously staged artifacts from an existing run or study directory.",
329 formatter_class=argparse.RawTextHelpFormatter,
331 "Consume an existing artifact set created by picurv --no-submit and\n"
332 "execute/submit it later without regenerating configs or scripts.\n\n"
334 " picurv submit --run-dir runs/my_run\n"
335 " picurv submit --run-dir runs/my_run --stage solve\n"
336 " picurv submit --study-dir studies/my_study --dry-run"
338 epilog=
"Next: use `picurv summarize --run-dir ...` or `picurv cancel --run-dir ...` after submission as needed.",
340 target_group = p_submit.add_mutually_exclusive_group(required=
True)
341 target_group.add_argument(
"--run-dir", help=
"Path to a staged run directory created by `picurv run ... --no-submit`.")
342 target_group.add_argument(
"--study-dir", help=
"Path to a staged study directory created by `picurv sweep --cluster ... --no-submit`.")
343 p_submit.add_argument(
345 choices=[
"all",
"solve",
"post-process"],
347 help=
"Which staged job(s) to submit (default: all).",
349 p_submit.add_argument(
352 help=
"Allow re-submitting a stage already marked as submitted in scheduler/submission.json.",
354 p_submit.add_argument(
357 help=
"Show which local command(s) or `sbatch` command(s) would run without starting anything.",
364 @brief Perform add init parser.
365 @param[in] subparsers Argument passed to `_add_init_parser()`.
366 @return Value returned by `_add_init_parser()`.
368 p_init = subparsers.add_parser(
370 help=
"Initialize a new case study directory from a template.",
371 formatter_class=argparse.RawTextHelpFormatter,
373 "Create a study directory from examples/<template_name>.\n\n"
375 " picurv init flat_channel --dest my_case\n"
376 " picurv init bent_channel --dest my_bent_case"
378 epilog=
"Next: run `picurv validate --case ... --solver ... --monitor ...` before execution.",
380 p_init.add_argument(
"template_name", help=
"Name of the case template directory to copy (e.g., 'flat_channel').")
384 help=
"Optional name for the new directory. Defaults to the template name.\nPath is relative to your current working directory.",
388 help=
"Optional override for the PICurv source repository root.\nUseful when running from a copied case without metadata.",
394 help=
"Copy simulator and postprocessor into the case directory.\nUse this to freeze specific binary versions for reproducibility\nor to protect running jobs from concurrent rebuilds.",
401 @brief Perform add build parser.
402 @param[in] subparsers Argument passed to `_add_build_parser()`.
403 @return Value returned by `_add_build_parser()`.
405 p_build = subparsers.add_parser(
407 help=
"Build project executables using the Makefile.",
408 formatter_class=argparse.RawTextHelpFormatter,
410 "Calls the project's Makefile directly through `make`.\n"
411 "If you do not pass an explicit make target, `picurv build` runs `make all`.\n"
412 "Any arguments provided after 'build' are passed directly to make.\n\n"
415 " picurv build clean-project\n"
416 " picurv build SYSTEM=cluster\n"
417 " picurv build all SYSTEM=cluster\n"
418 " picurv build postprocessor\n"
419 " ./picurv build clean-project # from an initialized case directory"
421 epilog=
"Next: run `picurv --help` or `picurv run --help` for execution commands.",
423 p_build.add_argument(
425 help=
"Optional override for the PICurv source repository root.",
427 p_build.add_argument(
429 help=
"Optional case directory used to resolve .picurv-origin.json when not running from that case.",
431 p_build.add_argument(
433 nargs=argparse.REMAINDER,
434 help=
"Arguments to pass directly to the make command (e.g., 'clean-project').",
441 @brief Perform add sync binaries parser.
442 @param[in] subparsers Argument passed to `_add_sync_binaries_parser()`.
443 @return Value returned by `_add_sync_binaries_parser()`.
445 p_sync_binaries = subparsers.add_parser(
447 help=
"Pin specific binary versions into a case directory (optional).",
448 formatter_class=argparse.RawTextHelpFormatter,
450 "Copy simulator/postprocessor from the source repo bin/ into a case directory.\n"
451 "This is optional — binaries are normally resolved via PATH. Use this only\n"
452 "when you need to pin a specific build into a case for reproducibility.\n\n"
454 " picurv sync-binaries --case-dir my_case\n"
455 " picurv sync-binaries --source-root /path/to/PICurv"
457 epilog=
"Next: run `./picurv build ...` first if the source repo bin/ is out of date.",
459 p_sync_binaries.add_argument(
"--case-dir", help=
"Optional case directory to refresh. Defaults to the current case.")
460 p_sync_binaries.add_argument(
"--source-root", help=
"Optional override for the PICurv source repository root.")
461 return p_sync_binaries
466 @brief Perform add sync config parser.
467 @param[in] subparsers Argument passed to `_add_sync_config_parser()`.
468 @return Value returned by `_add_sync_config_parser()`.
470 p_sync_config = subparsers.add_parser(
472 help=
"Refresh template-managed files in a case directory from examples/<template>.",
473 formatter_class=argparse.RawTextHelpFormatter,
475 "Copy updated example template files into an existing case directory.\n"
476 "Modified files are preserved unless --overwrite is used.\n"
477 "--prune removes only files that were previously tracked as template-managed and\n"
478 "have since been removed from the source template.\n\n"
480 " ./picurv sync-config\n"
481 " ./picurv sync-config --overwrite\n"
482 " ./picurv sync-config --prune\n"
483 " ./bin/picurv sync-config --case-dir my_case --template-name flat_channel"
485 epilog=
"Next: run `picurv validate ...` after syncing configs.",
487 p_sync_config.add_argument(
"--case-dir", help=
"Optional case directory to refresh. Defaults to the current case.")
488 p_sync_config.add_argument(
"--source-root", help=
"Optional override for the PICurv source repository root.")
489 p_sync_config.add_argument(
491 help=
"Optional template name override (e.g., flat_channel). Required when metadata is absent.",
493 p_sync_config.add_argument(
"--overwrite", action=
"store_true", help=
"Overwrite case files even when they differ from the template.")
494 p_sync_config.add_argument(
497 help=
"Remove stale files that were previously tracked as template-managed but no longer exist in the source template.",
504 @brief Perform add pull source parser.
505 @param[in] subparsers Argument passed to `_add_pull_source_parser()`.
506 @return Value returned by `_add_pull_source_parser()`.
508 p_pull = subparsers.add_parser(
510 help=
"Refresh source branches from an initialized case directory.",
511 formatter_class=argparse.RawTextHelpFormatter,
513 "Update the source repository without leaving an initialized case directory.\n\n"
514 "By default this refreshes every local branch that tracks an upstream,\n"
515 "then restores the branch you started on.\n\n"
517 " ./picurv pull-source\n"
518 " ./picurv pull-source --current-branch-only\n"
519 " ./picurv pull-source --no-rebase\n"
520 " ./picurv pull-source --remote origin --branch main"
522 epilog=
"Next: run `./picurv build` and `./picurv sync-binaries` if source changes require rebuilt executables.",
524 p_pull.add_argument(
"--case-dir", help=
"Optional case directory used to resolve .picurv-origin.json.")
525 p_pull.add_argument(
"--source-root", help=
"Optional override for the PICurv source repository root.")
526 p_pull.add_argument(
"--remote", help=
"Optional git remote name (e.g., origin).")
527 p_pull.add_argument(
"--branch", help=
"Optional branch name. If provided without --remote, origin is assumed.")
529 "--current-branch-only",
531 help=
"Only pull the currently checked out branch instead of iterating across all local tracking branches.",
533 p_pull.add_argument(
"--no-rebase", action=
"store_true", help=
"Use plain git pull instead of git pull --rebase.")
539 @brief Perform add status source parser.
540 @param[in] subparsers Argument passed to `_add_status_source_parser()`.
541 @return Value returned by `_add_status_source_parser()`.
543 p_status = subparsers.add_parser(
545 help=
"Report source/case drift for an initialized case directory.",
546 formatter_class=argparse.RawTextHelpFormatter,
548 "Inspect whether the source repo, copied binaries, and template-managed files have drifted\n"
549 "from the current case directory.\n\n"
551 " ./picurv status-source\n"
552 " ./picurv status-source --format json\n"
553 " ./bin/picurv status-source --case-dir my_case"
555 epilog=
"Next: use `pull-source`, `build`, `sync-binaries`, or `sync-config` based on the reported drift.",
557 p_status.add_argument(
"--case-dir", help=
"Optional case directory used to resolve .picurv-origin.json.")
558 p_status.add_argument(
"--source-root", help=
"Optional override for the PICurv source repository root.")
559 p_status.add_argument(
"--template-name", help=
"Optional template name override when metadata is absent.")
560 p_status.add_argument(
562 dest=
"output_format",
563 choices=[
"text",
"json"],
565 help=
"Output format (default: text).",
572 @brief Build and return the top-level CLI parser.
573 @return Value returned by `build_main_parser()`.
575 parser = argparse.ArgumentParser(
576 description=
"picurv: A comprehensive conductor for the PICurv simulation platform.",
577 formatter_class=argparse.RawTextHelpFormatter,
580 " picurv validate --case case.yml --solver solver.yml --monitor monitor.yml --post post.yml\n"
581 " picurv run --solve --post-process --case case.yml --solver solver.yml --monitor monitor.yml --post post.yml --dry-run\n"
582 " picurv run --solve --post-process --case case.yml --solver solver.yml --monitor monitor.yml --post post.yml --no-submit\n"
583 " picurv precompute --case case.yml --output-dir precomputed/my_case\n"
584 " picurv run --post-process --continue --run-dir runs/my_run --post post.yml\n"
585 " picurv summarize --run-dir runs/my_run --latest\n"
586 " picurv summarize --run-dir runs/my_run --list-plot-series\n"
587 " picurv summarize --run-dir runs/my_run --plot momentum.residual_norm --last 100\n"
588 " picurv submit --run-dir runs/my_run\n"
589 " picurv cancel --run-dir runs/my_run\n"
590 " picurv cancel --run-dir runs/my_run --stage solve --graceful\n"
591 " picurv sweep --study study.yml --cluster cluster.yml\n\n"
593 " - First run: picurv init ... -> picurv validate ... -> picurv run ...\n"
594 " - Config debugging: picurv validate ...\n"
595 " - Artifact generation: picurv precompute ...\n"
596 " - Launch planning: picurv run ... --dry-run\n"
597 " - Post-only catch-up: picurv run --post-process --continue --run-dir ... --post ...\n"
598 " - Deferred submission: picurv submit --run-dir ...\n"
599 " - Run inspection/plots: picurv summarize ...\n"
600 " - Run cancellation: picurv cancel --run-dir ...; use --graceful for solver final-output shutdown"
603 parser.add_argument(
"-v",
"--version", action=
"version", version=f
"picurv {PICURV_VERSION}")
604 subparsers = parser.add_subparsers(dest=
"command", required=
True, help=
"Available commands")
621def dispatch_command(args):
623 @brief Validate argument combinations and dispatch to command handlers.
624 @param[in] args Command-line style argument list supplied to the function.
626 if args.command ==
"run":
627 if not args.solve
and not args.post_process:
628 fail_cli_usage(
"At least one stage (--solve or --post-process) must be selected.")
629 if args.solve
and (
not args.case
or not args.solver
or not args.monitor):
630 fail_cli_usage(
"--solve requires --case, --solver, and --monitor.")
631 if args.post_process
and not args.post:
633 if args.scheduler
and not args.cluster:
637 if args.command ==
"sweep":
638 if args.continue_study:
639 if not args.study_dir:
644 if not args.study_dir:
652 fail_cli_usage(
"--cluster is required when launching a new sweep.")
655 if args.command ==
"validate":
658 if args.command ==
"precompute":
661 except ValueError
as e:
663 ERROR_CODE_CFG_INVALID_VALUE,
665 file_path=getattr(args,
"case",
"-"),
670 if args.command ==
"summarize":
673 if args.command ==
"submit":
676 if args.command ==
"cancel":
679 if args.command ==
"init":
682 if args.command ==
"build":
685 if args.command ==
"sync-binaries":
688 if args.command ==
"sync-config":
691 if args.command ==
"pull-source":
694 if args.command ==
"status-source":
_add_sweep_parser(subparsers)
Perform add sweep parser.
_add_run_parser(subparsers)
Attach run parser with staged execution and dry-run support.
_add_build_parser(subparsers)
Perform add build parser.
_add_init_parser(subparsers)
Perform add init parser.
build_main_parser()
Build and return the top-level CLI parser.
_add_sync_config_parser(subparsers)
Perform add sync config parser.
_add_pull_source_parser(subparsers)
Perform add pull source parser.
_add_precompute_parser(subparsers)
Attach precompute parser for deterministic artifact generation.
_add_cancel_parser(subparsers)
Perform add cancel parser.
_add_status_source_parser(subparsers)
Perform add status source parser.
_add_sync_binaries_parser(subparsers)
Perform add sync binaries parser.
_add_submit_parser(subparsers)
Perform add submit parser.
_add_summarize_parser(subparsers)
Attach summarize parser for read-only run-health views.
_add_validate_parser(subparsers)
Perform add validate parser.
summarize_workflow(args)
Build and render a read-only health summary for a run step.
status_source_command(args)
Report source/case drift for an initialized case directory.
validate_workflow(args)
Implements picurv validate without launching solver/post workflows.
sweep_reaggregate_workflow(args)
Re-run metrics aggregation and plot generation for an existing study.
emit_structured_error(str code, str key="-", str file_path="-", str message="", str hint=None, stream=None)
Emit one standardized error line for tooling and users.
init_case(args)
Implements the 'init' command.
sweep_workflow(args)
Study/sweep orchestration using Slurm job arrays.
submit_staged_jobs(args)
Submit previously staged Slurm artifacts from an existing run/study directory.
sync_case_config_command(args)
Refresh template-managed config/docs files in a case directory.
build_project(args)
Implements the 'build' command.
fail_cli_usage(str message, str hint=None)
Emit a structured CLI usage error and exit with code 2.
sync_case_binaries_command(args)
Refresh case-local executables from the source repository bin directory.
sweep_continue_workflow(args)
Continue a partially-completed Slurm parameter sweep study.
cancel_run_jobs(args)
Cancel Slurm-submitted jobs for an existing run directory.
precompute_workflow(args)
Generate deterministic case artifacts without launching solver/post stages.
run_workflow(args)
Main orchestrator for the 'run' command (local and Slurm modes).
pull_source_repo(args)
Refresh source branches in the repository resolved from a case directory.