diff --git a/src/openapi/branch.jl b/src/openapi/branch.jl index 8c39fd8..5524d8a 100644 --- a/src/openapi/branch.jl +++ b/src/openapi/branch.jl @@ -370,7 +370,9 @@ function _branch_type_matpower(d::Dict) end function _branch_type_psse(d::Dict, name::AbstractString) - if iszero(d["br_r"]) && iszero(d["br_x"]) + # A zero-impedance TRANSFORMER record is still a transformer: only a plain branch + # with no impedance is the modeled switching device this shortcut is looking for. + if !d["transformer"] && iszero(d["br_r"]) && iszero(d["br_x"]) return :switch end is_transformer = d["transformer"] diff --git a/test/test_openapi_branch.jl b/test/test_openapi_branch.jl index 8352962..feda5e2 100644 --- a/test/test_openapi_branch.jl +++ b/test/test_openapi_branch.jl @@ -271,6 +271,42 @@ end (PFP.get_value(switch, :available) ? "CLOSED" : "OPEN") end +@testset "zero-impedance TRANSFORMER stays a transformer, not a switch" begin + # The zero-impedance shortcut above must not fire on a TRANSFORMER record: a + # transformer with R1-2 = X1-2 = 0 is still a transformer, and misrouting it to + # `make_switch_from_zero_impedance_branch!` also slips past the `transformer=true but + # detected as a Line` guard, which only checks for `:line`. Port of + # PowerSystems.jl 00003f06d. + data = fourteen_bus_pm_data().data + d = first(v for v in values(data["branch"]) if v["transformer"]) + @test PFP._branch_type_psse(d, "as_parsed") == :transformer + + zero_z = merge( + Dict{String, Any}(k => v for (k, v) in d), + Dict{String, Any}("br_r" => 0.0, "br_x" => 0.0, "index" => 998), + ) + @test PFP._branch_type_psse(zero_z, "zero_z_transformer") == :transformer + + synthetic = deepcopy(data) + synthetic["branch"] = Dict{String, Any}("998" => zero_z) + for key in + ("3w_transformer", "dcline", "vscline", "interarea_transfer", "shunt", + "switched_shunt", "facts") + delete!(synthetic, key) + end + + sys = PFP.OpenAPISystem(Float64(synthetic["baseMVA"])) + PFP.read_loadzones!(sys, synthetic) + PFP.read_bus!(sys, synthetic) + PFP.read_branches!(sys, synthetic) + + @test isempty(PFP.get_components(sys, "DiscreteControlledACBranch")) + circuit = only(PFP.get_components(sys, "TransformerCircuit")) + @test PFP.get_value(circuit, :r) == 0.0 + @test PFP.get_value(circuit, :x) == 0.0 + @test !isnothing(_two_winding_transformer_for(sys, circuit)) +end + @testset "TransformerCircuit.controlled_quantity_limits passes through UNSCALED under COMPONENT_BASE for a power-flow-family control_objective" begin # Regression: `controlled_quantity_limits`'s schema quantity DOES switch with # `control_objective` (pu for VOLTAGE-family objectives, MW/MVAr for ACTIVE_POWER_FLOW/