From 4e6d229f9787f37cbad9d0a502777c78c9d9bb1a Mon Sep 17 00:00:00 2001 From: lbonaldo Date: Mon, 18 Dec 2023 18:10:16 -0500 Subject: [PATCH] Add PWFU to load_resource --- src/load_inputs/load_dataframe.jl | 20 ++ src/load_inputs/load_generators_data.jl | 79 +++--- src/load_inputs/load_resources_data.jl | 239 +++++++++++-------- src/model/resources/resources.jl | 4 + test/Inputfiles/piecewisefuel_usage_data.csv | 2 + 5 files changed, 212 insertions(+), 132 deletions(-) create mode 100644 test/Inputfiles/piecewisefuel_usage_data.csv diff --git a/src/load_inputs/load_dataframe.jl b/src/load_inputs/load_dataframe.jl index e5c7c9e3c0..62eab38b47 100644 --- a/src/load_inputs/load_dataframe.jl +++ b/src/load_inputs/load_dataframe.jl @@ -188,3 +188,23 @@ function extract_matrix_from_dataframe(df::DataFrame, columnprefix::AbstractStri Matrix(dropmissing(df[:, sorted_columns])) end +function sort_dataframe_by_resource_names!(df::DataFrame, gen::Vector{<:AbstractResource}) + # get all resource names + resource_names = resource_name.(gen) + # get resources in dataframe + resource_in_df = resource_ids(df) + # sort by resource name + indices = sortperm(findfirst.(isequal.(resource_in_df), Ref(resource_names))) + permute!(df, indices) + return resource_ids(df) +end + +function resource_ids(df::DataFrame) + if hasproperty(df, :Resource) + return df.Resource + elseif hasproperty(df, :resource) + return df.resource + else + error("Dataframe must have a column named 'Resource' or 'resource' with resource names.") + end +end \ No newline at end of file diff --git a/src/load_inputs/load_generators_data.jl b/src/load_inputs/load_generators_data.jl index 501091bf17..b8547fe0ea 100644 --- a/src/load_inputs/load_generators_data.jl +++ b/src/load_inputs/load_generators_data.jl @@ -248,10 +248,10 @@ end Function for checking that no other technology flags have been activated and specific data inputs have been zeroed for the co-located VRE-STOR module """ -function check_vre_stor_validity(df::DataFrame, setup::Dict) +function check_vre_stor_validity(setup::Dict, gen::Vector{AbstractResource}) # Determine if any VRE-STOR resources exist - vre_stor = is_nonzero(df, :VRE_STOR) - r_id = df[:, :R_ID] + vre_stor_exist = !isempty(GenX.vre_stor(gen)) + r_id = resource_id(gen) error_strings = String[] @@ -259,28 +259,29 @@ function check_vre_stor_validity(df::DataFrame, setup::Dict) string("Generators ", data, ", marked as VRE-STOR, have ", col, " ≠ 0. ", col, " must be 0.") end - function check_any_nonzero_with_vre_stor!(error_strings::Vector{String}, df::DataFrame, col::Symbol) - check = vre_stor .& is_nonzero(df, col) + function check_any_nonzero_with_vre_stor!(error_strings::Vector{String}, gen::Vector{AbstractResource}, col::Symbol) + check = vre_stor_exist .& is_nonzero(gen, col) if any(check) e = error_feedback(r_id[check], col) push!(error_strings, e) end end + # TODO: check it when VRE_STOR is ready # Confirm that any other flags/inputs are not activated (all other flags should be activated in the vre_stor_data.csv) - check_any_nonzero_with_vre_stor!(error_strings, df, :Var_OM_Cost_per_MWh_In) - if setup["EnergyShareRequirement"]==1 - nESR = count(occursin.("ESR_", names(df))) - for i in 1:nESR - check_any_nonzero_with_vre_stor!(error_strings, df, Symbol(string("ESR_",i))) - end - end - if setup["CapacityReserveMargin"]==1 - nCapRes = count(occursin.("CapRes_", names(df))) - for i in 1:nCapRes - check_any_nonzero_with_vre_stor!(error_strings, df, Symbol(string("CapRes_",i))) - end - end + # check_any_nonzero_with_vre_stor!(error_strings, res, :var_om_cost_per_mwh_in) + # if setup["EnergyShareRequirement"]==1 + # nESR = count(occursin.("ESR_", names(df))) + # for i in 1:nESR + # check_any_nonzero_with_vre_stor!(error_strings, df, Symbol(string("ESR_",i))) + # end + # end + # if setup["CapacityReserveMargin"]==1 + # nCapRes = count(occursin.("CapRes_", names(df))) + # for i in 1:nCapRes + # check_any_nonzero_with_vre_stor!(error_strings, df, Symbol(string("CapRes_",i))) + # end + # end return error_strings end @@ -371,8 +372,8 @@ Function for reading input parameters related to co-located VRE-storage resource function load_vre_stor_data!(inputs_gen::Dict, setup::Dict, path::AbstractString) error_strings = String[] - dfGen = inputs_gen["dfGen"] - inputs_gen["VRE_STOR"] = "VRE_STOR" in names(dfGen) ? dfGen[dfGen.VRE_STOR.==1,:R_ID] : Int[] + res = inputs_gen["RESOURCES"] + inputs_gen["VRE_STOR"] = vre_stor(res) # Check if VRE-STOR resources exist if !isempty(inputs_gen["VRE_STOR"]) @@ -518,22 +519,39 @@ function load_vre_stor_data!(inputs_gen::Dict, setup::Dict, path::AbstractString summarize_errors(error_strings) end -function process_piecewisefuelusage!(inputs::Dict, scale_factor) - gen_in = inputs["dfGen"] +function process_piecewisefuelusage!(inputs::Dict, path::AbstractString, gen::Vector{<:AbstractResource}, scale_factor) + filename = "piecewisefuel_usage_data.csv" + filepath = joinpath(path, filename) + + if isfile(filepath) + piecewisefuel_in = load_dataframe(filepath) + + # get the resource names from the dataframe + resource_with_pwfu = resource_ids(piecewisefuel_in) + # get all the resource names + resource_without_pwfu = setdiff(resource_name.(gen), resource_with_pwfu) + # fill dataframe with zeros for resources without piecewise fuel usage + for resource in resource_without_pwfu + new_row = (resource, zeros(ncol(piecewisefuel_in)-1)...) # first column is resource name + push!(piecewisefuel_in, new_row) + end - if any(occursin.(Ref("PWFU_"), names(gen_in))) - heat_rate_mat = extract_matrix_from_dataframe(gen_in, "PWFU_Heat_Rate_MMBTU_per_MWh") - load_point_mat = extract_matrix_from_dataframe(gen_in, "PWFU_Load_Point_MW") + # sort dataframe by resource names and return the sorted names + resource_in_df = sort_dataframe_by_resource_names!(piecewisefuel_in, gen) + + heat_rate_mat = extract_matrix_from_dataframe(piecewisefuel_in, "PWFU_Heat_Rate_MMBTU_per_MWh") + load_point_mat = extract_matrix_from_dataframe(piecewisefuel_in, "PWFU_Load_Point_MW") # check data input validate_piecewisefuelusage(heat_rate_mat, load_point_mat) # determine if a generator contains piecewise fuel usage segment based on non-zero heatrate - gen_in.HAS_PWFU = any(heat_rate_mat .!= 0 , dims = 2)[:] + nonzero_rows = any(heat_rate_mat .!= 0 , dims = 2)[:] + HAS_PWFU = resource_id.(resources_by_names(gen, resource_in_df))[nonzero_rows] num_segments = size(heat_rate_mat)[2] # translate the inital fuel usage, heat rate, and load points into intercept for each segment - fuel_usage_zero_load = gen_in[!,"PWFU_Fuel_Usage_Zero_Load_MMBTU_per_h"] + fuel_usage_zero_load = piecewisefuel_in[!,"PWFU_Fuel_Usage_Zero_Load_MMBTU_per_h"] # construct a matrix for intercept intercept_mat = zeros(size(heat_rate_mat)) # PWFU_Fuel_Usage_MMBTU_per_h is always the intercept of the first segment @@ -575,7 +593,7 @@ function process_piecewisefuelusage!(inputs::Dict, scale_factor) # create a PWFU_data that contain processed intercept and slope (i.e., heat rate) intercept_cols = [Symbol("PWFU_Intercept_", i) for i in 1:num_segments] intercept_df = DataFrame(intercept_mat, Symbol.(intercept_cols)) - slope_cols = Symbol.(filter(colname -> startswith(string(colname),"PWFU_Heat_Rate_MMBTU_per_MWh"),names(gen_in))) + slope_cols = Symbol.(filter(colname -> startswith(string(colname),"PWFU_Heat_Rate_MMBTU_per_MWh"),names(piecewisefuel_in))) slope_df = DataFrame(heat_rate_mat, Symbol.(slope_cols)) PWFU_data = hcat(slope_df, intercept_df) # no need to scale sclope, but intercept should be scaled when parameterscale is on (MMBTU -> billion BTU) @@ -584,9 +602,10 @@ function process_piecewisefuelusage!(inputs::Dict, scale_factor) inputs["slope_cols"] = slope_cols inputs["intercept_cols"] = intercept_cols inputs["PWFU_data"] = PWFU_data - inputs["PWFU_Num_Segments"] =num_segments - inputs["THERM_COMMIT_PWFU"] = intersect(gen_in[gen_in.THERM.==1,:R_ID], gen_in[gen_in.HAS_PWFU,:R_ID]) + inputs["PWFU_Num_Segments"] = num_segments + inputs["THERM_COMMIT_PWFU"] = intersect(thermal(gen), resource_id.(gen[HAS_PWFU])) end + return nothing end function validate_piecewisefuelusage(heat_rate_mat, load_point_mat) diff --git a/src/load_inputs/load_resources_data.jl b/src/load_inputs/load_resources_data.jl index 6849343395..96ae9e4270 100644 --- a/src/load_inputs/load_resources_data.jl +++ b/src/load_inputs/load_resources_data.jl @@ -1,4 +1,4 @@ -function _get_resource_info() +function _get_resource_info() resources = ( hydro = (filename="hydro.csv", type=HYDRO), thermal = (filename="thermal.csv", type=THERM), @@ -11,16 +11,16 @@ function _get_resource_info() return resources end -function _get_policy_info() +function _get_policyfile_info() policies = ( + esr = (filename="esr.csv", column_name="esr"), cap_res = (filename="cap_res.csv", column_name="derated_capacity"), - esr = (filename="esr.csv", column_name="esr"), - min_cap_tags = (filename="min_cap.csv", column_name="min_cap") + min_cap_tags = (filename="min_cap.csv", column_name="min_cap"), + max_cap_tags = (filename="max_cap.csv", column_name="max_cap") ) return policies end - function scale_resources_data!(resource_in::DataFrame, scale_factor::Float64) # See documentation for descriptions of each column # Generally, these scalings converts energy and power units from MW to GW @@ -73,7 +73,10 @@ function scale_resources_data!(resource_in::DataFrame, scale_factor::Float64) end function required_columns_for_co2() - return ["biomass", "co2_capture_fraction", "co2_capture_fraction_startup", "ccs_disposal_cost_per_metric_ton"] + return ("biomass", + "co2_capture_fraction", + "co2_capture_fraction_startup", + "ccs_disposal_cost_per_metric_ton") end function ensure_columns!(df::DataFrame) @@ -97,56 +100,6 @@ function _get_resource_df(path::AbstractString, scale_factor::Float64=1.0) return resource_in end -function _add_policy_to_resource!(resource::AbstractResource, new_symbols::Vector{Symbol}, new_values::T) where T <: Union{Real, Vector{<:Real}} - # loop over policy attributes - for (sym, value) in zip(new_symbols, new_values) - # add policy attribute to resource if value is not zero - value ≠ 0 && setproperty!(resource, sym, value) - end - return nothing -end - -function _add_policies_to_resources!(path::AbstractString, resources::Vector{<:AbstractResource}) - # get filename and column-name for each type of policy - resources_info = _get_policy_info() - # loop over policy files - for (filename, column_name) in values(resources_info) - # load policy file - policy_in = load_dataframe(joinpath(path, filename)) - # rename columns lowercase to ensure consistency with resources - rename!(policy_in, lowercase.(names(policy_in))) - # check if policy file has any attributes - validate_policy_dataframe!(policy_in, filename) - # extract attributes of policy - new_sym = Symbol.(filter(x -> x ≠ "resource", names(policy_in))) - # extract values of policy attributes - policy_attr_values = extract_matrix_from_dataframe(policy_in, column_name) - # add policy to resources - for i in 1:nrow(policy_in) - resource_name = policy_in[i, :resource] - resource = resource_by_name(resources, resource_name) - new_values = policy_attr_values[i,:] - _add_policy_to_resource!(resource, new_sym, new_values) - end - end - return nothing -end - -function validate_policy_dataframe!(policy_in::DataFrame, filename::AbstractString) - cols = names(policy_in) - n_cols = length(cols) - # check if policy file has any attributes - if n_cols == 1 - msg = "No policy attributes found in policy file: " * filename - error(msg) - end - # if the single column attribute does not have a tag number, add a tag number of 1 - if n_cols == 2 && cols[2][end-2] != "_1" - rename!(policy_in, Symbol.(cols[2]) => Symbol.(cols[2], "_1")) - end - return nothing -end - function _get_resource_indices(resources_in::DataFrame, offset::Int64) # return array of indices of resources range = (1,nrow(resources_in)) .+ offset @@ -193,7 +146,12 @@ function _get_all_resources(resources_folder::AbstractString, resources_info::Na return reduce(vcat, resources) end -function load_scaled_resources_data(resources_folder::AbstractString, scale_factor::Float64=1.0) +function load_scaled_resources_data(setup::Dict, case_path::AbstractString) +# Scale factor for energy and currency units + scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 + # get path to resources data + resources_folder = setup["ResourcePath"] + resources_folder = joinpath(case_path,resources_folder) # get type, filename and resource-key for each type of resources resources_info = _get_resource_info() # load each resource type, scale data and return array of resources @@ -203,51 +161,137 @@ function load_scaled_resources_data(resources_folder::AbstractString, scale_fact return resources end -function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources::Vector{<:AbstractResource}) +function _add_attributes_to_resource!(resource::AbstractResource, new_symbols::Vector{Symbol}, new_values::T) where T <: DataFrameRow + # loop over new attributes (new cols) + for (sym, value) in zip(new_symbols, new_values) + # add attribute to resource if value is not zero + value ≠ 0 && setproperty!(resource, sym, value) + end + return nothing +end + +function add_policies_to_resources!(setup::Dict, case_path::AbstractString, resources::Vector{<:AbstractResource}) + policy_folder = setup["PolicyPath"] + policy_folder = joinpath(case_path, policy_folder) + # get filename and column-name for each type of policy + resources_info = _get_policyfile_info() + # loop over policy files + for (filename, column_name) in values(resources_info) + # path to policy file + path = joinpath(policy_folder, filename) + # if file exists, add policy to resources + if isfile(path) + add_policy_to_resources!(path, filename, column_name, resources) + # print log + @info filename * " Successfully Read." + end + end +end + +function add_policy_to_resources!(path::AbstractString, filename::AbstractString, column_name::AbstractString, resources::Vector{<:AbstractResource}) + # load policy file + policy_in = load_dataframe(path) + # check if policy file has any attributes, validate clumn names + validate_policy_dataframe!(filename, column_name, policy_in) + # add policy attributes to resources + _add_df_to_resources!(resources, policy_in) + return nothing +end + +function add_module_to_resources!(resources::Vector{<:AbstractResource}, module_in::DataFrame) + _add_df_to_resources!(resources, module_in) + return nothing +end + +function validate_policy_dataframe!(filename::AbstractString, column_name::AbstractString, policy_in::DataFrame) + cols = names(policy_in) + n_cols = length(cols) + # check if policy file has any attributes + if n_cols == 1 + msg = "No policy attributes found in policy file: " * filename + error(msg) + end + # if the single column attribute does not have a tag number, add a tag number of 1 + if n_cols == 2 && cols[2][end-2] != "_1" + rename!(policy_in, Symbol.(cols[2]) => Symbol.(cols[2], "_1")) + end + # get policy column names + cols = lowercase.(names(policy_in)) + tag_names = cols[startswith.(cols, column_name)] + # Check that all policy columns are of the form policyname_tagnum + # - any: at least one column matches policyname_tagnum + # - all: all matches are found in the policy file + if !all(any(occursin.(string(column_name, "_$tag_num"), tag_names)) for tag_num in 1:length(tag_names)) + column_names = [string(column_name, "_$tag_num") for tag_num in 1:length(tag_names)] + msg = "Policy file $filename must have columns named $column_names, case insensitive." + error(msg) + end + return nothing +end + +function _add_df_to_resources!(resources::Vector{<:AbstractResource}, module_in::DataFrame) + # rename columns lowercase to ensure consistency with resources + rename!(module_in, lowercase.(names(module_in))) + # extract columns of module -> new resource attributes + new_sym = Symbol.(filter(x -> x ≠ "resource", names(module_in))) + # loop oper rows of module and add new attributes to resources + for row in eachrow(module_in) + resource_name = row[:resource] + resource = resource_by_name(resources, resource_name) + new_values = row[new_sym] + _add_attributes_to_resource!(resource, new_sym, new_values) + end + return nothing +end + +function add_resources_to_input_data!(setup::Dict, case_path::AbstractString, input_data::Dict, gen::Vector{<:AbstractResource}) # Number of resources - G = length(resources) + G = length(gen) input_data["G"] = G + # Scale factor + scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 + # Number of time steps (periods) T = input_data["T"] ## HYDRO # Set of all reservoir hydro resources - input_data["HYDRO_RES"] = hydro(resources) + input_data["HYDRO_RES"] = hydro(gen) # Set of hydro resources modeled with known reservoir energy capacity if !isempty(input_data["HYDRO_RES"]) - input_data["HYDRO_RES_KNOWN_CAP"] = intersect(input_data["HYDRO_RES"], has_hydro_energy_to_power_ratio(resources)) + input_data["HYDRO_RES_KNOWN_CAP"] = intersect(input_data["HYDRO_RES"], has_hydro_energy_to_power_ratio(gen)) end ## STORAGE # Set of storage resources with symmetric charge/discharge capacity - input_data["STOR_SYMMETRIC"] = symmetric_storage(resources) + input_data["STOR_SYMMETRIC"] = symmetric_storage(gen) # Set of storage resources with asymmetric (separte) charge/discharge capacity components - input_data["STOR_ASYMMETRIC"] = asymmetric_storage(resources) + input_data["STOR_ASYMMETRIC"] = asymmetric_storage(gen) # Set of all storage resources input_data["STOR_ALL"] = union(input_data["STOR_SYMMETRIC"],input_data["STOR_ASYMMETRIC"]) # Set of storage resources with long duration storage capabilitites - input_data["STOR_HYDRO_LONG_DURATION"] = intersect(input_data["HYDRO_RES"], is_LDS(resources)) - input_data["STOR_LONG_DURATION"] = intersect(input_data["STOR_ALL"], is_LDS(resources)) - input_data["STOR_SHORT_DURATION"] = intersect(input_data["STOR_ALL"], is_SDS(resources)) + input_data["STOR_HYDRO_LONG_DURATION"] = intersect(input_data["HYDRO_RES"], is_LDS(gen)) + input_data["STOR_LONG_DURATION"] = intersect(input_data["STOR_ALL"], is_LDS(gen)) + input_data["STOR_SHORT_DURATION"] = intersect(input_data["STOR_ALL"], is_SDS(gen)) ## VRE # Set of controllable variable renewable resources - input_data["VRE"] = vre(resources) + input_data["VRE"] = vre(gen) ## FLEX # Set of flexible demand-side resources - input_data["FLEX"] = flex_demand(resources) + input_data["FLEX"] = flex_demand(gen) ## TODO: MUST_RUN # Set of must-run plants - could be behind-the-meter PV, hydro run-of-river, must-run fossil or thermal plants - input_data["MUST_RUN"] = must_run(resources) + input_data["MUST_RUN"] = must_run(gen) ## ELECTROLYZER # Set of hydrogen electolyzer resources: - input_data["ELECTROLYZER"] = electrolyzer(resources) + input_data["ELECTROLYZER"] = electrolyzer(gen) ## Retrofit ## TODO: ask how to add it input_data["RETRO"] = [] @@ -255,30 +299,30 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources:: ## Reserves if setup["Reserves"] >= 1 # Set for resources with regulation reserve requirements - input_data["REG"] = has_regulation_reserve_requirements(resources) + input_data["REG"] = has_regulation_reserve_requirements(gen) # Set for resources with spinning reserve requirements - input_data["RSV"] = has_spinning_reserve_requirements(resources) + input_data["RSV"] = has_spinning_reserve_requirements(gen) end ## THERM # Set of all thermal resources - input_data["THERM_ALL"] = thermal(resources) + input_data["THERM_ALL"] = thermal(gen) # Unit commitment if setup["UCommit"] >= 1 # Set of thermal resources with unit commitment - input_data["THERM_COMMIT"] = has_unit_commitment(resources) + input_data["THERM_COMMIT"] = has_unit_commitment(gen) # Set of thermal resources without unit commitment - input_data["THERM_NO_COMMIT"] = no_unit_commitment(resources) + input_data["THERM_NO_COMMIT"] = no_unit_commitment(gen) # Start-up cost is sum of fixed cost per start startup input_data["C_Start"] = zeros(Float64, G, T) for g in input_data["THERM_COMMIT"] - start_up_cost = start_cost_per_mw(resources[g]) * cap_size(resources[g]) + start_up_cost = start_cost_per_mw(gen[g]) * cap_size(gen[g]) input_data["C_Start"][g,:] .= start_up_cost end # Piecewise fuel usage option input_data["PWFU_Num_Segments"] = 0 input_data["THERM_COMMIT_PWFU"] = Int64[] - # process_piecewisefuelusage!(input_data, scale_factor) + process_piecewisefuelusage!(input_data, case_path, gen, scale_factor) else # Set of thermal resources with unit commitment input_data["THERM_COMMIT"] = [] @@ -293,21 +337,21 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources:: input_data["VRE_STOR"] = [] # load_vre_stor_data!(input_data, setup, path) - buildable = is_buildable(resources) - retirable = is_retirable(resources) + buildable = is_buildable(gen) + retirable = is_retirable(gen) # Set of all resources eligible for new capacity - input_data["NEW_CAP"] = intersect(buildable, has_max_capacity_mw(resources)) + input_data["NEW_CAP"] = intersect(buildable, has_max_capacity_mw(gen)) # Set of all resources eligible for capacity retirements - input_data["RET_CAP"] = intersect(retirable, has_existing_capacity_mw(resources)) + input_data["RET_CAP"] = intersect(retirable, has_existing_capacity_mw(gen)) new_cap_energy = Set{Int64}() ret_cap_energy = Set{Int64}() if !isempty(input_data["STOR_ALL"]) # Set of all storage resources eligible for new energy capacity - new_cap_energy = intersect(buildable, has_max_capacity_mwh(resources), input_data["STOR_ALL"]) + new_cap_energy = intersect(buildable, has_max_capacity_mwh(gen), input_data["STOR_ALL"]) # Set of all storage resources eligible for energy capacity retirements - ret_cap_energy = intersect(retirable, has_existing_capacity_mwh(resources), input_data["STOR_ALL"]) + ret_cap_energy = intersect(retirable, has_existing_capacity_mwh(gen), input_data["STOR_ALL"]) end input_data["NEW_CAP_ENERGY"] = new_cap_energy input_data["RET_CAP_ENERGY"] = ret_cap_energy @@ -316,27 +360,27 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources:: ret_cap_charge = Set{Int64}() if !isempty(input_data["STOR_ASYMMETRIC"]) # Set of asymmetric charge/discharge storage resources eligible for new charge capacity - new_cap_charge = intersect(buildable, has_max_charge_capacity_mw(resources), input_data["STOR_ASYMMETRIC"]) + new_cap_charge = intersect(buildable, has_max_charge_capacity_mw(gen), input_data["STOR_ASYMMETRIC"]) # Set of asymmetric charge/discharge storage resources eligible for charge capacity retirements - ret_cap_charge = intersect(buildable, has_existing_charge_capacity_mw(resources), input_data["STOR_ASYMMETRIC"]) + ret_cap_charge = intersect(buildable, has_existing_charge_capacity_mw(gen), input_data["STOR_ASYMMETRIC"]) end input_data["NEW_CAP_CHARGE"] = new_cap_charge input_data["RET_CAP_CHARGE"] = ret_cap_charge # Names of resources - input_data["RESOURCE_NAMES"] = resource_name.(resources) + input_data["RESOURCE_NAMES"] = resource_name.(gen) # Zones resources are located in - zones = zone_id.(resources) + zones = zone_id.(gen) # Resource identifiers by zone (just zones in resource order + resource and zone concatenated) input_data["R_ZONES"] = zones input_data["RESOURCE_ZONES"] = input_data["RESOURCE_NAMES"] .* "_z" .* string.(zones) # Fuel - input_data["HAS_FUEL"] = has_fuel(resources) + input_data["HAS_FUEL"] = has_fuel(gen) - input_data["RESOURCES"] = resources + input_data["RESOURCES"] = gen return nothing end @@ -351,23 +395,14 @@ function load_resources_data!(setup::Dict, case_path::AbstractString, input_data # load_generators_data!(setup, case_path, input_data) # translate_generators_data!(setup, input_data) else - # Scale factor for energy and currency units - scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 - - # get path to resources data - resources_folder = setup["ResourcePath"] - resources_folder = joinpath(case_path,resources_folder) - # load resources data and scale it if necessary - resources = load_scaled_resources_data(resources_folder, scale_factor) + resources = load_scaled_resources_data(setup, case_path) # add policies-related attributes to resource dataframe - policy_folder = setup["PolicyPath"] - policy_folder = joinpath(case_path, policy_folder) - _add_policies_to_resources!(policy_folder, resources) - + add_policies_to_resources!(setup, case_path, resources) + # add resources to input_data dict - add_resources_to_input_data!(setup, input_data, resources) + add_resources_to_input_data!(setup, case_path, input_data, resources) return nothing end diff --git a/src/model/resources/resources.jl b/src/model/resources/resources.jl index ffea497847..63d603ee3e 100644 --- a/src/model/resources/resources.jl +++ b/src/model/resources/resources.jl @@ -386,6 +386,10 @@ function resource_by_name(rs::Vector{AbstractResource}, name::String) return rs[findfirst(r -> resource_name(r) == name, rs)] end +function resources_by_names(rs::Vector{AbstractResource}, names::Vector{String}) + return rs[findall(r -> resource_name(r) ∈ names, rs)] +end + @doc raw""" check_resource_type_flags(r::GenXResource) diff --git a/test/Inputfiles/piecewisefuel_usage_data.csv b/test/Inputfiles/piecewisefuel_usage_data.csv new file mode 100644 index 0000000000..eca69e4a13 --- /dev/null +++ b/test/Inputfiles/piecewisefuel_usage_data.csv @@ -0,0 +1,2 @@ +Resource,PWFU_Fuel_Usage_Zero_Load_MMBTU_per_h,PWFU_Heat_Rate_MMBTU_per_MWh_1,PWFU_Heat_Rate_MMBTU_per_MWh_2,PWFU_Load_Point_MW_1,PWFU_Load_Point_MW_2 +NENGREST_natural_gas_fired_combined_cycle_1,400,6,7.2,160,250 \ No newline at end of file