Embedding Neural Networks into Dynamic Power System Simulators
This repository contains the implementation and workflow for integrating Physics-Informed Neural Networks (PINNs) into the RAMSES power system simulator using the roseNNa inference library.
RamsesNN enables the use of neural networks trained in PyTorch to replace or augment traditional power system component models in RAMSES simulations. The workflow converts PyTorch models to ONNX format, processes them with roseNNa, and integrates them into RAMSES through custom Fortran injector models.
- Fast Inference: Uses roseNNa's optimized Fortran implementation (2-5x faster than PyTorch for small networks)
- Minimal Intrusion: Seamlessly integrates into existing RAMSES workflows
- Flexible Models: Supports both full and reduced-order power system models
- ONNX Standard: Universal format compatible with PyTorch, TensorFlow, and Keras
- roseNNa - Fast neural network inference library for Fortran/C
- URAMSES - RAMSES power system simulator distribution
Train your neural network in PyTorch and export it to ONNX format:
import torch
# Train your model
model = YourNeuralNetwork()
# ... training code ...
# Export to ONNX
init_cond = torch.randn(1, 10) # Example input matching your network's input shape
torch.onnx.export(model, init_cond, "NN.onnx")Navigate to the fLibrary/ directory in your roseNNa installation and follow these steps:
Install required dependencies:
# Create conda environment for ONNX parsing
conda create -n roparse python=3.10 -y
conda activate roparse
# Install dependencies
pip install "numpy<2" onnx==1.15.0 onnxruntime==1.16.3
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install fyppONNX models need shape inference for proper parsing. Create and run a helper script:
PowerShell (Windows):
$code = @"
import onnx, onnx.shape_inference
m = onnx.load(r'.\NN.onnx')
m_inf = onnx.shape_inference.infer_shapes(m)
onnx.save(m_inf, r'.\NN_inferred.onnx')
print('Wrote NN_inferred.onnx')
"@
Set-Content -Encoding ASCII infer_shapes.py $code
python .\infer_shapes.pyBash (Linux/Mac):
cat > infer_shapes.py << 'EOF'
import onnx, onnx.shape_inference
m = onnx.load('./NN.onnx')
m_inf = onnx.shape_inference.infer_shapes(m)
onnx.save(m_inf, './NN_inferred.onnx')
print('Wrote NN_inferred.onnx')
EOF
python infer_shapes.py# Clean previous files
rm -f onnxModel.txt onnxWeights.txt variables.fpp modelCreator.f90
# Parse ONNX model
python modelParserONNX.py -f "./NN.onnx" -w "./NN.onnx" -i "./NN_inferred.onnx"
# Verify output files were created
ls onnxModel.txt onnxWeights.txt variables.fpp
# Generate Fortran code via fypp preprocessor
python -m fypp ./modelCreator.fpp ./modelCreator.f90
# Fix C++ style namespace syntax for Fortran
sed -i 's/onnx:://g' modelCreator.f90 # Linux/Mac
# OR for Windows PowerShell:
# (Get-Content .\modelCreator.f90) -replace 'onnx::','' | Set-Content .\modelCreator.f90make libraryThis generates libcorelib.a, the core roseNNa library file.
Add these files from roseNNa's fLibrary/ directory to your RAMSES project (URAMSES):
activation_funcs.f90derived_types.f90layers.f90reader.f90rosenna.f90modelCreator.f90(generated for your specific model)
Place these generated files in your RAMSES executable directory:
onnxModel.txt- Model structureonnxWeights.txt- Model weights
Note: If using multiple neural networks simultaneously, you'll need to modify the reader logic in rosenna.f90 to handle multiple model files.
The inj_norton.f90 model demonstrates a complete integration. Key components:
module inj_norton_mod
use iso_fortran_env, only: real64, int32
use MODELING
use rosenna ! Neural network library
implicit none
! Neural network I/O arrays
real(real64), allocatable :: pinn_input(:,:) ! (1, n_in)
real(real64), allocatable :: pinn_output(:,:) ! (1, n_out)case (initialize)
! Define model dimensions (full or reduced)
if (prm(5) < 1.5) then
n_nn_input = 10
n_nn_output = 9
else
n_nn_input = 8
n_nn_output = 7
end if
! Allocate arrays
allocate(pinn_input(1, n_nn_input))
allocate(pinn_output(1, n_nn_output))
! Initialize input with steady-state values
pinn_input(1,1) = 0.01_real64 ! timestep
pinn_input(1,2) = -0.81854824_real64 ! delta
pinn_input(1,3) = omega ! omega
! ... additional states ...
! Initialize roseNNa with model name
nn_model_name = "full_frzd" ! or "redv2" for reduced model
call initialize_nnx(nn_model_name)case (evaluate_eqs)
! Update inputs based on current system state
call inf_bus_equations(vx, vy, old_ix, old_iy, Xline, Rline, &
new_v_infty, new_v_infty_ang)
pinn_input(1,8) = new_v_infty
pinn_input(1,9) = new_v_infty_ang
! Neural network forward pass
call use_model(pinn_input, pinn_output)
! Convert NN output to physical quantities
call machine_solver(pinn_output(1,1:9), machine_output(1,1:6))
! Transform to xy coordinates
call park_transform_dq_xy(machine_output(1,3), machine_output(1,4), &
pinn_output(1,1), ixnorton, iynorton)-
Add source files to your project:
- Right-click project β Add β Existing Item
- Select the 6 roseNNa
.f90files listed above
-
Configure module path:
- Project β Properties β Fortran β General β Additional Include Directories
- Add path to roseNNa's
objFiles/directory
-
Link library:
- Project β Properties β Linker β Input β Additional Dependencies
- Add path to
libcorelib.a
RamsesNN/
βββ README.md # This file
βββ URAMSES/ # RAMSES simulator files
β βββ my_models/
β βββ inj_norton.f90 # Example NN-integrated injector model
βββ [additional model files]
- Inputs: timestep, Ξ΄, Ο, e_d, e_q, Ο_d, Ο_q, V_β, ΞΈ_β, v_f
- Outputs: Ξ΄, Ο, e_d, e_q, Ο_d, Ο_q, V_β, ΞΈ_β, v_f
- Inputs: timestep, Ξ΄, Ο, e_d, e_q, V_β, ΞΈ_β, E_f
- Outputs: Ξ΄, Ο, e_d, e_q, V_β, ΞΈ_β, E_f
roseNNa currently supports:
- Multi-Layer Perceptrons (MLPs)
- Convolutional Neural Networks (CNNs)
- Recurrent Neural Networks (RNNs/LSTMs)
For small networks typical in physics applications:
- 2-5x faster than PyTorch inference
- Optimized for Fortran/C HPC environments
- Minimal memory footprint
When converting LSTMs to ONNX, you need two exports (with and without constant folding):
# Model structure (with optimization)
torch.onnx.export(model, (inp, hidden),
"model_structure.onnx",
export_params=True,
opset_version=12,
do_constant_folding=True,
input_names=['input', 'hidden_state', 'cell_state'],
output_names=['output'])
# Model weights (without optimization)
torch.onnx.export(model, (inp, hidden),
"model_weights.onnx",
export_params=True,
opset_version=12,
do_constant_folding=False,
input_names=['input', 'hidden_state', 'cell_state'],
output_names=['output'])- B. Gelford (2025). "Integrating neural networks into dynamic power system simulators", MSc Thesis, ETH Zurich.
- A. Bati, S. H. Bryngelson (2024). "RoseNNa: A performant, portable library for neural network inference with application to computational fluid dynamics". Computer Physics Communications. DOI: 10.1016/j.cpc.2023.109052
- P. Aristidou, S. Lebeau, and T. Van Cutsem (2016). "Power system dynamic simulations using a parallel two-level schur-complement decomposition". IEEE Transactions on Power Systems. doi: 10.1109/TPWRS.2015.2509023
Author: Bruno Gelfort
Web: https://sps-lab.org
Email: info@sps-lab.org
This project follows the licensing terms of its dependencies:
- roseNNa: MIT License
- RAMSES: Academic Public License
Planned additions to this repository:
- Complete PINN training pipeline documentation
- Additional example models for various power system components
- Automated testing framework
- Performance benchmarking tools
- Multi-model integration examples