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/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 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]);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 findjulia> 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 118julia> 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 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: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.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