Tip

  1. Need help? Please let us know in the SUEWS Community.

  2. Please report issues with the manual on GitHub Issues (or use Report Issue for This Page for page-specific feedback).

  3. Please cite SUEWS with proper information from our Zenodo page.

6.5. Python API for CLI Users#

This guide shows Python equivalents for common SUEWS command-line operations, enabling you to integrate SUEWS functionality directly into your Python scripts and analysis workflows.

6.5.1. Configuration Validation#

Validating YAML configuration files programmatically.

6.5.1.1. CLI Command#

suews-validate config.yml

6.5.1.2. Python Equivalent (Simple Validation)#

Quick schema validation for checking configuration files:

from pathlib import Path
from supy.cmd.validate_config import validate_single_file
from supy.data_model.schema.publisher import generate_json_schema

# Generate the current schema
schema = generate_json_schema()

# Validate a single file
is_valid, errors = validate_single_file(
    Path("config.yml"),
    schema,
    show_details=True
)

if not is_valid:
    for error in errors:
        print(f"Error: {error}")
else:
    print("Configuration is valid!")

6.5.1.3. Python Equivalent (Full Validation Pipeline)#

Complete validation including all phases (A→B→C) for thorough checking:

import importlib.resources
import supy
from supy.data_model.validation.pipeline.orchestrator import (
    validate_input_file,
    setup_output_paths,
    run_phase_a,
    run_phase_b,
    run_phase_c
)

# Validate and get standard config
user_yaml_file = validate_input_file("config.yml")
sample_data = importlib.resources.files(supy) / "sample_data"
with importlib.resources.as_file(sample_data / "sample_config.yml") as path:
    standard_yaml_file = str(path)

# Setup output paths
(uptodate_file, report_file,
 science_yaml_file, science_report_file,
 pydantic_yaml_file, pydantic_report_file,
 dirname) = setup_output_paths(user_yaml_file, pipeline="ABC")

# Run phases A → B → C
a_ok = run_phase_a(
    user_yaml_file, standard_yaml_file, uptodate_file,
    report_file, mode="public", phase="ABC", silent=False, forcing="on"
)

if not a_ok:
    print(f"✗ Phase A failed")
    print(f"  Report: {report_file}")
    print(f"  Updated YAML: {uptodate_file}")
else:
    b_ok = run_phase_b(
        user_yaml_file, uptodate_file, standard_yaml_file,
        science_yaml_file, science_report_file, report_file,
        phase_a_performed=True, mode="public", phase="ABC", silent=False
    )

    if not b_ok:
        print(f"✗ Phase B failed")
        print(f"  Report: {science_report_file}")
        print(f"  Updated YAML: {uptodate_file}")
    else:
        c_ok = run_phase_c(
            science_yaml_file, pydantic_yaml_file, pydantic_report_file,
            mode="public", phases_run=["A", "B", "C"], silent=False
        )

        if not c_ok:
            print(f"✗ Phase C failed")
            print(f"  Report: {pydantic_report_file}")
            print(f"  Updated YAML: {science_yaml_file}")
        else:
            print(f"✓ All phases passed!")
            print(f"  Report: {pydantic_report_file}")
            print(f"  Updated YAML: {pydantic_yaml_file}")

6.5.2. Configuration Conversion#

Converting between SUEWS input formats programmatically.

6.5.2.1. CLI Command#

suews-convert -i input_dir/RunControl.nml -o config.yml
suews-convert -i df_state.csv -o config.yml

6.5.2.2. Python Equivalent#

from supy.util.converter import convert_to_yaml
from pathlib import Path

# Convert table-based SUEWS input to YAML
convert_to_yaml(
    input_file="input_dir/RunControl.nml",
    output_file="config.yml",
    from_ver=None,  # Auto-detect version
    debug_dir=None,  # No debug files
    validate_profiles=True  # Validate profiles automatically
)

# Convert df_state format to YAML
convert_to_yaml(
    input_file="df_state.csv",
    output_file="config_from_state.yml",
    from_ver=None,  # Not used for df_state
    validate_profiles=True
)

print("Conversion completed successfully!")

6.5.2.3. Advanced Usage with Debugging#

from supy.util.converter import (
    convert_to_yaml,
    detect_table_version,
    detect_input_type
)
from pathlib import Path
import f90nml

input_path = Path("RunControl.nml")

# Detect input type and version
input_type = detect_input_type(input_path)
print(f"Input type: {input_type}")

if input_type == "nml":
    # Read RunControl.nml to get the input directory path
    nml = f90nml.read(str(input_path))
    file_input_path = Path(nml['runcontrol']['fileinputpath'])

    # Handle both absolute and relative paths
    if file_input_path.is_absolute():
        input_dir = file_input_path
    else:
        input_dir = input_path.parent / file_input_path

    version = detect_table_version(input_dir)
    print(f"Detected table version: {version}")

# Convert with debug directory to keep intermediate files
convert_to_yaml(
    input_file=str(input_path),
    output_file="config.yml",
    debug_dir="./debug_conversion",  # Keep intermediate files
    validate_profiles=False  # Skip profile validation if needed
)

6.5.3. Running SUEWS Simulations#

Executing SUEWS simulations from the command line or Python.

Note

SUEWS now uses YAML configuration files. The legacy namelist format (RunControl.nml) is deprecated.

To convert existing namelist files to YAML, see conversion.

6.5.3.1. CLI Command#

# Run with RunControl.nml in current directory (default behaviour)
suews-run

# Run with RunControl.nml at specific path
suews-run -p path/to/RunControl.nml

Note

The CLI command currently only supports the deprecated namelist format. YAML support is being added - see Issue #834.

For YAML configurations, use the Python API below.

6.5.3.2. Python API#

Run SUEWS simulations using YAML configuration files with the SUEWSSimulation class.

6.5.3.3. Basic Simulation#

from supy import SUEWSSimulation

# Create simulation from YAML configuration
sim = SUEWSSimulation("config.yml")

# Run simulation
sim.run()

# Save results
sim.save("./output")

print("Simulation completed successfully!")

6.5.3.4. Accessing Results#

from supy import SUEWSSimulation

# Run simulation
sim = SUEWSSimulation("config.yml")
sim.run()

# Access simulation results
results = sim.results  # DataFrame with simulation output

# Access configuration
config = sim.config

# Access forcing data
forcing = sim.forcing

# Perform custom analysis
print(f"Mean QH: {results['QH'].mean():.2f} W/m²")
print(f"Max QE: {results['QE'].max():.2f} W/m²")

6.5.3.5. Updating Configuration#

from supy import SUEWSSimulation

# Load base configuration
sim = SUEWSSimulation("base_config.yml")

# Update specific parameters
sim.update_config({
    "model": {
        "control": {
            "tstep": 300  # Change to 5-minute timestep
        }
    }
})

# Reset and run with new configuration
sim.reset()
sim.run()
sim.save("./output_modified")

6.5.3.6. Custom Forcing Data#

from supy import SUEWSSimulation
import pandas as pd

# Create simulation
sim = SUEWSSimulation("config.yml")

# Load custom forcing data
df_forcing = pd.read_csv("custom_forcing.csv", index_col=0, parse_dates=True)

# Update forcing
sim.update_forcing(df_forcing)

# Run with custom forcing
sim.run()
sim.save("./output_custom_forcing")

6.5.4. Schema Management#

Managing configuration schemas programmatically.

6.5.4.1. CLI Commands#

suews-schema export -o schema.json
suews-schema validate config.yml
suews-schema migrate old_config.yml --target-version 2.0

6.5.4.2. Python Equivalent (Schema Export)#

from supy.data_model.schema.publisher import generate_json_schema, save_schema
import json
from pathlib import Path

# Generate current schema
schema = generate_json_schema()

# Save as JSON
with open("schema.json", "w") as f:
    json.dump(schema, f, indent=2)

# Or use the built-in save function
save_schema(schema, Path("suews_schema.json"))

print("Schema exported successfully!")

6.5.4.3. Python Equivalent (Schema Validation)#

import yaml
import jsonschema
from supy.data_model.schema.publisher import generate_json_schema
from supy.data_model.core.config import SUEWSConfig

# Load configuration
with open("config.yml", "r") as f:
    config = yaml.safe_load(f)

# Generate schema and validate
schema = generate_json_schema()

try:
    # JSON Schema validation
    jsonschema.validate(config, schema)

    # Pydantic model validation (additional checks)
    suews_config = SUEWSConfig(**config)

    print("Configuration is valid!")

except jsonschema.ValidationError as e:
    print(f"Schema validation error: {e.message}")
except Exception as e:
    print(f"Model validation error: {e}")

6.5.4.4. Python Equivalent (Schema Migration)#

import yaml
from supy.data_model.schema.migration import SchemaMigrator
from supy.data_model.schema.version import CURRENT_SCHEMA_VERSION

# Load old configuration
with open("old_config.yml", "r") as f:
    old_config = yaml.safe_load(f)

# Migrate to current version
migrator = SchemaMigrator()

# Auto-detect current version
current_version = migrator.auto_detect_version(old_config)
print(f"Detected version: {current_version}")

# Migrate to target version
migrated_config = migrator.migrate(
    old_config,
    from_version=current_version,
    to_version=CURRENT_SCHEMA_VERSION
)

# Save migrated configuration
with open("migrated_config.yml", "w") as f:
    yaml.dump(migrated_config, f, default_flow_style=False, sort_keys=False)

print(f"Migration completed: {current_version}{CURRENT_SCHEMA_VERSION}")

6.5.4.5. Batch Operations#

import yaml
from pathlib import Path
from supy.data_model.schema.publisher import generate_json_schema
from supy.cmd.validate_config import validate_single_file

# Process multiple configuration files
config_dir = Path("configurations")
schema = generate_json_schema()

results = []
for config_file in config_dir.glob("*.yml"):
    is_valid, errors = validate_single_file(config_file, schema, show_details=False)
    results.append({
        "file": config_file.name,
        "valid": is_valid,
        "error_count": len(errors)
    })

# Summary report
valid_count = sum(1 for r in results if r["valid"])
total_count = len(results)

print(f"Validation Summary: {valid_count}/{total_count} files valid")

for result in results:
    status = "✓" if result["valid"] else "✗"
    print(f"  {status} {result['file']}")

6.5.5. See Also#