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-eradfor 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_ratewhere 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_gustshows example configuration.
Performance optimization for wind gust calculations¶
Pre-computed UTM projections, polygon buffers, and characteristic radii are cached as a
@cached_propertyonGustModelArea.Per-asset computation only requires a lightweight
pyproj.Transformerpoint 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 theengineparameter ('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.0andpyarrowas 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_documentationtool 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.
HazardScenarioGeneratorproduces 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
HazardFragilityCurvesdefinitions.
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")