Skip to content

Commit

Permalink
Merge pull request #836 from NREL-SIIP/jd/pv_bus_parsing_correction
Browse files Browse the repository at this point in the history
correct matpower parsing
  • Loading branch information
jd-lara authored Nov 19, 2021
2 parents 2c33f27 + f6a2f2d commit fc04bb9
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 164 deletions.
24 changes: 24 additions & 0 deletions src/parsers/pm_io/matpower.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# #
#########################################################################

const MP_FIX_VOLTAGE_BUSES = [2, 3]

"Parses the matpwer data from either a filename or an IO object"
function parse_matpower(io::IO; validate = true)::Dict
mp_data = _parse_matpower_string(read(io, String))
Expand Down Expand Up @@ -209,11 +211,15 @@ function _parse_matpower_string(data_string::String)

if haskey(matlab_data, "mpc.bus")
buses = []
pv_bus_lookup = Dict{Int, Any}()
for bus_row in matlab_data["mpc.bus"]
bus_data = row_to_typed_dict(bus_row, _mp_bus_columns)
bus_data["index"] = check_type(Int, bus_row[1])
bus_data["source_id"] = ["bus", bus_data["index"]]
push!(buses, bus_data)
if bus_data["bus_type"] MP_FIX_VOLTAGE_BUSES
pv_bus_lookup[bus_data["index"]] = bus_data
end
end
case["bus"] = buses
else
Expand All @@ -226,8 +232,26 @@ function _parse_matpower_string(data_string::String)

if haskey(matlab_data, "mpc.gen")
gens = []
corrected_pv_bus_vm = Dict{Int, Float64}()
for (i, gen_row) in enumerate(matlab_data["mpc.gen"])
gen_data = row_to_typed_dict(gen_row, _mp_gen_columns)
bus_data = get(pv_bus_lookup, gen_data["gen_bus"], nothing)
if bus_data !== nothing
if bus_data["bus_type"] MP_FIX_VOLTAGE_BUSES &&
bus_data["vm"] != gen_data["vg"]
@info "Correcting vm in bus $(gen_data["gen_bus"]) to $(gen_data["vg"]) to match generator set-point"
if gen_data["gen_bus"] keys(corrected_pv_bus_vm)
if corrected_pv_bus_vm[gen_data["gen_bus"]] != gen_data["vg"]
@error(
"Generator voltage set-points for bus $(gen_data["gen_bus"]) are inconsistent. This can lead to unexpected results"
)
end
else
bus_data["vm"] = gen_data["vg"]
corrected_pv_bus_vm[gen_data["gen_bus"]] = gen_data["vg"]
end
end
end
gen_data["index"] = i
gen_data["source_id"] = ["gen", i]
push!(gens, gen_data)
Expand Down
81 changes: 47 additions & 34 deletions test/test_busnumberchecks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,56 @@
base_dir = dirname(dirname(pathof(PowerSystems)))

@testset "Check bus index" begin
sys = System(PowerSystems.PowerModelsData(joinpath(MATPOWER_DIR, "case5_re.m")))
@test sort([b.number for b in collect(get_components(Bus, sys))]) == [1, 2, 3, 4, 10]
@test sort(
collect(Set([b.arc.from.number for b in collect(get_components(Branch, sys))])),
) == [1, 2, 3, 4]
@test sort(
collect(Set([b.arc.to.number for b in collect(get_components(Branch, sys))])),
) == [2, 3, 4, 10]

# TODO: add test for loadzones testing MAPPING_BUSNUMBER2INDEX
# This signature is used to capture expected error logs from parsing matpower
test_bus_index =
() -> begin
sys = System(PowerSystems.PowerModelsData(joinpath(MATPOWER_DIR, "case5_re.m")))
@test sort([b.number for b in collect(get_components(Bus, sys))]) == [1, 2, 3, 4, 10]
@test sort(
collect(
Set([b.arc.from.number for b in collect(get_components(Branch, sys))]),
),
) == [1, 2, 3, 4]
@test sort(
collect(
Set([b.arc.to.number for b in collect(get_components(Branch, sys))]),
),
) == [2, 3, 4, 10]

# TODO: add test for loadzones testing MAPPING_BUSNUMBER2INDEX
end
@test_logs min_level = Logging.Error match_mode = :any test_bus_index()
end

@testset "Test unique bus numbers" begin
sys = System(PowerSystems.PowerModelsData(joinpath(MATPOWER_DIR, "case5_re.m")))
number = 100
bus1 = Bus(;
number = number,
name = "bus100",
bustype = BusTypes.PV,
angle = 1.0,
magnitude = 1.0,
voltage_limits = (min = -1.0, max = 1.0),
base_voltage = 1.0,
)
bus2 = Bus(;
number = number,
name = "bus101",
bustype = BusTypes.PV,
angle = 1.0,
magnitude = 1.0,
voltage_limits = (min = -1.0, max = 1.0),
base_voltage = 1.0,
area = nothing,
load_zone = nothing,
)
# This signature is used to capture expected error logs from parsing matpower
test_bus_numbers =
() -> begin
sys = System(PowerSystems.PowerModelsData(joinpath(MATPOWER_DIR, "case5_re.m")))
number = 100
bus1 = Bus(;
number = number,
name = "bus100",
bustype = BusTypes.PV,
angle = 1.0,
magnitude = 1.0,
voltage_limits = (min = -1.0, max = 1.0),
base_voltage = 1.0,
)
bus2 = Bus(;
number = number,
name = "bus101",
bustype = BusTypes.PV,
angle = 1.0,
magnitude = 1.0,
voltage_limits = (min = -1.0, max = 1.0),
base_voltage = 1.0,
area = nothing,
load_zone = nothing,
)

add_component!(sys, bus1)
@test_throws ArgumentError add_component!(sys, bus2)
add_component!(sys, bus1)
@test_throws ArgumentError add_component!(sys, bus2)
end
@test_logs min_level = Logging.Error match_mode = :any test_bus_numbers()
end
35 changes: 33 additions & 2 deletions test/test_parse_matpower.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ POWER_MODELS_KEYS = [
]

badfiles = Dict("case30.m" => PSY.InvalidValue)
voltage_inconsistent_files =
["RTS_GMLC.m", "case5_re.m", "case5_re_intid.m", "case5_re_uc.m", "case5_re_uc_pwl.m"]

@testset "Parse Matpower data files" begin
files = [x for x in readdir(joinpath(MATPOWER_DIR)) if splitext(x)[2] == ".m"]
Expand All @@ -26,7 +28,12 @@ badfiles = Dict("case30.m" => PSY.InvalidValue)
@info "Parsing $f..."
path = joinpath(MATPOWER_DIR, f)

pm_dict = PowerSystems.parse_file(path)
if f in voltage_inconsistent_files
continue
else
pm_dict = PowerSystems.parse_file(path)
end

for key in POWER_MODELS_KEYS
@test haskey(pm_dict, key)
end
Expand Down Expand Up @@ -58,7 +65,12 @@ end
@info "Parsing $f..."
path = joinpath(joinpath(DATA_DIR, "pm_data", "matpower"), f)

pm_dict = PowerSystems.parse_file(path)
if f in voltage_inconsistent_files
continue
else
pm_dict = PowerSystems.parse_file(path)
end

for key in POWER_MODELS_KEYS
@test haskey(pm_dict, key)
end
Expand All @@ -76,3 +88,22 @@ end
end
end
end

@testset "Parse Matpower files with voltage inconsistencies" begin
test_parse = (path) -> begin
pm_dict = PowerSystems.parse_file(path)

for key in POWER_MODELS_KEYS
@test haskey(pm_dict, key)
end
@info "Successfully parsed $path to PowerModels dict"
sys = System(PowerSystems.PowerModelsData(pm_dict))
@info "Successfully parsed $path to System struct"
@test isa(sys, System)
end
for f in voltage_inconsistent_files
@info "Parsing $f..."
path = joinpath(MATPOWER_DIR, f)
@test_logs (:error,) match_mode = :any test_parse(path)
end
end
11 changes: 3 additions & 8 deletions test/test_parse_tamu.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
@testset "TAMU Parsing " begin
files = readdir(TAMU_DIR)
if length(files) == 0
error("No test files in the folder")
end

sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys")
@info "Successfully parsed $TAMU_DIR to System struct"

# Added due to inconsistencies in the incoming data. If the data is fixed, this test_logs call will fail
sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys"; force_build = true)
@test isa(sys, PSY.System)
# Test bad input
@test_throws PowerSystems.DataFormatError TamuSystem("")
end
Loading

0 comments on commit fc04bb9

Please sign in to comment.