Skip to main content

CITADEL Grid Simulator

A pandapower-based electrical grid simulation system with DERs (Distributed Energy Resources) for cybersecurity and zero-trust policy research. The simulator acts as SCADA substations/RTUs communicating via DNP3 and Modbus protocols.

Overview

The CITADEL Grid Simulator provides realistic power grid simulation capabilities for research and testing:

  • Modern Grid Model: Dickert LV distribution network with DER support
  • Real-time Simulation: 1-second timestep power flow calculations
  • SCADA Protocols: DNP3 outstation and Modbus server interfaces
  • Time-Series Data: Realistic load, solar PV, and wind generation profiles
  • Zero-Trust Ready: Control points for policy enforcement on breakers, loads, and DERs
  • Thread-Safe: Concurrent operation with command queue and state callbacks

Installation

# Using micromamba (recommended)
micromamba env create -f environment.yml
micromamba activate grid-simulator

# Or using conda
conda env create -f environment.yml
conda activate grid-simulator

Configuration

# Copy example config
cp src/settingslocal.py.example src/settingslocal.py

# Edit configuration as needed
nano src/settingslocal.py

Running the Simulator

# Using Makefile
make run

# Or directly
cd src && python -m main

# Or using the installed command
citadel-simulator

Grid Models

The simulator provides an extensible framework for power grid modeling. The default implementation uses the Dickert LV distribution network model, but the architecture supports custom grid models.

Default Model: Dickert LV Network

The included Dickert LV model is specifically designed for DER research and serves as a reference implementation:

  • DER-focused: Designed for distribution-level DER integration studies
  • Modern architecture: Reflects current grids with high DER penetration
  • Zero-trust suitable: Smaller scale ideal for cyber/control experimentation
  • Realistic topology: Includes residential/commercial loads, solar PV, storage
  • Manageable scale: Rapid iteration for policy enforcement scenarios

Network Classes:

  • Class 1: Smallest network (default)
  • Class 2-5: Increasing size and complexity

Custom Grid Models

The simulator architecture is designed to be model-agnostic. You can implement custom grid models by:

  1. Creating a new model class in src/models/
  2. Implementing the required pandapower network structure
  3. Registering the model in the configuration

See src/models/dickert_lv.py for a reference implementation.

Reference

@inproceedings{dickert2013,
  author = {Dickert, Jörg and Domagk, Max and Schegner, Peter},
  year = {2013},
  month = {06},
  title = {Benchmark Low Voltage Distribution Networks Based on Cluster Analysis of Actual Grid Properties},
  doi = {10.1109/PTC.2013.6652250}
}

SCADA Protocols

DNP3 Outstation

Port: 20000 (configurable)

Point Mapping:

  • Analog Inputs 0-N: Bus voltages (per unit)
  • Analog Inputs 100+: Line power flows (MW)
  • Analog Inputs 200+: DER generation (MW)
  • Binary Inputs 0-N: Breaker status (open/closed)
  • Binary Outputs 0-N: Breaker controls

Features:

  • Integrity polls
  • Event reporting
  • Control operations
  • Quality flags
  • Timestamps

Modbus Server

Port: 502 (configurable)

Register Mapping:

  • Holding Registers 0-999: Bus voltages (scaled)
  • Holding Registers 1000-1999: Line flows (scaled)
  • Holding Registers 2000-2999: DER generation (scaled)
  • Coils 0-999: Breaker status
  • Coils 1000-1999: Breaker controls

Features:

  • Function codes: 1, 2, 3, 4, 5, 6, 15, 16
  • Exception handling
  • Multiple client support

SCADA Commands

The simulator supports the following control commands:

  • OPEN_BREAKER - Open a line breaker
  • CLOSE_BREAKER - Close a line breaker
  • SET_DER_SETPOINT - Adjust DER power output
  • ADJUST_LOAD - Modify load demand
  • SET_STORAGE_POWER - Control energy storage (charge/discharge)

Configuration

Environment Variables

# Simulation
SIMULATION_TIMESTEP=1.0          # seconds
NETWORK_CLASS=1                  # Dickert LV network class (1-5)
NUM_TRANSFORMERS=1               # Number of transformers (1-2)

# DNP3
DNP3_ENABLED=true
DNP3_PORT=20000
DNP3_LOCAL_ADDRESS=1
DNP3_REMOTE_ADDRESS=10

# Modbus
MODBUS_ENABLED=true
MODBUS_PORT=502
MODBUS_UNIT_ID=1

# Logging
LOG_LEVEL=INFO
LOG_FILE=grid_simulator.log

Settings File

See src/settingslocal.py.example for all configuration options.

Time-Series Data Generation

The simulator includes comprehensive time-series generators for realistic load and generation profiles.

Load Profiles

Residential:

  • Morning peak: 6-9 AM
  • Evening peak: 5-10 PM
  • Overnight minimum: 11 PM - 5 AM
  • Seasonal variations (summer AC, winter heating)
  • Weekday/weekend differences
  • Stochastic noise (15%)

Commercial:

  • Office: 8 AM - 6 PM weekdays
  • Retail: 9 AM - 9 PM daily
  • Industrial: 24/7 operation
  • Stochastic noise (10%)

Solar PV Profiles

  • Cosine irradiance curve (sunrise to sunset)
  • Seasonal sun angle variations
  • Cloud effects: clear, partly cloudy, cloudy
  • Inverter efficiency (96% default)
  • Day-to-day variability

Wind Profiles

  • Power curve: cut-in 3 m/s, rated 12 m/s, cut-out 25 m/s
  • AR(1) process for temporal correlation
  • Cubic power relationship
  • Stochastic variations

Example Usage

from timeseries import LoadProfileGenerator, SolarProfileGenerator

# Generate load profiles
load_gen = LoadProfileGenerator(random_seed=42)
loads = load_gen.generate_multiple_loads(
    num_residential=5,
    num_commercial=2,
    num_days=7,
    season='summer'
)

# Generate solar profiles
solar_gen = SolarProfileGenerator(random_seed=42)
solar = solar_gen.generate_with_variability(
    num_days=7,
    rated_capacity_kw=10.0,
    season='summer'
)

# Convert to pandapower format
load_pp = load_gen.to_pandapower_format(loads, load_indices)
solar_pp = solar_gen.to_pandapower_format(solar, sgen_indices)

Development

Makefile Commands

make help      # Show available commands
make install   # Create conda environment
make test      # Run tests
make run       # Run simulator
make clean     # Remove generated files
make lint      # Run linting
make format    # Format code

Testing

# Run all tests
make test

# Run specific test file
python -m pytest src/tests/test_basic.py -v

# Run with coverage
python -m pytest --cov=src src/tests/

SCADA Client Testing

# Test with Modbus client
mbpoll -a 1 -r 0 -c 10 localhost

# Test with DNP3 client
# (Use dnp3demo or similar DNP3 master tool)

Performance

  • Timestep: 1 second (configurable)
  • State history: 3600 steps (1 hour at 1s timestep)
  • Protocol updates: On every timestep
  • Typical CPU: <5% for Class 1 network
  • Memory: ~100MB for Class 1 network

Troubleshooting

Common Issues

Power flow doesn't converge:

  • Check load/generation balance
  • Verify network connectivity
  • Review voltage limits

DNP3 connection fails:

  • Check port 20000 is not in use
  • Verify firewall settings
  • Check DNP3 addresses match

Modbus connection fails:

  • Check port 502 is not in use (may need sudo)
  • Verify unit ID matches
  • Check register addresses

Debug Mode

# Enable debug logging
export LOG_LEVEL=DEBUG
python -m src.main

License

See LICENSE file for details.

References

Acknowledgments

This software was developed under U.S. Department of Energy award DE-CR0000049, issued by the Office of Cybersecurity, Energy Security, and Emergency Response (CESER). The prime contractor on this work was Iowa State University, and the ideas herein are influenced by conversations with them. The submitted manuscript has been created by UChicago Argonne, LLC, operator of Argonne National Laboratory. Argonne, a DOE Office of Science laboratory, is operated under Contract No. DE-AC02-06CH11357. The U.S. Government retains for itself, and others acting on its behalf, a paid-up nonexclusive, irrevocable worldwide license in said article to reproduce, prepare derivative works, distribute copies to the public, and perform publicly and display publicly, by or on behalf of the Government.

Metadata

Release files for citadel-simulator 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for citadel-simulator 1.1.0
File Size Uploaded
citadel_simulator-1.1.0.tar.gz 123.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for citadel-simulator 1.1.0
File Interpreter ABI Platform
citadel_simulator-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 265.7 kB

Release files / citadel_simulator-1.1.0.tar.gz

Download URL citadel_simulator-1.1.0.tar.gz
Size 123.0 kB
Tags Source
SHA-256 checksum
How to use checksums
35c5030d8f3c2d5af3af019c4e70f4c72bff7b5eb52d1c4fe224dd4a25878f15
BLAKE2b-256 checksum
How to use checksums
6e2c85fd0bc95ea32cc11ede546418f5f1246ee85a04869b287ffec618eae7f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / citadel_simulator-1.1.0-py3-none-any.whl

Download URL citadel_simulator-1.1.0-py3-none-any.whl
Size 142.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3783cdeef0af09d9a18c8e0e1cee6cfd43b56560c6ad0bc677ed22c6f482c88
BLAKE2b-256 checksum
How to use checksums
8ebd5867b5798dc81b50f277dc9248e16dfc4cce6f5c3de68961b8ae89bf3171
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

1.1.2

2 release files

1.1.1

2 release files

This release

1.1.0 This release

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page