Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AC/DC docs #153

Merged
merged 15 commits into from
Dec 18, 2024
29 changes: 29 additions & 0 deletions docs/definitions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
convert_newcommand_to_pair(s::AbstractString)

Converts a string of LaTeX to a pair, e.g. \\newcommand{\\RR}{\\mathbb{R}} to :RR => "\\mathbb{R}".
"""
function convert_newcommand_to_pair(s::AbstractString)
if startswith(s, "\\newcommand{\\")
parts = split(s, "}{")
@assert length(parts) == 2
cmd = replace(parts[1], "\\newcommand{\\" => "")
return Symbol(cmd) => parts[2][1:end-1]
end

return nothing
end

"""
make_macros_dict(definitions_path::AbstractString)

Create a `Documenter.HTMLWriter.MathJax3` `config` based on a LaTeX file containing `\\newcomand` definitions.
"""
function make_macros_dict(definitions_path::AbstractString)
defs_txt = read(definitions_path, String)
pairs = filter(
!isnothing,
map(convert_newcommand_to_pair ∘ strip, split(defs_txt, "\n"))
)
return Dict(pairs)
end
12 changes: 11 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
using Documenter
using OPFGenerator


include("definitions.jl")

makedocs(
modules=[OPFGenerator],
sitename = "OPFGenerator",
format = Documenter.HTML(;
mathengine = Documenter.MathJax(),
assets = ["assets/wider.css"],
mathengine = Documenter.MathJax3(Dict(
:tex => Dict(
"macros" => make_macros_dict("docs/src/assets/definitions.tex"),
"inlineMath" => [["\$","\$"], ["\\(","\\)"]],
"tags" => "ams",
),
)),
),
pages = [
"Home" => "index.md",
Expand Down
157 changes: 157 additions & 0 deletions docs/src/assets/definitions.tex
klamike marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
% Imaginary unit
\newcommand{\im}{\mathbf{j}}

% Branch admittance parameters
\newcommand{\gff}{g^{\text{ff}}}
\newcommand{\gft}{g^{\text{ft}}}
\newcommand{\gtf}{g^{\text{tf}}}
\newcommand{\gtt}{g^{\text{tt}}}
\newcommand{\bff}{b^{\text{ff}}}
\newcommand{\bft}{b^{\text{ft}}}
\newcommand{\btf}{b^{\text{tf}}}
\newcommand{\btt}{b^{\text{tt}}}

% OPF parameters
% lower/upper bounds on active/reactive generation
\newcommand{\pgmin}{\underline{\text{p}}^{\text{g}}}
\newcommand{\pgmax}{\overline{\text{p}}^{\text{g}}}
\newcommand{\qgmin}{\underline{\text{q}}^{\text{g}}}
\newcommand{\qgmax}{\overline{\text{q}}^{\text{g}}}
% Lower / upper bounds on voltage magnitude
\newcommand{\vmmin}{\underline{\text{v}}}
\newcommand{\vmmax}{\overline{\text{v}}}
klamike marked this conversation as resolved.
Show resolved Hide resolved
% Lower/upper bounds on angle deviation
\newcommand{\dvamin}{\underline{\Delta} \theta}
\newcommand{\dvamax}{\overline{\Delta} \theta}

% w variables (for SOC formulation)
% SOC variables (and bounds)
\newcommand{\wm}{\mathbf{w}}
\newcommand{\wmin}{\underline{\mathbf{w}}}
\newcommand{\wmax}{\overline{\mathbf{w}}}
\renewcommand{\wr}{\mathbf{w}^{\text{re}}}
\newcommand{\wi}{\mathbf{w}^{\text{im}}}
\newcommand{\wrmin}{\underline{\mathbf{w}}^{\text{re}}}
\newcommand{\wrmax}{\overline{\mathbf{w}}^{\text{re}}}
\newcommand{\wimin}{\underline{\mathbf{w}}^{\text{im}}}
\newcommand{\wimax}{\overline{\mathbf{w}}^{\text{im}}}

% acopf
% Power System
\newcommand{\PS}{\mathcal{P}}
% edges (lines + transformers)
\newcommand{\EDGES}{\mathcal{E}}
\newcommand{\BRANCHES}{\EDGES}
\newcommand{\LOADS}{\mathcal{L}}
\newcommand{\GENERATORS}{\mathcal{G}}
\newcommand{\NODES}{\mathcal{N}}
% complex generation
\newcommand{\SG}{S^{\text{g}}}
% active power generation
\newcommand{\PG}{\mathbf{p}^{\text{g}}}
% active power reserve
\newcommand{\PR}{\mathbf{p}^{\text{r}}}
% reactive power generation
\newcommand{\QG}{\mathbf{q}^{\text{g}}}
% complex demand
\newcommand{\SD}{S^{\text{d}}}
% active load
\newcommand{\PD}{\mathbf{p}^{\text{d}}}
% reactive load
\newcommand{\QD}{\mathbf{q}^{\text{d}}}
klamike marked this conversation as resolved.
Show resolved Hide resolved
% active shunt
\newcommand{\GS}{{g}^{\text{s}}}
% reactive shunt
\newcommand{\BS}{{b}^{\text{s}}}
% complex voltage
\newcommand{\V}{\mathbf{V}}
% voltage magnitude
\newcommand{\VM}{\mathbf{v}}
% voltage angle
\newcommand{\VA}{\boldsymbol{\theta}}
% difference in voltage angle
\newcommand{\DVA}{\Delta\boldsymbol{\theta}}
% complex flow
\newcommand{\SF}{S^{\text{f}}}
% active flow
\newcommand{\PF}{\mathbf{p}^{\text{f}}}
% reactive flow
\newcommand{\QF}{\mathbf{q}^{\text{f}}}
% active flow
\newcommand{\PT}{\mathbf{p}^{\text{t}}}
% reactive flow
\newcommand{\QT}{\mathbf{q}^{\text{t}}}
% dual multipliers
\newcommand{\LMDA}{\boldsymbol{\lambda}}
% dual multipliers
\newcommand{\MU}{\boldsymbol{\mu}}
% penalty coefficients
\newcommand{\RHO}{\boldsymbol{\rho}}


%% Dual variables
% Notation convention:
% * \lambda --> equality constraints
% * \mu --> linear inequality constraints
% * \nu --> conic (SOC) constraints
% Power balance (active/reactive)
\newcommand{\lambdaP}{\lambda^{\text{p}}}
\newcommand{\lambdaQ}{\lambda^{\text{q}}}
% Ohm's law (active/reactive, from/to)
\newcommand{\lambdaf}{{\lambda}^{\text{f}}}
\newcommand{\lambdat}{{\lambda}^{\text{t}}}
\newcommand{\lambdaPf}{{\lambda}^{\text{pf}}}
\newcommand{\lambdaPt}{{\lambda}^{\text{pt}}}
\newcommand{\lambdaQf}{{\lambda}^{\text{qf}}}
\newcommand{\lambdaQt}{{\lambda}^{\text{qt}}}

% Transmission limit (from/to)
\newcommand{\nuThermalfr}{{\nu}^{\text{f}}}
\newcommand{\nuThermalSfr}{{\nu}^{\text{sf}}}
\newcommand{\nuThermalPfr}{{\nu}^{\text{pf}}}
\newcommand{\nuThermalQfr}{{\nu}^{\text{qf}}}
\newcommand{\nuThermalto}{{\nu}^{\text{t}}}
\newcommand{\nuThermalSto}{{\nu}^{\text{st}}}
\newcommand{\nuThermalPto}{{\nu}^{\text{pt}}}
\newcommand{\nuThermalQto}{{\nu}^{\text{qt}}}

% Voltage product (conic)
\newcommand{\omegaf}{\omega^{\text{f}}}
\newcommand{\omegat}{\omega^{\text{t}}}
\newcommand{\omegar}{\omega^{\text{re}}}
\newcommand{\omegai}{\omega^{\text{im}}}
% Dispatch bounds (active/reactive)
\newcommand{\muPg}{{\mu}^{\text{pg}}}
\newcommand{\muPgMin}{\underline{\mu}^{\text{pg}}}
\newcommand{\muPgMax}{\bar{\mu}^{\text{pg}}}
\newcommand{\muQg}{{\mu}^{\text{qg}}}
\newcommand{\muQgMin}{\underline{\mu}^{\text{qg}}}
\newcommand{\muQgMax}{\bar{\mu}^{\text{qg}}}
% Power flow bounds (active/reactive, to/fr)
\newcommand{\muPf}{{\mu}^{\text{pf}}}
\newcommand{\muPfMin}{\underline{\mu}^{\text{pf}}}
\newcommand{\muPfMax}{\bar{\mu}^{\text{pf}}}
\newcommand{\muQf}{{\mu}^{\text{qf}}}
\newcommand{\muQfMin}{\underline{\mu}^{\text{qf}}}
\newcommand{\muQfMax}{\bar{\mu}^{\text{qf}}}
\newcommand{\muPt}{{\mu}^{\text{pt}}}
\newcommand{\muPtMin}{\underline{\mu}^{\text{pt}}}
\newcommand{\muPtMax}{\bar{\mu}^{\text{pt}}}
\newcommand{\muQt}{{\mu}^{\text{qt}}}
\newcommand{\muQtMin}{\underline{\mu}^{\text{qt}}}
\newcommand{\muQtMax}{\bar{\mu}^{\text{qt}}}
% Voltage magnitude bounds
\newcommand{\muWm}{{\mu}^{\text{w}}}
\newcommand{\muWmMin}{\underline{\mu}^{\text{w}}}
\newcommand{\muWmMax}{\bar{\mu}^{\text{w}}}
% Voltage product bounds
\newcommand{\muWr}{{\mu}^{\text{wr}}}
\newcommand{\muWrMin}{\underline{\mu}^{\text{wr}}}
\newcommand{\muWrMax}{\bar{\mu}^{\text{wr}}}
\newcommand{\muWi}{{\mu}^{\text{wi}}}
\newcommand{\muWiMin}{\underline{\mu}^{\text{wi}}}
\newcommand{\muWiMax}{\bar{\mu}^{\text{wi}}}
% Angle difference bounds
\newcommand{\muAngleDiff}{{\mu}^{\theta}}
\newcommand{\muAngleDiffMin}{\underline{\mu}^{\theta}}
\newcommand{\muAngleDiffMax}{\bar{\mu}^{\theta}}
3 changes: 3 additions & 0 deletions docs/src/assets/wider.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.docs-main {
max-width: 55rem !important; /* default is 52 rem */
}
148 changes: 147 additions & 1 deletion docs/src/opf/acp.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,149 @@
# AC-OPF

See [`ACPPowerModel`](https://lanl-ansi.github.io/PowerModels.jl/stable/formulation-details/#PowerModels.ACPPowerModel) formulation in [`PowerModels.jl`](https://lanl-ansi.github.io/PowerModels.jl/stable/).

## Mathematical Formulation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall comment on the model: we've encountered some issues with the sign of dual variables for constraints that are written like

@constraint(model, x + y == z)

where x, y, z are all JuMP.VariableRefs. Namely, JuMP may end up with x + y - z == 0 or z - x - y == 0 (I assume there is some consistency in how that transformation is done, but I don't know what it is). We ended up with dual variables that were negated compared to what we expected :(

I wrote down all the constraints with variables on the left and constant terms on the right to avoid that risk.
Again, no impact on the primal, but it can change the sign of dual variables, so I'd like to have as clear conventions as possible.

The ACOPF model considered in OPFGenerator is presented below.

```math
\begin{align}
\min_{\PG, \QG, \PF, \QF, \PT, \QT, \VM, \VA} \quad
& \sum_{i \in \NODES} \sum_{j \in \GENERATORS_{i}} c_j \PG_j + c_0 \label{model:acopf:obj} \\
\text{s.t.} \quad \quad \quad
mtanneau marked this conversation as resolved.
Show resolved Hide resolved
klamike marked this conversation as resolved.
Show resolved Hide resolved
& \sum_{j\in\GENERATORS_i}\PG_j - \sum_{j\in\LOADS_i}\PD_j - \GS_i \VM_i^2 = \sum_{e \in \mathcal{E}_{i}} \PF_{e} + \sum_{e \in \mathcal{E}^R_{i}} \PT_{e}
& \forall i &\in \NODES
\label{model:acopf:kirchhoff:active} \\
klamike marked this conversation as resolved.
Show resolved Hide resolved
& \sum_{j\in\GENERATORS_i}\QG_j - \sum_{j\in\LOADS_i}\QD_j + \BS_i \VM_i^2 = \sum_{e \in \mathcal{E}_{i}} \QF_{e} + \sum_{e \in \mathcal{E}^R_{i}} \QT_{e}
& \forall i &\in \NODES
\label{model:acopf:kirchhoff:reactive} \\
& \PF_{e} = \gff_{e}\VM_i^2 + \gft_{e} \VM_i \VM_j \cos(\VA_i-\VA_j) + \bft_{e} \VM_i \VM_j \sin(\VA_i-\VA_j)
& \forall e = (i,j) &\in \EDGES
\label{model:acopf:ohm:active:from} \\
& \QF_{e} = -\bff_{e} \VM_i^2 - \bft_{e}\VM_i \VM_j \cos(\VA_i-\VA_j) + \gft_{e} \VM_i \VM_j \sin(\VA_i-\VA_j)
& \forall e = (i,j) &\in \EDGES
\label{model:acopf:ohm:reactive:from} \\
& \PT_{e} = \gtt_{e}\VM_j^2 + \gtf_{e} \VM_i \VM_j \cos(\VA_i-\VA_j) - \btf_{e} \VM_i \VM_j \sin(\VA_i-\VA_j)
& \forall e = (i,j) &\in \EDGES
\label{model:acopf:ohm:active:to} \\
& \QT_{e} = -\btt_{e} \VM_j^2 - \btf_{e}\VM_i \VM_j \cos(\VA_i-\VA_j) - \gtf_{e} \VM_i \VM_j \sin(\VA_i-\VA_j)
& \forall e = (i,j) &\in \EDGES
\label{model:acopf:ohm:reactive:to} \\
klamike marked this conversation as resolved.
Show resolved Hide resolved
& (\PF_{e})^2 + (\QF_{e})^2 \leq \overline{S_{e}}^2
& \forall e &\in \EDGES
\label{model:acopf:thrmbound:from} \\
klamike marked this conversation as resolved.
Show resolved Hide resolved
& (\PT_{e})^2 + (\QT_{e})^2 \leq \overline{S_{e}}^2
& \forall e &\in \EDGES
\label{model:acopf:thrmbound:to} \\
& \dvamin_{e} \leq \VA_i - \VA_j \leq \dvamax_{e}
& \forall e = (i,j) &\in \EDGES
\label{model:acopf:angledifference} \\
& \VA_\text{ref} = 0
\label{model:acopf:slackbus} \\
& \pgmin_{i} \leq \PG_i \leq \pgmax_{i}
& \forall i &\in \GENERATORS
\label{model:acopf:pgbound} \\
& \qgmin_{i} \leq \QG_i \leq \qgmax_{i}
& \forall i &\in \GENERATORS
\label{model:acopf:qgbound} \\
& \vmmin_{i} \leq \VM_i \leq \vmmax_{i}
& \forall i &\in \NODES
\label{model:acopf:vmbound} \\
& {-}\overline{S}_{e} \leq \PF_{e} \leq \overline{S}_{e}
& \forall e &\in \EDGES
\label{model:acopf:pfbound} \\
& {-}\overline{S}_{e} \leq \QF_{e} \leq \overline{S}_{e}
& \forall e &\in \EDGES
\label{model:acopf:qfbound} \\
& {-}\overline{S}_{e} \leq \PT_{e} \leq \overline{S}_{e}
& \forall e &\in \EDGES
\label{model:acopf:ptbound} \\
& {-}\overline{S}_{e} \leq \QT_{e} \leq \overline{S}_{e}
& \forall e &\in \EDGES
\label{model:acopf:qtbound}
\end{align}
```


### Variables

* ``\VM \in \mathbb{R}^{N}``: nodal voltage magnitude
* ``\VA \in \mathbb{R}^{N}``: nodal voltage angle
* ``\PG \in \mathbb{R}^{G}``: active power dispatch
* ``\QG \in \mathbb{R}^{G}``: reactive power dispatch
* ``\PF \in \mathbb{R}^{E}``: active power flow "from"
* ``\QF \in \mathbb{R}^{E}``: reactive power flow "from"
* ``\PT \in \mathbb{R}^{E}``: active power flow "to"
* ``\QT \in \mathbb{R}^{E}``: reactive power flow "to"

### Objective

The objective function ``\eqref{model:acopf:obj}`` minimizes the cost of active power generation.

!!! todo
OPFGenerator currently supports only linear cost functions.
Support for quadratic functions is planned for a later stage; please open an issue if
you would like to request this feature.

### Constraints

* ``\eqref{model:acopf:kirchhoff:active}-\eqref{model:acopf:kirchhoff:reactive}``:
active and reactive Kirchhoff's current law.
* ``\eqref{model:acopf:ohm:active:from}-\eqref{model:acopf:ohm:reactive:to}``:
Ohm's law expressing power flows as a function of nodal voltages.
* ``\eqref{model:acopf:thrmbound:from}-\eqref{model:acopf:thrmbound:to}``: Thermal limits
* ``\eqref{model:acopf:angledifference}``: Voltage angle deviation constraints.
* ``\eqref{model:acopf:slackbus}``: this constraint fixes the voltage angle of the
reference (slack) bus to zero.
* ``\eqref{model:acopf:pgbound}-\eqref{model:acopf:qgbound}``: Active/reactive generation limits
* ``\eqref{model:acopf:vmbound}``: Nodal voltage angle limits
* ``\eqref{model:acopf:pfbound}-\eqref{model:acopf:qtbound}``: Power flow bounds, derived from thermal limits

!!! info
Although power flow variable bounds ``\eqref{model:acopf:pfbound}-\eqref{model:acopf:qtbound}``
are redundant with thermal limits ``\eqref{model:acopf:thrmbound:from}-\eqref{model:acopf:thrmbound:to}``,
their inclusion improves the performance of interior-point solvers like Ipopt.


## Data format

### Primal solution

| Variable | Data | Size | Description
|:----------|:-----|:------|:----------------------------------|
| ``\VM`` | `vm` | ``N`` | Nodal voltage magnitude
| ``\VA`` | `va` | ``N`` | Nodal voltage angle
| ``\PG`` | `pg` | ``G`` | Active power generation
| ``\QG`` | `pg` | ``G`` | Reactive power generation
| ``\PF`` | `pf` | ``E`` | Active power flow (from)
| ``\PT`` | `pt` | ``E`` | Active power flow (to)
| ``\QF`` | `qf` | ``E`` | Reactive power flow (from)
| ``\QT`` | `qt` | ``E`` | Reactive power flow (to)

### Dual solution

| Constraint | Data | Size |
|:---------------------------------------------|:-------------|:------|
| ``\eqref{model:acopf:kirchhoff:active}`` | `kcl_p` | ``N`` |
| ``\eqref{model:acopf:kirchhoff:reactive}`` | `kcl_q` | ``N`` |
| ``\eqref{model:acopf:ohm:active:from}`` | `ohm_pf` | ``E`` |
| ``\eqref{model:acopf:ohm:reactive:from}`` | `ohm_qf` | ``E`` |
| ``\eqref{model:acopf:ohm:active:to}`` | `ohm_pt` | ``E`` |
| ``\eqref{model:acopf:ohm:reactive:to}`` | `ohm_qt` | ``E`` |
| ``\eqref{model:acopf:thrmbound:from}`` | `sm_fr` | ``E`` |
| ``\eqref{model:acopf:thrmbound:to}`` | `sm_to` | ``E`` |
| ``\eqref{model:acopf:angledifference}`` | `va_diff` | ``E`` |
| ``\eqref{model:acopf:slackbus}`` | `slack_bus` | ``N`` |
| ``\eqref{model:acopf:pgbound}`` (lower) | `pg_lb` | ``G`` |
| ``\eqref{model:acopf:pgbound}`` (upper) | `pg_ub` | ``G`` |
| ``\eqref{model:acopf:qgbound}`` (lower) | `qg_lb` | ``G`` |
| ``\eqref{model:acopf:qgbound}`` (upper) | `qg_ub` | ``G`` |
| ``\eqref{model:acopf:vmbound}`` (lower) | `vm_lb` | ``N`` |
| ``\eqref{model:acopf:vmbound}`` (upper) | `vm_ub` | ``N`` |
| ``\eqref{model:acopf:pfbound}`` (lower) | `pf_lb` | ``E`` |
| ``\eqref{model:acopf:pfbound}`` (upper) | `pf_ub` | ``E`` |
| ``\eqref{model:acopf:qfbound}`` (lower) | `qf_lb` | ``E`` |
| ``\eqref{model:acopf:qfbound}`` (upper) | `qf_ub` | ``E`` |
| ``\eqref{model:acopf:ptbound}`` (lower) | `pt_lb` | ``E`` |
| ``\eqref{model:acopf:ptbound}`` (upper) | `pt_ub` | ``E`` |
| ``\eqref{model:acopf:qtbound}`` (lower) | `qt_lb` | ``E`` |
| ``\eqref{model:acopf:qtbound}`` (upper) | `qt_ub` | ``E`` |
Loading
Loading