Running XBeach¶
What this shows: the ways to run XBeach on a workspace generated by rompy-xbeach: with your own installation, with the public Docker image, in parallel with MPI, and from the command line.
Prerequisites: Tutorial 1: Your first XBeach model.
You will learn:
- how rompy's run backends (
LocalConfig,DockerConfig) execute a model - how to run XBeach in parallel with MPI
- how to generate and run a YAML configuration with
rompy run
Data used: bathy.tif and ww3-spectra-20230101-short.nc. The runs need Docker (or XBeach installed locally) and are
skipped otherwise.
Setup¶
A small, fast model is used throughout.
import shutil
import subprocess
from pathlib import Path
import yaml
from rompy.core.time import TimeRange
from rompy.logging import config as logging_config
from rompy.model import ModelRun
from rompy_xbeach.components.boundary.parameters import TideBoundaryConditions
from rompy_xbeach.components.output import Output
from rompy_xbeach.components.physics import Physics
from rompy_xbeach.components.physics.wavemodel import Surfbeat
from rompy_xbeach.config import Config, DataInterface
from rompy_xbeach.data.bathy import SeawardExtensionLinear, XBeachBathy
from rompy_xbeach.data.boundary import BoundaryStationSpectraJons
from rompy_xbeach.grid import RegularGrid
from rompy_xbeach.source import SourceCRSWavespectra, SourceGeotiff
logging_config.update(level="WARNING") # rompy logs every step, show warnings only
DATA_DIR = Path("../data")
OUT_DIR = Path("_output") / "running_xbeach"
shutil.rmtree(OUT_DIR, ignore_errors=True)
IMAGE = "ghcr.io/rom-py/xbeach:trunk-r6147"
config = Config(
grid=RegularGrid(
ori={"x": 115.594239, "y": -32.641104, "crs": 4326},
alfa=347.0,
dx=20.0,
dy=30.0,
nx=115,
ny=110,
crs=28350,
),
bathy=XBeachBathy(
source=SourceGeotiff(filename=DATA_DIR / "bathy.tif"),
posdwn=False,
extension=SeawardExtensionLinear(depth=15.0, slope=0.05),
),
input=DataInterface(
wave=BoundaryStationSpectraJons(
source=SourceCRSWavespectra(
uri=DATA_DIR / "ww3-spectra-20230101-short.nc", reader="read_ww3"
),
location="offshore",
sel_method="nearest",
thetamin=-90.0,
thetamax=90.0,
dtheta_s=10.0,
)
),
physics=Physics(wavemodel=Surfbeat()),
tide_boundary=TideBoundaryConditions(tideloc=0, zs0=0.0),
output=Output(globalvars=["H", "zs"], tintg=300.0),
)
period = TimeRange(start="2023-01-01T00:00", end="2023-01-01T00:10", interval="10m")
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
HAS_DOCKER = docker_available()
HAS_XBEACH = shutil.which("xbeach") is not None
print(f"Docker available: {HAS_DOCKER}, local XBeach available: {HAS_XBEACH}")
Docker available: True, local XBeach available: False
1. Generate once, run with a backend¶
ModelRun.run(backend, workspace_dir=...) runs an existing workspace. Passing the
workspace avoids generating the files a second time.
modelrun = ModelRun(run_id="serial", period=period, output_dir=OUT_DIR, config=config)
workspace = Path(modelrun())
2. With your own XBeach installation¶
LocalConfig runs a shell command in the workspace. Use it when XBeach is installed
on your machine or cluster node.
from rompy.backends import LocalConfig
local = LocalConfig(command="xbeach", timeout=3600)
if HAS_XBEACH:
print("Finished:", modelrun.run(local, workspace_dir=workspace))
else:
print(
"XBeach is not installed locally, skipping. The backend would run:",
local.command,
)
XBeach is not installed locally, skipping. The backend would run: xbeach
3. With Docker¶
The public image ghcr.io/rom-py/xbeach contains XBeach built with NetCDF and MPI.
DockerConfig mounts the workspace into the container and runs executable there.
Tags select the XBeach version: a release such as 1.24.6057-halloween-beta, a
trunk revision such as trunk-r6147, or latest for the newest trunk. Pin a
release or revision so runs stay reproducible.
from rompy.backends import DockerConfig
docker = DockerConfig(image=IMAGE, executable="xbeach")
if HAS_DOCKER:
print("Finished:", modelrun.run(docker, workspace_dir=workspace))
print(sorted(p.name for p in workspace.iterdir()))
Finished: True ['E_reuse.bcf', 'Es_reuse.bcf', 'XBlog.txt', 'XBwarning.txt', 'bathy.txt', 'ebcflist.bcf', 'esbcflist.bcf', 'parametric-20230101T000000.txt', 'params.txt', 'q_reuse.bcf', 'qbcflist.bcf', 'xboutput.nc', 'xdata.txt', 'ydata.txt']
4. In parallel with MPI¶
XBeach splits the domain between MPI processes. The Mpi component chooses how.
auto lets XBeach decide, while x and y split along one direction only.
With mpirun -n N, XBeach uses one process for output and N - 1 for the
computation. DockerConfig sets N with cpu and uses mpiexec to launch.
from rompy_xbeach.components.mpi import Mpi
parallel_run = ModelRun(
run_id="mpi",
period=period,
output_dir=OUT_DIR,
config=config.model_copy(update={"mpi": Mpi(mpiboundary="auto")}),
)
parallel_workspace = Path(parallel_run())
if HAS_DOCKER:
docker_mpi = DockerConfig(image=IMAGE, executable="xbeach", mpiexec="mpirun", cpu=4)
print("Finished:", parallel_run.run(docker_mpi, workspace_dir=parallel_workspace))
log = (parallel_workspace / "XBlog.txt").read_text()
print([line.strip() for line in log.splitlines() if "MPI version" in line])
Finished: True ['MPI version, running on 3 processes']
5. From the command line¶
rompy run generates and runs a YAML configuration with a backend described in a
second YAML file. First the model is written to YAML. Paths are resolved from the
folder the command runs in.
model_yaml = {
"run_id": "cli",
"output_dir": str(OUT_DIR),
"period": {
"start": "2023-01-01T00:00",
"end": "2023-01-01T00:10",
"interval": "10m",
},
"config": {
"model_type": "xbeach",
"grid": {
"model_type": "regular",
"ori": {"x": 115.594239, "y": -32.641104, "crs": "EPSG:4326"},
"alfa": 347.0,
"dx": 20.0,
"dy": 30.0,
"nx": 115,
"ny": 110,
"crs": "EPSG:28350",
},
"bathy": {
"model_type": "xbeach_bathy",
"source": {
"model_type": "geotiff",
"filename": str(DATA_DIR / "bathy.tif"),
},
"posdwn": False,
"extension": {"model_type": "linear", "depth": 15.0, "slope": 0.05},
},
"input": {
"wave": {
"model_type": "station_spectra_jons",
"source": {
"model_type": "wavespectra",
"uri": str(DATA_DIR / "ww3-spectra-20230101-short.nc"),
"reader": "read_ww3",
},
"location": "offshore",
"sel_method": "nearest",
"thetamin": -90.0,
"thetamax": 90.0,
"dtheta_s": 10.0,
}
},
"physics": {"wavemodel": {"model_type": "surfbeat"}},
"tide_boundary": {"tideloc": 0, "zs0": 0.0},
"output": {"globalvars": ["H", "zs"], "tintg": 300.0},
},
}
model_file = OUT_DIR / "model.yml"
model_file.write_text(yaml.safe_dump(model_yaml, sort_keys=False))
print(model_file.read_text()[:300], "...")
run_id: cli
output_dir: _output/running_xbeach
period:
start: 2023-01-01T00:00
end: 2023-01-01T00:10
interval: 10m
config:
model_type: xbeach
grid:
model_type: regular
ori:
x: 115.594239
y: -32.641104
crs: EPSG:4326
alfa: 347.0
dx: 20.0
dy: 30.0
nx ...
The backend file holds the same fields as DockerConfig, plus its type.
rompy backends create --backend-type docker writes a template.
backend_yaml = {
"type": "docker",
"image": IMAGE,
"executable": "xbeach",
"timeout": 3600,
}
(OUT_DIR / "docker.yml").write_text(yaml.safe_dump(backend_yaml, sort_keys=False))
print((OUT_DIR / "docker.yml").read_text())
type: docker image: ghcr.io/rom-py/xbeach:trunk-r6147 executable: xbeach timeout: 3600
Then one command generates and runs the model:
rompy run model.yml --backend-config docker.yml
if HAS_DOCKER:
result = subprocess.run(
[
"rompy",
"run",
str(OUT_DIR / "model.yml"),
"--backend-config",
str(OUT_DIR / "docker.yml"),
],
capture_output=True,
text=True,
)
print("Exit code:", result.returncode)
print(sorted(p.name for p in (OUT_DIR / "cli").iterdir()))
Exit code: 0 ['E_reuse.bcf', 'Es_reuse.bcf', 'XBlog.txt', 'XBwarning.txt', 'bathy.txt', 'ebcflist.bcf', 'esbcflist.bcf', 'parametric-20230101T000000.txt', 'params.txt', 'q_reuse.bcf', 'qbcflist.bcf', 'xboutput.nc', 'xdata.txt', 'ydata.txt']
rompy run --skip-generate runs a workspace generated earlier, and --dry-run only
generates it.
Summary¶
| How | Backend | When |
|---|---|---|
| Own installation | LocalConfig(command="xbeach") |
XBeach installed on your machine or cluster |
| Docker | DockerConfig(image="ghcr.io/rom-py/xbeach:...", executable="xbeach") |
No local XBeach needed |
| MPI | Mpi(...) in the config, mpiexec and cpu in the backend |
Larger domains |
| Command line | rompy run model.yml --backend-config backend.yml |
Scripts and pipelines |