Overview
Argos acts as a layer built on top of ExaPF to formulate the OPF problem in a form suitable for optimization solvers (i.e. by formulating the problems with nonlinear callbacks). Depending on whether we are working in the full- or the reduced-space, the two principal evaluators are the FullSpaceEvaluator and the ReducedSpaceEvaluator.
Abstraction
Argos formulates the OPF problem in abstract form as
\[\min_{x, u } \; f(x, u) \quad \text{subject to}\quad \left\{ \begin{aligned} & g(x, u) = 0 \\ & h_l \leq h(x, u) \leq h_u . \end{aligned} \right.\]
By design, the control variable $u$ (voltage magnitude at PV and REF nodes, active power generations) is dissociated from the state variable $x$ (voltage angle, voltage magnitudes at PQ nodes). The function $f$ encodes the objective function, the function $g$ is associated with the power flow equations](https://exanauts.github.io/ExaPF.jl/stable/lib/formulations/#ExaPF.PowerFlowBalance) and $h$ encodes the remaining operational constraints (bounds on reactive power generations, line flow constraints).
Compared to MATPOWER or PowerModels, Argos comes with two major differences in the formulation.
- Only a subset of the power flow equations is considered, leading to a total of $n_x$ instead of $2 n_{bus}$ equations.
- The reactive power generations are considered only implicitly, through the remaining power flow equations.
Hence, the problem formulated by Argos has a smaller size as the ones obtained through PowerModels or MATPOWER, at the expense of robustness.
Usage
For demonstration purposes, we instantiate a new FullSpaceEvaluator object associated with case9:
using ExaPF, Argos
datafile = joinpath(INSTANCES_DIR, "case9.m")
flp = Argos.FullSpaceEvaluator(datafile)A FullSpaceEvaluator object
* device: KernelAbstractions.CPU(false)
* #vars: 19
* #cons: 36
We get the number of variables and constraints simply as
(n, m) = Argos.n_variables(flp), Argos.n_constraints(flp)(19, 36)Network variables
Internally, each evaluator stores the current state of the network in a ExaPF.NetworkStack object. We can query the current state of the network as:
stack = flp.stack
# Query current voltage magnitude and angle values
[stack.vmag stack.vang]9×2 Matrix{Float64}:
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0The function update! updates the values in the cache:
x = rand(n)
Argos.update!(flp, x)
# The values in the cache are modified accordingly
[stack.vmag stack.vang]9×2 Matrix{Float64}:
0.848416 0.0
0.0805513 0.141918
0.59004 0.384708
0.302376 0.153779
0.457351 0.361448
0.491978 0.584548
0.298669 0.468811
0.919493 0.925342
0.529845 0.515877Every time we have a new variable x, it is important to refresh the cache by calling explicitly Argos.update!(flp, x) before calling the other callbacks.
Callbacks
Now the cache has been refreshed by calling update!, one can query the different callbacks to evaluate the objective, the constraints and the derivatives:
Objective:
obj = Argos.objective(flp, x)2557.5763249374195Gradient:
g = zeros(n)
Argos.gradient!(flp, g, x)
g19-element Vector{Float64}:
0.0
0.0
4405.061166663853
0.0
0.0
0.0
0.0
0.0
2258.096736511352
0.0
0.0
0.0
0.0
0.0
804.7874540050929
0.0
0.0
1106.9134779683984
2154.860282702878Constraints:
cons = zeros(m)
Argos.constraint!(flp, cons, x)
cons36-element Vector{Float64}:
-1.4168474784045788
-1.822086998005177
-0.4410101904562455
1.0517630620103455
1.5675708506630845
-1.1442785553765182
5.09550661584778
0.7310041848822406
-3.8352350383430722
0.9733440092619392
⋮
8.38360039543063
0.6923941358279246
0.09473624401679183
1.4922010815228346
0.35065614637926124
70.03819024784804
1.2408188908806044
2.4884211065537105
0.9149204782766154All the callbacks are written to modify the data (constraints, gradient) inplace, to avoid unneeded allocations. In addition, Argos.jl provides a version allocating automatically the return values:
g = Argos.gradient(flp, x)
c = Argos.constraint(flp, x)36-element Vector{Float64}:
-1.4168474784045788
-1.822086998005177
-0.4410101904562455
1.0517630620103455
1.5675708506630845
-1.1442785553765182
5.09550661584778
0.7310041848822406
-3.8352350383430722
0.9733440092619392
⋮
8.38360039543063
0.6923941358279246
0.09473624401679183
1.4922010815228346
0.35065614637926124
70.03819024784804
1.2408188908806044
2.4884211065537105
0.9149204782766154Eventually, one can reset the evaluator to its original state by using reset!:
Argos.reset!(flp)
[stack.vmag stack.vang]9×2 Matrix{Float64}:
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0