Skip to content

Commit 535d3de

Browse files
authored
Merge pull request #1672 from Sienna-Platform/jd/add_monitored_components_table
Jd/add monitored components table
2 parents 667b576 + adbdcfa commit 535d3de

5 files changed

Lines changed: 372 additions & 6 deletions

File tree

docs/src/explanation/supplemental_attributes.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,62 @@ end
169169
- [`GeometricDistributionForcedOutage`](@ref)
170170
- [`PlannedOutage`](@ref)
171171

172+
#### Narrowing post-contingency monitoring
173+
174+
Every concrete [`Outage`](@ref) carries a `monitored_components` field of type
175+
`Vector{Base.UUID}`. It identifies the [`Device`](@ref)s whose post-contingency
176+
state a downstream simulation package (e.g., PowerSimulations) should model when
177+
this outage occurs. Limiting the list reduces the number of post-outage variables
178+
and constraints in security-constrained models.
179+
180+
PowerSystems itself does not attach meaning to the contents of the list. In
181+
particular, an empty `monitored_components` is left for the consumer to
182+
interpret — typical conventions are "monitor nothing" (skip post-contingency
183+
modeling) or "monitor everything" (preserve full N-1 behavior). Pick the policy
184+
that matches your downstream model.
185+
186+
The constructor accepts any iterable whose elements are `Base.UUID` or
187+
`Device`for example a `Vector`, a generator expression, or the iterator
188+
returned by [`get_components`](@ref). Devices are converted to UUIDs
189+
internally:
190+
191+
```julia
192+
gen1 = get_component(ThermalStandard, system, "gen1")
193+
gen2 = get_component(ThermalStandard, system, "gen2")
194+
outage = FixedForcedOutage(;
195+
outage_status = 0.0,
196+
monitored_components = [gen1, gen2],
197+
)
198+
add_supplemental_attribute!(system, gen1, outage)
199+
200+
# Equivalent — every ThermalStandard in the system:
201+
outage_all = FixedForcedOutage(;
202+
outage_status = 0.0,
203+
monitored_components = get_components(ThermalStandard, system),
204+
)
205+
```
206+
207+
Use the dedicated accessors to inspect or update the list at any time. The
208+
singular `add_/remove_*!` methods take one `UUID` or `Device`; the plural
209+
`add_/remove_*s!` and `set_` methods take any iterable of either.
210+
`set_monitored_components!` requires the list to be empty — call
211+
`clear_monitored_components!` first to replace an existing list:
212+
213+
```julia
214+
get_monitored_components(outage) # → Vector{UUID}
215+
clear_monitored_components!(outage) # wipe
216+
set_monitored_components!(outage, get_components(Line, system)) # populate (must be empty)
217+
add_monitored_component!(outage, gen2) # append one (deduped)
218+
add_monitored_components!(outage, [gen1, gen2]) # append many
219+
remove_monitored_component!(outage, gen1) # remove one
220+
remove_monitored_components!(outage, [gen1, gen2]) # remove many
221+
```
222+
223+
When `system.runchecks == true`, `add_supplemental_attribute!` resolves each
224+
UUID against the parent system and raises an `ArgumentError` for any UUID that
225+
does not point to a `Device` in the system. With `runchecks = false`, UUIDs are
226+
accepted as-is and resolution is deferred to the consumer.
227+
172228
### Plant Attributes
173229

174230
Plant attributes are a specialized category of supplemental attributes for grouping individual

src/PowerSystems.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,13 @@ export FixedForcedOutage
311311
export get_mean_time_to_recovery
312312
export get_outage_transition_probability
313313
export get_outage_schedule
314+
export get_monitored_components
315+
export set_monitored_components!
316+
export clear_monitored_components!
317+
export add_monitored_component!
318+
export add_monitored_components!
319+
export remove_monitored_component!
320+
export remove_monitored_components!
314321

315322
# Impedance Correction Data
316323
export ImpedanceCorrectionData

src/base.jl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,28 @@ function add_supplemental_attribute!(
20772077
return IS.add_supplemental_attribute!(sys.data, component, attribute)
20782078
end
20792079

2080+
function add_supplemental_attribute!(
2081+
sys::System,
2082+
component::Component,
2083+
outage::Outage,
2084+
)
2085+
if get_runchecks(sys)
2086+
for uuid in get_monitored_components(outage)
2087+
comp = IS.get_component(sys, uuid) # throws ArgumentError on miss
2088+
if !(comp isa Device)
2089+
throw(
2090+
ArgumentError(
2091+
"monitored_components on $(typeof(outage)) references UUID " *
2092+
"$(uuid), which resolves to $(typeof(comp)); only " *
2093+
"Device subtypes are allowed",
2094+
),
2095+
)
2096+
end
2097+
end
2098+
end
2099+
return IS.add_supplemental_attribute!(sys.data, component, outage)
2100+
end
2101+
20802102
"""
20812103
Begin an update of supplemental attributes. Use this function when adding
20822104
or removing many supplemental attributes in order to improve performance.
@@ -2432,7 +2454,7 @@ function check_ac_transmission_rate_values(sys::System)
24322454
end
24332455

24342456
"""
2435-
Serialize a [System](@ref) instance. Returns a `Dict{String, Any}`
2457+
Serialize a [System](@ref) instance. Returns a `Dict{String, Any}`
24362458
of the form `Dict("data_format_version" => "1.0", "field1" => serialize(sys.field1), ...)`,
24372459
which can then be written to a JSON3 file.
24382460
"""

src/outages.jl

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,112 @@ Supertype for outage contingencies representing planned or unplanned equipment o
33
44
Concrete subtypes include [`GeometricDistributionForcedOutage`](@ref),
55
[`PlannedOutage`](@ref), and [`FixedForcedOutage`](@ref).
6+
7+
# Interface for custom subtypes
8+
9+
Subtypes are expected to provide the following fields, or override the matching
10+
accessors via multiple dispatch:
11+
12+
- `monitored_components::Set{Base.UUID}` — UUIDs of devices whose
13+
post-contingency state should be modeled. The default
14+
[`get_monitored_components`](@ref) reads `value.monitored_components`; override
15+
it if your subtype does not carry the field directly.
16+
- `internal::InfrastructureSystemsInternal` — accessed via `get_internal`.
17+
18+
The default [`supports_time_series`](@ref) returns `true`; override for custom
19+
outage types that do not support time series.
620
"""
721
abstract type Outage <: Contingency end
822

923
abstract type UnplannedOutage <: Outage end
1024

1125
"""
12-
All PowerSystems [Outage](@ref) types support time series. This can be overridden for custom
26+
All PowerSystems [Outage](@ref) types support time series. This can be overridden for custom
1327
outage types that do not support time series.
1428
"""
1529
supports_time_series(::Outage) = true
1630

1731
"""Get `internal`."""
1832
get_internal(x::Outage) = x.internal
1933

34+
# Public API for monitored_components accepts UUIDs or Devices interchangeably.
35+
_as_uuid(uuid::Base.UUID) = uuid
36+
_as_uuid(device::Device) = IS.get_uuid(device)
37+
38+
"""
39+
Get the set of [`Device`](@ref) UUIDs whose post-contingency state should be modeled
40+
when this outage occurs. PowerSystems does not assign meaning to an empty set;
41+
downstream consumers (e.g., PowerSimulations) decide whether empty means "monitor
42+
nothing" or "monitor everything".
43+
"""
44+
get_monitored_components(value::Outage) = value.monitored_components
45+
46+
"""
47+
Replace the monitored-components set for an [`Outage`](@ref) with the contents
48+
of `items`. Accepts any iterable whose elements are `Base.UUID` or
49+
[`Device`](@ref) (e.g., a `Vector`, a generator, or the iterator returned by
50+
[`get_components`](@ref)). Devices are converted to their UUIDs internally.
51+
Pass an empty iterable (or call [`clear_monitored_components!`](@ref)) to
52+
clear the set.
53+
"""
54+
function set_monitored_components!(value::Outage, items)
55+
empty!(value.monitored_components)
56+
for x in items
57+
push!(value.monitored_components, _as_uuid(x))
58+
end
59+
return value.monitored_components
60+
end
61+
62+
"""
63+
Empty the monitored-components set of an [`Outage`](@ref). Returns the (now empty)
64+
underlying set.
65+
"""
66+
function clear_monitored_components!(value::Outage)
67+
empty!(value.monitored_components)
68+
return value.monitored_components
69+
end
70+
71+
"""
72+
Add a `Base.UUID` or [`Device`](@ref) to the monitored-components set of
73+
an [`Outage`](@ref). Adding an existing UUID is a no-op.
74+
"""
75+
function add_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
76+
push!(value.monitored_components, _as_uuid(x))
77+
return value.monitored_components
78+
end
79+
80+
"""
81+
Add every element of `items` (each a `Base.UUID` or [`Device`](@ref)) to
82+
the monitored-components set of an [`Outage`](@ref). Accepts any iterable, including
83+
the iterator returned by [`get_components`](@ref). Adding an existing UUID is a no-op.
84+
"""
85+
function add_monitored_components!(value::Outage, items)
86+
for x in items
87+
add_monitored_component!(value, x)
88+
end
89+
return value.monitored_components
90+
end
91+
92+
"""
93+
Remove a `Base.UUID` or [`Device`](@ref) from the monitored-components set
94+
of an [`Outage`](@ref). No-op when the entry is not present.
95+
"""
96+
function remove_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
97+
delete!(value.monitored_components, _as_uuid(x))
98+
return
99+
end
100+
101+
"""
102+
Remove every element of `items` (each a `Base.UUID` or [`Device`](@ref)) from
103+
the monitored-components set of an [`Outage`](@ref). Accepts any iterable.
104+
"""
105+
function remove_monitored_components!(value::Outage, items)
106+
for x in items
107+
remove_monitored_component!(value, x)
108+
end
109+
return
110+
end
111+
20112
"""
21113
Attribute that contains information regarding forced outages where the transition probabilities
22114
are modeled with geometric distributions. The outage probabilities and recovery probabilities can be modeled as time
@@ -25,32 +117,37 @@ series.
25117
# Arguments
26118
- `mean_time_to_recovery::Float64`: Time elapsed to recovery after a failure in Milliseconds.
27119
- `outage_transition_probability::Float64`: Characterizes the probability of failure (1 - p) in the geometric distribution.
120+
- `monitored_components::Set{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty set are decided by the downstream consumer.
28121
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
29122
"""
30123
struct GeometricDistributionForcedOutage <: UnplannedOutage
31124
mean_time_to_recovery::Float64
32125
outage_transition_probability::Float64
126+
monitored_components::Set{Base.UUID}
33127
internal::InfrastructureSystemsInternal
34128
end
35129

36130
"""
37-
GeometricDistributionForcedOutage(; mean_time_to_recovery, outage_transition_probability, internal)
131+
GeometricDistributionForcedOutage(; mean_time_to_recovery, outage_transition_probability, monitored_components, internal)
38132
39133
Construct a [`GeometricDistributionForcedOutage`](@ref).
40134
41135
# Arguments
42136
- `mean_time_to_recovery::Float64`: (default: `0.0`) Time elapsed to recovery after a failure in Milliseconds.
43137
- `outage_transition_probability::Float64`: (default: `0.0`) Characterizes the probability of failure (1 - p) in the geometric distribution.
138+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
44139
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
45140
"""
46141
function GeometricDistributionForcedOutage(;
47142
mean_time_to_recovery = 0.0,
48143
outage_transition_probability = 0.0,
144+
monitored_components = Base.UUID[],
49145
internal = InfrastructureSystemsInternal(),
50146
)
51147
return GeometricDistributionForcedOutage(
52148
mean_time_to_recovery,
53149
outage_transition_probability,
150+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
54151
internal,
55152
)
56153
end
@@ -67,28 +164,33 @@ Attribute that contains information regarding planned outages.
67164
68165
# Arguments
69166
- `outage_schedule::String`: String name of the time series used for the scheduled outages
167+
- `monitored_components::Set{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty set are decided by the downstream consumer.
70168
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
71169
"""
72170
struct PlannedOutage <: Outage
73171
outage_schedule::String
172+
monitored_components::Set{Base.UUID}
74173
internal::InfrastructureSystemsInternal
75174
end
76175

77176
"""
78-
PlannedOutage(; outage_schedule, internal)
177+
PlannedOutage(; outage_schedule, monitored_components, internal)
79178
80179
Construct a [`PlannedOutage`](@ref).
81180
82181
# Arguments
83182
- `outage_schedule::String`: String name of the time series used for the scheduled outages
183+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
84184
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
85185
"""
86186
function PlannedOutage(;
87187
outage_schedule,
188+
monitored_components = Base.UUID[],
88189
internal = InfrastructureSystemsInternal(),
89190
)
90191
return PlannedOutage(
91192
outage_schedule,
193+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
92194
internal,
93195
)
94196
end
@@ -102,27 +204,35 @@ The time series data for fixed outages can be obtained from the simulation of a
102204
103205
# Arguments
104206
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
207+
- `monitored_components::Set{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty set are decided by the downstream consumer.
105208
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
106209
"""
107210
struct FixedForcedOutage <: UnplannedOutage
108211
outage_status::Float64
212+
monitored_components::Set{Base.UUID}
109213
internal::InfrastructureSystemsInternal
110214
end
111215

112216
"""
113-
FixedForcedOutage(; outage_status, internal)
217+
FixedForcedOutage(; outage_status, monitored_components, internal)
114218
115219
Construct a [`FixedForcedOutage`](@ref).
116220
117221
# Arguments
118222
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
223+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
119224
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
120225
"""
121226
function FixedForcedOutage(;
122227
outage_status,
228+
monitored_components = Base.UUID[],
123229
internal = InfrastructureSystemsInternal(),
124230
)
125-
return FixedForcedOutage(outage_status, internal)
231+
return FixedForcedOutage(
232+
outage_status,
233+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
234+
internal,
235+
)
126236
end
127237

128238
"""Get [`FixedForcedOutage`](@ref) `outage_status`."""

0 commit comments

Comments
 (0)