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
11 changes: 6 additions & 5 deletions src/openapi/generation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ function _make_hydro_dispatch_body!(
"MW/min")
set_value!(component, :base_power, mbase, "MVA")
add_component!(sys, component)
set_component_ext!(sys, component, get(pm_gen, "ext", Dict{String, Any}()))
return
end

Expand Down Expand Up @@ -368,6 +369,7 @@ function make_renewable_dispatch!(
set_value!(component, :power_factor, 1.0, "1")
set_value!(component, :base_power, mbase, "MVA")
add_component!(sys, component)
set_component_ext!(sys, component, get(pm_gen, "ext", Dict{String, Any}()))
return
end

Expand Down Expand Up @@ -402,6 +404,7 @@ function make_renewable_nondispatch!(
set_value!(component, :power_factor, 1.0, "1")
set_value!(component, :base_power, mbase, "MVA")
add_component!(sys, component)
set_component_ext!(sys, component, get(pm_gen, "ext", Dict{String, Any}()))
return
end

Expand Down Expand Up @@ -485,11 +488,8 @@ function make_storage!(
set_value!(component, :storage_technology_type, "OTHER_CHEM")
set_value!(component, :storage_capacity, _natural_value(energy_rating, thermal_rating),
"MWh")
set_value!(
component,
:storage_level_limits,
IC.MinMax(; min = 0.0, max = _natural_value(energy_rating, thermal_rating)),
)
# A fraction of storage_capacity; pm data carries no derate, so the full band.
set_value!(component, :storage_level_limits, IC.MinMax(; min = 0.0, max = 1.0))
set_value!(component, :initial_storage_capacity_level, d["energy"] / energy_rating, "1")
set_value!(component, :rating, _natural_value(thermal_rating, thermal_rating), "MVA")
set_value!(component, :active_power, _natural_value(d["ps"], thermal_rating), "MW")
Expand Down Expand Up @@ -519,6 +519,7 @@ function make_storage!(
)
set_value!(component, :base_power, thermal_rating, "MVA")
add_component!(sys, component)
set_component_ext!(sys, component, get(d, "ext", Dict{String, Any}()))
return
end

Expand Down
3 changes: 3 additions & 0 deletions src/openapi/load.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function _make_standard_load!(
set_value!(load, :conformity, _conformity_string(Int(d["conformity"])))
_set_zip_fields!(load, d, base_power)
add_component!(sys, load)
set_component_ext!(sys, load, get(d, "ext", Dict{String, Any}()))
return
end

Expand All @@ -103,6 +104,7 @@ function _make_interruptible_standardload!(
set_value!(load, :conformity, _conformity_string(Int(d["conformity"])))
_set_zip_fields!(load, d, base_power)
add_component!(sys, load)
set_component_ext!(sys, load, get(d, "ext", Dict{String, Any}()))
return
end

Expand All @@ -122,6 +124,7 @@ function _make_power_load!(
set_value!(load, :max_reactive_power, d["qd"] * base_power, "MVAr")
set_value!(load, :conformity, _conformity_string(Int(d["conformity"])))
add_component!(sys, load)
set_component_ext!(sys, load, get(d, "ext", Dict{String, Any}()))
return
end

Expand Down
1 change: 1 addition & 0 deletions src/openapi/topology.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ function read_bus!(sys::OpenAPISystem, data::Dict; kwargs...)
set_value!(bus, :bustype, "SLACK")
end
add_component!(sys, bus)
set_component_ext!(sys, bus, get(d, "ext", Dict{String, Any}()))
end
return
end
Expand Down
40 changes: 40 additions & 0 deletions test/test_openapi_generation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ end
@test PFP.get_value(storage, :reactive_power_limits).min ≈ -0.0075
@test PFP.get_value(storage, :reactive_power_limits).max ≈ 0.0075
@test PFP.get_value(storage, :initial_storage_capacity_level) == 0.5
# A fraction of storage_capacity: no derate means the full [0, 1] band.
@test PFP.get_value(storage, :storage_level_limits).min == 0.0
@test PFP.get_value(storage, :storage_level_limits).max == 1.0
@test PFP.get_value(storage, :prime_mover_type) == "BA"
@test PFP.get_value(storage, :storage_technology_type) == "OTHER_CHEM"
end
Expand Down Expand Up @@ -386,6 +389,8 @@ end
@test PFP.get_value(storage, :reactive_power_limits).max ≈ d["qmax"]
# Dimensionless / ratio fields: untouched by COMPONENT_BASE either way.
@test PFP.get_value(storage, :initial_storage_capacity_level) == 0.5
@test PFP.get_value(storage, :storage_level_limits).min == 0.0
@test PFP.get_value(storage, :storage_level_limits).max == 1.0
end

@testset "COMPONENT_BASE conversion errors loudly on an unregistered instance-dispatched field" begin
Expand Down Expand Up @@ -451,6 +456,41 @@ end
@test PFP.get_value(hydro, :active_power) ≈ 500.0
end

@testset "hydro, renewable and storage makers carry their pm dict entry's ext" begin
sys = PFP.OpenAPISystem(100.0)
reg = PFP.get_registry(sys)
bus = _register_test_bus!(sys)
gen(type, name) = Dict{String, Any}(
"mbase" => 100.0, "gen_status" => true, "pg" => 5.0, "qg" => 1.0,
"pmax" => 20.0, "pmin" => 0.0, "qmax" => 10.0, "qmin" => -10.0,
"type" => type, "ext" => Dict{String, Any}("mRID" => name),
)
PFP.make_hydro_dispatch!(sys, reg, bus, gen("ROR", "hy1"), "hy1", 100.0)
PFP.make_hydro_reservoir!(sys, reg, bus, gen("HYDRO", "hy2"), "hy2", 100.0)
PFP.make_renewable_dispatch!(sys, reg, bus, gen("WIND", "wind1"), "wind1", 100.0)
PFP.make_renewable_nondispatch!(sys, reg, bus, gen("RTPV", "pv1"), "pv1", 100.0)
storage = Dict{String, Any}(
"energy_rating" => 50.0, "energy" => 25.0, "status" => true,
"thermal_rating" => 1.0, "ps" => 0.02, "charge_rating" => 0.05,
"discharge_rating" => 0.05, "charge_efficiency" => 0.9,
"discharge_efficiency" => 0.9, "qs" => 0.0, "qmin" => -0.05, "qmax" => 0.05,
"ext" => Dict{String, Any}("mRID" => "bat1"),
)
PFP.make_storage!(sys, reg, bus, storage, "bat1", 100.0)

components = vcat(
PFP.get_components(sys, "HydroDispatch"),
PFP.get_components(sys, "RenewableDispatch"),
PFP.get_components(sys, "RenewableNonDispatch"),
PFP.get_components(sys, "EnergyReservoirStorage"),
)
@test length(components) == 5
for component in components
@test PFP.get_ext(sys, PFP.get_value(component, :id)) ==
Dict{String, Any}("mRID" => PFP.get_value(component, :name))
end
end

@testset "get_generator_type resolving to EnergyReservoirStorage from \"gen\" is an error, not a skip" begin
sys = PFP.OpenAPISystem(100.0)
reg = PFP.get_registry(sys)
Expand Down
40 changes: 40 additions & 0 deletions test/test_openapi_load.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,43 @@ end
@test PFP.get_value(load, :conformity) == "CONFORMING"
end
end

@testset "every load type carries its pm dict entry's ext" begin
pm = fourteen_bus_pm_data()
data = deepcopy(pm.data)
data["load"][first(keys(data["load"]))]["interruptible"] = 1
for (key, d) in data["load"]
d["ext"]["mRID"] = "load-$key"
end
sys = PFP.OpenAPISystem(Float64(data["baseMVA"]))
PFP.read_loadzones!(sys, data)
PFP.read_bus!(sys, data)
PFP.read_loads!(sys, data)
loads = vcat(
PFP.get_components(sys, "StandardLoad"),
PFP.get_components(sys, "InterruptibleStandardLoad"),
)
@test length(loads) == 13
for load in loads
d = only(
v for v in values(data["load"]) if
strip(join(v["source_id"])) == PFP.get_value(load, :name)
)
@test PFP.get_ext(sys, PFP.get_value(load, :id)) == d["ext"]
end

pm = PFP.PowerModelsData(joinpath(MATPOWER_DIR, "case5.m"))
for (key, d) in pm.data["load"]
d["ext"] = Dict{String, Any}("mRID" => "load-$key")
end
sys = PFP.build_openapi_system(pm)
loads = PFP.get_components(sys, "PowerLoad")
@test length(loads) == 3
for load in loads
d = only(
v for v in values(pm.data["load"]) if
strip(join(v["source_id"])) == PFP.get_value(load, :name)
)
@test PFP.get_ext(sys, PFP.get_value(load, :id)) == d["ext"]
end
end
12 changes: 12 additions & 0 deletions test/test_openapi_topology.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ end
@test all(b -> PFP.get_value(b, :bustype) != "SLACK", other)
end

@testset "ACBus carries its pm dict entry's ext" begin
pm = fourteen_bus_pm_data()
for (number, d) in pm.data["bus"]
d["ext"] = Dict{String, Any}("mRID" => "bus-$number")
end
sys = PFP.build_openapi_system(pm)
for bus in PFP.get_components(sys, "ACBus")
ext = PFP.get_ext(sys, PFP.get_value(bus, :id))
@test ext == Dict{String, Any}("mRID" => "bus-$(PFP.get_value(bus, :number))")
end
end

@testset "Area.ext carries PSS/E AREA DATA metadata matched by area_number" begin
pm_data =
PFP.PowerModelsData(joinpath(@__DIR__, "fixtures", "v35_area_slack_variants.raw"))
Expand Down
Loading