REST API#
A FastAPI-based REST API for running hazard simulations on distribution systems using the ERAD (Energy Resilience Analysis for Distribution Systems) framework.
Features#
Hazard Simulation: Run simulations to compute asset survival probabilities and export results as SQLite database files
Scenario Generation: Generate Monte Carlo scenarios with specific asset outages, exported as ZIP files with tracked changes and time series data
Distribution System Management: Upload, list, retrieve, and delete distribution system models with persistent caching
Hazard Model Management: Upload, list, retrieve, and delete hazard models with persistent caching
Persistent Cache: Models are automatically stored in a standard system cache directory
File Downloads: Simulation results and scenarios are returned as downloadable files (SQLite and ZIP)
Multiple Hazard Types: Support for earthquakes, floods, wind, and fire hazards
Configurable Fragility Curves: Use default or custom fragility curves for asset vulnerability
Cache Directory#
Models are persistently stored in platform-specific cache directories:
Distribution Systems:
Linux/macOS:
~/.cache/erad/distribution_models/Windows:
%LOCALAPPDATA%\erad\distribution_models\
Hazard Models:
Linux/macOS:
~/.cache/erad/hazard_models/Windows:
%LOCALAPPDATA%\erad\hazard_models\
Models persist across API restarts and are automatically loaded on startup.
Installation#
Install the required dependencies:
pip install -e ".[dev]"
Or install just the API dependencies:
pip install fastapi uvicorn[standard] python-multipart loguru
Quick Start#
Running the API Server#
Start the API server:
python -m erad.api
Or using uvicorn directly:
uvicorn erad.api:app --host 0.0.0.0 --port 8000 --reload
The API will be available at http://localhost:8000
API Documentation#
Interactive API documentation is available at:
Swagger UI:
http://localhost:8000/docsReDoc:
http://localhost:8000/redoc
API Endpoints#
Health & Info#
GET / - Root endpoint with basic API information
GET /health - Health check endpoint
Simulation#
Run Simulation#
POST /simulate
Run a hazard simulation and export results as a SQLite database file.
Workflow:
Loads the distribution system model from cache by name
Loads the hazard system model from cache by name
Runs the simulation using HazardSimulator
Exports results to a SQLite database
Returns the database file for download
Request body:
{
"distribution_system_name": "my_system",
"hazard_system_name": "earthquake_scenario",
"curve_set": "DEFAULT_CURVES"
}
Response: SQLite database file download
Content-Type:
application/x-sqlite3Filename:
{distribution_name}_{hazard_name}_results.sqliteFile contains: Complete simulation results including asset survival probabilities, hazard intensities, and timestamps
Generate Scenarios#
POST /generate-scenarios
Generate Monte Carlo scenarios with specific asset outages and export as a ZIP file.
Workflow:
Loads the distribution system model from cache by name
Loads the hazard system model from cache by name
Generates scenarios using HazardScenarioGenerator
Creates a DistributionSystem with tracked_changes enabled
Exports tracked changes to JSON
Packages tracked_changes.json and time series folder into a ZIP file
Returns the ZIP file for download
Request body:
{
"distribution_system_name": "my_system",
"hazard_system_name": "earthquake_scenario",
"number_of_samples": 100,
"seed": 42,
"curve_set": "DEFAULT_CURVES"
}
Response: ZIP file download
Content-Type:
application/zipFilename:
{distribution_name}_scenarios_{num_samples}samples.zipZIP contents:
tracked_changes.json- JSON file with all scenario edits and metadatatime_series/- Folder containing time series data for each component
Distribution System Management#
Upload Distribution Model#
POST /distribution-models
Upload a distribution system model as a JSON file to the persistent cache.
curl -X POST "http://localhost:8000/distribution-models" \
-F "file=@system.json" \
-F "name=my_system" \
-F "description=My test system"
Response:
{
"status": "success",
"message": "Distribution system 'my_system' uploaded successfully",
"name": "my_system",
"file_path": "/home/user/.cache/erad/distribution_models/my_system_123456.json"
}
List Distribution Models#
GET /distribution-models
Get a list of all cached distribution system models.
Query parameters:
refresh(boolean, optional): If true, refresh from cache directory before listing
# List without refresh
curl "http://localhost:8000/distribution-models"
# List with refresh from disk
curl "http://localhost:8000/distribution-models?refresh=true"
Response:
[
{
"name": "my_system",
"description": "My test system",
"created_at": "2024-01-01T00:00:00",
"file_path": "/home/user/.cache/erad/distribution_models/my_system_123456.json"
}
]
Get Distribution Model#
GET /distribution-models/{model_name}
Retrieve a specific distribution system model.
Response:
{
"name": "my_system",
"description": "My test system",
"created_at": "2024-01-01T00:00:00",
"content": {
"name": "my_system",
"components": [],
"properties": {}
}
}
Delete Distribution Model#
DELETE /distribution-models/{model_name}
Delete a distribution system model from cache.
Response:
{
"status": "success",
"message": "Distribution system 'my_system' deleted successfully"
}
Hazard Model Management#
Upload Hazard Model#
POST /hazard-models
Upload a hazard system model as a JSON file to the persistent cache.
curl -X POST "http://localhost:8000/hazard-models" \
-F "file=@hazard.json" \
-F "name=earthquake_scenario" \
-F "description=Earthquake hazard scenario"
Response:
{
"status": "success",
"message": "Hazard model 'earthquake_scenario' uploaded successfully",
"name": "earthquake_scenario",
"file_path": "/home/user/.cache/erad/hazard_models/earthquake_scenario_123456.json"
}
List Hazard Models#
GET /hazard-models
Get a list of all cached hazard models.
Query parameters:
refresh(boolean, optional): If true, refresh from cache directory before listing
Response:
[
{
"name": "earthquake_scenario",
"description": "Earthquake hazard scenario",
"created_at": "2024-01-01T00:00:00",
"file_path": "/home/user/.cache/erad/hazard_models/earthquake_scenario_123456.json"
}
]
Get Hazard Model#
GET /hazard-models/{model_name}
Retrieve a specific hazard model.
Response:
{
"name": "earthquake_scenario",
"description": "Earthquake hazard scenario",
"created_at": "2024-01-01T00:00:00",
"content": {
"models": [],
"timestamps": []
}
}
Delete Hazard Model#
DELETE /hazard-models/{model_name}
Delete a hazard model from cache.
Response:
{
"status": "success",
"message": "Hazard model 'earthquake_scenario' deleted successfully"
}
Cache Management#
Get Cache Info#
GET /cache-info
Get information about the cache directory and stored models.
Response:
{
"cache_directory": "/home/user/.cache/erad/distribution_models",
"metadata_file": "/home/user/.cache/erad/distribution_models/models_metadata.json",
"total_models": 5,
"total_files": 5,
"total_size_bytes": 1048576,
"total_size_mb": 1.0
}
Refresh Cache#
POST /refresh-cache
Refresh the model list from the cache directory. This scans for new files and updates metadata.
Response:
{
"status": "success",
"message": "Cache refreshed successfully",
"total_models": 5,
"models": ["model1", "model2", "model3", "model4", "model5"]
}
Utility Endpoints#
Get Supported Hazard Types#
GET /supported-hazard-types
Get a list of supported hazard types.
Response:
{
"hazard_types": [
"earthquake",
"flood",
"flood_area",
"wind",
"fire",
"fire_area"
],
"descriptions": {
"earthquake": "Earthquake Model (EarthQuakeModel)",
"flood": "Flood Model (FloodModel)",
"flood_area": "Flood Area Model (FloodModelArea)",
"wind": "Wind Model (WindModel)",
"fire": "Fire Model (FireModel)",
"fire_area": "Fire Area Model (FireModelArea)"
}
}
Get Default Curve Sets#
GET /default-curve-sets
Get information about available fragility curve sets.
Response:
{
"curve_sets": ["DEFAULT_CURVES"],
"default": "DEFAULT_CURVES",
"description": "Default fragility curves for various hazard types and assets"
}
Usage Examples#
Python Client Example#
import requests
# Base URL
BASE_URL = "http://localhost:8000"
# 1. Upload a distribution system
with open("my_system.json", "rb") as f:
files = {"file": ("my_system.json", f, "application/json")}
data = {"name": "my_system", "description": "Test system"}
response = requests.post(f"{BASE_URL}/distribution-models", files=files, data=data)
print(response.json())
# 2. Upload a hazard model
with open("hazard.json", "rb") as f:
files = {"file": ("hazard.json", f, "application/json")}
data = {"name": "earthquake_scenario", "description": "Earthquake scenario"}
response = requests.post(f"{BASE_URL}/hazard-models", files=files, data=data)
print(response.json())
# 3. Run simulation and download SQLite results
simulation_request = {
"distribution_system_name": "my_system",
"hazard_system_name": "earthquake_scenario",
"curve_set": "DEFAULT_CURVES"
}
response = requests.post(f"{BASE_URL}/simulate", json=simulation_request)
with open("simulation_results.sqlite", "wb") as f:
f.write(response.content)
print("Simulation results saved to simulation_results.sqlite")
# 4. Generate scenarios and download ZIP file
scenario_request = {
"distribution_system_name": "my_system",
"hazard_system_name": "earthquake_scenario",
"number_of_samples": 10,
"seed": 42,
"curve_set": "DEFAULT_CURVES"
}
response = requests.post(f"{BASE_URL}/generate-scenarios", json=scenario_request)
with open("scenarios.zip", "wb") as f:
f.write(response.content)
print("Scenarios saved to scenarios.zip")
# 5. List all models
response = requests.get(f"{BASE_URL}/distribution-models")
models = response.json()
print(f"Available distribution models: {[m['name'] for m in models]}")
response = requests.get(f"{BASE_URL}/hazard-models")
models = response.json()
print(f"Available hazard models: {[m['name'] for m in models]}")
cURL Examples#
# Health check
curl http://localhost:8000/health
# Get supported hazard types
curl http://localhost:8000/supported-hazard-types
# Upload a distribution system
curl -X POST http://localhost:8000/distribution-models \
-F "file=@system.json" \
-F "name=test_system" \
-F "description=Test system"
# Upload a hazard model
curl -X POST http://localhost:8000/hazard-models \
-F "file=@hazard.json" \
-F "name=earthquake_scenario" \
-F "description=Earthquake scenario"
# List models
curl http://localhost:8000/distribution-models
curl http://localhost:8000/hazard-models
# Run simulation and save SQLite file
curl -X POST http://localhost:8000/simulate \
-H "Content-Type: application/json" \
-d '{
"distribution_system_name": "test_system",
"hazard_system_name": "earthquake_scenario",
"curve_set": "DEFAULT_CURVES"
}' \
-o simulation_results.sqlite
# Generate scenarios and save ZIP file
curl -X POST http://localhost:8000/generate-scenarios \
-H "Content-Type: application/json" \
-d '{
"distribution_system_name": "test_system",
"hazard_system_name": "earthquake_scenario",
"number_of_samples": 5,
"seed": 42,
"curve_set": "DEFAULT_CURVES"
}' \
-o scenarios.zip
# Delete models
curl -X DELETE http://localhost:8000/distribution-models/test_system
curl -X DELETE http://localhost:8000/hazard-models/earthquake_scenario
Testing#
Run the API tests:
pytest tests/test_api.py -v
Run with coverage:
pytest tests/test_api.py --cov=erad.api --cov-report=html
Run only integration tests:
pytest tests/test_api.py -v -m integration
Development#
Running in Development Mode#
uvicorn erad.api:app --reload --log-level debug
Code Quality#
Format code:
black src/erad/api.py tests/test_api.py
Lint code:
ruff check src/erad/api.py tests/test_api.py
Supported Hazard Types#
Hazard Type |
Description |
Model Class |
|---|---|---|
|
Earthquake hazard model |
|
|
Flood hazard model |
|
|
Flood area hazard model |
|
|
Wind hazard model |
|
|
Fire hazard model |
|
|
Fire area hazard model |
|
Error Handling#
The API uses standard HTTP status codes:
200 OK - Successful request
201 Created - Resource created successfully
400 Bad Request - Invalid request data
404 Not Found - Resource not found
409 Conflict - Resource conflict (e.g., duplicate name)
422 Unprocessable Entity - Validation error
500 Internal Server Error - Server error
Error responses include a detail field with a description of the error:
{
"detail": "Distribution system 'my_system' not found",
"status_code": 404
}
Production Deployment#
For production deployment, consider:
Use a production ASGI server:
gunicorn erad.api:app -w 4 -k uvicorn.workers.UvicornWorker
Add authentication/authorization (e.g., OAuth2, API keys)
Use a proper database instead of in-memory storage
Add rate limiting to prevent abuse
Enable CORS if needed for web clients:
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], )
Set up logging and monitoring
Use environment variables for configuration
License#
See LICENSE.txt for license information.
Contributing#
See CONTRIBUTING.md for contribution guidelines.