diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 35211c5df7..a2704bf243 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -10,7 +10,6 @@ assignees: ''
**If this is a question, something isn't working or an idea please start a Q&A discussion in the [Discussion tab](https://github.com/NREL-Sienna/PowerSimulations.jl/discussions)**
Open a bug report only if you can provide the details below
-
**Describe the bug**
A clear and concise description of what the bug is.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 13c23ba449..249b1bb6e6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,7 @@
# Contributing
Community driven development of this package is encouraged. To maintain code quality standards, please adhere to the following guidelines when contributing:
- - To get started, sign the Contributor License Agreement.
- - Please do your best to adhere to the lengthy [Julia style guide](https://docs.julialang.org/en/latest/manual/style-guide/).
- - To submit code contributions, [fork](https://help.github.com/articles/fork-a-repo/) the repository, commit your changes, and [submit a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
+
+ - To get started, sign the Contributor License Agreement.
+ - Please do your best to adhere to the lengthy [Julia style guide](https://docs.julialang.org/en/latest/manual/style-guide/).
+ - To submit code contributions, [fork](https://help.github.com/articles/fork-a-repo/) the repository, commit your changes, and [submit a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
diff --git a/README.md b/README.md
index 5d53a36cb5..ab29aa9878 100644
--- a/README.md
+++ b/README.md
@@ -9,24 +9,23 @@
`PowerSimulations.jl` is a Julia package for power system modeling and simulation of Power Systems operations. The objectives of the package are:
-- Provide a flexible modeling framework that can accommodate problems of different complexity and at different time-scales.
+ - Provide a flexible modeling framework that can accommodate problems of different complexity and at different time-scales.
-- Streamline the construction of large scale optimization problems to avoid repetition of work when adding/modifying model details.
-
-- Exploit Julia's capabilities to improve computational performance of large scale power system quasi-static simulations.
+ - Streamline the construction of large scale optimization problems to avoid repetition of work when adding/modifying model details.
+ - Exploit Julia's capabilities to improve computational performance of large scale power system quasi-static simulations.
The flexible modeling framework is enabled through a modular set of capabilities that enable scalable power system analysis and exploration of new analysis methods. The modularity of PowerSimulations results from the structure of the simulations enabled by the package:
-- _Simulations_ define a set of problems that can be solved using numerical techniques.
+ - _Simulations_ define a set of problems that can be solved using numerical techniques.
For example, an annual production cost modeling simulation can be created by formulating a unit commitment model against system data to assemble a set of 365 daily time-coupled scheduling problems.
## Simulations enabled by PowerSimulations
-- Integrated Resource Planning
-- Production Cost Modeling
-- Market Simulations
-
+ - Integrated Resource Planning
+ - Production Cost Modeling
+ - Market Simulations
+
## Installation
```julia
@@ -50,4 +49,4 @@ Contributions to the development and enhancement of PowerSimulations is welcome.
## License
-PowerSimulations is released under a BSD [license](https://github.com/NREL-Sienna/PowerSimulations.jl/blob/main/LICENSE). PowerSimulations has been developed as part of the Scalable Integrated Infrastructure Planning (SIIP) initiative at the U.S. Department of Energy's National Renewable Energy Laboratory ([NREL](https://www.nrel.gov/)) Software Record SWR-23-104.
+PowerSimulations is released under a BSD [license](https://github.com/NREL-Sienna/PowerSimulations.jl/blob/main/LICENSE). PowerSimulations has been developed as part of the Scalable Integrated Infrastructure Planning (SIIP) initiative at the U.S. Department of Energy's National Renewable Energy Laboratory ([NREL](https://www.nrel.gov/)) Software Record SWR-23-104.
diff --git a/docs/Project.toml b/docs/Project.toml
index 920e4c969c..14073203c1 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -16,7 +16,6 @@ PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd"
TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e"
[compat]
-Documenter = "0.27"
+Documenter = "^1.7"
InfrastructureSystems = "2"
julia = "^1.6"
-Latexify = "=0.16.3"
diff --git a/docs/make.jl b/docs/make.jl
index ce9640a385..2c0ce87194 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -29,6 +29,7 @@ pages = OrderedDict(
"Code Base Developer Guide" => Any[
"Developer Guide" => "code_base_developer_guide/developer.md",
"Troubleshooting" => "code_base_developer_guide/troubleshooting.md",
+ "Internals" => "code_base_developer_guide/internal.md",
],
"Formulation Library" => Any[
"Introduction" => "formulation_library/Introduction.md",
@@ -47,9 +48,11 @@ pages = OrderedDict(
makedocs(;
modules = [PowerSimulations],
- format = Documenter.HTML(; prettyurls = haskey(ENV, "GITHUB_ACTIONS")),
+ format = Documenter.HTML(;
+ prettyurls = haskey(ENV, "GITHUB_ACTIONS"),
+ size_threshold = nothing),
sitename = "PowerSimulations.jl",
- authors = "Jose Daniel Lara, Daniel Thom and Clayton Barrows",
+ authors = "Jose Daniel Lara, Daniel Thom, Kate Doubleday, Rodrigo Henriquez-Auba, and Clayton Barrows",
pages = Any[p for p in pages],
)
diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md
index be88337f83..b5442eee78 100644
--- a/docs/src/api/PowerSimulations.md
+++ b/docs/src/api/PowerSimulations.md
@@ -7,35 +7,10 @@ end
# API Reference
-## Table of Contents
-
-* [Device Models](#Device-Models)
- * [Formulations](#Formulations)
- * [Problem Templates](#Problem-Templates)
-* [Decision Models](#Decision-Models)
-* [Emulation Models](#Emulation-Models)
-* [Service Models](#Service-Models)
-* [Simulation Models](#Simulation-Models)
-* [Variables](#Variables)
- * [Common Variables](#Common-Variables)
- * [Thermal Unit Variables](#Thermal-Unit-Variables)
- * [Storage Unit Variables](#Storage-Unit-Variables)
- * [Branches and Network Variables](#Branches-and-Network-Variables)
- * [Services Variables](#Services-Variables)
- * [Feedforward Variables](#Feedforward-Variables)
-* [Constraints](#Constraints)
- * [Common Constraints](#Common-Constraints)
- * [Network Constraints](#Network-Constraints)
- * [Power Variable Limit Constraints](#Power-Variable-Limit-Constraints)
- * [Services Constraints](#Services-Constraints)
- * [Thermal Unit Constraints](#Thermal-Unit-Constraints)
- * [Renewable Unit Constraints](#Renewable-Unit-Constraints)
- * [Branches Constraints](#Branches-Constraints)
- * [Feedforward Constraints](#Feedforward-Constraints)
-* [Parameters](#Parameters)
- * [Time Series Parameters](#Time-Series-Parameters)
- * [Variable Value Parameters](#Variable-Value-Parameters)
- * [Objective Function Parameters](#Objective-Function-Parameters)
+```@contents
+Pages = ["PowerSimulations.md"]
+Depth = 3
+```
```@raw html
@@ -56,24 +31,32 @@ Refer to the [Formulations Page](@ref formulation_library) for each Abstract Dev
### Problem Templates
-Refer to the [Problem Templates Page](@ref op_problem_template) for available `ProblemTemplate`s.
-
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["problem_template.jl",
+ "operation_problem_templates.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
```@raw html
```
----
+* * *
## Decision Models
-```@docs
-DecisionModel
-DecisionModel(::Type{M} where {M <: DecisionProblem}, ::ProblemTemplate, ::PSY.System, ::Union{Nothing, JuMP.Model})
-DecisionModel(::AbstractString, ::MOI.OptimizerWithAttributes)
-build!(::DecisionModel)
-solve!(::DecisionModel)
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["decision_model.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
```
```@raw html
@@ -81,7 +64,7 @@ solve!(::DecisionModel)
```
----
+* * *
## Emulation Models
@@ -91,6 +74,7 @@ EmulationModel(::Type{M} where {M <: EmulationProblem}, ::ProblemTemplate, ::PSY
EmulationModel(::AbstractString, ::MOI.OptimizerWithAttributes)
build!(::EmulationModel)
run!(::EmulationModel)
+solve!(::Int, ::EmulationModel{<:EmulationProblem}, ::Dates.DateTime, ::SimulationStore)
```
```@raw html
@@ -98,7 +82,7 @@ run!(::EmulationModel)
```
----
+* * *
## Service Models
@@ -113,13 +97,14 @@ ServiceModel
```
----
+* * *
## Simulation Models
Refer to the [Simulations Page](@ref running_a_simulation) to explanations on how to setup a Simulation, with Sequencing and Feedforwards.
```@docs
+InitialCondition
SimulationModels
SimulationSequence
Simulation
@@ -128,16 +113,37 @@ build!(::Simulation)
execute!(::Simulation)
```
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["simulation_partitions.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
+
```@raw html
```
----
+## Chronology Models
-# Variables
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["initial_condition_chronologies.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
+
+* * *
+
+## Variables
For a list of variables for each device refer to its Formulations page.
+
### Common Variables
```@docs
@@ -165,6 +171,9 @@ PowerOutput
```@docs
ReservationVariable
+EnergyVariable
+ActivePowerOutVariable
+ActivePowerInVariable
```
### Branches and Network Variables
@@ -208,7 +217,7 @@ LowerBoundFeedForwardSlack
```
----
+* * *
## Constraints
@@ -225,6 +234,7 @@ PieceWiseLinearCostConstraint
CopperPlateBalanceConstraint
NodalBalanceActiveConstraint
NodalBalanceReactiveConstraint
+AreaParticipationAssignmentConstraint
```
### Power Variable Limit Constraints
@@ -289,7 +299,7 @@ FeedforwardLowerBoundConstraint
```
----
+* * *
## Parameters
@@ -315,3 +325,53 @@ FixValueParameter
```@docs
CostFunctionParameter
```
+
+## Results
+
+### Acessing Optimization Model
+
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["optimization_container.jl",
+ "optimization_debugging.jl"
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
+
+### Accessing Problem Results
+
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["operation/problem_results.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
+
+### Accessing Simulation Results
+
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["simulation_results.jl",
+ "simulation_problem_results.jl",
+ "simulation_partition_results.jl",
+ "hdf_simulation_store.jl"
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
+
+## Simulation Recorder
+
+```@autodocs
+Modules = [PowerSimulations]
+Pages = ["utils/recorder_events.jl",
+ ]
+Order = [:type, :function]
+Public = true
+Private = false
+```
diff --git a/docs/src/code_base_developer_guide/developer.md b/docs/src/code_base_developer_guide/developer.md
index b1a859819c..a2687c0233 100644
--- a/docs/src/code_base_developer_guide/developer.md
+++ b/docs/src/code_base_developer_guide/developer.md
@@ -4,8 +4,8 @@ In order to contribute to `PowerSystems.jl` repository please read the following
[`InfrastructureSystems.jl`](https://github.com/NREL-Sienna/InfrastructureSystems.jl)
documentation in detail:
-1. [Style Guide](https://nrel-Sienna.github.io/InfrastructureSystems.jl/stable/style/)
-2. [Contributing Guidelines](https://github.com/NREL-Sienna/PowerSystems.jl/blob/main/CONTRIBUTING.md)
+ 1. [Style Guide](https://nrel-Sienna.github.io/InfrastructureSystems.jl/stable/style/)
+ 2. [Contributing Guidelines](https://github.com/NREL-Sienna/PowerSystems.jl/blob/main/CONTRIBUTING.md)
Pull requests are always welcome to fix bugs or add additional modeling capabilities.
diff --git a/docs/src/code_base_developer_guide/internal.md b/docs/src/code_base_developer_guide/internal.md
new file mode 100644
index 0000000000..3167839977
--- /dev/null
+++ b/docs/src/code_base_developer_guide/internal.md
@@ -0,0 +1,10 @@
+```@meta
+CollapsedDocStrings = true
+```
+
+# Internal API
+
+```@autodocs
+Modules = [PowerSimulations]
+Public = false
+```
diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md
index 81c1dd6b89..5c5def8d3e 100644
--- a/docs/src/formulation_library/Branch.md
+++ b/docs/src/formulation_library/Branch.md
@@ -1,18 +1,19 @@
# `PowerSystems.Branch` Formulations
!!! note
+
The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created.
### Table of contents
-1. [`StaticBranch`](#StaticBranch)
-2. [`StaticBranchBounds`](#StaticBranchBounds)
-3. [`StaticBranchUnbounded`](#StaticBranchUnbounded)
-4. [`HVDCTwoTerminalUnbounded`](#HVDCTwoTerminalUnbounded)
-5. [`HVDCTwoTerminalLossless`](#HVDCTwoTerminalLossless)
-6. [`HVDCTwoTerminalDispatch`](#HVDCTwoTerminalDispatch)
-7. [`PhaseAngleControl`](#PhaseAngleControl)
-8. [Valid configurations](#Valid-configurations)
+ 1. [`StaticBranch`](#StaticBranch)
+ 2. [`StaticBranchBounds`](#StaticBranchBounds)
+ 3. [`StaticBranchUnbounded`](#StaticBranchUnbounded)
+ 4. [`HVDCTwoTerminalUnbounded`](#HVDCTwoTerminalUnbounded)
+ 5. [`HVDCTwoTerminalLossless`](#HVDCTwoTerminalLossless)
+ 6. [`HVDCTwoTerminalDispatch`](#HVDCTwoTerminalDispatch)
+ 7. [`PhaseAngleControl`](#PhaseAngleControl)
+ 8. [Valid configurations](#Valid-configurations)
## `StaticBranch`
@@ -24,22 +25,26 @@ StaticBranch
**Variables:**
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``(-\infty,\infty)``
- - Symbol: ``f``
-If Slack variables are enabled:
-- [`FlowActivePowerSlackUpperBound`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: 2e5
- - Symbol: ``f^\text{sl,up}``
-- [`FlowActivePowerSlackLowerBound`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: 2e5
- - Symbol: ``f^\text{sl,lo}``
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``(-\infty,\infty)``
+ + Symbol: ``f``
+ If Slack variables are enabled:
+
+ - [`FlowActivePowerSlackUpperBound`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 2e5
+ + Symbol: ``f^\text{sl,up}``
+ - [`FlowActivePowerSlackLowerBound`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 2e5
+ + Symbol: ``f^\text{sl,lo}``
**Static Parameters**
-- ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
+ - ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
**Objective:**
@@ -51,7 +56,7 @@ No expressions are used.
**Constraints:**
-For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
+For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
```math
\begin{aligned}
@@ -60,9 +65,10 @@ For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the cons
& f_t + f_t^\text{sl,lo} \ge -R^\text{max},\quad \forall t \in \{1,\dots, T\}
\end{aligned}
```
-on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
----
+on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
+
+* * *
## `StaticBranchBounds`
@@ -74,13 +80,14 @@ StaticBranchBounds
**Variables:**
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``\left[-R^\text{max},R^\text{max}\right]``
- - Symbol: ``f``
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``\left[-R^\text{max},R^\text{max}\right]``
+ + Symbol: ``f``
**Static Parameters**
-- ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
+ - ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
**Objective:**
@@ -92,16 +99,17 @@ No expressions are used.
**Constraints:**
-For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
+For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
```math
\begin{aligned}
& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\}
\end{aligned}
```
-on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
----
+on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
+
+* * *
## `StaticBranchUnbounded`
@@ -111,10 +119,10 @@ Formulation valid for `PTDFPowerModel` Network model
StaticBranchUnbounded
```
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``(-\infty,\infty)``
- - Symbol: ``f``
-
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``(-\infty,\infty)``
+ + Symbol: ``f``
**Objective:**
@@ -126,16 +134,17 @@ No expressions are used.
**Constraints:**
-For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
+For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
```math
\begin{aligned}
& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\}
\end{aligned}
```
-on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
----
+on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
+
+* * *
## `HVDCTwoTerminalUnbounded`
@@ -149,10 +158,10 @@ This model assumes that it can transfer power from two AC buses without losses a
**Variables:**
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``\left(-\infty,\infty\right)``
- - Symbol: ``f``
-
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``\left(-\infty,\infty\right)``
+ + Symbol: ``f``
**Objective:**
@@ -166,8 +175,7 @@ The variable `FlowActivePowerVariable` ``f`` is added to the nodal balance expre
No constraints are added.
-
----
+* * *
## `HVDCTwoTerminalLossless`
@@ -181,17 +189,17 @@ This model assumes that it can transfer power from two AC buses without losses.
**Variables:**
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``\left(-\infty,\infty\right)``
- - Symbol: ``f``
-
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``\left(-\infty,\infty\right)``
+ + Symbol: ``f``
**Static Parameters**
-- ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min`
-- ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max`
-- ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min`
-- ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max`
+ - ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min`
+ - ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max`
+ - ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min`
+ - ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max`
**Objective:**
@@ -208,7 +216,9 @@ The variable `FlowActivePowerVariable` ``f`` is added to the nodal balance expre
& R^\text{min} \le f_t \le R^\text{max},\quad \forall t \in \{1,\dots, T\} \\
\end{align*}
```
+
where:
+
```math
\begin{align*}
& R^\text{min} = \begin{cases}
@@ -219,7 +229,9 @@ where:
\end{cases}
\end{align*}
```
+
and
+
```math
\begin{align*}
& R^\text{max} = \begin{cases}
@@ -231,10 +243,9 @@ and
\end{align*}
```
----
+* * *
-
-## `HVDCTwoTerminalDispatch`
+## `HVDCTwoTerminalDispatch`
Formulation valid for `PTDFPowerModel` Network model
@@ -244,24 +255,29 @@ HVDCTwoTerminalDispatch
**Variables**
-- [`FlowActivePowerToFromVariable`](@ref):
- - Symbol: ``f^\text{to-from}``
-- [`FlowActivePowerFromToVariable`](@ref):
- - Symbol: ``f^\text{from-to}``
-- [`HVDCLosses`](@ref):
- - Symbol: ``\ell``
-- [`HVDCFlowDirectionVariable`](@ref)
- - Bounds: ``\{0,1\}``
- - Symbol: ``u^\text{dir}``
+ - [`FlowActivePowerToFromVariable`](@ref):
+
+ + Symbol: ``f^\text{to-from}``
+
+ - [`FlowActivePowerFromToVariable`](@ref):
+
+ + Symbol: ``f^\text{from-to}``
+ - [`HVDCLosses`](@ref):
+
+ + Symbol: ``\ell``
+ - [`HVDCFlowDirectionVariable`](@ref)
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u^\text{dir}``
**Static Parameters**
-- ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min`
-- ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max`
-- ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min`
-- ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max`
-- ``L_0`` = `PowerSystems.get_loss(branch).l0`
-- ``L_1`` = `PowerSystems.get_loss(branch).l1`
+ - ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min`
+ - ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max`
+ - ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min`
+ - ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max`
+ - ``L_0`` = `PowerSystems.get_loss(branch).l0`
+ - ``L_1`` = `PowerSystems.get_loss(branch).l1`
**Objective:**
@@ -271,7 +287,7 @@ No cost is added to the objective function.
Each `FlowActivePowerToFromVariable` ``f^\text{to-from}`` and `FlowActivePowerFromToVariable` ``f^\text{from-to}`` is added to the nodal balance expression `ActivePowerBalance`, by adding the respective flow in the receiving bus and subtracting it from the sending bus. That is, ``f^\text{to-from}`` adds the flow to the `from` bus, and subtracts the flow from the `to` bus, while ``f^\text{from-to}`` adds the flow to the `to` bus, and subtracts the flow from the `from` bus This is used then to compute the AC flows using the PTDF equation.
-In addition, the `HVDCLosses` are subtracted to the `from` bus in the `ActivePowerBalance` expression.
+In addition, the `HVDCLosses` are subtracted to the `from` bus in the `ActivePowerBalance` expression.
**Constraints:**
@@ -288,7 +304,7 @@ In addition, the `HVDCLosses` are subtracted to the `from` bus in the `ActivePow
\end{align*}
```
----
+* * *
## `PhaseAngleControl`
@@ -300,18 +316,21 @@ PhaseAngleControl
**Variables:**
-- [`FlowActivePowerVariable`](@ref):
- - Bounds: ``(-\infty,\infty)``
- - Symbol: ``f``
-- [`PhaseShifterAngle`](@ref):
- - Symbol: ``\theta^\text{shift}``
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``(-\infty,\infty)``
+ + Symbol: ``f``
+
+ - [`PhaseShifterAngle`](@ref):
+
+ + Symbol: ``\theta^\text{shift}``
**Static Parameters**
-- ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
-- ``\Theta^\text{min}`` = `PowerSystems.get_phase_angle_limits(branch).min`
-- ``\Theta^\text{max}`` = `PowerSystems.get_phase_angle_limits(branch).max`
-- ``X`` = `PowerSystems.get_x(branch)` (series reactance)
+ - ``R^\text{max}`` = `PowerSystems.get_rating(branch)`
+ - ``\Theta^\text{min}`` = `PowerSystems.get_phase_angle_limits(branch).min`
+ - ``\Theta^\text{max}`` = `PowerSystems.get_phase_angle_limits(branch).max`
+ - ``X`` = `PowerSystems.get_x(branch)` (series reactance)
**Objective:**
@@ -323,7 +342,7 @@ Adds to the `ActivePowerBalance` expression the term ``-\theta^\text{shift} /X``
**Constraints:**
-For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
+For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by:
```math
\begin{aligned}
@@ -331,10 +350,10 @@ For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the cons
& -R^\text{max} \le f_t \le R^\text{max},\quad \forall t \in \{1,\dots, T\}
\end{aligned}
```
-on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
+on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``.
----
+* * *
## Valid configurations
@@ -348,9 +367,13 @@ using Latexify
combos = PowerSimulations.generate_device_formulation_combinations()
filter!(x -> (x["device_type"] <: Branch) && (x["device_type"] != TModelHVDCLine), combos)
combo_table = DataFrame(
- "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
- "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos],
+ "Valid DeviceModel" =>
+ ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
+ "Device Type" => [
+ "[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)"
+ for c in combos
+ ],
"Formulation" => ["[$(c["formulation"])](@ref)" for c in combos],
- )
-mdtable(combo_table, latex = false)
-```
\ No newline at end of file
+)
+mdtable(combo_table; latex = false)
+```
diff --git a/docs/src/formulation_library/DCModels.md b/docs/src/formulation_library/DCModels.md
new file mode 100644
index 0000000000..edcfb37ef8
--- /dev/null
+++ b/docs/src/formulation_library/DCModels.md
@@ -0,0 +1,77 @@
+# DC Models formulations
+
+!!! note
+
+ Multi-terminal DC models are still in early stages of development and future versions will add a more comprehensive list of formulations
+
+* * *
+
+## LossLessLine
+
+`LossLessLine` models are used with `PSY.DCBranch` models.
+
+```@docs
+LossLessLine
+```
+
+**Variables:**
+
+ - [`FlowActivePowerVariable`](@ref):
+
+ + Bounds: ``(R^\text{min},R^\text{max})``
+ + Symbol: ``f``
+
+**Static Parameters**
+
+ - ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min`
+ - ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max`
+ - ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min`
+ - ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max`
+
+Then, the minimum and maximum are computed as `R^\text{min} = \min(R^\text{from,min}, R^\text{to,min})` and `R^\text{max} = \min(R^\text{from,max}, R^\text{to,max})`
+
+**Objective:**
+
+No cost is added to the objective function.
+
+**Expressions:**
+
+The variable `FlowActivePowerVariable` ``f`` is added to the nodal balance expression `ActivePowerBalance` for DC Buses, by adding the flow ``f`` in the receiving DC bus and subtracting it from the sending DC bus.
+
+**Constraints:**
+
+No constraints are added to the function.
+
+* * *
+
+## LossLessConverter
+
+Converters are used to interface the AC Buses with DC Buses.
+
+```@docs
+LossLessConverter
+```
+
+**Variables:**
+
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: ``(P^\text{min},P^\text{max})``
+ + Symbol: ``p``
+
+**Static Parameters:**
+
+ - ``P^\text{min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{max}`` = `PowerSystems.get_active_power_limits(device).max`
+
+**Objective:**
+
+No cost is added to the objective function.
+
+**Expressions:**
+
+The variable `ActivePowerVariable` ``p`` is added positive to the AC balance expression `ActivePowerBalance` for AC Buses, and added negative to `ActivePowerBalance` for DC Buses, balancing both sides.
+
+**Constraints:**
+
+No constraints are added to the function.
diff --git a/docs/src/formulation_library/Feedforward.md b/docs/src/formulation_library/Feedforward.md
index bdda721f36..fc16c0fe2d 100644
--- a/docs/src/formulation_library/Feedforward.md
+++ b/docs/src/formulation_library/Feedforward.md
@@ -6,12 +6,12 @@ The creation of a FeedForward requires at least specifying the `component_type`
### Table of contents
-1. [`SemiContinuousFeedforward`](#SemiContinuousFeedForward)
-2. [`FixValueFeedforward`](#FixValueFeedforward)
-3. [`UpperBoundFeedforward`](#UpperBoundFeedforward)
-4. [`LowerBoundFeedforward`](#LowerBoundFeedforward)
+ 1. [`SemiContinuousFeedforward`](#SemiContinuousFeedForward)
+ 2. [`FixValueFeedforward`](#FixValueFeedforward)
+ 3. [`UpperBoundFeedforward`](#UpperBoundFeedforward)
+ 4. [`LowerBoundFeedforward`](#LowerBoundFeedforward)
----
+* * *
## `SemiContinuousFeedforward`
@@ -25,7 +25,7 @@ No variables are created
**Parameters:**
-- ``\text{on}^\text{th}`` = `OnStatusParameter` obtained from the source variable, typically the commitment variable of the unit commitment problem ``u^\text{th}``.
+ - ``\text{on}^\text{th}`` = `OnStatusParameter` obtained from the source variable, typically the commitment variable of the unit commitment problem ``u^\text{th}``.
**Objective:**
@@ -48,7 +48,7 @@ Limits the `ActivePowerRangeExpressionUB` and `ActivePowerRangeExpressionLB` by
Thus, if the commitment parameter is zero, the dispatch is limited to zero, forcing to turn off the generator without introducing binary variables in the economic dispatch problem.
----
+* * *
## `FixValueFeedforward`
@@ -82,7 +82,7 @@ Set the `VariableType` from the `affected_values` to be equal to the source para
\end{align*}
```
----
+* * *
## `UpperBoundFeedforward`
@@ -93,11 +93,12 @@ UpperBoundFeedforward
**Variables:**
If slack variables are enabled:
-- [`UpperBoundFeedForwardSlack`](@ref)
- - Bounds: [0.0, ]
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{ff,ubsl}``
+ - [`UpperBoundFeedForwardSlack`](@ref)
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{ff,ubsl}``
**Parameters:**
@@ -121,7 +122,7 @@ Set the `VariableType` from the `affected_values` to be lower than the source pa
\end{align*}
```
----
+* * *
## `LowerBoundFeedforward`
@@ -132,11 +133,12 @@ LowerBoundFeedforward
**Variables:**
If slack variables are enabled:
-- [`LowerBoundFeedForwardSlack`](@ref)
- - Bounds: [0.0, ]
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{ff,lbsl}``
+ - [`LowerBoundFeedForwardSlack`](@ref)
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{ff,lbsl}``
**Parameters:**
@@ -158,4 +160,4 @@ Set the `VariableType` from the `affected_values` to be greater than the source
\begin{align*}
& \text{AffectedVariable}_t + p_t^\text{ff,lbsl} \ge \text{SourceVariableParameter}_t, \quad \forall t \in \{1,\dots, T\}
\end{align*}
-```
\ No newline at end of file
+```
diff --git a/docs/src/formulation_library/General.md b/docs/src/formulation_library/General.md
index ceb5e1b9da..cc62870440 100644
--- a/docs/src/formulation_library/General.md
+++ b/docs/src/formulation_library/General.md
@@ -14,12 +14,15 @@ No variables are created for `DeviceModel(<:DeviceType, FixedOutput)`
**Static Parameters:**
-- ThermalGen:
- - ``P^\text{th,max}`` = `PowerSystems.get_max_active_power(device)`
- - ``Q^\text{th,max}`` = `PowerSystems.get_max_reactive_power(device)`
-- Storage:
- - ``P^\text{st,max}`` = `PowerSystems.get_max_active_power(device)`
- - ``Q^\text{st,max}`` = `PowerSystems.get_max_reactive_power(device)`
+ - ThermalGen:
+
+ + ``P^\text{th,max}`` = `PowerSystems.get_max_active_power(device)`
+ + ``Q^\text{th,max}`` = `PowerSystems.get_max_reactive_power(device)`
+
+ - Storage:
+
+ + ``P^\text{st,max}`` = `PowerSystems.get_max_active_power(device)`
+ + ``Q^\text{st,max}`` = `PowerSystems.get_max_reactive_power(device)`
**Time Series Parameters:**
@@ -35,11 +38,11 @@ for t in [RenewableGen, ThermalGen, HydroGen, ElectricLoad]
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
+ )
insertcols!(combo_table, 1, "Device Type" => fill(string(t), length(combos)))
push!(combo_tables, combo_table)
end
-mdtable(vcat(combo_tables...), latex = false)
+mdtable(vcat(combo_tables...); latex = false)
```
**Objective:**
@@ -54,7 +57,7 @@ Adds the active and reactive parameters listed for specific device types above t
No constraints are created for `DeviceModel(<:DeviceType, FixedOutput)`
----
+* * *
## `FunctionData` Options
@@ -82,12 +85,15 @@ PowerSimulations can represent variable costs using a variety of different metho
where
-- For `QuadraticFunctionData`:
- - ``C_0`` = `get_constant_term(variable_cost)`
- - ``C_1`` = `get_proportional_term(variable_cost)`
- - ``C_2`` = `get_quadratic_term(variable_cost)`
-- For `PolynomialFunctionData`:
- - ``C_n`` = `get_coefficients(variable_cost)[n]`
+ - For `QuadraticFunctionData`:
+
+ + ``C_0`` = `get_constant_term(variable_cost)`
+ + ``C_1`` = `get_proportional_term(variable_cost)`
+ + ``C_2`` = `get_quadratic_term(variable_cost)`
+
+ - For `PolynomialFunctionData`:
+
+ + ``C_n`` = `get_coefficients(variable_cost)[n]`
### `` and `PiecewiseLinearSlopeData`
@@ -101,10 +107,10 @@ where
where
-- For `variable_cost::PiecewiseLinearData`, ``f(x)`` is the piecewise linear function obtained by connecting the `(x, y)` points `get_points(variable_cost)` in order.
-- For `variable_cost = PiecewiseLinearSlopeData([x0, x1, x2, ...], y0, [s0, s1, s2, ...])`, ``f(x)`` is the piecewise linear function obtained by starting at `(x0, y0)`, drawing a segment at slope `s0` to `x=x1`, drawing a segment at slope `s1` to `x=x2`, etc.
+ - For `variable_cost::PiecewiseLinearData`, ``f(x)`` is the piecewise linear function obtained by connecting the `(x, y)` points `get_points(variable_cost)` in order.
+ - For `variable_cost = PiecewiseLinearSlopeData([x0, x1, x2, ...], y0, [s0, s1, s2, ...])`, ``f(x)`` is the piecewise linear function obtained by starting at `(x0, y0)`, drawing a segment at slope `s0` to `x=x1`, drawing a segment at slope `s1` to `x=x2`, etc.
----
+* * *
## `StorageCost`
@@ -120,18 +126,17 @@ Adds an objective function cost term according to:
The following table describes all possible configurations of the `StorageCost` with the target constraint in hydro or storage device models. Cases 1(a) & 2(a) will not impact the model's operations, and the target constraint will be rendered useless. In most cases that have no energy target and a non-zero value for ``C^{value}``, if this cost is too high (``C^{value} >> 0``) or too low (``C^{value} <<0``) can result in either the model holding on to stored energy till the end of the model not storing any energy in the device. This is caused by the fact that when the energy target is zero, we have ``E_t = - E^{shortage}_t``, and ``- E^{shortage}_t * C^{value}`` in the objective function is replaced by ``E_t * C^{value}``, thus resulting in ``C^{value}`` to be seen as the cost of stored energy.
-
-| Case | Energy Target | Energy Shortage Cost | Energy Value / Energy Surplus cost | Effect |
-| ---------- | ------------- | ----------------- | ---------- | ----------------------- |
-| Case 1(a) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}=0$ | no change |
-| Case 1(b) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}<0$ | penalty for storing energy |
-| Case 1(c) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}=0$ | no penalties or incentives applied |
-| Case 1(d) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}>0$ | incentive for storing energy |
-| Case 1(e) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}<0$ | penalty for storing energy |
-| Case 1(f) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}>0$ | incentive for storing energy |
-| Case 2(a) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}=0$ | no change |
-| Case 2(b) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}<0$ | penalty on energy storage in excess of target |
-| Case 2(c) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}=0$ | penalty on energy storage short of target |
-| Case 2(d) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}>0$ | incentive on excess energy |
-| Case 2(e) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}<0$ | penalty on both excess/shortage of energy |
-| Case 2(f) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}>0$ | penalty for shortage, incentive for excess energy |
+| Case | Energy Target | Energy Shortage Cost | Energy Value / Energy Surplus cost | Effect |
+|:--------- |:------------- |:-------------------- |:---------------------------------- |:------------------------------------------------- |
+| Case 1(a) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}=0$ | no change |
+| Case 1(b) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}<0$ | penalty for storing energy |
+| Case 1(c) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}=0$ | no penalties or incentives applied |
+| Case 1(d) | $\hat{E}=0$ | $C^{penalty}=0$ | $C^{value}>0$ | incentive for storing energy |
+| Case 1(e) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}<0$ | penalty for storing energy |
+| Case 1(f) | $\hat{E}=0$ | $C^{penalty}>0$ | $C^{value}>0$ | incentive for storing energy |
+| Case 2(a) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}=0$ | no change |
+| Case 2(b) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}<0$ | penalty on energy storage in excess of target |
+| Case 2(c) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}=0$ | penalty on energy storage short of target |
+| Case 2(d) | $\hat{E}>0$ | $C^{penalty}=0$ | $C^{value}>0$ | incentive on excess energy |
+| Case 2(e) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}<0$ | penalty on both excess/shortage of energy |
+| Case 2(f) | $\hat{E}>0$ | $C^{penalty}>0$ | $C^{value}>0$ | penalty for shortage, incentive for excess energy |
diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md
index 47a8425d4e..340e7e46c3 100644
--- a/docs/src/formulation_library/Introduction.md
+++ b/docs/src/formulation_library/Introduction.md
@@ -20,23 +20,24 @@ For example a typical optimization problem in a `DecisionModel` in `PowerSimulat
```
Suppose this is a system with the following characteristics:
-- Horizon: 48 hours
-- Interval: 24 hours
-- Resolution: 1 hour
-- Three Buses: 1, 2 and 3
-- One `ThermalStandard` (device A) unit at bus 1
-- One `RenewableDispatch` (device B) unit at bus 2
-- One `PowerLoad` (device C) at bus 3
-- Three `Line` that connects all the buses
+
+ - Horizon: 48 hours
+ - Interval: 24 hours
+ - Resolution: 1 hour
+ - Three Buses: 1, 2 and 3
+ - One `ThermalStandard` (device A) unit at bus 1
+ - One `RenewableDispatch` (device B) unit at bus 2
+ - One `PowerLoad` (device C) at bus 3
+ - Three `Line` that connects all the buses
Now, we assign the following `DeviceModel` to each `PowerSystems.jl` with:
-| Type | Formulation |
-| ----------- | ----------- |
-| Network | `CopperPlatePowerModel` |
-| `ThermalStandard` | `ThermalDispatchNoMin` |
-| `RenewableDispatch` | `RenewableFullDispatch` |
-| `PowerLoad` | `StaticPowerLoad` |
+| Type | Formulation |
+|:------------------- |:----------------------- |
+| Network | `CopperPlatePowerModel` |
+| `ThermalStandard` | `ThermalDispatchNoMin` |
+| `RenewableDispatch` | `RenewableFullDispatch` |
+| `PowerLoad` | `StaticPowerLoad` |
Note that we did not assign any `DeviceModel` to `Line` since the `CopperPlatePowerModel` used for the network assumes that everything is lumped in the same node (like a copper plate with infinite capacity), and hence there are no flows between buses that branches can limit.
@@ -57,11 +58,9 @@ Note that the `StaticPowerLoad` does not impose any cost to the objective functi
# Nomenclature
In the formulations described in the other pages, the nomenclature is as follows:
-- Lowercase letters are used for variables, e.g., ``p`` for power.
-- Uppercase letters are used for parameters, e.g., ``C`` for costs.
-- Subscripts are used for indexing, e.g., ``(\cdot)_t`` for indexing at time ``t``.
-- Superscripts are used for descriptions, e.g., ``(\cdot)^\text{th}`` to describe a thermal (th) variable/parameter.
-- Bold letters are used for vectors, e.g., ``\boldsymbol{p} = \{p\}_{1,\dots,24}``.
-
-
+ - Lowercase letters are used for variables, e.g., ``p`` for power.
+ - Uppercase letters are used for parameters, e.g., ``C`` for costs.
+ - Subscripts are used for indexing, e.g., ``(\cdot)_t`` for indexing at time ``t``.
+ - Superscripts are used for descriptions, e.g., ``(\cdot)^\text{th}`` to describe a thermal (th) variable/parameter.
+ - Bold letters are used for vectors, e.g., ``\boldsymbol{p} = \{p\}_{1,\dots,24}``.
diff --git a/docs/src/formulation_library/Load.md b/docs/src/formulation_library/Load.md
index dcb7b9b8d0..7b7cb182a7 100644
--- a/docs/src/formulation_library/Load.md
+++ b/docs/src/formulation_library/Load.md
@@ -3,16 +3,17 @@
Electric load formulations define the optimization models that describe load units (demand) mathematical model in different operational settings, such as economic dispatch and unit commitment.
!!! note
+
The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created.
### Table of contents
-1. [`StaticPowerLoad`](#StaticPowerLoad)
-2. [`PowerLoadInterruption`](#PowerLoadInterruption)
-3. [`PowerLoadDispatch`](#PowerLoadDispatch)
-4. [Valid configurations](#Valid-configurations)
+ 1. [`StaticPowerLoad`](#StaticPowerLoad)
+ 2. [`PowerLoadInterruption`](#PowerLoadInterruption)
+ 3. [`PowerLoadDispatch`](#PowerLoadDispatch)
+ 4. [Valid configurations](#Valid-configurations)
----
+* * *
## `StaticPowerLoad`
@@ -37,8 +38,8 @@ combos = PowerSimulations.get_default_time_series_names(ElectricLoad, StaticPowe
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Expressions:**
@@ -49,7 +50,7 @@ Subtracts the parameters listed above from the respective active and reactive po
No constraints are created
----
+* * *
## `PowerLoadInterruption`
@@ -59,22 +60,27 @@ PowerLoadInterruption
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Symbol: ``p^\text{ld}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Symbol: ``q^\text{ld}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Default initial value: 1
- - Symbol: ``u^\text{ld}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Symbol: ``p^\text{ld}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Symbol: ``q^\text{ld}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Default initial value: 1
+ + Symbol: ``u^\text{ld}``
**Static Parameters:**
-- ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)`
-- ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)`
+
+ - ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)`
+ - ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)`
**Time Series Parameters:**
@@ -87,18 +93,17 @@ combos = PowerSimulations.get_default_time_series_names(ElectricLoad, PowerLoadI
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Objective:**
Creates an objective function term based on the [`FunctionData` Options](@ref) where the quantity term is defined as ``p^\text{ld}``.
-
**Expressions:**
-- Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations)
+ - Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations)
**Constraints:**
@@ -108,9 +113,10 @@ Creates an objective function term based on the [`FunctionData` Options](@ref) w
& q_t^\text{re} = \text{pf} \cdot p_t^\text{re}, \quad \forall t \in \{1,\dots, T\}
\end{aligned}
```
+
on which ``\text{pf} = \sin(\arctan(Q^\text{ld,max}/P^\text{ld,max}))``.
----
+* * *
## `PowerLoadDispatch`
@@ -120,18 +126,22 @@ PowerLoadDispatch
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: `PowerSystems.get_active_power(device)`
- - Symbol: ``p^\text{ld}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: `PowerSystems.get_reactive_power(device)`
- - Symbol: ``q^\text{ld}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: `PowerSystems.get_active_power(device)`
+ + Symbol: ``p^\text{ld}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: `PowerSystems.get_reactive_power(device)`
+ + Symbol: ``q^\text{ld}``
**Static Parameters:**
-- ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)`
-- ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)`
+
+ - ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)`
+ - ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)`
**Time Series Parameters:**
@@ -144,18 +154,17 @@ combos = PowerSimulations.get_default_time_series_names(ElectricLoad, PowerLoadD
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Objective:**
Creates an objective function term based on the [`FunctionData` Options](@ref) where the quantity term is defined as ``p^\text{ld}``.
-
**Expressions:**
-- Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations)
+ - Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations)
**Constraints:**
@@ -165,6 +174,7 @@ Creates an objective function term based on the [`FunctionData` Options](@ref) w
& q_t^\text{ld} = \text{pf} \cdot p_t^\text{ld}, \quad \forall t \in \{1,\dots, T\}\\
\end{aligned}
```
+
on which ``\text{pf} = \sin(\arctan(Q^\text{ld,max}/P^\text{ld,max}))``.
## Valid configurations
@@ -179,9 +189,13 @@ using Latexify
combos = PowerSimulations.generate_device_formulation_combinations()
filter!(x -> x["device_type"] <: ElectricLoad, combos)
combo_table = DataFrame(
- "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
- "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos],
+ "Valid DeviceModel" =>
+ ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
+ "Device Type" => [
+ "[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)"
+ for c in combos
+ ],
"Formulation" => ["[$(c["formulation"])](@ref)" for c in combos],
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
diff --git a/docs/src/formulation_library/Network.md b/docs/src/formulation_library/Network.md
index 9fad1c2d27..84de5c33e2 100644
--- a/docs/src/formulation_library/Network.md
+++ b/docs/src/formulation_library/Network.md
@@ -1,25 +1,31 @@
# [Network Formulations](@id network_formulations)
-Network formulations are used to describe how the network and buses are handled when constructing constraints. The most common constraint decided by the network formulation is the supply-demand balance constraint. Available Network Models are:
+Network formulations are used to describe how the network and buses are handled when constructing constraints. The most common constraint decided by the network formulation is the supply-demand balance constraint.
-| Formulation | Description |
-| ----- | ---- |
-| `CopperPlatePowerModel` | Copper plate connection between all components, i.e. infinite transmission capacity |
-| `AreaBalancePowerModel` | Network model approximation to represent inter-area flow with each area represented as a single node |
-| `PTDFPowerModel` | Uses the PTDF factor matrix to compute the fraction of power transferred in the network across the branches |
-| `AreaPTDFPowerModel` | Uses the PTDF factor matrix to compute the fraction of power transferred in the network across the branches and balances power by Area instead of system-wide |
+```@docs
+NetworkModel
+```
+
+Available Network Models are:
+
+| Formulation | Description |
+|:----------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `CopperPlatePowerModel` | Copper plate connection between all components, i.e. infinite transmission capacity |
+| `AreaBalancePowerModel` | Network model approximation to represent inter-area flow with each area represented as a single node |
+| `PTDFPowerModel` | Uses the PTDF factor matrix to compute the fraction of power transferred in the network across the branches |
+| `AreaPTDFPowerModel` | Uses the PTDF factor matrix to compute the fraction of power transferred in the network across the branches and balances power by Area instead of system-wide |
[`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl) available formulations:
-- Exact non-convex models: `ACPPowerModel`, `ACRPowerModel`, `ACTPowerModel`.
-- Linear approximations: `DCPPowerModel`, `NFAPowerModel`.
-- Quadratic approximations: `DCPLLPowerModel`, `LPACCPowerModel`
-- Quadratic relaxations: `SOCWRPowerModel`, `SOCWRConicPowerModel`, `SOCBFPowerModel`, `SOCBFConicPowerModel`, `QCRMPowerModel`, `QCLSPowerModel`.
-- SDP relaxations: `SDPWRMPowerModel`, `SparseSDPWRMPowerModel`.
+ - Exact non-convex models: `ACPPowerModel`, `ACRPowerModel`, `ACTPowerModel`.
+ - Linear approximations: `DCPPowerModel`, `NFAPowerModel`.
+ - Quadratic approximations: `DCPLLPowerModel`, `LPACCPowerModel`
+ - Quadratic relaxations: `SOCWRPowerModel`, `SOCWRConicPowerModel`, `SOCBFPowerModel`, `SOCBFConicPowerModel`, `QCRMPowerModel`, `QCLSPowerModel`.
+ - SDP relaxations: `SDPWRMPowerModel`, `SparseSDPWRMPowerModel`.
All of these formulations are described in the [PowerModels.jl documentation](https://lanl-ansi.github.io/PowerModels.jl/stable/formulation-details/) and will not be described here.
----
+* * *
## `CopperPlatePowerModel`
@@ -31,16 +37,19 @@ CopperPlatePowerModel
If Slack variables are enabled:
-- [`SystemBalanceSlackUp`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,up}``
-- [`SystemBalanceSlackDown`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,dn}``
+ - [`SystemBalanceSlackUp`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,up}``
+
+ - [`SystemBalanceSlackDown`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,dn}``
**Objective:**
@@ -60,7 +69,7 @@ Adds the `CopperPlateBalanceConstraint` to balance the active power of all compo
\end{align}
```
----
+* * *
## `AreaBalancePowerModel`
@@ -71,16 +80,19 @@ AreaBalancePowerModel
**Variables:**
If Slack variables are enabled:
-- [`SystemBalanceSlackUp`](@ref) by area:
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,up}``
-- [`SystemBalanceSlackDown`](@ref) by area:
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,dn}``
+ - [`SystemBalanceSlackUp`](@ref) by area:
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,up}``
+
+ - [`SystemBalanceSlackDown`](@ref) by area:
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,dn}``
**Objective:**
@@ -100,7 +112,7 @@ Adds the `CopperPlateBalanceConstraint` to balance the active power of all compo
\end{align}
```
----
+* * *
## `PTDFPowerModel`
@@ -112,16 +124,19 @@ PTDFPowerModel
If Slack variables are enabled:
-- [`SystemBalanceSlackUp`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,up}``
-- [`SystemBalanceSlackDown`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: 0.0
- - Default proportional cost: 1e6
- - Symbol: ``p^\text{sl,dn}``
+ - [`SystemBalanceSlackUp`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,up}``
+
+ - [`SystemBalanceSlackDown`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: 0.0
+ + Default proportional cost: 1e6
+ + Symbol: ``p^\text{sl,dn}``
**Objective:**
@@ -142,3 +157,33 @@ Adds the `CopperPlateBalanceConstraint` to balance the active power of all compo
```
In addition creates `NodalBalanceActiveConstraint` for HVDC buses balance, if DC components are connected to an HVDC network.
+
+## `AreaPTDFPowerModel`
+
+```@docs
+AreaPTDFPowerModel
+```
+
+**Variables**
+
+Slack variables are not supported.
+
+**Objective Function**
+
+No changes to the objective function.
+
+**Expressions**
+
+Creates the area-wide and nodal-wide active power balance expressions `ActivePowerBalance` to balance power based on each area independently. The flows across areas are computed based on the PTDF factors of lines connecting areas.
+
+**Constraints:**
+
+Adds the `ActivePowerBalance` constraint to balance the active power of all components available for each area.
+
+```math
+\begin{align}
+& \sum_{c \in \text{components}_a} p_t^c = 0, \quad \forall a\in \{1,\dots, A\}, t \in \{1, \dots, T\}
+\end{align}
+```
+
+This includes the flows of lines based on the PTDF factors.
diff --git a/docs/src/formulation_library/Piecewise.md b/docs/src/formulation_library/Piecewise.md
index 2167769162..d2ab0288f9 100644
--- a/docs/src/formulation_library/Piecewise.md
+++ b/docs/src/formulation_library/Piecewise.md
@@ -6,19 +6,20 @@ The choice for piecewise-linear (PWL) cost representation in `PowerSimulations.
A special ordered set (SOS) is an ordered set of variables used as an additional way to specify integrality conditions in an optimization model.
-- Special Ordered Sets of type 1 (SOS1) are a set of variables, at most one of which can take a non-zero value, all others being at 0. They most frequently applications is in a a set of variables that are actually binary variables: in other words, we have to choose at most one from a set of possibilities.
-- Special Ordered Sets of type 2 (SOS2) are an ordered set of non-negative variables, of which at most two can be non-zero, and if two are non-zero these must be consecutive in their ordering. Special Ordered Sets of type 2 are typically used to model non-linear functions of a variable in a linear model, such as non-convex quadratic functions using PWL functions.
+ - Special Ordered Sets of type 1 (SOS1) are a set of variables, at most one of which can take a non-zero value, all others being at 0. They most frequently applications is in a a set of variables that are actually binary variables: in other words, we have to choose at most one from a set of possibilities.
+ - Special Ordered Sets of type 2 (SOS2) are an ordered set of non-negative variables, of which at most two can be non-zero, and if two are non-zero these must be consecutive in their ordering. Special Ordered Sets of type 2 are typically used to model non-linear functions of a variable in a linear model, such as non-convex quadratic functions using PWL functions.
## Standard representation of PWL costs
Piecewise-linear costs are defined by a sequence of points representing the line segments for each generator: ``(P_k^\text{max}, C_k)`` on which we assume ``C_k`` is the cost of generating ``P_k^\text{max}`` power, and ``k \in \{1,\dots, K\}`` are the number of segments each generator cost function has.
!!! note
+
`PowerSystems` has more options to specify cost functions for each thermal unit. Independent of which form of the cost data is provided, `PowerSimulations.jl` will internally transform the data to use the λ-model formulation. See **TODO: ADD PSY COST DOCS** for more information.
### Commitment formulation
- With this the standard representation of PWL costs for a thermal unit commitment is given by:
+With this the standard representation of PWL costs for a thermal unit commitment is given by:
```math
\begin{align*}
@@ -30,6 +31,7 @@ Piecewise-linear costs are defined by a sequence of points representing the line
&\left \{\delta_{1,t}, \dots, \delta_{K,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T}
\end{align*}
```
+
on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted.
### Dispatch formulation
@@ -44,6 +46,7 @@ on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the
&\left \{\delta_{i,t}, \dots, \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T}
\end{align*}
```
+
on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``\text{on} \in \{0,1\}`` is the parameter that decides if the generator is available or not. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted.
## Compact representation of PWL costs
@@ -60,6 +63,7 @@ on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the
&\left \{\delta_{i,t} \dots \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T}
\end{align*}
```
+
on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p`` is the active power of the generator above the minimum power and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted.
### Dispatch formulation
@@ -74,4 +78,5 @@ on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p``
&\left \{\delta_{i,t} \dots \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T}
\end{align*}
```
-on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p`` is the active power of the generator above the minimum power and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted.
\ No newline at end of file
+
+on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p`` is the active power of the generator above the minimum power and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted.
diff --git a/docs/src/formulation_library/RenewableGen.md b/docs/src/formulation_library/RenewableGen.md
index 5dfca92c3e..ba4ce69809 100644
--- a/docs/src/formulation_library/RenewableGen.md
+++ b/docs/src/formulation_library/RenewableGen.md
@@ -3,18 +3,20 @@
Renewable generation formulations define the optimization models that describe renewable units mathematical model in different operational settings, such as economic dispatch and unit commitment.
!!! note
+
The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created.
!!! note
+
Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section.
### Table of contents
-1. [`RenewableFullDispatch`](#RenewableFullDispatch)
-2. [`RenewableConstantPowerFactor`](#RenewableConstantPowerFactor)
-3. [Valid configurations](#Valid-configurations)
+ 1. [`RenewableFullDispatch`](#RenewableFullDispatch)
+ 2. [`RenewableConstantPowerFactor`](#RenewableConstantPowerFactor)
+ 3. [Valid configurations](#Valid-configurations)
----
+* * *
## `RenewableFullDispatch`
@@ -24,18 +26,21 @@ RenewableFullDispatch
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{re}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{re}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{re}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{re}``
**Static Parameters:**
-- ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
**Time Series Parameters:**
@@ -50,15 +55,14 @@ combos = PowerSimulations.get_default_time_series_names(RenewableGen, RenewableF
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Objective:**
Creates an objective function term based on the [`FunctionData` Options](@ref) where the quantity term is defined as ``- p^\text{re}`` to incentivize generation from `RenewableGen` devices.
-
**Expressions:**
Adds ``p^\text{re}`` and ``q^\text{re}`` terms to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations).
@@ -72,7 +76,7 @@ Adds ``p^\text{re}`` and ``q^\text{re}`` terms to the respective active and reac
\end{aligned}
```
----
+* * *
## `RenewableConstantPowerFactor`
@@ -82,21 +86,24 @@ RenewableConstantPowerFactor
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: `PowerSystems.get_active_power(device)`
- - Symbol: ``p^\text{re}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Default initial value: `PowerSystems.get_reactive_power(device)`
- - Symbol: ``q^\text{re}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: `PowerSystems.get_active_power(device)`
+ + Symbol: ``p^\text{re}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default initial value: `PowerSystems.get_reactive_power(device)`
+ + Symbol: ``q^\text{re}``
**Static Parameters:**
-- ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``\text{pf}`` = `PowerSystems.get_power_factor(device)`
+ - ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``\text{pf}`` = `PowerSystems.get_power_factor(device)`
**Time Series Parameters:**
@@ -105,12 +112,15 @@ using PowerSimulations
using PowerSystems
using DataFrames
using Latexify
-combos = PowerSimulations.get_default_time_series_names(RenewableGen, RenewableConstantPowerFactor)
+combos = PowerSimulations.get_default_time_series_names(
+ RenewableGen,
+ RenewableConstantPowerFactor,
+)
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Objective:**
@@ -130,7 +140,7 @@ Adds ``p^\text{re}`` and ``q^\text{re}`` terms to the respective active and reac
\end{aligned}
```
----
+* * *
## Valid configurations
@@ -144,10 +154,13 @@ using Latexify
combos = PowerSimulations.generate_device_formulation_combinations()
filter!(x -> x["device_type"] <: RenewableGen, combos)
combo_table = DataFrame(
- "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
- "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos],
+ "Valid DeviceModel" =>
+ ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
+ "Device Type" => [
+ "[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)"
+ for c in combos
+ ],
"Formulation" => ["[$(c["formulation"])](@ref)" for c in combos],
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
-
diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md
index d43cd334d6..c07df6cee9 100644
--- a/docs/src/formulation_library/Service.md
+++ b/docs/src/formulation_library/Service.md
@@ -10,15 +10,16 @@ In this documentation, we first specify the available `Services` in the grid, an
### Table of contents
-1. [`RangeReserve`](#RangeReserve)
-2. [`StepwiseCostReserve`](#StepwiseCostReserve)
-3. [`GroupReserve`](#GroupReserve)
-4. [`RampReserve`](#RampReserve)
-5. [`NonSpinningReserve`](#NonSpinningReserve)
-6. [`ConstantMaxInterfaceFlow`](#ConstantMaxInterfaceFlow)
-7. [Changes on Expressions](#Changes-on-Expressions-due-to-Service-models)
+ 1. [`RangeReserve`](#RangeReserve)
+ 2. [`StepwiseCostReserve`](#StepwiseCostReserve)
+ 3. [`GroupReserve`](#GroupReserve)
+ 4. [`RampReserve`](#RampReserve)
+ 5. [`NonSpinningReserve`](#NonSpinningReserve)
+ 6. [`ConstantMaxInterfaceFlow`](#ConstantMaxInterfaceFlow)
+ 7. [`VariableMaxInterfaceFlow`](#VariableMaxInterfaceFlow)
+ 8. [Changes on Expressions](#Changes-on-Expressions-due-to-Service-models)
----
+* * *
## `RangeReserve`
@@ -30,28 +31,33 @@ For each service ``s`` of the model type `RangeReserve` the following variables
**Variables**:
-- [`ActivePowerReserveVariable`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: ``1.0 / \text{SystemBasePower}``
- - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
-If slacks are enabled:
-- [`ReserveRequirementSlack`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: 1e5
- - Symbol: ``r^\text{sl}``
+ - [`ActivePowerReserveVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: ``1.0 / \text{SystemBasePower}``
+ + Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
+ If slacks are enabled:
+
+ - [`ReserveRequirementSlack`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 1e5
+ + Symbol: ``r^\text{sl}``
Depending on the `PowerSystems.jl` type associated to the `RangeReserve` formulation model, the parameters are:
**Static Parameters**
-- ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)`
+ - ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)`
For a `ConstantReserve` `PowerSystems` type:
-- ``\text{Req}`` = `PowerSystems.get_requirement(service)`
-**Time Series Parameters**
+ - ``\text{Req}`` = `PowerSystems.get_requirement(service)`
+
+**Time Series Parameters**
For a `VariableReserve` `PowerSystems` type:
+
```@eval
using PowerSimulations
using PowerSystems
@@ -61,13 +67,13 @@ combos = PowerSimulations.get_default_time_series_names(VariableReserve, RangeRe
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Relevant Methods:**
-- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
**Objective:**
@@ -79,17 +85,19 @@ Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contr
For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable
-
*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin):
+
```math
\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max}
```
+
similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down):
+
```math
\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min}
```
-**Constraints:**
+**Constraints:**
A RangeReserve implements two fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RangeReserve` requirement. Thus, for a service ``s``:
@@ -105,7 +113,7 @@ r_{d,t} \le \text{Req} \cdot \text{PF} ,\quad \forall d\in \mathcal{D}_s, \foral
r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for a VariableReserve)}
```
----
+* * *
## `StepwiseCostReserve`
@@ -119,32 +127,37 @@ For each service ``s`` of the model type `ReserveDemandCurve` the following vari
**Variables**:
-- [`ActivePowerReserveVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
-- [`ServiceRequirementVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``\text{req}``
+ - [`ActivePowerReserveVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
-**Time Series Parameters**
+ - [`ServiceRequirementVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``\text{req}``
+
+**Time Series Parameters**
For a `ReserveDemandCurve` `PowerSystems` type:
+
```@eval
using PowerSimulations
using PowerSystems
using DataFrames
using Latexify
-combos = PowerSimulations.get_default_time_series_names(ReserveDemandCurve, StepwiseCostReserve)
+combos =
+ PowerSimulations.get_default_time_series_names(ReserveDemandCurve, StepwiseCostReserve)
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Relevant Methods:**
-- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
**Objective:**
@@ -156,17 +169,19 @@ Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contr
For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable
-
*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin):
+
```math
\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max}
```
+
similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down):
+
```math
\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min}
```
-**Constraints:**
+**Constraints:**
A `StepwiseCostReserve` implements a single constraint, such that the sum of all reserves of contributing devices must be larger than the `ServiceRequirementVariable` variable. Thus, for a service ``s``:
@@ -190,12 +205,12 @@ No variables are created, but the services associated with the `GroupReserve` mu
**Static Parameters**
-- ``\text{Req}`` = `PowerSystems.get_requirement(service)`
+ - ``\text{Req}`` = `PowerSystems.get_requirement(service)`
**Relevant Methods:**
-- ``\mathcal{S}_s`` = `PowerSystems.get_contributing_services(system, service)`: Set (vector) of all contributing services to the group service ``s`` in the system.
-- ``\mathcal{D}_{s_i}`` = `PowerSystems.get_contributing_devices(system, service_aux)`: Set (vector) of all contributing devices to the service ``s_i`` in the system.
+ - ``\mathcal{S}_s`` = `PowerSystems.get_contributing_services(system, service)`: Set (vector) of all contributing services to the group service ``s`` in the system.
+ - ``\mathcal{D}_{s_i}`` = `PowerSystems.get_contributing_devices(system, service_aux)`: Set (vector) of all contributing devices to the service ``s_i`` in the system.
**Objective:**
@@ -213,7 +228,7 @@ A GroupReserve implements that the sum of all reserves of contributing devices,
\sum_{d\in\mathcal{D}_{s_i}} \sum_{i \in \mathcal{S}_s} r_{d,t} \ge \text{Req},\quad \forall t\in \{1,\dots, T\}
```
----
+* * *
## `RampReserve`
@@ -225,28 +240,31 @@ For each service ``s`` of the model type `RampReserve` the following variables a
**Variables**:
-- [`ActivePowerReserveVariable`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: ``1.0 / \text{SystemBasePower}``
- - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
-If slacks are enabled:
-- [`ReserveRequirementSlack`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: 1e5
- - Symbol: ``r^\text{sl}``
+ - [`ActivePowerReserveVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: ``1.0 / \text{SystemBasePower}``
+ + Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
+ If slacks are enabled:
+
+ - [`ReserveRequirementSlack`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 1e5
+ + Symbol: ``r^\text{sl}``
`RampReserve` only accepts `VariableReserve` `PowerSystems.jl` type. With that, the parameters are:
**Static Parameters**
-- ``\text{TF}`` = `PowerSystems.get_time_frame(service)`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` for thermal contributing devices
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices
-
+ - ``\text{TF}`` = `PowerSystems.get_time_frame(service)`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` for thermal contributing devices
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices
-**Time Series Parameters**
+**Time Series Parameters**
For a `VariableReserve` `PowerSystems` type:
+
```@eval
using PowerSimulations
using PowerSystems
@@ -256,13 +274,13 @@ combos = PowerSimulations.get_default_time_series_names(VariableReserve, RampRes
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Relevant Methods:**
-- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
**Objective:**
@@ -274,17 +292,19 @@ Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contr
For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable
-
*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin):
+
```math
\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max}
```
+
similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down):
+
```math
\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min}
```
-**Constraints:**
+**Constraints:**
A RampReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RampReserve` requirement. Thus, for a service ``s``:
@@ -299,7 +319,7 @@ r_{d,t} \le R^\text{th,up} \cdot \text{TF}\quad \forall d\in \mathcal{D}_s, \fo
r_{d,t} \le R^\text{th,dn} \cdot \text{TF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for ReserveDown)}
```
----
+* * *
## `NonSpinningReserve`
@@ -311,33 +331,37 @@ For each service ``s`` of the model type `NonSpinningReserve`, the following var
**Variables**:
-- [`ActivePowerReserveVariable`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: ``1.0 / \text{SystemBasePower}``
- - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
-If slacks are enabled:
-- [`ReserveRequirementSlack`](@ref):
- - Bounds: [0.0, ]
- - Default proportional cost: 1e5
- - Symbol: ``r^\text{sl}``
+ - [`ActivePowerReserveVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: ``1.0 / \text{SystemBasePower}``
+ + Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s``
+ If slacks are enabled:
+
+ - [`ReserveRequirementSlack`](@ref):
+
+ + Bounds: [0.0, ]
+ + Default proportional cost: 1e5
+ + Symbol: ``r^\text{sl}``
`NonSpinningReserve` only accepts `VariableReserve` `PowerSystems.jl` type. With that, the parameters are:
**Static Parameters**
-- ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)`
-- ``\text{TF}`` = `PowerSystems.get_time_frame(service)`
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` for thermal contributing devices
-- ``T^\text{st,up}`` = `PowerSystems.get_time_limits(d).up` for thermal contributing devices
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices
+ - ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)`
+ - ``\text{TF}`` = `PowerSystems.get_time_frame(service)`
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` for thermal contributing devices
+ - ``T^\text{st,up}`` = `PowerSystems.get_time_limits(d).up` for thermal contributing devices
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices
Other parameters:
-- ``\Delta T``: Resolution of the problem in minutes.
+ - ``\Delta T``: Resolution of the problem in minutes.
-**Time Series Parameters**
+**Time Series Parameters**
For a `VariableReserve` `PowerSystems` type:
+
```@eval
using PowerSimulations
using PowerSystems
@@ -347,13 +371,13 @@ combos = PowerSimulations.get_default_time_series_names(VariableReserve, NonSpin
combo_table = DataFrame(
"Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
"Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
**Relevant Methods:**
-- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system.
**Objective:**
@@ -365,17 +389,19 @@ Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contr
For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable
-
*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin):
+
```math
\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max}
```
+
similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down):
+
```math
\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min}
```
-**Constraints:**
+**Constraints:**
A NonSpinningReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `NonSpinningReserve` requirement. Thus, for a service ``s``:
@@ -390,6 +416,7 @@ r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \for
```
Finally, there is a restriction based on the reserve response time for the non-spinning reserve if the unit is off. To do so, compute ``R^\text{limit}_d`` as the reserve response limit as:
+
```math
R^\text{limit}_d = \begin{cases}
0 & \text{ if TF } \le T^\text{st,up}_d \\
@@ -403,7 +430,7 @@ Then, the constraint depends on the commitment variable ``u_t^\text{th}`` as:
r_{d,t} \le (1 - u_{d,t}^\text{th}) \cdot R^\text{limit}_d, \quad \forall d \in \mathcal{D}_s, \forall t \in \{1,\dots, T\}
```
----
+* * *
## `ConstantMaxInterfaceFlow`
@@ -416,23 +443,27 @@ ConstantMaxInterfaceFlow
**Variables**
If slacks are used:
-- [`InterfaceFlowSlackUp`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``f^\text{sl,up}``
-- [`InterfaceFlowSlackDown`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``f^\text{sl,dn}``
+
+ - [`InterfaceFlowSlackUp`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``f^\text{sl,up}``
+
+ - [`InterfaceFlowSlackDown`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``f^\text{sl,dn}``
**Static Parameters**
-- ``F^\text{max}`` = `PowerSystems.get_active_power_flow_limits(service).max`
-- ``F^\text{min}`` = `PowerSystems.get_active_power_flow_limits(service).min`
-- ``C^\text{flow}`` = `PowerSystems.get_violation_penalty(service)`
-- ``\mathcal{M}_s`` = `PowerSystems.get_direction_mapping(service)`. Dictionary of contributing branches with its specified direction (``\text{Dir}_d = 1`` or ``\text{Dir}_d = -1``) with respect to the interface.
+ - ``F^\text{max}`` = `PowerSystems.get_active_power_flow_limits(service).max`
+ - ``F^\text{min}`` = `PowerSystems.get_active_power_flow_limits(service).min`
+ - ``C^\text{flow}`` = `PowerSystems.get_violation_penalty(service)`
+ - ``\mathcal{M}_s`` = `PowerSystems.get_direction_mapping(service)`. Dictionary of contributing branches with its specified direction (``\text{Dir}_d = 1`` or ``\text{Dir}_d = -1``) with respect to the interface.
**Relevant Methods**
-- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing branches to the service ``s`` in the system.
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing branches to the service ``s`` in the system.
**Objective:**
@@ -450,6 +481,75 @@ It adds the constraint to limit the `InterfaceTotalFlow` by the specified bounds
F^\text{min} \le f^\text{sl,up}_t - f^\text{sl,dn}_t + \sum_{d\in\mathcal{D}_s} \text{Dir}_d f_{d,t} \le F^\text{max}, \quad \forall t \in \{1,\dots,T\}
```
+## `VariableMaxInterfaceFlow`
+
+This Service model only accepts the `PowerSystems.jl` `TransmissionInterface` type to properly function. It is used to model a collection of branches that make up an interface or corridor with a maximum transfer of power.
+
+```@docs
+VariableMaxInterfaceFlow
+```
+
+**Variables**
+
+If slacks are used:
+
+ - [`InterfaceFlowSlackUp`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``f^\text{sl,up}``
+
+ - [`InterfaceFlowSlackDown`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``f^\text{sl,dn}``
+
+**Static Parameters**
+
+ - ``F^\text{max}`` = `PowerSystems.get_active_power_flow_limits(service).max`
+ - ``F^\text{min}`` = `PowerSystems.get_active_power_flow_limits(service).min`
+ - ``C^\text{flow}`` = `PowerSystems.get_violation_penalty(service)`
+ - ``\mathcal{M}_s`` = `PowerSystems.get_direction_mapping(service)`. Dictionary of contributing branches with its specified direction (``\text{Dir}_d = 1`` or ``\text{Dir}_d = -1``) with respect to the interface.
+
+**Time Series Parameters**
+
+For a `TransmissionInterface` `PowerSystems` type:
+
+```@eval
+using PowerSimulations
+using PowerSystems
+using DataFrames
+using Latexify
+combos = PowerSimulations.get_default_time_series_names(
+ TransmissionInterface,
+ VariableMaxInterfaceFlow,
+)
+combo_table = DataFrame(
+ "Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))),
+ "Default Time Series Name" => map(x -> "`$x`", collect(values(combos))),
+)
+mdtable(combo_table; latex = false)
+```
+
+**Relevant Methods**
+
+ - ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing branches to the service ``s`` in the system.
+
+**Objective:**
+
+Add the violation penalty proportional cost to the objective function if slack variables are used ``+ (f^\text{sl,up} + f^\text{sl,dn}) \cdot C^\text{flow}``.
+
+**Expressions:**
+
+Creates the expression `InterfaceTotalFlow` to keep track of all `FlowActivePowerVariable` of contributing branches to the transmission interface.
+
+**Constraints:**
+
+It adds the constraint to limit the `InterfaceTotalFlow` by the specified bounds of the service ``s``:
+
+```math
+F^\text{min} \cdot \text{MinInterfaceFlowLimitParameter}_t \le f^\text{sl,up}_t - f^\text{sl,dn}_t + \sum_{d\in\mathcal{D}_s} \text{Dir}_d f_{d,t} \le F^\text{max}\cdot \text{MaxInterfaceFlowLimitParameter}_t, \quad \forall t \in \{1,\dots,T\}
+```
+
## Changes on Expressions due to Service models
It is important to note that by adding a service to a Optimization Problem, variables for each contributing device must be created. For example, for every contributing generator ``d \in \mathcal{D}`` that is participating in services ``s_1,s_2,s_3``, it is required to create three set of `ActivePowerReserveVariable` variables:
@@ -466,6 +566,7 @@ Each contributing generator ``d`` has active power limits that the reserve varia
\text{ActivePowerRangeExpressionUB}_t \le P^\text{max} \\
\text{ActivePowerRangeExpressionLB}_t \ge P^\text{min}
```
+
`ReserveUp` type variables contribute to the upper bound expression, while `ReserveDown` variables contribute to the lower bound expressions. So if ``s_1,s_2`` are `ReserveUp` services, and ``s_3`` is a `ReserveDown` service, then for a thermal generator ``d`` using a `ThermalStandardDispatch`:
```math
@@ -487,6 +588,7 @@ while for a renewable generator ``d`` using a `RenewableFullDispatch`:
### Changes in Ramp limits
For the case of Ramp Limits (of formulation that model these limits), the reserve variables only affect the current time, and not the previous time. Then, for the same example as before:
+
```math
\begin{align*}
& p_{d,t}^\text{th} + r_{s_1,d,t} + r_{s_2,d,t} - p_{d,t-1}^\text{th}\le R^\text{th,up},\quad \forall d\in \mathcal{D}^\text{th}, \forall t \in \{1,\dots,T\}\\
diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md
index e179c8c8e1..6e8ab9fcc8 100644
--- a/docs/src/formulation_library/ThermalGen.md
+++ b/docs/src/formulation_library/ThermalGen.md
@@ -2,51 +2,56 @@
Thermal generation formulations define the optimization models that describe thermal units mathematical model in different operational settings, such as economic dispatch and unit commitment.
-
!!! note
- Thermal units can include multiple terms added to the objective function, such as no-load cost, turn-on/off cost, fixed cost and variable cost. In addition, variable costs can be linear, quadratic or piecewise-linear formulations. These methods are properly described in the [cost function page](@ref pwl_cost).
-
+
+ Thermal units can include multiple terms added to the objective function, such as no-load cost, turn-on/off cost, fixed cost and variable cost. In addition, variable costs can be linear, quadratic or piecewise-linear formulations. These methods are properly described in the [cost function page](@ref pwl_cost).
!!! note
+
The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created.
!!! note
+
Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section.
### Table of Contents
-1. [`ThermalBasicDispatch`](#ThermalBasicDispatch)
-2. [`ThermalDispatchNoMin`](#ThermalDispatchNoMin)
-3. [`ThermalCompactDispatch`](#ThermalCompactDispatch)
-4. [`ThermalStandardDispatch`](#ThermalStandardDispatch)
-5. [`ThermalBasicUnitCommitment`](#ThermalBasicUnitCommitment)
-6. [`ThermalBasicCompactUnitCommitment`](#ThermalBasicCompactUnitCommitment)
-7. [`ThermalStandardUnitCommitment`](#ThermalStandardUnitCommitment)
-8. [`ThermalMultiStartUnitCommitment`](#ThermalMultiStartUnitCommitment)
-9. [Valid configurations](#Valid-configurations)
+ 1. [`ThermalBasicDispatch`](#ThermalBasicDispatch)
+ 2. [`ThermalDispatchNoMin`](#ThermalDispatchNoMin)
+ 3. [`ThermalCompactDispatch`](#ThermalCompactDispatch)
+ 4. [`ThermalStandardDispatch`](#ThermalStandardDispatch)
+ 5. [`ThermalBasicUnitCommitment`](#ThermalBasicUnitCommitment)
+ 6. [`ThermalBasicCompactUnitCommitment`](#ThermalBasicCompactUnitCommitment)
+ 7. [`ThermalStandardUnitCommitment`](#ThermalStandardUnitCommitment)
+ 8. [`ThermalMultiStartUnitCommitment`](#ThermalMultiStartUnitCommitment)
+ 9. [Valid configurations](#Valid-configurations)
----
+* * *
## `ThermalBasicDispatch`
```@docs
ThermalBasicDispatch
```
+
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
**Static Parameters:**
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
**Objective:**
@@ -67,8 +72,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align*}
```
----
-
+* * *
## `ThermalDispatchNoMin`
@@ -78,18 +82,21 @@ ThermalDispatchNoMin
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
**Static Parameters:**
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
**Objective:**
@@ -110,7 +117,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align}
```
----
+* * *
## `ThermalCompactDispatch`
@@ -120,30 +127,35 @@ ThermalCompactDispatch
**Variables:**
-- [`PowerAboveMinimumVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``\Delta p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
+ - [`PowerAboveMinimumVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``\Delta p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
**Auxiliary Variables:**
-- [`PowerOutput`](@ref):
- - Symbol: ``P^\text{th}``
- - Definition: ``P^\text{th} = \text{on}^\text{th}P^\text{min} + \Delta p^\text{th}``
+
+ - [`PowerOutput`](@ref):
+
+ + Symbol: ``P^\text{th}``
+ + Definition: ``P^\text{th} = \text{on}^\text{th}P^\text{min} + \Delta p^\text{th}``
**Static Parameters:**
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
**Variable Value Parameters:**
-- ``\text{on}^\text{th}``: Used in feedforwards to define if the unit is on/off at each time-step from another problem. If no feedforward is used, the parameter takes a {0,1} value if the unit is available or not.
+ - ``\text{on}^\text{th}``: Used in feedforwards to define if the unit is on/off at each time-step from another problem. If no feedforward is used, the parameter takes a {0,1} value if the unit is available or not.
**Objective:**
@@ -166,8 +178,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align*}
```
----
-
+* * *
## `ThermalStandardDispatch`
@@ -177,21 +188,24 @@ ThermalStandardDispatch
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
**Static Parameters:**
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
**Objective:**
@@ -214,7 +228,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align*}
```
----
+* * *
## `ThermalBasicUnitCommitment`
@@ -224,30 +238,34 @@ ThermalBasicUnitCommitment
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``u_t^\text{th}``
-- [`StartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``v_t^\text{th}``
-- [`StopVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``w_t^\text{th}``
-
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u_t^\text{th}``
+ - [`StartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``v_t^\text{th}``
+ - [`StopVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``w_t^\text{th}``
**Static Parameters:**
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
**Objective:**
@@ -271,7 +289,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align*}
```
----
+* * *
## `ThermalBasicCompactUnitCommitment`
@@ -279,38 +297,43 @@ For each thermal unit creates the range constraints for its active and reactive
ThermalBasicCompactUnitCommitment
```
-
**Variables:**
-- [`PowerAboveMinimumVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``\Delta p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``u_t^\text{th}``
-- [`StartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``v_t^\text{th}``
-- [`StopVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``w_t^\text{th}``
+ - [`PowerAboveMinimumVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``\Delta p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u_t^\text{th}``
+ - [`StartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``v_t^\text{th}``
+ - [`StopVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``w_t^\text{th}``
**Auxiliary Variables:**
-- [`PowerOutput`](@ref):
- - Symbol: ``P^\text{th}``
- - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
+ - [`PowerOutput`](@ref):
+
+ + Symbol: ``P^\text{th}``
+ + Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
**Static Parameters:**
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
**Objective:**
@@ -318,7 +341,7 @@ Add a cost to the objective function depending on the defined cost structure of
**Expressions:**
-Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
+Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
**Constraints:**
@@ -334,7 +357,7 @@ For each thermal unit creates the range constraints for its active and reactive
\end{align*}
```
----
+* * *
## `ThermalCompactUnitCommitment`
@@ -344,44 +367,54 @@ ThermalCompactUnitCommitment
**Variables:**
-- [`PowerAboveMinimumVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``\Delta p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``u_t^\text{th}``
-- [`StartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``v_t^\text{th}``
-- [`StopVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``w_t^\text{th}``
+ - [`PowerAboveMinimumVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``\Delta p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u_t^\text{th}``
+ - [`StartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``v_t^\text{th}``
+ - [`StopVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``w_t^\text{th}``
**Auxiliary Variables:**
-- [`PowerOutput`](@ref):
- - Symbol: ``P^\text{th}``
- - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
-- [`TimeDurationOn`](@ref):
- - Symbol: ``V_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
-- [`TimeDurationOff`](@ref):
- - Symbol: ``W_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
-**Static Parameters:**
+ - [`PowerOutput`](@ref):
+
+ + Symbol: ``P^\text{th}``
+ + Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
+
+ - [`TimeDurationOn`](@ref):
+
+ + Symbol: ``V_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
+ - [`TimeDurationOff`](@ref):
+
+ + Symbol: ``W_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
-- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
-- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
+**Static Parameters:**
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
+ - ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
+ - ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
**Objective:**
@@ -389,7 +422,7 @@ Add a cost to the objective function depending on the defined cost structure of
**Expressions:**
-Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
+Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
**Constraints:**
@@ -412,6 +445,7 @@ In addition, this formulation adds duration constraints, i.e. minimum-up time an
The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started.
Minimum up-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\
@@ -422,6 +456,7 @@ Minimum up-time constraint for ``t \in \{1,\dots T\}``:
```
Minimum down-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\
@@ -431,7 +466,7 @@ Minimum down-time constraint for ``t \in \{1,\dots T\}``:
\end{align*}
```
----
+* * *
## `ThermalStandardUnitCommitment`
@@ -441,41 +476,50 @@ ThermalStandardUnitCommitment
**Variables:**
-- [`ActivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``u_t^\text{th}``
-- [`StartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``v_t^\text{th}``
-- [`StopVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``w_t^\text{th}``
+ - [`ActivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u_t^\text{th}``
+ - [`StartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``v_t^\text{th}``
+ - [`StopVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``w_t^\text{th}``
**Auxiliary Variables:**
-- [`TimeDurationOn`](@ref):
- - Symbol: ``V_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
-- [`TimeDurationOff`](@ref):
- - Symbol: ``W_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
-**Static Parameters:**
+ - [`TimeDurationOn`](@ref):
+
+ + Symbol: ``V_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
-- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
-- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
+ - [`TimeDurationOff`](@ref):
+
+ + Symbol: ``W_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
+**Static Parameters:**
+
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
+ - ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
+ - ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
**Objective:**
@@ -506,6 +550,7 @@ In addition, this formulation adds duration constraints, i.e. minimum-up time an
The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started.
Minimum up-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\
@@ -516,6 +561,7 @@ Minimum up-time constraint for ``t \in \{1,\dots T\}``:
```
Minimum down-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\
@@ -525,8 +571,7 @@ Minimum down-time constraint for ``t \in \{1,\dots T\}``:
\end{align*}
```
-
----
+* * *
## `ThermalMultiStartUnitCommitment`
@@ -534,61 +579,73 @@ Minimum down-time constraint for ``t \in \{1,\dots T\}``:
ThermalMultiStartUnitCommitment
```
-
**Variables:**
-- [`PowerAboveMinimumVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``\Delta p^\text{th}``
-- [`ReactivePowerVariable`](@ref):
- - Bounds: [0.0, ]
- - Symbol: ``q^\text{th}``
-- [`OnVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``u_t^\text{th}``
-- [`StartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``v_t^\text{th}``
-- [`StopVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``w_t^\text{th}``
-- [`ColdStartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``x_t^\text{th}``
-- [`WarmStartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``y_t^\text{th}``
-- [`HotStartVariable`](@ref):
- - Bounds: ``\{0,1\}``
- - Symbol: ``z_t^\text{th}``
+ - [`PowerAboveMinimumVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``\Delta p^\text{th}``
+
+ - [`ReactivePowerVariable`](@ref):
+
+ + Bounds: [0.0, ]
+ + Symbol: ``q^\text{th}``
+ - [`OnVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``u_t^\text{th}``
+ - [`StartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``v_t^\text{th}``
+ - [`StopVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``w_t^\text{th}``
+ - [`ColdStartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``x_t^\text{th}``
+ - [`WarmStartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``y_t^\text{th}``
+ - [`HotStartVariable`](@ref):
+
+ + Bounds: ``\{0,1\}``
+ + Symbol: ``z_t^\text{th}``
**Auxiliary Variables:**
-- [`PowerOutput`](@ref):
- - Symbol: ``P^\text{th}``
- - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
-- [`TimeDurationOn`](@ref):
- - Symbol: ``V_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
-- [`TimeDurationOff`](@ref):
- - Symbol: ``W_t^\text{th}``
- - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
-**Static Parameters:**
+ - [`PowerOutput`](@ref):
+
+ + Symbol: ``P^\text{th}``
+ + Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}``
+
+ - [`TimeDurationOn`](@ref):
+
+ + Symbol: ``V_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}``
+ - [`TimeDurationOff`](@ref):
+
+ + Symbol: ``W_t^\text{th}``
+ + Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}``
-- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
-- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
-- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
-- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
-- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
-- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
-- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
-- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
-- ``D^\text{cold}`` = `PowerSystems.get_start_time_limits(device).cold`
-- ``D^\text{warm}`` = `PowerSystems.get_start_time_limits(device).warm`
-- ``D^\text{hot}`` = `PowerSystems.get_start_time_limits(device).hot`
-- ``P^\text{th,startup}`` = `PowerSystems.get_power_trajectory(device).startup`
-- ``P^\text{th, shdown}`` = `PowerSystems.get_power_trajectory(device).shutdown`
+**Static Parameters:**
+ - ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min`
+ - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max`
+ - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min`
+ - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max`
+ - ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up`
+ - ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down`
+ - ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up`
+ - ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down`
+ - ``D^\text{cold}`` = `PowerSystems.get_start_time_limits(device).cold`
+ - ``D^\text{warm}`` = `PowerSystems.get_start_time_limits(device).warm`
+ - ``D^\text{hot}`` = `PowerSystems.get_start_time_limits(device).hot`
+ - ``P^\text{th,startup}`` = `PowerSystems.get_power_trajectory(device).startup`
+ - ``P^\text{th, shdown}`` = `PowerSystems.get_power_trajectory(device).shutdown`
**Objective:**
@@ -596,7 +653,7 @@ Add a cost to the objective function depending on the defined cost structure of
**Expressions:**
-Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
+Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used.
**Constraints:**
@@ -620,6 +677,7 @@ In addition, this formulation adds duration constraints, i.e. minimum-up time an
The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started.
Minimum up-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\
@@ -630,6 +688,7 @@ Minimum up-time constraint for ``t \in \{1,\dots T\}``:
```
Minimum down-time constraint for ``t \in \{1,\dots T\}``:
+
```math
\begin{align*}
& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\
@@ -653,8 +712,7 @@ Finally, multi temperature start/stop constraints are implemented using the foll
\end{align*}
```
-
----
+* * *
## Valid configurations
@@ -668,9 +726,13 @@ using Latexify
combos = PowerSimulations.generate_device_formulation_combinations()
filter!(x -> x["device_type"] <: ThermalGen, combos)
combo_table = DataFrame(
- "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
- "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos],
+ "Valid DeviceModel" =>
+ ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos],
+ "Device Type" => [
+ "[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)"
+ for c in combos
+ ],
"Formulation" => ["[$(c["formulation"])](@ref)" for c in combos],
- )
-mdtable(combo_table, latex = false)
+)
+mdtable(combo_table; latex = false)
```
diff --git a/docs/src/index.md b/docs/src/index.md
index 8f8b8ebf0d..e49aaa5906 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -11,8 +11,8 @@ CurrentModule = PowerSimulations
`PowerSimulations.jl` supports the workflows to develop simulations by separating the development
of operations models and simulation models.
-- **Operation Models**: Optimization model used to find the solution of an operation problem.
-- **Simulations Models**: Defined the requirements to find solutions to a sequence of operation problems in a way that resembles the procedures followed by operators.
+ - **Operation Models**: Optimization model used to find the solution of an operation problem.
+ - **Simulations Models**: Defined the requirements to find solutions to a sequence of operation problems in a way that resembles the procedures followed by operators.
The most common Simulation Model is the solution of a Unit Commitment and Economic Dispatch sequence of problems. This model is used in commercial Production Cost Modeling tools, but it has a limited scope of analysis.
@@ -20,11 +20,10 @@ The most common Simulation Model is the solution of a Unit Commitment and Econom
`PowerSimulations.jl` documentation and code are organized according to the needs of different users depending on their skillset and requirements. In broad terms there are three categories:
-- **Modeler**: Users that want to solve an operations problem or run a simulation using the existing models in `PowerSimulations.jl`. For instance, answer questions about the change in operation costs in future fuel mixes. Check the formulations library page to choose a modeling strategy that fits your needs.
+ - **Modeler**: Users that want to solve an operations problem or run a simulation using the existing models in `PowerSimulations.jl`. For instance, answer questions about the change in operation costs in future fuel mixes. Check the formulations library page to choose a modeling strategy that fits your needs.
-- **Model Developer**: Users that want to develop custom models and workflows for the simulation of a power system operation. For instance, study the impacts of an stochastic optimization problem over a deterministic.
-
-- **Code Base Developers**: Users that want to add new core functionalities or fix bugs in the core capabilities of `PowerSimulations.jl`.
+ - **Model Developer**: Users that want to develop custom models and workflows for the simulation of a power system operation. For instance, study the impacts of an stochastic optimization problem over a deterministic.
+ - **Code Base Developers**: Users that want to add new core functionalities or fix bugs in the core capabilities of `PowerSimulations.jl`.
`PowerSimulations.jl` is an active project under development, and we welcome your feedback,
suggestions, and bug reports.
@@ -48,7 +47,8 @@ For the current development version, "checkout" this package with
An appropriate optimization solver is required for running PowerSimulations models. Refer to [`JuMP.jl` solver's page](https://jump.dev/JuMP.jl/stable/installation/#Install-a-solver) to select the most appropriate for the application of interest.
-------------
+* * *
+
PowerSystems has been developed as part of the Scalable Integrated Infrastructure Planning
(SIIP) initiative at the U.S. Department of Energy's National Renewable Energy
Laboratory ([NREL](https://www.nrel.gov/)).
diff --git a/docs/src/model_developer_guide/structure_of_operation_problem.md b/docs/src/model_developer_guide/structure_of_operation_problem.md
index 8b793d7948..05b88f204c 100644
--- a/docs/src/model_developer_guide/structure_of_operation_problem.md
+++ b/docs/src/model_developer_guide/structure_of_operation_problem.md
@@ -7,9 +7,11 @@ The first aspect to consider when thinking about developing a model compatible w
and register the constraints, variables and other optimization objects into PowerSimulations.jl's optimization container. Otherwise the features to use your problem in the simulation like the coordination with other problems and post processing won't work.
!!! info
+
The requirements for the simulation of Power Systems operations are more strict than solving an optimization problem once with just `JuMP.jl`. The requirements imposed by `PowerSimulations.jl` to integrate your models in a simulation are designed to help with other complex operations that go beyond `JuMP.jl` scope.
!!! warning
+
All the code in this page is considered "pseudo-code". Copy-paste will likely not work out of the box. You need to develop the internals of the functions correctly for the examples below to work.
## Registering a variable in the model
@@ -18,33 +20,34 @@ To register a variable in the model, the developer must first allocate the conta
optimization container and then populate it. For example, it require start the build function as follows:
!!! info
+
We recommend calling `import PowerSimulations` and defining the constant `CONST PSI = PowerSimulations` to
make it easier to read the code and determine which package is responsible for defining the functions.
```julia
- function PSI.build_model!(model::PSI.DecisionModel{MyCustomModel})
- container = PSI.get_optimization_container(model)
- PSI.set_time_steps!(container, 1:24)
-
- # Create the container for the variable
- variable = PSI.add_variable_container!(
- container,
- PSI.ActivePowerVariable(), # <- This variable is defined in PowerSimulations but the user can define their own
- PSY.ThermalGeneration, # <- Device type for the variable. Can be from PSY or custom defined
- devices_names, # <- First container dimension
- time_steps, # <- Second container dimension
- )
-
- # Iterate over the devices and time to store the JuMP variables into the container.
- for t in time_steps, d in devices
- name = PSY.get_name(d)
- variable[name, t] = JuMP.@variable(get_jump_model(container))
- # It is possible to use PSY getter functions to retrieve data from the generators
- # Any other variable property can be specified inside this loop.
- JuMP.set_upper_bound(variable[name, t], UB_DATA) # <- Optional
- JuMP.set_lower_bound(variable[name, t], LB_DATA) # <- Optional
- end
+function PSI.build_model!(model::PSI.DecisionModel{MyCustomModel})
+ container = PSI.get_optimization_container(model)
+ PSI.set_time_steps!(container, 1:24)
+
+ # Create the container for the variable
+ variable = PSI.add_variable_container!(
+ container,
+ PSI.ActivePowerVariable(), # <- This variable is defined in PowerSimulations but the user can define their own
+ PSY.ThermalGeneration, # <- Device type for the variable. Can be from PSY or custom defined
+ devices_names, # <- First container dimension
+ time_steps, # <- Second container dimension
+ )
+
+ # Iterate over the devices and time to store the JuMP variables into the container.
+ for t in time_steps, d in devices
+ name = PSY.get_name(d)
+ variable[name, t] = JuMP.@variable(get_jump_model(container))
+ # It is possible to use PSY getter functions to retrieve data from the generators
+ # Any other variable property can be specified inside this loop.
+ JuMP.set_upper_bound(variable[name, t], UB_DATA) # <- Optional
+ JuMP.set_lower_bound(variable[name, t], LB_DATA) # <- Optional
+ end
return
- end
+end
```
diff --git a/docs/src/modeler_guide/debugging_infeasible_models.md b/docs/src/modeler_guide/debugging_infeasible_models.md
index cc52f7a2ec..9fd0286736 100644
--- a/docs/src/modeler_guide/debugging_infeasible_models.md
+++ b/docs/src/modeler_guide/debugging_infeasible_models.md
@@ -5,18 +5,19 @@ Getting infeasible solutions to models is a common occurrence in operations simu
## Adding slacks to the model
-One of the most common infeasibility issues observed is due to not enough generation to supply demand, or conversely, excessive fixed (non-curtailable) generation in a low demand scenario.
+One of the most common infeasibility issues observed is due to not enough generation to supply demand, or conversely, excessive fixed (non-curtailable) generation in a low demand scenario.
The recommended solution for any of these cases is adding slack variables to the network model, for example:
```julia
template_uc = ProblemTemplate(
- NetworkModel(
- CopperPlatePowerModel,
- use_slacks=true,
- ),
- )
+ NetworkModel(
+ CopperPlatePowerModel;
+ use_slacks = true,
+ ),
+)
```
+
will add slack variables to the `ActivePowerBalance` expression.
In this case, if the problem is now feasible, the user can check the solution of the variables `SystemBalanceSlackUp` and `SystemBalanceSlackDown`, and if one value is greater than zero, it represents that not enough generation (for Slack Up) or not enough demand (for Slack Down) in the optimization problem.
@@ -24,21 +25,23 @@ In this case, if the problem is now feasible, the user can check the solution of
### Services cases
In many scenarios, certain units are also required to provide reserve requirements, e.g. thermal units mandated to provide up-regulation. In such scenarios, it is also possible to add slack variables, by specifying the service model (`RangeReserve`) for the specific service type (`VariableReserve{ReserveUp}`) as:
+
```julia
set_service_model!(
template_uc,
ServiceModel(
VariableReserve{ReserveUp},
RangeReserve;
- use_slacks=true
+ use_slacks = true,
),
)
```
+
Again, if the problem is now feasible, check the solution of `ReserveRequirementSlack` variable, and if it is larger than zero in a specific time-step, then it is evidence that there is not enough reserve available to satisfy the requirement.
## Getting the infeasibility conflict
-Some solvers allows to identify which constraints and variables are producing the infeasibility, by finding the irreducible infeasible set (IIS), that is the subset of constraints and variable bounds that will become feasible if any single constraint or variable bound is removed.
+Some solvers allows to identify which constraints and variables are producing the infeasibility, by finding the irreducible infeasible set (IIS), that is the subset of constraints and variable bounds that will become feasible if any single constraint or variable bound is removed.
To enable this feature in `PowerSimulations` the keyword argument `calculate_conflict` must be set to `true`, when creating the `DecisionModel`. Note that not all solvers allow the computation of the IIS, but most commercial solvers have this capability. It is also recommended to enable the keyword argument `store_variable_names=true` to help understanding which variables are with infeasibility issues.
@@ -48,11 +51,11 @@ The following code creates a decision model with the `Xpress` optimizer, and ena
DecisionModel(
template_ed,
sys_rts_rt;
- name="ED",
- optimizer=optimizer_with_attributes(Xpress.Optimizer, "MIPRELSTOP" => 1e-2),
- optimizer_solve_log_print=true,
- calculate_conflict=true,
- store_variable_names=true,
+ name = "ED",
+ optimizer = optimizer_with_attributes(Xpress.Optimizer, "MIPRELSTOP" => 1e-2),
+ optimizer_solve_log_print = true,
+ calculate_conflict = true,
+ store_variable_names = true,
)
```
@@ -169,4 +172,4 @@ Error: Constraints participating in conflict basis (IIS)
Note that the IIS clearly identify that the issue is happening at time step 26, and constraints are related with the `CopperPlateBalanceConstraint__System`, with multiple upper bound constraints, for the hybrid system, renewable units and thermal units. This highlights that there may not be enough generation in the system. Indeed, by enabling system slacks, the problem become feasible.
-Finally, the infeasible model is exported in a `json` file that can be loaded directly in `JuMP` to be explored. More information about this is [available here](https://jump.dev/JuMP.jl/stable/moi/submodules/FileFormats/overview/#Read-from-file).
\ No newline at end of file
+Finally, the infeasible model is exported in a `json` file that can be loaded directly in `JuMP` to be explored. More information about this is [available here](https://jump.dev/JuMP.jl/stable/moi/submodules/FileFormats/overview/#Read-from-file).
diff --git a/docs/src/modeler_guide/definitions.md b/docs/src/modeler_guide/definitions.md
index a8effa9ab5..c063dc81f5 100644
--- a/docs/src/modeler_guide/definitions.md
+++ b/docs/src/modeler_guide/definitions.md
@@ -2,14 +2,15 @@
## A
-* *Attributes*: Certain device formulations can be customized by specifying attributes that will include/remove certain variables, expressions and/or constraints. For example, in `StorageSystemsSimulations.jl`, the device formulation of `StorageDispatchWithReserves` can be specified with the following dictionary of attributes:
+ - *Attributes*: Certain device formulations can be customized by specifying attributes that will include/remove certain variables, expressions and/or constraints. For example, in `StorageSystemsSimulations.jl`, the device formulation of `StorageDispatchWithReserves` can be specified with the following dictionary of attributes:
+
```julia
set_device_model!(
template,
DeviceModel(
GenericBattery,
StorageDispatchWithReserves;
- attributes=Dict{String, Any}(
+ attributes = Dict{String, Any}(
"reservation" => false,
"cycling_limits" => false,
"energy_target" => false,
@@ -19,48 +20,48 @@ set_device_model!(
),
)
```
+
Changing the attributes between `true` or `false` can enable/disable multiple aspects of the formulation.
## C
-* *Chronologies:* In `PowerSimulations.jl`, chronologies define where information is flowing. There are two types of chronologies. 1) **inter-stage chronologies** (`InterProblemChronology`) that define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems; and 2) **intra-stage chronologies** (`IntraProblemChronology`) that define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
+ - *Chronologies:* In `PowerSimulations.jl`, chronologies define where information is flowing. There are two types of chronologies. 1) **inter-stage chronologies** (`InterProblemChronology`) that define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems; and 2) **intra-stage chronologies** (`IntraProblemChronology`) that define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
## D
-* *Decision Problem*: A decision problem calculates the desired system operation based on forecasts of uncertain inputs and information about the state of the system. The output of a decision problem represents the policies used to drive the set-points of the system's devices, like generators or switches, and depends on the purpose of the problem. See the [Decision Model Tutorial](@ref op_problem_tutorial) to learn more about solving individual problems.
+ - *Decision Problem*: A decision problem calculates the desired system operation based on forecasts of uncertain inputs and information about the state of the system. The output of a decision problem represents the policies used to drive the set-points of the system's devices, like generators or switches, and depends on the purpose of the problem. See the [Decision Model Tutorial](@ref op_problem_tutorial) to learn more about solving individual problems.
-* *Device Formulation*: The model of a device that is incorporated into a large system optimization models. For instance, the storage device model used inside of a Unit Commitment (UC) problem. A device model needs to follow some requirements to be integrated into operation problems. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro).
+ - *Device Formulation*: The model of a device that is incorporated into a large system optimization models. For instance, the storage device model used inside of a Unit Commitment (UC) problem. A device model needs to follow some requirements to be integrated into operation problems. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro).
## E
-* *Emulation Problem*: An emulation problem is used to mimic the system's behavior subject to an incoming decision and the realization of a forecasted inputs. The solution of the emulator produces outputs representative of the system performance when operating subject the policies resulting from the decision models.
+ - *Emulation Problem*: An emulation problem is used to mimic the system's behavior subject to an incoming decision and the realization of a forecasted inputs. The solution of the emulator produces outputs representative of the system performance when operating subject the policies resulting from the decision models.
## F
-* *FeedForward*: The definition of exactly what information is passed using the defined chronologies is accomplished using FeedForwards. Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables.
+ - *FeedForward*: The definition of exactly what information is passed using the defined chronologies is accomplished using FeedForwards. Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables.
## H
-* *Horizon*: The number of steps in the look-ahead of a decision problem. For instance, a Day-Ahead problem usually has a 48 step horizon. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
+ - *Horizon*: The number of steps in the look-ahead of a decision problem. For instance, a Day-Ahead problem usually has a 48 step horizon. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
## I
-* *Interval*: The amount of time between updates to the decision problem. For instance, Day-Ahead problems usually have a 24-hour intervals and Real-Time problems have 5-minute intervals. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
+ - *Interval*: The amount of time between updates to the decision problem. For instance, Day-Ahead problems usually have a 24-hour intervals and Real-Time problems have 5-minute intervals. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
## R
-* *Resolution*: The amount of time between time steps in a simulation. For instance 1-hour or 5-minutes. In Julia these are defined using the syntax `Hour(1)` and `Minute(5)`. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
+ - *Resolution*: The amount of time between time steps in a simulation. For instance 1-hour or 5-minutes. In Julia these are defined using the syntax `Hour(1)` and `Minute(5)`. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/)
-* *Results vs Realized Results*: In `PowerSimulations.jl` the term *results* is used to refer to the solution of all optimization problems in a *Simulation*. When using `read_variable(results, Variable)` in a `DecisionModel` of a simulation, the output is a dictionary with the values of such variable for every optimization problem solved, while `read_realized_variable(results, Variable)` will return the values of the specified interval and number of steps in the simulation. See the [Read Results page](@ref read_results) for more details.
+ - *Results vs Realized Results*: In `PowerSimulations.jl` the term *results* is used to refer to the solution of all optimization problems in a *Simulation*. When using `read_variable(results, Variable)` in a `DecisionModel` of a simulation, the output is a dictionary with the values of such variable for every optimization problem solved, while `read_realized_variable(results, Variable)` will return the values of the specified interval and number of steps in the simulation. See the [Read Results page](@ref read_results) for more details.
## S
-* *Service Formulation*: The model of a service that is incorporated into a large system optimization models. `Services` (or ancillary services) are models used to ensure that there is necessary support to the power grid from generators to consumers, in order to ensure reliable operation of the system. The most common application for ancillary services are reserves, i.e., generation (or load) that is not currently being used, but can be quickly made available in case of unexpected changes of grid conditions, for example a sudden loss of load or generation. A service model needs to follow some requirements to be integrated into operation problems. For more information about valid `ServiceModel`s and their mathematical representations, check out the [Formulation Library](@ref service_formulations).
-
-* *Simulation*: A simulation is a pre-determined sequence of decision problems in a way that solving it, resembles the solution procedures commonly used by operators. The most common simulation model is the solution of a Unit Commitment and Economic Dispatch sequence of problems.
+ - *Service Formulation*: The model of a service that is incorporated into a large system optimization models. `Services` (or ancillary services) are models used to ensure that there is necessary support to the power grid from generators to consumers, in order to ensure reliable operation of the system. The most common application for ancillary services are reserves, i.e., generation (or load) that is not currently being used, but can be quickly made available in case of unexpected changes of grid conditions, for example a sudden loss of load or generation. A service model needs to follow some requirements to be integrated into operation problems. For more information about valid `ServiceModel`s and their mathematical representations, check out the [Formulation Library](@ref service_formulations).
-* *Solver*: A solver is a software package that incorporates algorithms for finding solutions to one or more classes of optimization problem. For example, FICO Xpress is a commercial optimization solver for linear programming (LP), convex quadratic programming (QP) problems, convex quadratically constrained quadratic programming (QCQP), second-order cone programming (SOCP) and their mixed integer counterparts. **A solver is required to be specified** in order to solve any computer optimization problem.
+ - *Simulation*: A simulation is a pre-determined sequence of decision problems in a way that solving it, resembles the solution procedures commonly used by operators. The most common simulation model is the solution of a Unit Commitment and Economic Dispatch sequence of problems.
+ - *Solver*: A solver is a software package that incorporates algorithms for finding solutions to one or more classes of optimization problem. For example, FICO Xpress is a commercial optimization solver for linear programming (LP), convex quadratic programming (QP) problems, convex quadratically constrained quadratic programming (QCQP), second-order cone programming (SOCP) and their mixed integer counterparts. **A solver is required to be specified** in order to solve any computer optimization problem.
## T
-* *Template*: A `ProblemTemplate` is just a collection of `DeviceModel`s that allows the user to specify the formulations of each set of devices (by device type) independently so that the modeler can adjust the level of detail according to the question of interest and the available data. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro).
\ No newline at end of file
+ - *Template*: A `ProblemTemplate` is just a collection of `DeviceModel`s that allows the user to specify the formulations of each set of devices (by device type) independently so that the modeler can adjust the level of detail according to the question of interest and the available data. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro).
diff --git a/docs/src/modeler_guide/logging.md b/docs/src/modeler_guide/logging.md
index dcf2f426b0..951fe1f593 100644
--- a/docs/src/modeler_guide/logging.md
+++ b/docs/src/modeler_guide/logging.md
@@ -21,10 +21,10 @@ function provided by PowerSimulations. This example will log messages of level
```julia
import Logging
using PowerSimulations
-logger = configure_logging(
+logger = configure_logging(;
console_level = Logging.Error,
file_level = Logging.Info,
- filename = "power-simulations.log"
+ filename = "power-simulations.log",
)
```
@@ -37,7 +37,7 @@ You can configure the logging level used by the simulation logger when you call
import Logging
using PowerSimulations
simulation = Simulation(...)
-build!(simulation, console_level = Logging.Info, file_level = Logging.Debug)
+build!(simulation; console_level = Logging.Info, file_level = Logging.Debug)
```
The log file will be located at `///logs/simulation.log`.
diff --git a/docs/src/modeler_guide/modeling_faq.md b/docs/src/modeler_guide/modeling_faq.md
index fa02e3ec1a..0f7899621a 100644
--- a/docs/src/modeler_guide/modeling_faq.md
+++ b/docs/src/modeler_guide/modeling_faq.md
@@ -1,7 +1,9 @@
# Modeling FAQ
!!! question "How do I reduce the amount of print on my REPL?"
+
The print to the REPL is controlled with the logging. Check the [Logging](@ref) documentation page to see how to reduce the print out
!!! question "How do I print the optimizer logs to see the solution process?"
+
When specifying the `DecisionModel` or `EmulationModel` pass the keyword `print_optimizer_log = true`
diff --git a/docs/src/modeler_guide/parallel_simulations.md b/docs/src/modeler_guide/parallel_simulations.md
index ebd4d8f9c8..93e2d3e00a 100644
--- a/docs/src/modeler_guide/parallel_simulations.md
+++ b/docs/src/modeler_guide/parallel_simulations.md
@@ -2,8 +2,8 @@
This section contains instructions to:
-- [Run a Simulation in Parallel on a local computer](@ref)
-- [Run a Simulation in Parallel on an HPC](@ref)
+ - [Run a Simulation in Parallel on a local computer](@ref)
+ - [Run a Simulation in Parallel on an HPC](@ref)
## Run a Simulation in Parallel on a local computer
@@ -15,11 +15,11 @@ and then join the results.
Create a Julia script to build and run simulations. It must meet the requirements below.
A full example is in the PowerSimulations repository in `test/run_partitioned_simulation.jl`.
-- Call `using PowerSimulations`.
+ - Call `using PowerSimulations`.
-- Implement a build function that matches the signature below.
- It must construct a `Simulation`, call `build!`, and then return the `Simulation` instance.
- It must throw an exception if the build fails.
+ - Implement a build function that matches the signature below.
+ It must construct a `Simulation`, call `build!`, and then return the `Simulation` instance.
+ It must throw an exception if the build fails.
```
function build_simulation(
@@ -46,8 +46,8 @@ Here is example code to construct the `Simulation` with these parameters:
end
```
-- Implement an execute function that matches the signature below. It must throw an exception
- if the execute fails.
+ - Implement an execute function that matches the signature below. It must throw an exception
+ if the execute fails.
```
function execute_simulation(sim, args...; kwargs...)
@@ -99,15 +99,15 @@ to a new Julia package.
### Setup
-1. Create a conda environment and install the Python package `NREL-jade`:
- https://nrel.github.io/jade/installation.html. The rest of this page assumes that
- the environment is called `jade`.
-2. Activate the environment with `conda activate jade`.
-3. Locate the path to that conda environment. It will likely be `~/.conda-envs/jade` or
- `~/.conda/envs/jade`.
-4. Load the Julia environment that you use to run simulations. Add the packages `Conda` and
- `PyCall`.
-5. Setup Conda to use the existing `jade` environment by running these commands:
+ 1. Create a conda environment and install the Python package `NREL-jade`:
+ https://nrel.github.io/jade/installation.html. The rest of this page assumes that
+ the environment is called `jade`.
+ 2. Activate the environment with `conda activate jade`.
+ 3. Locate the path to that conda environment. It will likely be `~/.conda-envs/jade` or
+ `~/.conda/envs/jade`.
+ 4. Load the Julia environment that you use to run simulations. Add the packages `Conda` and
+ `PyCall`.
+ 5. Setup Conda to use the existing `jade` environment by running these commands:
```
julia> run(`conda create -n conda_jl python conda`)
@@ -115,9 +115,9 @@ julia> ENV["CONDA_JL_HOME"] = joinpath(ENV["HOME"], ".conda-envs", "jade") # ch
pkg> build Conda
```
-6. Copy the code below into a Julia file called `configure_parallel_simulation.jl`.
- This is an interface to Jade through PyCall. It will be used to create a Jade configuration.
- (It may eventually be moved to a separate package.)
+ 6. Copy the code below into a Julia file called `configure_parallel_simulation.jl`.
+ This is an interface to Jade through PyCall. It will be used to create a Jade configuration.
+ (It may eventually be moved to a separate package.)
```
function configure_parallel_simulation(
@@ -154,14 +154,14 @@ function configure_parallel_simulation(
end
```
-7. Create a Julia script to build and run simulations. It must meet the requirements below.
- A full example is in the PowerSimulations repository in `test/run_partitioned_simulation.jl`.
+ 7. Create a Julia script to build and run simulations. It must meet the requirements below.
+ A full example is in the PowerSimulations repository in `test/run_partitioned_simulation.jl`.
-- Call `using PowerSimulations`.
+ - Call `using PowerSimulations`.
-- Implement a build function that matches the signature below.
- It must construct a `Simulation`, call `build!`, and then return the `Simulation` instance.
- It must throw an exception if the build fails.
+ - Implement a build function that matches the signature below.
+ It must construct a `Simulation`, call `build!`, and then return the `Simulation` instance.
+ It must throw an exception if the build fails.
```
function build_simulation(
@@ -188,8 +188,8 @@ Here is example code to construct the `Simulation` with these parameters:
end
```
-- Implement an execute function that matches the signature below. It must throw an exception
- if the execute fails.
+ - Implement an execute function that matches the signature below. It must throw an exception
+ if the execute fails.
```
function execute_simulation(sim, args...; kwargs...)
@@ -200,8 +200,8 @@ function execute_simulation(sim, args...; kwargs...)
end
```
-- Make the script runnable as a CLI command by including the following code at the bottom of the
-file.
+ - Make the script runnable as a CLI command by including the following code at the bottom of the
+ file.
```
function main()
@@ -215,11 +215,11 @@ end
### Execution
-1. Create a Jade configuration that defines the partitioned simulation jobs. Load your Julia
- environment.
-
- This example splits a year-long simulation into weekly partitions for a total of 53 individual
- jobs.
+ 1. Create a Jade configuration that defines the partitioned simulation jobs. Load your Julia
+ environment.
+
+ This example splits a year-long simulation into weekly partitions for a total of 53 individual
+ jobs.
```
julia> include("configure_parallel_simulation.jl")
@@ -238,26 +238,26 @@ Created Jade configuration in config.json. Run 'jade submit-jobs [options] confi
Exit Julia.
-2. View the configuration for accuracy.
+ 2. View the configuration for accuracy.
```
$ jade config show config.json
```
-3. Start an interactive session on a debug node. *Do not submit the jobs on a login node!* The submission
- step will run a full build of the simulation and that may consume too many CPU and memory resources
- for the login node.
+ 3. Start an interactive session on a debug node. *Do not submit the jobs on a login node!* The submission
+ step will run a full build of the simulation and that may consume too many CPU and memory resources
+ for the login node.
```
$ salloc -t 01:00:00 -N1 --account= --partition=debug
```
-4. Follow the instructions at https://nrel.github.io/jade/tutorial.html to submit the jobs.
- The example below will configure Jade to run each partition on its own compute node. Depending on
- the compute and memory constraints of your simulation, you may be able to pack more jobs on each
- node.
-
- Adjust the walltime as necessary.
+ 4. Follow the instructions at https://nrel.github.io/jade/tutorial.html to submit the jobs.
+ The example below will configure Jade to run each partition on its own compute node. Depending on
+ the compute and memory constraints of your simulation, you may be able to pack more jobs on each
+ node.
+
+ Adjust the walltime as necessary.
```
$ jade config hpc -c hpc_config.toml -t slurm --walltime=04:00:00 -a
@@ -273,8 +273,8 @@ $ jade submit-jobs config.json --per-node-batch-size=1 -o output --resource-moni
Jade will create HTML plots of the resource utilization in `output/stats`. You may be able to customize
`--per-node-batch-size` and `--num-processes` to finish the simulations more quickly.
-5. Jade will run a final command to join the simulation partitions into one unified file. You can load the
- results as you normally would.
+ 5. Jade will run a final command to join the simulation partitions into one unified file. You can load the
+ results as you normally would.
```
julia> results = SimulationResults("/job-outputs/")
diff --git a/docs/src/modeler_guide/problem_templates.md b/docs/src/modeler_guide/problem_templates.md
index 09addbde94..94fa0cc46a 100644
--- a/docs/src/modeler_guide/problem_templates.md
+++ b/docs/src/modeler_guide/problem_templates.md
@@ -22,7 +22,7 @@ set_service_model!(template, VariableReserve{ReserveUp}, RangeReserve)
`PowerSimulations.jl` provides default templates for common operation problems. You can retrieve a default template and modify it according
to your requirements. Currently supported default templates are:
-```@docs
+```@docs; canonical=false
template_economic_dispatch
```
@@ -31,7 +31,7 @@ using PowerSimulations #hide
template_economic_dispatch()
```
-```@docs
+```@docs; canonical=false
template_unit_commitment
```
diff --git a/docs/src/modeler_guide/psi_structure.md b/docs/src/modeler_guide/psi_structure.md
index 65a5356cca..03b6fe6a39 100644
--- a/docs/src/modeler_guide/psi_structure.md
+++ b/docs/src/modeler_guide/psi_structure.md
@@ -2,10 +2,11 @@
PowerSimulations enables the simulation of a sequence of power systems optimization problems and provides user control over each aspect of the simulation configuration. Specifically:
-- mathematical formulations can be selected for each component with [`DeviceModel`](@ref) and [`ServiceModel`](@ref)
-- a problem can be defined by creating model entries in a [Operations `ProblemTemplate`s](@ref op_problem_template)
-- models ([`DecisionModel`](@ref) or [`EmulationModel`](@ref)) can be built by applying a `ProblemTemplate` to a `System` and can be executed/solved in isolation or as part of a [`Simulation`](@ref)
-- [`Simulation`](@ref)s can be defined and executed by sequencing one or more models and defining how and when data flows between models.
+ - mathematical formulations can be selected for each component with [`DeviceModel`](@ref) and [`ServiceModel`](@ref)
+ - a problem can be defined by creating model entries in a [Operations `ProblemTemplate`s](@ref op_problem_template)
+ - models ([`DecisionModel`](@ref) or [`EmulationModel`](@ref)) can be built by applying a `ProblemTemplate` to a `System` and can be executed/solved in isolation or as part of a [`Simulation`](@ref)
+ - [`Simulation`](@ref)s can be defined and executed by sequencing one or more models and defining how and when data flows between models.
!!! question "What is the difference between a Model and a Problem?"
+
A "Problem" is an abstract mathematical description of how to represent power system behavior, whereas a "Model" is a concrete representation of a "Problem" applied to a dataset. I.e. once a Problem is populated with data describing all the loads, generators, lines, etc., it becomes a Model.
diff --git a/docs/src/modeler_guide/read_results.md b/docs/src/modeler_guide/read_results.md
index d1292f7b85..ba3ef4103d 100644
--- a/docs/src/modeler_guide/read_results.md
+++ b/docs/src/modeler_guide/read_results.md
@@ -8,7 +8,7 @@ Once a `DecisionModel` is solved, results are accessed using `OptimizationProble
```julia
# The DecisionModel is already constructed
-build!(model, output_dir = mktempdir())
+build!(model; output_dir = mktempdir())
solve!(model)
results = OptimizationProblemResults(model)
@@ -90,7 +90,8 @@ Then the following code can be used to read results:
thermal_active_power = read_variable(results, "ActivePowerVariable__ThermalStandard")
# Read max active power parameter of RenewableDispatch
-renewable_param = read_parameter(results, "ActivePowerTimeSeriesParameter__RenewableDispatch")
+renewable_param =
+ read_parameter(results, "ActivePowerTimeSeriesParameter__RenewableDispatch")
# Read cost expressions of ThermalStandard units
cost_thermal = read_expression(results, "ProductionCostExpression__ThermalStandard")
@@ -109,7 +110,7 @@ Results will be in the form of DataFrames that can be easily explored.
```julia
# The Simulation is already constructed
build!(sim)
-execute!(sim; enable_progress_bar=true)
+execute!(sim; enable_progress_bar = true)
results_sim = SimulationResults(sim)
```
@@ -151,7 +152,9 @@ In this case, using `read_variable` (or read expression, parameter or dual), wil
```julia
thermal_active_power = read_variable(results_uc, "ActivePowerVariable__ThermalStandard")
```
+
will return:
+
```
DataStructures.SortedDict{Any, Any, Base.Order.ForwardOrdering} with 8 entries:
DateTime("2020-10-02T00:00:00") => 72×54 DataFrame…
@@ -163,6 +166,7 @@ DataStructures.SortedDict{Any, Any, Base.Order.ForwardOrdering} with 8 entries:
DateTime("2020-10-08T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-09T00:00:00") => 72×54 DataFrame…
```
+
That is, a sorted dictionary for each simulation step, using as a key the initial timestamp for that specific simulation step.
Note that in this case, each DataFrame, has a dimension of ``72 \times 54``, since the horizon is 72 hours (number of rows), but the interval is only 24 hours. Indeed, note the initial timestamp of each simulation step is the beginning of each day, i.e. 24 hours. Finally, there 54 columns, since this example system has 53 `ThermalStandard` units (plus 1 column for the timestamps). The user is free to explore the solution of any simulation step as needed.
@@ -172,10 +176,14 @@ Note that in this case, each DataFrame, has a dimension of ``72 \times 54``, sin
Using `read_realized_variable` (or read realized expression, parameter or dual), will return the DataFrame of the realized solution of any specific variable. That is, it will concatenate the corresponding simulation step with the specified interval of that step, to construct a single DataFrame with the "realized solution" of the entire simulation.
For example, the code:
+
```julia
-th_realized_power = read_realized_variable(results_uc, "ActivePowerVariable__ThermalStandard")
+th_realized_power =
+ read_realized_variable(results_uc, "ActivePowerVariable__ThermalStandard")
```
+
will return:
+
```raw
92×54 DataFrame
Row │ DateTime 322_CT_6 321_CC_1 202_STEAM_3 223_CT_4 123_STEAM_2 213_CT_1 223_CT_6 313_CC_1 101_STEAM_3 123_C ⋯
@@ -196,6 +204,5 @@ will return:
192 │ 2020-10-09T23:00:00 0.0 0.0 60.6667 0.0 117.81 0.0 0.0 0.0 76.0 0.0
44 columns and 180 rows omitted
```
-In this case, the 8 simulation steps of 24 hours (192 hours), in a single DataFrame, to enable easy exploration of the realized results for the user.
-
+In this case, the 8 simulation steps of 24 hours (192 hours), in a single DataFrame, to enable easy exploration of the realized results for the user.
diff --git a/docs/src/modeler_guide/running_a_simulation.md b/docs/src/modeler_guide/running_a_simulation.md
index 80df408917..f01b481bbe 100644
--- a/docs/src/modeler_guide/running_a_simulation.md
+++ b/docs/src/modeler_guide/running_a_simulation.md
@@ -1,6 +1,7 @@
# [Simulation](@id running_a_simulation)
!!! tip "Always try to solve the operations problem first before putting together the simulation"
+
It is not uncommon that when trying to solve a complex simulation the resulting models are infeasible. This situation can be the result of many factors like the input data, the incorrect specification of the initial conditions for models with time dependencies or a poorly specified model. Therefore, it's highly recommended to run and analyze an [Operations Problems](@ref psi_structure) that reflect the problems that will be included in a simulation prior to executing a simulation.
Check out the [Operations Problem Tutorial](@ref op_problem_tutorial)
@@ -16,10 +17,10 @@ The creation of a FeedForward requires at least to specify the `component_type`
The following code specify the creation of semi-continuous range constraints on the `ActivePowerVariable` based on the solution of the commitment variable `OnVariable` for all `ThermalStandard` units.
```julia
-SemiContinuousFeedforward(
- component_type=ThermalStandard,
- source=OnVariable,
- affected_values=[ActivePowerVariable],
+SemiContinuousFeedforward(;
+ component_type = ThermalStandard,
+ source = OnVariable,
+ affected_values = [ActivePowerVariable],
)
```
@@ -28,14 +29,13 @@ SemiContinuousFeedforward(
In PowerSimulations, chronologies define where information is flowing. There are two types
of chronologies.
-- inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems
-- intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
+ - inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems
+ - intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
## Sequencing
In a typical simulation pipeline, we want to connect daily (24-hours) day-ahead unit commitment problems, with multiple economic dispatch problems. Usually, our day-ahead unit commitment problem will have an hourly (1-hour) resolution, while the economic dispatch will have a 5-minute resolution.
-
Depending on your problem, it is common to use a 2-day look-ahead for unit commitment problems, so in this case, the Day-Ahead problem will have: resolution = Hour(1) with interval = Hour(24) and horizon = Hour(48). In the case of the economic dispatch problem, it is common to use a look-ahead of two hours. Thus, the Real-Time problem will have: resolution = Minute(5), with interval = Minute(5) (we only store the first operating point) and horizon = 24 (24 time steps of 5 minutes are 120 minutes, that is 2 hours).
## Simulation Setup
@@ -53,8 +53,8 @@ The following code creates the entire simulation pipeline:
decision_model_uc = DecisionModel(
template_uc,
sys_da;
- name="UC",
- optimizer=optimizer_with_attributes(
+ name = "UC",
+ optimizer = optimizer_with_attributes(
Xpress.Optimizer,
"MIPRELSTOP" => 1e-1,
),
@@ -64,55 +64,55 @@ decision_model_uc = DecisionModel(
decision_model_ed = DecisionModel(
template_ed,
sys_rt;
- name="ED",
- optimizer=optimizer_with_attributes(Xpress.Optimizer),
+ name = "ED",
+ optimizer = optimizer_with_attributes(Xpress.Optimizer),
)
# Specify the SimulationModels using a Vector of decision_models: UC, ED
-sim_models = SimulationModels(
- decision_models=[
+sim_models = SimulationModels(;
+ decision_models = [
decision_model_uc,
decision_model_ed,
],
)
# Create the FeedForwards:
-semi_ff = SemiContinuousFeedforward(
- component_type=ThermalStandard,
- source=OnVariable,
- affected_values=[ActivePowerVariable],
+semi_ff = SemiContinuousFeedforward(;
+ component_type = ThermalStandard,
+ source = OnVariable,
+ affected_values = [ActivePowerVariable],
)
# Specify the sequencing:
-sim_sequence = SimulationSequence(
+sim_sequence = SimulationSequence(;
# Specify the vector of decision models: sim_models
- models=sim_models,
+ models = sim_models,
# Specify a Dict of feedforwards on which the FF applies
# based on the DecisionModel name, in this case "ED"
- feedforwards=Dict(
+ feedforwards = Dict(
"ED" => [semi_ff],
),
# Specify the chronology, in this case inter-stage
- ini_cond_chronology=InterProblemChronology(),
+ ini_cond_chronology = InterProblemChronology(),
)
# Construct the simulation:
-sim = Simulation(
- name="compact_sim",
- steps=10, # 10 days
- models=sim_models,
- sequence=sim_sequence,
+sim = Simulation(;
+ name = "compact_sim",
+ steps = 10, # 10 days
+ models = sim_models,
+ sequence = sim_sequence,
# Specify the start_time as a DateTime: e.g. DateTime("2020-10-01T00:00:00")
- initial_time=start_time,
+ initial_time = start_time,
# Specify a temporary folder to avoid storing logs if not needed
- simulation_folder=mktempdir(cleanup=true),
+ simulation_folder = mktempdir(; cleanup = true),
)
# Build the decision models and simulation setup
build!(sim)
# Execute the simulation using the Optimizer specified in each DecisionModel
-execute!(sim, enable_progress_bar=true)
+execute!(sim; enable_progress_bar = true)
```
Check the [PCM tutorial](@ref pcm_tutorial) for a more detailed tutorial on executing a simulation in a production cost modeling (PCM) environment.
diff --git a/docs/src/modeler_guide/simulation_recorder.md b/docs/src/modeler_guide/simulation_recorder.md
index 824274e36e..ae104968de 100644
--- a/docs/src/modeler_guide/simulation_recorder.md
+++ b/docs/src/modeler_guide/simulation_recorder.md
@@ -5,7 +5,7 @@ during a simulation. These events can be post-processed to help debug problems.
By default only SimulationStepEvent and ProblemExecutionEvent are recorded. Here is an example.
-Suppose a simulation is run in the directory ./output.
+Suppose a simulation is run in the directory `./output`.
Assume that setup commands have been run:
diff --git a/docs/src/modeler_guide/tips_and_tricks.md b/docs/src/modeler_guide/tips_and_tricks.md
index 8a40cb3c80..0157bdab87 100644
--- a/docs/src/modeler_guide/tips_and_tricks.md
+++ b/docs/src/modeler_guide/tips_and_tricks.md
@@ -1,3 +1 @@
# Tips and tricks
-
-
diff --git a/docs/src/quick_start_guide.md b/docs/src/quick_start_guide.md
index 20056949e6..45374486b8 100644
--- a/docs/src/quick_start_guide.md
+++ b/docs/src/quick_start_guide.md
@@ -1,11 +1,12 @@
# Quick Start Guide
-* **Julia:** If this is your first time using Julia visit our [Introduction to Julia](https://nrel-Sienna.github.io/SIIP-Tutorial/fundamentals/introduction-to-julia/) and the official [Getting started with Julia](https://julialang.org/learning/).
-* **Package Installation:** If you want to install packages check the [Package Manager](https://pkgdocs.julialang.org/v1/environments/) instructions, or you can refer to the [PowerSimulations installation instructions](@ref Installation).
-* **PowerSystems:** [PowerSystems.jl](https://github.com/nrel-Sienna/PowerSystems.jl) manages the data and is a fundamental dependency of PowerSimulations.jl. Check the [PowerSystems.jl Basics Tutorial](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/basics/) and [PowerSystems.jl documentation](https://nrel-Sienna.github.io/PowerSystems.jl/stable/) to understand how the inputs to the models are organized.
-* **Dataset Library:** If you don't have a data set to start using `PowerSimulations.jl` check the test systems provided in [`PowerSystemCaseBuilder.jl`](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/powersystembuilder/)
+ - **Julia:** If this is your first time using Julia visit our [Introduction to Julia](https://nrel-Sienna.github.io/SIIP-Tutorial/fundamentals/introduction-to-julia/) and the official [Getting started with Julia](https://julialang.org/learning/).
+ - **Package Installation:** If you want to install packages check the [Package Manager](https://pkgdocs.julialang.org/v1/environments/) instructions, or you can refer to the [PowerSimulations installation instructions](@ref Installation).
+ - **PowerSystems:** [PowerSystems.jl](https://github.com/nrel-Sienna/PowerSystems.jl) manages the data and is a fundamental dependency of PowerSimulations.jl. Check the [PowerSystems.jl Basics Tutorial](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/basics/) and [PowerSystems.jl documentation](https://nrel-Sienna.github.io/PowerSystems.jl/stable/) to understand how the inputs to the models are organized.
+ - **Dataset Library:** If you don't have a data set to start using `PowerSimulations.jl` check the test systems provided in [`PowerSystemCaseBuilder.jl`](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/powersystembuilder/)
!!! tip
+
If you need to develop a dataset for a simulation check the [PowerSystems.jl Tutorials](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/basics/) on how to parse data and attach time series
-* **Tutorial:** If you are eager to run your first simulation visit the Solve a Day Ahead Market Scheduling Problem using PowerSimulations.jl tutorial
+ - **Tutorial:** If you are eager to run your first simulation visit the Solve a Day Ahead Market Scheduling Problem using PowerSimulations.jl tutorial
diff --git a/docs/src/tutorials/adding_new_problem_model.md b/docs/src/tutorials/adding_new_problem_model.md
index 6f420817de..1b0f25b49e 100644
--- a/docs/src/tutorials/adding_new_problem_model.md
+++ b/docs/src/tutorials/adding_new_problem_model.md
@@ -3,25 +3,25 @@
This tutorial will show how to create a custom decision problem model. These cases are the ones
where the user want to solve a fully specified problem. Some examples of custom decision models include:
-- Solving a custom Security Constrained Unit Commitment Problem
-- Solving a market agent utility maximization Problem. See examples of this functionality in HybridSystemsSimulations.jl
+ - Solving a custom Security Constrained Unit Commitment Problem
+ - Solving a market agent utility maximization Problem. See examples of this functionality in HybridSystemsSimulations.jl
The tutorial follows the usual steps for operational model building. First, build the decision model in isolation and second, integrate it into a simulation. In most cases there will be more than one way of achieving
the same objective when it comes to implementing the model. This guide shows a general set of steps and requirements but it is by no means an exhaustive and detailed guide on developing custom decision models.
!!! warning
+
All the code in this tutorial is considered "pseudo-code". Copy-paste will likely not work out of the box. You need to develop the internals of the functions correctly for the examples below to work.
## General Rules
-1. As a general rule you need to understand Julia's terminology such as multiple dispatch, parametric structs and method overloading, among others. Developing custom models for an operational simulation is a highly technical task and requires skilled development. This tutorial also requires good understanding of PowerSystems.jl data structures and features which are covered in the tutorials section of PowerSystems.jl documentation.
-Finally, developing a custom model decision model that will employ an optimization model under the hood requires understanding JuMP.jl.
+ 1. As a general rule you need to understand Julia's terminology such as multiple dispatch, parametric structs and method overloading, among others. Developing custom models for an operational simulation is a highly technical task and requires skilled development. This tutorial also requires good understanding of PowerSystems.jl data structures and features which are covered in the tutorials section of PowerSystems.jl documentation.
+ Finally, developing a custom model decision model that will employ an optimization model under the hood requires understanding JuMP.jl.
-2. Need to employ [anonymous constraints and variables in JuMP](https://jump.dev/JuMP.jl/stable/manual/variables/#anonymous_variables)
-and register the constraints, variables and other optimization objects into PowerSimulations.jl's optimization container. Otherwise the
-features to use your problem in the simulation like the coordination with other problems and post processing won't work. More on this in the section [How to develop your `build_model!` function](@ref) below.
-
-3. Implement the required methods for your custom decision models. In some cases it will be possible to re-use some of the other methods that exist in PowerSimulations to make life easier for variable addition and constraint creation but this is not required.
+ 2. Need to employ [anonymous constraints and variables in JuMP](https://jump.dev/JuMP.jl/stable/manual/variables/#anonymous_variables)
+ and register the constraints, variables and other optimization objects into PowerSimulations.jl's optimization container. Otherwise the
+ features to use your problem in the simulation like the coordination with other problems and post processing won't work. More on this in the section [How to develop your `build_model!` function](@ref) below.
+ 3. Implement the required methods for your custom decision models. In some cases it will be possible to re-use some of the other methods that exist in PowerSimulations to make life easier for variable addition and constraint creation but this is not required.
## Decision Problem
@@ -49,22 +49,22 @@ my_model = DecisionModel{MyCustomDecisionProblem}(
sys;
name = "MyModel",
optimizer = optimizer_with_attributes(HiGHS.Optimizer),
- optimizer_solve_log_print = true,
+ optimizer_solve_log_print = true,
)
```
#### Mandatory Method Implementations
-1. `build_model!`: This method build the `JuMP` optimization model.
+ 1. `build_model!`: This method build the `JuMP` optimization model.
#### Optional Method Overloads
These methods can be defined optionally for your problem. By default for problems subtyped from `DecisionProblem` these checks are not executed. If the problems are subtyped from `DefaultDecisionProblem` these checks are always conducted with PowerSimulations defaults and require compliance with those defaults to pass. In any case, these can be overloaded when necessary depending on the problem requirements.
-1. `validate_template`
-2. `validate_time_series!`
-3. `reset!`
-4. `solve_impl!`
+ 1. `validate_template`
+ 2. `validate_time_series!`
+ 3. `reset!`
+ 4. `solve_impl!`
### How to develop your `build_model!` function
@@ -74,41 +74,42 @@ To register a variable in the model, the developer must first allocate the conta
optimization container and then populate it. For example, it require start the build function as follows:
!!! info
+
We recommend calling `import PowerSimulations` and defining the constant `CONST PSI = PowerSimulations` to
make it easier to read the code and determine which package is responsible for defining the functions.
```julia
- function PSI.build_model!(model::PSI.DecisionModel{MyCustomDecisionProblem})
- container = PSI.get_optimization_container(model)
- time_steps = 1:24
- PSI.set_time_steps!(container, time_steps)
- system = PSI.get_system(model)
-
- thermal_gens = PSY.get_components(PSY.ThermalStandard, system)
- thermal_gens_names = PSY.get_name.(thermal_gens)
-
- # Create the container for the variable
- variable = PSI.add_variable_container!(
- container,
- PSI.ActivePowerVariable(), # <- This variable is defined in PowerSimulations but the user can define their own
- PSY.ThermalGeneration, # <- Device type for the variable. Can be from PSY or custom defined
- thermal_gens_names, # <- First container dimension
- time_steps, # <- Second container dimension
- )
-
- # Iterate over the devices and time to store the JuMP variables into the container.
- for t in time_steps, d in thermal_gens_names
- name = PSY.get_name(d)
- variable[name, t] = JuMP.@variable(get_jump_model(container))
- # It is possible to use PSY getter functions to retrieve data from the generators
- JuMP.set_upper_bound(variable[name, t], UB_DATA) # <- Optional
- JuMP.set_lower_bound(variable[name, t], LB_DATA) # <- Optional
- end
-
- # Add More Variables.....
-
- return
+function PSI.build_model!(model::PSI.DecisionModel{MyCustomDecisionProblem})
+ container = PSI.get_optimization_container(model)
+ time_steps = 1:24
+ PSI.set_time_steps!(container, time_steps)
+ system = PSI.get_system(model)
+
+ thermal_gens = PSY.get_components(PSY.ThermalStandard, system)
+ thermal_gens_names = PSY.get_name.(thermal_gens)
+
+ # Create the container for the variable
+ variable = PSI.add_variable_container!(
+ container,
+ PSI.ActivePowerVariable(), # <- This variable is defined in PowerSimulations but the user can define their own
+ PSY.ThermalGeneration, # <- Device type for the variable. Can be from PSY or custom defined
+ thermal_gens_names, # <- First container dimension
+ time_steps, # <- Second container dimension
+ )
+
+ # Iterate over the devices and time to store the JuMP variables into the container.
+ for t in time_steps, d in thermal_gens_names
+ name = PSY.get_name(d)
+ variable[name, t] = JuMP.@variable(get_jump_model(container))
+ # It is possible to use PSY getter functions to retrieve data from the generators
+ JuMP.set_upper_bound(variable[name, t], UB_DATA) # <- Optional
+ JuMP.set_lower_bound(variable[name, t], LB_DATA) # <- Optional
end
+
+ # Add More Variables.....
+
+ return
+end
```
#### Registering a constraint in the model
@@ -117,44 +118,44 @@ A similar pattern is used to add constraints to the model, in this example the f
to avoid creating unnecessary duplicate constraint types. For instance to reflect upper_bound and lower_bound or upwards and downwards constraints. Meta can take any string value except for the `_` character.
```julia
- function PSI.build_model!(model::PSI.DecisionModel{MyCustomDecisionProblem})
- container = PSI.get_optimization_container(model)
- time_steps = 1:24
- PSI.set_time_steps!(container, time_steps)
- system = PSI.get_system(model)
-
- # VARIABLE ADDITION CODE
-
- # Constraint additions
- con_ub = PSI.add_constraints_container!(
- container,
- PSI.RangeLimitConstraint(), # <- Constraint Type defined by PSI or your own
- PSY.ThermalGeneration, # <- Device type for variable. Can be PSY or custom
- thermal_gens_names, # <- First container dimension
- time_steps; # <- Second container dimension
- meta = "ub" # <- meta allows to reuse a constraint definition for similar constraints. It only requires to be a string
- )
-
- con_lb = PSI.add_constraints_container!(
- container,
- PSI.RangeLimitConstraint(),
- PSY.ThermalGeneration,
- thermal_gens_names, # <- First container dimension
- time_steps; # <- Second container dimension
- meta = "lb" # <- meta allows to reuse a constraint definition for similar constraints. It only requires to be a string
- )
-
- # Retrieve a relevant variable from the container if not defined in
- variable = PSI.get_variable(container, PSI.ActivePowerVariable(), PSY.ThermalGeneration)
- for device in devices, t in time_steps
- ci_name = PSY.get_name(device)
- limits = get_min_max_limits(device) # depends on constraint type and formulation type
- con_ub[ci_name, t] =
- JuMP.@constraint(get_jump_model(container), variable[ci_name, t] >= limits.min)
- con_lb[ci_name, t] =
- JuMP.@constraint(get_jump_model(container), variable[ci_name, t] >= limits.min)
- end
-
- return
+function PSI.build_model!(model::PSI.DecisionModel{MyCustomDecisionProblem})
+ container = PSI.get_optimization_container(model)
+ time_steps = 1:24
+ PSI.set_time_steps!(container, time_steps)
+ system = PSI.get_system(model)
+
+ # VARIABLE ADDITION CODE
+
+ # Constraint additions
+ con_ub = PSI.add_constraints_container!(
+ container,
+ PSI.RangeLimitConstraint(), # <- Constraint Type defined by PSI or your own
+ PSY.ThermalGeneration, # <- Device type for variable. Can be PSY or custom
+ thermal_gens_names, # <- First container dimension
+ time_steps; # <- Second container dimension
+ meta = "ub", # <- meta allows to reuse a constraint definition for similar constraints. It only requires to be a string
+ )
+
+ con_lb = PSI.add_constraints_container!(
+ container,
+ PSI.RangeLimitConstraint(),
+ PSY.ThermalGeneration,
+ thermal_gens_names, # <- First container dimension
+ time_steps; # <- Second container dimension
+ meta = "lb", # <- meta allows to reuse a constraint definition for similar constraints. It only requires to be a string
+ )
+
+ # Retrieve a relevant variable from the container if not defined in
+ variable = PSI.get_variable(container, PSI.ActivePowerVariable(), PSY.ThermalGeneration)
+ for device in devices, t in time_steps
+ ci_name = PSY.get_name(device)
+ limits = get_min_max_limits(device) # depends on constraint type and formulation type
+ con_ub[ci_name, t] =
+ JuMP.@constraint(get_jump_model(container), variable[ci_name, t] >= limits.min)
+ con_lb[ci_name, t] =
+ JuMP.@constraint(get_jump_model(container), variable[ci_name, t] >= limits.min)
end
+
+ return
+end
```
diff --git a/docs/src/tutorials/decision_problem.md b/docs/src/tutorials/decision_problem.md
index be6325b600..aeac935807 100644
--- a/docs/src/tutorials/decision_problem.md
+++ b/docs/src/tutorials/decision_problem.md
@@ -22,6 +22,7 @@ using Dates
## Data
!!! note
+
`PowerSystemCaseBuilder.jl` is a helper library that makes it easier to reproduce examples in the documentation and tutorials. Normally you would pass your local files to create the system data instead of calling the function `build_system`.
For more details visit [PowerSystemCaseBuilder Documentation](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/powersystembuilder/)
@@ -115,14 +116,17 @@ to `System` data to create a JuMP model.
```@example op_problem
problem = DecisionModel(template_uc, sys; optimizer = solver, horizon = Hour(24))
-build!(problem, output_dir = mktempdir())
+build!(problem; output_dir = mktempdir())
```
!!! tip
+
The principal component of the `DecisionModel` is the JuMP model. But you can serialize to a file using the following command:
+
```julia
- serialize_optimization_model(problem, save_path)
+ serialize_optimization_model(problem, save_path)
```
+
Keep in mind that if the setting "store_variable_names" is set to `False` then the file won't show the model's names.
### Solve an `DecisionModel`
diff --git a/docs/src/tutorials/pcm_simulation.md b/docs/src/tutorials/pcm_simulation.md
index 6f0ae2e92e..9b9993ae3a 100644
--- a/docs/src/tutorials/pcm_simulation.md
+++ b/docs/src/tutorials/pcm_simulation.md
@@ -77,8 +77,8 @@ In addition to the manual specification process demonstrated in the OperationsPr
example, PSI also provides pre-specified templates for some standard problems:
```@example pcm
-template_ed = template_economic_dispatch(
- network = NetworkModel(PTDFPowerModel, use_slacks = true),
+template_ed = template_economic_dispatch(;
+ network = NetworkModel(PTDFPowerModel; use_slacks = true),
)
```
@@ -91,10 +91,10 @@ a stage. In this case, we want to define two stages with the `ProblemTemplate`s
and the `System`s that we've already created.
```@example pcm
-models = SimulationModels(
+models = SimulationModels(;
decision_models = [
- DecisionModel(template_uc, sys_DA, optimizer = solver, name = "UC"),
- DecisionModel(template_ed, sys_RT, optimizer = solver, name = "ED"),
+ DecisionModel(template_uc, sys_DA; optimizer = solver, name = "UC"),
+ DecisionModel(template_ed, sys_RT; optimizer = solver, name = "ED"),
],
)
```
@@ -111,8 +111,8 @@ Let's review some of the `SimulationSequence` arguments.
In PowerSimulations, chronologies define where information is flowing. There are two types
of chronologies.
-- inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems
-- intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
+ - inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems
+ - intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem.
### `FeedForward`
@@ -125,7 +125,7 @@ in the economic dispatch problems based on the value of the unit-commitment vari
```@example pcm
feedforward = Dict(
"ED" => [
- SemiContinuousFeedforward(
+ SemiContinuousFeedforward(;
component_type = ThermalStandard,
source = OnVariable,
affected_values = [ActivePowerVariable],
@@ -140,19 +140,18 @@ The stage problem length, look-ahead, and other details surrounding the temporal
of stages are controlled using the structure of the time series data in the `System`s.
So, to define a typical day-ahead - real-time sequence:
-- Day ahead problems should represent 48 hours, advancing 24 hours after each execution (24-hour look-ahead)
-- Real time problems should represent 1 hour (12 5-minute periods), advancing 15 min after each execution (15 min look-ahead)
+ - Day ahead problems should represent 48 hours, advancing 24 hours after each execution (24-hour look-ahead)
+ - Real time problems should represent 1 hour (12 5-minute periods), advancing 15 min after each execution (15 min look-ahead)
We can adjust the time series data to reflect this structure in each `System`:
-- `transform_single_time_series!(sys_DA, 48, Hour(1))`
-- `transform_single_time_series!(sys_RT, 12, Minute(15))`
-
+ - `transform_single_time_series!(sys_DA, 48, Hour(1))`
+ - `transform_single_time_series!(sys_RT, 12, Minute(15))`
Now we can put it all together to define a `SimulationSequence`
```@example pcm
-DA_RT_sequence = SimulationSequence(
+DA_RT_sequence = SimulationSequence(;
models = models,
ini_cond_chronology = InterProblemChronology(),
feedforwards = feedforward,
@@ -166,7 +165,7 @@ that we've defined.
```@example pcm
path = mkdir(joinpath(".", "rts-store")) #hide
-sim = Simulation(
+sim = Simulation(;
name = "rts-test",
steps = 2,
models = models,
@@ -187,7 +186,7 @@ the following command returns the status of the simulation (0: is proper executi
stores the results in a set of HDF5 files on disk.
```@example pcm
-execute!(sim, enable_progress_bar = false)
+execute!(sim; enable_progress_bar = false)
```
## Results
@@ -245,13 +244,15 @@ problem definition), we can use:
```@example pcm
read_parameter(
ed_results,
- "ActivePowerTimeSeriesParameter__RenewableNonDispatch",
+ "ActivePowerTimeSeriesParameter__RenewableNonDispatch";
initial_time = DateTime("2020-01-01T06:00:00"),
count = 5,
)
```
!!! info
+
+
note that this returns the results of each execution step in a separate dataframe
If you want the realized results (without lookahead periods), you can call `read_realized_*`:
@@ -260,10 +261,9 @@ read_realized_variables(
uc_results,
["ActivePowerVariable__ThermalStandard", "ActivePowerVariable__RenewableDispatch"],
)
-rm(path, force = true, recursive = true) #hide
+rm(path; force = true, recursive = true) #hide
```
-
## Plotting
Take a look at the plotting capabilities in [PowerGraphics.jl](https://github.com/nrel-siip/powergraphics.jl)
diff --git a/scripts/formatter/formatter_code.jl b/scripts/formatter/formatter_code.jl
index 75bc991e6f..c6ca78f929 100644
--- a/scripts/formatter/formatter_code.jl
+++ b/scripts/formatter/formatter_code.jl
@@ -10,7 +10,7 @@ for main_path in main_paths
for (root, dir, files) in walkdir(main_path)
for f in files
@show file_path = abspath(root, f)
- !occursin(".jl", f) && continue
+ !((occursin(".jl", f) || occursin(".md", f))) && continue
format(file_path;
whitespace_ops_in_indices = true,
remove_extra_newlines = true,
@@ -20,6 +20,14 @@ for main_path in main_paths
conditional_to_if = true,
join_lines_based_on_source = true,
separate_kwargs_with_semicolon = true,
+ format_markdown = true,
+ # extending_powersimulations causes format failures but is not current public
+ ignore = [
+ "README.md",
+ "index.md",
+ "extending_powersimulations.md",
+ "simulation_recorder.md",
+ ],
# always_use_return = true. # Disabled since it throws a lot of false positives
)
diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl
index f4d882d98d..8c45d84187 100644
--- a/src/PowerSimulations.jl
+++ b/src/PowerSimulations.jl
@@ -232,6 +232,7 @@ export UpperBoundFeedForwardSlack
export LowerBoundFeedForwardSlack
export InterfaceFlowSlackUp
export InterfaceFlowSlackDown
+export PieceWiseLinearCostVariable
# Auxiliary variables
export TimeDurationOn
@@ -271,6 +272,7 @@ export FrequencyResponseConstraint
export HVDCPowerBalance
export HVDCLosses
export HVDCFlowDirectionVariable
+export HVDCLossesAbsoluteValue
export InputActivePowerVariableLimitsConstraint
export NetworkFlowConstraint
export NodalBalanceActiveConstraint
@@ -302,9 +304,13 @@ export ActivePowerTimeSeriesParameter
export ReactivePowerTimeSeriesParameter
export RequirementTimeSeriesParameter
+# Cost Parameters
+export CostFunctionParameter
+
# Feedforward Parameters
export OnStatusParameter
export UpperBoundValueParameter
+export LowerBoundValueParameter
export FixValueParameter
# Expressions
diff --git a/src/core/formulations.jl b/src/core/formulations.jl
index 1b6ddb109a..2f98f365dd 100644
--- a/src/core/formulations.jl
+++ b/src/core/formulations.jl
@@ -167,7 +167,7 @@ Approximation to represent inter-area flow with each area represented as a singl
"""
struct AreaBalancePowerModel <: PM.AbstractActivePowerModel end
"""
-Linear active power approximation using the power transfer distribution factor [PTDF](https://nrel-sienna.github.io/PowerNetworkMatrices.jl/stable/tutorials/tutorial_PTDF_matrix/) matrix. Balacing areas independently.
+Linear active power approximation using the power transfer distribution factor [PTDF](https://nrel-sienna.github.io/PowerNetworkMatrices.jl/stable/tutorials/tutorial_PTDF_matrix/) matrix. Balancing areas independently.
"""
struct AreaPTDFPowerModel <: AbstractPTDFModel end
diff --git a/src/initial_conditions/initial_condition_chronologies.jl b/src/initial_conditions/initial_condition_chronologies.jl
index c81d344130..bcee423433 100644
--- a/src/initial_conditions/initial_condition_chronologies.jl
+++ b/src/initial_conditions/initial_condition_chronologies.jl
@@ -1,16 +1,20 @@
+""" Supertype for initial condition chronologies """
abstract type InitialConditionChronology end
"""
InterProblemChronology()
- Type struct to select an information sharing model between stages that uses results from the most recent stage executed to calculate the initial conditions. This model takes into account solutions from stages defined finer resolutions
-"""
+Type struct to select an information sharing model between stages that uses results from the most recent stage executed to calculate the initial conditions. This model takes into account solutions from stages defined with finer temporal resolutions
+See also: [`IntraProblemChronology`](@ref)
+"""
struct InterProblemChronology <: InitialConditionChronology end
"""
- InterProblemChronology()
+ IntraProblemChronology()
+
+Type struct to select an information sharing model between stages that uses results from the same recent stage to calculate the initial conditions. This model ignores solutions from stages defined with finer temporal resolutions.
- Type struct to select an information sharing model between stages that uses results from the same recent stage to calculate the initial conditions. This model ignores solutions from stages defined finer resolutions.
+See also: [`InterProblemChronology`](@ref)
"""
struct IntraProblemChronology <: InitialConditionChronology end
diff --git a/src/operation/emulation_model_store.jl b/src/operation/emulation_model_store.jl
index f92c947d21..222fb48161 100644
--- a/src/operation/emulation_model_store.jl
+++ b/src/operation/emulation_model_store.jl
@@ -16,6 +16,11 @@ function EmulationModelStore()
)
end
+"""
+ Base.empty!(store::EmulationModelStore)
+
+Empty the [`EmulationModelStore`](@ref)
+"""
function Base.empty!(store::EmulationModelStore)
stype = DatasetContainer
for (name, _) in zip(fieldnames(stype), fieldtypes(stype))
diff --git a/src/simulation/optimization_output_cache.jl b/src/simulation/optimization_output_cache.jl
index cd7d14bff4..aeeb92e060 100644
--- a/src/simulation/optimization_output_cache.jl
+++ b/src/simulation/optimization_output_cache.jl
@@ -42,6 +42,11 @@ function is_dirty(cache::OptimizationOutputCache, timestamp)
return timestamp >= first(cache.dirty_timestamps)
end
+"""
+ Base.empty!(cache::OptimizationOutputCache)
+
+Empty the [`OptimizationOutputCache`](@ref)
+"""
function Base.empty!(cache::OptimizationOutputCache)
@assert isempty(cache.dirty_timestamps) "dirty cache was still present $(cache.key) $(cache.dirty_timestamps)"
empty!(cache.data)
diff --git a/src/simulation/optimization_output_caches.jl b/src/simulation/optimization_output_caches.jl
index a97fa93af3..6563ae493b 100644
--- a/src/simulation/optimization_output_caches.jl
+++ b/src/simulation/optimization_output_caches.jl
@@ -23,6 +23,11 @@ function OptimizationOutputCaches(rules::CacheFlushRules)
)
end
+"""
+ Base.empty!(cache::OptimizationOutputCaches)
+
+Empty the [`OptimizationOutputCaches`](@ref)
+"""
function Base.empty!(cache::OptimizationOutputCaches)
for output_cache in values(cache.data)
empty!(output_cache)
diff --git a/src/simulation/simulation.jl b/src/simulation/simulation.jl
index 68a2ab172c..aeb5e7ab65 100644
--- a/src/simulation/simulation.jl
+++ b/src/simulation/simulation.jl
@@ -8,17 +8,17 @@
initial_time::Union{Nothing, Dates.DateTime}
)
-Construct the Simulation structure to run the sequence of decision and emulation models specified.
+Construct the `Simulation` structure to run the sequence of decision and emulation models specified.
# Arguments
- -`sequence::SimulationSequence`: Simulation sequence that specify how the decision and emulation models will be executed.
- -`name::String`: Name of the Simulation
- -`steps::Int`: Number of steps on which the sequence of models will be executed
- -`models::SimulationModels`: List of Decision and Emulation Models
- -`simulation_folder::String`: Folder on which results will be stored
- -`initial_time::Union{Nothing, Dates.DateTime} = nothing`: Initial time of which the simulation starts. If nothing it will default to the first timestamp
- of time series of the system.
+ - `sequence::SimulationSequence`: Simulation sequence that specify how the decision and emulation models will be executed.
+ - `name::String`: Name of the Simulation
+ - `steps::Int`: Number of steps on which the sequence of models will be executed
+ - `models::SimulationModels`: List of Decision and Emulation Models
+ - `simulation_folder::String`: Folder on which results will be stored
+ - `initial_time::Union{Nothing, Dates.DateTime} = nothing`: Initial time of which the
+ simulation starts. If nothing it will default to the first timestamp of time series of the system.
# Example
diff --git a/src/simulation/simulation_results.jl b/src/simulation/simulation_results.jl
index b76ada8c65..14bdb12e08 100644
--- a/src/simulation/simulation_results.jl
+++ b/src/simulation/simulation_results.jl
@@ -184,6 +184,11 @@ function SimulationResults(sim::Simulation; ignore_status = false, kwargs...)
)
end
+"""
+ Base.empty!(res::SimulationResults)
+
+Empty the [`SimulationResults`](@ref)
+"""
function Base.empty!(res::SimulationResults)
foreach(empty!, values(res.decision_problem_results))
empty!(res.emulation_problem_results)
diff --git a/src/simulation/simulation_sequence.jl b/src/simulation/simulation_sequence.jl
index 9fdb656743..95d5fc912d 100644
--- a/src/simulation/simulation_sequence.jl
+++ b/src/simulation/simulation_sequence.jl
@@ -211,8 +211,9 @@ Construct the simulation sequence between decision and emulation models.
- `models::SimulationModels`: Vector of decisions and emulation models.
- `feedforward = Dict{String, Vector{<:AbstractAffectFeedforward}}()`: Optional dictionary to specify how information
- and variables are exchanged between decision and emulation models.
- - `ini_cond_chronology::nitialConditionChronology = InterProblemChronology()`: TODO
+ and variables are exchanged between decision and emulation models.
+ - `ini_cond_chronology::InitialConditionChronology = InterProblemChronology()`: Define
+ information sharing model between stages with [`InterProblemChronology`](@ref)
# Example