Tutorial 5: solving the optimal power flow with ExaModelsPower.jl

In the previous tutorial, we have seen how to implement power flow equations and optimal power flow using ExaModels.jl. In this tutorial, we will see how to use the off-the-shelf modeling library ExaModelsPower.jl to create optimal power flow models, and how to solve them with GPUs.

What is ExaModelsPower.jl?

ExaModelsPower.jl can model OPF problems using the ExaModels package to generate models that can be solved using either CPU or GPU. This tutorial will demonstrate how ExaModelsPower.jl can be leveraged to solve different versions of the OPF, and how the user can customize the solving technique to better match their needs. Currently, all models generated by ExaModelsPower represent the full, AC version of the OPF formulation without any modifications (e.g, convex relaxations).

Getting started

ExaModelsPower.jl package can be installed via

using Pkg
Pkg.add(url="https://github.com/exanauts/ExaModelsPower.jl.git")

We will start by importing the necessary packages.

using ExaModelsPower, CUDA, MadNLPGPU

ExaModelsPower.jl provides a set of functions to create power flow models, CUDA.jl provides the GPU backend, and MadNLPGPU.jl provides the solver interface to solve the models on the GPU.

Static optimal power flow

We will begin by constructing and solving a static OPF using the function opf_model. A static OPF problem can be created by the following one-liner:

model, vars, cons = opf_model(
    "pglib_opf_case118_ieee.m";
    backend = CUDABackend(),
    form = :polar,
    T = Float64
)
(An ExaModel{Float64, CUDA.CuArray{Float64, 1, CUDA.DeviceMemory}, ...}

  Problem name: Generic
   All variables: ████████████████████ 1088   All constraints: ████████████████████ 1539  
            free: ███⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 118               free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
           lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
           upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                upper: █████⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 372   
         low/upp: ██████████████████⋅⋅ 935            low/upp: ███⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 186   
           fixed: █⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 35               fixed: █████████████⋅⋅⋅⋅⋅⋅⋅ 981   
          infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0               infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
            nnzh: ( 98.57% sparsity)   8474            linear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0     
                                                    nonlinear: ████████████████████ 1539  
                                                         nnzj: ( 99.65% sparsity)   5925  

, (va = Variable

  x ∈ R^{118}
, vm = Variable

  x ∈ R^{118}
, pg = Variable

  x ∈ R^{54}
, qg = Variable

  x ∈ R^{54}
, p = Variable

  x ∈ R^{372}
, q = Variable

  x ∈ R^{372}
), (c_ref_angle = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 1
, c_to_active_power_flow = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_to_reactive_power_flow = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_from_active_power_flow = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_from_reactive_power_flow = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_phase_angle_diff = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_active_power_balance = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 118
, c_reactive_power_balance = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 118
, c_from_thermal_limit = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
, c_to_thermal_limit = Constraint

  s.t. (...)
       g♭ ≤ [g(x,p)]_{p ∈ P} ≤ g♯

  where |P| = 186
))

For the static OPF, the only input required is the filename for the OPF matpower file. The file does not need to be locally installed, and it will be automatically downloaded from power-grid-library if the file is not found in the user's data folder. If keyword T is not specified, the numerical type will default to Float64. The backend defaults to nothing (used on CPU) and the form will default to polar coordinates. Here, we use CUDABackend() to create the problem on an NVIDIA GPU, and form = :polar to specify that the voltage angles will be represented in polar coordinates. The function returns a tuple containing the model, the variables, and the constraints.

Once the model is built, we can solve it using the madnlp function from MadNLPGPU.jl. This function will automatically detect the model type and solve it using the appropriate solver.

result = madnlp(model; tol=1e-6)
"Execution stats: Optimal Solution Found (tol = 1.0e-06)."

Once a solution has been generated, the values of any of the variables in the model can be unpacked using the vars NamedTuple

using ExaModels
solution(result, vars.vm)
118-element CUDA.CuArray{Float64, 1, CUDA.DeviceMemory}:
 1.0580716350063224
 1.0115180763228726
 1.0371931522392663
 1.0127082836507844
 1.0170507308304568
 1.0544400227643436
 1.0575145617412032
 1.030348826311262
 1.0418488543965072
 1.0519350573476918
 ⋮
 1.0245456833227162
 0.9931093445424384
 1.0451403680411238
 1.0194988393326634
 1.0074882558327414
 1.0573020470273515
 1.0514563471755558
 1.046703450188535
 1.033183859050054

Result also stores the objective value

result.objective
97213.58215815344

ExaModelsPower supports solving the OPF in either polar or rectangular coordinates. For example, to create a rectangular OPF model, we can simply change the form keyword argument to :rectangular:

model, vars, cons = opf_model(
    "pglib_opf_case118_ieee.m";
    form = :rect
)
result = madnlp(model; tol=1e-6)

# Other features
"Execution stats: Optimal Solution Found (tol = 1.0e-06)."

ExaModelsPower.jl is currently under active development. We are currently implementing the following formulations:

  • Multi-period OPF (with ramping constraints and/or storage)
  • Security-constrained OPF (with N-1 constraints)
  • Distribution system OPF (with unbalanced three-phase models)

For more information on the current status of ExaModelsPower.jl, please refer to the ExaModelsPower.jl documentation.


This page was generated using Literate.jl.