Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Recent Feature Additions

Version 0.1.14

CLI engine commands

New erad engine sub-commands for working with the DuckDB simulation engine from the command line:

  • erad engine run <model> <hazard> -o results.parquet — Run a simulation using the DuckDB engine and export directly to Parquet, SQLite, or CSV.

  • erad engine convert input.parquet output.db — Convert simulation results between formats (auto-detects from file extension).

  • erad engine query results.parquet --sql "SELECT ..." — Run SQL queries against result files.

  • erad engine info results.parquet — Display summary statistics (asset count, survival probability distribution, high-risk asset count).

Conversion utilities

Bidirectional conversion between Pydantic models and DuckDB engine:

  • SimulationEngine.from_asset_system(asset_system) — Convert existing Pydantic results into DuckDB for fast export and analysis.

  • AssetSystem.from_engine(engine) — Convert DuckDB engine results back to a full Pydantic AssetSystem (for plotting, GDM integration, etc.).

  • SimulationEngine.from_parquet(path) — Load previously exported Parquet results into DuckDB for querying.

  • engine.to_asset_states() — Extract DuckDB results as a list of (asset_id, AssetState) tuples.

Organization rename

  • Updated references from “National Renewable Energy Laboratory (NREL)” to “National Laboratory of the Rockies (NLR)”.

  • Author/maintainer emails updated to @nlr.gov.

  • PyPI package name remains NREL-erad for backward compatibility.

from erad.engine import SimulationEngine
from erad.systems.asset_system import AssetSystem

# Conversion utilities overview
print("Pydantic → DuckDB:")
print("  engine = SimulationEngine.from_asset_system(asset_system)")
print()
print("DuckDB → Pydantic:")
print("  asset_system = AssetSystem.from_engine(engine)")
print()
print("Parquet → DuckDB:")
print("  engine = SimulationEngine.from_parquet('results.parquet')")
print()
print("CLI commands:")
print("  erad engine run <model> <hazard> -o results.parquet")
print("  erad engine convert results.parquet results.db")
print("  erad engine query results.parquet --sql 'SELECT ...'")
print("  erad engine info results.parquet")

Version 0.1.13

Non-hurricane wind gust model

New WindGustModel for non-hurricane wind events (thunderstorms, derechos, microbursts):

  • GustModelArea: Defines a maximum wind polygon with wind speed, surrounding affected distance, and configurable decay rate.

  • Distance-decay physics: Assets inside the max wind polygon receive full wind speed; assets in the surrounding affected area receive decayed speed based on (R / (d + R))^decay_rate where R is the characteristic polygon radius and d is distance from boundary.

  • UTM-based calculations: Uses estimate_utm_crs() for accurate distance measurements with pre-computed projections cached per area for performance.

  • Visualization: WindGustModel.plot() renders both max wind and affected area polygons on interactive maps.

  • CLI support: erad hazards example wind_gust shows example configuration.

Performance optimization for wind gust calculations

  • Pre-computed UTM projections, polygon buffers, and characteristic radii are cached as a @cached_property on GustModelArea.

  • Per-asset computation only requires a lightweight pyproj.Transformer point reprojection + Shapely distance calculation.

  • ~10-50x faster than the initial implementation for multi-asset simulations.

from erad.models.hazard.wind_gust import WindGustModel, GustModelArea
from shapely.geometry import Polygon
from infrasys.quantities import Distance
from erad.quantities import Speed
from datetime import datetime

# Create a wind gust model
area = GustModelArea(
    name="Thunderstorm Cell",
    max_wind_area=Polygon([
        (-97.286, 28.297),
        (-97.280, 28.305),
        (-97.267, 28.287),
        (-97.275, 28.282),
    ]),
    max_wind_speed=Speed(120, "miles/hour"),
    wind_aff_distance=Distance(5, "miles"),
    decay_rate=0.5,
)

model = WindGustModel(
    name="severe_tstorm",
    timestamp=datetime(2024, 6, 15, 14, 30),
    max_wind_areas=[area],
)

print(f"Wind gust model: {model.name}")
print(f"  Timestamp: {model.timestamp}")
print(f"  Areas: {len(model.max_wind_areas)}")
print(f"  Max wind speed: {area.max_wind_speed}")
print(f"  Affected distance: {area.wind_aff_distance}")
print(f"  Decay rate: {area.decay_rate}")

Version 0.1.12

DuckDB-powered simulation engine

A new high-performance simulation engine backed by DuckDB enables ERAD to scale to million+ assets:

  • Vectorized computation: All hazard vector calculations (earthquake, wind, flood, fire) are expressed as SQL CROSS JOINs with custom UDFs, replacing the previous Python loop-based approach.

  • Custom UDFs: Haversine distance, Holland wind model, MMI/PGA/PGV calculations, and CDF evaluation are registered as native DuckDB functions for SIMD-accelerated processing.

  • Automatic parallelism: DuckDB uses all available CPU cores for computation without requiring explicit multiprocessing.

  • Multiple export formats: Results can be exported directly to Parquet (ZSTD compressed), SQLite, CSV, PyArrow Table, or pandas DataFrame.

  • Direct array loading: For million+ asset scale, assets can be loaded from numpy arrays or DataFrames without Pydantic object overhead.

  • API preservation: The existing HazardSimulator.run() interface is unchanged — the engine selection is controlled by the engine parameter ('duckdb' or 'legacy').

GDM interface improvements

  • Updated AssetSystem.from_gdm() to support the latest GDM component types.

  • Improved coordinate extraction and elevation handling during model loading.

Dependency updates

  • Added duckdb>=1.0 and pyarrow as core dependencies.

  • Pinned grid-data-models==2.3.7.

from erad.engine import SimulationEngine

# Initialize the DuckDB-powered engine
engine = SimulationEngine()
print(f"Engine initialized: {type(engine).__name__}")

Using the DuckDB engine via HazardSimulator

The DuckDB engine is the new default. Use engine='legacy' to fall back to the original loop-based approach.

from erad.runner import HazardSimulator

# DuckDB engine (default)
# sim = HazardSimulator(asset_system, engine='duckdb')
# sim.run(hazard_system)

# Legacy engine (for comparison/debugging)
# sim = HazardSimulator(asset_system, engine='legacy')
# sim.run(hazard_system)

print("Available engine modes: 'duckdb' (default), 'legacy'")

Scale workloads (100K+ assets)

For large-scale simulations, skip Pydantic object hydration and use the engine’s export methods directly:

import numpy as np
from uuid import uuid4
from datetime import datetime
from shapely.geometry import Point
from infrasys.quantities import Distance
from erad.engine import SimulationEngine
from erad.systems.hazard_system import HazardSystem
from erad.models.hazard.earthquake import EarthQuakeModel
from erad.default_fragility_curves import DEFAULT_FRAGILTY_CURVES

# Generate 10K synthetic assets
N = 10_000
np.random.seed(42)
asset_ids = [str(uuid4()) for _ in range(N)]
asset_names = [f'pole_{i}' for i in range(N)]
asset_types = np.full(N, 6, dtype=np.int32)  # distribution_poles
asset_type_names = ['distribution_poles'] * N
lats = 36.0 + np.random.random(N)
lons = -121.0 + np.random.random(N)
heights = np.full(N, 10.0)
elevations = np.zeros(N)

# Create hazard
eq = EarthQuakeModel(
    name='eq1',
    timestamp=datetime(2024, 1, 1),
    origin=Point(-120.5, 36.5),
    depth=Distance(15, 'km'),
    magnitude=5.5,
)
hazard_system = HazardSystem(name='demo')
hazard_system.add_component(eq)

# Run via engine directly (fastest path)
engine = SimulationEngine()
engine.load_assets_from_arrays(
    asset_ids, asset_names, asset_types, asset_type_names,
    lats, lons, heights, elevations
)
engine.load_hazards(hazard_system)
engine.load_fragility_curves(DEFAULT_FRAGILTY_CURVES)
engine._loaded = True
engine.run()

# Export results
df = engine.export_to_dataframe()
print(f"Computed {len(df):,} asset-state records")
print(f"Mean survival probability: {df['survival_probability'].mean():.4f}")
print(f"Min survival probability: {df['survival_probability'].min():.6f}")
df.head()

Version 0.1.11

MCP server

Introduction of a Model Context Protocol (MCP) server for ERAD, enabling AI agents and language models to interact with simulation capabilities through standardized tools:

  • erad-mcp: CLI entry point exposing ERAD functionality as MCP tools.

  • Core tools: load_distribution_model, run_simulation, generate_scenarios, export_to_json, export_to_sqlite, and more.

  • Query tools: list_asset_types, query_assets, get_asset_details, get_asset_statistics, get_network_topology.

  • Hazard tools: list_historic_earthquakes, list_historic_hurricanes, list_historic_wildfires, load_historic_earthquake, load_historic_hurricane, load_historic_wildfire.

  • Documentation search: search_documentation tool for querying ERAD docs.

CLI interface

New command-line interface via typer:

  • erad run — Run hazard simulations from the command line.

  • erad export — Export results to various formats.

  • erad list-hazards — List available historic hazard events.

Dependency updates

  • Added mcp>=1.0.0, typer>=0.9.0, rich>=13.0.0.

# MCP server can be started via CLI:
# $ erad-mcp

# Or used programmatically:
from erad.mcp import TOOLS
print(f"MCP server exposes {len(TOOLS)} tools")

Version 0.1.10

Multi-hazard simulation

ERAD now supports simultaneous multi-hazard scenarios:

  • Earthquake, wind (hurricane), flood, and wildfire hazards can be combined in a single simulation.

  • Survival probability is computed as the product of independent per-hazard survival probabilities.

  • HazardScenarioGenerator produces Monte Carlo damage scenarios accounting for all active hazards.

Hydrological hazard parameters

Extended flood model with additional hydrological parameters:

  • soil_saturation — Ratio of soil moisture content.

  • snow_water_equivalent — Depth of water if all snow were melted.

  • runoff_volume — Surface runoff flow rate.

  • groundwater_flow — Subsurface groundwater flow rate.

Default fragility curves

Comprehensive set of default fragility curves for all 14 asset types across 6 hazard parameters:

  • Wind speed, flood depth, flood velocity, fire boundary distance, PGA, and PGV.

  • Based on published literature (EPRI, PNNL, IEEE Transactions on Power Systems).

  • Users can override with custom HazardFragilityCurves definitions.

from erad.default_fragility_curves import DEFAULT_FRAGILTY_CURVES

print(f"Default fragility curve sets: {len(DEFAULT_FRAGILTY_CURVES)}")
for fc in DEFAULT_FRAGILTY_CURVES:
    print(f"  - {fc.asset_state_param}: {len(fc.curves)} asset type curves")