Skip to content

Headless Linux/Wine/Ras2Cng Setup and Qualification

Set up an isolated Linux task that uses official HEC-RAS Linux executables where supported and the same Windows HEC-RAS version under Wine only for RasProcess/RASMapper gaps and ras2cng stored-map export. This is a qualification workflow: a passing process exit code is not sufficient.

Route operations before provisioning

Operation Preferred route
Unsteady/steady computation Official HEC-RAS Linux executable
Geometry preprocessing Official Linux RasGeomPreprocess when supported; qualified Wine RasProcess.compute_geometry() for version gaps
RASMapper mesh, terrain, property tables Qualified Windows components under Wine when no vendor Linux path exists
Stored result maps ras2cng with bundled RasStoreMapHelper under Wine
GeoParquet, COG, PMTiles Native-Linux Python/GDAL/ras2cng

Notebook 510 covers the official Linux solver. Notebooks 960–962 cover cloud-native exports. The repository skill .claude/skills/hecras-setup-linux-wine-ras2cng/ contains the complete setup and qualification runbook.

Python
USE_LOCAL_SOURCE = False

import json
import os
from pathlib import Path
import platform
import shlex
import subprocess
import sys

if USE_LOCAL_SOURCE:
    local_path = str(Path.cwd().parent)
    if local_path not in sys.path:
        sys.path.insert(0, local_path)

from ras_commander import RasProcess
try:
    from ras_commander import RasTcu
except ImportError:
    RasTcu = None  # Requires ras-commander with the audited TCU API.

RAS_VERSION = os.environ.get("RAS_VERSION", "7.0")
WINE_PREFIX = Path(os.environ.get("WINEPREFIX", "/work/job/wine"))
RAS_INSTALL_DIR = Path(os.environ.get("RAS_INSTALL_DIR", WINE_PREFIX / "drive_c/HEC-RAS/7.0"))
PROJECT_DIR = Path(os.environ.get("RAS_PROJECT_DIR", "/work/job/project"))
OUTPUT_DIR = Path(os.environ.get("RAS_OUTPUT_DIR", "/work/job/output"))
print(platform.platform())

Create one prefix and project copy per task

Create a qualified read-only template prefix during image provisioning. At job start, copy it to node-local storage together with a writable project copy. Never share an active HDF or writable prefix. Run one RASMapper helper per prefix and use --map-workers 1; scale with isolated scheduler tasks.

Bash
task_root="${TMPDIR:-/tmp}/hecras-${SLURM_JOB_ID:-$$}-${SLURM_ARRAY_TASK_ID:-0}"
export WINEPREFIX="$task_root/wine"
export RAS_INSTALL_DIR="$WINEPREFIX/drive_c/HEC-RAS/7.0"
mkdir -p "$task_root/project" "$task_root/output"
cp -a /opt/hecras-prefix-template/. "$WINEPREFIX/"
cp -a /approved/model/. "$task_root/project/"
Python
# Run this inside the actual scheduler/cgroup namespace before Wine starts.
skill_relative = Path(".claude/skills/hecras-setup-linux-wine-ras2cng/scripts/headless_wine_preflight.py")
candidates = [Path.cwd() / skill_relative, Path.cwd().parent / skill_relative]
preflight_script = next((path for path in candidates if path.exists()), None)

if platform.system() != "Linux":
    print("Run this cell on the Linux worker that will launch Wine.")
elif preflight_script is None:
    print("Preflight script not found; run from a ras-commander source checkout.")
else:
    command = [
        sys.executable, str(preflight_script),
        "--wine-prefix", str(WINE_PREFIX),
        "--ras-install-dir", str(RAS_INSTALL_DIR),
        "--require-safe-topology", "--require-runtime", "--require-python-packages",
    ]
    completed = subprocess.run(command, capture_output=True, text=True, check=False)
    print(completed.stdout)
    completed.check_returncode()

Why the CPU gate matters

Wine can combine a counted processor topology with raw Linux CPU IDs. A sparse cpuset such as 2,5-7 with a reported count of four can return invalid Windows processor indices, producing nondeterministic CLR 0x80131506, access violations, or non-returning RASMapper calls. Prefer a coherent zero-based visible CPU namespace and enforce consumption with a CPU quota. Single-CPU pinning is a fallback only, and taskset cannot renumber CPU IDs.

Provision TCU state without an unknown-modal click

RasTcu.status() is read-only. If the operator has already accepted the same installed version and authorizes transfer, call RasTcu.accept() from Windows Python inside the target prefix, or initialize with accept_tcu=True. The audited flow copies an existing accepted registry subtree and writes an acknowledgement record. If no donor exists, stop; never make a generic watchdog click the first button on an unknown modal.

Python
# TCU status is read-only. Run this branch with Windows Python inside Wine.
if os.name == "nt" and RasTcu is not None:
    print(RasTcu.status(ras_version=RAS_VERSION))
elif RasTcu is None:
    print("Upgrade ras-commander before provisioning TCU state.")

# Configure only after the native-Linux preflight passes.
if platform.system() == "Linux" and WINE_PREFIX.exists() and RAS_INSTALL_DIR.exists():
    RasProcess.configure_wine(
        wine_prefix=WINE_PREFIX,
        wine_executable=os.environ.get("WINE", "wine"),
        ras_install_dir=RAS_INSTALL_DIR,
    )
    wine_status = RasProcess.check_wine_environment()
    print(json.dumps(wine_status, indent=2, default=str))
else:
    print("Set WINEPREFIX and RAS_INSTALL_DIR on the Linux worker before configuring Wine.")

Generate stored maps through ras2cng

Use the bundled RasStoreMapHelper path because RasProcess alone does not preserve the required interpolation behavior for stored maps. Keep mapping serial within one prefix.

Python
ras2cng_command = [
    "ras2cng", "map", str(PROJECT_DIR), str(OUTPUT_DIR / "maps"),
    "--ras-version", RAS_VERSION,
    "--rasprocess", str(RAS_INSTALL_DIR),
    "--map-workers", "1",
    "--depth", "--wse", "--velocity", "--cog", "--fail-fast",
]
print(shlex.join(ras2cng_command))
# subprocess.run(ras2cng_command, check=True)  # Enable only on an isolated qualified worker.

Qualify content before promotion

Compare a representative fixture to native Windows using the exact HEC-RAS version. Record exact cell/face counts, boundary assignments, property-table completeness, geometry/terrain fingerprints, hydraulic tolerances, and raster CRS, transform, dimensions, nodata, overlap, values, and pixel hashes. Muncie is the small parity fixture; BaldEagleCrkMulti2D is appropriate for projection and mesh/mapper coverage. Do not promote a runner with critical skips.