SCHISM Backend Framework
Overview
The SCHISM backend framework provides a unified approach to executing SCHISM simulations using the ROMPY backend system. This framework integrates SCHISM model execution with Docker containers, providing consistent, reproducible, and scalable model runs.
Key Features
- Docker Integration - Automatic Docker image building and container management
- Backend Framework - Uses the unified ROMPY backend system
- Boundary Conditions Testing - Comprehensive testing suite for boundary condition examples
- Python-based Execution - Python scripts replace bash scripts for better maintainability
- Automatic Resource Management - Proper cleanup and resource allocation
Architecture
The backend framework consists of several key components:
Backend Configuration
The framework uses DockerConfig from the ROMPY backend system to configure Docker execution:
from rompy.backends import DockerConfig
from pathlib import Path
# Docker configuration with automatic image building
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=Path("docker/schism"),
timeout=3600,
cpu=8,
memory="4g",
executable="bash -c 'cd /tmp/schism && mpirun --allow-run-as-root -n 8 schism_v5.13.0 4'",
volumes=[f"{schism_dir}:/tmp/schism:rw"],
env_vars={
"OMPI_ALLOW_RUN_AS_ROOT": "1",
"OMPI_ALLOW_RUN_AS_ROOT_CONFIRM": "1"
},
remove_container=True,
user="root"
)
Model Execution
The framework integrates with ModelRun for complete model execution:
from rompy.model import ModelRun
from rompy.core.time import TimeRange
from datetime import datetime
# Create model run
model_run = ModelRun(
run_id="schism_example",
period=TimeRange(
start=datetime(2023, 1, 1),
end=datetime(2023, 1, 2),
interval="1H"
),
output_dir="/path/to/output",
delete_existing=True
)
# Generate configuration files
model_run.generate()
# Execute with backend
success = model_run.run(backend=docker_config)
Boundary Conditions Testing
The framework includes a comprehensive testing system for SCHISM boundary conditions examples.
Test Runner
The run_boundary_conditions_examples.py script provides automated testing of boundary condition configurations:
# Run all examples
python run_boundary_conditions_examples.py
# Run specific categories
python run_boundary_conditions_examples.py --tidal
python run_boundary_conditions_examples.py --hybrid
python run_boundary_conditions_examples.py --river
python run_boundary_conditions_examples.py --nested
# Run single example
python run_boundary_conditions_examples.py --single basic_tidal
# Dry run (validation only)
python run_boundary_conditions_examples.py --dry-run
Example Categories
The testing framework supports several categories of boundary condition examples:
Tidal Examples:
basic_tidal- Pure tidal forcing with M2, S2, N2 constituentsextended_tidal- Enhanced tidal setup with refined parameterstidal_with_potential- Tidal forcing with earth tidal potentialtide_wave- Tidal forcing with wave interaction (WWM)tidal_with_mdt- Tidal forcing with Mean Dynamic Topography correctiontidal_with_mdt_const- Tidal forcing with constant MDT
Hybrid Examples:
hybrid_elevation- Combined tidal and external elevation datafull_hybrid- Complete hybrid setup with all boundary types
River Examples:
simple_river- Single river inflow with tidal ocean boundarymulti_river- Multiple river boundaries with different properties
Nested Examples:
nested_with_tides- Nested boundary conditions with relaxation
Docker Image Management
The framework automatically manages Docker image building and container lifecycle.
Automatic Image Building
Docker images are built automatically from the SCHISM Dockerfile:
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=project_root / "docker" / "schism",
# ... other parameters
)
The framework will:
- Locate the Dockerfile in the build context
- Build the Docker image with appropriate tags
- Use the built image for model execution
- Clean up containers after execution
Resource Management
The framework provides proper resource allocation and cleanup:
docker_config = DockerConfig(
cpu=8, # Number of CPU cores
memory="4g", # Memory limit
timeout=3600, # Execution timeout (seconds)
remove_container=True, # Clean up after execution
volumes=[ # Volume mounts
f"{schism_dir}:/tmp/schism:rw"
]
)
Testing Framework
The backend framework includes comprehensive testing capabilities.
Test Suite Components
The test suite validates multiple aspects of the framework:
Initialization Testing:
def test_runner_initialization():
"""Test that the runner initializes correctly."""
runner = SchismExampleRunner()
assert runner.project_root.exists()
assert runner.examples_dir.exists()
assert len(runner.examples) > 0
Configuration Validation:
def test_configuration_validation():
"""Test that example configurations are valid."""
runner = SchismExampleRunner()
for name, config in runner.examples.items():
config_file = config["file"]
assert config_file.exists()
# Validate YAML structure
with open(config_file, 'r') as f:
yaml_data = yaml.safe_load(f)
assert "run_id" in yaml_data
assert "period" in yaml_data
assert "config" in yaml_data
Docker Configuration Testing:
def test_docker_config_creation():
"""Test Docker configuration creation."""
runner = SchismExampleRunner()
test_path = Path("/tmp/test_schism")
test_path.mkdir(parents=True, exist_ok=True)
config = runner._create_docker_config(test_path, "")
assert config.dockerfile == Path("Dockerfile")
assert config.build_context.exists()
assert config.cpu == 8
assert config.memory == "4g"
Running Tests
Execute the test suite to validate the framework:
# Run comprehensive test suite
python test_backend_examples.py
# Expected output:
# ============================================================
# SCHISM Backend Framework Test Suite
# ============================================================
# Testing SchismExampleRunner initialization... ✓
# Testing example discovery... ✓
# Testing configuration validation... ✓
# Testing dry run functionality... ✓
# Testing Docker configuration creation... ✓
# Testing prerequisites... ✓
# ============================================================
# TEST SUMMARY
# ============================================================
# Passed: 6
# Failed: 0
# Total: 6
#
# 🎉 All tests passed!
Configuration Examples
YAML Configuration
Configure backend execution in YAML configuration files:
# Basic SCHISM configuration with backend
run_id: schism_example
period:
start: 2023-01-01T00:00:00
end: 2023-01-02T00:00:00
interval: 1H
output_dir: schism_output
config:
model_type: schism
grid:
grid_type: schism
hgrid:
model_type: data_blob
source: tests/data/schism/hgrid.gr3
data:
data_type: schism
boundary_conditions:
data_type: boundary_conditions
setup_type: tidal
tidal_data:
tidal_database: tests/data/schism/tides
tidal_model: 'OCEANUM-atlas'
constituents: [M2, S2, N2]
boundaries:
0:
elev_type: 3
vel_type: 3
temp_type: 0
salt_type: 0
Python Configuration
Configure backend execution programmatically:
from rompy.model import ModelRun
from rompy.backends import DockerConfig
from rompy.core.time import TimeRange
from datetime import datetime
from pathlib import Path
# Create model configuration
model_run = ModelRun(
run_id="schism_backend_example",
period=TimeRange(
start=datetime(2023, 1, 1),
end=datetime(2023, 1, 2),
interval="1H"
),
output_dir="schism_output",
delete_existing=True
)
# Configure Docker backend
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=Path("docker/schism"),
timeout=3600,
cpu=8,
memory="4g",
executable="bash -c 'cd /tmp/schism && mpirun --allow-run-as-root -n 8 schism_v5.13.0 4'",
volumes=[f"{output_dir}:/tmp/schism:rw"],
env_vars={
"OMPI_ALLOW_RUN_AS_ROOT": "1",
"OMPI_ALLOW_RUN_AS_ROOT_CONFIRM": "1"
}
)
# Generate configuration
model_run.generate()
# Execute simulation
success = model_run.run(backend=docker_config)
if success:
print("SCHISM simulation completed successfully")
# Process results
model_run.postprocess()
else:
print("SCHISM simulation failed")
Advanced Usage
Custom Docker Images
Use custom Docker images for specific SCHISM versions or configurations:
# Build custom image with specific SCHISM version
docker_config = DockerConfig(
dockerfile=Path("Dockerfile.custom"),
build_context=Path("docker/schism"),
build_args={
"SCHISM_VERSION": "v5.11.1",
"ENABLE_WWM": "ON"
}
)
Parallel Execution
Configure parallel execution for multiple examples:
import concurrent.futures
from rompy.backends import DockerConfig
def run_example(example_name):
"""Run a single example."""
runner = SchismExampleRunner()
return runner._run_example(example_name)
# Run multiple examples in parallel
examples = ["basic_tidal", "extended_tidal", "hybrid_elevation"]
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(run_example, name) for name in examples]
results = [future.result() for future in concurrent.futures.as_completed(futures)]
Performance Optimization
Optimize Docker configuration for better performance:
# High-performance configuration
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=Path("docker/schism"),
cpu=16, # More CPU cores
memory="8g", # More memory
timeout=7200, # Longer timeout
volumes=[
f"{schism_dir}:/tmp/schism:rw",
"/tmp:/tmp:rw" # Additional temp space
],
env_vars={
"OMPI_ALLOW_RUN_AS_ROOT": "1",
"OMPI_ALLOW_RUN_AS_ROOT_CONFIRM": "1",
"OMP_NUM_THREADS": "16"
}
)
Error Handling
The framework provides comprehensive error handling and logging.
Common Error Scenarios
Docker Build Failures:
try:
success = model_run.run(backend=docker_config)
except Exception as e:
if "dockerfile" in str(e).lower():
print(f"Docker build failed: {e}")
print("Check Dockerfile and build context paths")
else:
raise
Resource Constraints:
# Handle resource constraint errors
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=Path("docker/schism"),
cpu=min(8, os.cpu_count()), # Don't exceed available CPUs
memory="4g",
timeout=3600
)
Volume Mount Issues:
# Ensure directories exist before mounting
output_dir = Path("schism_output")
output_dir.mkdir(parents=True, exist_ok=True)
docker_config = DockerConfig(
volumes=[f"{output_dir.absolute()}:/tmp/schism:rw"]
)
Logging and Monitoring
Enable detailed logging for debugging:
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Run with detailed logging
logger = logging.getLogger(__name__)
logger.info("Starting SCHISM backend execution")
try:
success = model_run.run(backend=docker_config)
logger.info(f"Execution completed: {'success' if success else 'failed'}")
except Exception as e:
logger.error(f"Execution failed: {e}")
raise
Migration Guide
Migrating from Bash Scripts
If you're migrating from bash-based SCHISM execution:
Old Approach (Bash):
#!/bin/bash
docker build -t schism -f Dockerfile .
docker run -v $PWD:/tmp/schism schism bash -c "cd /tmp/schism && mpirun --allow-run-as-root -n 8 schism_v5.13.0 4"
New Approach (Python + Backend Framework):
from rompy.backends import DockerConfig
from rompy.model import ModelRun
from pathlib import Path
# Configure backend
docker_config = DockerConfig(
dockerfile=Path("Dockerfile"),
build_context=Path("."),
executable="bash -c 'cd /tmp/schism && mpirun --allow-run-as-root -n 8 schism_v5.13.0 4'",
volumes=[f"{Path.cwd()}:/tmp/schism:rw"]
)
# Execute
model_run = ModelRun(run_id="example", ...)
success = model_run.run(backend=docker_config)
Benefits of Migration
- Consistent Backend System - Uses the same backend framework as other ROMPY models
- Automatic Image Building - No manual Docker commands required
- Better Error Handling - Comprehensive exception handling and logging
- Type Safety - Python type hints and Pydantic validation
- Testing Framework - Built-in testing and validation capabilities
- Maintainability - Python code is easier to maintain than bash scripts
Best Practices
- Use Dockerfile Parameters
Always use dockerfile and build_context instead of pre-built images for reproducibility.
- Resource Management
Set appropriate CPU, memory, and timeout limits based on your model requirements.
- Volume Mounts
Use absolute paths for volume mounts to avoid path resolution issues.
- Environment Variables
Set required environment variables for MPI execution in Docker containers.
- Testing
Always test configurations with dry runs before full execution.
- Cleanup
Enable container cleanup with remove_container=True to avoid accumulating containers.
Troubleshooting
Common Issues and Solutions
Docker Build Failures: - Check that Dockerfile exists in build context - Verify build context path is correct - Ensure Docker daemon is running
Container Execution Failures: - Check volume mount paths exist - Verify executable command syntax - Review environment variable settings
Resource Exhaustion: - Reduce CPU/memory allocation - Increase timeout values - Monitor system resources during execution
Permission Issues:
- Use user: root for MPI execution
- Set appropriate volume mount permissions
- Configure MPI environment variables
See Also
- Boundary Conditions - SCHISM boundary conditions documentation
- Hotstart - Hotstart configuration documentation
- ROMPY Backend Framework - ROMPY backend framework documentation
rompy.backends.DockerConfig- Docker backend configurationrompy.model.ModelRun- Model execution framework