Tutorial 3: solving constrained power flow with MadNLP

In this third tutorial, we look at a variant of the power flow equations, where we incorporate operational constraints on the different variables: we add bounds on the voltage magnitude, the active and the reactive power genenerations. Our goal is to identify if a solution of the power flow equations exists within these bounds (without implementing a proper PV/PQ switching routine as in matpower).

We start by importing the usual packages:

using LinearAlgebra
using SparseArrays

using NLPModels
using ExaModels

using JLD2

include("utils.jl")

We import a small instance:

DATA_DIR = joinpath(splitdir(Base.active_project())[1], "instances")
data = JLD2.load(joinpath(DATA_DIR, "case9.jld2"))["data"]
ngen = length(data.gen)
nbus = length(data.bus)
nlines = length(data.branch);

Constrained power flow

On the contrary to the Tutorial 2, we consider again the power flow equations with a batch size equal to 1. The bounds are easy to define in ExaModels, as we can pass them to the model directly when calling the function variable using the keywords lvar and uvar. We use the bounds specified in the data. As a results, the variables are initialized as follows:

core = ExaCore()

va = variable(core, nbus)
vm = variable(core, nbus; start = data.vm0, lvar = data.vmin, uvar = data.vmax)
pg = variable(core, ngen;  start=data.pg0, lvar = data.pmin, uvar = data.pmax)
qg = variable(core, ngen;  start=data.qg0, lvar = data.qmin, uvar = data.qmax)
p = variable(core, 2*nlines)
q = variable(core, 2*nlines);

As we obtain a bounded feasible set, we are not guaranteed to find a solution of the power flow constraints satisfying also the bound constraints. As a result, we relax the power flow constraints and penalize their violation in the objective using a ℓ1 penalty. If we denote by $g(x) = 0$ the original power flow equations, the relaxed model writes

\[g(x) = σ_p - σ_n \; , \; σ_p ≥ 0 \; , \; σ_n ≥ 0\]

and we define the penalization in the objective as $f(σ) = 1^⊤ σ_P + 1^⊤ σ_N$.

The variables $σ$ and the objective are defined in ExaModels as

spp = variable(core, nbus; lvar=0.0)
spn = variable(core, nbus; lvar=0.0)
sqp = variable(core, nbus; lvar=0.0)
sqn = variable(core, nbus; lvar=0.0)

obj = objective(
    core,
    spp[b.i] + spn[b.i] + sqp[b.i] + sqn[b.i] for b in data.bus
)
Objective

  min (...) + ∑_{p ∈ P} f(x,p)

  where |P| = 9

We implement the full power flow model with bounds in the following function:

function constrained_power_flow_model(
    data;
    backend = nothing,
    T = Float64,
    kwargs...
)
    ngen = length(data.gen)
    nbus = length(data.bus)
    nlines = length(data.branch)

    pv_buses = get_pv_buses(data)
    free_gen = get_free_generators(data)

    w = ExaCore(T; backend = backend)

    va = variable(w, nbus)
    vm = variable(
        w,
        nbus;
        start = data.vm0,
        lvar = data.vmin,
        uvar = data.vmax,
    )
    pg = variable(w, ngen;  start=data.pg0, lvar = data.pmin, uvar = data.pmax)
    qg = variable(w, ngen;  start=data.qg0, lvar = data.qmin, uvar = data.qmax)
    p = variable(w, 2*nlines)
    q = variable(w, 2*nlines)
    # slack variables
    spp = variable(w, nbus; lvar=0.0)
    spn = variable(w, nbus; lvar=0.0)
    sqp = variable(w, nbus; lvar=0.0)
    sqn = variable(w, nbus; lvar=0.0)

    # Fix variables to setpoint
    c1 = constraint(w, va[i] for i in data.ref_buses)
    c01 = constraint(w, vm[i] for i in pv_buses; lcon=data.vm0[pv_buses], ucon=data.vm0[pv_buses])
    c02 = constraint(w, pg[i] for i in free_gen; lcon=data.pg0[free_gen], ucon=data.pg0[free_gen])

    # Active power flow, FR
    c2 = constraint(
        w,
        p[b.f_idx] - b.c5 * vm[b.f_bus]^2 -
        b.c3 * (vm[b.f_bus] * vm[b.t_bus] * cos(va[b.f_bus] - va[b.t_bus])) -
        b.c4 * (vm[b.f_bus] * vm[b.t_bus] * sin(va[b.f_bus] - va[b.t_bus])) for
        b in data.branch
    )
    # Reactive power flow, FR
    c3 = constraint(
        w,
        q[b.f_idx] +
        b.c6 * vm[b.f_bus]^2 +
        b.c4 * (vm[b.f_bus] * vm[b.t_bus] * cos(va[b.f_bus] - va[b.t_bus])) -
        b.c3 * (vm[b.f_bus] * vm[b.t_bus] * sin(va[b.f_bus] - va[b.t_bus])) for
        b in data.branch
    )
    # Active power flow, TO
    c4 = constraint(
        w,
        p[b.t_idx] - b.c7 * vm[b.t_bus]^2 -
        b.c1 * (vm[b.t_bus] * vm[b.f_bus] * cos(va[b.t_bus] - va[b.f_bus])) -
        b.c2 * (vm[b.t_bus] * vm[b.f_bus] * sin(va[b.t_bus] - va[b.f_bus])) for
        b in data.branch
    )
    # Reactive power flow, TO
    c5 = constraint(
        w,
        q[b.t_idx] +
        b.c8 * vm[b.t_bus]^2 +
        b.c2 * (vm[b.t_bus] * vm[b.f_bus] * cos(va[b.t_bus] - va[b.f_bus])) -
        b.c1 * (vm[b.t_bus] * vm[b.f_bus] * sin(va[b.t_bus] - va[b.f_bus])) for
        b in data.branch
    )

    # Power flow constraints
    c9 = constraint(w, b.pd + b.gs * vm[b.i]^2 - spp[b.i] + spn[b.i] for b in data.bus)
    c10 = constraint(w, b.qd - b.bs * vm[b.i]^2 - sqp[b.i] + sqn[b.i] for b in data.bus)
    c11 = constraint!(w, c9, a.bus => p[a.i] for a in data.arc)
    c12 = constraint!(w, c10, a.bus => q[a.i] for a in data.arc)
    c13 = constraint!(w, c9, g.bus => -pg[g.i] for g in data.gen)
    c14 = constraint!(w, c10, g.bus => -qg[g.i] for g in data.gen)

    o = objective(
        w,
        spp[b.i] + spn[b.i] + sqp[b.i] + sqn[b.i] for b in data.bus
    )
    return ExaModel(w; kwargs...)
end
constrained_power_flow_model (generic function with 1 method)

Solution with the interior-point solver MadNLP

We generate a new model using our function constrained_power_flow_model:

nlp = constrained_power_flow_model(data)
nothing

As we have incorporated bounds on our optimization variables, the constrained power flow is not solvable using the Newton method we used in the two previous tutorials. However, it is good candidate for an interior-point method, as implemented in MadNLP.

MadNLP takes as input any model following the AbstractNLPModel abstraction, as it is the case with our model nlp. As a consequence, solving the constrained power flow equations simply amounts to call the function madnlp:

using MadNLP
results = madnlp(nlp)
nothing
This is MadNLP version v0.8.7, running with umfpack

Number of nonzeros in constraint Jacobian............:      282
Number of nonzeros in Lagrangian Hessian.............:      378

Total number of variables............................:       96
                     variables with only lower bounds:       36
                variables with lower and upper bounds:       15
                     variables with only upper bounds:        0
Total number of equality constraints.................:       60
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  3.5999964e-01 1.63e+00 0.00e+00  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  3.5999993e+00 8.40e-02 3.48e-01  -1.0 1.63e+00    -  9.65e-01 1.00e+00h  1
   2  6.8717434e-01 1.12e-03 3.39e-02  -1.7 2.19e-01    -  9.79e-01 1.00e+00h  1
   3  1.0529729e-01 2.89e-04 2.92e-02  -2.5 9.13e-02    -  8.99e-01 1.00e+00h  1
   4  1.0176107e-01 1.06e-05 1.22e-04  -2.5 2.05e-02    -  1.00e+00 1.00e+00h  1
   5  3.8859643e-04 3.74e-08 9.24e-05  -5.7 2.86e-03    -  1.00e+00 9.97e-01h  1
   6  6.6057503e-05 7.04e-10 1.12e-09  -5.7 1.59e-04    -  1.00e+00 1.00e+00h  1
   7 -3.2727273e-07 3.55e-15 9.25e-15  -9.0 1.84e-06    -  1.00e+00 1.00e+00h  1

Number of Iterations....: 7

                                   (scaled)                 (unscaled)
Objective...............:  -3.2727272524260281e-07   -3.2727272524260281e-07
Dual infeasibility......:   9.2533699276651886e-15    9.2533699276651886e-15
Constraint violation....:   3.5527136788005009e-15    3.5527136788005009e-15
Complementarity.........:   9.1358752630263181e-10    9.1358752630263181e-10
Overall NLP error.......:   9.1358752630263181e-10    9.1358752630263181e-10

Number of objective function evaluations             = 8
Number of objective gradient evaluations             = 8
Number of constraint evaluations                     = 8
Number of constraint Jacobian evaluations            = 8
Number of Lagrangian Hessian evaluations             = 7
Total wall-clock secs in solver (w/o fun. eval./lin. alg.)  =  0.001
Total wall-clock secs in linear solver                      =  0.002
Total wall-clock secs in NLP function evaluations           =  0.000
Total wall-clock secs                                       =  0.002

EXIT: Optimal Solution Found (tol = 1.0e-08).

We observe that MadNLP converges with a final objective close to 0, meaning that the power flow is feasible within the bounds. The solution returned by MadNLP is the same as those returned previously in Tutorial 1 by our custom Newton solver:

vm = results.solution[nbus+1:2*nbus]
9-element Vector{Float64}:
 0.9754721770836192
 0.987006852390764
 1.0033754364523084
 0.9856448817242128
 1.0
 0.9576210404281067
 0.996185245808383
 1.0
 1.0

Observe that this is not the case on most instances. E.g., MadNLP converges to a solution with a nonzero objective on 89pegase, meaning this instance does not have a solution of the power flow equations within bounded feasibility set.

data = JLD2.load(joinpath(DATA_DIR, "pglib_opf_case89_pegase.jld2"))["data"]
nlp = constrained_power_flow_model(data)
results = madnlp(nlp)
nothing
This is MadNLP version v0.8.7, running with umfpack

Number of nonzeros in constraint Jacobian............:     5622
Number of nonzeros in Lagrangian Hessian.............:     8578

Total number of variables............................:     1398
                     variables with only lower bounds:      356
                variables with lower and upper bounds:      113
                     variables with only upper bounds:        0
Total number of equality constraints.................:     1042
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  3.5599964e+00 1.33e+01 0.00e+00  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  7.3631612e+00 1.18e+01 1.02e+00  -1.0 1.30e+01    -  5.94e-02 1.19e-01h  1
   2  1.7804291e+01 7.69e+00 2.21e+00  -1.0 1.15e+01    -  1.54e-01 3.46e-01h  1
   3  3.4138828e+01 1.38e+00 1.56e+01  -1.0 7.48e+00    -  4.96e-01 8.20e-01h  1
   4  3.8964234e+01 4.02e-01 4.76e+00  -1.0 1.44e+00    -  9.03e-01 7.09e-01h  1
   5  4.1805753e+01 1.08e-03 2.22e-01  -1.0 4.86e-01    -  1.00e+00 1.00e+00h  1
   6  1.8088828e+01 8.02e-04 2.19e-01  -1.7 4.83e-01    -  7.04e-01 8.58e-01h  1
   7  1.4659573e+01 2.11e-03 5.22e-02  -1.7 6.97e-01    -  1.00e+00 8.65e-01h  1
   8  1.0155645e+01 8.89e-04 7.16e-02  -2.5 4.15e-01    -  8.22e-01 7.24e-01h  1
   9  8.4935420e+00 8.01e-04 1.27e-01  -2.5 2.66e-01    -  4.18e-01 1.00e+00h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  8.4218185e+00 8.39e-04 4.53e-02  -2.5 3.39e-01    -  1.00e+00 1.00e+00h  1
  11  8.4241228e+00 1.81e-06 8.72e-05  -2.5 2.96e-02    -  1.00e+00 1.00e+00h  1
  12  7.4545132e+00 6.81e-04 2.01e-02  -5.7 3.23e-01    -  6.33e-01 9.85e-01h  1
  13  7.4335829e+00 1.03e-04 4.17e-03  -5.7 2.85e-01    -  8.54e-01 1.00e+00h  1
  14  7.4326151e+00 1.58e-05 2.35e-04  -5.7 1.07e-01    -  9.65e-01 1.00e+00h  1
  15  7.4325086e+00 4.85e-06 5.23e-05  -5.7 6.94e-02    -  1.00e+00 1.00e+00h  1
  16  7.4324952e+00 7.41e-07 7.53e-06  -5.7 1.33e-02    -  1.00e+00 1.00e+00h  1
  17  7.4318388e+00 8.08e-08 1.62e-05  -8.6 2.51e-02    -  9.06e-01 1.00e+00h  1
  18  7.4318384e+00 1.31e-09 2.10e-07  -8.6 1.63e-02    -  1.00e+00 1.00e+00h  1
  19  7.4318384e+00 3.67e-10 6.61e-08  -8.6 8.63e-03    -  1.00e+00 1.00e+00h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  20  7.4318384e+00 8.53e-11 1.54e-08  -8.6 4.16e-03    -  1.00e+00 1.00e+00h  1
  21  7.4318379e+00 2.33e-11 4.23e-09  -9.0 2.18e-03    -  1.00e+00 1.00e+00h  1

Number of Iterations....: 21

                                   (scaled)                 (unscaled)
Objective...............:   7.4318378577662187e+00    7.4318378577662187e+00
Dual infeasibility......:   4.2301090221470037e-09    4.2301090221470037e-09
Constraint violation....:   2.3340544430539012e-11    2.3340544430539012e-11
Complementarity.........:   3.0228554039960585e-09    3.0228554039960585e-09
Overall NLP error.......:   4.2301090221470037e-09    4.2301090221470037e-09

Number of objective function evaluations             = 22
Number of objective gradient evaluations             = 22
Number of constraint evaluations                     = 22
Number of constraint Jacobian evaluations            = 22
Number of Lagrangian Hessian evaluations             = 21
Total wall-clock secs in solver (w/o fun. eval./lin. alg.)  =  0.029
Total wall-clock secs in linear solver                      =  0.098
Total wall-clock secs in NLP function evaluations           =  0.001
Total wall-clock secs                                       =  0.128

EXIT: Optimal Solution Found (tol = 1.0e-08).

Deporting the solution on the GPU

Like our previous Newton algorithm, MadNLP supports offloading the solution of the model on the GPU using the extension MadNLPGPU:

using CUDA
using MadNLPGPU

Once MadNLPGPU is imported, you just have to instantiate the previous model on the GPU to solve it using the same madnlp function:

nlp_gpu = constrained_power_flow_model(data; backend=CUDABackend())
results = madnlp(nlp_gpu)
nothing
This is MadNLP version v0.8.7, running with cuDSS v0.4.0

Number of nonzeros in constraint Jacobian............:     5622
Number of nonzeros in Lagrangian Hessian.............:     8578

Total number of variables............................:     1398
                     variables with only lower bounds:      356
                variables with lower and upper bounds:      113
                     variables with only upper bounds:        0
Total number of equality constraints.................:     1042
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  3.5599964e+00 1.33e+01 0.00e+00  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  3.5600088e+00 1.33e+01 9.34e-03  -1.0 1.31e+01    -  5.37e-02 3.85e-07h  1
   2  7.2635289e+00 1.19e+01 3.00e-03  -1.0 1.30e+01  -4.0 1.16e-01 1.07e-01h  1
   3  1.7864400e+01 8.00e+00 8.07e-04  -1.0 1.16e+01  -4.5 2.39e-01 3.27e-01h  1
   4  2.4966916e+01 5.26e+00 5.46e-04  -1.0 7.80e+00  -5.0 5.49e-01 3.43e-01h  1
   5  3.8668922e+01 6.08e-01 5.72e-05  -1.0 5.10e+00  -5.4 8.48e-01 8.84e-01h  1
   6  2.3160465e+01 2.40e-01 2.57e-04  -1.7 1.04e+00  -5.9 4.95e-01 6.05e-01h  1
   7  1.4609627e+01 1.09e-02 6.02e-04  -1.7 7.12e-01  -6.4 5.56e-01 9.55e-01h  1
   8  1.0668151e+01 4.94e-03 1.06e-03  -3.8 5.76e-01  -6.9 6.50e-01 5.46e-01h  1
   9  9.1490168e+00 2.46e-03 3.05e-03  -3.8 2.34e-01  -7.3 5.83e-01 5.02e-01h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  7.5095345e+00 2.22e-03 1.29e-03  -3.8 5.98e-01  -7.8 1.06e-01 1.00e+00h  1
  11  7.4919884e+00 2.45e-04 6.78e-04  -3.8 1.72e-01  -8.3 9.00e-01 1.00e+00h  1
  12  7.4357225e+00 1.11e-04 6.91e-04  -5.0 5.51e-01  -8.8 8.92e-01 1.00e+00h  1
  13  7.4353784e+00 2.35e-06 3.36e-02  -5.0 1.12e-01    -  1.00e+00 1.00e+00h  1
  14  7.4349587e+00 3.26e-05 8.01e-03  -5.0 4.89e-02    -  1.00e+00 7.62e-01h  1
  15  7.4350709e+00 9.08e-07 4.12e-06  -5.0 6.14e-03    -  1.00e+00 1.00e+00h  1

Number of Iterations....: 15

                                   (scaled)                 (unscaled)
Objective...............:   7.4350709070769021e+00    7.4350709070769021e+00
Dual infeasibility......:   4.1153108521963170e-06    4.1153108521963170e-06
Constraint violation....:   9.0800228215956274e-07    9.0800228215956274e-07
Complementarity.........:   1.4463294623273424e-06    1.4463294623273424e-06
Overall NLP error.......:   4.1153108521963170e-06    4.1153108521963170e-06

Number of objective function evaluations             = 16
Number of objective gradient evaluations             = 16
Number of constraint evaluations                     = 16
Number of constraint Jacobian evaluations            = 16
Number of Lagrangian Hessian evaluations             = 15
Total wall-clock secs in solver (w/o fun. eval./lin. alg.)  =  0.064
Total wall-clock secs in linear solver                      =  0.056
Total wall-clock secs in NLP function evaluations           =  0.010
Total wall-clock secs                                       =  0.129

EXIT: Optimal Solution Found (tol = 1.0e-04).

MadNLP detects automatically that the ExaModel instance nlp_gpu has been instantiated on the GPU. As a result the solver is able to solve the instance entirely on the GPU with the linear solver cuDSS. Note that we converge to the same objective value, but the number of iterations is different, as well as the final convergence tolerance (tol=1e-4): when solving a model on the GPU with cuDSS, MadNLP has to use a few numerical tricks that impact slightly the accuracy in the evaluation. The tolerance has to be loosened to obtain a reliable convergence on the GPU. If you find the solution not satisfactory, you can specify your own convergence tolerance by using the option tol. E.g., to solve the model with the same precision as on the CPU:

results = madnlp(nlp_gpu; tol=1e-8)
nothing
This is MadNLP version v0.8.7, running with cuDSS v0.4.0

Number of nonzeros in constraint Jacobian............:     5622
Number of nonzeros in Lagrangian Hessian.............:     8578

Total number of variables............................:     1398
                     variables with only lower bounds:      356
                variables with lower and upper bounds:      113
                     variables with only upper bounds:        0
Total number of equality constraints.................:     1042
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  3.5599964e+00 1.33e+01 0.00e+00  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  3.5600088e+00 1.33e+01 9.34e-03  -1.0 1.31e+01    -  5.37e-02 3.85e-07h  1
   2  7.2635281e+00 1.19e+01 3.00e-03  -1.0 1.30e+01  -4.0 1.16e-01 1.07e-01h  1
   3  1.7864404e+01 8.00e+00 8.07e-04  -1.0 1.16e+01  -4.5 2.39e-01 3.27e-01h  1
   4  2.4966909e+01 5.26e+00 5.46e-04  -1.0 7.80e+00  -5.0 5.49e-01 3.43e-01h  1
   5  3.8668934e+01 6.08e-01 5.72e-05  -1.0 5.10e+00  -5.4 8.48e-01 8.84e-01h  1
   6  2.3160471e+01 2.40e-01 2.57e-04  -1.7 1.04e+00  -5.9 4.95e-01 6.05e-01h  1
   7  1.4609621e+01 1.09e-02 6.02e-04  -1.7 7.12e-01  -6.4 5.56e-01 9.55e-01h  1
   8  1.0668154e+01 4.94e-03 1.06e-03  -3.8 5.76e-01  -6.9 6.50e-01 5.46e-01h  1
   9  9.1490289e+00 2.46e-03 3.05e-03  -3.8 2.34e-01  -7.3 5.83e-01 5.02e-01h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  7.5095335e+00 2.22e-03 1.29e-03  -3.8 5.98e-01  -7.8 1.06e-01 1.00e+00h  1
  11  7.4919879e+00 2.45e-04 6.78e-04  -3.8 1.72e-01  -8.3 9.00e-01 1.00e+00h  1
  12  7.4331739e+00 1.13e-04 7.68e-04  -5.7 5.53e-01  -8.8 7.44e-01 1.00e+00h  1
  13  7.4326289e+00 1.20e-05 9.52e-04  -5.7 1.15e-01  -9.2 9.54e-01 1.00e+00h  1
  14  7.4325038e+00 4.39e-06 9.83e-04  -5.7 6.49e-02    -  9.68e-01 1.00e+00h  1
  15  7.4324801e+00 6.71e-07 3.59e-06  -5.7 1.17e-02    -  1.00e+00 1.00e+00h  1
  16  7.4318178e+00 8.04e-08 1.31e-03  -8.6 2.48e-02    -  8.31e-01 1.00e+00h  1
  17  7.4318119e+00 1.18e-09 5.05e-04  -8.6 1.54e-02    -  7.22e-01 1.00e+00h  1
  18  7.4318112e+00 2.95e-10 7.12e-05  -8.6 7.74e-03    -  8.61e-01 1.00e+00h  1
  19  7.4318087e+00 1.03e-10 1.31e-05  -8.6 4.58e-03    -  8.99e-01 1.00e+00h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  20  7.4318066e+00 2.75e-11 4.96e-09  -8.6 2.36e-03    -  1.00e+00 1.00e+00h  1

Number of Iterations....: 20

                                   (scaled)                 (unscaled)
Objective...............:   7.4318065566212432e+00    7.4318065566212432e+00
Dual infeasibility......:   4.9601260947084643e-09    4.9601260947084643e-09
Constraint violation....:   2.7459548431884232e-11    2.7459548431884232e-11
Complementarity.........:   5.6637610575922063e-09    5.6637610575922063e-09
Overall NLP error.......:   5.6637610575922063e-09    5.6637610575922063e-09

Number of objective function evaluations             = 21
Number of objective gradient evaluations             = 21
Number of constraint evaluations                     = 21
Number of constraint Jacobian evaluations            = 21
Number of Lagrangian Hessian evaluations             = 20
Total wall-clock secs in solver (w/o fun. eval./lin. alg.)  =  0.088
Total wall-clock secs in linear solver                      =  0.073
Total wall-clock secs in NLP function evaluations           =  0.012
Total wall-clock secs                                       =  0.173

EXIT: Optimal Solution Found (tol = 1.0e-08).

We have now all the elements in hand to solve the full optimal power flow problem on the GPU using MadNLP.


This page was generated using Literate.jl.