diff --git a/src/load_inputs/load_generators_data.jl b/src/load_inputs/load_generators_data.jl index 9601b5334b..1e927aee40 100755 --- a/src/load_inputs/load_generators_data.jl +++ b/src/load_inputs/load_generators_data.jl @@ -26,7 +26,7 @@ function check_thermal_storage_validity(df::DataFrame) c = ts .& .!is_nonzero(df, :THERM) if any(c) - e = string("Generators ", r_id[c], ", marked as TS, do not also have THERM=1") + e = string("Generators ", r_id[c], ", marked as TS, do not also have THERM != 0") push!(error_strings, e) end @@ -335,7 +335,8 @@ function load_thermal_storage_data!(setup::Dict, path::AbstractString, inputs_ge :Max_Cap_MW_th, :Fixed_Cost_per_MW_th, :Var_OM_Cost_per_MWh_th, - :Fixed_Cost_per_MWh_th] + :Fixed_Cost_per_MWh_th, + :Fixed_Cost_per_MW_RH] ts_in[!, columns_to_scale] ./= ModelScalingFactor end inputs_gen["dfTS"] = ts_in diff --git a/src/model/resources/thermal_storage/thermal_storage.jl b/src/model/resources/thermal_storage/thermal_storage.jl index 7a101b3fc5..d5ef26dfdb 100644 --- a/src/model/resources/thermal_storage/thermal_storage.jl +++ b/src/model/resources/thermal_storage/thermal_storage.jl @@ -100,18 +100,15 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) T = inputs["T"] # Number of time steps (hours) Z = inputs["Z"] # Number of zones - START_SUBPERIODS = inputs["START_SUBPERIODS"] - INTERIOR_SUBPERIODS = inputs["INTERIOR_SUBPERIODS"] - hours_per_subperiod = inputs["hours_per_subperiod"] + p = inputs["hours_per_subperiod"] # Load thermal storage inputs TS = inputs["TS"] dfTS = inputs["dfTS"] RH = get_resistive_heating(inputs) - by_rid(rid, sym) = by_rid_df(rid, sym, dfTS) - load_fuel_data!(inputs, setup) + load_thermal_storage_fuel_data!(inputs, setup) @variables(EP, begin # Thermal core variables @@ -129,11 +126,12 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) vRHCAP[y in RH] >= 0 #RH power capacity for resource end) + ### THERMAL CORE CONSTRAINTS ### # Core power output must be <= installed capacity, including hourly capacity factors @constraint(EP, cCPMax[y in TS, t=1:T], vCP[y,t] <= vCCAP[y]*inputs["pP_Max"][y,t]) # Total installed capacity is less than specified maximum limit - those_with_max_cap = dfTS[dfTS.Max_Cap_MW_th.>0, :R_ID] + those_with_max_cap = dfTS[dfTS.Max_Cap_MW_th.>=0, :R_ID] @constraint(EP, cCCAPMax[y in those_with_max_cap], vCCAP[y] <= by_rid(y, :Max_Cap_MW_th)) # Variable cost of core operation @@ -161,21 +159,21 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) # (previous SOC) - (discharge to turbines) - (turbine startup energy use) + (core power output) - (self discharge) # first for resources with no RH - @constraint(EP, cTSocBalInterior[t in INTERIOR_SUBPERIODS, y in setdiff(TS, RH)], ( - vTS[y,t] == vTS[y,t-1] + @constraint(EP, cTSocBal[y in setdiff(TS, RH), t = 1:T], ( + vTS[y,t] == vTS[y, hoursbefore(p, t, 1)] - (1 / dfGen[y, :Eff_Down] * EP[:vP][y,t]) - (1 / dfGen[y, :Eff_Down] * dfGen[y, :Start_Fuel_MMBTU_per_MW] * dfGen[y,:Cap_Size] * EP[:vSTART][y,t]) + (dfGen[y,:Eff_Up] * vCP[y,t]) - - (dfGen[y,:Self_Disch] * vTS[y,t-1])) + - (dfGen[y,:Self_Disch] * vTS[y, hoursbefore(p, t, 1)])) ) # then for resources with RH - @constraint(EP, cTSocBalInteriorRH[t in INTERIOR_SUBPERIODS, y in intersect(TS, RH)], ( - vTS[y,t] == vTS[y,t-1] + @constraint(EP, cTSocBalRH[y in intersect(TS, RH), t = 1:T], ( + vTS[y,t] == vTS[y, hoursbefore(p, t, 1)] - (1 / dfGen[y, :Eff_Down] * EP[:vP][y,t]) - (1 / dfGen[y, :Eff_Down] * dfGen[y, :Start_Fuel_MMBTU_per_MW] * dfGen[y,:Cap_Size] * EP[:vSTART][y,t]) + (dfGen[y,:Eff_Up] * vCP[y,t]) - - (dfGen[y,:Self_Disch] * vTS[y,t-1]) + - (dfGen[y,:Self_Disch] * vTS[y, hoursbefore(p, t, 1)]) + vRH[y, t]) #100% resistive heating efficiency ) @@ -184,17 +182,15 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) -1 * sum(vRH[y, t] for y in intersect(RH, dfGen[dfGen[!, :Zone].==z, :R_ID]))) EP[:ePowerBalance] += ePowerBalanceRH + # add capacity constraint for RH + @constraint(EP, cRHMax[t = 1:T, y in RH], + vRH[y, t] <= vRHCAP[y] + ) + # TODO: perhaps avoid recomputing these; instead use sets TS_LONG_DURATION, etc TS_and_LDS, TS_and_nonLDS = split_LDS_and_nonLDS(dfGen, inputs, setup) - @constraint(EP, cTSoCBalStart[t in START_SUBPERIODS, y in TS_and_nonLDS],( - vTS[y,t] == vTS[y, t + hours_per_subperiod - 1] - - (1 / dfGen[y, :Eff_Down] * EP[:vP][y,t]) - - (1 / dfGen[y, :Eff_Down] * dfGen[y, :Start_Fuel_MMBTU_per_MW] * dfGen[y, :Cap_Size] * EP[:vSTART][y,t]) - + (dfGen[y, :Eff_Up] * vCP[y,t]) - (dfGen[y, :Self_Disch] * vTS[y,t + hours_per_subperiod - 1]) - )) - if !isempty(TS_and_LDS) REP_PERIOD = inputs["REP_PERIOD"] # Number of representative periods @@ -278,7 +274,7 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) # use thermal core constraints for thermal cores not tagged 'FUS' if !isempty(NONFUS) thermal_core_constraints!(EP, inputs, setup) - thermal_core_max_cap_constraint!(EP, inputs, setup) + thermal_core_max_cap_constraint!(EP, inputs) end # Capacity Reserves Margin policy @@ -303,55 +299,53 @@ function thermal_storage(EP::Model, inputs::Dict, setup::Dict) return EP end -function load_fuel_data!(inputs::Dict, setup::Dict) +function load_thermal_storage_fuel_data!(inputs::Dict, setup::Dict) dfTS = inputs["dfTS"] TS = inputs["TS"] NONFUS = get_nonfus(inputs) - N = nrow(dfTS) + TSG = nrow(dfTS) + THERM_COMMIT = inputs["THERM_COMMIT"] + scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 # for unit commitment decisions if setup["UCommit"]>=1 # Convert to $ million/GW with objective function in millions - if setup["ParameterScale"] ==1 - dfTS[!,:Start_Cost_per_MW] = dfTS[!,:Start_Cost_per_MW]/ModelScalingFactor - end + dfTS[!,:Start_Cost_per_MW] /= scale_factor + # Fuel consumed on start-up (million BTUs per MW per start) if unit commitment is modelled - start_fuel = convert(Array{Float64}, collect(skipmissing(dfTS[!,:Start_Fuel_MMBTU_per_MW]))) + start_fuel = convert(Array{Float64}, dfTS[!,: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(dfTS[!,:Start_Cost_per_MW]))) + start_cost = convert(Array{Float64}, dfTS[!,:Start_Cost_per_MW]) inputs["TS_C_Start"] = Dict() - dfTS[!,:CO2_per_Start] = zeros(Float64, N) + dfTS[!,:CO2_per_Start] = zeros(Float64, TSG) end # Heat rate of all resources (million BTUs/MWh) - heat_rate = convert(Array{Float64}, collect(skipmissing(dfTS[!,:Heat_Rate_MMBTU_per_MWh]))) + heat_rate = convert(Array{Float64}, dfTS[!,:Heat_Rate_MMBTU_per_MWh]) # Fuel used by each resource - fuel_type = collect(skipmissing(dfTS[!,:Fuel])) - # Maximum fuel cost in $ per MWh and CO2 emissions in tons per MWh + fuel_type = dfTS[!,:Fuel] + # fuel cost in $ per MWh and CO2 emissions in tons per MWh inputs["TS_C_Fuel_per_MWh"] = Dict() - dfTS[!,:CO2_per_MWh] = zeros(Float64,N) + dfTS[!,:CO2_per_MWh] = zeros(Float64, TSG) - for g in 1:N + for gen_id in 1:TSG #calculate fuel costs - inputs["TS_C_Fuel_per_MWh"][dfTS[g, :R_ID]] = inputs["fuel_costs"][fuel_type[g]] .* heat_rate[g] + inputs["TS_C_Fuel_per_MWh"][dfTS[gen_id, :R_ID]] = inputs["fuel_costs"][fuel_type[gen_id]] .* heat_rate[gen_id] #calculate fuel emissions - dfTS[g, :CO2_per_MWh] = inputs["fuel_CO2"][fuel_type[g]] .* heat_rate[g] - if setup["ParameterScale"] ==1 - dfTS[g,:CO2_per_MWh] = dfTS[g,:CO2_per_MWh] * ModelScalingFactor - end + dfTS[gen_id, :CO2_per_MWh] = inputs["fuel_CO2"][fuel_type[gen_id]] .* heat_rate[gen_id] + dfTS[gen_id,:CO2_per_MWh] *= scale_factor + # add start up costs and emissions for committed thermal cores. - if dfTS[g, :R_ID] in intersect(inputs["THERM_COMMIT"]) - inputs["TS_C_Start"][dfTS[g, :R_ID]] = by_rid_df(g, :Cap_Size, dfTS) .* (inputs["fuel_costs"][fuel_type[g]] .* start_fuel[g] .+ start_cost[g]) + if dfTS[gen_id, :R_ID] in THERM_COMMIT + inputs["TS_C_Start"][dfTS[gen_id, :R_ID]] = by_rid_df(gen_id, :Cap_Size, dfTS) .* (inputs["fuel_costs"][fuel_type[gen_id]] .* start_fuel[gen_id] .+ start_cost[gen_id]) - dfTS[g, :CO2_per_Start] = by_rid_df(g, :Cap_Size, dfTS) * (inputs["fuel_CO2"][fuel_type[g]] * start_fuel[g]) + dfTS[gen_id, :CO2_per_Start] = by_rid_df(gen_id, :Cap_Size, dfTS) * (inputs["fuel_CO2"][fuel_type[gen_id]] * start_fuel[gen_id]) #scale appropriately - if setup["ParameterScale"] == 1 - dfTS[g, :CO2_per_Start] = dfTS[g, :CO2_per_Start] * ModelScalingFactor - end + dfTS[gen_id, :CO2_per_Start] *= scale_factor end end end @@ -362,6 +356,7 @@ function fusion_max_cap_constraint!(EP::Model, inputs::Dict, setup::Dict) dfGen = inputs["dfGen"] G = inputs["G"] # Number of resources (generators, storage, DR, and DERs) + TS = inputs["TS"] dfTS = inputs["dfTS"] by_rid(rid, sym) = by_rid_df(rid, sym, dfTS) @@ -369,7 +364,8 @@ function fusion_max_cap_constraint!(EP::Model, inputs::Dict, setup::Dict) FUS = get_fus(inputs) #System-wide installed capacity is less than a specified maximum limit - has_max_up = dfTS[by_rid(FUS, :Max_Up) .> 0, :R_ID] + has_max_up = dfTS[dfTS.Max_Up .>= 0, :R_ID] + has_max_up = intersect(has_max_up, FUS) active_frac = ones(G) avg_start_power = zeros(G) @@ -391,16 +387,18 @@ function fusion_max_cap_constraint!(EP::Model, inputs::Dict, setup::Dict) end # set maximum power capacity constraint for the thermal core (in MWth) -function thermal_core_max_cap_constraint!(EP::Model, inputs::Dict, setup::Dict) +function thermal_core_max_cap_constraint!(EP::Model, inputs::Dict) dfTS = inputs["dfTS"] + TS = inputs["TS"] by_rid(rid, sym) = by_rid_df(rid, sym, dfTS) NONFUS = get_nonfus(inputs) #System-wide installed capacity is less than a specified maximum limit - HAS_MAX_LIMIT = dfTS[by_rid(NONFUS, :Max_Core_Power_Capacity) .> 0, :R_ID] - @constraint(EP, cCoreMaxCapacity[y in HAS_MAX_LIMIT], EP[:vCCAP][y] <= by_rid(y, :Max_Core_Power_Capacity) / by_rid(y, :Eff_Down)) + HAS_MAX_LIMIT = dfTS[dfTS.Max_Core_Power_Capacity_MWe .>= 0, :R_ID] + HAS_MAX_LIMIT = intersect(HAS_MAX_LIMIT, NONFUS) + @constraint(EP, cCoreMaxCapacity[y in HAS_MAX_LIMIT], EP[:vCCAP][y] <= by_rid(y, :Max_Core_Power_Capacity_MWe) / by_rid(y, :Eff_Down)) end @@ -535,15 +533,13 @@ function thermal_core_constraints!(EP::Model, inputs::Dict, setup::Dict) by_rid(rid, sym) = by_rid_df(rid, sym, dfTS) T = inputs["T"] # Number of time steps (hours) - Z = inputs["Z"] # Number of zones G = inputs["G"] # Number of resources NONFUS = get_nonfus(inputs) # non fusion thermal cores + THERM_COMMIT = inputs["THERM_COMMIT"] - hours_per_subperiod = inputs["hours_per_subperiod"] #total number of hours per subperiod - START_SUBPERIODS = inputs["START_SUBPERIODS"] - INTERIOR_SUBPERIODS = inputs["INTERIOR_SUBPERIODS"] + p = inputs["hours_per_subperiod"] #total number of hours per subperiod - COMMIT = intersect(inputs["THERM_COMMIT"], NONFUS) + COMMIT = intersect(THERM_COMMIT, NONFUS) NON_COMMIT = intersect(inputs["THERM_NO_COMMIT"], NONFUS) # constraints for generators not subject to UC @@ -552,20 +548,14 @@ function thermal_core_constraints!(EP::Model, inputs::Dict, setup::Dict) # ramp up and ramp down rates @constraints(EP, begin - # ramp up, start - [y in NON_COMMIT, t in START_SUBPERIODS], EP[:vCP][y, t] - EP[:vCP][y, t+hours_per_subperiod-1] <= by_rid(y, :Ramp_Up_Percentage) * EP[:vCCAP][y] - - # ramp up, interior - [y in NON_COMMIT, t in INTERIOR_SUBPERIODS], EP[:vCP][y, t] - EP[:vCP][y, t-1] <= by_rid(y, :Ramp_Up_Percentage) * EP[:vCCAP][y] + # ramp up + [y in NON_COMMIT, t in T], EP[:vCP][y, t] - EP[:vCP][y, hoursbefore(p, t, 1)] <= by_rid(y, :Ramp_Up_Percentage) * EP[:vCCAP][y] - # ramp dn, start - [y in NON_COMMIT, t in START_SUBPERIODS], EP[:vCP][y,t+hours_per_subperiod-1] - EP[:vCP][y,t] <= by_rid(y, :Ramp_Dn_Percentage) * EP[:vCCAP][y] - - # ramp dn, interior - [y in NON_COMMIT, t in INTERIOR_SUBPERIODS], EP[:vCP][y,t-1] - EP[:vCP][y,t] <= by_rid(y, :Ramp_Dn_Percentage) * EP[:vCCAP][y] + # ramp dn + [y in NON_COMMIT, t in T], EP[:vCP][y, hoursbefore(p, t, 1)] - EP[:vCP][y,t] <= by_rid(y, :Ramp_Dn_Percentage) * EP[:vCCAP][y] end) - # minimum stable power (assumes capacity factor of 1, so max power already implemented) + # minimum stable power @constraint(EP, [y in NON_COMMIT, t=1:T], EP[:vCP][y,t] >= by_rid(y, :Min_Power)* EP[:vCCAP][y]) end @@ -585,7 +575,7 @@ function thermal_core_constraints!(EP::Model, inputs::Dict, setup::Dict) @expression(EP, eTotalCStartTST[t=1:T], sum(eCStartTS[y,t] for y in COMMIT)) @expression(EP, eTotalCStartTS, sum(eTotalCStartTST[t] for t=1:T)) EP[:eObj] += eTotalCStartTS - + ## Declaration of integer/binary variables if setup["UCommit"] == 1 # Integer UC constraints for y in COMMIT @@ -605,48 +595,37 @@ function thermal_core_constraints!(EP::Model, inputs::Dict, setup::Dict) # Commitment state constraint linking startup and shutdown decisions (Constraint #4) @constraints(EP, begin - # For Start Hours, links first time step with last time step in subperiod - [y in COMMIT, t in START_SUBPERIODS], vCCOMMIT[y,t] == vCCOMMIT[y,(t+hours_per_subperiod-1)] + vCSTART[y,t] - vCSHUT[y,t] - # For all other hours, links commitment state in hour t with commitment state in prior hour + sum of start up and shut down in current hour - [y in COMMIT, t in INTERIOR_SUBPERIODS], vCCOMMIT[y,t] == vCCOMMIT[y,t-1] + vCSTART[y,t] - vCSHUT[y,t] + + [y in COMMIT, t = 1:T], vCCOMMIT[y,t] == vCCOMMIT[y,hoursbefore(p, t, 1)] + vCSTART[y,t] - vCSHUT[y,t] + end) - #ramp up, start - @constraint(EP,[y in COMMIT, t in START_SUBPERIODS], - EP[:vCP][y,t]-EP[:vCP][y,(t+hours_per_subperiod-1)] <= by_rid(y,:Ramp_Up_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) + #ramp up + @constraint(EP,[y in COMMIT, t= 1:T], + EP[:vCP][y,t]-EP[:vCP][y,hoursbefore(p, t, 1)] <= by_rid(y,:Ramp_Up_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) + min(1, max(by_rid(y,:Min_Power), by_rid(y,:Ramp_Up_Percentage)))*by_rid(y,:Cap_Size)*vCSTART[y,t] - by_rid(y,:Min_Power)*by_rid(y,:Cap_Size)*vCSHUT[y,t]) - #ramp up, interior - @constraint(EP,[y in COMMIT, t in INTERIOR_SUBPERIODS], - EP[:vCP][y,t]-EP[:vCP][y,(t-1)] <= by_rid(y,:Ramp_Up_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) - + min(1,max(by_rid(y,:Min_Power), by_rid(y,:Ramp_Up_Percentage)))*by_rid(y,:Cap_Size)*vCSTART[y,t] - - by_rid(y,:Min_Power)*by_rid(y,:Cap_Size)*vCSHUT[y,t]) - - #ramp down, start - @constraint(EP,[y in COMMIT, t in START_SUBPERIODS], - EP[:vCP][y,(t+hours_per_subperiod-1)]-EP[:vCP][y,t] <= by_rid(y,:Ramp_Dn_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) - - by_rid(y,:Min_Power)*by_rid(y,:Cap_Size)*vCSTART[y,t] - + min(1,max(by_rid(y,:Min_Power), by_rid(y,:Ramp_Dn_Percentage)))*by_rid(y,:Cap_Size)*vCSHUT[y,t]) - - #ramp down, interior - @constraint(EP,[y in COMMIT, t in INTERIOR_SUBPERIODS], - EP[:vCP][y,(t-1)]-EP[:vCP][y,t] <= by_rid(y,:Ramp_Dn_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) + #ramp down + @constraint(EP,[y in COMMIT, t= 1:T], + EP[:vCP][y,hoursbefore(p, t, 1)]-EP[:vCP][y,t] <= by_rid(y,:Ramp_Dn_Percentage)*by_rid(y,:Cap_Size)*(vCCOMMIT[y,t]-vCSTART[y,t]) - by_rid(y,:Min_Power)*by_rid(y,:Cap_Size)*vCSTART[y,t] + min(1,max(by_rid(y,:Min_Power), by_rid(y,:Ramp_Dn_Percentage)))*by_rid(y,:Cap_Size)*vCSHUT[y,t]) - # minimum and maximum stable power (assumes capacity factor of 1, so max power already implemented) + # minimum and maximum stable power @constraints(EP, begin [y in COMMIT, t=1:T], EP[:vCP][y,t] >= by_rid(y,:Min_Power)*by_rid(y,:Cap_Size)*vCCOMMIT[y,t] + + [y in COMMIT, t=1:T], EP[:vCP][y,t] <= by_rid(y, :Cap_Size) * vCCOMMIT[y,t] end) ### Minimum up and down times (Constraints #9-10) - p = hours_per_subperiod Up_Time = zeros(Int, nrow(dfGen)) Up_Time[COMMIT] .= Int.(floor.(dfTS[COMMIT,:Up_Time])) @constraint(EP, [y in COMMIT, t in 1:T], vCCOMMIT[y,t] >= sum(vCSTART[y, hoursbefore(p, t, 0:(Up_Time[y] - 1))]) ) + Down_Time = zeros(Int, nrow(dfGen)) Down_Time[COMMIT] .= Int.(floor.(dfTS[COMMIT,:Down_Time])) @constraint(EP, [y in COMMIT, t in 1:T], @@ -729,15 +708,14 @@ function thermal_core_emissions!(EP::Model, inputs::Dict, setup::Dict) Z = inputs["Z"] # Number of zones FUS = get_fus(inputs) #FUS generators NONFUS = get_nonfus(inputs) #NONFUS generators + THERM_COMMIT = inputs["THERM_COMMIT"] # units subject to UC by_rid(rid, sym) = by_rid_df(rid, sym, dfTS) @expression(EP, eEmissionsByPlantTS[y = 1:G, t = 1:T], if y ∉ TS 0 - elseif y in FUS - by_rid(y, :CO2_per_MWh) * EP[:vCP][y, t] + by_rid(y, :CO2_per_Start) * EP[:vFSTART][y, t] - elseif y in intersect(NONFUS, inputs["THERM_COMMIT"]) + elseif y in intersect(THERM_COMMIT, NONFUS) by_rid(y, :CO2_per_MWh) * EP[:vCP][y, t] + by_rid(y, :CO2_per_Start) * EP[:vCSTART][y, t] else by_rid(y, :CO2_per_MWh) * EP[:vCP][y,t] diff --git a/src/write_outputs/write_thermal_storage.jl b/src/write_outputs/write_thermal_storage.jl index 37f6a9600a..4a636ebb0d 100644 --- a/src/write_outputs/write_thermal_storage.jl +++ b/src/write_outputs/write_thermal_storage.jl @@ -15,12 +15,13 @@ received this license file. If not, see . """ -function write_core_capacities(EP::Model, inputs::Dict, filename::AbstractString, msf) +function write_core_capacities(EP::Model, inputs::Dict, filename::AbstractString, scale_factor) # Capacity decisions dfGen = inputs["dfGen"] dfTS = inputs["dfTS"] T = inputs["T"] + RH = get_resistive_heating(inputs) # load capacity power TSResources = dfTS[!,:Resource] @@ -39,7 +40,7 @@ function write_core_capacities(EP::Model, inputs::Dict, filename::AbstractString # load rh capacity rhcapacity = zeros(TSG) for i in 1:TSG - if dfTS[i, :RH] == 1 + if dfTS[i, :R_ID] in RH rhcapacity[i] = first(value.(EP[:vRHCAP][dfTS[i,:R_ID]])) end end @@ -54,9 +55,9 @@ function write_core_capacities(EP::Model, inputs::Dict, filename::AbstractString ) # adjust files and write - dfCoreCap.CorePowerCap = dfCoreCap.CorePowerCap * msf - dfCoreCap.TSEnergyCap = dfCoreCap.TSEnergyCap * msf - dfCoreCap.RHPowerCap = dfCoreCap.RHPowerCap * msf + dfCoreCap.CorePowerCap *= scale_factor + dfCoreCap.TSEnergyCap *= scale_factor + dfCoreCap.RHPowerCap *= scale_factor CSV.write(filename, dfCoreCap) return dfCoreCap @@ -86,7 +87,7 @@ function write_core_commitments(EP::Model, inputs::Dict, SET::Vector{Int},symbol return df end -function write_scaled_values(EP::Model, inputs::Dict, SET::Vector{Int}, symbol::Symbol, filename::AbstractString, msf) +function write_scaled_values(EP::Model, inputs::Dict, SET::Vector{Int}, symbol::Symbol, filename::AbstractString, scale_factor) dfTS = inputs["dfTS"] T = inputs["T"] @@ -94,7 +95,7 @@ function write_scaled_values(EP::Model, inputs::Dict, SET::Vector{Int}, symbol:: zones = by_rid_df(SET, :Zone, dfTS) df = DataFrame(Resource = resources, Zone=zones) - quantity = value.(EP[symbol][SET, :]).data * msf + quantity = value.(EP[symbol][SET, :]).data * scale_factor df.AnnualSum = quantity * inputs["omega"] df = hcat(df, DataFrame(quantity, :auto)) @@ -109,7 +110,7 @@ function write_scaled_values(EP::Model, inputs::Dict, SET::Vector{Int}, symbol:: return df end -function write_thermal_storage_system_max_dual(EP::Model, inputs::Dict, setup::Dict, filename::AbstractString, msf) +function write_thermal_storage_system_max_dual(EP::Model, inputs::Dict, setup::Dict, filename::AbstractString, scale_factor) dfTS = inputs["dfTS"] FUS = dfTS[dfTS.FUS .== 1, :R_ID] @@ -117,24 +118,26 @@ function write_thermal_storage_system_max_dual(EP::Model, inputs::Dict, setup::D FIRST_ROW = 1 if dfTS[FIRST_ROW, :System_Max_Cap_MWe_net] >= 0 val = -1*dual.(EP[:cCSystemTot]) - val *= msf + val *= scale_factor df = DataFrame(:System_Max_Cap_MW_th_dual => val) CSV.write(filename, dftranspose(df, false), writeheader=false) end end end -function write_thermal_storage_capacity_duals(EP::Model, inputs::Dict, setup::Dict, filename::AbstractString, msf) +function write_thermal_storage_capacity_duals(EP::Model, inputs::Dict, setup::Dict, filename::AbstractString, scale_factor) dfTS = inputs["dfTS"] - NONFUS = dfTS[dfTS.FUS .== 0, :R_ID] + TS = inputs["TS"] + NONFUS = get_nonfus(inputs) if !isempty(NONFUS) - HAS_MAX_LIMIT = dfTS[by_rid_df(NONFUS, :Max_Core_Power_Capacity, dfTS) .> 0, :R_ID] + HAS_MAX_LIMIT = dfTS[dfTS.Max_Core_Power_Capacity_MWe .> 0, :R_ID] + HAS_MAX_LIMIT = intersect(HAS_MAX_LIMIT, NONFUS) resources = by_rid_df(HAS_MAX_LIMIT, :Resource, dfTS) n_max = length(HAS_MAX_LIMIT) vals = zeros(n_max) for i in 1:n_max - vals[i] = -1 * dual.(EP[:cCoreMaxCapacity][i]) * msf + vals[i] = -1 * dual.(EP[:cCoreMaxCapacity][HAS_MAX_LIMIT[i]]) * scale_factor end df = DataFrame( Resource = resources, @@ -156,32 +159,32 @@ function write_thermal_storage(path::AbstractString, inputs::Dict, setup::Dict, dfGen = inputs["dfGen"] dfTS = inputs["dfTS"] T = inputs["T"] - msf = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 + scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 ### WRITE CORE CAPACITY DECISIONS ### - dfCoreCap = write_core_capacities(EP, inputs, joinpath(path,"TS_capacity.csv"), msf) + dfCoreCap = write_core_capacities(EP, inputs, joinpath(path,"TS_capacity.csv"), scale_factor) ### LOAD RELEVANT SETS ### THERMAL_STORAGE = dfTS.R_ID - RH = dfTS[dfTS.RH .==1, :R_ID] + RH = get_resistive_heating(inputs) FUS = dfTS[dfTS.FUS .== 1, :R_ID] NONFUS = dfTS[dfTS.FUS .== 0, :R_ID] ### CORE POWER TIME SERIES ### - dfCorePwr = write_scaled_values(EP, inputs, THERMAL_STORAGE, :vCP, joinpath(path, "TS_CorePwr.csv"), msf) + dfCorePwr = write_scaled_values(EP, inputs, THERMAL_STORAGE, :vCP, joinpath(path, "TS_CorePwr.csv"), scale_factor) ### THERMAL SOC TIME SERIES ### - dfTSOC = write_scaled_values(EP, inputs, THERMAL_STORAGE, :vTS, joinpath(path, "TS_SOC.csv"), msf) + dfTSOC = write_scaled_values(EP, inputs, THERMAL_STORAGE, :vTS, joinpath(path, "TS_SOC.csv"), scale_factor) ### RESISTIVE HEATING TIME SERIES ### if !isempty(RH) - dfRH = write_scaled_values(EP, inputs, RH, :vRH, joinpath(path, "TS_RH.csv"), msf) + dfRH = write_scaled_values(EP, inputs, RH, :vRH, joinpath(path, "TS_RH.csv"), scale_factor) end ### FUSION SPECIFIC OUTPUTS ### if !isempty(FUS) ### RECIRCULATING POWER TIME SERIES ### - dfRecirc = write_scaled_values(EP, inputs, FUS, :eTotalRecircFus, joinpath(path, "TS_Recirc.csv"), msf) + dfRecirc = write_scaled_values(EP, inputs, FUS, :eTotalRecircFus, joinpath(path, "TS_Recirc.csv"), scale_factor) ### CORE STARTS, SHUTS, COMMITS, and MAINTENANCE TIMESERIES ### dfFStart = write_core_commitments(EP, inputs, FUS, :vFSTART, joinpath(path, "TS_FUS_start.csv")) @@ -202,7 +205,7 @@ function write_thermal_storage(path::AbstractString, inputs::Dict, setup::Dict, end # Write dual values of certain constraints - write_thermal_storage_system_max_dual(EP, inputs, setup, joinpath(path, "TS_System_Max_Cap_dual.csv"), msf) - write_thermal_storage_capacity_duals(EP, inputs, setup, joinpath(path, "TS_Capacity_Duals"), msf) + write_thermal_storage_system_max_dual(EP, inputs, setup, joinpath(path, "TS_System_Max_Cap_dual.csv"), scale_factor) + write_thermal_storage_capacity_duals(EP, inputs, setup, joinpath(path, "TS_Capacity_Duals.csv"), scale_factor) end