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 PowerNetworkMatricesjulia> using PowerSystemCaseBuilderjulia> import PowerNetworkMatrices as PNMjulia> import PowerSystemCaseBuilder as PSBjulia> # 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/uwQkn/src/parsers/pm_io/psse.jl:2204 ┌ Warning: This PSS(R)E parser currently doesn't support Storage data parsing... └ @ PowerSystems ~/.julia/packages/PowerSystems/uwQkn/src/parsers/pm_io/psse.jl:2229 [ 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/euBLZ/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/euBLZ/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/euBLZ/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/euBLZ/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/euBLZ/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/euBLZ/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/euBLZ/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_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/uwQkn/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/uwQkn/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 8123.57 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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/src/utils/IO/branchdata_checks.jl:104 ┌ Warning: rating 89847.24 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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/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/uwQkn/src/utils/IO/branchdata_checks.jl:208 ┌ 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/uwQkn/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/uwQkn/src/parsers/power_models_data.jl:201 [ 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 118julia> # 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]);ERROR: MethodError: no method matching DegreeTwoReduction(; irreducible_buses::Vector{Int64}) This method does not support all of the given keyword arguments (and may not support any). Closest candidates are: DegreeTwoReduction(::Bool) got unsupported keyword argument "irreducible_buses" @ PowerNetworkMatrices ~/work/PowerNetworkMatrices.jl/PowerNetworkMatrices.jl/src/degree_two_reduction.jl:13 DegreeTwoReduction(::Any) got unsupported keyword argument "irreducible_buses" @ PowerNetworkMatrices ~/work/PowerNetworkMatrices.jl/PowerNetworkMatrices.jl/src/degree_two_reduction.jl:13 DegreeTwoReduction(; reduce_reactive_power_injectors) got unsupported keyword argument "irreducible_buses" @ PowerNetworkMatrices ~/work/PowerNetworkMatrices.jl/PowerNetworkMatrices.jl/src/degree_two_reduction.jl:12julia> # Apply to system (if these buses exist in the system) ybus_protected = Ybus(sys; network_reductions = NetworkReduction[reduction]);ERROR: UndefVarError: `reduction` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned.julia> reduction_data_protected = get_network_reduction_data(ybus_protected);ERROR: UndefVarError: `ybus_protected` not defined in `Main` Suggestion: check for spelling errors or missing imports.julia> # Compare with unprotected case: PNM.get_removed_buses(reduction_data)Set{Int64} with 4 elements: 117 107 115 118julia> PNM.get_removed_buses(reduction_data_protected)ERROR: UndefVarError: `reduction_data_protected` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned.
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 findjulia> # Apply to LODF matrix lodf = LODF(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # Apply to BA Matrix ba_matrix = BA_Matrix(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # 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:
- Smaller Matrices: Eliminates intermediate buses from network matrices
- Faster Computations: Reduced matrix dimensions lead to faster operations
- Simplified Topology: Creates a more direct representation of the network
- 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 findjulia> # Create Ybus with degree-two reduction ybus_reduced = Ybus(sys; network_reductions = NetworkReduction[DegreeTwoReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # 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 CIf 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 CThe 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 findjulia> # 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.0imjulia> # 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:61 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.0imjulia> # 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