Skip to content

Commit

Permalink
change load generator data.jl
Browse files Browse the repository at this point in the history
  • Loading branch information
yifueve committed Feb 12, 2024
1 parent 8cdc389 commit 26689d0
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 138 deletions.
239 changes: 102 additions & 137 deletions src/load_inputs/load_generators_data_old.jl → src/load_inputs/load_generators_data.jl
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ Function for reading input parameters related to electricity generators (plus st
function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Dict, fuel_costs::Dict, fuel_CO2::Dict)

filename = "Generators_data.csv"
gen_in = DataFrame(CSV.File(joinpath(path, filename), header=true), copycols=true)
gen_in = load_dataframe(joinpath(path, filename))

# Add Resource IDs after reading to prevent user errors
gen_in[!,:R_ID] = 1:length(collect(skipmissing(gen_in[!,1])))

# Store DataFrame of generators/resources input data for use in model
inputs_gen["dfGen"] = gen_in
# Store DataFrame of generators/resources input data for use in model
inputs_gen["dfGen"] = gen_in

# Number of resources
inputs_gen["G"] = length(collect(skipmissing(gen_in[!,:R_ID])))
# initial screen that resources are valid
resources = dataframerow_to_dict.(eachrow(gen_in))
validate_resources(resources)
inputs_gen["resources_d"] = resources

# Set indices for internal use
G = inputs_gen["G"] # Number of resources (generators, storage, DR, and DERs)
# Number of resources (generators, storage, DR, and DERs)
G = nrow(gen_in)
inputs_gen["G"] = G

scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1
# Add Resource IDs after reading to prevent user errors
gen_in[!,:R_ID] = 1:G

scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1
## Defining sets of generation and storage resources

# Set of storage resources with symmetric charge/discharge capacity
Expand Down Expand Up @@ -62,6 +66,7 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
if !("RETRO" in names(gen_in))
gen_in[!, "RETRO"] = zero(gen_in[!, "R_ID"])
end

inputs_gen["RETRO"] = gen_in[gen_in.RETRO.==1,:R_ID]
# Disable Retrofit while it's under development
# if !(isempty(inputs_gen["RETRO"]))
Expand All @@ -71,12 +76,10 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
# Set of thermal generator resources
if setup["UCommit"]>=1
# Set of thermal resources eligible for unit committment
# Yifu changed to include retrofit (change later!)
inputs_gen["THERM_COMMIT"] = union(gen_in[gen_in.THERM.==1,:R_ID])
inputs_gen["THERM_COMMIT"] = gen_in[gen_in.THERM.==1,:R_ID]
# Set of thermal resources not eligible for unit committment
# Yifu changed to include retrofit (change later!)
inputs_gen["THERM_NO_COMMIT"] = union(gen_in[gen_in.THERM.==2,:R_ID])
else # When UCommit == 0, no thermal resources are eligible for unit committment (change later!)
inputs_gen["THERM_NO_COMMIT"] = gen_in[gen_in.THERM.==2,:R_ID]
else # When UCommit == 0, no thermal resources are eligible for unit committment
inputs_gen["THERM_COMMIT"] = Int64[]
inputs_gen["THERM_NO_COMMIT"] = union(gen_in[gen_in.THERM.==1,:R_ID], gen_in[gen_in.THERM.==2,:R_ID])
end
Expand All @@ -101,31 +104,45 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
end

# Set of all resources eligible for new capacity
inputs_gen["NEW_CAP"] = intersect(gen_in[gen_in.New_Build.>=1,:R_ID], gen_in[gen_in.Max_Cap_MW.!=0,:R_ID])
inputs_gen["NEW_CAP"] = intersect(buildable, gen_in[gen_in.Max_Cap_MW.!=0,:R_ID])
# Set of all resources eligible for capacity retirements
inputs_gen["RET_CAP"] = intersect(gen_in[gen_in.New_Build.!=-1,:R_ID], gen_in[gen_in.Existing_Cap_MW.>=0,:R_ID])
# Set of all resources eligible for capacity retrofitting (by Yifu, same with retirement)
inputs_gen["RETRO_CAP"] = intersect(gen_in[gen_in.New_Build.>=2,:R_ID], gen_in[gen_in.Existing_Cap_MW.>=0,:R_ID])

# Set of all storage resources eligible for new energy capacity
inputs_gen["NEW_CAP_ENERGY"] = intersect(gen_in[gen_in.New_Build.==1,:R_ID], gen_in[gen_in.Max_Cap_MWh.!=0,:R_ID], inputs_gen["STOR_ALL"])
# Set of all storage resources eligible for energy capacity retirements
inputs_gen["RET_CAP_ENERGY"] = intersect(gen_in[gen_in.New_Build.!=-1,:R_ID], gen_in[gen_in.Existing_Cap_MWh.>=0,:R_ID], inputs_gen["STOR_ALL"])
# Set of all storage resources for created energy capacity retrofit (Yifu)
inputs_gen["RETRO_CAP_ENERGY"] = intersect(gen_in[gen_in.New_Build.==-1,:R_ID], inputs_gen["RETRO"], inputs_gen["STOR_ALL"])

# Set of asymmetric charge/discharge storage resources eligible for new charge capacity
inputs_gen["NEW_CAP_CHARGE"] = intersect(gen_in[gen_in.New_Build.==1,:R_ID], gen_in[gen_in.Max_Charge_Cap_MW.!=0,:R_ID], inputs_gen["STOR_ASYMMETRIC"])
# Set of asymmetric charge/discharge storage resources eligible for charge capacity retirements
inputs_gen["RET_CAP_CHARGE"] = intersect(gen_in[gen_in.New_Build.!=-1,:R_ID], gen_in[gen_in.Existing_Charge_Cap_MW.>=0,:R_ID], inputs_gen["STOR_ASYMMETRIC"])
# Set of all storage resources for created energy capacity retrofit (Yifu)
inputs_gen["RETRO_CAP_CHARGE"] = intersect(gen_in[gen_in.New_Build.==-1,:R_ID], inputs_gen["RETRO"], inputs_gen["STOR_ASYMMETRIC"])
inputs_gen["RET_CAP"] = intersect(retirable, gen_in[gen_in.Existing_Cap_MW.>=0,:R_ID])
# Set of all resources eligible for capacity retrofitting (by Yifu, same with retirement)
inputs_gen["RETRO_CAP"] = intersect(gen_in[gen_in.New_Build.>=2,:R_ID], gen_in[gen_in.Existing_Cap_MW.>=0,:R_ID])

new_cap_energy = Set{Int64}()
ret_cap_energy = Set{Int64}()
retro_cap_energy = Set{Int64}()
if !isempty(inputs_gen["STOR_ALL"])
# Set of all storage resources eligible for new energy capacity
new_cap_energy = intersect(buildable, gen_in[gen_in.Max_Cap_MWh.!=0,:R_ID], inputs_gen["STOR_ALL"])
# Set of all storage resources eligible for energy capacity retirements
ret_cap_energy = intersect(retirable, gen_in[gen_in.Existing_Cap_MWh.>=0,:R_ID], inputs_gen["STOR_ALL"])
retro_cap_energy = intersect(gen_in[gen_in.New_Build.==-1,:R_ID], inputs_gen["RETRO"], inputs_gen["STOR_ALL"])
end
inputs_gen["NEW_CAP_ENERGY"] = new_cap_energy
inputs_gen["RET_CAP_ENERGY"] = ret_cap_energy
inputs_gen["RETRO_CAP_ENERGY"] = retro_cap_energy

new_cap_charge = Set{Int64}()
ret_cap_charge = Set{Int64}()
retro_cap_charge = Set{Int64}()
if !isempty(inputs_gen["STOR_ASYMMETRIC"])
# Set of asymmetric charge/discharge storage resources eligible for new charge capacity
new_cap_charge = intersect(buildable, gen_in[gen_in.Max_Charge_Cap_MW.!=0,:R_ID], inputs_gen["STOR_ASYMMETRIC"])
# Set of asymmetric charge/discharge storage resources eligible for charge capacity retirements
ret_cap_charge = intersect(buildable, gen_in[gen_in.Existing_Charge_Cap_MW.>=0,:R_ID], inputs_gen["STOR_ASYMMETRIC"])
# Set of all storage resources (power capacity component) CREATED AFTER RETROFITTING (Yifu)
retro_cap_charge = intersect(gen_in[gen_in.New_Build.==-1,:R_ID], inputs_gen["RETRO"], inputs_gen["STOR_ASYMMETRIC"])
end
inputs_gen["NEW_CAP_CHARGE"] = new_cap_charge
inputs_gen["RET_CAP_CHARGE"] = ret_cap_charge
inputs_gen["RETRO_CAP_CHARGE"] = retro_cap_charge

# Names of resources
inputs_gen["RESOURCES"] = collect(skipmissing(gen_in[!,:Resource][1:inputs_gen["G"]]))
inputs_gen["RESOURCE_TYPE"] = collect(skipmissing(gen_in[!,:Resource_Type][1:inputs_gen["G"]]))
inputs_gen["RESOURCES"] = gen_in[!,:Resource]
# Zones resources are located in
zones = collect(skipmissing(gen_in[!,:Zone][1:inputs_gen["G"]]))
zones = gen_in[!,:Zone]
# Resource identifiers by zone (just zones in resource order + resource and zone concatenated)
inputs_gen["R_ZONES"] = zones
inputs_gen["RESOURCE_ZONES"] = inputs_gen["RESOURCES"] .* "_z" .* string.(zones)
Expand All @@ -135,114 +152,62 @@ function load_generators_data!(setup::Dict, path::AbstractString, inputs_gen::Di
# Resource identifiers by clsuter (just zones in resource order + resource and zone concatenated)
inputs_gen["R_CLUSTERING"] = clusters

if setup["ParameterScale"] == 1 # Parameter scaling turned on - adjust values of subset of parameter values

# The existing capacity of a power plant in megawatts
inputs_gen["dfGen"][!,:Existing_Charge_Cap_MW] = gen_in[!,:Existing_Charge_Cap_MW]/ModelScalingFactor # Convert to GW
# The existing capacity of storage in megawatt-hours STOR = 1 or STOR = 2
inputs_gen["dfGen"][!,:Existing_Cap_MWh] = gen_in[!,:Existing_Cap_MWh]/ModelScalingFactor # Convert to GWh
# The existing charging capacity for resources where STOR = 2
inputs_gen["dfGen"][!,:Existing_Cap_MW] = gen_in[!,:Existing_Cap_MW]/ModelScalingFactor # Convert to GW

# Cap_Size scales only capacities for those technologies with capacity >1
# Step 1: convert vector to float
inputs_gen["dfGen"][!,:Cap_Size] =convert(Array{Float64}, gen_in[!,:Cap_Size])
inputs_gen["dfGen"][!,:Cap_Size] ./= ModelScalingFactor

# Min capacity terms
# Limit on minimum discharge capacity of the resource. -1 if no limit on minimum capacity
inputs_gen["dfGen"][!,:Min_Cap_MW] = gen_in[!,:Min_Cap_MW]/ModelScalingFactor # Convert to GW
# Limit on minimum energy capacity of the resource. -1 if no limit on minimum capacity
inputs_gen["dfGen"][!,:Min_Cap_MWh] = gen_in[!,:Min_Cap_MWh]/ModelScalingFactor # Convert to GWh
# Limit on minimum charge capacity of the resource. -1 if no limit on minimum capacity
inputs_gen["dfGen"][!,:Min_Charge_Cap_MW] = gen_in[!,:Min_Charge_Cap_MW]/ModelScalingFactor # Convert to GWh

## Max capacity terms
# Limit on maximum discharge capacity of the resource. -1 if no limit on maximum capacity
inputs_gen["dfGen"][!,:Max_Cap_MW] = gen_in[!,:Max_Cap_MW]/ModelScalingFactor # Convert to GW
# Limit on maximum energy capacity of the resource. -1 if no limit on maximum capacity
inputs_gen["dfGen"][!,:Max_Cap_MWh] = gen_in[!,:Max_Cap_MWh]/ModelScalingFactor # Convert to GWh
# Limit on maximum charge capacity of the resource. -1 if no limit on maximum capacity
inputs_gen["dfGen"][!,:Max_Charge_Cap_MW] = gen_in[!,:Max_Charge_Cap_MW]/ModelScalingFactor # Convert to GW

## Investment cost terms
# Annualized capacity investment cost of a generation technology
inputs_gen["dfGen"][!,:Inv_Cost_per_MWyr] = gen_in[!,:Inv_Cost_per_MWyr]/ModelScalingFactor # Convert to $ million/GW/yr with objective function in millions
# Annualized investment cost of the energy capacity for a storage technology with STOR = 1 or STOR = 2
inputs_gen["dfGen"][!,:Inv_Cost_per_MWhyr] = gen_in[!,:Inv_Cost_per_MWhyr]/ModelScalingFactor # Convert to $ million/GWh/yr with objective function in millions
# Annualized capacity investment cost for the charging portion of a storage technology with STOR = 2
inputs_gen["dfGen"][!,:Inv_Cost_Charge_per_MWyr] = gen_in[!,:Inv_Cost_Charge_per_MWyr]/ModelScalingFactor # Convert to $ million/GWh/yr with objective function in millions

## Fixed O&M cost terms
# Fixed operations and maintenance cost of a generation or storage technology
inputs_gen["dfGen"][!,:Fixed_OM_Cost_per_MWyr] = gen_in[!,:Fixed_OM_Cost_per_MWyr]/ModelScalingFactor # Convert to $ million/GW/yr with objective function in millions
# Fixed operations and maintenance cost of the power aspect of a storage technology of type STOR = 1 or STOR = 2
inputs_gen["dfGen"][!,:Fixed_OM_Cost_per_MWhyr] = gen_in[!,:Fixed_OM_Cost_per_MWhyr]/ModelScalingFactor # Convert to $ million/GW/yr with objective function in millions
# Fixed operations and maintenance cost of the charging aspect of a storage technology of type STOR = 2
inputs_gen["dfGen"][!,:Fixed_OM_Cost_Charge_per_MWyr] = gen_in[!,:Fixed_OM_Cost_Charge_per_MWyr]/ModelScalingFactor # Convert to $ million/GW/yr with objective function in millions

## Variable O&M cost terms
# Variable operations and maintenance cost of a generation or storage technology
inputs_gen["dfGen"][!,:Var_OM_Cost_per_MWh] = gen_in[!,:Var_OM_Cost_per_MWh]/ModelScalingFactor # Convert to $ million/GWh with objective function in millions
# Variable operations and maintenance cost of the charging aspect of a storage technology with STOR = 2,
# or variable operations and maintenance costs associated with flexible demand with FLEX = 1
inputs_gen["dfGen"][!,:Var_OM_Cost_per_MWh_In] = gen_in[!,:Var_OM_Cost_per_MWh_In]/ModelScalingFactor # Convert to $ million/GWh with objective function in millions
# Cost of providing regulation reserves
inputs_gen["dfGen"][!,:Reg_Cost] = gen_in[!,:Reg_Cost]/ModelScalingFactor # Convert to $ million/GW with objective function in millions
# Cost of providing spinning reserves
inputs_gen["dfGen"][!,:Rsv_Cost] = gen_in[!,:Rsv_Cost]/ModelScalingFactor # Convert to $ million/GW with objective function in millions

if setup["MultiStage"] == 1
inputs_gen["dfGen"][!,:Min_Retired_Cap_MW] = gen_in[!,:Min_Retired_Cap_MW]/ModelScalingFactor
inputs_gen["dfGen"][!,:Min_Retired_Charge_Cap_MW] = gen_in[!,:Min_Retired_Charge_Cap_MW]/ModelScalingFactor
inputs_gen["dfGen"][!,:Min_Retired_Energy_Cap_MW] = gen_in[!,:Min_Retired_Energy_Cap_MW]/ModelScalingFactor
end
end
# See documentation for descriptions of each column
# Generally, these scalings converts energy and power units from MW to GW
# and $/MW to $M/GW. Both are done by dividing the values by 1000.
columns_to_scale = [:Existing_Charge_Cap_MW, # to GW
:Existing_Cap_MWh, # to GWh
:Existing_Cap_MW, # to GW

# Dharik - Done, we have scaled fuel costs above so any parameters on per MMBtu do not need to be scaled
if setup["UCommit"]>=1
if setup["ParameterScale"] ==1 # Parameter scaling turned on - adjust values of subset of parameter values
# Cost per MW of nameplate capacity to start a generator
inputs_gen["dfGen"][!,:Start_Cost_per_MW] = gen_in[!,:Start_Cost_per_MW]/ModelScalingFactor # Convert to $ million/GW with objective function in millions
end
:Cap_Size, # to GW

:Min_Cap_MW, # to GW
:Min_Cap_MWh, # to GWh
:Min_Charge_Cap_MW, # to GWh

:Max_Cap_MW, # to GW
:Max_Cap_MWh, # to GWh
:Max_Charge_Cap_MW, # to GW

# Fuel consumed on start-up (million BTUs per MW per start) if unit commitment is modelled
start_fuel = convert(Array{Float64}, collect(skipmissing(gen_in[!,:Start_Fuel_MMBTU_per_MW])))
# Fixed cost per start-up ($ per MW per start) if unit commitment is modelled
start_cost = convert(Array{Float64}, collect(skipmissing(inputs_gen["dfGen"][!,:Start_Cost_per_MW])))
:Inv_Cost_per_MWyr, # to $M/GW/yr
:Inv_Cost_per_MWhyr, # to $M/GWh/yr
:Inv_Cost_Charge_per_MWyr, # to $M/GW/yr

:Fixed_OM_Cost_per_MWyr, # to $M/GW/yr
:Fixed_OM_Cost_per_MWhyr, # to $M/GWh/yr
:Fixed_OM_Cost_Charge_per_MWyr, # to $M/GW/yr

:Var_OM_Cost_per_MWh, # to $M/GWh
:Var_OM_Cost_per_MWh_In, # to $M/GWh

:Reg_Cost, # to $M/GW
:Rsv_Cost, # to $M/GW

:Min_Retired_Cap_MW, # to GW
:Min_Retired_Charge_Cap_MW, # to GW
:Min_Retired_Energy_Cap_MW, # to GW

:Start_Cost_per_MW, # to $M/GW

:Hydrogen_MWh_Per_Tonne, # to GWh/t
]

for column in columns_to_scale
if string(column) in names(gen_in)
gen_in[!, column] /= scale_factor
end
end

if setup["UCommit"] >= 1
start_cost = convert(Array{Float64}, gen_in[!,:Start_Cost_per_MW])
inputs_gen["C_Start"] = zeros(Float64, G, inputs_gen["T"])
inputs_gen["dfGen"][!,:CO2_per_Start] = zeros(Float64, G)
end

# Heat rate of all resources (million BTUs/MWh)
heat_rate = convert(Array{Float64}, collect(skipmissing(gen_in[!,:Heat_Rate_MMBTU_per_MWh])) )
# Fuel used by each resource
fuel_type = collect(skipmissing(gen_in[!,:Fuel]))
# Maximum fuel cost in $ per MWh and CO2 emissions in tons per MWh
inputs_gen["C_Fuel_per_MWh"] = zeros(Float64, G, inputs_gen["T"])
inputs_gen["dfGen"][!,:CO2_per_MWh] = zeros(Float64, G)
# scale the start costs
for g in 1:G
# NOTE: When Setup[ParameterScale] =1, fuel costs are scaled in fuels_data.csv, so no if condition needed to scale C_Fuel_per_MWh
inputs_gen["C_Fuel_per_MWh"][g,:] = fuel_costs[fuel_type[g]].*heat_rate[g]
inputs_gen["dfGen"][g,:CO2_per_MWh] = fuel_CO2[fuel_type[g]]*heat_rate[g]
if setup["ParameterScale"] ==1
inputs_gen["dfGen"][g,:CO2_per_MWh] = inputs_gen["dfGen"][g,:CO2_per_MWh] * ModelScalingFactor
end
# kton/MMBTU * MMBTU/MWh = kton/MWh, to get kton/GWh, we need to mutiply 1000
if g in inputs_gen["COMMIT"]
# Start-up cost is sum of fixed cost per start plus cost of fuel consumed on startup.
# CO2 from fuel consumption during startup also calculated

inputs_gen["C_Start"][g,:] = inputs_gen["dfGen"][g,:Cap_Size] * (fuel_costs[fuel_type[g]] .* start_fuel[g] .+ start_cost[g])
# No need to re-scale C_Start since Cap_size, fuel_costs and start_cost are scaled When Setup[ParameterScale] =1 - Dharik
inputs_gen["dfGen"][g,:CO2_per_Start] = inputs_gen["dfGen"][g,:Cap_Size]*(fuel_CO2[fuel_type[g]]*start_fuel[g])
if setup["ParameterScale"] ==1
inputs_gen["dfGen"][g,:CO2_per_Start] = inputs_gen["dfGen"][g,:CO2_per_Start] * ModelScalingFactor
end
# Setup[ParameterScale] =1, inputs_gen["dfGen"][g,:Cap_Size] is GW, fuel_CO2[fuel_type[g]] is ktons/MMBTU, start_fuel is MMBTU/MW,
# thus the overall is MTons/GW, and thus inputs_gen["dfGen"][g,:CO2_per_Start] is Mton, to get kton, change we need to multiply 1000
# Setup[ParameterScale] =0, inputs_gen["dfGen"][g,:Cap_Size] is MW, fuel_CO2[fuel_type[g]] is tons/MMBTU, start_fuel is MMBTU/MW,
# thus the overall is MTons/GW, and thus inputs_gen["dfGen"][g,:CO2_per_Start] is ton
# Start-up cost is sum of fixed cost per start startup.
inputs_gen["C_Start"][g,:] .= gen_in[g,:Cap_Size] * ( start_cost[g])
end
end

Expand Down
1 change: 0 additions & 1 deletion src/write_outputs/write_capacity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ function write_capacity(path::AbstractString, inputs::Dict, setup::Dict, EP::Mod
end
dfCap = DataFrame(
Resource = inputs["RESOURCES"],
Resource_type = inputs["RESOURCE_TYPE"],
Zone = dfGen[!,:Zone],
Cluster = dfGen[!,:cluster],
StartCap = MultiStage == 1 ? value.(EP[:vEXISTINGCAP]) : dfGen[!,:Existing_Cap_MW],
Expand Down

0 comments on commit 26689d0

Please sign in to comment.