DegreeTwoReduction

In this tutorial the DegreeTwoReduction network reduction algorithm is presented. This reduction eliminates buses with exactly two connections by combining the incident branches into a single equivalent branch while preserving the electrical characteristics of the network.

Before diving into this tutorial we encourage the user to load PowerNetworkMatrices, hit the ? key in the REPL terminal and look for the documentation of DegreeTwoReduction.

Understanding Degree-Two Buses

Degree-two buses are nodes in the network topology that have exactly two connections. These intermediate buses can be eliminated by replacing the two incident branches with a single equivalent branch, simplifying the network while maintaining its electrical behavior. The reduction is performed recursively, identifying and eliminating chains of degree-two nodes to maximize network simplification.

Basic Usage of DegreeTwoReduction

The DegreeTwoReduction can be applied when constructing various network matrices. The most common use case is with the Ybus matrix:

julia> using PowerNetworkMatrices
julia> using PowerSystemCaseBuilder
julia> import PowerNetworkMatrices as PNM
julia> import PowerSystemCaseBuilder as PSB
julia> # Load a test system sys = PSB.build_system(PSSEParsingTestSystems, "psse_14_network_reduction_test_system")┌ Info: Building new system psse_14_network_reduction_test_system from raw data sys_descriptor.raw_data = "/home/runner/.julia/artifacts/edcb5940e84a802a86ad4f2223214d33121ac044/PowerSystemsTestData-4.0.2/psse_raw/case14_reductions.raw" [ Info: The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, switches, breakers, IC tables, transformers, facts, and dc lines [ Info: Found 2 breakers in the system modeled as branches. [ Info: Parsing PSS(R)E InterAreaTransfer data into a PowerModels Dict... [ Info: Parsing PSS(R)E AreaInterchange data into a PowerModels Dict... [ Info: Parsing PSS(R)E Zone data into a PowerModels Dict... [ Info: Parsing PSS(R)E Bus data into a PowerModels Dict... [ Info: Parsing PSS(R)E Branch data into a PowerModels Dict... [ Info: Parsing PSS(R)E Switches & Breakers data into a PowerModels Dict... [ Info: Adding PSS(R)E Multi-section Lines data into the branches PowerModels Dict... [ Info: Parsing PSS(R)E Transformer data into a PowerModels Dict... [ Info: Parsing PSS(R)E Load data into a PowerModels Dict... [ Info: Parsing PSS(R)E Fixed & Switched Shunt data into a PowerModels Dict... [ Info: Parsing PSS(R)E Generator data into a PowerModels Dict... [ Info: Parsing PSS(R)E FACTs devices data into a PowerModels Dict... [ Info: FACTs are supported via a simplification approach for terminal_bus = 0 (STATCOM operation) [ Info: Parsing PSS(R)E Two-Terminal and VSC DC line data into a PowerModels Dict... [ Info: Parsing PSS(R)E Transformer Impedance Correction Tables data into a PowerModels Dict... ┌ Warning: Parsing PSS(R)E Substation data into a PowerModels Dict... @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/parsers/pm_io/psse.jl:2169 ┌ Warning: This PSS(R)E parser currently doesn't support Storage data parsing... @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/parsers/pm_io/psse.jl:2194 [ Info: angmin and angmax values are 0, widening these values on branch 4 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 1 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 12 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 20 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 2 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 6 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 11 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 13 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 5 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 15 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 16 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 14 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 7 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 8 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 17 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 10 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 19 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 9 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 18 to +/- 60.0 deg. [ Info: angmin and angmax values are 0, widening these values on branch 3 to +/- 60.0 deg. [ Info: this code only supports positive rate_a values, changing the value on branch 4 to 30212.3223 [ Info: this code only supports positive rate_a values, changing the value on branch 1 to 755882.6106 [ Info: this code only supports positive rate_a values, changing the value on branch 12 to 8123.5674 [ Info: this code only supports positive rate_a values, changing the value on branch 20 to 4013.3252 [ Info: this code only supports positive rate_a values, changing the value on branch 2 to 755882.6106 [ Info: this code only supports positive rate_a values, changing the value on branch 6 to 48361.429 [ Info: this code only supports positive rate_a values, changing the value on branch 11 to 89847.2382 [ Info: this code only supports positive rate_a values, changing the value on branch 13 to 27810.6871 [ Info: this code only supports positive rate_a values, changing the value on branch 5 to 48361.429 [ Info: this code only supports positive rate_a values, changing the value on branch 15 to 97552.3062 [ Info: this code only supports positive rate_a values, changing the value on branch 16 to 4966.8601 [ Info: this code only supports positive rate_a values, changing the value on branch 14 to 2385.1085 [ Info: this code only supports positive rate_a values, changing the value on branch 7 to 14755.2072 [ Info: this code only supports positive rate_a values, changing the value on branch 8 to 22717.2953 [ Info: this code only supports positive rate_a values, changing the value on branch 17 to 4966.8601 [ Info: this code only supports positive rate_a values, changing the value on branch 10 to 14959.4086 [ Info: this code only supports positive rate_a values, changing the value on branch 19 to 6019.9878 [ Info: this code only supports positive rate_a values, changing the value on branch 9 to 55499.9403 [ Info: this code only supports positive rate_a values, changing the value on branch 18 to 12039.9755 [ Info: this code only supports positive rate_a values, changing the value on branch 3 to 30212.3223 [ Info: the voltage setpoint on generator 4 does not match the value at bus 106 [ Info: the voltage setpoint on generator 7 does not match the value at bus 111 [ Info: the voltage setpoint on generator 3 does not match the value at bus 102 ┌ Info: Constructing System from Power Models data["name"] = "case14_reductions" data["source_type"] = "pti" [ Info: Reading bus data [ Info: Reading Load data in PowerModels dict to populate System ... [ Info: Reading LoadZones data in PowerModels dict to populate System ... [ Info: Reading Zone data [ Info: Reading generator data ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-106-1: name: generator-106-1 available: true status: true bus: ACBus: BUS 106_106 active_power: 2.0 reactive_power: -0.5 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-101-1: name: generator-101-1 available: true status: true bus: ACBus: BUS 101_101 active_power: 20.87711 reactive_power: -24.406309999999998 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-108-1: name: generator-108-1 available: true status: true bus: ACBus: BUS 108_108 active_power: 5.00094 reactive_power: -0.04934 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-101-2: name: generator-101-2 available: true status: true bus: ACBus: BUS 101_101 active_power: 10.8924 reactive_power: -12.73372 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-110-1: name: generator-110-1 available: true status: true bus: ACBus: BUS 110_110 active_power: 3.0 reactive_power: 27.26679 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-111-1: name: generator-111-1 available: true status: true bus: ACBus: BUS 111_111 active_power: 0.9 reactive_power: 7.84759 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 ┌ Warning: Invalid range valid_info.struct_name = "ThermalStandard" field_name = "active_power_limits" valid_range = Dict{String, Any} with 2 entries: … valid_info.ist_struct = ThermalStandard: generator-102-1: name: generator-102-1 available: true status: true bus: ACBus: BUS 102_102 active_power: 3.0 reactive_power: -0.5 rating: 141.40721410168578 active_power_limits: (min = -99.99, max = 99.99) reactive_power_limits: (min = -99.99, max = 99.99) ramp_limits: (up = 99.99, down = 99.99) operation_cost: PowerSystems.ThermalGenerationCost composed of variable: InfrastructureSystems.CostCurve{InfrastructureSystems.QuadraticCurve} base_power: 100.0 time_limits: nothing must_run: false prime_mover_type: PowerSystems.PrimeMoversModule.PrimeMovers.OT = 19 fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.OTHER = 35 services: 0-element Vector{PowerSystems.Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}("WPF" => 1.0, "GTAP" => 1.0, "IREG" => 0, "WMOD" => 0, "rt" => 0.0, "x" => 1.0, "xt" => 0.0, "r" => 0.0, "RMPCT" => 100.0) InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: false @ InfrastructureSystems ~/.julia/packages/InfrastructureSystems/0oq8N/src/validation.jl:219 [ Info: Reading switch data [ Info: Reading breaker data [ Info: Reading branch data [ Info: Reading Impedance Correction Table data ┌ Warning: rating 30212.32 MW for BUS 101_101-BUS 116_117-i_2 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 755882.61 MW for BUS 101_101-BUS 115_115-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 8123.57 MW for BUS 106_106-BUS 111_111-i_2 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: Transformer BUS 109_109-BUS 110_110-i_1 per-unit reactance 0.03 is lower than the typical range (min = 0.05, max = 0.2). Check if the reactance source data is correct. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:224 ┌ Warning: rating 4013.33 MW for BUS 109_109-BUS 110_110-i_1 is 2x larger than the max expected rating 1383.0 MW for Transformer at a 500.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: rating 755882.61 MW for BUS 115_115-BUS 102_102-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 48361.43 MW for BUS 116_118-BUS 105_105-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 89847.24 MW for BUS 106_106-BUS 111_111-i_1 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 27810.69 MW for BUS 106_106-BUS 112_112-i_1 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 48361.43 MW for BUS 116_117-BUS 116_118-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 97552.31 MW for BUS 109_109-BUS 114_114-i_1 is 2x larger than the max expected rating 3464.0 MW for Line at a 500.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 4966.86 MW for BUS 110_110-BUS 111_111-i_1 is 2x larger than the max expected rating 797.0 MW for Line at a 230.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 2385.11 MW for BUS 107_107-BUS 108_108-i_1 is 2x larger than the max expected rating 115.0 MW for Line at a 69.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 14755.21 MW for BUS 102_102-BUS 103_103-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 22717.3 MW for BUS 102_102-BUS 104_104-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 4966.86 MW for BUS 103_103-BUS 116_116-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 14959.41 MW for BUS 103_103-BUS 104_104-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: Transformer BUS 105_105-BUS 106_106-i_1 per-unit reactance 0.02 is lower than the typical range (min = 0.05, max = 0.2). Check if the reactance source data is correct. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:224 ┌ Warning: rating 6019.99 MW for BUS 105_105-BUS 106_106-i_1 is 2x larger than the max expected rating 470.0 MW for Transformer at a 230.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: rating 55499.94 MW for BUS 102_102-BUS 105_105-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: Transformer BUS 104_104-BUS 109_109-i_1 per-unit reactance 0.01 is lower than the typical range (min = 0.05, max = 0.2). Check if the reactance source data is correct. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:224 ┌ Warning: rating 12039.98 MW for BUS 104_104-BUS 109_109-i_1 is 2x larger than the max expected rating 1383.0 MW for Transformer at a 500.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: rating 30212.32 MW for BUS 101_101-BUS 116_117-i_1 is 2x larger than the max expected rating 344.0 MW for Line at a 138.0 kV Voltage level. @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:104 [ Info: Reading switched shunt data [ Info: Reading shunt data [ Info: Reading DC Line data [ Info: Reading VSC Line data [ Info: Reading FACTS data [ Info: Reading storage data [ Info: Reading 3W transformer data [ Info: Reading Impedance Correction Table data [ Info: Transformer3W BUS 109_109-BUS 104_104-BUS 107_107-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual [ Info: Transformer3W BUS 109_109-BUS 104_104-BUS 107_107-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual [ Info: Transformer3W BUS 109_109-BUS 104_104-BUS 107_107-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual [ Info: Transformer3W BUS 113_113-BUS 110_110-BUS 114_114-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual [ Info: Transformer3W BUS 113_113-BUS 110_110-BUS 114_114-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual [ Info: Transformer3W BUS 113_113-BUS 110_110-BUS 114_114-i_1 rating value: 0.0. Unbounded value implied as per PSSe Manual ┌ Warning: No substation data found @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/parsers/power_models_data.jl:176 [ Info: Serialized System to /home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/psse_14_network_reduction_test_system.json [ Info: Serialized System metadata to /home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/psse_14_network_reduction_test_system_metadata.json System ┌───────────────────┬─────────────┐ │ Property Value │ ├───────────────────┼─────────────┤ │ Name │ │ │ Description │ │ │ System Units Base │ SYSTEM_BASE │ │ Base Power │ 100.0 │ │ Base Frequency │ 60.0 │ │ Num Components │ 103 │ └───────────────────┴─────────────┘ Static Components ┌────────────────────────────┬───────┐ │ Type Count │ ├────────────────────────────┼───────┤ │ ACBus │ 20 │ │ Arc │ 28 │ │ Area │ 1 │ │ DiscreteControlledACBranch │ 2 │ │ FACTSControlDevice │ 1 │ │ FixedAdmittance │ 4 │ │ Line │ 17 │ │ LoadZone │ 1 │ │ PhaseShiftingTransformer │ 1 │ │ StandardLoad │ 13 │ │ SwitchedAdmittance │ 2 │ │ TapTransformer │ 1 │ │ ThermalStandard │ 7 │ │ Transformer2W │ 1 │ │ Transformer3W │ 2 │ │ TwoTerminalLCCLine │ 1 │ │ TwoTerminalVSCLine │ 1 │ └────────────────────────────┴───────┘
julia> # Create Ybus with degree-two reduction ybus = Ybus(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find

Accessing Reduction Information

After applying the reduction, you can access information about which buses were eliminated and how branches were combined:

julia> # Get the network reduction data
       reduction_data = get_network_reduction_data(ybus);
julia> # View the series branch mapping # This shows how multiple branches were combined into composite branches PNM.get_series_branch_map(reduction_data)Dict{Tuple{Int64, Int64}, PowerNetworkMatrices.BranchesSeries} with 3 entries: (108, 1001) => BranchesSeries(Dict{DataType, Vector{<:ACTransmission}}(Line=>… (101, 104) => BranchesSeries(Dict{DataType, Vector{<:ACTransmission}}(Line=>… (101, 102) => BranchesSeries(Dict{DataType, Vector{<:ACTransmission}}(Line=>…
julia> # View the removed buses PNM.get_removed_buses(reduction_data)Set{Int64} with 4 elements: 117 107 115 118
julia> # View the removed arcs (series branches that were combined) PNM.get_removed_arcs(reduction_data)Set{Tuple{Int64, Int64}} with 9 elements: (107, 108) (107, 1001) (118, 104) (115, 102) (101, 117) (112, 113) (104, 105) (101, 115) (117, 118)

Configuration Options

The DegreeTwoReduction provides several configuration options:

Protecting Specific Buses

You can protect certain buses from reduction even if they have degree two:

julia> # Create degree-two reduction that protects specific buses
       reduction = DegreeTwoReduction(; irreducible_buses = [115]);
julia> # Apply to system (if these buses exist in the system) ybus_protected = Ybus(sys; network_reductions = NetworkReduction[reduction]);[ Info: Finding subnetworks via iterative union find
julia> reduction_data_protected = get_network_reduction_data(ybus_protected);
julia> # Compare with unprotected case: PNM.get_removed_buses(reduction_data)Set{Int64} with 4 elements: 117 107 115 118
julia> PNM.get_removed_buses(reduction_data_protected)Set{Int64} with 3 elements: 117 107 118

Handling Reactive Power Injectors

By default, DegreeTwoReduction reduces buses with reactive power injections. You can change this behavior:

julia> # Create reduction that preserves buses with reactive power injections
       reduction = DegreeTwoReduction(; reduce_reactive_power_injectors = false);
julia> # Apply to system ybus_preserve_reactive = Ybus(sys; network_reductions = NetworkReduction[reduction]);[ Info: Finding subnetworks via iterative union find

Combining with Other Network Matrices

The DegreeTwoReduction can be applied to other network matrix types as well:

julia> # Apply to PTDF matrix
       ptdf = PTDF(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find
julia> # Apply to LODF matrix lodf = LODF(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find
julia> # Apply to BA Matrix ba_matrix = BA_Matrix(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find
julia> # Apply to ABA Matrix aba_matrix = ABA_Matrix(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find

Benefits of Degree-Two Reduction

Using DegreeTwoReduction provides several advantages:

  1. Smaller Matrices: Eliminates intermediate buses from network matrices
  2. Faster Computations: Reduced matrix dimensions lead to faster operations
  3. Simplified Topology: Creates a more direct representation of the network
  4. Preserved Accuracy: Maintains exact electrical equivalence for the reduced network

Example: Comparing Matrix Sizes

julia> # Create Ybus without reduction
       ybus_full = Ybus(sys);[ Info: Finding subnetworks via iterative union find
julia> # Create Ybus with degree-two reduction ybus_reduced = Ybus(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union find
julia> # Compare sizes size(ybus_full)(18, 18)
julia> size(ybus_reduced)(14, 14)

Understanding Series Branch Chains

When degree-two buses are eliminated, the reduction algorithm identifies chains of series-connected branches. For example:

Bus A --- Branch 1 --- Bus B --- Branch 2 --- Bus C

If Bus B has degree two, it can be eliminated, and Branches 1 and 2 are combined into a single equivalent branch:

Bus A --- Equivalent Branch --- Bus C

The equivalent branch's electrical parameters (impedance, admittance) are calculated to preserve the overall electrical behavior.

Combining Multiple Reductions

DegreeTwoReduction can be combined with other network reduction algorithms like RadialReduction:

julia> # Apply both radial and degree-two reductions
       reductions = [RadialReduction(), DegreeTwoReduction()];
julia> ybus_multi = Ybus(sys; network_reductions = reductions);[ Info: Finding subnetworks via iterative union find
julia> # Get combined reduction data multi_reduction_data = get_network_reduction_data(ybus_multi);

Order of Reductions

When combining multiple reductions, the order can affect the final result:

julia> # First apply radial, then degree-two
       reductions1 = [RadialReduction(), DegreeTwoReduction()];
julia> ybus1 = Ybus(sys; network_reductions = reductions1)[ Info: Finding subnetworks via iterative union find AC_Ybus_Matrix Dimension 1, [101, 102, 103, 104, 106, 109, 110, 111, 112, 114, 1001, 1002] Dimension 2, [101, 102, 103, 104, 106, 109, 110, 111, 112, 114, 1001, 1002] And data with size (12, 12): 2581.62-1957.33im -2439.02+1951.22im … ⋅ ⋅ -2439.02+1951.22im 2973.92-2451.45im ⋅ ⋅ ⋅ -19.0339+120.449im ⋅ ⋅ -142.595+6.11246im -515.367+376.783im 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ … 0.0-20000.0im ⋅ ⋅ ⋅ ⋅ 0.0-30000.0im
julia> # First apply degree-two, then radial reductions2 = [DegreeTwoReduction(), RadialReduction()];
julia> ybus2 = Ybus(sys; network_reductions = reductions2)[ Info: Finding subnetworks via iterative union find ┌ Warning: When applying both RadialReduction and DegreeTwoReduction, it is likely beneficial to apply RadialReduction first. @ PowerNetworkMatrices ~/work/PowerNetworkMatrices.jl/PowerNetworkMatrices.jl/src/ReductionContainer.jl:32 AC_Ybus_Matrix Dimension 1, [101, 102, 103, 104, 106, 109, 110, 111, 112, 114, 1001, 1002] Dimension 2, [101, 102, 103, 104, 106, 109, 110, 111, 112, 114, 1001, 1002] And data with size (12, 12): 2581.62-1957.33im -2439.02+1951.22im … ⋅ ⋅ -2439.02+1951.22im 2973.92-2451.45im ⋅ ⋅ ⋅ -19.0339+120.449im ⋅ ⋅ -142.595+6.11246im -515.367+376.783im 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ ⋅ 0.0+10000.0im ⋅ ⋅ … 0.0-20000.0im ⋅ ⋅ ⋅ ⋅ 0.0-30000.0im
julia> # Compare results size(ybus1)(12, 12)
julia> size(ybus2)(12, 12)

In this case, the result is the same, however this is not guaranteed. In general, applying RadialReduction first is recommended, as it can create new degree-two buses that can then be eliminated by DegreeTwoReduction.

Important Notes

  • Topology Preservation: The reduction maintains essential network connectivity
  • Reference Bus Protection: Reference (slack) buses are automatically protected from elimination
  • Parallel Paths: The algorithm handles parallel branches correctly
  • Three-Winding Transformers: Special handling for three-winding transformer connections
  • Reversibility: The reduction maintains detailed mapping information for result interpretation
  • Electrical Equivalence: Equivalent branches are computed to maintain exact electrical behavior