RadialReduction
In this tutorial the RadialReduction network reduction algorithm is presented. This reduction eliminates radial (dangling) buses and their associated branches from the power network while preserving the electrical behavior of the core 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 RadialReduction.
Understanding Radial Branches
Radial buses are leaf nodes in the network topology with only one connection. These buses do not affect the electrical behavior of the rest of the network and for certain applications can be safely eliminated to simplify network matrices and improve computational efficiency.
Basic Usage of RadialReduction
The RadialReduction 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(PSB.PSITestSystems, "c_sys14");┌ Info: Building new system c_sys14 from raw data └ sys_descriptor.raw_data = "/home/runner/.julia/artifacts/edcb5940e84a802a86ad4f2223214d33121ac044/PowerSystemsTestData-4.0.2/psy_data/data_14bus_pu.jl" ┌ Warning: Invalid range │ valid_info.struct_name = "ACBus" │ field_name = "magnitude" │ valid_range = "voltage_limits" │ valid_info.ist_struct = │ ACBus: Bus 6: │ number: 6 │ name: Bus 6 │ available: true │ bustype: PowerSystems.ACBusTypesModule.ACBusTypes.PV = 2 │ angle: -0.24818581963359368 │ magnitude: 1.07 │ voltage_limits: (min = 0.94, max = 1.06) │ base_voltage: 13.8 │ area: nothing │ load_zone: nothing │ ext: Dict{String, Any}() │ 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 = "ACBus" │ field_name = "magnitude" │ valid_range = "voltage_limits" │ valid_info.ist_struct = │ ACBus: Bus 7: │ number: 7 │ name: Bus 7 │ available: true │ bustype: PowerSystems.ACBusTypesModule.ACBusTypes.PQ = 1 │ angle: -0.23335052099164186 │ magnitude: 1.062 │ voltage_limits: (min = 0.94, max = 1.06) │ base_voltage: 13.8 │ area: nothing │ load_zone: nothing │ ext: Dict{String, Any}() │ 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 = "ACBus" │ field_name = "magnitude" │ valid_range = "voltage_limits" │ valid_info.ist_struct = │ ACBus: Bus 8: │ number: 8 │ name: Bus 8 │ available: true │ bustype: PowerSystems.ACBusTypesModule.ACBusTypes.PV = 2 │ angle: -0.2331759880664424 │ magnitude: 1.09 │ voltage_limits: (min = 0.94, max = 1.06) │ base_voltage: 18.0 │ area: nothing │ load_zone: nothing │ ext: Dict{String, Any}() │ 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 = "reactive_power" │ valid_range = "reactive_power_limits" │ valid_info.ist_struct = │ ThermalStandard: Bus1: │ name: Bus1 │ available: true │ status: true │ bus: ACBus: Bus 1 │ active_power: 2.0 │ reactive_power: -0.169 │ rating: 2.324 │ active_power_limits: (min = 0.0, max = 3.332) │ reactive_power_limits: (min = 0.0, max = 0.1) │ ramp_limits: nothing │ 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.ST = 20 │ fuel: PowerSystems.ThermalFuelsModule.ThermalFuels.COAL = 1 │ services: 0-element Vector{PowerSystems.Service} │ time_at_status: 10000.0 │ dynamic_injector: nothing │ ext: Dict{String, Any}() │ 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: Transformer Trans3 per-unit reactance 0.20912 is higher 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:229 ┌ Warning: rating 2000.0 MW for Trans3 is 2x larger than the max expected rating 115.0 MW for Transformer at a 69.0 kV Voltage level. └ @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: Transformer Trans1 per-unit reactance 0.55618 is higher 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:229 ┌ Warning: rating 2000.0 MW for Trans1 is 2x larger than the max expected rating 115.0 MW for Transformer at a 69.0 kV Voltage level. └ @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: Transformer Trans2 per-unit reactance 0.25202 is higher 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:229 ┌ Warning: rating 2000.0 MW for Trans2 is 2x larger than the max expected rating 115.0 MW for Transformer at a 69.0 kV Voltage level. └ @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 ┌ Warning: rating 2000.0 MW for Trans4 is 2x larger than the max expected rating 115.0 MW for Transformer at a 69.0 kV Voltage level. └ @ PowerSystems ~/.julia/packages/PowerSystems/d2x1q/src/utils/IO/branchdata_checks.jl:208 [ Info: Serialized time series data to /home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/8584b9e729c8aa68ee5405660c6258cde1f67ed3b68f114823707912e9a0d16c/c_sys14_time_series_storage.h5. [ Info: Serialized System to /home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/8584b9e729c8aa68ee5405660c6258cde1f67ed3b68f114823707912e9a0d16c/c_sys14.json [ Info: Serialized System metadata to /home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/8584b9e729c8aa68ee5405660c6258cde1f67ed3b68f114823707912e9a0d16c/c_sys14_metadata.jsonjulia> # Create Ybus with radial reduction ybus = Ybus(sys; network_reductions = NetworkReduction[RadialReduction()])[ Info: Finding subnetworks via iterative union find AC_Ybus_Matrix Dimension 1, [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14] Dimension 2, [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14] And data with size (13, 13): 6.02503-19.4471im -4.99913+15.2631im … ⋅ -4.99913+15.2631im 9.52132-30.2721im ⋅ ⋅ -1.13502+4.78186im ⋅ ⋅ -1.68603+5.11584im ⋅ -1.0259+4.23498im -1.70114+5.19393im ⋅ ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ -1.42401+3.02905im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋅ -1.13699+2.31496im ⋅ ⋅ 2.561-5.34401im
Accessing Reduction Information
After applying the reduction, you can access information about which buses and branches were eliminated:
julia> # Get the network reduction data reduction_data = get_network_reduction_data(ybus);julia> # View the bus reduction mapping # This shows which buses were reduced to which parent buses get_bus_reduction_map(reduction_data)Dict{Int64, Set{Int64}} with 13 entries: 5 => Set() 12 => Set() 1 => Set() 6 => Set() 11 => Set() 9 => Set() 14 => Set() 3 => Set() 7 => Set([8]) 4 => Set() 13 => Set() 2 => Set() 10 => Set()julia> # View the reverse bus search mapping # This maps each reduced bus to its ultimate parent PNM.get_reverse_bus_search_map(reduction_data)Dict{Int64, Int64} with 1 entry: 8 => 7julia> # View the removed arcs (branches) PNM.get_removed_arcs(reduction_data)Set{Tuple{Int64, Int64}} with 1 element: (7, 8)
Protecting Specific Buses from Reduction
In some cases, you may want to preserve certain buses even if they are radial. This can be done using the irreducible_buses parameter:
julia> # Create radial reduction that protects buses 8 and 14 reduction = RadialReduction(; irreducible_buses = [8, 14]);julia> # Apply to system (buses must exist in the system) ybus_protected = Ybus(sys; network_reductions = NetworkReduction[reduction]);[ Info: Finding subnetworks via iterative union findjulia> # Bus 8 was radial, but preserved from reduction reduction_data = get_network_reduction_data(ybus_protected);julia> get_bus_reduction_map(reduction_data)Dict{Int64, Set{Int64}} with 14 entries: 5 => Set() 12 => Set() 8 => Set() 1 => Set() 6 => Set() 11 => Set() 9 => Set() 14 => Set() 3 => Set() 7 => Set() 4 => Set() 13 => Set() 2 => Set() 10 => Set()
Combining with Other Network Matrices
The RadialReduction can be applied to other network matrix types as well:
julia> # Apply to PTDF matrix ptdf = PTDF(sys; network_reductions = NetworkReduction[RadialReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # Apply to LODF matrix lodf = LODF(sys; network_reductions = NetworkReduction[RadialReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # Apply to Incidence Matrix incidence = IncidenceMatrix(sys; network_reductions = NetworkReduction[RadialReduction()]);[ Info: Finding subnetworks via iterative union find
Benefits of Radial Reduction
Using RadialReduction provides several advantages:
- Smaller Matrices: Eliminates unnecessary rows and columns from network matrices
- Faster Computations: Reduced matrix dimensions lead to faster linear algebra operations
- Better Conditioning: Removing radial elements can improve numerical properties
- Memory Efficiency: Reduces storage requirements for large network models
Example: Comparing Matrix Sizes
julia> # Create Ybus without reduction ybus_full = Ybus(sys);[ Info: Finding subnetworks via iterative union findjulia> # Create Ybus with radial reduction ybus_reduced = Ybus(sys; network_reductions = NetworkReduction[RadialReduction()]);[ Info: Finding subnetworks via iterative union findjulia> # Compare sizes size(ybus_full)(14, 14)julia> size(ybus_reduced)(13, 13)
Combining Multiple Reductions
RadialReduction can be combined with other network reduction algorithms like DegreeTwoReduction:
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
Important Notes
- Reference Bus Protection: Reference (slack) buses are automatically protected from elimination, regardless of their connectivity
- Order Matters: When combining multiple reductions, they are applied in the order specified in the vector
- Reversibility: The reduction maintains mapping information (
bus_reduction_mapandreverse_bus_search_map) that can be used for result interpretation - Electrical Equivalence: The reduced network maintains the same electrical behavior as the original network for all non-eliminated elements