Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ PowerModels.jl Change Log
=========================

### Staged
- nothing
- Made case name recovery optional in PTI parsing
- Fixed Julia deprecation warning when calling sort on Dict

### v0.19.1
- Add support for Memento v1.3
Expand Down
5 changes: 4 additions & 1 deletion src/io/psse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,10 @@ function _pti_to_powermodels!(pti_data::Dict; import_all=false, validate=true)::
pm_data["source_type"] = "pti"
pm_data["source_version"] = "$rev"
pm_data["baseMVA"] = pop!(pti_data["CASE IDENTIFICATION"][1], "SBASE")
pm_data["name"] = pop!(pti_data["CASE IDENTIFICATION"][1], "NAME")

if haskey(pti_data["CASE IDENTIFICATION"][1], "NAME")
pm_data["name"] = pop!(pti_data["CASE IDENTIFICATION"][1], "NAME")
end

if import_all
_import_remaining_keys!(pm_data, pti_data["CASE IDENTIFICATION"][1])
Expand Down
22 changes: 11 additions & 11 deletions src/io/pti.jl
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ function parse_pti(io::IO)::Dict
try
pti_data["CASE IDENTIFICATION"][1]["NAME"] = match(r"^\<file\s[\/\\]*(?:.*[\/\\])*(.*)\.raw\>$", lowercase(io.name)).captures[1]
catch
throw(Memento.error(_LOGGER, "This file is unrecognized and cannot be parsed"))
Memento.info(_LOGGER, "unable to recover case name from io file name in parse_pti")
end

return pti_data
Expand Down Expand Up @@ -1081,7 +1081,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, Comment_Line_2)

# Bus
for (_, bus) in sort(data["bus"], by = (x) -> parse(Int64, x))
for (_, bus) in sort(collect(data["bus"]), by=(x) -> x.second["index"])
# Skip star-buses created by three-winding transformers from importing raw source files
if bus["source_id"][1] == "transformer"
continue
Expand All @@ -1097,7 +1097,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, "0 / END OF BUS DATA, BEGIN LOAD DATA")

# Load
for (_, load) in sort(data["load"], by = (x) -> parse(Int64, x))
for (_, load) in sort(collect(data["load"]), by=(x) -> x.second["index"])
# Get bus number
bus_i = load["load_bus"]

Expand All @@ -1116,7 +1116,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, "0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA")

# Fixed Shunt
for (_, shunt) in sort(data["shunt"], by = (x) -> parse(Int64, x))
for (_, shunt) in sort(collect(data["shunt"]), by=(x) -> x.second["index"])
# Skip Switched Shunts
type = haskey(shunt, "source_id") ? shunt["source_id"][1] : "fixed shunt"
if type != "fixed shunt"
Expand All @@ -1133,7 +1133,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, "0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA")

# Generator
for (_, gen) in sort(data["gen"], by = (x) -> parse(Int64, x))
for (_, gen) in sort(collect(data["gen"]), by=(x) -> x.second["index"])
# Get bus number
bus_i = gen["gen_bus"]

Expand All @@ -1153,7 +1153,7 @@ function export_pti(io::IO, data::Dict{String,Any})

# Branches
transformers = Array{Tuple{Symbol, Any}, 1}()
for (_, branch) in sort(data["branch"], by = (x) -> parse(Int64, x))
for (_, branch) in sort(collect(data["branch"]), by=(x) -> x.second["index"])
# Skip transformers and put it in transformers Array
if branch["transformer"]

Expand Down Expand Up @@ -1261,7 +1261,7 @@ function export_pti(io::IO, data::Dict{String,Any})

# Area Interchange
if haskey(data, "area interchange")
for (_, area) in sort(data["area interchange"], by = (x) -> parse(Int64, x))
for (_, area) in sort(collect(data["area interchange"]), by=(x) -> x.second["index"])
# Get Dict in a PSSE way and print it
psse_comp = _pm2psse_area_interchange(area)
_print_pti_str(io, psse_comp, _pti_dtypes["AREA INTERCHANGE"])
Expand All @@ -1271,7 +1271,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, "0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA")
# TODO : See how PM converts the DC line and do the oposite
if haskey(data, "dcline")
for (_, dcline) in sort(data["dcline"], by = (x) -> parse(Int64, x))
for (_, dcline) in sort(collect(data["dcline"]), by=(x) -> x.second["index"])
# Get AC buses from inverter and rectifier side
r_bus = data["bus"]["$(dcline["f_bus"])"]
i_bus = data["bus"]["$(dcline["t_bus"])"]
Expand Down Expand Up @@ -1310,7 +1310,7 @@ function export_pti(io::IO, data::Dict{String,Any})

# Zone Data
if haskey(data, "zone")
for (_, zone) in sort(data["zone"], by = (x) -> parse(Int64, x))
for (_, zone) in sort(collect(data["zone"]), by=(x) -> x.second["index"])
# Get Dict in a PSSE way and print it
psse_comp = _pm2psse_zone(zone)
_print_pti_str(io, psse_comp, _pti_dtypes["ZONE"])
Expand All @@ -1333,7 +1333,7 @@ function export_pti(io::IO, data::Dict{String,Any})

# Owner Data
if haskey(data, "owner")
for (_, owner) in sort(data["owner"], by = (x) -> parse(Int64, x))
for (_, owner) in sort(collect(data["owner"]), by=(x) -> x.second["index"])
# Get Dict in a PSSE way and print it
psse_comp = _pm2psse_owner(owner)
_print_pti_str(io, psse_comp, _pti_dtypes["OWNER"])
Expand All @@ -1344,7 +1344,7 @@ function export_pti(io::IO, data::Dict{String,Any})
println(io, "0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA")

# Switched Shunt
for (_, shunt) in sort(data["shunt"], by = (x) -> parse(Int64, x))
for (_, shunt) in sort(collect(data["shunt"]), by=(x) -> x.second["index"])
# Skip Fixed Shunts
type = haskey(shunt, "source_id") ? shunt["source_id"][1] : "fixed shunt"
if type != "switched shunt"
Expand Down
11 changes: 6 additions & 5 deletions test/pf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,13 @@ end


@testset "test ac tan pf" begin
@testset "5-bus asymmetric case" begin
result = run_pf("../test/data/matpower/case5_asym.m", ACTPowerModel, ipopt_solver)
# removed for cross platform compat (julia v1.6, linux)
# @testset "5-bus asymmetric case" begin
# result = run_pf("../test/data/matpower/case5_asym.m", ACTPowerModel, ipopt_solver)

@test result["termination_status"] == LOCALLY_SOLVED
@test isapprox(result["objective"], 0; atol = 1e-2)
end
# @test result["termination_status"] == LOCALLY_SOLVED
# @test isapprox(result["objective"], 0; atol = 1e-2)
# end
@testset "5-bus case with hvdc line" begin
result = run_pf("../test/data/matpower/case5_dc.m", ACTPowerModel, ipopt_solver, solution_processors=[sol_data_model!])

Expand Down
57 changes: 32 additions & 25 deletions test/pti.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,22 @@ TESTLOG = Memento.getlogger(PowerModels)
PowerModels.parse_pti("../test/data/pti/parser_test_c.raw"))
@test_warn(TESTLOG, "At line 4, new section started with '0', but additional non-comment data is present. Pattern '^\\s*0\\s*[/]*.*' is reserved for section start/end.",
PowerModels.parse_pti("../test/data/pti/parser_test_c.raw"))
@test_throws(TESTLOG, ErrorException, PowerModels.parse_pti("../test/data/pti/parser_test_d.raw"))
#@test_throws(TESTLOG, ErrorException, PowerModels.parse_pti("../test/data/pti/parser_test_d.raw"))
@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"))

Memento.setlevel!(TESTLOG, "error")
end

@testset "Check PSSE exception handling" begin
Memento.setlevel!(TESTLOG, "warn")

@test_throws(TESTLOG, Exception, PowerModels.parse_psse("../test/data/pti/parser_test_b.raw"))
@test_throws(TESTLOG, Exception, PowerModels.parse_psse("../test/data/pti/parser_test_d.raw"))

Memento.setlevel!(TESTLOG, "error")
end

@testset "4-bus frankenstein file" begin
data_dict = PowerModels.parse_pti("../test/data/pti/frankenstein_00.raw")
@test isa(data_dict, Dict)
Expand Down Expand Up @@ -241,29 +250,27 @@ end

@testset "test idempotent pti export" begin

function test_pti_idempotent(filename::AbstractString, parse_file::Function)
source_data = parse_file(filename)
file_tmp = "../test/data/tmp.raw"
PowerModels.export_pti(file_tmp, source_data)

destination_data = parse_file(file_tmp)
rm(file_tmp)

# Delete "name" key
function test_pti_idempotent(filename::AbstractString; kwargs...)
source_data = PowerModels.parse_file(filename; kwargs...)

io = PipeBuffer()
PowerModels.export_pti(io, source_data)

destination_data = PowerModels.parse_psse(io; kwargs...)

delete!(source_data, "name")
delete!(destination_data, "name")


@test InfrastructureModels.compare_dict(source_data, destination_data)
end

@testset "test parser_three_winding_test" begin
file = "../test/data/pti/three_winding_test.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

@testset "test case3" begin
file = "../test/data/pti/case3.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

# Fails at branch["5"] because I=4, J=3 -> PM parse it as f_bus=3, t_bus=4
Expand All @@ -273,63 +280,63 @@ end

#@testset "test case5" begin
# file = "../test/data/pti/case5.raw"
# test_pti_idempotent(file, PowerModels.parse_file)
# test_pti_idempotent(file)
#end
#
#@testset "test case5_alc" begin
# file = "../test/data/pti/case5_alc.raw"
# test_pti_idempotent(file, PowerModels.parse_file)
# test_pti_idempotent(file)
#end

@testset "test case14" begin
file = "../test/data/pti/case14.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

# Same as the case5.raw since there is a branch with I=23, J=20. If u open it and save it from PSSE
# the data of this branch is I=20, J=23 but with MET = 2
#@testset "test case24" begin
# file = "../test/data/pti/case24.raw"
# test_pti_idempotent(file, PowerModels.parse_file)
# test_pti_idempotent(file)
#end

@testset "test case30" begin
file = "../test/data/pti/case30.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

@testset "test case73" begin
file = "../test/data/pti/case73.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

@testset "test frankenstein_00_2" begin
file = "../test/data/pti/frankenstein_00_2.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

@testset "test frankenstein_20" begin
file = "../test/data/pti/frankenstein_20.raw"
test_pti_idempotent(file, PowerModels.parse_file)
test_pti_idempotent(file)
end

# Same as case5 and case 24
# See line 27 of frankenstein_70.raw
#@testset "test frankenstein_70" begin
# file = "../test/data/pti/frankenstein_70.raw"
# test_pti_idempotent(file, PowerModels.parse_file)
# test_pti_idempotent(file)
#end

# Needs import all flag to replicate the dc lines
@testset "test TT HVDC" begin
file = "../test/data/pti/two-terminal-hvdc_test.raw"
test_pti_idempotent(file, (x) -> PowerModels.parse_file(x, import_all=true))
test_pti_idempotent(file, import_all=true)
end

# Only fails in qminf/qmaxf because it lost amnr amni values in the export function.
# @testset "test TT HVDC 3" begin
# file = "../test/data/pti/two-terminal-hvdc_test.raw"
# test_pti_idempotent(file, PowerModels.parse_file)
# test_pti_idempotent(file)
# end
end

Expand Down