Skip to content

Commit 33b73cc

Browse files
authored
Merge pull request #1579 from Sienna-Platform/sm/n-1_modf
SCUC branch formulation using MODF
2 parents f801e64 + b215564 commit 33b73cc

85 files changed

Lines changed: 4720 additions & 654 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ JuMP = "^1.28"
4949
LinearAlgebra = "1"
5050
Logging = "1"
5151
MathOptInterface = "1"
52-
PowerFlows = "^0.16"
52+
PowerFlows = "^0.17"
5353
PowerModels = "^0.21.5"
54-
PowerNetworkMatrices = "^0.20"
55-
PowerSystems = "^5.8"
54+
PowerNetworkMatrices = "^0.21"
55+
PowerSystems = "^5.10"
5656
PrettyTables = "2.4, 3.1"
5757
ProgressMeter = "^1.5"
5858
Random = "^1.10"

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pages = OrderedDict(
2222
"Tutorials" => Any[
2323
"Single-step Problem" => "tutorials/generated_decision_problem.md",
2424
"Multi-stage Production Cost Simulation" => "tutorials/generated_pcm_simulation.md",
25+
"Dynamic Line Ratings" => "tutorials/generated_dynamic_line_ratings.md",
2526
],
2627
"How to..." => Any[
2728
"...register a variable in a custom operation model" => "how_to/register_variable.md",
@@ -37,6 +38,7 @@ pages = OrderedDict(
3738
"explanation/feedforward.md",
3839
"explanation/chronologies.md",
3940
"explanation/sequencing.md",
41+
"explanation/branch_rating_limits.md",
4042
],
4143
"Reference" => Any[
4244
"Glossary and Acronyms" => "api/glossary.md",

docs/src/api/PowerSimulations.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ VoltageDispatchHVDCNetworkModel
3939
HVDCTwoTerminalLCC
4040
```
4141

42+
### Security-Constrained Branch Formulations
43+
44+
```@docs
45+
SecurityConstrainedStaticBranch
46+
```
47+
4248
### Converter Formulations
4349

4450
```@docs
@@ -419,6 +425,7 @@ DCCurrentBalance
419425
PostContingencyGenerationBalanceConstraint
420426
PostContingencyActivePowerVariableLimitsConstraint
421427
PostContingencyActivePowerReserveDeploymentVariableLimitsConstraint
428+
PostContingencyFlowRateConstraint
422429
```
423430

424431
### Market Bid Cost Constraints
@@ -480,8 +487,8 @@ CostFunctionParameter
480487
AvailableStatusChangeCountdownParameter
481488
AvailableStatusParameter
482489
ActivePowerOffsetParameter
483-
DynamicBranchRatingTimeSeriesParameter
484-
PostContingencyDynamicBranchRatingTimeSeriesParameter
490+
BranchRatingTimeSeriesParameter
491+
PostContingencyBranchRatingTimeSeriesParameter
485492
```
486493

487494
## Results
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# [Branch Rating Limits](@id branch_rating_limits)
2+
3+
This page explains how PowerSimulations derives a flow **rating** for an AC
4+
transmission element and how that rating is turned into optimization
5+
constraints. It covers every reduction type produced by
6+
`PowerNetworkMatrices` (PNM), the formulations that consume the rating, the
7+
difference between linear and nonlinear network models, the parallel-branch
8+
aggregation attribute and its defaults, and the branch-rating time series.
9+
10+
For the per-formulation constraint algebra (variable names, slacks, objective
11+
terms) see the [`PowerSystems.Branch` Formulations](@ref) page in the
12+
Formulation Library. This page is the conceptual companion: *where the
13+
constraint branch flow limit on the right-hand side comes from*.
14+
15+
## The single source of truth
16+
17+
All branch flow ratings flow through one type-aware entry point.
18+
`branch_rating(entry, device_model)` returns the scalar rating for a reduction
19+
`entry`, and `min_max_flow_limits(entry, device_model)` wraps it into the
20+
symmetric pair `(min = -rating, max = rating)` used by linear constraints. Every
21+
constraint builder — linear or nonlinear, static or time series — resolves its
22+
rating through this path so the same physical element gets the same limit
23+
regardless of the network model.
24+
25+
`branch_rating` is *type-aware*: it dispatches on the kind of reduction entry
26+
PNM produced for the element and delegates the aggregation policy to PNM. It
27+
does **not** reimplement per-type rating math; extend
28+
`PowerNetworkMatrices.get_equivalent_rating` instead if a new element type needs
29+
support.
30+
31+
## PNM rating aggregation by reduction type
32+
33+
Network reduction can collapse several physical branches into one equivalent
34+
element. The rating of that equivalent element depends on its topology:
35+
36+
| Reduction entry | Rating policy |
37+
|:------------------------------------------------------------------------ |:------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
38+
| Single line / two-winding transformer (`PSY.ACTransmission`) | `PSY.get_rating` |
39+
| `PSY.GenericArcImpedance` | `PSY.get_max_flow` (used as a rating proxy) |
40+
| Three-winding transformer winding (`PNM.ThreeWindingTransformerWinding`) | The winding-specific rating (primary/secondary/tertiary); falls back to the parent transformer rating when the winding rating is `0.0` |
41+
| Series chain (`PNM.BranchesSeries`) | **Weakest link**: the minimum equivalent rating across the chain members. A parallel block embedded in the chain contributes its single-element-contingency rating |
42+
| Parallel group (`PNM.BranchesParallel`) | Attribute-driven — see [Parallel-branch aggregation](@ref parallel_branch_aggregation) |
43+
| Mixed parallel group (`PNM.MixedBranchesParallel`) | Always the plain sum of member ratings (`sum_of_max`); the attribute is ignored |
44+
45+
A **post-contingency** (emergency) rating uses the parallel structure with
46+
`PSY.rating_b` in place of `PSY.rating`:
47+
`PowerNetworkMatrices.get_equivalent_emergency_rating` mirrors the normal
48+
aggregation type-for-type, and for a single `ACTransmission` it returns
49+
`PSY.get_rating_b` and falls back to `PSY.get_rating` when no `rating_b` is
50+
defined. Three-winding windings have no `rating_b` in PowerSystems and fall
51+
back to their normal winding rating.
52+
53+
## [Parallel-branch aggregation](@id parallel_branch_aggregation)
54+
55+
When PNM produces a homogeneous `PNM.BranchesParallel` group, the way the
56+
individual circuit ratings combine into one limit is a modeling choice carried
57+
on the `DeviceModel` as an attribute:
58+
59+
- **Attribute key:** `"parallel_branch_max_rating_method"` (the constant
60+
`PARALLEL_BRANCH_MAX_RATING_KEY`).
61+
- **Default:** `"single_element_contingency"`.
62+
63+
Valid values:
64+
65+
| Value | Aggregated rating | Meaning |
66+
|:---------------------------------------- |:-------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------- |
67+
| `"single_element_contingency"` (default) | ``\sum_i S_i - \max_i S_i`` | N-1 surviving capacity after the largest-rated circuit trips. A single-circuit group has zero capacity under this policy |
68+
| `"sum_of_max"` | ``\sum_i S_i`` | Each circuit independently loadable to its own thermal limit (least conservative) |
69+
| `"impedance_averaged"` | ``\sum_i f_i S_i,\; f_i = b_i / \sum_k b_k`` | Susceptance-weighted average, reflecting how DC flow physically splits across the group. Errors if total series susceptance is zero or non-finite |
70+
71+
`PNM.MixedBranchesParallel` (a parallel group whose members carry different
72+
`DeviceModel` preferences) always uses `sum_of_max`, because there is no
73+
defensible way to pick one member's attribute for the whole group.
74+
75+
Security-constrained branch formulations carry one additional default
76+
attribute, `"include_planned_outages" => false`, alongside the same
77+
`"single_element_contingency"` parallel default.
78+
79+
## How the rating enters the optimization
80+
81+
### Linear network models
82+
83+
This covers the PTDF family (`PTDFPowerModel`, `AreaPTDFPowerModel`, and other
84+
`AbstractPTDFModel`s) and the DC power model (`DCPPowerModel` and other
85+
`PM.AbstractActivePowerModel`s). `CopperPlatePowerModel` and
86+
`AreaBalancePowerModel` enforce no per-branch flow limits.
87+
88+
The active-power flow is bounded symmetrically by the aggregated rating:
89+
90+
```math
91+
-R \;\le\; f_{b,t} \;\le\; R
92+
```
93+
94+
applied through `FlowRateConstraint` (PTDF and DC) or, for
95+
`StaticBranchBounds`, directly as variable bounds on the flow variable.
96+
A **standalone** `MonitoredLine` is the exception — it carries explicit,
97+
possibly asymmetric `flow_limits`. Note this exception only applies when the
98+
`MonitoredLine` is *not* collapsed by network reduction: a `MonitoredLine` that
99+
is a member of a reduction group resolves through the symmetric aggregated
100+
`branch_rating` like any other reduced element and its `flow_limits` are not
101+
carried into the equivalent (see [MonitoredLine](@ref monitored_line_rating)).
102+
`PhaseShiftingTransformer` under `PhaseAngleControl` additionally bounds the
103+
phase-shifter angle to ``[-\pi/2,\, \pi/2]``.
104+
105+
### Nonlinear network models
106+
107+
For full AC models (any `PM.AbstractPowerModel` that is not an active-power-only
108+
model) the apparent-power limit is enforced per flow direction:
109+
110+
```math
111+
(P^{ft}_{b,t})^2 + (Q^{ft}_{b,t})^2 \;\le\; R^2
112+
\qquad
113+
(P^{tf}_{b,t})^2 + (Q^{tf}_{b,t})^2 \;\le\; R^2
114+
```
115+
116+
through `FlowRateConstraintFromTo` and `FlowRateConstraintToFrom`. The rating
117+
``R`` is the **same** type-aware `branch_rating` value used by the linear path;
118+
only its application differs (squared, on apparent power).
119+
120+
### Formulation behavior summary
121+
122+
| Formulation | Rating limit behavior |
123+
|:---------------------------------------------------- |:------------------------------------------------------------ |
124+
| `StaticBranch` | Flow rate limits enforced (and apparent-power limits for AC) |
125+
| `StaticBranchBounds` | Rating applied as bounds on the flow variable |
126+
| `StaticBranchUnbounded` | No flow limits enforced |
127+
| `AbstractSecurityConstrainedStaticBranch` (SCUC/N-1) | Flow rate limits enforced; pre- and post-contingency ratings |
128+
| `PhaseAngleControl` | Flow rate limits plus phase-angle limits |
129+
130+
## Branch rating time series
131+
132+
A branch may carry a time series that makes its rating vary per time step (for
133+
example dynamic line ratings). The time series is **normalized**: its values
134+
are a per-unit multiplier of a static rating, and the static rating is supplied
135+
as the parameter *multiplier*. The right-hand side that the solver sees is
136+
therefore:
137+
138+
- Linear models: `parameter[t] * multiplier`, equal to the time-varying
139+
rating `rating(t)`.
140+
- Nonlinear models: `parameter[t] * multiplier`, equal to `rating(t)^2`.
141+
142+
PSI parameter objects are **never squared** in a constraint expression (a
143+
squared parameter is not a valid parametric term). So for the apparent-power
144+
limit the right-hand side stays linear in the parameter — `parameter * multiplier` — and the parameter and multiplier are instead configured so their
145+
product is `rating(t)^2` directly. The simulation update policy refreshes only
146+
the parameter value each window; the multiplier is fixed at build time.
147+
148+
The multiplier is resolved with the **same type-aware aggregation as the static
149+
path**, so series chains, three-winding windings and single elements all scale
150+
the correct equivalent rating. There is one deliberate exception:
151+
152+
- **Parallel groups:** a rating time series attached to one member of a
153+
parallel group cannot be distributed across the group's other circuits.
154+
The multiplier is the group's combined rating (`sum_of_max`) **regardless
155+
of `"parallel_branch_max_rating_method"`**, and a warning is emitted.
156+
- **Post-contingency** (`PostContingencyBranchRatingTimeSeriesParameter`):
157+
the multiplier uses the emergency (`rating_b`) aggregation — the summed
158+
emergency rating for parallel groups, and the type-aware emergency rating
159+
otherwise.
160+
161+
Support and validation:
162+
163+
- Branch rating time series are only supported with `StaticBranch` and
164+
`AbstractSecurityConstrainedStaticBranch`. Any other formulation —
165+
including `StaticBranchUnbounded`, which enforces no flow limits — raises a
166+
`ConflictingInputsError` rather than silently ignoring the time series.
167+
- At build a sanity check warns if the normalized series scaled by the
168+
multiplier ever falls below the device's static rating (i.e. the series is
169+
expected to represent at-or-above-nominal capacity).
170+
171+
## [MonitoredLine](@id monitored_line_rating)
172+
173+
A **standalone** `PSY.MonitoredLine` (one that is *not* collapsed into a
174+
reduction group — see below) does not use the symmetric `branch_rating` path
175+
because it carries explicit `flow_limits` that can be tighter than, and
176+
asymmetric to, its rating. The asymmetry is **not discarded** — it is enforced
177+
by the directional constraints; only the single symmetric general rate limit
178+
cannot represent it and falls back to the tighter of the two:
179+
180+
- General rate limit (`FlowRateConstraint`): a single symmetric bound
181+
``\min(\text{rating},\ \text{flow\_limits.from\_to},\ \text{flow\_limits.to\_from})``.
182+
Because one symmetric constraint structurally cannot encode an asymmetric
183+
limit, the minimum of the two directions is used and a warning is emitted
184+
when `from_to` and `to_from` differ — flagging that this particular
185+
constraint is conservative, not that the asymmetry is lost.
186+
- Directional limits (`FlowLimitFromToConstraint` / `FlowLimitToFromConstraint`):
187+
these **preserve the asymmetry**, using `flow_limits.from_to` and
188+
`flow_limits.to_from` respectively. This is where the asymmetric input is
189+
actually honored.
190+
191+
### `MonitoredLine` inside a reduction
192+
193+
When a `MonitoredLine` is collapsed by network reduction into an equivalent
194+
element (`PNM.BranchesParallel`, `PNM.BranchesSeries`,
195+
`PNM.MixedBranchesParallel`, …), the reduction `entry` is a PNM reduction type,
196+
**not** a `PSY.MonitoredLine`, so dispatch goes through the type-aware
197+
`branch_rating``PowerNetworkMatrices.get_equivalent_rating` path. That
198+
aggregation consumes only the scalar `PSY.get_rating` (or `PSY.get_rating_b`
199+
for the post-contingency rating); the explicit, possibly asymmetric
200+
`flow_limits` are **not propagated into the reduced equivalent element**. A
201+
reduced arc that contains a `MonitoredLine` is therefore bounded by the
202+
symmetric aggregated rating like any other reduced element. The directional
203+
`FlowLimitFromToConstraint` / `FlowLimitToFromConstraint` are only added when
204+
the `MonitoredLine` is modeled as a standalone (un-reduced) element. If the
205+
asymmetric directional limits must be enforced, pin the `MonitoredLine`'s
206+
endpoints so reduction does not collapse it (e.g. via `irreducible_buses`).
207+
208+
## Defaults at a glance
209+
210+
| Setting | Default |
211+
|:------------------------------------------------------------------- |:--------------------------------------------------------- |
212+
| `"parallel_branch_max_rating_method"` (`AbstractBranchFormulation`) | `"single_element_contingency"` |
213+
| Security-constrained extra attribute | `"include_planned_outages" => false` |
214+
| `MixedBranchesParallel` aggregation | `sum_of_max` (attribute ignored) |
215+
| Single branch / transformer rating | `PSY.get_rating` |
216+
| Series chain rating | weakest link (minimum member rating) |
217+
| Three-winding winding rating | winding rating, falling back to parent transformer rating |
218+
| Post-contingency single-branch rating | `PSY.get_rating_b`, falling back to `PSY.get_rating` |
219+
| Time-series multiplier (parallel) | `sum_of_max` regardless of the attribute (with warning) |
220+
| Branch rating time series formulation support | `StaticBranch`, `AbstractSecurityConstrainedStaticBranch` |

0 commit comments

Comments
 (0)