diff --git a/CHANGELOG.md b/CHANGELOG.md index edd13e2e..f02d95c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ PowerModels.jl Change Log ### Staged - nothing +### v0.21.2 +- In place building of basic network data (#915,#916) +- Performance improvements to `calc_connected_components` (#914) +- Fix three winding transformer parsing in psse data (#917) +- Fix quote counting check in psse parser (#920) + ### v0.21.1 - Fix bug in `calc_theta_delta_bounds` (#907) diff --git a/Project.toml b/Project.toml index 0a599ff3..ccb5b02f 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "PowerModels" uuid = "c36e90e8-916a-50a6-bd94-075b64ef4655" authors = ["Carleton Coffrin"] repo = "https://github.com/lanl-ansi/PowerModels.jl" -version = "0.21.1" +version = "0.21.2" [deps] InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0" diff --git a/README.md b/README.md index b8b4106f..bc1b7d44 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,12 @@ The primary developer is Carleton Coffrin (@ccoffrin) with support from the foll - David Fobes (@pseudocubic) LANL, PSS(R)E v33 data support - Rory Finnegan (@rofinn) Invenia, Memento Logging - Frederik Geth (@frederikgeth) CSIRO, storage modeling advise, Branch Flow and current-voltage formulation +- Rahmat Heidari (@hei06j) CSIRO, improved PSS(R)E data support - Jonas Kersulis (@kersulis) University of Michigan, Sparse SDP formulation - Miles Lubin (@mlubin) MIT, Julia/JuMP advise - Yeesian Ng (@yeesian) MIT, Documenter.jl setup - Kaarthik Sundar (@kaarthiksundar) LANL, OBBT utility -- Mathieu Tanneau (@mtanneau) Georgia Tech, PTDF matrix computation +- Mathieu Tanneau (@mtanneau) Georgia Tech, PTDF matrix computation, performance and memory improvements - Byron Tasseff (@tasseff) LANL, multi-infrastructure updates diff --git a/src/core/data.jl b/src/core/data.jl index cdc22dcb..7f6b654a 100644 --- a/src/core/data.jl +++ b/src/core/data.jl @@ -2526,16 +2526,43 @@ function calc_connected_components(data::Dict{String,<:Any}; edges=["branch", "d end end - component_lookup = Dict(i => Set{Int}([i]) for i in active_bus_ids) - touched = Set{Int}() - - for i in active_bus_ids - if !(i in touched) - _cc_dfs(i, neighbors, component_lookup, touched) + sorted_bus_ids = sort(collect(active_bus_ids)) + i0 = sorted_bus_ids[1] - 1 # this is to track un-visited buses + # The two dictionaries below are used to track the connected components + # `component_lookup` maps each bus to the ID of the connected component it belongs to, which is the smallest bus ID in said components + # `components` maps the connected component ID to a `Set{Int}` of all bus IDs in that component + component_lookup = Dict(i => i0 for i in active_bus_ids) + components = Dict{Int,Set{Int}}() + + # ⚠️ it is important to iterate over _sorted_ bus IDs to ensure that components are labeled correctly + for i in sorted_bus_ids + if component_lookup[i] != i0 + continue # bus already flagged; skip + end + + # We have a new connected component! + component_lookup[i] = i + components[i] = Set([i]) + V = [i] # list of all bus IDs in this connected component + while length(V) > 0 + j = pop!(V) + for k in neighbors[j] + if component_lookup[k] == i0 + # Bus `k` is connected to a bus `j` that's connected to `i` + push!(V, k) + component_lookup[k] = i + push!(components[i], k) + else + # Bus was already visited + # Check that it's in the right connected component, and move on + @assert component_lookup[k] == i "Unexpected error with bus $k while computing connected components; please report this as a bug." + continue + end + end end end - ccs = (Set(values(component_lookup))) + ccs = (Set(values(components))) return ccs end diff --git a/src/core/data_basic.jl b/src/core/data_basic.jl index e3e8f777..58f10e39 100644 --- a/src/core/data_basic.jl +++ b/src/core/data_basic.jl @@ -17,17 +17,38 @@ users requiring any of the features listed above for their analysis should use the non-basic PowerModels routines. """ function make_basic_network(data::Dict{String,<:Any}) + # These initial checks are redundant with the checks in make_basic_network! + # We keep them here so they run _before_ we create a deepcopy of the data + # The checks are fast so this redundancy has little performance impact if _IM.ismultiinfrastructure(data) Memento.error(_LOGGER, "make_basic_network does not support multiinfrastructure data") end - + if _IM.ismultinetwork(data) Memento.error(_LOGGER, "make_basic_network does not support multinetwork data") end - # make a copy of data so that modifications do not change the input data data = deepcopy(data) + make_basic_network!(data) + + return data +end + +""" +given a powermodels data dict, modifies it in-place to conform to basic network model requirements. + +See [`make_basic_network`](@ref) for more information. +""" +function make_basic_network!(data::Dict{String,<:Any}) + if _IM.ismultiinfrastructure(data) + Memento.error(_LOGGER, "make_basic_network does not support multiinfrastructure data") + end + + if _IM.ismultinetwork(data) + Memento.error(_LOGGER, "make_basic_network does not support multinetwork data") + end + # TODO transform PWL costs into linear costs for (i,gen) in data["gen"] if get(gen, "cost_model", 2) != 2 @@ -110,7 +131,7 @@ function make_basic_network(data::Dict{String,<:Any}) # re-number non-bus component ids for comp_key in keys(pm_component_status) if comp_key != "bus" - data[comp_key] = _renumber_components(data[comp_key]) + data[comp_key] = _renumber_components!(data[comp_key]) end end @@ -148,18 +169,18 @@ end given a component dict returns a new dict where components have been renumbered from 1-to-n ordered by the increasing values of the orginal component id. """ -function _renumber_components(comp_dict::Dict{String,<:Any}) - renumbered_dict = Dict{String,Any}() - - comp_ordered = sort([comp for (i,comp) in comp_dict], by=(x) -> x["index"]) +function _renumber_components!(comp_dict::Dict{String,<:Any}) + comp_ordered = sort([(comp["index"], comp) for (i, comp) in comp_dict], by=(x) -> x[1]) - for (i,comp) in enumerate(comp_ordered) - comp = deepcopy(comp) - comp["index"] = i - renumbered_dict["$i"] = comp + # Delete existing keys + empty!(comp_dict) + # Update component indices and re-build the dict keys + for (i_new, (i_old, comp)) in enumerate(comp_ordered) + comp["index"] = i_new + comp_dict["$(i_new)"] = comp end - return renumbered_dict + return comp_dict end diff --git a/src/io/psse.jl b/src/io/psse.jl index f377a0b6..356de551 100644 --- a/src/io/psse.jl +++ b/src/io/psse.jl @@ -434,8 +434,13 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) else br_r, br_x = transformer["R1-2"], transformer["X1-2"] end - br_r *= (transformer["NOMV1"]^2 / _get_bus_value(transformer["I"], "base_kv", pm_data)^2) * (pm_data["baseMVA"] / transformer["SBASE1-2"]) - br_x *= (transformer["NOMV1"]^2 / _get_bus_value(transformer["I"], "base_kv", pm_data)^2) * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + if isapprox(transformer["NOMV1"], 0.0) + br_r *= (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_x *= (pm_data["baseMVA"] / transformer["SBASE1-2"]) + else + br_r *= (transformer["NOMV1"]^2 / _get_bus_value(transformer["I"], "base_kv", pm_data)^2) * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_x *= (transformer["NOMV1"]^2 / _get_bus_value(transformer["I"], "base_kv", pm_data)^2) * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + end end # Zeq scaling for tap2 (see eq (4.21b) in PROGRAM APPLICATION GUIDE 1 in PSSE installation folder) @@ -448,8 +453,13 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) br_r *= (transformer["WINDV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data))^2 br_x *= (transformer["WINDV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data))^2 else # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3." - br_r *= (transformer["WINDV2"]*(transformer["NOMV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data)))^2 - br_x *= (transformer["WINDV2"]*(transformer["NOMV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data)))^2 + if isapprox(transformer["NOMV2"], 0.0) + br_r *= transformer["WINDV2"]^2 + br_x *= transformer["WINDV2"]^2 + else + br_r *= (transformer["WINDV2"]*(transformer["NOMV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data)))^2 + br_x *= (transformer["WINDV2"]*(transformer["NOMV2"]/_get_bus_value(transformer["J"], "base_kv", pm_data)))^2 + end end end @@ -474,7 +484,7 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) if sub_data["rate_c"] == 0.0 delete!(sub_data, "rate_c") end - + if import_all sub_data["windv1"] = transformer["WINDV1"] sub_data["windv2"] = transformer["WINDV2"] @@ -482,15 +492,23 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) sub_data["nomv2"] = transformer["NOMV2"] end + # Assumes CW = 1, namely, for off-nominal turns ratio in pu of windning bus base voltage sub_data["tap"] = pop!(transformer, "WINDV1") / pop!(transformer, "WINDV2") sub_data["shift"] = pop!(transformer, "ANG1") # Unit Transformations - if transformer["CW"] != 1 # NOT "for off-nominal turns ratio in pu of winding bus base voltage" + if transformer["CW"] == 1 # "for off-nominal turns ratio in pu of winding bus base voltage" + # do nothing + elseif transformer["CW"] == 2 # "for winding voltage in kv" sub_data["tap"] *= _get_bus_value(transformer["J"], "base_kv", pm_data) / _get_bus_value(transformer["I"], "base_kv", pm_data) - if transformer["CW"] == 3 # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3." - sub_data["tap"] *= transformer["NOMV1"] / transformer["NOMV2"] + elseif transformer["CW"] == 3 # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3." + if !iszero(transformer["NOMV1"]) && !iszero(transformer["NOMV2"]) + sub_data["tap"] *= (transformer["NOMV1"] / transformer["NOMV2"]) * (_get_bus_value(transformer["J"], "base_kv", pm_data) / _get_bus_value(transformer["I"], "base_kv", pm_data)) + else + # do nothing end + else + error(_LOGGER, "psse data parsing error, unsupported value for `CW` on transformer") end if import_all @@ -544,13 +562,29 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) # Unit Transformations if transformer["CZ"] != 1 # NOT "for resistance and reactance in pu on system MVA base and winding voltage base" - br_r12 *= (transformer["NOMV1"] / _get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) - br_r23 *= (transformer["NOMV2"] / _get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) - br_r31 *= (transformer["NOMV3"] / _get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + if isapprox(transformer["NOMV1"], 0.0) + br_r12 *= (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_x12 *= (pm_data["baseMVA"] / transformer["SBASE1-2"]) + else + br_r12 *= (transformer["NOMV1"] / _get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_x12 *= (transformer["NOMV1"] / _get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + end - br_x12 *= (transformer["NOMV1"] / _get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) - br_x23 *= (transformer["NOMV2"] / _get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) - br_x31 *= (transformer["NOMV3"] / _get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + if isapprox(transformer["NOMV2"], 0.0) + br_r23 *= (pm_data["baseMVA"] / transformer["SBASE2-3"]) + br_x23 *= (pm_data["baseMVA"] / transformer["SBASE2-3"]) + else + br_r23 *= (transformer["NOMV2"] / _get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) + br_x23 *= (transformer["NOMV2"] / _get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) + end + + if isapprox(transformer["NOMV3"], 0.0) + br_r31 *= (pm_data["baseMVA"] / transformer["SBASE3-1"]) + br_x31 *= (pm_data["baseMVA"] / transformer["SBASE3-1"]) + else + br_r31 *= (transformer["NOMV3"] / _get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + br_x31 *= (transformer["NOMV3"] / _get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + end end # See "Power System Stability and Control", ISBN: 0-07-035958-X, Eq. 6.72 @@ -600,11 +634,18 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) sub_data["shift"] = pop!(transformer, "ANG$m") # Unit Transformations - if transformer["CW"] != 1 # NOT "for off-nominal turns ratio in pu of winding bus base voltage" + if transformer["CW"] == 1 # "for off-nominal turns ratio in pu of winding bus base voltage" + # do nothing + elseif transformer["CW"] == 2 # "for winding voltage in kv" sub_data["tap"] /= _get_bus_value(bus_id, "base_kv", pm_data) - if transformer["CW"] == 3 # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3." - sub_data["tap"] *= transformer["NOMV$m"] + elseif transformer["CW"] == 3 # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3." + if !iszero(transformer["NOMV$m"]) + sub_data["tap"] *= transformer["NOMV$m"] / _get_bus_value(bus_id, "base_kv", pm_data) + else + # do nothing end + else + error(_LOGGER, "psse data parsing error, unsupported value for `CW` on transformer") end if import_all diff --git a/src/io/pti.jl b/src/io/pti.jl index 908a1f7f..d69c23f8 100644 --- a/src/io/pti.jl +++ b/src/io/pti.jl @@ -607,8 +607,8 @@ Comments, typically indicated at the end of a line with a `'/'` character, are also extracted separately, and `Array{Array{String}, String}` is returned. """ function _get_line_elements(line::AbstractString) - if count(i->(i=="'"), line) % 2 == 1 - throw(Memento.error(_LOGGER, "There are an uneven number of single-quotes in \"{line}\", the line cannot be parsed.")) + if count(i->(i=='\''), line) % 2 == 1 + throw(Memento.error(_LOGGER, "There are an uneven number of single-quotes in \"$line\", the line cannot be parsed.")) end line_comment = split(line, _comment_split, limit=2) diff --git a/test/data-basic.jl b/test/data-basic.jl index 47f6bcab..a6ab1c88 100644 --- a/test/data-basic.jl +++ b/test/data-basic.jl @@ -35,6 +35,57 @@ @test isapprox(result["objective"], 16551.7; atol=1e0) end + @testset "Re-number components in-place" begin + data = PowerModels.parse_file("../test/data/matpower/case7_tplgy.m") + + # Scramble indices before renumbering + v = [data["gen"]["$i"] for i in 1:3] + data["gen"]["1"]["index"] = 2 + data["gen"]["2"]["index"] = 3 + data["gen"]["3"]["index"] = 1 + + d = PowerModels._renumber_components!(data["gen"]) + @test length(data["gen"]) == 3 + # Check index consistency + @test data["gen"]["1"]["index"] == 1 + @test data["gen"]["2"]["index"] == 2 + @test data["gen"]["3"]["index"] == 3 + # Check that modifications were done in-place + @test d === data["gen"] + @test data["gen"]["1"] === v[3] + @test data["gen"]["2"] === v[1] + @test data["gen"]["3"] === v[2] + end + + @testset "In-place make_basic_network!" begin + data = PowerModels.parse_file("../test/data/matpower/case7_tplgy.m") + data_ = deepcopy(data) + + # Test #1: `make_basic_network` is non-destructive + data_basic = PowerModels.make_basic_network(data) + @test data == data_ # input data not modified + @test data_basic !== data # we created a new dict + + # Test #2: `make_basic_network!` is destructive and works in-place + data_basic = PowerModels.make_basic_network!(data) + @test data_basic === data # in-place modification + + # Test #3: double-check the output of `make_basic_network!` + # This may be redundant with the above tests, unless `make_basic_network` does not + # call the in-place version under the hood + @test length(data_basic["bus"]) == 4 + @test length(data_basic["load"]) == 2 + @test length(data_basic["shunt"]) == 2 + @test length(data_basic["gen"]) == 1 + @test length(data_basic["branch"]) == 2 + @test length(data_basic["dcline"]) == 0 + @test length(data_basic["switch"]) == 0 + @test length(data_basic["storage"]) == 0 + + result = solve_opf(data_basic, ACPPowerModel, nlp_solver) + @test isapprox(result["objective"], 1036.52; atol=1e0) + end + end diff --git a/test/data/pti/case3_2wtf_vmon0.raw b/test/data/pti/case3_2wtf_vmon0.raw new file mode 100644 index 00000000..a8bc21ee --- /dev/null +++ b/test/data/pti/case3_2wtf_vmon0.raw @@ -0,0 +1,33 @@ +0, 100.00, 33, 0, 1, 50.00 / PSS(R)E 33 RAW created by rawd33 MON, SEP 18 2023 13:45 +Example +SOME ITEMS IS NOT SUPPORTED, PLEASE CHECK THE DOCS. + 2,' ', 132.0000,1, 6, 6, 1,0.82852, -0.7009,1.10000,0.9000,1.1000,0.90000 + 3,' ', 132.0000,1, 6, 5, 1,0.93835, -2.1349,1.10000,0.9000,1.1000,0.90000 + 4,' ', 11.0000,3, 6, 6, 1,1.05000, 0.0000,1.1000,0.9000,1.10000,0.90000 +0 / END OF BUS DATA, BEGIN LOAD DATA + 2,'1 ',1, 6, 6, 15.000, 50.000, 0.000, 0.000, 0.000, 0.000, 1,1,0 +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA + 4,'1 ', 18.352, 58.958, 400.000, 0.000,1.05000, 0, 100.000, 0.00000E+0, 2.00000E-1, 0.00000E+0, 0.00000E+0,1.00000,1, 100.0, 300.000, 0.000, 60,1.0000 +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA + 2, 3,'1 ', 9.00000E-2, 1.60000E-1, 0.05000, 90.30, 99.30, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 60.00, 1,1.0000 +0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA + 3, 4, 0,'1 ',3,2,1, 0.00000E+0, 0.00000E+0,2,' ',1, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,' ' + 0.00000E+0, 1.20000E-1, 100.00 +1.00000, 0.000, 0.000, 100.00, 66.00, 0.00, 1, 0, 1.15003, 0.94999, 1.10000, 0.90000, 17, 0, 0.00000, 0.00000, 0.000 +1.00000, 0.000 +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 +Q diff --git a/test/data/pti/case4_3wtf_vnom0_cw2.raw b/test/data/pti/case4_3wtf_vnom0_cw2.raw new file mode 100644 index 00000000..d05b042e --- /dev/null +++ b/test/data/pti/case4_3wtf_vnom0_cw2.raw @@ -0,0 +1,33 @@ +0, 100.0, 33, 0, 1, 50.0 + + + 1, 'BUS1 ', 380.0, 3, 1, 1, 1, 1.0, 0.0, 1.1, 0.9, 1.1, 0.9 + 2, 'BUS2 ', 110.0, 2, 1, 1, 1, 1.03828, -0.0017, 1.1, 0.9, 1.1, 0.9 + 81, 'BUS81 ', 30.0, 1, 1, 1, 1, 0.95156, -150.4438, 1.1, 0.9, 1.1, 0.9 +0 / END OF BUS DATA, BEGIN LOAD DATA + 81, '1 ', 1, 1, 1, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1, 1, 0 +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA + 1, 'Q1', 0.05, -2.757, 100.0, -100.0, 1.0, 1, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1, 100.0, 100.0, 0.0, 1, 1.0, 0, 1.0, 0, 1.0, 0, 1.0, 0, 1.0 +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA +0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA + 1, 2, 81, 'T4', 2, 2, 1, 0.0, 0.0, 2, 'T4 ', 1, 1, 1.0, 0, 1.0, 0, 1.0, 0, 1.0, ' ' +0.0026, 0.209984, 350.0, 0.0016, 0.0699817, 50.0, 0.0016, 0.0999872, 50.0, 0.95174, -0.001 +400.0, 0.0, 0.0, 350.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +120.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +30.0, 0.0, -30.0, 50.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +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 VOLTAGE SOURCE CONVERTER DATA +0 / END OF VOLTAGE SOURCE CONVERTER 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 CONTROL DEVICE DATA +0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA +0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DEVICE DATA +0 / END OF GNE DEVICE DATA, BEGIN INDUCTION MACHINE DATA +0 / END INDUCTION MACHINE DATA +Q \ No newline at end of file diff --git a/test/data/pti/case4_3wtf_vnom0_cw3.raw b/test/data/pti/case4_3wtf_vnom0_cw3.raw new file mode 100644 index 00000000..f02e6889 --- /dev/null +++ b/test/data/pti/case4_3wtf_vnom0_cw3.raw @@ -0,0 +1,33 @@ +0, 100.0, 33, 0, 1, 50.0 + + + 1, 'BUS1 ', 380.0, 3, 1, 1, 1, 1.0, 0.0, 1.1, 0.9, 1.1, 0.9 + 2, 'BUS2 ', 110.0, 2, 1, 1, 1, 1.03828, -0.0017, 1.1, 0.9, 1.1, 0.9 + 81, 'BUS81 ', 30.0, 1, 1, 1, 1, 0.95156, -150.4438, 1.1, 0.9, 1.1, 0.9 +0 / END OF BUS DATA, BEGIN LOAD DATA + 81, '1 ', 1, 1, 1, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1, 1, 0 +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA + 1, 'Q1', 0.05, -2.757, 100.0, -100.0, 1.0, 1, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1, 100.0, 100.0, 0.0, 1, 1.0, 0, 1.0, 0, 1.0, 0, 1.0, 0, 1.0 +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA +0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA + 1, 2, 81, 'T4', 3, 2, 1, 0.0, 0.0, 2, 'T4 ', 1, 1, 1.0, 0, 1.0, 0, 1.0, 0, 1.0, ' ' +0.0026, 0.209984, 350.0, 0.0016, 0.0699817, 50.0, 0.0016, 0.0999872, 50.0, 0.95174, -0.001 +1.0, 0.0, 0.0, 350.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +1.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +1.0, 0.0, -30.0, 50.0, 0.0, 0.0, 0, 0, 1.1, 0.9, 1.1, 0.9, 33, 0, 0.0, 0.0, 0.0 +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 VOLTAGE SOURCE CONVERTER DATA +0 / END OF VOLTAGE SOURCE CONVERTER 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 CONTROL DEVICE DATA +0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA +0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DEVICE DATA +0 / END OF GNE DEVICE DATA, BEGIN INDUCTION MACHINE DATA +0 / END INDUCTION MACHINE DATA +Q \ No newline at end of file diff --git a/test/data/pti/parser_test_l.raw b/test/data/pti/parser_test_l.raw new file mode 100644 index 00000000..aadab322 --- /dev/null +++ b/test/data/pti/parser_test_l.raw @@ -0,0 +1,13 @@ +0, 100.00, 33, 0, 1, 60.00 + + +0 / END OF BUS DATA, BEGIN LOAD DATA +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA +0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA +1005,1002, 0,'1 ',1,1,1,0.00000E0,0.00000E0,2,'T2 ',1, 301,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,' ' ' + 5.21000E-3,1.77370E-1, 100.00, 1 + 1.025000, 115.000, 0.000, 84.00, 84.00, 84.00,0, 0,1.500000,0.500000,1.500000,0.500000,9999, 0, 0.00000, 0.00000, 0.000 + 1.000000, 13.800 +Q \ No newline at end of file diff --git a/test/psse.jl b/test/psse.jl index 6c4ddbe0..5306512c 100644 --- a/test/psse.jl +++ b/test/psse.jl @@ -316,6 +316,32 @@ end end end + + + @testset "with nomV=0 cw=2" begin + data = PowerModels.parse_file("../test/data/pti/case4_3wtf_vnom0_cw2.raw") + + opf = PowerModels.solve_opf(data, PowerModels.ACPPowerModel, nlp_solver) + @test opf["termination_status"] == LOCALLY_SOLVED + @test isapprox(opf["objective"], 5.00079; atol=1e-3) + end + + @testset "with nomV=0 cw=3" begin + data = PowerModels.parse_file("../test/data/pti/case4_3wtf_vnom0_cw3.raw") + + opf = PowerModels.solve_opf(data, PowerModels.ACPPowerModel, nlp_solver) + @test opf["termination_status"] == LOCALLY_SOLVED + @test isapprox(opf["objective"], 5.00079; atol=1e-3) + end + + @testset "2-windning transformer with nomV=0 cw=3" begin + data = PowerModels.parse_file("../test/data/pti/case3_2wtf_vmon0.raw") + + opf = PowerModels.solve_opf(data, PowerModels.ACPPowerModel, nlp_solver) + @test opf["termination_status"] == LOCALLY_SOLVED + @test isapprox(opf["objective"], 17.5101; atol=1e-3) + end + end @testset "transformer magnetizing admittance" begin diff --git a/test/pti.jl b/test/pti.jl index 0b662811..e1d03801 100644 --- a/test/pti.jl +++ b/test/pti.jl @@ -16,6 +16,8 @@ TESTLOG = Memento.getlogger(PowerModels) @test_warn(TESTLOG, "GNE DEVICE parsing is not supported.", PowerModels.parse_pti("../test/data/pti/parser_test_h.raw")) @test_throws(TESTLOG, ErrorException, PowerModels.parse_pti("../test/data/pti/parser_test_j.raw")) + @test_throws(TESTLOG, ErrorException, PowerModels.parse_pti("../test/data/pti/parser_test_l.raw")) + Memento.setlevel!(TESTLOG, "error") end