Running SWAN¶
What this shows: the ways to run SWAN on a workspace generated by rompy-swan: with your own installation, with the public Docker image, in parallel with MPI, and from the command line.
Prerequisites: Tutorial 1: Your first SWAN model.
You will learn:
- how rompy's run backends (
LocalConfig,DockerConfig) execute a model - which SWAN image to use, and why
- how to run SWAN in parallel with MPI
- how to check a run
Data used: etopo15s_perth.nc. The runs need Docker (or SWAN installed locally)
and are skipped otherwise.
Setup¶
import shutil
import subprocess
from pathlib import Path
from rompy.backends import DockerConfig, LocalConfig
from rompy.core.source import SourceFile
from rompy.core.time import TimeRange
from rompy.logging import config as logging_config
from rompy.model import ModelRun
from rompy_swan.components.boundary import BOUNDSPEC
from rompy_swan.components.cgrid import REGULAR
from rompy_swan.components.group import LOCKUP, OUTPUT, PHYSICS, STARTUP
from rompy_swan.components.lockup import COMPUTE
from rompy_swan.components.output import BLOCK
from rompy_swan.components.physics import GEN3, OFF, OFFS
from rompy_swan.components.startup import COORDINATES, SET
from rompy_swan.config import SwanConfig
from rompy_swan.data import SwanDataGrid
from rompy_swan.grid import SwanGrid
from rompy_swan.interface import DataInterface
from rompy_swan.subcomponents.boundary import CONSTANTPAR, SIDE
from rompy_swan.subcomponents.spectrum import JONSWAP, SHAPESPEC, SPECTRUM
from rompy_swan.subcomponents.startup import SPHERICAL
logging_config.update(level="WARNING")
DATA_DIR = Path("../data")
OUT_DIR = Path("_output") / "running_swan"
shutil.rmtree(OUT_DIR, ignore_errors=True)
1. Generate once, run with a backend¶
rompy separates generating the workspace from running the model. ModelRun.run()
takes a backend that says where and how to run. We use the model of Tutorial 1.
grid = SwanGrid(x0=114.5, y0=-32.8, dx=0.02, dy=0.02, nx=71, ny=66)
config = SwanConfig(
startup=STARTUP(set=SET(direction_convention="nautical"), coordinates=COORDINATES(kind=SPHERICAL())),
cgrid=REGULAR(grid=grid.component, spectrum=SPECTRUM(mdc=36, flow=0.04, fhigh=1.0)),
inpgrid=DataInterface(
bottom=SwanDataGrid(
var="bottom",
source=SourceFile(uri=DATA_DIR / "etopo15s_perth.nc"),
z1="z",
fac=-1.0,
coords={"x": "longitude", "y": "latitude"},
buffer=0.1,
)
),
boundary=BOUNDSPEC(
shapespec=SHAPESPEC(shape=JONSWAP(gamma=3.3), per_type="peak", dspr_type="degrees"),
location=SIDE(side="west", direction="ccw"),
data=CONSTANTPAR(hs=2.0, per=12.0, dir=240.0, dd=20.0),
),
physics=PHYSICS(gen=GEN3(), deactivate=OFFS(offs=[OFF(physics="quadrupl")])),
output=OUTPUT(block=BLOCK(sname="COMPGRID", fname="swangrid.nc", output=["hsign"])),
lockup=LOCKUP(compute=COMPUTE()),
)
period = TimeRange(start="2023-01-01T00:00", end="2023-01-01T00:00", interval="1h")
def new_run(run_id: str) -> tuple[ModelRun, Path]:
"""Generate a workspace for this model under its own run id."""
modelrun = ModelRun(run_id=run_id, period=period, output_dir=OUT_DIR, config=config)
return modelrun, Path(modelrun())
def docker_available() -> bool:
"""Return True if the Docker daemon can be reached."""
try:
return subprocess.run(["docker", "info"], capture_output=True).returncode == 0
except FileNotFoundError:
return False
SWAN reads the command file INPUT in its working directory and writes its log to
PRINT. Every backend runs swan.exe in the workspace.
2. With your own SWAN installation¶
SWAN can be installed from the SWAN website
(source code and pre-compiled binaries). LocalConfig runs a command in the workspace
on this machine.
if shutil.which("swan.exe"):
modelrun, workspace = new_run("local")
ok = modelrun.run(LocalConfig(command="swan.exe"), workspace_dir=workspace)
print("SWAN finished" if ok else "SWAN failed")
else:
print("swan.exe is not installed here, skipping the local run")
swan.exe is not installed here, skipping the local run
3. With Docker¶
The image ghcr.io/rom-py/swan contains SWAN built with NetCDF output, serially and
with MPI, and is tested with rompy-swan workspaces. Pin a version tag, e.g. 41.51,
for reproducible runs. The official image from the SWAN team, delftwaves/swan, is
built without NetCDF, so it writes .nc output files as text.
if docker_available():
modelrun, workspace = new_run("docker")
backend = DockerConfig(image="ghcr.io/rom-py/swan:41.51", executable="swan.exe")
ok = modelrun.run(backend, workspace_dir=workspace)
print("SWAN finished" if ok else "SWAN failed")
print(sorted(p.name for p in workspace.iterdir()))
else:
print("Docker is not available, skipping the Docker run")
SWAN finished ['INPUT', 'PRINT', 'bottom.grd', 'norm_end', 'swangrid.nc', 'swaninit']
The container runs as root, so on Linux the output files belong to root.
4. In parallel with MPI¶
mpiexec and cpu run SWAN with MPI across several processes. The image picks its
MPI build automatically. SWAN then writes one PRINT file per process
(PRINT-001, PRINT-002, ...), and the output files are combined.
if docker_available():
modelrun, workspace = new_run("mpi")
backend = DockerConfig(
image="ghcr.io/rom-py/swan:41.51", executable="swan.exe", mpiexec="mpirun", cpu=2
)
ok = modelrun.run(backend, workspace_dir=workspace)
print("SWAN finished" if ok else "SWAN failed")
print(sorted(p.name for p in workspace.iterdir()))
SWAN finished ['INPUT', 'PRINT-001', 'PRINT-002', 'bottom.grd', 'norm_end', 'swangrid.nc', 'swaninit']
MPI pays off for large grids and long runs. For small models like this one, starting the processes costs more than it saves.
5. Checking a run¶
A backend reports whether SWAN exited, but SWAN can stop a computation because of an
error and still exit normally. Look for errors in PRINT:
for print_file in sorted(OUT_DIR.glob("*/PRINT*")):
errors = [line for line in print_file.read_text().splitlines() if "** Error" in line]
print(f"{print_file.parent.name}/{print_file.name}: {len(errors)} errors")
docker/PRINT: 0 errors mpi/PRINT-001: 0 errors mpi/PRINT-002: 0 errors
6. From the command line¶
rompy run generates and runs a YAML configuration, with the backend in its own YAML
file (type: docker, image, executable, ...). Tutorial 7
shows it.
Summary¶
ModelRun.run(backend)runs SWAN on a generated workspace;LocalConfiguses your installation andDockerConfiga container.ghcr.io/rom-py/swanhas SWAN with NetCDF and MPI; pin a version tag.mpiexecandcpurun SWAN in parallel.- Always check
PRINTfor errors.