Argos.jl extends the power-system modeler ExaPF.jl and the interior-point solver MadNLP.jl to solve optimal power flow (OPF) problems entirely in Julia.

The package is structured as follows:

- in
`src/Evaluators/`

, various optimization evaluators implement the different callbacks (objective, gradient, Hessian) required in the optimization algorithms. - in
`src/Algorithms/`

, an Augmented Lagrangian algorithm is implemented, targeting primarily the resolution of large-scale OPF problems on GPU architectures. - in
`src/Wrappers/`

, a wrapper for MathOptInterface and a wrapper for NLPModels.jl are implemented.

One can install Argos with the default package manager:

`add Argos`

To check that everything is working as expected, please run

`test Argos`

By default, this command tests all the `Evaluators`

implemented in Argos
on the CPU and, if available, on a CUDA GPU.

The function `run_opf`

is the entry point to Argos.
It takes as input a path to a MATPOWER file and solves the associated OPF with MadNLP:

```
# Solve in the full-space
ips = Argos.run_opf("data/case9.m", Argos.FullSpace())
```

The second argument specifies the formulation used inside MadNLP to solve
the OPF problem. `FullSpace()`

implements the classical full-space formulation,
(as implemented inside MATPOWER or
PowerModels.jl). Alternatively,
one may want to solve the OPF using the reduced-space formulation of Dommel and
Tinney:

```
# Solve in the reduced-space
ips = Argos.run_opf("data/case9.m", Argos.DommelTinney())
```

Argos implements two evaluators to solve the OPF problem:
the `FullSpaceEvaluator`

implements the classical OPF formulation
in the full-space, whereas `ReducedSpaceEvaluator`

implements the
reduced-space formulation of Dommel & Tinney.

Instantiating a new evaluator from a MATPOWER file simply amounts to

```
# Reduced-space evaluator
nlp = Argos.ReducedSpaceEvaluator("case57.m")
# Full-space evaluator
flp = Argos.FullSpaceEvaluator("case57.m")
```

An initial optimization variable can be computed as

`u = Argos.initial(nlp)`

The variable `u`

is the control that will be used throughout the
optimization. Once a new point `u`

obtained, one can refresh all the structures
inside `nlp`

with:

`Argos.update!(nlp, u)`

Once the structures are refreshed, the other callbacks can be evaluated as well:

```
Argos.objective(nlp, u) # objective
Argos.gradient(nlp, u) # reduced gradient
Argos.jacobian(nlp, u) # reduced Jacobian
Argos.hessian(nlp, u) # reduced Hessian
```

Argos implements a wrapper to MathOptInterface to solve the optimal power flow problem with any nonlinear optimization solver compatible with MathOptInterface:

```
nlp = Argos.ReducedSpaceEvaluator("case57.m")
optimizer = Ipopt.Optimizer() # MOI optimizer
# Update tolerance to be above tolerance of Newton-Raphson subsolver
MOI.set(optimizer, MOI.RawOptimizerAttribute("tol"), 1e-5)
# Solve reduced space problem
solution = Argos.optimize!(optimizer, nlp)
```

Alternatively, one can use NLPModels.jl to wrap any evaluators implemented in Argos. This amounts simply to:

```
nlp = Argos.FullSpaceEvaluator("case57.m")
# Wrap in NLPModels
model = Argos.OPFModel(nlp)
x0 = NLPModels.get_x0(model)
obj = NLPModels.obj(model, x0)
```

Once the evaluator is wrapped inside NLPModels.jl, we can leverage any solver implemented in JuliaSmoothOptimizers to solve the OPF problem.

`ExaPF.jl`

is
using `KernelAbstractions`

to implement all its core operations. Hence, deporting the computation
on GPU accelerators is straightforward. Argos.jl inherits this behavior and
all evaluators can be instantiated on GPU accelerators, simply as

```
using CUDAKernels # Load CUDA backend for KernelAbstractions
using ArgosCUDA
nlp = Argos.ReducedSpaceEvaluator("case57.m"; device=CUDADevice())
```

When doing so, all kernels are instantiated on the GPU to avoid
memory transfer between the host and the device. The sparse linear
algebra operations are handled by `cuSPARSE`

, and the sparse factorizations
are performed using `cusolverRF`

via the Julia wrapper CUSOLVERRF.jl.
This package is loaded via the included `ArgosCUDA.jl`

package in `/lib`

.
When deporting the computation on the GPU, the reduced Hessian can be evaluated
in parallel.

Instead of computing the reduced Hessian one Hessian-vector product after one Hessian-vector product, the Hessian-vector products can be evaluated in batch. To activate the batch evaluation for the reduced Hessian, please specify the number of Hessian-vector products to perform in one batch as

`nlp = Argos.ReducedSpaceEvaluator("case57.m"; device=CUDADevice(), nbatch_hessian=8)`

Note that on large instances, the batch computation can be demanding in terms of GPU's memory.