From c725e771a3c3d9ebb49456dc538bc2ebd7270e7a Mon Sep 17 00:00:00 2001 From: Luke Kiernan Date: Fri, 4 Sep 2026 13:46:04 -0600 Subject: [PATCH] fix: use actual R value when switch/breaker symbol is present in v35 BRANCH entry Port of PowerSystems.jl PR #1702, which never made it over when the parsing code moved into this package. v35 SWITCHING DEVICE records have no R field, but legacy BRANCH records parsed as switches/breakers (CKT starting with '@' or '*') do. Unconditionally zeroing R for source_version 35 discarded the latter's resistance. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HmGuSdKu4g7a3uqgPXZ4NC --- src/pm_io/psse.jl | 4 +- .../synthetic_v35_branch_switch_r.raw | 38 +++++++++++++++++++ test/test_openapi_switch.jl | 33 ++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/synthetic_v35_branch_switch_r.raw diff --git a/src/pm_io/psse.jl b/src/pm_io/psse.jl index d0e5c15..2ddaee6 100644 --- a/src/pm_io/psse.jl +++ b/src/pm_io/psse.jl @@ -2319,7 +2319,9 @@ function _build_switch_breaker_sub_data( end if pm_data["source_version"] == ("35") - sub_data["r"] = 0.0 + # SWITCHING DEVICE records (v35 dedicated section) have no R field; legacy + # BRANCH records parsed as switches/breakers (CKT starts with '@' or '*') do. + sub_data["r"] = pop!(dict_object, "R", 0.0) sub_data["rating"] = pop!(dict_object, "RATE1") for i in 2:12 rate_key = "RATE$i" diff --git a/test/fixtures/synthetic_v35_branch_switch_r.raw b/test/fixtures/synthetic_v35_branch_switch_r.raw new file mode 100644 index 0000000..964d30a --- /dev/null +++ b/test/fixtures/synthetic_v35_branch_switch_r.raw @@ -0,0 +1,38 @@ +@!IC,SBASE,REV,XFRRAT,NXFRAT,BASFRQ +0, 100.00, 35, 0, 1, 60.00 +Synthetic fictional v35 case with legacy BRANCH-section switch/breaker records carrying nonzero R +Second comment line +0 / END OF SYSTEM-WIDE DATA, BEGIN BUS DATA +@! I,'NAME ', BASKV, IDE,AREA,ZONE,OWNER, VM, VA, NVHI, NVLO, EVHI, EVLO + 1,'BUSONE ', 138.0000,3, 1, 1, 1,1.00000, 0.0000,1.10000,0.90000,1.10000,0.90000 + 2,'BUSTWO ', 138.0000,2, 1, 1, 1,1.00000, 0.0000,1.10000,0.90000,1.10000,0.90000 + 3,'BUSTHREE ', 138.0000,1, 1, 1, 1,1.00000, 0.0000,1.10000,0.90000,1.10000,0.90000 + 4,'BUSFOUR ', 138.0000,1, 1, 1, 1,1.00000, 0.0000,1.10000,0.90000,1.10000,0.90000 +0 / END OF BUS DATA, BEGIN LOAD DATA + 3,'1 ', 1, 1, 1, 50.000, 10.000, 0.000, 0.000, 0.000, 0.000, 1, 1, 0, 0.000, 0.000, 0,' V' +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA + 1,'1 ', 80.000, 0.000, 80.000, -80.000,1.00000, 0, 0, 100.000, 0.00000E+0, 1.00000E-1, 0.00000E+0, 0.00000E+0,1.00000,1, 100.0, 120.000, 0.000, 0,1,1.0000 + 2,'1 ', 30.000, 0.000, 50.000, -50.000,1.00000, 0, 0, 100.000, 0.00000E+0, 1.00000E-1, 0.00000E+0, 0.00000E+0,1.00000,1, 100.0, 120.000, 0.000, 0,1,1.0000 +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA + 1, 2,'1 ', 1.00000E-02, 1.00000E-01,0.02000,'BRANCH_1_2 ', 500.00, 500.00, 500.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 1.00, 1,1.0000 + 2, 3,'*1', 2.50000E-04, 1.00000E-04,0.00000,'SWITCH_2_3 ', 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000 + 3, 4,'@1', 7.50000E-04, 1.00000E-04,0.00000,'BREAKER_3_4 ', 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000 +0 / END OF BRANCH DATA, BEGIN SYSTEM SWITCHING DEVICE DATA +0 / END OF SYSTEM SWITCHING DEVICE DATA, BEGIN TRANSFORMER DATA +0 / END OF TRANSFORMER DATA, BEGIN AREA DATA +0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA +0 / END OF TWO-TERMINAL DC DATA, BEGIN VSC DC LINE DATA +0 / END OF VSC DC LINE DATA, BEGIN IMPEDANCE CORRECTION DATA +0 / END OF IMPEDANCE CORRECTION DATA, BEGIN MULTI-TERMINAL DC DATA +0 / END OF MULTI-TERMINAL DC DATA, BEGIN MULTI-SECTION LINE DATA +0 / END OF MULTI-SECTION LINE DATA, BEGIN ZONE DATA +0 / END OF ZONE DATA, BEGIN INTER-AREA TRANSFER DATA +0 / END OF INTER-AREA TRANSFER DATA, BEGIN OWNER DATA +0 / END OF OWNER DATA, BEGIN FACTS DEVICE DATA +0 / END OF FACTS DEVICE DATA, BEGIN SWITCHED SHUNT DATA +0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DATA +0 / END OF GNE DATA, BEGIN INDUCTION MACHINE DATA +0 / END OF INDUCTION MACHINE DATA, BEGIN SUBSTATION DATA +0 / END OF SUBSTATION DATA +Q diff --git a/test/test_openapi_switch.jl b/test/test_openapi_switch.jl index 5e12bc5..3fa6d8b 100644 --- a/test/test_openapi_switch.jl +++ b/test/test_openapi_switch.jl @@ -87,3 +87,36 @@ end @test PFP.get_value(gc, :r) == gc_d["r"] @test PFP.get_value(gc, :x) == gc_d["x"] end + +@testset "v35 BRANCH-section switch/breaker keeps its R value (PSY#1702)" begin + pm = PFP.PowerModelsData( + joinpath(@__DIR__, "fixtures", "synthetic_v35_branch_switch_r.raw"), + ) + data = pm.data + + # Unlike the v35 SWITCHING DEVICE section, legacy BRANCH records flagged as + # switches/breakers (CKT starting with '*' or '@') carry an R field, which must be + # used verbatim instead of being zeroed out. + switch_d = only(values(data["switch"])) + @test switch_d["r"] == 2.5e-4 + @test switch_d["x"] == 1.0e-4 + + breaker_d = only(values(data["breaker"])) + @test breaker_d["r"] == 7.5e-4 + @test breaker_d["x"] == 1.0e-4 + + sys = PFP.OpenAPISystem(Float64(data["baseMVA"])) + PFP.read_loadzones!(sys, data) + PFP.read_bus!(sys, data) + PFP.read_switch_breaker!(sys, data) + switch = only( + c for c in PFP.get_components(sys, "DiscreteControlledACBranch") if + PFP.get_value(c, :discrete_branch_type) == "SWITCH" + ) + @test PFP.get_value(switch, :r) == 2.5e-4 + breaker = only( + c for c in PFP.get_components(sys, "DiscreteControlledACBranch") if + PFP.get_value(c, :discrete_branch_type) == "BREAKER" + ) + @test PFP.get_value(breaker, :r) == 7.5e-4 +end