Skip to content

Commit

Permalink
Add built-in entities for load, storage, fuel types
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielKS committed Feb 27, 2024
1 parent 71312b7 commit 236607c
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/PowerAnalytics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export is_col_meta, set_col_meta, set_col_meta!, time_df, time_vec, data_cols, d
export compute, compute_set, compute_all, hcat_timed, aggregate_time, compose_metrics
export read_serialized_system,
get_populated_decision_problem_results, create_problem_results_dict
export load_entity, storage_entity, generator_entities_by_fuel
export calc_active_power, calc_production_cost, calc_startup_cost, calc_shutdown_cost,
calc_discharge_cycles, calc_system_slack_up, calc_load_forecast, calc_active_power_in,
calc_active_power_out, calc_stored_energy, calc_active_power_forecast, calc_curtailment,
Expand Down Expand Up @@ -56,6 +57,7 @@ include("fuel_results.jl")
include("entities.jl")
include("metrics.jl")
include("input.jl")
include("builtin_entities.jl")
include("builtin_metrics.jl")

greet() = print("Hello World!")
Expand Down
62 changes: 62 additions & 0 deletions src/builtin_entities.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"An Entity representing all the electric load in a System"
load_entity = make_entity(PSY.ElectricLoad)

"An Entity representing all the storage in a System"
storage_entity = make_entity(PSY.Storage)

FUEL_TYPES_DATA_FILE =
joinpath(dirname(dirname(pathof(PowerAnalytics))), "deps", "generator_mapping.yaml")

# Parse the strings in generator_mapping.yaml into types and enum items
function parse_fuel_category(category_spec::Dict)
# Use reflection to look up the type corresponding the generator type string (this isn't a security risk, is it?)
gen_type = getproperty(PowerSystems, Symbol(get(category_spec, "gentype", Component)))
(gen_type === Any) && (gen_type = Component)
@assert gen_type <: Component

pm = get(category_spec, "primemover", nothing)
isnothing(pm) || (pm = PowerSystems.parse_enum_mapping(PowerSystems.PrimeMovers, pm))

fc = get(category_spec, "fuel", nothing)
isnothing(fc) || (fc = PowerSystems.parse_enum_mapping(PowerSystems.ThermalFuels, fc))

return gen_type, pm, fc
end

function make_fuel_entity(category_spec::Dict)
parse_results = parse_fuel_category(category_spec)
(gen_type, prime_mover, fuel_category) = parse_results

function filter_closure(comp::Component)
comp_sig = Tuple{typeof(comp)}
if !isnothing(prime_mover)
hasmethod(PowerSystems.get_prime_mover_type, comp_sig) || return false
(PowerSystems.get_prime_mover_type(comp) == prime_mover) || return false
end
if !isnothing(fuel_category)
hasmethod(PowerSystems.get_fuel, comp_sig) || return false
(PowerSystems.get_fuel(comp) == fuel_category) || return false
end
return true
end

# Create a nice name that is guaranteed to never collide with fully-qualified component names
entity_name = join(ifelse.(isnothing.(parse_results), "", string.(parse_results)),
NAME_DELIMETER)

return make_entity(filter_closure, gen_type, entity_name)
end

# Based on old PowerAnalytics' get_generator_mapping
function load_generator_fuel_mappings(filename = FUEL_TYPES_DATA_FILE)
in_data = open(YAML.load, filename)
mappings = OrderedDict{String, Entity}()
for top_level in in_data |> keys |> collect |> sort
sub_entities = make_fuel_entity.(in_data[top_level])
mappings[top_level] = make_entity(sub_entities...; name = top_level)
end
return mappings
end

"A dictionary of nested entities representing all the generators in a System categorized by fuel type"
generator_entities_by_fuel = load_generator_fuel_mappings()
4 changes: 2 additions & 2 deletions src/builtin_metrics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,15 @@ calc_system_load_forecast = SystemTimedMetric(
"SystemLoadForecast",
"Fetch the forecast active load of all the ElectricLoad Components in the system",
(res::IS.Results, st::Union{Nothing, Dates.DateTime}, len::Union{Int, Nothing}) ->

Check warning on line 187 in src/builtin_metrics.jl

View check run for this annotation

Codecov / codecov/patch

src/builtin_metrics.jl#L187

Added line #L187 was not covered by tests
compute(calc_load_forecast, res, make_entity(PSY.ElectricLoad), st, len),
compute(calc_load_forecast, res, load_entity, st, len),
)

calc_system_load_from_storage = let
SystemTimedMetric(
"SystemLoadFromStorage",
"Fetch the LoadFromStorage of all storage in the system",
(res::IS.Results, st::Union{Nothing, Dates.DateTime}, len::Union{Int, Nothing}) ->

Check warning on line 195 in src/builtin_metrics.jl

View check run for this annotation

Codecov / codecov/patch

src/builtin_metrics.jl#L195

Added line #L195 was not covered by tests
compute(calc_load_from_storage, res, make_entity(PSY.Storage), st, len),
compute(calc_load_from_storage, res, storage_entity, st, len),
)
end

Expand Down
27 changes: 27 additions & 0 deletions test/test_builtin_entities.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
test_sys = PSB.build_system(PSB.PSITestSystems, "c_sys5_all_components")
test_sys2 = PSB.build_system(PSB.PSITestSystems, "c_sys5_bat")
name_and_type = component -> (typeof(component), get_name(component))

@testset "Test load_entity and storage_entity" begin
@test Set(name_and_type.(get_components(load_entity, test_sys))) ==
Set([(PowerLoad, "Bus2"), (PowerLoad, "Bus4"), (StandardLoad, "Bus3")])
@test Set(name_and_type.(get_components(storage_entity, test_sys2))) ==
Set([(GenericBattery, "Bat")])
end

@testset "Test generator_entities_by_fuel" begin
@test isfile(PA.FUEL_TYPES_DATA_FILE)
@test Set(keys(generator_entities_by_fuel)) ==
Set(["Biopower", "CSP", "Coal", "Geothermal", "Hydropower", "NG-CC", "NG-CT",
"NG-Steam", "Natural gas", "Nuclear", "Other", "PV", "Petroleum", "Storage",
"Wind"])
@test Set(
name_and_type.(get_components(generator_entities_by_fuel["Wind"], test_sys)),
) == Set([(RenewableDispatch, "WindBusB"), (RenewableDispatch, "WindBusC"),
(RenewableDispatch, "WindBusA")])
@test Set(
name_and_type.(get_components(generator_entities_by_fuel["Coal"], test_sys)),
) == Set([(ThermalStandard, "Park City"), (ThermalStandard, "Sundance"),
(ThermalStandard, "Alta"), (ThermalStandard, "Solitude"),
(ThermalStandard, "Brighton")])
end

0 comments on commit 236607c

Please sign in to comment.