Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/src/explanation/supplemental_attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,62 @@ end
- [`GeometricDistributionForcedOutage`](@ref)
- [`PlannedOutage`](@ref)

#### Narrowing post-contingency monitoring

Every concrete [`Outage`](@ref) carries a `monitored_components` field of type
`Vector{Base.UUID}`. It identifies the [`Device`](@ref)s whose post-contingency
state a downstream simulation package (e.g., PowerSimulations) should model when
this outage occurs. Limiting the list reduces the number of post-outage variables
and constraints in security-constrained models.

PowerSystems itself does not attach meaning to the contents of the list. In
particular, an empty `monitored_components` is left for the consumer to
interpret — typical conventions are "monitor nothing" (skip post-contingency
modeling) or "monitor everything" (preserve full N-1 behavior). Pick the policy
that matches your downstream model.

The constructor accepts any iterable whose elements are `Base.UUID` or
`Device` — for example a `Vector`, a generator expression, or the iterator
returned by [`get_components`](@ref). Devices are converted to UUIDs
internally:

```julia
gen1 = get_component(ThermalStandard, system, "gen1")
gen2 = get_component(ThermalStandard, system, "gen2")
outage = FixedForcedOutage(;
outage_status = 0.0,
monitored_components = [gen1, gen2],
)
add_supplemental_attribute!(system, gen1, outage)

# Equivalent — every ThermalStandard in the system:
outage_all = FixedForcedOutage(;
outage_status = 0.0,
monitored_components = get_components(ThermalStandard, system),
)
```

Use the dedicated accessors to inspect or update the list at any time. The
singular `add_/remove_*!` methods take one `UUID` or `Device`; the plural
`add_/remove_*s!` and `set_` methods take any iterable of either.
`set_monitored_components!` requires the list to be empty — call
`clear_monitored_components!` first to replace an existing list:

```julia
get_monitored_components(outage) # → Vector{UUID}
clear_monitored_components!(outage) # wipe
set_monitored_components!(outage, get_components(Line, system)) # populate (must be empty)
add_monitored_component!(outage, gen2) # append one (deduped)
add_monitored_components!(outage, [gen1, gen2]) # append many
remove_monitored_component!(outage, gen1) # remove one
remove_monitored_components!(outage, [gen1, gen2]) # remove many
```

When `system.runchecks == true`, `add_supplemental_attribute!` resolves each
UUID against the parent system and raises an `ArgumentError` for any UUID that
does not point to a `Device` in the system. With `runchecks = false`, UUIDs are
accepted as-is and resolution is deferred to the consumer.

### Plant Attributes

Plant attributes are a specialized category of supplemental attributes for grouping individual
Expand Down
7 changes: 7 additions & 0 deletions src/PowerSystems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,13 @@ export FixedForcedOutage
export get_mean_time_to_recovery
export get_outage_transition_probability
export get_outage_schedule
export get_monitored_components
export set_monitored_components!
export clear_monitored_components!
export add_monitored_component!
export add_monitored_components!
export remove_monitored_component!
export remove_monitored_components!

# Impedance Correction Data
export ImpedanceCorrectionData
Expand Down
24 changes: 23 additions & 1 deletion src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,28 @@ function add_supplemental_attribute!(
return IS.add_supplemental_attribute!(sys.data, component, attribute)
end

function add_supplemental_attribute!(
sys::System,
component::Component,
outage::Outage,
)
if get_runchecks(sys)
for uuid in get_monitored_components(outage)
comp = IS.get_component(sys, uuid) # throws ArgumentError on miss
if !(comp isa Device)
throw(
ArgumentError(
"monitored_components on $(typeof(outage)) references UUID " *
"$(uuid), which resolves to $(typeof(comp)); only " *
"Device subtypes are allowed",
),
)
end
end
end
return IS.add_supplemental_attribute!(sys.data, component, outage)
end

"""
Begin an update of supplemental attributes. Use this function when adding
or removing many supplemental attributes in order to improve performance.
Expand Down Expand Up @@ -2432,7 +2454,7 @@ function check_ac_transmission_rate_values(sys::System)
end

"""
Serialize a [System](@ref) instance. Returns a `Dict{String, Any}`
Serialize a [System](@ref) instance. Returns a `Dict{String, Any}`
of the form `Dict("data_format_version" => "1.0", "field1" => serialize(sys.field1), ...)`,
which can then be written to a JSON3 file.
"""
Expand Down
120 changes: 115 additions & 5 deletions src/outages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,112 @@ Supertype for outage contingencies representing planned or unplanned equipment o

Concrete subtypes include [`GeometricDistributionForcedOutage`](@ref),
[`PlannedOutage`](@ref), and [`FixedForcedOutage`](@ref).

# Interface for custom subtypes

Subtypes are expected to provide the following fields, or override the matching
accessors via multiple dispatch:

- `monitored_components::Set{Base.UUID}` — UUIDs of devices whose
post-contingency state should be modeled. The default
[`get_monitored_components`](@ref) reads `value.monitored_components`; override
it if your subtype does not carry the field directly.
- `internal::InfrastructureSystemsInternal` — accessed via `get_internal`.

The default [`supports_time_series`](@ref) returns `true`; override for custom
outage types that do not support time series.
"""
abstract type Outage <: Contingency end

abstract type UnplannedOutage <: Outage end

"""
All PowerSystems [Outage](@ref) types support time series. This can be overridden for custom
All PowerSystems [Outage](@ref) types support time series. This can be overridden for custom
outage types that do not support time series.
"""
supports_time_series(::Outage) = true

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

# Public API for monitored_components accepts UUIDs or Devices interchangeably.
_as_uuid(uuid::Base.UUID) = uuid
_as_uuid(device::Device) = IS.get_uuid(device)

"""
Get the set of [`Device`](@ref) UUIDs whose post-contingency state should be modeled
when this outage occurs. PowerSystems does not assign meaning to an empty set;
downstream consumers (e.g., PowerSimulations) decide whether empty means "monitor
nothing" or "monitor everything".
"""
get_monitored_components(value::Outage) = value.monitored_components
Comment thread
jd-lara marked this conversation as resolved.

"""
Replace the monitored-components set for an [`Outage`](@ref) with the contents
of `items`. Accepts any iterable whose elements are `Base.UUID` or
[`Device`](@ref) (e.g., a `Vector`, a generator, or the iterator returned by
[`get_components`](@ref)). Devices are converted to their UUIDs internally.
Pass an empty iterable (or call [`clear_monitored_components!`](@ref)) to
clear the set.
"""
function set_monitored_components!(value::Outage, items)
empty!(value.monitored_components)
for x in items
push!(value.monitored_components, _as_uuid(x))
end
return value.monitored_components
end

"""
Empty the monitored-components set of an [`Outage`](@ref). Returns the (now empty)
underlying set.
"""
function clear_monitored_components!(value::Outage)
empty!(value.monitored_components)
return value.monitored_components
end

"""
Add a `Base.UUID` or [`Device`](@ref) to the monitored-components set of
an [`Outage`](@ref). Adding an existing UUID is a no-op.
"""
function add_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
push!(value.monitored_components, _as_uuid(x))
return value.monitored_components
end

"""
Add every element of `items` (each a `Base.UUID` or [`Device`](@ref)) to
the monitored-components set of an [`Outage`](@ref). Accepts any iterable, including
the iterator returned by [`get_components`](@ref). Adding an existing UUID is a no-op.
"""
function add_monitored_components!(value::Outage, items)
for x in items
add_monitored_component!(value, x)
end
return value.monitored_components
end

"""
Remove a `Base.UUID` or [`Device`](@ref) from the monitored-components set
of an [`Outage`](@ref). No-op when the entry is not present.
"""
function remove_monitored_component!(value::Outage, x::Union{Base.UUID, Device})
delete!(value.monitored_components, _as_uuid(x))
return
end

"""
Remove every element of `items` (each a `Base.UUID` or [`Device`](@ref)) from
the monitored-components set of an [`Outage`](@ref). Accepts any iterable.
"""
function remove_monitored_components!(value::Outage, items)
for x in items
remove_monitored_component!(value, x)
end
return
end

"""
Attribute that contains information regarding forced outages where the transition probabilities
are modeled with geometric distributions. The outage probabilities and recovery probabilities can be modeled as time
Expand All @@ -25,32 +117,37 @@ series.
# Arguments
- `mean_time_to_recovery::Float64`: Time elapsed to recovery after a failure in Milliseconds.
- `outage_transition_probability::Float64`: Characterizes the probability of failure (1 - p) in the geometric distribution.
- `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.
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
"""
struct GeometricDistributionForcedOutage <: UnplannedOutage
mean_time_to_recovery::Float64
outage_transition_probability::Float64
monitored_components::Set{Base.UUID}
internal::InfrastructureSystemsInternal
end

"""
GeometricDistributionForcedOutage(; mean_time_to_recovery, outage_transition_probability, internal)
GeometricDistributionForcedOutage(; mean_time_to_recovery, outage_transition_probability, monitored_components, internal)

Construct a [`GeometricDistributionForcedOutage`](@ref).

# Arguments
- `mean_time_to_recovery::Float64`: (default: `0.0`) Time elapsed to recovery after a failure in Milliseconds.
- `outage_transition_probability::Float64`: (default: `0.0`) Characterizes the probability of failure (1 - p) in the geometric distribution.
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
"""
function GeometricDistributionForcedOutage(;
mean_time_to_recovery = 0.0,
outage_transition_probability = 0.0,
monitored_components = Base.UUID[],
internal = InfrastructureSystemsInternal(),
)
return GeometricDistributionForcedOutage(
mean_time_to_recovery,
outage_transition_probability,
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
internal,
)
end
Expand All @@ -67,28 +164,33 @@ Attribute that contains information regarding planned outages.

# Arguments
- `outage_schedule::String`: String name of the time series used for the scheduled outages
- `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.
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
"""
struct PlannedOutage <: Outage
outage_schedule::String
monitored_components::Set{Base.UUID}
internal::InfrastructureSystemsInternal
end

"""
PlannedOutage(; outage_schedule, internal)
PlannedOutage(; outage_schedule, monitored_components, internal)

Construct a [`PlannedOutage`](@ref).

# Arguments
- `outage_schedule::String`: String name of the time series used for the scheduled outages
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
"""
function PlannedOutage(;
outage_schedule,
monitored_components = Base.UUID[],
internal = InfrastructureSystemsInternal(),
)
return PlannedOutage(
outage_schedule,
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
internal,
)
end
Expand All @@ -102,27 +204,35 @@ The time series data for fixed outages can be obtained from the simulation of a

# Arguments
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
- `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.
- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems internal reference
"""
struct FixedForcedOutage <: UnplannedOutage
outage_status::Float64
monitored_components::Set{Base.UUID}
internal::InfrastructureSystemsInternal
end

"""
FixedForcedOutage(; outage_status, internal)
FixedForcedOutage(; outage_status, monitored_components, internal)

Construct a [`FixedForcedOutage`](@ref).

# Arguments
- `outage_status::Float64`: The forced outage status in the model. 1 represents outaged and 0 represents available.
- `monitored_components`: (default: `Base.UUID[]`) Any iterable of `Base.UUID` or [`Device`](@ref). Devices are converted to their UUIDs internally; duplicates are collapsed.
- `internal::InfrastructureSystemsInternal`: (default: `InfrastructureSystemsInternal()`) (**Do not modify.**) PowerSystems internal reference
"""
function FixedForcedOutage(;
outage_status,
monitored_components = Base.UUID[],
internal = InfrastructureSystemsInternal(),
)
return FixedForcedOutage(outage_status, internal)
return FixedForcedOutage(
outage_status,
Set{Base.UUID}(_as_uuid(x) for x in monitored_components),
internal,
)
end

"""Get [`FixedForcedOutage`](@ref) `outage_status`."""
Expand Down
Loading
Loading