Skip to content
Draft
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
114 changes: 94 additions & 20 deletions src/network_modification.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,28 @@ Construct a `NetworkModification` from a branch component using network
reduction reverse maps to classify the branch as direct, parallel, or series.
"""
function NetworkModification(mat::PowerNetworkMatrix, branch::PSY.ACTransmission)
nr = get_network_reduction_data(mat)
arc_lookup = get_arc_lookup(mat)
arc_sus = _get_arc_susceptances(mat)
mods = _classify_branch_modification(nr, arc_lookup, arc_sus, branch)
return NetworkModification(
get_name(branch),
mods,
)
end

"""
$(TYPEDSIGNATURES)

Construct a `NetworkModification` from a `ThreeWindingTransformer` component.
Automatically decomposes the transformer into its three winding arcs and classifies
each one. For a partial outage (single winding trip), use a
`ThreeWindingTransformerWinding` instead.
"""
function NetworkModification(
mat::PowerNetworkMatrix,
branch::PSY.ThreeWindingTransformer,
)
nr = get_network_reduction_data(mat)
arc_lookup = get_arc_lookup(mat)
arc_sus = _get_arc_susceptances(mat)
Expand Down Expand Up @@ -238,11 +260,21 @@ function _classify_outage_component!(
)
tag, arc_tuple = _resolve_branch_arc(nr, component)

if tag === :direct || tag === :transformer3w
if tag === :direct
arc_idx = arc_lookup[arc_tuple]
b_arc = arc_susceptances[arc_idx]
dy11, dy12, dy21, dy22 = _compute_arc_ybus_delta(nr, arc_tuple, -b_arc)
push!(direct_mods, ArcModification(arc_idx, -b_arc, dy11, dy12, dy21, dy22))
elseif tag === :transformer3w
arc_idx = arc_lookup[arc_tuple]
b_arc = arc_susceptances[arc_idx]
tr = nr.transformer3W_map[arc_tuple]
Y11, Y12, Y21, Y22 = ybus_branch_entries(tr)
push!(direct_mods, ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
))
Comment on lines +273 to +277
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
push!(direct_mods, ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
))
push!(
direct_mods,
ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
),
)

elseif tag === :parallel
arc_idx = arc_lookup[arc_tuple]
b_circuit = PSY.get_series_susceptance(component)
Expand All @@ -256,11 +288,11 @@ function _classify_outage_component!(
end
push!(series_components_by_arc[arc_idx], component)
else
@info "Branch $(PSY.get_name(component)) not found in any reduction map. " *
@info "Branch $(get_name(component)) not found in any reduction map. " *
"The component may have been eliminated by a radial reduction."
return
end
push!(component_names, PSY.get_name(component))
push!(component_names, get_name(component))
return
end

Expand Down Expand Up @@ -325,23 +357,31 @@ function _classify_outage_component!(
end

function _classify_outage_component!(
::NetworkReductionData,
::Dict,
::Vector{Float64},
::Dict{Int, Int},
nr::NetworkReductionData,
arc_lookup::Dict,
arc_susceptances::Vector{Float64},
bus_lookup::Dict{Int, Int},
component::PSY.ThreeWindingTransformer,
::Vector{ArcModification},
::Vector{ArcModification},
::Dict{Int, Vector{PSY.ACTransmission}},
::Dict{Int, Tuple{Int, Int}},
::Vector{ShuntModification},
::Vector{String},
direct_mods::Vector{ArcModification},
parallel_mods::Vector{ArcModification},
series_components_by_arc::Dict{Int, Vector{PSY.ACTransmission}},
series_arc_tuples::Dict{Int, Tuple{Int, Int}},
shunt_mods::Vector{ShuntModification},
component_names::Vector{String},
)
error(
"Outages on ThreeWindingTransformer components are not yet supported. " *
"Component: $(PSY.get_name(component)). " *
"Use individual ThreeWindingTransformerWinding arcs instead.",
)
for winding_num in 1:3
winding = ThreeWindingTransformerWinding(component, winding_num)
if !get_equivalent_available(winding)
continue
end
_classify_outage_component!(
nr, arc_lookup, arc_susceptances, bus_lookup, winding,
direct_mods, parallel_mods,
series_components_by_arc, series_arc_tuples,
shunt_mods, component_names,
)
end
return
end

"""
Expand All @@ -360,6 +400,30 @@ function _classify_branch_modification(
_assert_not_phase_shifting(branch)
end

"""
_classify_branch_modification(nr, arc_lookup, arc_susceptances, branch::PSY.ThreeWindingTransformer) -> Vector{ArcModification}

Classify a `ThreeWindingTransformer` by decomposing it into its three winding arcs
and classifying each one individually. Returns arc modifications for all windings
present in the network.
"""
function _classify_branch_modification(
nr::NetworkReductionData,
arc_lookup::Dict,
arc_susceptances::Vector{Float64},
branch::PSY.ThreeWindingTransformer,
)::Vector{ArcModification}
mods = ArcModification[]
for winding_num in 1:3
winding = ThreeWindingTransformerWinding(branch, winding_num)
if !get_equivalent_available(winding)
continue
end
append!(mods, _classify_branch_modification(nr, arc_lookup, arc_susceptances, winding))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
append!(mods, _classify_branch_modification(nr, arc_lookup, arc_susceptances, winding))
append!(
mods,
_classify_branch_modification(nr, arc_lookup, arc_susceptances, winding),
)

end
return mods
end

function _classify_branch_modification(
nr::NetworkReductionData,
arc_lookup::Dict,
Expand All @@ -368,11 +432,21 @@ function _classify_branch_modification(
)::Vector{ArcModification}
tag, arc_tuple = _resolve_branch_arc(nr, branch)

if tag === :direct || tag === :transformer3w
if tag === :direct
arc_idx = arc_lookup[arc_tuple]
b_arc = arc_susceptances[arc_idx]
dy11, dy12, dy21, dy22 = _compute_arc_ybus_delta(nr, arc_tuple, -b_arc)
return [ArcModification(arc_idx, -b_arc, dy11, dy12, dy21, dy22)]
elseif tag === :transformer3w
arc_idx = arc_lookup[arc_tuple]
b_arc = arc_susceptances[arc_idx]
tr = nr.transformer3W_map[arc_tuple]
Y11, Y12, Y21, Y22 = ybus_branch_entries(tr)
return [ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
)]
Comment on lines +445 to +449
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
return [ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
)]
return [
ArcModification(
arc_idx, -b_arc,
YBUS_ELTYPE(-Y11), YBUS_ELTYPE(-Y12),
YBUS_ELTYPE(-Y21), YBUS_ELTYPE(-Y22),
),
]

elseif tag === :parallel
arc_idx = arc_lookup[arc_tuple]
b_circuit = PSY.get_series_susceptance(branch)
Expand All @@ -385,7 +459,7 @@ function _classify_branch_modification(
dy11, dy12, dy21, dy22 = _compute_arc_ybus_delta(nr, arc_tuple, delta_b)
return [ArcModification(arc_idx, delta_b, dy11, dy12, dy21, dy22)]
else
@info "Branch $(PSY.get_name(branch)) not found in any reduction map. " *
@info "Branch $(get_name(branch)) not found in any reduction map. " *
"The component may have been eliminated by a radial reduction."
return ArcModification[]
end
Expand Down
69 changes: 69 additions & 0 deletions test/test_network_modification.jl
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,72 @@ end
end
end
end

@testset "NetworkModification: full ThreeWindingTransformer outage" begin
sys = PSB.build_system(PSB.PSITestSystems, "case10_radial_series_reductions")
trf = first(PSY.get_components(PSY.ThreeWindingTransformer, sys))
vptdf = VirtualPTDF(sys)

# Full 3WT outage should produce 3 arc modifications (one per winding)
mod = NetworkModification(vptdf, trf)
@test length(mod.arc_modifications) == 3
@test mod.label == PSY.get_name(trf)

# Each modification should have negative delta_b (removing susceptance)
for am in mod.arc_modifications
@test am.delta_b < 0
end

# Should produce valid PTDF rows
row = get_post_modification_ptdf_row(vptdf, 1, mod)
@test length(row) == length(PNM.get_bus_axis(vptdf))
end

@testset "NetworkModification: single ThreeWindingTransformerWinding outage" begin
sys = PSB.build_system(PSB.PSITestSystems, "case10_radial_series_reductions")
trf = first(PSY.get_components(PSY.ThreeWindingTransformer, sys))
vptdf = VirtualPTDF(sys)

# Single winding outage
for w in 1:3
winding = PNM.ThreeWindingTransformerWinding(trf, w)
mod = NetworkModification(vptdf, winding)
@test length(mod.arc_modifications) == 1
@test mod.arc_modifications[1].delta_b < 0
end
end

@testset "NetworkModification: partial ThreeWindingTransformer (disabled winding)" begin
sys = PSB.build_system(PSB.PSITestSystems, "case10_radial_series_reductions")
trf = first(PSY.get_components(PSY.ThreeWindingTransformer, sys))

# Disable one winding before building the matrix
PSY.set_available_secondary!(trf, false)
vptdf = VirtualPTDF(sys)

# Full 3WT outage should only produce 2 mods (secondary is unavailable)
mod = NetworkModification(vptdf, trf)
@test length(mod.arc_modifications) == 2
end

@testset "NetworkModification: ThreeWindingTransformer via Outage attribute" begin
sys = PSB.build_system(PSB.PSITestSystems, "case10_radial_series_reductions")
trf = first(PSY.get_components(PSY.ThreeWindingTransformer, sys))

# Attach an outage supplemental attribute to the 3WT
outage = GeometricDistributionForcedOutage(;
mean_time_to_recovery = 0.0,
outage_transition_probability = 0.0,
)
add_supplemental_attribute!(sys, trf, outage)
vptdf = VirtualPTDF(sys)

# Construct modification via Outage path
mod = NetworkModification(vptdf, sys, outage)
@test length(mod.arc_modifications) == 3
@test !isempty(mod.label)

# Should produce valid PTDF rows
row = get_post_modification_ptdf_row(vptdf, 1, mod)
@test length(row) == length(PNM.get_bus_axis(vptdf))
end
Loading