Skip to content
Open
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
54257ae
add: start adding standalone infrastructure
leclairm Aug 19, 2025
9a6717a
add:ref: a lot ...
leclairm Aug 26, 2025
c7826df
FIX: leftover name change
leclairm Aug 26, 2025
ef8c680
FIX: more robust task status capturing
leclairm Aug 26, 2025
867294e
FIX: typo
leclairm Aug 26, 2025
e6ec8fd
FIX: do not continue worflow when failing
leclairm Aug 26, 2025
d7a3779
FIX: status can have details after the uppercase status code
leclairm Aug 26, 2025
64df52b
FIX: was still continuing if a task fails
leclairm Aug 26, 2025
08ec6c3
REF: reintroduce workflow status and sanitize logging
leclairm Aug 26, 2025
af356f1
REF: target the right line from sacct
leclairm Aug 27, 2025
292380f
REF: minor
leclairm Aug 27, 2025
5683bc9
Enable standalone orchestration for IconTask (#221)
leclairm Sep 15, 2025
d9ab22f
ref: assets => runtime
leclairm Sep 15, 2025
05ad53e
add: cancel task before restarting just in case
leclairm Sep 18, 2025
7714ea0
ref: put labels to all GraphItem subclasses
leclairm Sep 22, 2025
1efd9d1
add:fix: load/dump front from/to yaml
leclairm Sep 23, 2025
99752bc
add: chesk sirocco task before stop or restart
leclairm Sep 23, 2025
d0807f6
ref: dump/load full state
leclairm Sep 24, 2025
52f67b2
Status yaml (#226)
leclairm Sep 25, 2025
f5aafe7
FIX: rank comes from index in front list
leclairm Sep 26, 2025
4c20d2a
FIX: done already
leclairm Sep 26, 2025
a9634f1
Merge branch 'status_yaml' into standalone
leclairm Sep 26, 2025
c9b0da5
FIX: TODO is done
leclairm Sep 26, 2025
940fc44
fix: longer time step, less output for APE
leclairm Oct 9, 2025
608cb44
add: linked inputs and date env vars
leclairm Oct 9, 2025
0d36ad5
add[icon]: extpar_file
leclairm Oct 9, 2025
8c0a515
fix:ref: mainly issues with yaml state algorithm
leclairm Oct 14, 2025
9d4d135
REF: moving also cool-down info to yaml state
leclairm Oct 15, 2025
4963f11
fix: hatch fmt
leclairm Oct 15, 2025
4f5e12b
add: relocatable venv + build on /dev/shm
leclairm Nov 5, 2025
83b4037
fix: ensure state is dumped
leclairm Nov 5, 2025
a0aa3cd
add: date in workflow log
leclairm Nov 5, 2025
83c89d5
ref[icon_task]: introduce multiple model namelists
Nov 17, 2025
b9b02f0
Merge branch 'main' into standalone
Nov 17, 2025
f76eba5
fix: hatch fmt
Nov 17, 2025
93651d5
fix[install]: unquote command with uenv run
Nov 17, 2025
dd99ed5
fix[icon]: explicit local icon binary
Nov 19, 2025
6589edf
add[scheduler]: report errors from subprocess commands
Nov 20, 2025
d052547
ref: try to ignore environment when submitting
Nov 24, 2025
4f8be36
fix: add path to sbatch
Nov 24, 2025
8e66918
fix: missing HOME
Nov 24, 2025
7a4d16c
ref: pass --uenv and --view to sbatch as cli args
Nov 24, 2025
e8c29a9
FIX: quotes needed around uenv and view
Nov 24, 2025
d231c02
fix: remove space from command
Nov 24, 2025
4f4fe73
ref: introduce lock file ...
Nov 25, 2025
9a259e2
upd: new uenv
Nov 26, 2025
f2dcced
add[standalone]: status visualization
Dec 1, 2025
03d9146
fix: typing and formatting
Dec 8, 2025
ed64028
upd: use hatch 1.16
Dec 9, 2025
52b064b
add: ty type checker
Dec 12, 2025
3d1675b
del: remove ty server from .envrc
Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions install_santis.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/bash -l

if [ $# != 0 ]; then
VENV="${1}/.venv"
VENV_SQUASHFS="${1}/venv.squashfs"
rm -rf ${VENV}
mkdir -p ${VENV}
ln -s ${VENV} .venv
else
VENV=.venv
VENV_SQUASHFS=".venv.squashfs"
fi

uv venv --relocatable --python="$(which python)" --prompt="༄ sirocco ༄"
source .venv/bin/activate
CC=$(which gcc) \
CFLAGS="-I/user-environment/env/sirocco/include -I/user-environment/env/sirocco/include/graphviz" \
LDFLAGS="-L/user-environment/env/sirocco/lib -L/user-environment/env/sirocco/lib/graphviz" \
uv sync --no-cache --link-mode=copy --compile-bytecode --active --no-editable --inexact || exit

mksquashfs ${VENV} ${VENV_SQUASHFS} -no-recovery -noappend -Xcompression-level 3 || exit

rm -rf ${VENV}

if [ -n "${1}" ]; then
rsync -av ${VENV_SQUASHFS} .
rm -rf ${1}
fi

8 changes: 8 additions & 0 deletions launch_install_santis.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/bash

BUILD_DIR="/dev/shm/${USER}/sirocco"
mkdir -p "${BUILD_DIR}"
uenv run --view sirocco /capstor/store/cscs/userlab/cwd01/leclairm/uenvs/images/sirocco_v0.0.1.sqfs -- ${PWD}/install_santis.sh ${BUILD_DIR}
# srun --uenv /capstor/store/cscs/userlab/cwd01/leclairm/uenvs/images/sirocco_v0.0.1.sqfs --view sirocco $PWD/install_santis.sh ${BUILD_DIR}


188 changes: 184 additions & 4 deletions src/sirocco/cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import shutil
from datetime import datetime
from pathlib import Path
from typing import Annotated

Expand All @@ -7,6 +9,7 @@
from rich.traceback import install as install_rich_traceback

from sirocco import core, parsing, pretty_print, vizgraph
from sirocco.core._tasks.sirocco_task import SiroccoContinueTask
from sirocco.workgraph import AiidaWorkGraph

# --- Typer App and Rich Console Setup ---
Expand All @@ -15,12 +18,12 @@

# Create the Typer app instance
app = typer.Typer(
help="Sirocco Weather and Climate Workflow Management Tool.",
help="Sirocco Climate and Weather Workflow Management Tool.",
add_completion=True,
)

# Create a Rich console instance for printing
console = Console()
console = Console(record=True)


def _create_aiida_workflow(workflow_file: Path) -> AiidaWorkGraph:
Expand Down Expand Up @@ -171,7 +174,7 @@ def represent(
raise typer.Exit(code=1) from e


@app.command(help="Run the workflow in a blocking fashion.")
@app.command(help="Run the workflow in a blocking fashion. [AiiDA]")
def run(
workflow_file: Annotated[
Path,
Expand All @@ -198,7 +201,7 @@ def run(
raise typer.Exit(code=1) from e


@app.command(help="Submit the workflow to the AiiDA daemon.")
@app.command(help="Submit the workflow to the AiiDA daemon. [AiiDA]")
def submit(
workflow_file: Annotated[
Path,
Expand Down Expand Up @@ -229,6 +232,183 @@ def submit(
raise typer.Exit(code=1) from e


@app.command(help="Start a workflow. [standalone]")
def start(
workflow_file: Annotated[
Path,
typer.Argument(
...,
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
help="Path to the workflow definition YAML file.",
),
],
cleanup: Annotated[ # noqa: FBT002
bool,
typer.Option(
"--cleanup",
help="clean up before starting",
),
] = False,
):
console.print(add_now())
wf = core.Workflow.from_config_file(workflow_file)
if cleanup:
console.print(f"▶️ Cleaning up workflow at {wf.config_rootdir} ...")
if (run_dir := wf.config_rootdir / wf.RUN_ROOT).exists():
shutil.rmtree(run_dir)
(wf.config_rootdir / SiroccoContinueTask.SUBMIT_FILENAME).unlink(missing_ok=True)
(wf.config_rootdir / SiroccoContinueTask.STDOUTERR_FILENAME).unlink(missing_ok=True)
if (wf.config_rootdir / wf.RUN_ROOT).exists():
msg = "Workflow already exists, cannot start. Use --cleanup to clean up before starting."
raise ValueError(msg)
console.print(f"▶️ Starting workflow at {wf.config_rootdir} ...")
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))
try:
wf.start()
if wf.status == core.workflow.WorkflowStatus.CONTINUE:
console.print("✅ Workflow started successfully.")
elif wf.status == core.workflow.WorkflowStatus.FAILED:
console.print("❌ Workflow start failed")
except Exception as e:
console.print(f"❌ Workflow start failed: {e}")
console.print_exception()
raise typer.Exit(code=1) from e
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))


@app.command(help="Restart a stopped workflow. [standalone]")
def restart(
workflow_file: Annotated[
Path,
typer.Argument(
...,
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
help="Path to the workflow definition YAML file.",
),
],
):
console.print(add_now())
wf = core.Workflow.from_config_file(workflow_file)
console.print(f"▶️ Restarting workflow at {wf.config_rootdir} ...")
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))
try:
wf.restart()
if wf.status == core.workflow.WorkflowStatus.CONTINUE:
console.print("✅ Workflow restarted successfully.")
elif wf.status == core.workflow.WorkflowStatus.COMPLETED:
console.print("✅ Workflow completed!")
elif wf.status == core.workflow.WorkflowStatus.RESTART_FAILED:
console.print("❌ Workflow restart failed")
except Exception as e:
console.print(f"❌ Workflow restart failed: {e}")
console.print_exception()
raise typer.Exit(code=1) from e
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))


@app.command(help="Stop a workflow. [standalone]")
def stop(
workflow_file: Annotated[
Path,
typer.Argument(
...,
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
help="Path to the workflow definition YAML file.",
),
],
cool_down: Annotated[ # noqa: FBT002
bool,
typer.Option(
"--cool-down",
help="Do not cancel currently running tasks",
),
] = False,
):
console.print(add_now())
wf = core.Workflow.from_config_file(workflow_file)
msg = f"▶️ Stopping workflow at {wf.config_rootdir}"
if cool_down:
msg += " in cool down mode"
msg += " ..."
console.print(msg)
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))
try:
wf.stop(mode="cool-down" if cool_down else "cancel")
if wf.status == core.workflow.WorkflowStatus.STOPPED:
console.print("✅ Workflow stopped successfully.")
else:
console.print("❌ Workflow stop failed")
except Exception as e:
console.print(f"❌ Workflow stop failed: {e}")
console.print_exception()
raise typer.Exit(code=1) from e
with (wf.config_rootdir / core.SiroccoContinueTask.STDOUTERR_FILENAME).open("a") as logfile:
logfile.write(console.export_text(clear=True))


@app.command(name="continue", hidden=True)
def continue_wf(
workflow_file: Annotated[
Path,
typer.Argument(
...,
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
help="Path to the workflow definition YAML file.",
),
],
from_wf: Annotated[ # noqa: FBT002
bool,
typer.Option(
"--from_wf",
help="Specify command is executed from a running worflow (as opposed to interactively)",
),
] = False,
):
console.print(add_now())
if not from_wf:
msg = "Do not use interactively, the continue command is reserved for internal use"
raise ValueError(msg)
wf = core.Workflow.from_config_file(workflow_file)
console.print("▶️ Continue workflow ...")
try:
wf.continue_wf()
if wf.status == core.workflow.WorkflowStatus.CONTINUE:
console.print("✅ Workflow continuation submitted successfully.")
elif wf.status == core.workflow.WorkflowStatus.COMPLETED:
console.print("✅ Workflow completed!")
elif wf.status == core.workflow.WorkflowStatus.FAILED:
console.print("❌ Workflow failed")
except Exception as e:
console.print(f"❌ Workflow continuation failed: {e}")
console.print_exception()
raise typer.Exit(code=1) from e


def add_now(width: int = 20) -> str:
rule = width * "─"
space = width * " "
date_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # noqa: DTZ005
date_rule = (len(date_str) + 2) * "─"
return "\n".join([f"{space}╭{date_rule}╮", f"{rule}┤ {date_str} ├{rule}", f"{space}╰{date_rule}╯"])


# --- Main entry point for the script ---
if __name__ == "__main__":
app()
3 changes: 2 additions & 1 deletion src/sirocco/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._tasks import IconTask, ShellTask
from ._tasks import IconTask, ShellTask, SiroccoContinueTask
from .graph_items import AvailableData, Cycle, Data, GeneratedData, GraphItem, MpiCmdPlaceholder, Task
from .workflow import Workflow

Expand All @@ -12,5 +12,6 @@
"Cycle",
"ShellTask",
"IconTask",
"SiroccoContinueTask",
"MpiCmdPlaceholder",
]
3 changes: 2 additions & 1 deletion src/sirocco/core/_tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .icon_task import IconTask
from .shell_task import ShellTask
from .sirocco_task import SiroccoContinueTask

__all__ = ["IconTask", "ShellTask"]
__all__ = ["IconTask", "ShellTask", "SiroccoContinueTask"]
Loading