Skip to content

Commit adbdcfa

Browse files
committed
use sets
1 parent d5573a1 commit adbdcfa

2 files changed

Lines changed: 57 additions & 54 deletions

File tree

src/outages.jl

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Concrete subtypes include [`GeometricDistributionForcedOutage`](@ref),
99
Subtypes are expected to provide the following fields, or override the matching
1010
accessors via multiple dispatch:
1111
12-
- `monitored_components::Vector{Base.UUID}` — UUIDs of devices whose
12+
- `monitored_components::Set{Base.UUID}` — UUIDs of devices whose
1313
post-contingency state should be modeled. The default
1414
[`get_monitored_components`](@ref) reads `value.monitored_components`; override
1515
it if your subtype does not carry the field directly.
@@ -36,20 +36,20 @@ _as_uuid(uuid::Base.UUID) = uuid
3636
_as_uuid(device::Device) = IS.get_uuid(device)
3737

3838
"""
39-
Get the list of [`Device`](@ref) UUIDs whose post-contingency state should be modeled
40-
when this outage occurs. PowerSystems does not assign meaning to an empty list;
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;
4141
downstream consumers (e.g., PowerSimulations) decide whether empty means "monitor
4242
nothing" or "monitor everything".
4343
"""
4444
get_monitored_components(value::Outage) = value.monitored_components
4545

4646
"""
47-
Replace the monitored-components list for an [`Outage`](@ref) with the contents
47+
Replace the monitored-components set for an [`Outage`](@ref) with the contents
4848
of `items`. Accepts any iterable whose elements are `Base.UUID` or
4949
[`Device`](@ref) (e.g., a `Vector`, a generator, or the iterator returned by
5050
[`get_components`](@ref)). Devices are converted to their UUIDs internally.
5151
Pass an empty iterable (or call [`clear_monitored_components!`](@ref)) to
52-
clear the list.
52+
clear the set.
5353
"""
5454
function set_monitored_components!(value::Outage, items)
5555
empty!(value.monitored_components)
@@ -60,30 +60,27 @@ function set_monitored_components!(value::Outage, items)
6060
end
6161

6262
"""
63-
Empty the monitored-components list of an [`Outage`](@ref). Returns the (now empty)
64-
underlying vector.
63+
Empty the monitored-components set of an [`Outage`](@ref). Returns the (now empty)
64+
underlying set.
6565
"""
6666
function clear_monitored_components!(value::Outage)
6767
empty!(value.monitored_components)
6868
return value.monitored_components
6969
end
7070

7171
"""
72-
Append a `Base.UUID` or [`Device`](@ref) to the monitored-components list of
73-
an [`Outage`](@ref). Duplicate UUIDs are ignored.
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.
7474
"""
7575
function add_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
76-
uuid = _as_uuid(x)
77-
if !(uuid in value.monitored_components)
78-
push!(value.monitored_components, uuid)
79-
end
76+
push!(value.monitored_components, _as_uuid(x))
8077
return value.monitored_components
8178
end
8279

8380
"""
84-
Append every element of `items` (each a `Base.UUID` or [`Device`](@ref)) to
85-
the monitored-components list of an [`Outage`](@ref). Accepts any iterable, including
86-
the iterator returned by [`get_components`](@ref). Duplicate UUIDs are ignored.
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.
8784
"""
8885
function add_monitored_components!(value::Outage, items)
8986
for x in items
@@ -93,26 +90,22 @@ function add_monitored_components!(value::Outage, items)
9390
end
9491

9592
"""
96-
Remove a `Base.UUID` or [`Device`](@ref) from the monitored-components list
93+
Remove a `Base.UUID` or [`Device`](@ref) from the monitored-components set
9794
of an [`Outage`](@ref). No-op when the entry is not present.
9895
"""
9996
function remove_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
100-
uuid = _as_uuid(x)
101-
idx = findfirst(==(uuid), value.monitored_components)
102-
isnothing(idx) || deleteat!(value.monitored_components, idx)
103-
value.monitored_components
97+
delete!(value.monitored_components, _as_uuid(x))
10498
return
10599
end
106100

107101
"""
108102
Remove every element of `items` (each a `Base.UUID` or [`Device`](@ref)) from
109-
the monitored-components list of an [`Outage`](@ref). Accepts any iterable.
103+
the monitored-components set of an [`Outage`](@ref). Accepts any iterable.
110104
"""
111105
function remove_monitored_components!(value::Outage, items)
112106
for x in items
113107
remove_monitored_component!(value, x)
114108
end
115-
value.monitored_components
116109
return
117110
end
118111

@@ -124,13 +117,13 @@ series.
124117
# Arguments
125118
- `mean_time_to_recovery::Float64`: Time elapsed to recovery after a failure in Milliseconds.
126119
- `outage_transition_probability::Float64`: Characterizes the probability of failure (1 - p) in the geometric distribution.
127-
- `monitored_components::Vector{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty list are decided by the downstream consumer.
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.
128121
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
129122
"""
130123
struct GeometricDistributionForcedOutage <: UnplannedOutage
131124
mean_time_to_recovery::Float64
132125
outage_transition_probability::Float64
133-
monitored_components::Vector{Base.UUID}
126+
monitored_components::Set{Base.UUID}
134127
internal::InfrastructureSystemsInternal
135128
end
136129

@@ -142,7 +135,7 @@ Construct a [`GeometricDistributionForcedOutage`](@ref).
142135
# Arguments
143136
- `mean_time_to_recovery::Float64`: (default: `0.0`) Time elapsed to recovery after a failure in Milliseconds.
144137
- `outage_transition_probability::Float64`: (default: `0.0`) Characterizes the probability of failure (1 - p) in the geometric distribution.
145-
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally.
138+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
146139
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
147140
"""
148141
function GeometricDistributionForcedOutage(;
@@ -154,7 +147,7 @@ function GeometricDistributionForcedOutage(;
154147
return GeometricDistributionForcedOutage(
155148
mean_time_to_recovery,
156149
outage_transition_probability,
157-
Base.UUID[_as_uuid(x) for x in monitored_components],
150+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
158151
internal,
159152
)
160153
end
@@ -171,12 +164,12 @@ Attribute that contains information regarding planned outages.
171164
172165
# Arguments
173166
- `outage_schedule::String`: String name of the time series used for the scheduled outages
174-
- `monitored_components::Vector{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty list are decided by the downstream consumer.
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.
175168
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
176169
"""
177170
struct PlannedOutage <: Outage
178171
outage_schedule::String
179-
monitored_components::Vector{Base.UUID}
172+
monitored_components::Set{Base.UUID}
180173
internal::InfrastructureSystemsInternal
181174
end
182175

@@ -187,7 +180,7 @@ Construct a [`PlannedOutage`](@ref).
187180
188181
# Arguments
189182
- `outage_schedule::String`: String name of the time series used for the scheduled outages
190-
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally.
183+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
191184
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
192185
"""
193186
function PlannedOutage(;
@@ -197,7 +190,7 @@ function PlannedOutage(;
197190
)
198191
return PlannedOutage(
199192
outage_schedule,
200-
Base.UUID[_as_uuid(x) for x in monitored_components],
193+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
201194
internal,
202195
)
203196
end
@@ -211,12 +204,12 @@ The time series data for fixed outages can be obtained from the simulation of a
211204
212205
# Arguments
213206
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
214-
- `monitored_components::Vector{Base.UUID}`: UUIDs of devices whose post-contingency state should be modeled when this outage occurs. Empty by default; semantics of an empty list are decided by the downstream consumer.
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.
215208
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
216209
"""
217210
struct FixedForcedOutage <: UnplannedOutage
218211
outage_status::Float64
219-
monitored_components::Vector{Base.UUID}
212+
monitored_components::Set{Base.UUID}
220213
internal::InfrastructureSystemsInternal
221214
end
222215

@@ -227,7 +220,7 @@ Construct a [`FixedForcedOutage`](@ref).
227220
228221
# Arguments
229222
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
230-
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally.
223+
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
231224
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
232225
"""
233226
function FixedForcedOutage(;
@@ -237,7 +230,7 @@ function FixedForcedOutage(;
237230
)
238231
return FixedForcedOutage(
239232
outage_status,
240-
Base.UUID[_as_uuid(x) for x in monitored_components],
233+
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
241234
internal,
242235
)
243236
end

test/test_outages.jl

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -174,63 +174,72 @@ end
174174
outage_transition_probability = 0.5,
175175
monitored_components = [uuid1, uuid2],
176176
)
177-
@test get_monitored_components(fo_uuid) == [uuid1, uuid2]
177+
@test get_monitored_components(fo_uuid) == Set([uuid1, uuid2])
178178

179179
# Construct with Device references
180180
fo_dev = GeometricDistributionForcedOutage(;
181181
mean_time_to_recovery = 1.0,
182182
outage_transition_probability = 0.5,
183183
monitored_components = [gen1, gen2],
184184
)
185-
@test get_monitored_components(fo_dev) == [uuid1, uuid2]
185+
@test get_monitored_components(fo_dev) == Set([uuid1, uuid2])
186186

187187
# Construct with the FlattenIteratorWrapper returned by get_components
188188
fo_iter = GeometricDistributionForcedOutage(;
189189
mean_time_to_recovery = 1.0,
190190
outage_transition_probability = 0.5,
191191
monitored_components = get_components(ThermalStandard, sys),
192192
)
193-
@test Set(get_monitored_components(fo_iter)) == Set(IS.get_uuid.(gens))
193+
@test get_monitored_components(fo_iter) == Set(IS.get_uuid.(gens))
194+
195+
# Construction silently dedups duplicate UUIDs
196+
fo_dup = GeometricDistributionForcedOutage(;
197+
mean_time_to_recovery = 1.0,
198+
outage_transition_probability = 0.5,
199+
monitored_components = [uuid1, uuid1, uuid2],
200+
)
201+
@test get_monitored_components(fo_dup) == Set([uuid1, uuid2])
194202

195203
# Same for PlannedOutage and FixedForcedOutage
196204
po = PlannedOutage(; outage_schedule = "1", monitored_components = [gen1])
197-
@test get_monitored_components(po) == [uuid1]
205+
@test get_monitored_components(po) == Set([uuid1])
198206
ff = FixedForcedOutage(; outage_status = 1.0, monitored_components = [uuid2])
199-
@test get_monitored_components(ff) == [uuid2]
207+
@test get_monitored_components(ff) == Set([uuid2])
200208

201209
# set_monitored_components! accepts UUID and Device iterables
202210
o = FixedForcedOutage(; outage_status = 0.0)
203211
set_monitored_components!(o, [uuid1])
204-
@test get_monitored_components(o) == [uuid1]
212+
@test get_monitored_components(o) == Set([uuid1])
205213
set_monitored_components!(o, [gen2])
206-
@test get_monitored_components(o) == [uuid2]
214+
@test get_monitored_components(o) == Set([uuid2])
207215
set_monitored_components!(o, Base.UUID[])
208216
@test isempty(get_monitored_components(o))
209217
# set_ also accepts a FlattenIteratorWrapper from get_components
210218
set_monitored_components!(o, get_components(ThermalStandard, sys))
211-
@test Set(get_monitored_components(o)) == Set(IS.get_uuid.(gens))
219+
@test get_monitored_components(o) == Set(IS.get_uuid.(gens))
212220
set_monitored_components!(o, Base.UUID[])
213221

214-
# add_monitored_component! with single UUID or Device, including dedupe
222+
# add_monitored_component! with single UUID or Device, including dedup
215223
add_monitored_component!(o, uuid1)
216224
add_monitored_component!(o, gen2)
217-
@test get_monitored_components(o) == [uuid1, uuid2]
225+
@test get_monitored_components(o) == Set([uuid1, uuid2])
218226
add_monitored_component!(o, gen1) # duplicate, should no-op
219-
@test get_monitored_components(o) == [uuid1, uuid2]
227+
@test get_monitored_components(o) == Set([uuid1, uuid2])
228+
@test length(get_monitored_components(o)) == 2
220229

221230
# add_monitored_components! with iterables: Vector, generator, FlattenIteratorWrapper
222231
o2 = FixedForcedOutage(; outage_status = 0.0)
223232
add_monitored_components!(o2, [uuid1, gen2]) # mixed UUID + Device
224-
@test get_monitored_components(o2) == [uuid1, uuid2]
233+
@test get_monitored_components(o2) == Set([uuid1, uuid2])
225234
add_monitored_components!(o2, (g for g in gens[1:2])) # generator, all already present
226-
@test get_monitored_components(o2) == [uuid1, uuid2]
235+
@test get_monitored_components(o2) == Set([uuid1, uuid2])
227236
o3 = FixedForcedOutage(; outage_status = 0.0)
228237
add_monitored_components!(o3, get_components(ThermalStandard, sys))
229-
@test Set(get_monitored_components(o3)) == Set(IS.get_uuid.(gens))
238+
@test get_monitored_components(o3) == Set(IS.get_uuid.(gens))
230239

231240
# remove_monitored_component! with single UUID or Device
232241
remove_monitored_component!(o, uuid1)
233-
@test get_monitored_components(o) == [uuid2]
242+
@test get_monitored_components(o) == Set([uuid2])
234243
remove_monitored_component!(o, gen2)
235244
@test isempty(get_monitored_components(o))
236245
# Removing absent UUID is a no-op
@@ -287,14 +296,15 @@ end
287296
to_json(sys, path; force = true)
288297
sys2 = System(path)
289298

290-
# Every outage must come back with the same monitored UUIDs in the same
291-
# order, and each UUID must still resolve to a Device in the new system.
292-
expected_uuids = IS.get_uuid.(gens)
299+
# Every outage must come back with the same monitored UUIDs (set semantics —
300+
# order is not preserved), and each UUID must still resolve to a Device in
301+
# the new system.
302+
expected_uuids = Set(IS.get_uuid.(gens))
293303
outages2 = collect(get_supplemental_attributes(Outage, sys2))
294304
@test length(outages2) == 4
295305
for outage in outages2
296306
uuids = get_monitored_components(outage)
297-
@test eltype(uuids) === Base.UUID
307+
@test uuids isa Set{Base.UUID}
298308
@test uuids == expected_uuids
299309
for uuid in uuids
300310
comp = IS.get_component(sys2, uuid)

0 commit comments

Comments
 (0)