Skip to content

Commit

Permalink
More changes to interface data - write_outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
lbonaldo committed Dec 11, 2023
1 parent 9715090 commit 561f8ce
Show file tree
Hide file tree
Showing 64 changed files with 431 additions and 511 deletions.

This file was deleted.

8 changes: 7 additions & 1 deletion src/GenX.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ using HiGHS
# To translate $/MWh to $M/GWh, multiply by ModelScalingFactor
const ModelScalingFactor = 1e+3

# abstract type for all resources
abstract type AbstractResource end
# Name of the type of resources available in the model
const resources_type = (:ELECTROLYZER, :FLEX, :HYDRO, :STOR, :THERM, :VRE, :MUST_RUN)

# thanks, ChatGPT
function include_all_in_folder(folder)
base_path = joinpath(@__DIR__, folder)
Expand All @@ -60,8 +65,8 @@ end
include_all_in_folder("case_runners")
include_all_in_folder("configure_settings")
include_all_in_folder("configure_solver")
include_all_in_folder("model")
include_all_in_folder("load_inputs")
include_all_in_folder("model")
include_all_in_folder("write_outputs")

include("time_domain_reduction/time_domain_reduction.jl")
Expand All @@ -72,4 +77,5 @@ include("simple_operation.jl")

include_all_in_folder("multi_stage")
include_all_in_folder("additional_tools")

end
8 changes: 4 additions & 4 deletions src/additional_tools/modeling_to_generate_alternatives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ function mga(EP::Model, path::AbstractString, setup::Dict, inputs::Dict, outpath
Least_System_Cost = objective_value(EP)

# Read sets
resources = inputs["RESOURCES"]
res = inputs["RESOURCES"]
T = inputs["T"] # Number of time steps (hours)
Z = inputs["Z"] # Number of zonests
zones = unique(inputs["R_ZONES"])

# Create a set of unique technology types
resources_with_mga = has_mga_on(resources)
resources_with_mga = has_mga_on(res)
TechTypes = unique(resource_type.(resources_with_mga))

# Read slack parameter representing desired increase in budget from the least cost solution
Expand All @@ -52,8 +52,8 @@ function mga(EP::Model, path::AbstractString, setup::Dict, inputs::Dict, outpath

# Constraint to compute total generation in each zone from a given Technology Type
function resource_in_zone_with_TechType(tt::Int64, z::Int64)
condition::BitVector = (resource_type.(resources) .== TechTypes[tt]) .& (zone_id.(resources) .== z)
return resource_id.(resources[condition])
condition::BitVector = (resource_type.(res) .== TechTypes[tt]) .& (zone_id.(res) .== z)
return resource_id.(res[condition])
end
@constraint(EP,cGeneration[tt = 1:length(TechTypes), z = 1:Z], vSumvP[tt,z] == sum(EP[:vP][y,t] * inputs["omega"][t] for y in resource_in_zone_with_TechType(tt,z), t in 1:T))

Expand Down
4 changes: 1 addition & 3 deletions src/load_inputs/load_generators_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
# initial screen that resources are valid
resources = dataframerow_to_dict.(eachrow(gen_in))
validate_resources(resources)
inputs_gen["resources_d"] = resources
inputs_gen["RESOURCES"] = resources

# Number of resources (generators, storage, DR, and DERs)
G = nrow(gen_in)
Expand Down Expand Up @@ -520,8 +520,6 @@ end

function process_piecewisefuelusage!(inputs::Dict, scale_factor)
gen_in = inputs["dfGen"]
inputs["PWFU_Num_Segments"] = 0
inputs["THERM_COMMIT_PWFU"] = Int64[]

if any(occursin.(Ref("PWFU_"), names(gen_in)))
heat_rate_mat = extract_matrix_from_dataframe(gen_in, "PWFU_Heat_Rate_MMBTU_per_MWh")
Expand Down
1 change: 0 additions & 1 deletion src/load_inputs/load_inputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function load_inputs(setup::Dict,path::AbstractString)
load_fuels_data!(setup, path, inputs)
# Read in generator/resource related inputs
load_resources_data!(setup, path, inputs)
return inputs
# Read in generator/resource availability profiles
load_generators_variability!(setup, path, inputs)

Expand Down
6 changes: 3 additions & 3 deletions src/load_inputs/load_reserves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function load_reserves!(setup::Dict, path::AbstractString, inputs::Dict)
filename = "Reserves.csv"
res_in = load_dataframe(joinpath(path, filename))

resources = inputs["RESOURCES"]
res = inputs["RESOURCES"]

function load_field_with_deprecated_symbol(df::DataFrame, columns::Vector{Symbol})
best = popfirst!(columns)
Expand Down Expand Up @@ -52,11 +52,11 @@ function load_reserves!(setup::Dict, path::AbstractString, inputs::Dict)
if inputs["pDynamic_Contingency"] > 0
inputs["pContingency_BigM"] = zeros(Float64, inputs["G"])
for y in inputs["COMMIT"]
inputs["pContingency_BigM"][y] = max_capacity_mw(resources[y])
inputs["pContingency_BigM"][y] = max_capacity_mw(res[y])
# When Max_Cap_MW == -1, there is no limit on capacity size
if inputs["pContingency_BigM"][y] < 0
# NOTE: this effectively acts as a maximum cluster size when not otherwise specified, adjust accordingly
inputs["pContingency_BigM"][y] = 5000 * cap_size(resources[y])
inputs["pContingency_BigM"][y] = 5000 * cap_size(res[y])
end
end
end
Expand Down
20 changes: 11 additions & 9 deletions src/load_inputs/load_resources_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function _get_resource_info()
storage = (filename="storage.csv", type=STOR), #key="STOR_ALL"),
flex_demand = (filename="flex_demand.csv", type=FLEX), #key="FLEX"),
electrolyzer = (filename="electrolyzer.csv", type=ELECTROLYZER), #key="ELECTROLYZER")
must_run = (filename="must_run.csv", type=MUST_RUN) #key="MUST_RUN")
)
return resources
end
Expand Down Expand Up @@ -80,9 +81,6 @@ function _get_resource_df(path::AbstractString, scale_factor::Float64=1.0)
scale_resources_data!(resource_in, scale_factor)
# ensure columns
ensure_columns!(resource_in)

println("co2_capture_fraction" names(resource_in))

# return dataframe
return resource_in
end
Expand Down Expand Up @@ -115,8 +113,6 @@ function _get_all_resources(resources_folder::AbstractString, resources_info::Na
resource_id_offset += length(resources_same_type)
# print log
@info filename * " Successfully Read."
# add indices to input_data
# input_data[key] = resources_indices
end
return reduce(vcat, resources)
end
Expand All @@ -135,11 +131,13 @@ end
function load_resources_data!(setup::Dict, case_path::AbstractString, input_data::Dict)
if isfile(joinpath(case_path, "Generators_data.csv"))
Base.depwarn(
"The `Generators_data.csv` file will be deprecated in a future release. " *
"The `Generators_data.csv` file was deprecated in release v0.4. " *
"Please use the new interface for generators creation, and see the documentation for additional details.",
:load_resources_data!, force=true)
load_generators_data!(setup, case_path, input_data)
translate_generators_data!(setup, input_data)
@info "Exiting GenX..."
exit(-1)
# 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
Expand Down Expand Up @@ -206,13 +204,14 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources::

## 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(resources)

## ELECTROLYZER
# Set of hydrogen electolyzer resources:
input_data["ELECTROLYZER"] = electrolyzer(resources)

## Retrofit ## TODO: ask how to add it
input_data["RETRO"] = []

## Reserves
if setup["Reserves"] >= 1
Expand All @@ -238,6 +237,8 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources::
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)
else
# Set of thermal resources with unit commitment
Expand All @@ -250,6 +251,7 @@ function add_resources_to_input_data!(setup::Dict, input_data::Dict, resources::

## Co-located resources
# VRE and storage
input_data["VRE_STOR"] = []
# load_vre_stor_data!(input_data, setup, path)

buildable = is_buildable(resources)
Expand Down
26 changes: 10 additions & 16 deletions src/model/core/co2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,30 @@ function co2!(EP::Model, inputs::Dict)

println("CO2 Module")

resources = inputs["RESOURCES"]
res = inputs["RESOURCES"]
G = inputs["G"] # Number of resources (generators, storage, DR, and DERs)
T = inputs["T"] # Number of time steps (hours)
Z = inputs["Z"] # Number of zones
fuel_CO2 = inputs["fuel_CO2"] # CO2 content of fuel (t CO2/MMBTU or ktCO2/Billion BTU)

biomass(y::Int64) = biomass(resources[y])
fuel(y::Int64) = fuel(resources[y])
co2_capture_fraction(y::Int64) = co2_capture_fraction(resources[y])
co2_capture_fraction_startup(y::Int64) = co2_capture_fraction_startup(resources[y])
ccs_disposal_cost_per_metric_ton(y::Int64) = ccs_disposal_cost_per_metric_ton(resources[y])

### Expressions ###
# CO2 emissions from power plants in "Generators_data.csv"
# If all the CO2 capture fractions from Generators_data are zeros, the CO2 emissions from thermal generators are determined by fuel consumption times CO2 content per MMBTU
if all(co2_capture_fraction(resources) .==0)
if all(co2_capture_fraction.(res) .==0)
@expression(EP, eEmissionsByPlant[y=1:G, t=1:T],
((1-biomass(y)) *(EP[:vFuel][y, t] + EP[:eStartFuel][y, t]) * fuel_CO2[fuel(y)]))
((1-biomass(res[y])) *(EP[:vFuel][y, t] + EP[:eStartFuel][y, t]) * fuel_CO2[fuel(res[y])]))
else
@info "Using the CO2 module to determine the CO2 emissions of CCS-equipped plants"
# CO2_Capture_Fraction refers to the CO2 capture rate of CCS equiped power plants at a steady state
# CO2_Capture_Fraction_Startup refers to the CO2 capture rate of CCS equiped power plants during startup events
@expression(EP, eEmissionsByPlant[y=1:G, t=1:T],
(1-biomass(y) - co2_capture_fraction(y)) * EP[:vFuel][y, t] * fuel_CO2[fuel(y)]+
(1-biomass(y) - co2_capture_fraction_startup(y)) * EP[:eStartFuel][y, t] * fuel_CO2[fuel(y)])
(1-biomass(res[y]) - co2_capture_fraction(res[y])) * EP[:vFuel][y, t] * fuel_CO2[fuel(res[y])]+
(1-biomass(res[y]) - co2_capture_fraction_startup(res[y])) * EP[:eStartFuel][y, t] * fuel_CO2[fuel(res[y])])

# CO2 captured from power plants in "Generators_data.csv"
@expression(EP, eEmissionsCaptureByPlant[y=1:G, t=1:T],
co2_capture_fraction(y) * EP[:vFuel][y, t] * fuel_CO2[fuel(y)]+
co2_capture_fraction_startup(y) * EP[:eStartFuel][y, t] * fuel_CO2[fuel(y)])
co2_capture_fraction(res[y]) * EP[:vFuel][y, t] * fuel_CO2[fuel(res[y])]+
co2_capture_fraction_startup(res[y]) * EP[:eStartFuel][y, t] * fuel_CO2[fuel(res[y])])

@expression(EP, eEmissionsCaptureByPlantYear[y=1:G],
sum(inputs["omega"][t] * eEmissionsCaptureByPlant[y, t]
Expand All @@ -92,10 +86,10 @@ function co2!(EP::Model, inputs::Dict)
# when scale factor is on tCO2/MWh = > kt CO2/GWh
@expression(EP, ePlantCCO2Sequestration[y=1:G],
sum(inputs["omega"][t] * eEmissionsCaptureByPlant[y, t] *
ccs_disposal_cost_per_metric_ton(y) for t in 1:T))
ccs_disposal_cost_per_metric_ton(res[y]) for t in 1:T))

@expression(EP, eZonalCCO2Sequestration[z=1:Z],
sum(ePlantCCO2Sequestration[y] for y in resources_in_zone_by_rid(resources,z)))
sum(ePlantCCO2Sequestration[y] for y in resources_in_zone_by_rid(res,z)))

@expression(EP, eTotaleCCO2Sequestration,
sum(eZonalCCO2Sequestration[z] for z in 1:Z))
Expand All @@ -105,7 +99,7 @@ function co2!(EP::Model, inputs::Dict)

# emissions by zone
@expression(EP, eEmissionsByZone[z = 1:Z, t = 1:T],
sum(eEmissionsByPlant[y, t] for y in resources_in_zone_by_rid(resources,z)))
sum(eEmissionsByPlant[y, t] for y in resources_in_zone_by_rid(res,z)))
return EP

end
5 changes: 2 additions & 3 deletions src/model/core/discharge/discharge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ function discharge!(EP::Model, inputs::Dict, setup::Dict)

println("Discharge Module")

resources = inputs["RESOURCES"]
var_om_cost_per_mwh(y) = var_om_cost_per_mwh(resources[y])
res = inputs["RESOURCES"]

G = inputs["G"] # Number of resources (generators, storage, DR, and DERs)
T = inputs["T"] # Number of time steps
Expand All @@ -29,7 +28,7 @@ function discharge!(EP::Model, inputs::Dict, setup::Dict)
## Objective Function Expressions ##

# Variable costs of "generation" for resource "y" during hour "t" = variable O&M
@expression(EP, eCVar_out[y=1:G,t=1:T], (inputs["omega"][t]*(var_om_cost_per_mwh(y)*vP[y,t])))
@expression(EP, eCVar_out[y=1:G,t=1:T], (inputs["omega"][t]*(var_om_cost_per_mwh(res[y])*vP[y,t])))
# Sum individual resource contributions to variable discharging costs to get total variable discharging costs
@expression(EP, eTotalCVarOutT[t=1:T], sum(eCVar_out[y,t] for y in 1:G))
@expression(EP, eTotalCVarOut, sum(eTotalCVarOutT[t] for t in 1:T))
Expand Down
39 changes: 16 additions & 23 deletions src/model/core/discharge/investment_discharge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,7 @@ function investment_discharge!(EP::Model, inputs::Dict, setup::Dict)
println("Investment Discharge Module")
MultiStage = setup["MultiStage"]

resources = inputs["RESOURCES"]
cap_size(y) = cap_size(resources[y])
existing_capacity_mw(y) = existing_capacity_mw(resources[y])
max_capacity_mw(y) = max_capacity_mw(resources[y])
min_capacity_mw(y) = min_capacity_mw(resources[y])
inv_cost_per_mwyr(y) = inv_cost_per_mwyr(resources[y])
fixed_om_cost_per_mwyr(y) = fixed_om_cost_per_mwyr(resources[y])

res = inputs["RESOURCES"]

G = inputs["G"] # Number of resources (generators, storage, DR, and DERs)

Expand Down Expand Up @@ -87,27 +80,27 @@ function investment_discharge!(EP::Model, inputs::Dict, setup::Dict)
if MultiStage == 1
@expression(EP, eExistingCap[y in 1:G], vEXISTINGCAP[y])
else
@expression(EP, eExistingCap[y in 1:G], existing_capacity_mw(y))
@expression(EP, eExistingCap[y in 1:G], existing_capacity_mw(res[y]))
end

# Cap_Size is set to 1 for all variables when unit UCommit == 0
# When UCommit > 0, Cap_Size is set to 1 for all variables except those where THERM == 1
@expression(EP, eTotalCap[y in 1:G],
if y in intersect(NEW_CAP, RET_CAP) # Resources eligible for new capacity and retirements
if y in COMMIT
eExistingCap[y] + cap_size(y)*(EP[:vCAP][y] - EP[:vRETCAP][y])
eExistingCap[y] + cap_size(res[y])*(EP[:vCAP][y] - EP[:vRETCAP][y])
else
eExistingCap[y] + EP[:vCAP][y] - EP[:vRETCAP][y]
end
elseif y in setdiff(NEW_CAP, RET_CAP) # Resources eligible for only new capacity
if y in COMMIT
eExistingCap[y] + cap_size(y)*EP[:vCAP][y]
eExistingCap[y] + cap_size(res[y])*EP[:vCAP][y]
else
eExistingCap[y] + EP[:vCAP][y]
end
elseif y in setdiff(RET_CAP, NEW_CAP) # Resources eligible for only capacity retirements
if y in COMMIT
eExistingCap[y] - cap_size(y)*EP[:vRETCAP][y]
eExistingCap[y] - cap_size(res[y])*EP[:vRETCAP][y]
else
eExistingCap[y] - EP[:vRETCAP][y]
end
Expand All @@ -123,18 +116,18 @@ function investment_discharge!(EP::Model, inputs::Dict, setup::Dict)
@expression(EP, eCFix[y in 1:G],
if y in setdiff(NEW_CAP, RETRO) # Resources eligible for new capacity (Non-Retrofit)
if y in COMMIT
inv_cost_per_mwyr(y)*cap_size(y)*vCAP[y] + fixed_om_cost_per_mwyr(y)*eTotalCap[y]
inv_cost_per_mwyr(res[y])*cap_size(res[y])*vCAP[y] + fixed_om_cost_per_mwyr(res[y])*eTotalCap[y]
else
inv_cost_per_mwyr(y)*vCAP[y] + fixed_om_cost_per_mwyr(y)*eTotalCap[y]
inv_cost_per_mwyr(res[y])*vCAP[y] + fixed_om_cost_per_mwyr(res[y])*eTotalCap[y]
end
elseif y in intersect(NEW_CAP, RETRO) # Resources eligible for new capacity (Retrofit yr -> y)
if y in COMMIT
sum( RETRO_SOURCE_IDS[y][i] in RET_CAP ? RETRO_INV_CAP_COSTS[y][i]*cap_size(y)*vRETROFIT[RETRO_SOURCE_IDS[y][i],y]*RETRO_EFFICIENCY[y][i] : 0 for i in 1:NUM_RETRO_SOURCES[y]) + fixed_om_cost_per_mwyr(y)*eTotalCap[y]
sum( RETRO_SOURCE_IDS[y][i] in RET_CAP ? RETRO_INV_CAP_COSTS[y][i]*cap_size(res[y])*vRETROFIT[RETRO_SOURCE_IDS[y][i],y]*RETRO_EFFICIENCY[y][i] : 0 for i in 1:NUM_RETRO_SOURCES[y]) + fixed_om_cost_per_mwyr(res[y])*eTotalCap[y]
else
sum( RETRO_SOURCE_IDS[y][i] in RET_CAP ? RETRO_INV_CAP_COSTS[y][i]*vRETROFIT[RETRO_SOURCE_IDS[y][i],y]*RETRO_EFFICIENCY[y][i] : 0 for i in 1:NUM_RETRO_SOURCES[y]) + fixed_om_cost_per_mwyr(y)*eTotalCap[y]
sum( RETRO_SOURCE_IDS[y][i] in RET_CAP ? RETRO_INV_CAP_COSTS[y][i]*vRETROFIT[RETRO_SOURCE_IDS[y][i],y]*RETRO_EFFICIENCY[y][i] : 0 for i in 1:NUM_RETRO_SOURCES[y]) + fixed_om_cost_per_mwyr(res[y])*eTotalCap[y]
end
else
fixed_om_cost_per_mwyr(y)*eTotalCap[y]
fixed_om_cost_per_mwyr(res[y])*eTotalCap[y]
end
)

Expand All @@ -155,24 +148,24 @@ function investment_discharge!(EP::Model, inputs::Dict, setup::Dict)

if MultiStage == 1
# Existing capacity variable is equal to existing capacity specified in the input file
@constraint(EP, cExistingCap[y in 1:G], EP[:vEXISTINGCAP][y] == existing_capacity_mw(y))
@constraint(EP, cExistingCap[y in 1:G], EP[:vEXISTINGCAP][y] == existing_capacity_mw(res[y]))
end

## Constraints on retirements and capacity additions
# Cannot retire more capacity than existing capacity
@constraint(EP, cMaxRetNoCommit[y in setdiff(RET_CAP,COMMIT)], vRETCAP[y] <= eExistingCap[y])
@constraint(EP, cMaxRetCommit[y in intersect(RET_CAP,COMMIT)], cap_size(y)*vRETCAP[y] <= eExistingCap[y])
@constraint(EP, cMaxRetCommit[y in intersect(RET_CAP,COMMIT)], cap_size(res[y])*vRETCAP[y] <= eExistingCap[y])

## Constraints on new built capacity
# Constraint on maximum capacity (if applicable) [set input to -1 if no constraint on maximum capacity]
# DEV NOTE: This constraint may be violated in some cases where Existing_Cap_MW is >= Max_Cap_MW and lead to infeasabilty
MAX_CAP = has_positive_max_capacity_mw(resources)
@constraint(EP, cMaxCap[y in MAX_CAP], eTotalCap[y] <= max_capacity_mw(y))
MAX_CAP = has_positive_max_capacity_mw(res)
@constraint(EP, cMaxCap[y in MAX_CAP], eTotalCap[y] <= max_capacity_mw(res[y]))

# Constraint on minimum capacity (if applicable) [set input to -1 if no constraint on minimum capacity]
# DEV NOTE: This constraint may be violated in some cases where Existing_Cap_MW is <= Min_Cap_MW and lead to infeasabilty
MIN_CAP = has_positive_min_capacity_mw(resources)
@constraint(EP, cMinCap[y in MIN_CAP], eTotalCap[y] >= min_capacity_mw(y))
MIN_CAP = has_positive_min_capacity_mw(res)
@constraint(EP, cMinCap[y in MIN_CAP], eTotalCap[y] >= min_capacity_mw(res[y]))



Expand Down
Loading

0 comments on commit 561f8ce

Please sign in to comment.