diff --git a/Project.toml b/Project.toml index d8b15d7..3d2ad78 100644 --- a/Project.toml +++ b/Project.toml @@ -11,15 +11,21 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" OpenAPI = "d5e62ea6-ddf3-4d43-8e4c-ad5e6c8bfd7d" PowerCoreOpenAPIModels = "b7b40286-e793-417d-a9a0-b1583e4da1cb" +PowerDynamicsOpenAPIModels = "044a0b22-31f8-4ef6-8282-c9a61b3013f6" +PowerInvestmentsOpenAPIModels = "33cb4396-f4d9-4f59-8585-787ebb56cb1b" PowerOpenAPIModels = "0730f07c-cff6-4c3b-a9df-c546153be50a" PowerOperationsOpenAPIModels = "a372b6d7-45a2-44c2-8199-6a724b72e8ff" +PowerTimeSeriesOpenAPIModels = "8c2f6a0d-6b0e-4d0a-9d8f-2b5e6a4f9c31" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - [sources] # These packages are generated and not yet released, so they resolve from the branch # rather than the registry. Switch to a tagged rev once one exists. PowerCoreOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerCoreOpenAPIModels.jl"} PowerOperationsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerOperationsOpenAPIModels.jl"} +PowerOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerOpenAPIModels.jl"} +PowerDynamicsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerDynamicsOpenAPIModels.jl"} +PowerInvestmentsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerInvestmentsOpenAPIModels.jl"} +PowerTimeSeriesOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerTimeSeriesOpenAPIModels.jl"} InfrastructureSystems = {rev = "IS4", url = "https://github.com/Sienna-Platform/InfrastructureSystems.jl"} [compat] @@ -30,7 +36,10 @@ JSON = "1" LinearAlgebra = "1" OpenAPI = "0.2" PowerCoreOpenAPIModels = "0.1" +PowerDynamicsOpenAPIModels = "0.1" +PowerInvestmentsOpenAPIModels = "0.1" PowerOpenAPIModels = "0.1" PowerOperationsOpenAPIModels = "0.1" +PowerTimeSeriesOpenAPIModels = "0.1" Unicode = "1" julia = "^1.10" diff --git a/src/pm_io/psse.jl b/src/pm_io/psse.jl index e64a591..1d121b6 100644 --- a/src/pm_io/psse.jl +++ b/src/pm_io/psse.jl @@ -951,17 +951,15 @@ function _psse2pm_shunt!(pm_data::Dict, pti_data::Dict, import_all::Bool, nb) sub_data["ext"]["NREG"] = pop!(switched_shunt, "NREG") elseif pm_data["source_version"] ∈ ("30", "32", "33") - sub_data["initial_status"] = ones(Int, length(sub_data["y_increment"])) + # Pre-v35 SWITCHED SHUNT records carry no per-block status field. BINIT + # already holds the total in-service admittance, which `bs` above passes + # on, so every block must start out of service whatever MODSW says — + # counting any of them in would double-count that same admittance. + sub_data["initial_status"] = zeros(Int, length(sub_data["y_increment"])) else error("Unsupported PSS(R)E source version: $(pm_data["source_version"])") end - if switched_shunt["MODSW"] ∈ (0, 1, 2) - # BINIT is treated as the total shunt admittance. - # Keep Y_increase but zero all initial states to avoid double counting. - sub_data["initial_status"] = zeros(Int, length(sub_data["y_increment"])) - end - sub_data["index"] = length(pm_data["switched_shunt"]) + 1 sub_data["source_id"] = ["switched shunt", bus_number, sub_data["index"]] diff --git a/test/Project.toml b/test/Project.toml index 073a6a9..7aa0341 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,4 @@ [deps] -PowerOpenAPIModels = "0730f07c-cff6-4c3b-a9df-c546153be50a" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" @@ -7,15 +6,21 @@ LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" OpenAPI = "d5e62ea6-ddf3-4d43-8e4c-ad5e6c8bfd7d" PowerCoreOpenAPIModels = "b7b40286-e793-417d-a9a0-b1583e4da1cb" +PowerDynamicsOpenAPIModels = "044a0b22-31f8-4ef6-8282-c9a61b3013f6" PowerFlowFileParser = "bed98974-b02e-5e2f-9ee0-a103f5c450dd" +PowerInvestmentsOpenAPIModels = "33cb4396-f4d9-4f59-8585-787ebb56cb1b" +PowerOpenAPIModels = "0730f07c-cff6-4c3b-a9df-c546153be50a" PowerOperationsOpenAPIModels = "a372b6d7-45a2-44c2-8199-6a724b72e8ff" +PowerTimeSeriesOpenAPIModels = "8c2f6a0d-6b0e-4d0a-9d8f-2b5e6a4f9c31" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - [sources] PowerFlowFileParser = {path = ".."} PowerCoreOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerCoreOpenAPIModels.jl"} PowerOperationsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerOperationsOpenAPIModels.jl"} PowerOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerOpenAPIModels.jl"} +PowerDynamicsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerDynamicsOpenAPIModels.jl"} +PowerInvestmentsOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerInvestmentsOpenAPIModels.jl"} +PowerTimeSeriesOpenAPIModels = {url = "https://github.com/Sienna-Platform/PowerOpenAPIModels.git", rev = "main", subdir = "PowerTimeSeriesOpenAPIModels.jl"} [compat] julia = "^1.6" diff --git a/test/test_parse_psse.jl b/test/test_parse_psse.jl index 8c2c52f..9a127cd 100644 --- a/test/test_parse_psse.jl +++ b/test/test_parse_psse.jl @@ -126,3 +126,31 @@ end @test pm_v33["bus"][1]["area_slack"] === true @test !haskey(pm_v33["bus"][3], "area_slack") end + +@testset "PSSE pre-v35 switched shunt blocks start out of service" begin + # Pre-v35 SWITCHED SHUNT records have no per-block status field, so the parser has to + # fabricate one. BINIT already carries the total in-service admittance into `bs`, so + # every block must start at zero whatever MODSW says; an in-service block would + # double-count the admittance BINIT has already contributed. + raw = read_fixture(FOURTEEN_BUS_FIXTURE) + pm_data = parse_file(IOBuffer(raw); filetype = "raw") + @test pm_data["source_version"] == "33" + + shunts = collect(values(pm_data["switched_shunt"])) + @test !isempty(shunts) + for shunt in shunts + @test shunt["initial_status"] == zeros(Int, length(shunt["y_increment"])) + end + + # Bus 101's record is MODSW=1, which an earlier mode-specific patch already zeroed. + # MODSW=3 took the fabricated all-ones path and is the case this fixes. + modsw3 = replace(raw, " 101,1,0,1," => " 101,3,0,1,"; count = 1) + @test modsw3 != raw + pm_modsw3 = parse_file(IOBuffer(modsw3); filetype = "raw") + shunt_101 = + only(v for v in values(pm_modsw3["switched_shunt"]) if v["shunt_bus"] == 101) + @test shunt_101["control_mode"] == 3 + @test shunt_101["step_number"] == [5] + @test length(shunt_101["y_increment"]) == 1 + @test shunt_101["initial_status"] == [0] +end