diff --git a/DOLPHYN/Project.toml b/DOLPHYN/Project.toml new file mode 100644 index 000000000..81648c0b1 --- /dev/null +++ b/DOLPHYN/Project.toml @@ -0,0 +1 @@ +[deps] diff --git a/Project.toml b/Project.toml index c31e1bd23..b64e4528d 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,7 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" +Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -47,4 +48,4 @@ Revise = "3.4.0" Statistics = "1" StatsBase = "0.33.0, 0.34" YAML = "0.4.0" -julia = "1.6" +julia = "1.8.5" diff --git a/docs/src/write_co2_outputs.md b/docs/src/write_co2_outputs.md index c6a9b7783..c00c365a2 100644 --- a/docs/src/write_co2_outputs.md +++ b/docs/src/write_co2_outputs.md @@ -61,4 +61,40 @@ Pages = ["CSC/write_outputs/write_co2_emission_balance_system.jl"] ```@autodocs Modules = [Dolphyn] Pages = ["CSC/write_outputs/write_co2_emission_balance_zone.jl"] -``` \ No newline at end of file +``` + +## Write Balancing of CO2 Outflow from a zone +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_capture_outflow_balance.jl"] +``` + +## Write Balancing of CO2 Inflow from Spur pipeline to a CO2 Storage site +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_spur_inflow_storage_balance.jl"] +``` + +## Write amount of CO2 flowing across trunk pipelines at a given time t +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_trunk_pipeline_flow.jl"] +``` + +## Write amount of CO2 flowing across spur pipelines at a given time t +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_spur_pipeline_flow.jl"] +``` + +## Write expansion of trunk pipelines +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_trunk_pipeline_expansion.jl"] +``` + +## Write expansion of spur pipelines +```@autodocs +Modules = [Dolphyn] +Pages = ["CSC/write_outputs/write_co2_spur_pipeline_expansion.jl"] +``` diff --git a/src/CSC/load_inputs/load_co2_capture_DAC.jl b/src/CSC/load_inputs/load_co2_capture_DAC.jl index 17f1b4234..2b988bf56 100644 --- a/src/CSC/load_inputs/load_co2_capture_DAC.jl +++ b/src/CSC/load_inputs/load_co2_capture_DAC.jl @@ -15,7 +15,7 @@ received this license file. If not, see . """ @doc raw""" - load_co2_capture_DAC(setup::Dict, path::AbstractString, sep::AbstractString, inputs_capture::Dict) + load_co2_capture_DAC(setup::Dict, path::AbstractString, sep::AbstractString, inputs_capture::Dict) Function for reading input parameters related to DAC resources in the carbon supply chain. """ diff --git a/src/CSC/load_inputs/load_co2_capture_compression.jl b/src/CSC/load_inputs/load_co2_capture_compression.jl index ced5b1196..bd7a1060b 100644 --- a/src/CSC/load_inputs/load_co2_capture_compression.jl +++ b/src/CSC/load_inputs/load_co2_capture_compression.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reading input parameters related to CO2 compression resources in the carbon supply chain. """ + function load_co2_capture_compression(setup::Dict, path::AbstractString, sep::AbstractString, inputs_co2_capture_comp::Dict) #Read in CO2 capture related inputs diff --git a/src/CSC/load_inputs/load_co2_demand.jl b/src/CSC/load_inputs/load_co2_demand.jl new file mode 100644 index 000000000..d41c33c3d --- /dev/null +++ b/src/CSC/load_inputs/load_co2_demand.jl @@ -0,0 +1,60 @@ +""" +DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks +Copyright (C) 2022, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + load_co2_demand(setup::Dict, path::AbstractString, sep::AbstractString, inputs_load::Dict) + + Function for reading input parameters related to exogenous CO2 load (demand) of each zone. +""" +function load_co2_demand(setup::Dict, path::AbstractString, sep::AbstractString, inputs_load::Dict) + + data_directory = joinpath(path, setup["TimeDomainReductionFolder"]) + if setup["TimeDomainReduction"] == 1 && isfile(joinpath(data_directory,"Load_data.csv")) && isfile(joinpath(data_directory,"Generators_variability.csv")) && isfile(joinpath(data_directory,"Fuels_data.csv")) && isfile(joinpath(data_directory,"CSC_load_data.csv")) # Use Time Domain Reduced data for GenX + CSC_load_in = DataFrame(CSV.File(string(joinpath(data_directory,"CSC_load_data.csv")), header=true), copycols=true) + else # Run without Time Domain Reduction OR Getting original input data for Time Domain Reduction + CSC_load_in = DataFrame(CSV.File(string(path,sep,"CSC_load_data.csv"), header=true), copycols=true) + end + + # Number of demand curtailment/lost load segments + inputs_load["CO2_SEG"]=size(collect(skipmissing(CSC_load_in[!,:Demand_Segment])),1) + + # Demand in tonnes per hour for each zone + #print_and_log(names(load_in)) + start = findall(s -> s == "Load_CO2_tonne_per_hr_z1", names(CSC_load_in))[1] #gets the starting column number of all the columns, with header "Load_CO2_z1" + + # Max value of non-served energy in $/(tonne) + inputs_load["CO2_Voll"] = collect(skipmissing(CSC_load_in[!,:Voll])) + # Demand in Tonnes per hour + inputs_load["CO2_D"] =Matrix(CSC_load_in[1:inputs_load["T"],start:start-1+inputs_load["Z"]]) #form a matrix with columns as the different zonal load CO2 demand values and rows as the hours + + + # Cost of non-served energy/demand curtailment (for each segment) + CO2_SEG = inputs_load["CO2_SEG"] # Number of demand segments + inputs_load["pC_CO2_D_Curtail"] = zeros(CO2_SEG) + inputs_load["pMax_CO2_D_Curtail"] = zeros(CO2_SEG) + for s in 1:CO2_SEG + # Cost of each segment reported as a fraction of value of non-served energy - scaled implicitly + inputs_load["pC_CO2_D_Curtail"][s] = collect(skipmissing(CSC_load_in[!,:Cost_of_Demand_Curtailment_per_Tonne]))[s]*inputs_load["Voll"][1] + # Maximum hourly demand curtailable as % of the max demand (for each segment) + inputs_load["pMax_CO2_D_Curtail"][s] = collect(skipmissing(CSC_load_in[!,:Max_Demand_Curtailment]))[s] + end + + print_and_log("CSC_load_data.csv Successfully Read!") + + return inputs_load + +end + diff --git a/src/CSC/load_inputs/load_co2_inputs.jl b/src/CSC/load_inputs/load_co2_inputs.jl index d9b2bdb1a..9cafed35e 100644 --- a/src/CSC/load_inputs/load_co2_inputs.jl +++ b/src/CSC/load_inputs/load_co2_inputs.jl @@ -16,7 +16,7 @@ received this license file. If not, see . @doc raw""" - load_co2_inputs(inputs::Dict,setup::Dict,path::AbstractString) + load_co2_inputs(inputs::Dict,setup::Dict,path::AbstractString) Loads various data inputs from multiple input .csv files in path directory and stores variables in a Dict (dictionary) object for use in model() function @@ -27,6 +27,7 @@ path - string path to working directory returns: Dict (dictionary) object containing all data inputs of carbon supply chain. """ + function load_co2_inputs(inputs::Dict,setup::Dict,path::AbstractString) ## Use appropriate directory separator depending on Mac or Windows config @@ -48,6 +49,11 @@ function load_co2_inputs(inputs::Dict,setup::Dict,path::AbstractString) inputs = load_co2_storage(setup, path, sep, inputs) inputs = load_co2_capture_compression(setup, path, sep, inputs) inputs = load_co2_pipeline_data(setup, path, sep, inputs) + + # Adding a new line over here to specify loading of exogeneous CO2 demand + if setup["Exogeneous_CO2_Demand"] == 1 + inputs = load_co2_demand(setup, path, sep, inputs) + end println("CSC Input CSV Files Successfully Read In From $path$sep") diff --git a/src/CSC/load_inputs/load_co2_pipeline_data.jl b/src/CSC/load_inputs/load_co2_pipeline_data.jl index 2001cb76e..4972afeec 100644 --- a/src/CSC/load_inputs/load_co2_pipeline_data.jl +++ b/src/CSC/load_inputs/load_co2_pipeline_data.jl @@ -17,86 +17,167 @@ received this license file. If not, see . @doc raw""" load_co2_pipeline_data(setup::Dict, path::AbstractString, sep::AbstractString, inputs_co2_nw::Dict) -Function for reading input parameters related to the CO2 transmission network via pipelines. +Function for reading input parameters related to the CO2 transmission network via trunk and spur pipelines. """ + function load_co2_pipeline_data(setup::Dict, path::AbstractString, sep::AbstractString, inputs_co2_nw::Dict) # Network zones inputs and Network topology inputs - co2_pipeline_var = DataFrame(CSV.File(string(path,sep,"CSC_pipelines.csv"), header=true), copycols=true) - - inputs_co2_nw["Z"] = size(findall(s -> (startswith(s, "z")) & (tryparse(Float64, s[2:end]) != nothing), names(co2_pipeline_var)),1) + # Trunk Pipelines + co2_trunk_pipeline_var = DataFrame(CSV.File(string(path,sep,"CSC_trunk_pipelines.csv"), header=true), copycols=true) + # Spur Pipelines + co2_spur_pipeline_var = DataFrame(CSV.File(string(path,sep,"CSC_spur_pipelines.csv"), header=true), copycols=true) - #Number of CO2 Pipelines = L - inputs_co2_nw["CO2_P"]=size(collect(skipmissing(co2_pipeline_var[!,:CO2_Pipelines])),1) + # For Trunk Pipelines + inputs_co2_nw["Z"] = size(findall(s -> (startswith(s, "z")) & (tryparse(Float64, s[2:end]) != nothing), names(co2_trunk_pipeline_var)),1) + + #Number of CO2 Trunk Pipelines = L + inputs_co2_nw["Trunk_CO2_P"]=size(collect(skipmissing(co2_trunk_pipeline_var[!,:CO2_Pipelines])),1) #Find first column of pipe map table - start = findall(s -> s == "z1", names(co2_pipeline_var))[1] + start = findall(s -> s == "z1", names(co2_trunk_pipeline_var))[1] #Select pipe map L x N matrix where L is number of pipelines and N is number of nodes - co2_pipe_map = co2_pipeline_var[1:inputs_co2_nw["CO2_P"], start:start+inputs_co2_nw["Z"]-1] + co2_trunk_pipe_map = co2_trunk_pipeline_var[1:inputs_co2_nw["Trunk_CO2_P"], start:start+inputs_co2_nw["Z"]-1] #Create pipe number column - co2_pipe_map[!,:pipe_no] = 1:size(co2_pipe_map,1) + co2_trunk_pipe_map[!,:pipe_no] = 1:size(co2_trunk_pipe_map,1) #Pivot table - co2_pipe_map = stack(co2_pipe_map, 1:inputs_co2_nw["Z"]) + co2_trunk_pipe_map = stack(co2_trunk_pipe_map, 1:inputs_co2_nw["Z"]) #Create zone column - co2_pipe_map[!,:Zone] = parse.(Float64,SubString.(co2_pipe_map[!,:variable],2)) + co2_trunk_pipe_map[!,:Zone] = parse.(Float64,SubString.(co2_trunk_pipe_map[!,:variable],2)) #Remove redundant rows - co2_pipe_map = co2_pipe_map[co2_pipe_map[!,:value].!=0,:] + co2_trunk_pipe_map = co2_trunk_pipe_map[co2_trunk_pipe_map[!,:value].!=0,:] #Rename column - colnames_co2_pipe_map = ["pipe_no", "zone_str", "d", "Zone"] - rename!(co2_pipe_map, Symbol.(colnames_co2_pipe_map)) + colnames_co2_trunk_pipe_map = ["pipe_no", "zone_str", "d", "Zone"] + rename!(co2_trunk_pipe_map, Symbol.(colnames_co2_trunk_pipe_map)) - inputs_co2_nw["CO2_Pipe_Map"] = co2_pipe_map + inputs_co2_nw["CO2_Trunk_Pipe_Map"] = co2_trunk_pipe_map # Number of pipelines routes in the network - inputs_co2_nw["CO2_P"]=size(collect(skipmissing(co2_pipeline_var[!,:CO2_Pipelines])),1) + inputs_co2_nw["Trunk_CO2_P"]=size(collect(skipmissing(co2_trunk_pipeline_var[!,:CO2_Pipelines])),1) # Length in miles of each pipeline - inputs_co2_nw["pCO2_Pipe_length_miles"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:Pipe_length_miles]))) + inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:Pipe_length_miles]))) # Length between two booster compressor stations in miles - inputs_co2_nw["CO2_len_bw_comp_mile"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2_len_bw_comp_mile]))) + inputs_co2_nw["Trunk_CO2_len_bw_comp_mile"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2_len_bw_comp_mile]))) # Number of booster compressors between source and sink # DEV NOTE: we should make the total number of compressors if the ratio is less than 1 for a particular line ############################################################################################################# #Check if there should be a "." after inputs_co2_nw["pCO2_Pipe_length_miles"] for division - inputs_co2_nw["CO2_no_booster_comp_stations"] = floor.(inputs_co2_nw["pCO2_Pipe_length_miles"]./inputs_co2_nw["CO2_len_bw_comp_mile"]) - ones(length(inputs_co2_nw["CO2_len_bw_comp_mile"])) + inputs_co2_nw["Trunk_CO2_no_booster_comp_stations"] = floor.(inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"]./inputs_co2_nw["Trunk_CO2_len_bw_comp_mile"]) - ones(length(inputs_co2_nw["Trunk_CO2_len_bw_comp_mile"])) #Maximum number of pipelines - inputs_co2_nw["pCO2_Pipe_No_Max"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:Max_No_Pipe]))) + inputs_co2_nw["Trunk_pCO2_Pipe_No_Max"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:Max_No_Pipe]))) #Current number of pipelines - inputs_co2_nw["pCO2_Pipe_No_Curr"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:Existing_No_Pipe]))) + inputs_co2_nw["Trunk_pCO2_Pipe_No_Curr"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:Existing_No_Pipe]))) #Maxiumum Pipe Flow per Pipe - inputs_co2_nw["pCO2_Pipe_Max_Flow"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:Max_Flow_Tonne_p_hr_Per_Pipe]))) + inputs_co2_nw["Trunk_pCO2_Pipe_Max_Flow"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:Max_Flow_Tonne_p_hr_Per_Pipe]))) #Maximum Pipeline storage capacity in tonnes per pipe - inputs_co2_nw["pCO2_Pipe_Max_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2PipeCap_tonne_per_mile]))) .* inputs_co2_nw["pCO2_Pipe_length_miles"] + inputs_co2_nw["Trunk_pCO2_Pipe_Max_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2PipeCap_tonne_per_mile]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] #Minimum Pipeline storage capacity in tonnes per pipe - inputs_co2_nw["pCO2_Pipe_Min_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:Min_pipecap_stor_frac]))) .* inputs_co2_nw["pCO2_Pipe_Max_Cap"] + inputs_co2_nw["Trunk_pCO2_Pipe_Min_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:Min_pipecap_stor_frac]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_Max_Cap"] #Capital Cost Per Pipe using mean cost - inputs_co2_nw["pCAPEX_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2Pipe_Inv_Cost_per_mile_yr]))) .* inputs_co2_nw["pCO2_Pipe_length_miles"] - inputs_co2_nw["pFixed_OM_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2Pipe_Fixed_OM_Cost_per_mile_yr]))) .* inputs_co2_nw["pCO2_Pipe_length_miles"] - inputs_co2_nw["pMWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2Pipe_Energy_MWh_per_mile_per_tonne]))) .* inputs_co2_nw["pCO2_Pipe_length_miles"] + inputs_co2_nw["Trunk_pCAPEX_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2Pipe_Inv_Cost_per_mile_yr]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] + inputs_co2_nw["Trunk_pFixed_OM_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2Pipe_Fixed_OM_Cost_per_mile_yr]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] + inputs_co2_nw["Trunk_pMWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2Pipe_Energy_MWh_per_mile_per_tonne]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] - inputs_co2_nw["pLoss_tonne_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:CO2PipeLoss_tonne_per_mile_per_tonne]))) .* inputs_co2_nw["pCO2_Pipe_length_miles"] + inputs_co2_nw["Trunk_pLoss_tonne_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:CO2PipeLoss_tonne_per_mile_per_tonne]))) .* inputs_co2_nw["Trunk_pCO2_Pipe_length_miles"] #Capital cost associated with booster compressors per pipe= capex per tonne/hour flow rate x pipe max flow rate (tonne/hour) x number of booster compressor stations per pipe route - inputs_co2_nw["pCAPEX_Comp_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:BoosterCompCapex_per_tonne_p_hr_yr]))).* inputs_co2_nw["pCO2_Pipe_Max_Flow"].*inputs_co2_nw["CO2_no_booster_comp_stations"] + inputs_co2_nw["Trunk_pCAPEX_Comp_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:BoosterCompCapex_per_tonne_p_hr_yr]))).* inputs_co2_nw["Trunk_pCO2_Pipe_Max_Flow"].*inputs_co2_nw["Trunk_CO2_no_booster_comp_stations"] #Compression energy requirement Per Pipe = MWh electricity per tonne of gas flow rate x number of compressor stations enroute a pipeline route - inputs_co2_nw["pComp_MWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_pipeline_var[!,:BoosterCompEnergy_MWh_per_tonne]))).* inputs_co2_nw["CO2_no_booster_comp_stations"] + inputs_co2_nw["Trunk_pComp_MWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_trunk_pipeline_var[!,:BoosterCompEnergy_MWh_per_tonne]))).* inputs_co2_nw["Trunk_CO2_no_booster_comp_stations"] + + ## For Spur Pipelines ## + + # These are from the GenX/HSC domains + inputs_co2_nw["Z"] = size(findall(s -> (startswith(s, "z")) & (tryparse(Float64, s[2:end]) != nothing), names(co2_spur_pipeline_var)),1) + + # The following are from the CO2 Domain + inputs_co2_nw["S"] = size(findall(s -> (startswith(s, "s")), names(co2_spur_pipeline_var)),1) + + #Number of CO2 Pipelines = L + inputs_co2_nw["Spur_CO2_P"]=size(collect(skipmissing(co2_spur_pipeline_var[!,:CO2_Pipelines])),1) + + #Find first column of pipe map table + start = findall(s -> s == "z1", names(co2_spur_pipeline_var))[1] + + #Select pipe map L x N matrix where L is number of pipelines and N is number of nodes + co2_spur_pipe_map = co2_spur_pipeline_var[1:inputs_co2_nw["Spur_CO2_P"], start:start+inputs_co2_nw["Z"]+inputs_co2_nw["S"]-1] + + #Create pipe number column + co2_spur_pipe_map[!,:pipe_no] = 1:size(co2_spur_pipe_map,1) + + #Pivot table + co2_spur_pipe_map = stack(co2_spur_pipe_map, 1:inputs_co2_nw["Z"]+inputs_co2_nw["S"]) + + #Create zone column + co2_spur_pipe_map[!,:Zone] = parse.(Float64,SubString.(co2_spur_pipe_map[!,:variable],2)) + + #Remove redundant rows + co2_spur_pipe_map = co2_spur_pipe_map[co2_spur_pipe_map[!,:value].!=0,:] + + #Rename column + colnames_co2_spur_pipe_map = ["pipe_no", "zone_str", "d", "Zone"] + rename!(co2_spur_pipe_map, Symbol.(colnames_co2_spur_pipe_map)) + + inputs_co2_nw["CO2_Spur_Pipe_Map"] = co2_spur_pipe_map + + # Number of pipelines routes in the network + inputs_co2_nw["Spur_CO2_P"]=size(collect(skipmissing(co2_spur_pipeline_var[!,:CO2_Pipelines])),1) + + # Length in miles of each pipeline + inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:Pipe_length_miles]))) + + # Length between two booster compressor stations in miles + inputs_co2_nw["Spur_CO2_len_bw_comp_mile"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2_len_bw_comp_mile]))) + + # Number of booster compressors between source and sink + inputs_co2_nw["Spur_CO2_no_booster_comp_stations"] = floor.(inputs_co2_nw["Spur_pCO2_Pipe_length_miles"]./inputs_co2_nw["Spur_CO2_len_bw_comp_mile"]) - ones(length(inputs_co2_nw["Spur_CO2_len_bw_comp_mile"])) + + #Maximum number of pipelines + inputs_co2_nw["Spur_pCO2_Pipe_No_Max"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:Max_No_Pipe]))) + + #Current number of pipelines + inputs_co2_nw["Spur_pCO2_Pipe_No_Curr"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:Existing_No_Pipe]))) + + #Maxiumum Pipe Flow per Pipe + inputs_co2_nw["Spur_pCO2_Pipe_Max_Flow"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:Max_Flow_Tonne_p_hr_Per_Pipe]))) + + #Maximum Pipeline storage capacity in tonnes per pipe + inputs_co2_nw["Spur_pCO2_Pipe_Max_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2PipeCap_tonne_per_mile]))) .* inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] + + #Minimum Pipeline storage capacity in tonnes per pipe + inputs_co2_nw["Spur_pCO2_Pipe_Min_Cap"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:Min_pipecap_stor_frac]))) .* inputs_co2_nw["Spur_pCO2_Pipe_Max_Cap"] + + #Capital Cost Per Pipe using mean cost + inputs_co2_nw["Spur_pCAPEX_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2Pipe_Inv_Cost_per_mile_yr]))) .* inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] + inputs_co2_nw["Spur_pFixed_OM_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2Pipe_Fixed_OM_Cost_per_mile_yr]))) .* inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] + inputs_co2_nw["Spur_pMWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2Pipe_Energy_MWh_per_mile_per_tonne]))) .* inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] + + inputs_co2_nw["Spur_pLoss_tonne_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:CO2PipeLoss_tonne_per_mile_per_tonne]))) .* inputs_co2_nw["Spur_pCO2_Pipe_length_miles"] + + #Capital cost associated with booster compressors per pipe= capex per tonne/hour flow rate x pipe max flow rate (tonne/hour) x number of booster compressor stations per pipe route + inputs_co2_nw["Spur_pCAPEX_Comp_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:BoosterCompCapex_per_tonne_p_hr_yr]))).* inputs_co2_nw["Spur_pCO2_Pipe_Max_Flow"].*inputs_co2_nw["Spur_CO2_no_booster_comp_stations"] + + #Compression energy requirement Per Pipe = MWh electricity per tonne of gas flow rate x number of compressor stations enroute a pipeline route + inputs_co2_nw["Spur_pComp_MWh_per_tonne_CO2_Pipe"] = convert(Array{Float64}, collect(skipmissing(co2_spur_pipeline_var[!,:BoosterCompEnergy_MWh_per_tonne]))).* inputs_co2_nw["Spur_CO2_no_booster_comp_stations"] - println("CO2_pipelines.csv Successfully Read!") + println("CO2_trunk_pipelines.csv Successfully Read!") + println("CO2_spur_pipelines.csv Successfully Read!") return inputs_co2_nw end diff --git a/src/CSC/load_inputs/load_co2_storage.jl b/src/CSC/load_inputs/load_co2_storage.jl index ca867bc29..4d6b9dc9f 100644 --- a/src/CSC/load_inputs/load_co2_storage.jl +++ b/src/CSC/load_inputs/load_co2_storage.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reading input parameters related to CO2 storage resources in the carbon supply chain. """ + function load_co2_storage(setup::Dict, path::AbstractString, sep::AbstractString, inputs_co2_storage::Dict) #Read in CO2 capture related inputs diff --git a/src/CSC/model/capture/co2_capture.jl b/src/CSC/model/capture/co2_capture.jl index d3833d4d9..1201d0cb7 100644 --- a/src/CSC/model/capture/co2_capture.jl +++ b/src/CSC/model/capture/co2_capture.jl @@ -15,7 +15,7 @@ received this license file. If not, see . """ @doc raw""" - co2_capture(EP::Model, inputs::Dict, setup::Dict) +co2_capture(EP::Model, inputs::Dict, setup::Dict) This module models the CO2 captured by flue gas CCS units present in power, H2, and DAC plants and adds them to the total captured CO2 balance """ @@ -78,6 +78,12 @@ function co2_capture(EP::Model, inputs::Dict, setup::Dict) #ADD TO CO2 BALANCE EP[:eCaptured_CO2_Balance] += EP[:eDAC_Fuel_CO2_captured_per_time_per_zone] + ### Adding a New Constraint that forces CO2 Captured and Stored to be Equal to a Given Target ### + # Note: DAC capture values can also be added here + + if setup["CO2CaptureTarget"] > 0 + @constraint(EP, cMatchingCO2CaptureTarget, sum(sum(inputs["omega"].* ((EP[:ePower_CO2_captured_per_zone_per_time_acc])[z,:])) for z in 1:Z) + sum(sum(inputs["omega"].* ((EP[:eHydrogen_CO2_captured_per_zone_per_time_acc])[z,:])) for z in 1:Z) - setup["CO2CaptureTarget"] == 0) + end return EP end diff --git a/src/CSC/model/capture/co2_capture_DAC.jl b/src/CSC/model/capture/co2_capture_DAC.jl index bbd36efda..a06ef8107 100644 --- a/src/CSC/model/capture/co2_capture_DAC.jl +++ b/src/CSC/model/capture/co2_capture_DAC.jl @@ -21,11 +21,11 @@ The DAC module creates decision variables, expressions, and constraints related This module defines the power consumption decision variable $x_{z,t}^{\textrm{E,DAC}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power consumed by DAC in zone $z$ at time period $t$. -The variable defined in this file named after ```vPower\textunderscore{DAC}``` cover variable $x_{z,t}^{E,H-GEN}$. +The variable defined in this file named after ```vPower\textunderscore DAC``` cover variable $x_{z,t}^{E,H-GEN}$. This module defines the power generation decision variable $x_{z,t}^{\textrm{EGEN,DAC}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power generated by DAC in zone $z$ at time period $t$. -The variable defined in this file named after ```vPower\textunderscore{Produced}\textunderscore{DAC}``` cover variable $x_{z,t}^{EGEN,DAC}$. +The variable defined in this file named after ```vPower\textunderscore Produced\textunderscore DAC``` cover variable $x_{z,t}^{EGEN,DAC}$. **Minimum and maximum DAC output** @@ -60,7 +60,9 @@ DAC resources adhere to the following ramping limits on hourly changes in CO2 ca (See Constraints 5-8 in the code) This set of time-coupling constraints wrap around to ensure the DAC capture output in the first time step of each year (or each representative period), $t \in \mathcal{T}^{start}$, is within the eligible ramp of the output in the final time step of the year (or each representative period), $t+\tau^{period}-1$. + """ + function co2_capture_DAC(EP::Model, inputs::Dict,setup::Dict) #Rename CO2Capture dataframe diff --git a/src/CSC/model/compression/co2_capture_compression.jl b/src/CSC/model/compression/co2_capture_compression.jl index a76bda8c6..bdce1e5f1 100644 --- a/src/CSC/model/compression/co2_capture_compression.jl +++ b/src/CSC/model/compression/co2_capture_compression.jl @@ -15,32 +15,34 @@ received this license file. If not, see . """ @doc raw""" - co2_capture_compression(EP::Model, inputs::Dict,setup::Dict) + co2_capture_compression(EP::Model, inputs::Dict, setup::Dict) -The CO2 compression module creates decision variables, expressions, and constraints related to CO2 compression infrastructure for captured CO2 by DAC units. + The CO2 compression module creates decision variables, expressions, and constraints related to CO2 compression infrastructure for captured CO2 by DAC units. -This module defines the CO2 compression decision variable $x_{k,z,t}^{\textrm{C,COMP}} \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T}$, representing CO2 compressed by resource $k$ in zone $z$ at time period $t$ after being captured by DAC. + This module defines the CO2 compression decision variable $x_{k,z,t}^{\textrm{C,COMP}} \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T}$, representing CO2 compressed by resource $k$ in zone $z$ at time period $t$ after being captured by DAC. -The variable defined in this file named after ```vDAC\textunderscore{CO2}\textunderscore{Capture}\textunderscore{Compressed}$``` covers all variables $x_{k,z,t}^{\textrm{C,COMP}}$. + The variable defined in this file named after ```$vDAC\textunderscore CO2\textunderscore Capture\textunderscore Compressed$``` covers all variables $x_{k,z,t}^{\textrm{C,COMP}}$. -This module defines the power consumption decision variable $x_{z,t}^{\textrm{E,COMP}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power consumed by CO2 compression in zone $z$ at time period $t$. + This module defines the power consumption decision variable $x_{z,t}^{\textrm{E,COMP}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power consumed by CO2 compression in zone $z$ at time period $t$. -The variable defined in this file named after ```vPower\textunderscore{CO2}\textunderscore{Capture}\textunderscore{Compressed}``` cover variable $x_{z,t}^{E,COMP}$. + The variable defined in this file named after ```vPower\textunderscore CO2\textunderscore Capture\textunderscore Compressed``` cover variable $x_{z,t}^{E,COMP}$. -**Minimum and maximum CO2 compression output** + **Minimum and maximum CO2 compression output** -```math -\begin{equation*} - x_{k,z,t}^{\textrm{C,COMP}} \geq \underline{R_{k,z}^{\textrm{C,COMP}}} \times y_{k,z}^{\textrm{C,COMP}} \quad \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T} -\end{equation*} -``` + ```math + \begin{equation*} + x_{k,z,t}^{\textrm{C,COMP}} \geq \underline{R_{k,z}^{\textrm{C,COMP}}} \times y_{k,z}^{\textrm{C,COMP}} \quad \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} + ``` + + ```math + \begin{equation*} + x_{k,z,t}^{\textrm{C,COMP}} \leq \overline{R_{k,z}^{\textrm{C,COMP}}} \times y_{k,z}^{\textrm{C,COMP}} \quad \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} + ``` -```math -\begin{equation*} - x_{k,z,t}^{\textrm{C,COMP}} \leq \overline{R_{k,z}^{\textrm{C,COMP}}} \times y_{k,z}^{\textrm{C,COMP}} \quad \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T} -\end{equation*} -``` """ + function co2_capture_compression(EP::Model, inputs::Dict,setup::Dict) #Rename CO2CaptureComp dataframe @@ -67,7 +69,7 @@ function co2_capture_compression(EP::Model, inputs::Dict,setup::Dict) sum(EP[:vPower_CO2_Capture_Comp][k,t] for k in dfCO2CaptureComp[dfCO2CaptureComp[!,:Zone].==z,:][!,:R_ID])) #Add to power balance to take power away from generated - EP[:ePowerBalance] += -ePower_Balance_CO2_Capture_Comp + EP[:ePowerBalance] -= ePower_Balance_CO2_Capture_Comp ##For CO2 Policy constraint right hand side development - power consumption by zone and each time step EP[:eCSCNetpowerConsumptionByAll] += ePower_Balance_CO2_Capture_Comp diff --git a/src/CSC/model/compression/co2_capture_compression_investment.jl b/src/CSC/model/compression/co2_capture_compression_investment.jl index c7ebf44c2..83ddb0f59 100644 --- a/src/CSC/model/compression/co2_capture_compression_investment.jl +++ b/src/CSC/model/compression/co2_capture_compression_investment.jl @@ -15,7 +15,7 @@ received this license file. If not, see . """ @doc raw""" - co2_capture_compression_investment(EP::Model, inputs::Dict, setup::Dict) + co2_capture_compression_investment(EP::Model, inputs::Dict, setup::Dict) This module defines the total fixed cost (Investment + Fixed O&M) of compressing the CO2 after capture by DAC @@ -23,7 +23,7 @@ Sets up constraints common to all CO2 compression resources. This function defines the expressions and constraints keeping track of total available CO2 compression capacity $y_{k}^{\textrm{C,COMP}}$ as well as constraints on capacity. -The expression defined in this file named after ```vCapacity\textunderscore{CO2}\textunderscore{Caputure}\textunderscore{Comp}\textunderscore{per}\textunderscore{type}``` covers all variables $y_{k}^{\textrm{C,DAC}}$. +The expression defined in this file named after ```vCapacity\textunderscore CO2\textunderscore Caputure\textunderscore Comp\textunderscore per\textunderscore type``` covers all variables $y_{k}^{\textrm{C,DAC}}$. The total capacity of each CO2 compression resource is defined as the sum of newly invested capacity based on the assumption there are no existing CO2 compression resources. diff --git a/src/CSC/model/core/DAC_investment.jl b/src/CSC/model/core/DAC_investment.jl index 3192b3637..4a76faa13 100644 --- a/src/CSC/model/core/DAC_investment.jl +++ b/src/CSC/model/core/DAC_investment.jl @@ -15,13 +15,13 @@ received this license file. If not, see . """ @doc raw""" - DAC_investment(EP::Model, inputs::Dict, setup::Dict) + DAC_investment(EP::Model, inputs::Dict, setup::Dict) Sets up constraints common to all DAC resources. This function defines the expressions and constraints keeping track of total available DAC CO2 capture capacity $y_{d}^{\textrm{C,DAC}}$ as well as constraints on capacity. -The expression defined in this file named after ```vCapacity\textunderscore{DAC}\textunderscore{per}\textunderscore{type}``` covers all variables $y_{d}^{\textrm{C,DAC}}$. +The expression defined in this file named after ```vCapacity\textunderscore DAC\textunderscore per\textunderscore type``` covers all variables $y_{d}^{\textrm{C,DAC}}$. The total capacity of each DAC resource is defined as the sum of newly invested capacity based on the assumption there are no existing DAC resources. @@ -44,6 +44,7 @@ For resources where upper bound $\overline{y_{d}^{\textrm{C,DAC}}}$ and lower bo \underline{y_{d}^{\textrm{C,DAC}}} \leq y_{d}^{\textrm{C,DAC}} \leq \overline{y_{d}^{\textrm{C,DAC}}} \quad \forall d \in \mathcal{D} \end{equation*} ``` + """ function DAC_investment(EP::Model, inputs::Dict, setup::Dict) diff --git a/src/CSC/model/core/DAC_var_cost.jl b/src/CSC/model/core/DAC_var_cost.jl index b8f1f74c9..828257a1c 100644 --- a/src/CSC/model/core/DAC_var_cost.jl +++ b/src/CSC/model/core/DAC_var_cost.jl @@ -15,13 +15,13 @@ received this license file. If not, see . """ @doc raw""" - DAC_var_cost(EP::Model, inputs::Dict, setup::Dict) + DAC_var_cost(EP::Model, inputs::Dict, setup::Dict) Sets up variables common to all direct air capture (DAC) resources. This module defines the DAC decision variable $x_{d,z,t}^{\textrm{C,DAC}} \forall k \in \mathcal{K}, z \in \mathcal{Z}, t \in \mathcal{T}$, representing CO2 injected into the grid by DAC resource $d$ in zone $z$ at time period $t$. -The variable defined in this file named after ```vDAC\textunderscore{CO2}\textunderscore{Captured}``` covers all variables $x_{d,z,t}^{\textrm{C,DAC}}$. +The variable defined in this file named after ```$vDAC\textunderscore CO2\textunderscore Captured$``` covers all variables $x_{d,z,t}^{\textrm{C,DAC}}$. **Cost expressions** @@ -34,6 +34,7 @@ This module additionally defines contributions to the objective function from va \end{equation*} ``` """ + function DAC_var_cost(EP::Model, inputs::Dict, setup::Dict) println("DAC variable cost module") diff --git a/src/CSC/model/core/emissions_csc.jl b/src/CSC/model/core/emissions_csc.jl index d934aaffc..648992538 100644 --- a/src/CSC/model/core/emissions_csc.jl +++ b/src/CSC/model/core/emissions_csc.jl @@ -15,20 +15,21 @@ received this license file. If not, see . """ @doc raw""" - emissions_csc(EP::Model, inputs::Dict, setup::Dict) + emissions_csc(EP::Model, inputs::Dict, setup::Dict) -This function creates expression to add the CO2 emissions for carbon supply chain in each zone, which is subsequently added to the total emissions. - -These include emissions from fuel utilization in DAC minus CO2 captured by flue gas CCS and also pipeline losses. + This function creates expression to add the CO2 emissions for carbon supply chain in each zone, which is subsequently added to the total emissions. + + These include emissions from fuel utilization in DAC minus CO2 captured by flue gas CCS and also pipeline losses. -In addition, there is a constraint that specify that amount of CO2 that undergoes compression in each zone has to be equal to the amount of CO2 captured by DAC + In addition, there is a constraint that specify that amount of CO2 that undergoes compression in each zone has to be equal to the amount of CO2 captured by DAC -```math -\begin{equation*} - x_{z,t}^{\textrm{C,DAC}} = x_{z,t}^{\textrm{C,COMP}} \quad \forall z \in \mathcal{Z}, t \in \mathcal{T} -\end{equation*} -``` + ```math + \begin{equation*} + x_{z,t}^{\textrm{C,DAC}} = x_{z,t}^{\textrm{C,COMP}} \quad \forall z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} + ``` """ + function emissions_csc(EP::Model, inputs::Dict, setup::Dict) println("CO2 Emissions Module for CO2 Policy modularization") @@ -62,7 +63,7 @@ function emissions_csc(EP::Model, inputs::Dict, setup::Dict) @expression(EP, eDAC_Emissions_per_zone_per_time[z=1:Z, t=1:T], sum(eDAC_Fuel_CO2_Production_per_plant_per_time[k,t] for k in dfDAC[(dfDAC[!,:Zone].==z),:R_ID])) if setup["ModelCO2Pipelines"] ==1 & setup["CO2Pipeline_Loss"] ==1 - @expression(EP, eCSC_Emissions_per_zone_per_time[z=1:Z, t=1:T], EP[:eDAC_Emissions_per_zone_per_time][z,t] + EP[:eCO2Loss_Pipes_zt][z,t]) + @expression(EP, eCSC_Emissions_per_zone_per_time[z=1:Z, t=1:T], EP[:eDAC_Emissions_per_zone_per_time][z,t] + EP[:eCO2Loss_Pipes_Trunk_zt][z,t] + EP[:eCO2Loss_Pipes_Spur_zt][z,t]) else @expression(EP, eCSC_Emissions_per_zone_per_time[z=1:Z, t=1:T], EP[:eDAC_Emissions_per_zone_per_time][z,t]) end diff --git a/src/CSC/model/storage/co2_injection.jl b/src/CSC/model/storage/co2_injection.jl index db6d448cb..0b31a3bea 100644 --- a/src/CSC/model/storage/co2_injection.jl +++ b/src/CSC/model/storage/co2_injection.jl @@ -15,51 +15,64 @@ received this license file. If not, see . """ @doc raw""" - co2_injection(EP::Model, inputs::Dict,setup::Dict) + co2_injection(EP::Model, inputs::Dict, setup::Dict) -This module defines the CO2 injection decision variable $x_{s,z,t}^{\textrm{C,INJ}} \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T}$, representing CO2 injected into storage resource $s$ in zone $z$ at time period $t$. + This module defines the CO2 injection decision variable $x_{s,z,t}^{\textrm{C,INJ}} \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T}$, representing CO2 injected into storage resource $k$ at site $S$ at time period $t$. -The variable defined in this file named after ```vDAC\textunderscore{CO2}\textunderscore{Injected}``` covers all variables $x_{s,z,t}^{\textrm{C,INJ}}$. - -This module defines the power consumption decision variable $x_{z,t}^{\textrm{E,INJ}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power consumed by CO2 injection in zone $z$ at time period $t$. - -The variable defined in this file named after ```vPower\textunderscore{CO2}\textunderscore{Injection}``` cover variable $x_{z,t}^{E,INJ}$. - -**Cost expressions** - -This module additionally defines contributions to the objective function from variable costs of CO2 injection (variable OM) from all resources over all time periods. - -```math -\begin{equation*} - \textrm{C}^{\textrm{C,INJ,o}} = \sum_{s \in \mathcal{S}} \sum_{t \in \mathcal{T}} \omega_t \times \textrm{c}_{s}^{\textrm{INJ,VOM}} \times x_{s,z,t}^{\textrm{C,INJ}} -\end{equation*} -``` - -**Minimum and maximum injection output hourly** - -For resources where upper bound $\overline{x_{s}^{\textrm{C,INJ}}}$ of injection rate is defined, then we impose constraints on minimum and maximum injection rate + The variable defined in this file named after ```$vDAC\textunderscore CO2\textunderscore Injected$``` covers all variables $x_{s,z,t}^{\textrm{C,INJ}}$. + + This module defines the power consumption decision variable $x_{s,z,t}^{\textrm{E,INJ}} \forall z\in \mathcal{Z}, t \in \mathcal{T}$, representing power consumed by CO2 injection at site $s$ in zone $z$ at time period $t$. + + The variable defined in this file named after ```vPower\textunderscore CO2\textunderscore Injection``` cover variable $x_{s,z,t}^{E,INJ}$. + + **Cost expressions** + + This module additionally defines contributions to the objective function from variable costs of CO2 injection (variable OM) from all resources over all time periods. + + ```math + \begin{equation*} + \textrm{C}^{\textrm{C,INJ,o}} = \sum_{s \in \mathcal{S}} \sum_{t \in \mathcal{T}} \omega_t \times \textrm{c}_{s}^{\textrm{INJ,VOM}} \times x_{s,z,t}^{\textrm{C,INJ}} + \end{equation*} + ``` + + **Minimum and maximum injection output hourly** + + For resources where upper bound $\overline{x_{s}^{\textrm{C,INJ}}}$ of injection rate is defined, then we impose constraints on minimum and maximum injection rate + + + ```math + \begin{equation*} + x_{s,z,t}^{\textrm{C,INJ}} \geq \underline{R_{s,z}^{\textrm{C,INJ}}} \times \overline{x_{s,z,t}^{\textrm{INJ}}} \quad \forall s \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} + ``` + + ```math + \begin{equation*} + x_{s,z,t}^{\textrm{C,INJ}} \leq \overline{R_{s,z}^{\textrm{C,INJ}}} \times \overline{x_{s,z,t}^{\textrm{INJ}}} \quad \forall s \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} + ``` + Additionally, CO2 is injected into a site only through the flow on the spur pipelines. This is expressed as follows -```math -\begin{equation*} - x_{s,z,t}^{\textrm{C,INJ}} \geq \underline{R_{s,z}^{\textrm{C,INJ}}} \times \overline{x_{s,z,t}^{\textrm{INJ}}} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} -\end{equation*} -``` + \begin{equation*} + x_{s,z,t}^{\textrm{C,INJ}} = \sum_{l^S \in P^{Spur}_{Map}}Flow^{Spur}_{l^S(z, t \rightarrow s,t)} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} -```math -\begin{equation*} - x_{s,z,t}^{\textrm{C,INJ}} \leq \overline{R_{s,z}^{\textrm{C,INJ}}} \times \overline{x_{s,z,t}^{\textrm{INJ}}} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} -\end{equation*} -``` + The flow of CO2 into a site is also constrained by the maximum hourly flow on the spur pipelines -**Maximum injection per year according to CO2 storage capacity per year** + \begin{equation*} + \sum_{s \in S}x_{i,z \rightarrow s,t}^{\textrm{C,Spur}} \leq \overline{R_{s,z}^{\textrm{C,INJ}}} \times \overline{x_{s,z,t}^{\textrm{INJ}}} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z}, t \in \mathcal{T} + \end{equation*} -```math -\begin{equation*} - \sum_{t \in \mathcal{T}} x_{s,z,t}^{\textrm{C,INJ}} \leq y_{s,z}^{\textrm{C,STO}} -\end{equation*} -``` + **Maximum injection per year according to CO2 storage capacity per year** + + ```math + \begin{equation*} + \sum_{t \in \mathcal{T}} x_{s,z,t}^{\textrm{C,INJ}} \leq y_{s,z}^{\textrm{C,STO}} + \end{equation*} + ``` """ + function co2_injection(EP::Model, inputs::Dict,setup::Dict) #Rename CO2Storage dataframe @@ -69,6 +82,12 @@ function co2_injection(EP::Model, inputs::Dict,setup::Dict) T = inputs["T"] # Number of time steps (hours) Z = inputs["Z"] # Number of zones + S = inputs["S"] # Number of CO2 Storage Sites + + ## Adding Fields from CO2 Pipelines ## + CO2_P = inputs["Spur_CO2_P"] # Number of Spur Pipelines + CO2_Pipe_Map = inputs["CO2_Spur_Pipe_Map"] + ##################################################################################################################################### ##Variables #CO2 injected into geological sequestration from carbon storage resource k (tonnes of CO2/hr) in time t @@ -87,21 +106,22 @@ function co2_injection(EP::Model, inputs::Dict,setup::Dict) sum(EP[:vPower_CO2_Injection][k,t] for k in dfCO2Storage[dfCO2Storage[!,:Zone].==z,:][!,:R_ID])) #Add to power balance to take power away from generated - EP[:ePowerBalance] += -ePower_Balance_CO2_Storage + EP[:ePowerBalance] -= ePower_Balance_CO2_Storage ##For CO2 Policy constraint right hand side development - power consumption by zone and each time step EP[:eCSCNetpowerConsumptionByAll] += ePower_Balance_CO2_Storage #CO2 Balance expressions - @expression(EP, eStored_Captured_CO2[t=1:T, z=1:Z], - sum(EP[:vCO2_Injected][k,t] for k in dfCO2Storage[(dfCO2Storage[!,:Zone].==z),:R_ID])) + @expression(EP, eStored_Captured_CO2[t=1:T, z=1:S], + sum(EP[:vCO2_Injected][k,t] for k in dfCO2Storage[(dfCO2Storage[!,:Site].==z),:R_ID])) #ADD TO CO2 BALANCE - EP[:eCaptured_CO2_Balance] -= eStored_Captured_CO2 + EP[:eCO2Store_Flow_Balance] -= eStored_Captured_CO2 + ##Storage #Amount of carbon injected into geological sequestration in zone z at time t - @expression(EP, eCO2_Injected_per_zone[z=1:Z, t=1:T], sum(EP[:vCO2_Injected][k,t] for k in dfCO2Storage[(dfCO2Storage[!,:Zone].==z),:R_ID])) + @expression(EP, eCO2_Injected_per_zone[z=1:S, t=1:T], sum(EP[:vCO2_Injected][k,t] for k in dfCO2Storage[(dfCO2Storage[!,:Site].==z),:R_ID])) #Amount of carbon injected into geological sequestration in zone z at time t @expression(EP, eCO2_Injected_per_year[k=1:CO2_STOR_ALL], sum(inputs["omega"][t]*EP[:vCO2_Injected][k,t] for t in 1:T)) @@ -120,6 +140,23 @@ function co2_injection(EP::Model, inputs::Dict,setup::Dict) ############################################################################################################################### + ## Constraint that ensures that the summation of multiple pipelines injecting into a zone does not exceed its injection limit ## + eTotal_Flow_CO2_In = EP[:ePipeZoneCO2Demand_Inflow_Spur] + df_pipeInflow = DataFrame(eTotal_Flow_CO2_In, :auto) + df_pipeInflow = DataFrame(permutedims((df_pipeInflow))) + + for col in names(df_pipeInflow) + df_pipeInflow[!, col] = convert(Vector{AffExpr}, df_pipeInflow[!, col]) + end + + ePipeZoneTotalCO2InFlowOfCO2 = Matrix(df_pipeInflow) + + EP[:ePipeZoneTotalCO2InFlowOfCO2] = ePipeZoneTotalCO2InFlowOfCO2 + + @constraint(EP,cMax_Flow_in_per_time_per_type[k=1:CO2_STOR_ALL, t=1:T], EP[:ePipeZoneTotalCO2InFlowOfCO2][k,t] <= dfCO2Storage[!,:Max_injection_rate_tonne_per_hr][k] * dfCO2Storage[!,:CO2_Injection_Max_Output][k]) + + ################################################################################################################################### + #Variable Cost of CO2 Storage (Injection) if setup["ParameterScale"] ==1 @expression(EP, eVar_OM_CO2_Injection_per_type_per_time[k = 1:CO2_STOR_ALL,t = 1:T], diff --git a/src/CSC/model/storage/co2_storage_investment.jl b/src/CSC/model/storage/co2_storage_investment.jl index b689c9004..d40ae890a 100644 --- a/src/CSC/model/storage/co2_storage_investment.jl +++ b/src/CSC/model/storage/co2_storage_investment.jl @@ -15,37 +15,38 @@ received this license file. If not, see . """ @doc raw""" - co2_storage_investment(EP::Model, inputs::Dict, setup::Dict) + co2_storage_investment(EP::Model, inputs::Dict, setup:Dict) -This module defines the total fixed cost (Investment + Fixed O&M) of the CO2 storage infrastructure in the CO2 supply chain + This module defines the total fixed cost (Investment + Fixed O&M) of the CO2 storage infrastructure in the CO2 supply chain -Sets up constraints common to all CO2 storage resources. - -This function defines the expressions and constraints keeping track of total available CO2 storage capacity $y_{s}^{\textrm{C,STO}}$ (per year) as well as constraints on capacity. - -The expression defined in this file named after ```vCapacity\textunderscore{CO2}\textunderscore{Storage}\textunderscore{per}\textunderscore{type}``` covers all variables $y_{s}^{\textrm{C,STO}}$. - -The total capacity (per year) of each CO2 storage resource is defined as the sum of newly invested capacity (per year) based on the assumption there are no existing CO2 storage resources. - -**Cost expressions** - -This module additionally defines contributions to the objective function from investment costs of CO2 storage (fixed O\&M plus investment costs) from all resources $s \in \mathcal{S}$: - -```math -\begin{equation*} - \textrm{C}^{\textrm{C,STO,c}} = \sum_{s \in \mathcal{S}} \sum_{z \in \mathcal{Z}} y_{s, z}^{\textrm{C,STO}}\times \textrm{c}_{s}^{\textrm{STO,INV}} + \sum_{s \in \mathcal{S}} \sum_{z \in \mathcal{Z}} y_{g, z}^{\textrm{C,STO,total}} \times \textrm{c}_{s}^{\textrm{STO,FOM}} -\end{equation*} -``` + Sets up constraints common to all CO2 storage resources. + + This function defines the expressions and constraints keeping track of total available CO2 storage capacity $y_{s}^{\textrm{C,STO}}$ (per year) as well as constraints on capacity. + + The expression defined in this file named after ```vCapacity\textunderscore CO2\textunderscore Storage\textunderscore per\textunderscore type``` covers all variables $y_{s}^{\textrm{C,STO}}$. + + The total capacity (per year) of each CO2 storage resource is defined as the sum of newly invested capacity (per year) based on the assumption there are no existing CO2 storage resources. + + **Cost expressions** + + This module additionally defines contributions to the objective function from investment costs of CO2 storage (fixed O\&M plus investment costs) from all resources $s \in \mathcal{S}$: + + ```math + \begin{equation*} + \textrm{C}^{\textrm{C,STO,c}} = \sum_{s \in \mathcal{S}} \sum_{z \in \mathcal{Z}} y_{s, z}^{\textrm{C,STO}}\times \textrm{c}_{s}^{\textrm{STO,INV}} + \sum_{s \in \mathcal{S}} \sum_{z \in \mathcal{Z}} y_{g, z}^{\textrm{C,STO,total}} \times \textrm{c}_{s}^{\textrm{STO,FOM}} + \end{equation*} + ``` -**Constraints on CO2 storage capacity (per year)** + **Constraints on CO2 storage capacity (per year)** -For resources where upper bound $\overline{y_{s}^{\textrm{C,STO}}}$ and lower bound $\underline{y_{s}^{\textrm{C,STO}}}$ of capacity is defined, then we impose constraints on minimum and maximum storage capacity (per year). + For resources where upper bound $\overline{y_{s}^{\textrm{C,STO}}}$ and lower bound $\underline{y_{s}^{\textrm{C,STO}}}$ of capacity is defined, then we impose constraints on minimum and maximum storage capacity (per year). + + ```math + \begin{equation*} + \underline{y_{s,z}^{\textrm{C,STO}}} \leq y_{s,z}^{\textrm{C,STO}} \leq \overline{y_{s,z}^{\textrm{C,STO}}} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z} + \end{equation*} + ``` -```math -\begin{equation*} - \underline{y_{s,z}^{\textrm{C,STO}}} \leq y_{s,z}^{\textrm{C,STO}} \leq \overline{y_{s,z}^{\textrm{C,STO}}} \quad \forall k \in \mathcal{S}, z \in \mathcal{Z} -\end{equation*} -``` """ function co2_storage_investment(EP::Model, inputs::Dict, setup::Dict) #Model the capacity and cost of injecting the CO2 into geological storage, ignore the cost of the geological storage itself as assume it is naturally ocurring and very large so no limit to how much can be stored diff --git a/src/CSC/model/transmission/co2_pipeline.jl b/src/CSC/model/transmission/co2_pipeline.jl index 58c91c180..1e87327ee 100644 --- a/src/CSC/model/transmission/co2_pipeline.jl +++ b/src/CSC/model/transmission/co2_pipeline.jl @@ -17,79 +17,160 @@ received this license file. If not, see . @doc raw""" co2_pipeline(EP::Model, inputs::Dict, setup::Dict) -This function includes the variables, expressions and objective funtion of CO2 pipeline. + This function includes the variables, expressions and objective funtion of CO2 trunk and spur pipelines. -This function expresses CO2 exchange through pipeline i between two zones and can be split into CO2 delivering and flowing out. - -This module defines the CO2 pipeline construction decision variable $y_{i,z \rightarrow z^{\prime}}^{\textrm{C,PIP}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}$, representing newly constructed CO2 pipeline of type $i$ through path $z \rightarrow z^{\prime}$. + This function expresses CO2 exchange through trunk pipeline i between two zones which can be split into CO2 delivering and flowing out. + + This function also expresses CO2 exchange through spur pipeline i between a zone and a CO2 storage site. + + This module defines the CO2 trunk pipeline construction decision variable $y_{i,z \rightarrow z^{\prime}}^{\textrm{C,Trunk}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}$, representing newly constructed CO2 trunk pipeline of type $i$ through path $z \rightarrow z^{\prime}$. + + Similarly, the module also defines the CO2 spur pipeline construction decision variable $y_{i, z \rightarrow s}^{\textrm(C, Spur)} \forall i in \mathcal{I}, z \rightarrow s \in \mathcal{B}$, representing newly constructed CO2 spur pipeline of type $i$ through path $z \rightarrow s$. + + This module defines the CO2 trunk pipeline flow decision variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}, t \in \mathcal{T}$, representing CO2 flow via trunk pipeline of type $i$ through path $z \rightarrow z^{\prime}$ at time period $t$. + + This module defines the CO2 spur pipeline flow decision variable $x_{i,z \rightarrow s,t}^{\textrm{C,Spur}} \forall i \in \mathcal{I}, z \rightarrow s \in \mathcal{B}, t \in \mathcal{T}$, representing CO2 flow via spur pipeline of type $i$ through path $z \rightarrow s$ at time period $t$. + + This module defines the CO2 trunk pipeline storage level decision variable $U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}, t \in \mathcal{T}$, representing CO2 stored in trunk pipeline of type $i$ through path $z \rightarrow z^{\prime}$ at time period $t$. + + The variable defined in this file named after ```vCO2NPipe\_Trunk''' covers variable $y_{i,z \rightarrow z^{\prime}}^{\textrm{C,Trunk}}$ and the variable named after ```vCO2NPipe\_Spur''' covers variable $y_{i,z \rightarrow s}^{\textrm{C,Spur}}$ + + The variable defined in this file named after ```vCO2PipeFlow\_spur\_pos''' covers variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk+}}$, and the variable defined in this file named after ```vCO2PipeFlow\_spur\_pos''' covers the variable $x_{i,z \rightarrow s,t}^{\textrm{C,Spur+}}$ that covers the flow from a zone to the CO2 site. + + The variable defined in this file named after ```vCO2PipeFlow\_trunk\_neg''' covers variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk-}}$ + + The variable defined in this file named after ```vCO2PipeLevel\_Trunk``` covers variable $U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk}}$ + + **Cost expressions** + + This module additionally defines contributions to the objective function from investment costs of generation (fixed OM plus construction) from all trunk pipeline resources $i \in \mathcal{I}$: + + ```math + \begin{equation*} + \textrm{C}^{\textrm{C,Trunk,c}}=\delta_{i}^{\textrm{C,Trunk}} \sum_{i \in \mathbb{I}} \sum_{z \rightarrow z^{\prime} \in \mathbb{B}} \textrm{c}_{i}^{\textrm{C,Trunk}} \textrm{L}_{z \rightarrow z^{\prime}} l_{i,z \rightarrow z^{\prime}} + h_{i,z \rightarrow z^{\prime}, t}^{\textrm{C,Trunk}}=h_{i, z \rightarrow z^{\prime}, t}^{\textrm{C,Trunk+}}-h_{i, z \rightarrow z^{\prime}, t}^{\textrm{Trunk-}} \quad \forall i \in \mathbb{I}, z \rightarrow z^{\prime} \in \mathbb{B}, t \in \mathbb{T} + \end{equation*} + ``` + + This module additionally defines contributions to the objective function from investment costs of generation (fixed OM plus construction) from all spur pipeline resources $i \in \mathcal{I}$: + + ```math + \begin{equation*} + \textrm{C}^{\textrm{C,Spur,c}}=\delta_{i}^{\textrm{C,Spur}} \sum_{i \in \mathbb{I}} \sum_{z \rightarrow s \in \mathbb{B}} \textrm{c}_{i}^{\textrm{C,Spur}} \textrm{L}_{z \rightarrow s} l_{i,z \rightarrow s} + h_{i,z \rightarrow s, t}^{\textrm{C,Spur}}=h_{i, z \rightarrow s, t}^{\textrm{C,Spur}} \quad \forall i \in \mathbb{I}, z \rightarrow z^{\prime} \in \mathbb{B}, t \in \mathbb{T} + \end{equation*} + ``` + + The flow rate of CO2 through trunk pipeline type $i$ is capped by the operational limits of the pipeline, multiplied by the number of constructed pipeline $i$ + ```math + \begin{equation*} + \overline{\textrm{F}}_{i} l_{i,z \rightarrow z^{\prime}} \geq x_{i,z \rightarrow z^{\prime}, t}^{\textrm{\textrm{C,Trunk+}}}, x_{i,z \rightarrow z^{\prime}, t}^{\textrm{\textrm{C,Trunk-}}} \geq 0 \quad \forall i \in \mathbb{I}, z \rightarrow z^{\prime} \in \mathbb{B}, t \in \mathbb{T} + \end{equation*} + ``` + + The flow rate of CO2 through spur pipeline type $i$ is capped by the operational limits of the pipeline, multiplied by the number of constructed pipeline $i$ + + ```math + \begin{equation*} + \overline{\textrm{F}}_{i} l_{i,z \rightarrow s} \geq x_{i,z \rightarrow s, t}^{\textrm{\textrm{C,Spur}}} \geq 0 \quad \forall i \in \mathbb{I}, z \rightarrow s \in \mathbb{B}, t \in \mathbb{T} + \end{equation*} + ``` + + The trunk pipeline has storage capacity via line packing: + ```math + \begin{equation*} + \overline{\textrm{U}}_{i}^{\textrm{\textrm{C,Trunk}}} l_{i,z \rightarrow z^{\prime}} \geq -\sum_{\tau=t_{0}}^{t}\left(x_{i,z^{\prime} \rightarrow z, \tau}^{\textrm{\textrm{C,Trunk}}}+x_{i,z \rightarrow z^{\prime}, \tau}^{\textrm{\textrm{C,Trunk}}}\right) \Delta t \geq \underline{\textrm{R}}_{i}^{\textrm{\textrm{C,Trunk}}} \overline{\textrm{E}}_{i}^{\textrm{\textrm{C,Trunk}}} l_{i,z \rightarrow z^{\prime}} \\ + & \forall z^{\prime} \in \mathbb{Z}, z \in \mathbb{Z}, i \in \mathbb{I}, t \in \mathbb{T} + \end{equation*} + ``` + + The change of CO2 trunk pipeline storage inventory is modeled as follows: + ```math + \begin{equation*} + U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk}} - U_{i,z \rightarrow z^{\prime},t-1}^{\textrm{C, Trunk}} = x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk-}} + x_{i,z^{\prime} \rightarrow z,t}^{\textrm{C,Trunk-}} + \end{equation*} + ``` + + Lastly the equality constraint which defines the flow of CO2 captured at a zone is given by: + + ```math + \begin{equation*} + C^{Capture}_{z,t} = x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,Trunk}} + x_{i,z \rightarrow s,t}^{\textrm{C,Spur}} \quad \forall i \in \mathcal{I}, \forall z \in \mathcal{B}, \forall s \in \mathcal{S}, t \in \mathcal{T} + \end{equation*} + ``` -This module defines the CO2 pipeline flow decision variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}, t \in \mathcal{T}$, representing CO2 flow via pipeline of type $i$ through path $z \rightarrow z^{\prime}$ at time period $t$. +""" -This module defines the CO2 pipeline storage level decision variable $U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP}} \forall i \in \mathcal{I}, z \rightarrow z^{\prime} \in \mathcal{B}, t \in \mathcal{T}$, representing CO2 stored in pipeline of type $i$ through path $z \rightarrow z^{\prime}$ at time period $t$. +function co2_pipeline(EP::Model, inputs::Dict, setup::Dict) -The variable defined in this file named after ```vCO2NPipe``` covers variable $y_{i,z \rightarrow z^{\prime}}^{\textrm{C,PIP}}$. + println("CO2 Pipeline Module") -The variable defined in this file named after ```vCO2PipeFlow_pos``` covers variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP+}}$. + T = inputs["T"] # Model operating time steps + Z = inputs["Z"] # Model demand zones - assumed to be same for CO2 and electricity -The variable defined in this file named after ```vCO2PipeFlow_neg``` covers variable $x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP-}}$. + if setup["CO2NetworkExpansion"] == 1 + S = inputs["S"] # Number of CO2 Storage Sites + end -The variable defined in this file named after ```vCO2PipeLevel``` covers variable $U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP}}$. + INTERIOR_SUBPERIODS = inputs["INTERIOR_SUBPERIODS"] + START_SUBPERIODS = inputs["START_SUBPERIODS"] + hours_per_subperiod = inputs["hours_per_subperiod"] + + # Number of Trunk and Spur Pipelines + Trunk_CO2_P = inputs["Trunk_CO2_P"] + Spur_CO2_P = inputs["Spur_CO2_P"] -**Cost expressions** + # Specifying Trunk and Spur CO2 Pipe Maps + CO2_Trunk_Pipe_Map = inputs["CO2_Trunk_Pipe_Map"] + CO2_Spur_Pipe_Map = inputs["CO2_Spur_Pipe_Map"] -This module additionally defines contributions to the objective function from investment costs of generation (fixed OM plus construction) from all pipeline resources $i \in \mathcal{I}$: + # From the CO2_Spur_Pipe_Map the Source_CO2_Pipe_Map and Sink_CO2_Pipe_Map is inferred. This helps with creating unidirectional pipeline flows + Source_CO2_Spur_Pipe_Map = CO2_Spur_Pipe_Map[CO2_Spur_Pipe_Map.d .==1,:] + Sink_CO2_Spur_Pipe_Map = CO2_Spur_Pipe_Map[CO2_Spur_Pipe_Map.d .==-1, :] -```math -\begin{equation*} - \textrm{C}^{\textrm{C,PIP,c}}=\delta_{i}^{\textrm{C,PIP}} \sum_{i \in \mathbb{I}} \sum_{z \rightarrow z^{\prime} \in \mathbb{B}} \textrm{c}_{i}^{\textrm{C,PIP}} \textrm{L}_{z \rightarrow z^{\prime}} l_{i,z \rightarrow z^{\prime}} - h_{i,z \rightarrow z^{\prime}, t}^{\textrm{C,PIP}}=h_{i, z \rightarrow z^{\prime}, t}^{\textrm{C,PIP+}}-h_{i, z \rightarrow z^{\prime}, t}^{\textrm{PIP-}} \quad \forall i \in \mathbb{I}, z \rightarrow z^{\prime} \in \mathbb{B}, t \in \mathbb{T} -\end{equation*} -``` + ### Variables ### + + # Variable for Number of Trunk and Spur Pipelines + @variable(EP, vCO2NPipe_Trunk[p=1:Trunk_CO2_P] >= 0) + @variable(EP, vCO2NPipe_Spur[p=1:Spur_CO2_P] >= 0) -The flow rate of CO2 through pipeline type $i$ is capped by the operational limits of the pipeline, multiplied by the number of constructed pipeline $i$ -```math -\begin{equation*} - \overline{\textrm{F}}_{i} l_{i,z \rightarrow z^{\prime}} \geq x_{i,z \rightarrow z^{\prime}, t}^{\textrm{\textrm{C,PIP+}}}, x_{i,z \rightarrow z^{\prime}, t}^{\textrm{\textrm{C,PIP-}}} \geq 0 \quad \forall i \in \mathbb{I}, z \rightarrow z^{\prime} \in \mathbb{B}, t \in \mathbb{T} -\end{equation*} -``` + # Variable for PipeLevel (for CO2 Storage in Pipe) for Trunk and Spur Pipelines + @variable(EP, vCO2PipeLevel_Trunk[p=1:Trunk_CO2_P, t = 1:T] >= 0) + @variable(EP, vCO2PipeLevel_Spur[p=1:Spur_CO2_P, t = 1:T] >= 0) + + # Variable for Pipe Flow Positive Direction (Specifying Inflow into a zone) + @variable(EP, vCO2PipeFlow_trunk_pos[p=1:Trunk_CO2_P, t=1:T, d=[1,-1]] >= 0) -The pipeline has storage capacity via line packing: -```math -\begin{equation*} - \overline{\textrm{U}}_{i}^{\textrm{\text{C,PIP}}} l_{i,z \rightarrow z^{\prime}} \geq -\sum_{\tau=t_{0}}^{t}\left(x_{i,z^{\prime}\rightarrow z, \tau}^{\textrm{\textrm{C,PIP}}}+x_{i,z \rightarrow z^{\prime} \tau}^{\textrm{\textrm{C,PIP}}}\right)\Delta t \geq\underline{\textrm{R}}_{i}^{\textrm{\textrm{C,PIP}}}\overline{\textrm{E}}_{i}^{\textrm{\textrm{C,PIP}}} l_{i,z \rightarrow z^{\prime}} \forall z^{\prime} \in Z, z \in Z, i \in I, t \in T -\end{equation*} -``` + # DEV NOTE: Positive Notation for Spur Pipeline might not be required + # Spur Pipeline + @variable(EP, vCO2PipeFlow_spur_uni_pos[p=1:Spur_CO2_P, t=1:T] >= 0) -The change of CO2 pipeline storage inventory is modeled as follows: -```math -\begin{equation*} - U_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP}} - U_{i,z \rightarrow z^{\prime},t-1} = x_{i,z \rightarrow z^{\prime},t}^{\textrm{C,PIP-}} + x_{i,z^{\prime} \rightarrow z,t}^{\textrm{C,PIP-}} -\end{equation*} -``` -""" -function co2_pipeline(EP::Model, inputs::Dict, setup::Dict) + # Variable for Outflow from Zones through Pipes + # Trunk Pipeline + @variable(EP, vCO2PipeFlow_trunk_neg[p=1:Trunk_CO2_P, t=1:T, d=[1,-1]] >= 0) # Negative Pipeflow - println("CO2 Pipeline Module") + # Spur Pipeline + @variable(EP, vCO2PipeFlow_spur_uni_neg[p=1:Spur_CO2_P, t = 1:T] >= 0) - T = inputs["T"] # Model operating time steps - Z = inputs["Z"] # Model demand zones - assumed to be same for CO2 and electricity - INTERIOR_SUBPERIODS = inputs["INTERIOR_SUBPERIODS"] - START_SUBPERIODS = inputs["START_SUBPERIODS"] - hours_per_subperiod = inputs["hours_per_subperiod"] + # Variable for CO2 Loss Across time period and zones for Trunk and Spur Pipelines + # Trunk Pipeline + @variable(EP, vCO2Loss_trunk[t=1:T, z=1:Z] >= 0) - CO2_P = inputs["CO2_P"] # Number of CO2 Pipelines - CO2_Pipe_Map = inputs["CO2_Pipe_Map"] + # Spur Pipeline + @variable(EP, vCO2Loss_spur[t=1:T, z=1:Z] >= 0) - ### Variables ### - @variable(EP, vCO2NPipe[p=1:CO2_P] >= 0 ) #Number of Pipes - @variable(EP, vCO2PipeLevel[p=1:CO2_P, t = 1:T] >= 0 ) #Storage in the pipe - @variable(EP, vCO2PipeFlow_pos[p=1:CO2_P, t = 1:T, d = [1,-1]] >= 0) #positive pipeflow - @variable(EP, vCO2PipeFlow_neg[p=1:CO2_P, t = 1:T, d = [1,-1]] >= 0) #negative pipeflow - @variable(EP, vCO2Loss[t=1:T,z=1:Z] >= 0 ) #CO2 Loss in Pipe + #@variable(EP, vCO2PipeFlow_pos[p=1:CO2_P, t = 1:T, d = [1,-1]] >= 0) #positive pipeflow + #@variable(EP, vCO2PipeFlow_neg[p=1:CO2_P, t = 1:T, d = [1,-1]] >= 0) #negative pipeflow + #@variable(EP, vCO2Loss[t=1:T,z=1:Z] >= 0 ) #CO2 Loss in Pipe ### Expressions ### - #Calculate the number of new pipes - @expression(EP, eCO2NPipeNew[p = 1:CO2_P], vCO2NPipe[p] - inputs["pCO2_Pipe_No_Curr"][p]) + #Calculate the number of new trunk and spur pipelines + + # Trunk Pipelines + @expression(EP, eCO2NPipeNew_trunk[p = 1:Trunk_CO2_P], vCO2NPipe_Trunk[p] - inputs["Trunk_pCO2_Pipe_No_Curr"][p]) + + # Spur Pipelines + @expression(EP, eCO2NPipeNew_spur[p = 1:Spur_CO2_P], vCO2NPipe_Spur[p] - inputs["Spur_pCO2_Pipe_No_Curr"][p]) ## Objective Function Expressions ## # Capital cost of pipelines @@ -97,25 +178,30 @@ function co2_pipeline(EP::Model, inputs::Dict, setup::Dict) # ParameterScale = 0 --> objective function is in $ if setup["ParameterScale"] ==1 - @expression(EP, eCCO2Pipe, sum(eCO2NPipeNew[p] * inputs["pCAPEX_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:CO2_P) + sum(vCO2NPipe[p] * inputs["pFixed_OM_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:CO2_P)) + @expression(EP, eCCO2Pipe_Trunk, sum(eCO2NPipeNew_trunk[p] * inputs["Trunk_pCAPEX_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Trunk_CO2_P) + sum(vCO2NPipe_Trunk[p] * inputs["Trunk_pFixed_OM_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Trunk_CO2_P)) + @expression(EP, eCCO2Pipe_Spur, sum(eCO2NPipeNew_spur[p] * inputs["Spur_pCAPEX_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Spur_CO2_P) + sum(vCO2NPipe_Spur[p] * inputs["Spur_pFixed_OM_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Spur_CO2_P)) else - @expression(EP, eCCO2Pipe, sum(eCO2NPipeNew[p] * inputs["pCAPEX_CO2_Pipe"][p] for p = 1:CO2_P) + sum(vCO2NPipe[p] * inputs["pFixed_OM_CO2_Pipe"][p] for p = 1:CO2_P)) + @expression(EP, eCCO2Pipe_Trunk, sum(eCO2NPipeNew_trunk[p] * inputs["Trunk_pCAPEX_CO2_Pipe"][p] for p = 1:Trunk_CO2_P) + sum(vCO2NPipe_Trunk[p] * inputs["Trunk_pFixed_OM_CO2_Pipe"][p] for p = 1:Trunk_CO2_P)) + @expression(EP, eCCO2Pipe_Spur, sum(eCO2NPipeNew_spur[p] * inputs["Spur_pCAPEX_CO2_Pipe"][p] for p = 1:Spur_CO2_P) + sum(vCO2NPipe_Spur[p] * inputs["Spur_pFixed_OM_CO2_Pipe"][p] for p = 1:Spur_CO2_P)) end - EP[:eObj] += eCCO2Pipe + EP[:eObj] += eCCO2Pipe_Trunk + EP[:eObj] += eCCO2Pipe_Spur # Capital cost of booster compressors located along each pipeline - more booster compressors needed for longer pipelines than shorter pipelines # ParameterScale = 1 --> objective function is in million $ # ParameterScale = 0 --> objective function is in $ if setup["ParameterScale"] ==1 - @expression(EP, eCCO2CompPipe, sum(eCO2NPipeNew[p] * inputs["pCAPEX_Comp_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:CO2_P)) - + @expression(EP, eCCO2CompPipe_trunk, sum(eCO2NPipeNew_trunk[p] * inputs["Trunk_pCAPEX_Comp_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Trunk_CO2_P)) + @expression(EP, eCCO2CompPipe_spur, sum(eCO2NPipeNew_spur[p] * inputs["Spur_pCAPEX_Comp_CO2_Pipe"][p]/(ModelScalingFactor)^2 for p = 1:Spur_CO2_P)) else - @expression(EP, eCCO2CompPipe, sum(eCO2NPipeNew[p] * inputs["pCAPEX_Comp_CO2_Pipe"][p] for p = 1:CO2_P)) + @expression(EP, eCCO2CompPipe_trunk, sum(eCO2NPipeNew_trunk[p] * inputs["Trunk_pCAPEX_Comp_CO2_Pipe"][p] for p = 1:Trunk_CO2_P)) + @expression(EP, eCCO2CompPipe_spur, sum(eCO2NPipeNew_spur[p] * inputs["Spur_pCAPEX_Comp_CO2_Pipe"][p] for p = 1:Spur_CO2_P)) end - EP[:eObj] += eCCO2CompPipe + EP[:eObj] += eCCO2CompPipe_trunk + EP[:eObj] += eCCO2CompPipe_spur ## End Objective Function Expressions ## @@ -123,106 +209,207 @@ function co2_pipeline(EP::Model, inputs::Dict, setup::Dict) # Electrical energy requirement for pipeline operation # IF ParameterScale = 1, power system operation/capacity modeled in GW rather than MW, , no need to scale as MW/ton = GW/kton # IF ParameterScale = 0, power system operation/capacity modeled in MW so no scaling of CO2 related power consumption - @expression(EP, ePowerDemandCO2Pipe[t=1:T, z=1:Z], - sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pMWh_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - @expression(EP, ePowerDemandCO2Pipe_zt[z=1:Z,t=1:T], - sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pMWh_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, ePowerDemandCO2Pipe_Trunk[t=1:T, z=1:Z], + sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pMWh_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePowerDemandCO2Pipe_Spur[t=1:T, z=1:Z], + sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pMWh_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePowerDemandCO2Pipe_Trunk_zt[z=1:Z, t=1:T], + sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pMWh_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePowerDemandCO2Pipe_Spur_zt[z=1:Z, t=1:T], + sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pMWh_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) # Electrical energy requirement for booster compression # IF ParameterScale = 1, power system operation/capacity modeled in GW rather than MW, , no need to scale as MW/ton = GW/kton # IF ParameterScale = 0, power system operation/capacity modeled in MW so no scaling of CO2 related power consumption - @expression(EP, ePowerDemandCO2PipeCompression[t=1:T, z=1:Z], - sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pComp_MWh_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePowerDemandCO2PipeCompression_Trunk[t=1:T, z=1:Z], + sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pComp_MWh_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - @expression(EP, ePowerDemandCO2PipeCompression_zt[z=1:Z,t=1:T], - sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pComp_MWh_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, ePowerDemandCO2PipeCompression_Spur[t=1:T, z=1:Z], + sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pComp_MWh_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - EP[:ePowerBalance] += -ePowerDemandCO2Pipe - EP[:ePowerBalance] += -ePowerDemandCO2PipeCompression + @expression(EP, ePowerDemandCO2PipeCompression_Trunk_zt[z=1:Z,t=1:T], + sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pComp_MWh_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2Pipe - EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2PipeCompression + @expression(EP, ePowerDemandCO2PipeCompression_Spur_zt[z=1:Z, t=1:T], + sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pComp_MWh_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + EP[:ePowerBalance] -= ePowerDemandCO2Pipe_Trunk + EP[:ePowerBalance] -= ePowerDemandCO2PipeCompression_Trunk + + EP[:ePowerBalance] -= ePowerDemandCO2Pipe_Spur + EP[:ePowerBalance] -= ePowerDemandCO2PipeCompression_Spur + + EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2Pipe_Trunk + EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2Pipe_Spur + + EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2PipeCompression_Trunk + EP[:eCSCNetpowerConsumptionByAll] += ePowerDemandCO2PipeCompression_Spur ### Constraints ### # Constraints if setup["CO2PipeInteger"] == 1 - for p=1:CO2_P - set_integer.(vCO2NPipe[p]) + for p=1:Trunk_CO2_P + set_integer.(vCO2NPipe_Trunk[p]) + end + for p=1:Spur_CO2_P + set_integer.(vCO2NPipe_Spur[p]) end end # Modeling expansion of the pipleline network if setup["CO2NetworkExpansion"]==1 # If network expansion allowed Total no. of Pipes >= Existing no. of Pipe - @constraint(EP, cCO2NetworkExpansion[p in 1:CO2_P], EP[:eCO2NPipeNew][p] >= 0) + @constraint(EP, cCO2NetworkExpansion_Trunk[p in 1:Trunk_CO2_P], EP[:eCO2NPipeNew_trunk][p] >= 0) + @constraint(EP, cCO2NetworkExpansion_Spur[p in 1:Spur_CO2_P], EP[:eCO2NPipeNew_spur][p] >= 0) else # If network expansion is not alllowed Total no. of Pipes == Existing no. of Pipe - @constraint(EP, cCO2NetworkExpansion[p in 1:CO2_P], EP[:eCO2NPipeNew][p] == 0) + @constraint(EP, cCO2NetworkExpansion_Trunk[p in 1:Trunk_CO2_P], EP[:eCO2NPipeNew_trunk][p] == 0) + @constraint(EP, cCO2NetworkExpansion_Spur[p in 1:Spur_CO2_P], EP[:eCO2NPipeNew_spur][p] == 0) end # Modeling loss of CO2 in the piplelines if setup["CO2Pipeline_Loss"]==1 # If modeling CO2 loss from pipe - @expression(EP, eCO2Loss_Pipes_per_pipe[t=1:T,z=1:Z], sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, eCO2Loss_Pipes_per_pipe_Trunk[t=1:T,z=1:Z], sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - @expression(EP, eCO2Loss_Pipes[t=1:T,z=1:Z], sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) - @expression(EP, eCO2Loss_Pipes_zt[z=1:Z,t=1:T], sum(vCO2PipeFlow_neg[p,t,CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, eCO2Loss_Pipes_Trunk[t=1:T,z=1:Z], sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, eCO2Loss_Pipes_Trunk_zt[z=1:Z,t=1:T], sum(vCO2PipeFlow_trunk_neg[p,t,CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] * inputs["Trunk_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + # If modeling CO2 Loss from Pipe: For Spur Pipelines + @expression(EP, eCO2Loss_Pipes_per_pipe_Spur[t=1:T,z=1:Z], sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, eCO2Loss_Pipes_Spur[t=1:T,z=1:Z], sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + @expression(EP, eCO2Loss_Pipes_Spur_zt[z=1:Z,t=1:T], sum(vCO2PipeFlow_spur_uni_neg[p,t] * inputs["Spur_pLoss_tonne_per_tonne_CO2_Pipe"][p] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + else # If not modeling CO2 loss from pipe - @expression(EP, eCO2Loss_Pipes[t=1:T,z=1:Z], 0) - @expression(EP, eCO2Loss_Pipes_zt[z=1:Z,t=1:T], 0) - end + # Trunk Pipelines + @expression(EP, eCO2Loss_Pipes_Trunk[t=1:T,z=1:Z], 0) + @expression(EP, eCO2Loss_Pipes_Trunk_zt[z=1:Z,t=1:T], 0) - + # Spur Pipelines + @expression(EP, eCO2Loss_Pipes_Spur[t=1:T,z=1:Z], 0) + @expression(EP, eCO2Loss_Pipes_Spur_zt[z=1:Z,t=1:T], 0) + + end #Calculate net flow at each pipe-zone interfrace - @expression(EP, eCO2PipeFlow_net[p = 1:CO2_P, t = 1:T, d = [-1,1]], vCO2PipeFlow_pos[p,t,d] - vCO2PipeFlow_neg[p,t,d]*(1-inputs["pLoss_tonne_per_tonne_CO2_Pipe"][p])) + @expression(EP, eCO2PipeFlow_Trunk_net[p = 1:Trunk_CO2_P, t = 1:T, d = [-1,1]], vCO2PipeFlow_trunk_pos[p,t,d] - vCO2PipeFlow_trunk_neg[p,t,d]*(1-inputs["Trunk_pLoss_tonne_per_tonne_CO2_Pipe"][p])) - # CO2 balance - net flows of CO2 from between z and zz via pipeline p over time period t - @expression(EP, ePipeZoneCO2Demand_No_Loss[t=1:T,z=1:Z], - sum(eCO2PipeFlow_net[p,t, CO2_Pipe_Map[(CO2_Pipe_Map[!,:Zone] .== z) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] for p in CO2_Pipe_Map[CO2_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + ## DEV NOTE: We might not require the net flow for Spur Pipelines ## + @expression(EP, eCO2PipeFlow_Spur_net[p = 1:Spur_CO2_P, t = 1:T], vCO2PipeFlow_spur_uni_pos[p,t] - vCO2PipeFlow_spur_uni_neg[p,t]*(1-inputs["Spur_pLoss_tonne_per_tonne_CO2_Pipe"][p])) + + # Specifying expression for CO2 Outflows in Spur Pipelines + @expression(EP, eCO2PipeOutFlow_Spur[p=1:Spur_CO2_P, t = 1:T], vCO2PipeFlow_spur_uni_neg[p,t]) # CO2 balance - net flows of CO2 from between z and zz via pipeline p over time period t - @expression(EP, ePipeZoneCO2Demand[t=1:T,z=1:Z],ePipeZoneCO2Demand_No_Loss[t,z] - eCO2Loss_Pipes[t,z]) + @expression(EP, ePipeZoneCO2Demand_No_Loss_Trunk[t=1:T,z=1:Z], + sum(eCO2PipeFlow_Trunk_net[p,t, CO2_Trunk_Pipe_Map[(CO2_Trunk_Pipe_Map[!,:Zone] .== z) .& (CO2_Trunk_Pipe_Map[!,:pipe_no] .== p), :][!,:d][1]] for p in CO2_Trunk_Pipe_Map[CO2_Trunk_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + # CO2 balance - net flows of CO2 from between z and zz via pipeline p over time period t + @expression(EP, ePipeZoneCO2Demand_Trunk[t=1:T,z=1:Z],ePipeZoneCO2Demand_No_Loss_Trunk[t,z] - eCO2Loss_Pipes_Trunk[t,z]) + + # Specifying CO2 Balance for Spur Pipelines + @expression(EP, ePipeZoneCO2OutFlowDemand_No_Loss_Spur[t=1:T,z=1:Z], + sum(eCO2PipeOutFlow_Spur[p,t] for p in Source_CO2_Spur_Pipe_Map[Source_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePipeZoneCO2Demand_Outflow_Spur[t=1:T,z=1:Z], ePipeZoneCO2OutFlowDemand_No_Loss_Spur[t,z] - eCO2Loss_Pipes_Spur[t,z]) + + ##### NOTE: If we do want to account for losses they can be tracked in a new expression as follows ######## + ### New Expression ### + @expression(EP, eCO2PipeInFlow_Spur[p=1:Spur_CO2_P, t = 1:T], vCO2PipeFlow_spur_uni_neg[p,t] * (1 - inputs["Spur_pLoss_tonne_per_tonne_CO2_Pipe"][p])) + + + # Specifying expression for Pipe Zone CO2 Inflow Demand while ignoring losses + @expression(EP, ePipeZoneCO2InFlowDemand_No_Loss_Spur[t=1:T,z=1:S], + sum(eCO2PipeOutFlow_Spur[p,t] for p in Sink_CO2_Spur_Pipe_Map[Sink_CO2_Spur_Pipe_Map[!,:Zone].==z,:][!,:pipe_no])) + + @expression(EP, ePipeZoneCO2Demand_Inflow_Spur[t=1:T,z=1:S], ePipeZoneCO2InFlowDemand_No_Loss_Spur[t,z]) + + #### Adding a new expression here to be used later in co2_injection script #### + EP[:ePipeZoneCO2Demand_Inflow_Spur] = ePipeZoneCO2Demand_Inflow_Spur + + if setup["Exogeneous_CO2_Demand"] == 1 + EP[:eCaptured_CO2_Balance] -= inputs["CO2_D"] + end + + EP[:eCaptured_CO2_Balance] += ePipeZoneCO2Demand_Trunk + EP[:eCaptured_CO2_Balance] -= ePipeZoneCO2Demand_Outflow_Spur #EP[:eCaptured_CO2_Balance] -= eCO2Loss_Pipes #No need as we have already deducted the loss from the balance - EP[:eCaptured_CO2_Balance] += ePipeZoneCO2Demand + #EP[:eCaptured_CO2_Balance] += ePipeZoneCO2Demand if setup["ParameterScale"] ==1 #Pipe flow constraint - @constraint(EP, cMinCO2Pipeflow[d in [-1,1], p in 1:CO2_P, t=1:T], EP[:eCO2PipeFlow_net][p,t,d] >= -EP[:vCO2NPipe][p] * inputs["pCO2_Pipe_Max_Flow"][p]/ModelScalingFactor) - @constraint(EP, cMaxCO2Pipeflow[d in [-1,1], p in 1:CO2_P, t=1:T], EP[:eCO2PipeFlow_net][p,t,d] <= EP[:vCO2NPipe][p] * inputs["pCO2_Pipe_Max_Flow"][p]/ModelScalingFactor) + @constraint(EP, cMinCO2Pipeflow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], EP[:eCO2PipeFlow_Trunk_net][p,t,d] >= -EP[:vCO2NPipe_Trunk][p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p]/ModelScalingFactor) + @constraint(EP, cMaxCO2Pipeflow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], EP[:eCO2PipeFlow_Trunk_net][p,t,d] <= EP[:vCO2NPipe_Trunk][p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p]/ModelScalingFactor) #Constrain positive and negative pipe flows - @constraint(EP, cMaxPositiveCO2Flow[d in [-1,1], p in 1:CO2_P, t=1:T], vCO2NPipe[p] * inputs["pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_pos[p,t,d]/ModelScalingFactor) - @constraint(EP, cMaxNegativeCO2Flow[d in [-1,1], p in 1:CO2_P, t=1:T], vCO2NPipe[p] * inputs["pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_neg[p,t,d]/ModelScalingFactor) + @constraint(EP, cMaxPositiveCO2Flow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], vCO2NPipe_Trunk[p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_trunk_pos[p,t,d]/ModelScalingFactor) + @constraint(EP, cMaxNegativeCO2Flow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], vCO2NPipe_Trunk[p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_trunk_neg[p,t,d]/ModelScalingFactor) #Pipe level constraint - @constraint(EP, cMinCO2PipeLevel[p in 1:CO2_P, t=1:T], vCO2PipeLevel[p,t] >= inputs["pCO2_Pipe_Min_Cap"][p] * vCO2NPipe[p]/ModelScalingFactor) - @constraint(EP, cMaxCO2PipeLevel[p in 1:CO2_P, t=1:T], vCO2PipeLevel[p,t] <= inputs["pCO2_Pipe_Max_Cap"][p] * vCO2NPipe[p]/ModelScalingFactor) + @constraint(EP, cMinCO2PipeLevel_Trunk[p in 1:Trunk_CO2_P, t=1:T], vCO2PipeLevel_Trunk[p,t] >= inputs["Trunk_pCO2_Pipe_Min_Cap"][p] * vCO2NPipe_Trunk[p]/ModelScalingFactor) + @constraint(EP, cMaxCO2PipeLevel_Trunk[p in 1:Trunk_CO2_P, t=1:T], vCO2PipeLevel_Trunk[p,t] <= inputs["Trunk_pCO2_Pipe_Max_Cap"][p] * vCO2NPipe_Trunk[p]/ModelScalingFactor) + + ## For Spur Pipelines ## + + @constraint(EP, cMinCO2Pipeflow_Spur[p in 1:Spur_CO2_P, t=1:T], EP[:eCO2PipeOutFlow_Spur][p,t] >= 0) + @constraint(EP, cMaxCO2Pipeflow_Spur[p in 1:Spur_CO2_P, t=1:T], EP[:eCO2PipeOutFlow_Spur][p,t] <= EP[:vCO2NPipe_Spur][p] * inputs["Spur_pCO2_Pipe_Max_Flow"][p]/ModelScalingFactor) + @constraint(EP, cMaxCO2Outflow_Spur[p in 1:Spur_CO2_P, t = 1:T], vCO2NPipe_Spur[p] * inputs["Spur_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_spur_uni_neg[p,t]/ModelScalingFactor) + + #Pipe level constraint + @constraint(EP, cMinCO2PipeLevel_Spur[p in 1:Spur_CO2_P, t=1:T], vCO2PipeLevel_Spur[p,t] >= inputs["Spur_pCO2_Pipe_Min_Cap"][p] * vCO2NPipe_Spur[p]/ModelScalingFactor) + @constraint(EP, cMaxCO2PipeLevel_Spur[p in 1:Spur_CO2_P, t=1:T], vCO2PipeLevel_Spur[p,t] <= inputs["Spur_pCO2_Pipe_Max_Cap"][p] * vCO2NPipe_Spur[p]/ModelScalingFactor) + else #Pipe flow constraint - @constraint(EP, cMinCO2Pipeflow[d in [-1,1], p in 1:CO2_P, t=1:T], EP[:eCO2PipeFlow_net][p,t,d] >= -EP[:vCO2NPipe][p] * inputs["pCO2_Pipe_Max_Flow"][p]) - @constraint(EP, cMaxCO2Pipeflow[d in [-1,1], p in 1:CO2_P, t=1:T], EP[:eCO2PipeFlow_net][p,t,d] <= EP[:vCO2NPipe][p] * inputs["pCO2_Pipe_Max_Flow"][p]) + @constraint(EP, cMinCO2Pipeflow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], EP[:eCO2PipeFlow_Trunk_net][p,t,d] >= -EP[:vCO2NPipe_Trunk][p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p]) + @constraint(EP, cMaxCO2Pipeflow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], EP[:eCO2PipeFlow_Trunk_net][p,t,d] <= EP[:vCO2NPipe_Trunk][p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p]) #Constrain positive and negative pipe flows - @constraint(EP, cMaxPositiveCO2Flow[d in [-1,1], p in 1:CO2_P, t=1:T], vCO2NPipe[p] * inputs["pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_pos[p,t,d]) - @constraint(EP, cMaxNegativeCO2Flow[d in [-1,1], p in 1:CO2_P, t=1:T], vCO2NPipe[p] * inputs["pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_neg[p,t,d]) + @constraint(EP, cMaxPositiveCO2Flow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], vCO2NPipe_Trunk[p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_trunk_pos[p,t,d]) + @constraint(EP, cMaxNegativeCO2Flow_Trunk[d in [-1,1], p in 1:Trunk_CO2_P, t=1:T], vCO2NPipe_Trunk[p] * inputs["Trunk_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_trunk_neg[p,t,d]) #Pipe level constraint - @constraint(EP, cMinCO2PipeLevel[p in 1:CO2_P, t=1:T], vCO2PipeLevel[p,t] >= inputs["pCO2_Pipe_Min_Cap"][p] * vCO2NPipe[p]) - @constraint(EP, cMaxCO2PipeLevel[p in 1:CO2_P, t=1:T], vCO2PipeLevel[p,t] <= inputs["pCO2_Pipe_Max_Cap"][p] * vCO2NPipe[p]) + @constraint(EP, cMinCO2PipeLevel_Trunk[p in 1:Trunk_CO2_P, t=1:T], vCO2PipeLevel_Trunk[p,t] >= inputs["Trunk_pCO2_Pipe_Min_Cap"][p] * vCO2NPipe_Trunk[p]) + @constraint(EP, cMaxCO2PipeLevel_Trunk[p in 1:Trunk_CO2_P, t=1:T], vCO2PipeLevel_Trunk[p,t] <= inputs["Trunk_pCO2_Pipe_Max_Cap"][p] * vCO2NPipe_Trunk[p]) + + ## For Spur Pipelines ## + + @constraint(EP, cMinCO2Pipeflow_Spur[p in 1:Spur_CO2_P, t=1:T], EP[:eCO2PipeOutFlow_Spur][p,t] >= 0) + @constraint(EP, cMaxCO2Pipeflow_Spur[p in 1:Spur_CO2_P, t=1:T], EP[:eCO2PipeOutFlow_Spur][p,t] <= EP[:vCO2NPipe_Spur][p] * inputs["Spur_pCO2_Pipe_Max_Flow"][p]) + @constraint(EP, cMaxCO2Outflow_Spur[p in 1:Spur_CO2_P, t = 1:T], vCO2NPipe_Spur[p] * inputs["Spur_pCO2_Pipe_Max_Flow"][p] >= vCO2PipeFlow_spur_uni_neg[p,t]) + + #Pipe level constraint + @constraint(EP, cMinCO2PipeLevel_Spur[p in 1:Spur_CO2_P, t=1:T], vCO2PipeLevel_Spur[p,t] >= inputs["Spur_pCO2_Pipe_Min_Cap"][p] * vCO2NPipe_Spur[p]) + @constraint(EP, cMaxCO2PipeLevel_Spur[p in 1:Spur_CO2_P, t=1:T], vCO2PipeLevel_Spur[p,t] <= inputs["Spur_pCO2_Pipe_Max_Cap"][p] * vCO2NPipe_Spur[p]) + end #CO2 Balance in pipe - @constraint(EP, cCO2PipeBalanceStart[p in 1:CO2_P, t in START_SUBPERIODS], vCO2PipeLevel[p,t] == vCO2PipeLevel[p,t + hours_per_subperiod - 1] - eCO2PipeFlow_net[p,t, -1] - eCO2PipeFlow_net[p,t,1]) - @constraint(EP, cCO2PipeBalanceInterior[p in 1:CO2_P, t in INTERIOR_SUBPERIODS], vCO2PipeLevel[p,t] == vCO2PipeLevel[p,t - 1] - eCO2PipeFlow_net[p,t, -1] - eCO2PipeFlow_net[p,t,1]) + # Trunk Pipelines + @constraint(EP, cCO2PipeBalanceStart_Trunk[p in 1:Trunk_CO2_P, t in START_SUBPERIODS], vCO2PipeLevel_Trunk[p,t] == vCO2PipeLevel_Trunk[p,t + hours_per_subperiod - 1] - eCO2PipeFlow_Trunk_net[p,t, -1] - eCO2PipeFlow_Trunk_net[p,t,1]) + + @constraint(EP, cCO2PipeBalanceInterior_Trunk[p in 1:Trunk_CO2_P, t in INTERIOR_SUBPERIODS], vCO2PipeLevel_Trunk[p,t] == vCO2PipeLevel_Trunk[p,t - 1] - eCO2PipeFlow_Trunk_net[p,t, -1] - eCO2PipeFlow_Trunk_net[p,t,1]) + + @constraint(EP, cCO2PipesMaxNumber_Trunk[p in 1:Trunk_CO2_P], vCO2NPipe_Trunk[p] <= inputs["Trunk_pCO2_Pipe_No_Max"][p]) + + ## DEV NOTE: Line Packing is ignored for Spur Pipelines as it is assumed that its a delivery network ## + ## and CO2 would rather be stored in the final site ## + + # Spur Pipelines + @constraint(EP, cCO2PipesMaxNumber_Spur[p in 1:Spur_CO2_P], vCO2NPipe_Spur[p] <= inputs["Spur_pCO2_Pipe_No_Max"][p]) - @constraint(EP, cCO2PipesMaxNumber[p in 1:CO2_P], vCO2NPipe[p] <= inputs["pCO2_Pipe_No_Max"][p]) + ## Constraint related to CO2 Inflow (from Spur Pipelines) == CO2 Stored ## + EP[:eCO2Store_Flow_Balance] += ePipeZoneCO2Demand_Inflow_Spur return EP end diff --git a/src/CSC/write_outputs/write_CSC_costs.jl b/src/CSC/write_outputs/write_CSC_costs.jl index c31e56602..597a457bf 100644 --- a/src/CSC/write_outputs/write_CSC_costs.jl +++ b/src/CSC/write_outputs/write_CSC_costs.jl @@ -27,7 +27,7 @@ function write_CSC_costs(path::AbstractString, sep::AbstractString, inputs::Dict Z = inputs["Z"] # Number of zones T = inputs["T"] # Number of time steps (hours) - dfCost = DataFrame(Costs = ["cTotal", "cDACFix", "cDACVar", "cCO2Comp", "cCO2Stor", "cCO2Injection", "cCO2NetworkExp"]) + dfCost = DataFrame(Costs = ["cTotal", "cDACFix", "cDACVar", "cCO2Comp", "cCO2Stor", "cCO2Injection", "cCO2NetworkExp_Trunk", "cCO2NetworkExp_Spur"]) if setup["ParameterScale"] == 1 cDACVar = value(EP[:eVar_OM_DAC]) * ModelScalingFactor^2 cDACFix = value(EP[:eFixed_Cost_DAC_total]) * ModelScalingFactor^2 @@ -36,9 +36,11 @@ function write_CSC_costs(path::AbstractString, sep::AbstractString, inputs::Dict cCO2Injection= value(EP[:eVar_OM_CO2_Injection_total]) * ModelScalingFactor^2 if setup["ModelCO2Pipelines"] != 0 - cCO2NetworkExpansion = value(EP[:eCCO2Pipe]) * ModelScalingFactor^2 + cCO2NetworkExp_Trunk = value(EP[:eCCO2Pipe_Trunk]) * ModelScalingFactor^2 + cCO2NetworkExp_Spur = value(EP[:eCCO2Pipe_Spur]) * ModelScalingFactor^2 else - cCO2NetworkExpansion = 0 + cCO2NetworkExp_Trunk = 0 + cCO2NetworkExp_Spur = 0 end else @@ -48,17 +50,19 @@ function write_CSC_costs(path::AbstractString, sep::AbstractString, inputs::Dict cCO2Stor = value(EP[:eFixed_Cost_CO2_Storage_total]) cCO2Injection= value(EP[:eVar_OM_CO2_Injection_total]) if setup["ModelCO2Pipelines"] != 0 - cCO2NetworkExpansion = value(EP[:eCCO2Pipe]) + cCO2NetworkExp_Trunk = value(EP[:eCCO2Pipe_Trunk]) + cCO2NetworkExp_Spur = value(EP[:eCCO2Pipe_Spur]) else - cCO2NetworkExpansion = 0 + cCO2NetworkExp_Trunk = 0 + cCO2NetworkExp_Spur = 0 end end # Define total costs - cTotal = cDACFix + cDACVar + cCO2Comp + cCO2Stor + cCO2Injection + cCO2NetworkExpansion + cTotal = cDACFix + cDACVar + cCO2Comp + cCO2Stor + cCO2Injection + cCO2NetworkExp_Trunk + cCO2NetworkExp_Spur # Define total column, i.e. column 2 - dfCost[!,Symbol("Total")] = [cTotal, cDACFix, cDACVar, cCO2Comp, cCO2Stor, cCO2Injection, cCO2NetworkExpansion] + dfCost[!,Symbol("Total")] = [cTotal, cDACFix, cDACVar, cCO2Comp, cCO2Stor, cCO2Injection, cCO2NetworkExp_Trunk, cCO2NetworkExp_Spur] # Computing zonal cost breakdown by cost category for z in 1:Z @@ -68,6 +72,7 @@ function write_CSC_costs(path::AbstractString, sep::AbstractString, inputs::Dict tempCCO2Comp = 0 tempCCO2Stor = 0 tempCCO2Injection = 0 + for y in dfDAC[dfDAC[!,:Zone].==z,:][!,:R_ID] @@ -102,7 +107,7 @@ function write_CSC_costs(path::AbstractString, sep::AbstractString, inputs::Dict tempCCO2Injection = tempCCO2Injection * (ModelScalingFactor^2) end - dfCost[!,Symbol("Zone$z")] = [tempCTotal, tempCDACFix, tempCDACVar, tempCCO2Comp, tempCCO2Stor, tempCCO2Injection, "-"] + dfCost[!,Symbol("Zone$z")] = [tempCTotal, tempCDACFix, tempCDACVar, tempCCO2Comp, tempCCO2Stor, tempCCO2Injection, "-", "-"] end CSV.write(string(path,sep,"CSC_costs.csv"), dfCost) diff --git a/src/CSC/write_outputs/write_CSC_outputs.jl b/src/CSC/write_outputs/write_CSC_outputs.jl index 6cc4a9731..5a6b787aa 100644 --- a/src/CSC/write_outputs/write_CSC_outputs.jl +++ b/src/CSC/write_outputs/write_CSC_outputs.jl @@ -22,7 +22,7 @@ received this license file. If not, see . ## returns: n/a ################################################################################ @doc raw""" - write_CSC_outputs(EP::Model, genx_path::AbstractString, setup::Dict, inputs::Dict) + write_CSC_outputs(EP::Model, path::AbstractString, setup::Dict, inputs::Dict) Function (entry-point) for reporting the different output files of CO2 supply chain. From here, onward several other functions are called, each for writing specific output files, like costs, capacities, etc. """ @@ -56,14 +56,18 @@ function write_CSC_outputs(EP::Model, genx_path::AbstractString, setup::Dict, in write_CSC_costs(path, sep, inputs, setup, EP) write_co2_capture_capacity(path, sep, inputs, setup, EP) write_co2_emission_balance_zone(path, sep, inputs, setup, EP) - write_co2_storage_balance_zone(path, sep, inputs, setup, EP) + #write_co2_storage_balance_zone(path, sep, inputs, setup, EP) + write_co2_capture_outflow_balance(path, sep, inputs, setup, EP) write_co2_storage_balance(path, sep, inputs, setup, EP) + write_co2_spur_inflow_storage_balance(path, sep, inputs, setup, EP) write_co2_storage_capacity(path, sep, inputs, setup, EP) write_co2_total_injection(path, sep, inputs, setup, EP) if setup["ModelCO2Pipelines"] ==1 - write_co2_pipeline_flow(path, sep, inputs, setup, EP) - write_co2_pipeline_expansion(path, sep, inputs, setup, EP) + write_co2_trunk_pipeline_flow(path, sep, inputs, setup, EP) + write_co2_spur_pipeline_flow(path, sep, inputs, setup, EP) + write_co2_trunk_pipeline_expansion(path, sep, inputs, setup, EP) + write_co2_spur_pipeline_expansion(path, sep, inputs, setup, EP) end ## Print confirmation diff --git a/src/CSC/write_outputs/write_co2_capture_outflow_balance.jl b/src/CSC/write_outputs/write_co2_capture_outflow_balance.jl new file mode 100644 index 000000000..e3353228b --- /dev/null +++ b/src/CSC/write_outputs/write_co2_capture_outflow_balance.jl @@ -0,0 +1,153 @@ +""" +GenX: An Configurable Capacity Expansion Model +Copyright (C) 2021, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_capture_outflow_balance(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting co2 capture and outflow from zone across all zones +""" +function write_co2_capture_outflow_balance(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + + Z = inputs["Z"] # Number of zones + S = inputs["S"] # Number of CO2 Sites + + ## CO2 balance for each zone + dfCost = DataFrame(Costs = ["Power_CCS", "H2_CCS", "DAC_Capture", "DAC_Fuel_CCS", "Biorefinery_Capture", "Synfuel_Production_Capture", "Synfuel_Production_Consumption", "CO2_Trunk_Pipeline_Import", "CO2_Spur_Pipeline_Outflow", "CO2_Demand", "Total"]) + + Power_CCS = sum(sum(inputs["omega"].* (value.(EP[:ePower_CO2_captured_per_zone_per_time])[z,:])) for z in 1:Z) + + if setup["ModelH2"] == 1 + H2_CCS = sum(sum(inputs["omega"].* (value.(EP[:eHydrogen_CO2_captured_per_zone_per_time])[z,:])) for z in 1:Z) + else + H2_CCS = 0 + end + + DAC_Capture = sum(sum(inputs["omega"].* (value.(EP[:eDAC_CO2_Captured_per_zone_per_time])[z,:])) for z in 1:Z) + DAC_Fuel_CCS = sum(sum(inputs["omega"].* (value.(EP[:eDAC_Fuel_CO2_captured_per_zone_per_time])[z,:])) for z in 1:Z) + + if setup["ModelBIO"] == 1 + Biorefinery_Capture = sum(sum(inputs["omega"].* (value.(EP[:eBiorefinery_CO2_captured_per_zone_per_time])[z,:])) for z in 1:Z) + else + Biorefinery_Capture = 0 + end + + if setup["ModelSynFuels"] == 1 + Synfuel_Production_Capture = sum(sum(inputs["omega"].* (value.(EP[:eSyn_Fuels_CO2_Capture_Per_Zone_Per_Time])[z,:])) for z in 1:Z) + Synfuel_Production_Consumption = - sum(sum(inputs["omega"].* (value.(EP[:eSynFuelCO2Cons_Per_Zone_Per_Time])[z,:])) for z in 1:Z) + else + Synfuel_Production_Capture = 0 + Synfuel_Production_Consumption = 0 + end + + if setup["ModelCO2Pipelines"] == 1 + CO2_Trunk_Pipeline_Import = sum(sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Trunk])[:,z])) for z in 1:Z) + CO2_Spur_Pipeline_Outflow = - sum(sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Outflow_Spur])[:,z])) for z in 1:Z) + else + CO2_Trunk_Pipeline_Import = 0 + CO2_Spur_Pipeline_Outflow = 0 + end + + if setup["Exogeneous_CO2_Demand"] == 1 + CO2_Demand = sum(sum(inputs["omega"].* inputs["CO2_D"][:,z]) for z in 1:Z) + else + CO2_Demand = 0 + end + + # Define Annual Balance + cTotal = Power_CCS + H2_CCS + DAC_Capture + DAC_Fuel_CCS + Biorefinery_Capture + Synfuel_Production_Capture + Synfuel_Production_Consumption + CO2_Trunk_Pipeline_Import + CO2_Spur_Pipeline_Outflow + CO2_Demand + + if setup["ParameterScale"] == 1 + Power_CCS = Power_CCS * ModelScalingFactor + H2_CCS = H2_CCS * ModelScalingFactor + DAC_Capture = DAC_Capture * ModelScalingFactor + DAC_Fuel_CCS = DAC_Fuel_CCS * ModelScalingFactor + Biorefinery_Capture = Biorefinery_Capture * ModelScalingFactor + Synfuel_Production_Capture = Synfuel_Production_Capture * ModelScalingFactor + Synfuel_Production_Consumption = Synfuel_Production_Consumption * ModelScalingFactor + CO2_Trunk_Pipeline_Import = CO2_Trunk_Pipeline_Import * ModelScalingFactor + CO2_Spur_Pipeline_Outflow = CO2_Spur_Pipeline_Outflow * ModelScalingFactor + CO2_Demand = CO2_Demand * ModelScalingFactor + cTotal = cTotal * ModelScalingFactor + end + + # Define total column, i.e. column 2 + dfCost[!,Symbol("Total")] = [Power_CCS, H2_CCS, DAC_Capture, DAC_Fuel_CCS, Biorefinery_Capture, Synfuel_Production_Capture, Synfuel_Production_Consumption, CO2_Trunk_Pipeline_Import, CO2_Spur_Pipeline_Outflow, CO2_Demand, cTotal] + + ################################################################################################################################ + + for z in 1:Z + tempPower_CCS = 0 + tempH2_CCS = 0 + tempDAC_Capture = 0 + tempDAC_Fuel_CCS = 0 + tempBiorefinery_Capture = 0 + tempSynfuel_Production_Capture = 0 + tempSynfuel_Production_Consumption = 0 + tempCO2_Trunk_Pipeline_Import = 0 + tempCO2_Spur_Pipeline_Outflow = 0 + tempCO2_Demand = 0 + + tempPower_CCS = tempPower_CCS + sum(inputs["omega"].* (value.(EP[:ePower_CO2_captured_per_zone_per_time])[z,:])) + + if setup["ModelH2"] == 1 + tempH2_CCS = tempH2_CCS + sum(inputs["omega"].* (value.(EP[:eHydrogen_CO2_captured_per_zone_per_time])[z,:])) + end + + tempDAC_Capture = tempDAC_Capture + sum(inputs["omega"].* (value.(EP[:eDAC_CO2_Captured_per_zone_per_time])[z,:])) + tempDAC_Fuel_CCS = tempDAC_Fuel_CCS + sum(inputs["omega"].* (value.(EP[:eDAC_Fuel_CO2_captured_per_zone_per_time])[z,:])) + + if setup["ModelBIO"] == 1 + tempBiorefinery_Capture = tempBiorefinery_Capture + sum(inputs["omega"].* (value.(EP[:eBiorefinery_CO2_captured_per_zone_per_time])[z,:])) + end + + if setup["ModelSynFuels"] == 1 + tempSynfuel_Production_Capture = tempSynfuel_Production_Capture + sum(inputs["omega"].* (value.(EP[:eSyn_Fuels_CO2_Capture_Per_Zone_Per_Time])[z,:])) + tempSynfuel_Production_Consumption = tempSynfuel_Production_Consumption - sum(inputs["omega"].* (value.(EP[:eSynFuelCO2Cons_Per_Zone_Per_Time])[z,:])) + end + + if setup["ModelCO2Pipelines"] == 1 + tempCO2_Trunk_Pipeline_Import = tempCO2_Trunk_Pipeline_Import + sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Trunk])[:,z])) + tempCO2_Spur_Pipeline_Outflow = - tempCO2_Spur_Pipeline_Outflow - sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Outflow_Spur])[:,z])) + end + + if setup["Exogeneous_CO2_Demand"] == 1 + #tempCO2_Demand = tempCO2_Demand + sum(inputs["omega"].* (inputs["CO2_D"][z,:])) + tempCO2_Demand = tempCO2_Demand + sum(inputs["omega"].* (inputs["CO2_D"][:,z])) + end + + tempCTotal = tempPower_CCS + tempH2_CCS + tempDAC_Capture + tempDAC_Fuel_CCS + tempBiorefinery_Capture + tempSynfuel_Production_Capture + tempSynfuel_Production_Consumption + tempCO2_Trunk_Pipeline_Import + tempCO2_Spur_Pipeline_Outflow + tempCO2_Demand + + if setup["ParameterScale"] == 1 + tempPower_CCS = tempPower_CCS * ModelScalingFactor + tempH2_CCS = tempH2_CCS * ModelScalingFactor + tempDAC_Capture = tempDAC_Capture * ModelScalingFactor + tempDAC_Fuel_CCS = tempDAC_Fuel_CCS * ModelScalingFactor + tempBiorefinery_Capture = tempBiorefinery_Capture * ModelScalingFactor + tempSynfuel_Production_Capture = tempSynfuel_Production_Capture * ModelScalingFactor + tempSynfuel_Production_Consumption = tempSynfuel_Production_Consumption * ModelScalingFactor + tempCO2_Trunk_Pipeline_Import = tempCO2_Trunk_Pipeline_Import * ModelScalingFactor + tempCO2_Spur_Pipeline_Outflow = tempCO2_Spur_Pipeline_Outflow * ModelScalingFactor + tempCO2_Demand = tempCO2_Demand * ModelScalingFactor + tempCTotal = tempCTotal * ModelScalingFactor + end + + dfCost[!,Symbol("Zone$z")] = [tempPower_CCS, tempH2_CCS, tempDAC_Capture, tempDAC_Fuel_CCS, tempBiorefinery_Capture, tempSynfuel_Production_Capture, tempSynfuel_Production_Consumption, tempCO2_Trunk_Pipeline_Import, tempCO2_Spur_Pipeline_Outflow, tempCO2_Demand, tempCTotal] + + end + + CSV.write(string(path,sep,"CSC_capture_outflow_balanace.csv"), dfCost) + +end \ No newline at end of file diff --git a/src/CSC/write_outputs/write_co2_emission_balance_system.jl b/src/CSC/write_outputs/write_co2_emission_balance_system.jl index 401d29e59..c36c3bc28 100644 --- a/src/CSC/write_outputs/write_co2_emission_balance_system.jl +++ b/src/CSC/write_outputs/write_co2_emission_balance_system.jl @@ -15,7 +15,7 @@ received this license file. If not, see . """ @doc raw""" - write_co2_emission_balance_system(path::AbstractString, inputs::Dict, setup::Dict, EP::Model) + write_co2_emission_balance_system(path::AbstractString, inputs::Dict, setup::Dict, EP::Model) Function for reporting CO2 balance of resources across the entire system """ @@ -39,14 +39,10 @@ function write_co2_emission_balance_system(path::AbstractString, inputs::Dict, s dfTemp1[t+rowoffset,2] = 0 end - if setup["ModelCSC"] == 1 - if setup["ModelCO2Pipelines"] == 1 && setup["CO2Pipeline_Loss"] == 1 - dfTemp1[t+rowoffset,3] = value(sum(EP[:eDAC_Emissions_per_zone_per_time][z,t] for z in 1:Z)) + value(sum(EP[:eCO2Loss_Pipes_zt][z,t] for z in 1:Z)) - value(sum(EP[:eDAC_CO2_Captured_per_zone_per_time][z,t] for z in 1:Z)) - else - dfTemp1[t+rowoffset,3] = value(sum(EP[:eDAC_Emissions_per_zone_per_time][z,t] for z in 1:Z)) - value(sum(EP[:eDAC_CO2_Captured_per_zone_per_time][z,t] for z in 1:Z)) - end + if setup["ModelCO2Pipelines"] == 1 && setup["CO2Pipeline_Loss"] == 1 + dfTemp1[t+rowoffset,3] = value(sum(EP[:eDAC_Emissions_per_zone_per_time][z,t] for z in 1:Z)) + value(sum(EP[:eCO2Loss_Pipes_Trunk_zt][z,t] for z in 1:Z)) + + value(sum(EP[:eCO2Loss_Pipes_Spur_zt][z,t] for z in 1:Z)) - value(sum(EP[:eDAC_CO2_Captured_per_zone_per_time][z,t] for z in 1:Z)) else - dfTemp1[t+rowoffset,3] = 0 + dfTemp1[t+rowoffset,3] = value(sum(EP[:eDAC_Emissions_per_zone_per_time][z,t] for z in 1:Z)) - value(sum(EP[:eDAC_CO2_Captured_per_zone_per_time][z,t] for z in 1:Z)) end if setup["ModelBIO"] == 1 @@ -59,7 +55,7 @@ function write_co2_emission_balance_system(path::AbstractString, inputs::Dict, s dfTemp1[t+rowoffset,6] = 0 end - if setup["ModelLiquidFuels"] == 1 + if setup["ModelSynFuels"] == 1 dfTemp1[t+rowoffset,7] = value(sum(EP[:eSyn_Fuels_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) + value(sum(EP[:eByProdConsCO2EmissionsByZone][z,t] for z in 1:Z)) dfTemp1[t+rowoffset,8] = value(sum(EP[:eLiquid_Fuels_Con_Diesel_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) + value(sum(EP[:eLiquid_Fuels_Con_Jetfuel_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) + value(sum(EP[:eLiquid_Fuels_Con_Gasoline_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) dfTemp1[t+rowoffset,9] = value(sum(EP[:eSyn_Fuels_Diesel_Cons_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) + value(sum(EP[:eSyn_Fuels_Jetfuel_Cons_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) + value(sum(EP[:eSyn_Fuels_Gasoline_Cons_CO2_Emissions_By_Zone][z,t] for z in 1:Z)) diff --git a/src/CSC/write_outputs/write_co2_emission_balance_zone.jl b/src/CSC/write_outputs/write_co2_emission_balance_zone.jl index 9e59a693a..6dbee6a63 100644 --- a/src/CSC/write_outputs/write_co2_emission_balance_zone.jl +++ b/src/CSC/write_outputs/write_co2_emission_balance_zone.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting CO2 balance of resources across different zones. """ + function write_co2_emission_balance_zone(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) T = inputs["T"] # Number of time steps (hours) @@ -28,8 +29,8 @@ function write_co2_emission_balance_zone(path::AbstractString, sep::AbstractStri dfCO2Balance = Array{Any} rowoffset=3 for z in 1:Z - dfTemp1 = Array{Any}(nothing, T+rowoffset, 20) - dfTemp1[1,1:size(dfTemp1,2)] = ["Power Emissions", "H2 Emissions", "DAC Emissions", "Biorefinery Emissions", "Bioresource Emissions", "DAC Capture", "Biomass Capture", "CO2 Pipeline Loss","Synfuel Production Emissions","Synfuel Byproducts Emissions","Conventional Diesel Utilization","Conventional Jetfuel Utilization","Conventional Gasoline Utilization","Syn Diesel Utilization","Syn Jetfuel Utilization","Syn Gasoline Utilization","Bio Diesel Utilization","Bio Jetfuel Utilization", "Bio Gasoline Utilization","Bio Ethanol Utilization"] + dfTemp1 = Array{Any}(nothing, T+rowoffset, 21) + dfTemp1[1,1:size(dfTemp1,2)] = ["Power Emissions", "H2 Emissions", "DAC Emissions", "Biorefinery Emissions", "Bioresource Emissions", "DAC Capture", "Biomass Capture", "CO2 Trunk Pipeline Loss", "CO2 Spur Pipeline Loss", "Synfuel Production Emissions","Synfuel Byproducts Emissions","Conventional Diesel Utilization","Conventional Jetfuel Utilization","Conventional Gasoline Utilization","Syn Diesel Utilization","Syn Jetfuel Utilization","Syn Gasoline Utilization","Bio Diesel Utilization","Bio Jetfuel Utilization", "Bio Gasoline Utilization","Bio Ethanol Utilization"] dfTemp1[2,1:size(dfTemp1,2)] = repeat([z],size(dfTemp1,2)) for t in 1:T dfTemp1[t+rowoffset,1] = value(EP[:eEmissionsByZone][z,t]) @@ -60,36 +61,37 @@ function write_co2_emission_balance_zone(path::AbstractString, sep::AbstractStri if setup["ModelCO2Pipelines"] == 1 && setup["CO2Pipeline_Loss"] == 1 - dfTemp1[t+rowoffset,8] = value(EP[:eCO2Loss_Pipes_zt][z,t]) + dfTemp1[t+rowoffset,8] = value(EP[:eCO2Loss_Pipes_Trunk_zt][z,t]) + dfTemp1[t+rowoffset,9] = value(EP[:eCO2Loss_Pipes_Spur_zt][z,t]) else dfTemp1[t+rowoffset,8] = 0 + dfTemp1[t+rowoffset,9] = 0 end - if setup["ModelLiquidFuels"] == 1 - dfTemp1[t+rowoffset,9] = value(EP[:eSyn_Fuels_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,10] = value(EP[:eByProdConsCO2EmissionsByZone][z,t]) - dfTemp1[t+rowoffset,11] = value(EP[:eLiquid_Fuels_Con_Diesel_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,12] = value(EP[:eLiquid_Fuels_Con_Jetfuel_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,13] = value(EP[:eLiquid_Fuels_Con_Gasoline_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,14] = value(EP[:eSyn_Fuels_Diesel_Cons_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,15] = value(EP[:eSyn_Fuels_Jetfuel_Cons_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,16] = value(EP[:eSyn_Fuels_Gasoline_Cons_CO2_Emissions_By_Zone][z,t]) + if setup["ModelSynFuels"] == 1 + dfTemp1[t+rowoffset,10] = value(EP[:eSyn_Fuels_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,11] = value(EP[:eByProdConsCO2EmissionsByZone][z,t]) + dfTemp1[t+rowoffset,12] = value(EP[:eLiquid_Fuels_Con_Diesel_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,13] = value(EP[:eLiquid_Fuels_Con_Jetfuel_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,14] = value(EP[:eLiquid_Fuels_Con_Gasoline_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,15] = value(EP[:eSyn_Fuels_Diesel_Cons_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,16] = value(EP[:eSyn_Fuels_Jetfuel_Cons_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,17] = value(EP[:eSyn_Fuels_Gasoline_Cons_CO2_Emissions_By_Zone][z,t]) if setup["ModelBIO"] == 1 - dfTemp1[t+rowoffset,17] = value(EP[:eBio_Fuels_Con_Diesel_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,18] = value(EP[:eBio_Fuels_Con_Jetfuel_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,19] = value(EP[:eBio_Fuels_Con_Gasoline_CO2_Emissions_By_Zone][z,t]) - dfTemp1[t+rowoffset,20] = value(EP[:eBio_Fuels_Con_Ethanol_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,18] = value(EP[:eBio_Fuels_Con_Diesel_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,19] = value(EP[:eBio_Fuels_Con_Jetfuel_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,20] = value(EP[:eBio_Fuels_Con_Gasoline_CO2_Emissions_By_Zone][z,t]) + dfTemp1[t+rowoffset,21] = value(EP[:eBio_Fuels_Con_Ethanol_CO2_Emissions_By_Zone][z,t]) else - dfTemp1[t+rowoffset,17] = 0 dfTemp1[t+rowoffset,18] = 0 dfTemp1[t+rowoffset,19] = 0 dfTemp1[t+rowoffset,20] = 0 + dfTemp1[t+rowoffset,21] = 0 end else - dfTemp1[t+rowoffset,9] = 0 dfTemp1[t+rowoffset,10] = 0 dfTemp1[t+rowoffset,11] = 0 dfTemp1[t+rowoffset,12] = 0 @@ -101,6 +103,7 @@ function write_co2_emission_balance_zone(path::AbstractString, sep::AbstractStri dfTemp1[t+rowoffset,18] = 0 dfTemp1[t+rowoffset,19] = 0 dfTemp1[t+rowoffset,20] = 0 + dfTemp1[t+rowoffset,21] = 0 end if setup["ParameterScale"] == 1 @@ -124,6 +127,7 @@ function write_co2_emission_balance_zone(path::AbstractString, sep::AbstractStri dfTemp1[t+rowoffset,18] = dfTemp1[t+rowoffset,18] * ModelScalingFactor dfTemp1[t+rowoffset,19] = dfTemp1[t+rowoffset,19] * ModelScalingFactor dfTemp1[t+rowoffset,20] = dfTemp1[t+rowoffset,20] * ModelScalingFactor + dfTemp1[t+rowoffset,21] = dfTemp1[t+rowoffset,21] * ModelScalingFactor end # DEV NOTE: need to add terms for electricity consumption from H2 balance end diff --git a/src/CSC/write_outputs/write_co2_pipeline_expansion.jl b/src/CSC/write_outputs/write_co2_pipeline_expansion.jl index 8a210d87a..6192e94b3 100644 --- a/src/CSC/write_outputs/write_co2_pipeline_expansion.jl +++ b/src/CSC/write_outputs/write_co2_pipeline_expansion.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting the expansion of CO2 pipelines. """ + function write_co2_pipeline_expansion(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) L = inputs["CO2_P"] # Number of CO2 pipelines diff --git a/src/CSC/write_outputs/write_co2_pipeline_flow.jl b/src/CSC/write_outputs/write_co2_pipeline_flow.jl index 1dcaa31a2..a78ae13fd 100644 --- a/src/CSC/write_outputs/write_co2_pipeline_flow.jl +++ b/src/CSC/write_outputs/write_co2_pipeline_flow.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting the CO2 flow via pipeliens. """ + function write_co2_pipeline_flow(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) T = inputs["T"] # Number of time steps (hours) Z = inputs["Z"] # Number of zones diff --git a/src/CSC/write_outputs/write_co2_spur_inflow_storage_balance.jl b/src/CSC/write_outputs/write_co2_spur_inflow_storage_balance.jl new file mode 100644 index 000000000..5a3e2205a --- /dev/null +++ b/src/CSC/write_outputs/write_co2_spur_inflow_storage_balance.jl @@ -0,0 +1,59 @@ +""" +GenX: An Configurable Capacity Expansion Model +Copyright (C) 2021, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_spur_inflow_storage_balance(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting CO2 storage and spur pipeline inflow balance across different zones with time. +""" + +function write_co2_spur_inflow_storage_balance(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + + T = inputs["T"] # Number of time steps (hours) + Z = inputs["Z"] # Number of zones + S = inputs["S"] # Number of CO2 Storage Sites + + ## CO2 balance for each zone + dfCO2StorBalance = Array{Any} + rowoffset=3 + for s in 1:S + dfTemp1 = Array{Any}(nothing, T+rowoffset, 2) + dfTemp1[1,1:size(dfTemp1,2)] = [ "CO2 Pipeline Inflow", "CO2 Storage"] + dfTemp1[2,1:size(dfTemp1,2)] = repeat([s],size(dfTemp1,2)) + for t in 1:T + + dfTemp1[t+rowoffset,1] = value(EP[:ePipeZoneCO2Demand_Inflow_Spur][t,s]) + + dfTemp1[t+rowoffset, 2] = -value(EP[:eCO2_Injected_per_zone][s,t]) + + if setup["ParameterScale"] == 1 + dfTemp1[t+rowoffset,1] = dfTemp1[t+rowoffset,1] * ModelScalingFactor + dfTemp1[t+rowoffset,2] = dfTemp1[t+rowoffset,2] * ModelScalingFactor + end + end + + if s==1 + dfCO2StorBalance = hcat(vcat(["", "Zone", "AnnualSum"], ["t$t" for t in 1:T]), dfTemp1) + else + dfCO2StorBalance = hcat(dfCO2StorBalance, dfTemp1) + end + end + for c in 2:size(dfCO2StorBalance,2) + dfCO2StorBalance[rowoffset,c]=sum(inputs["omega"].*dfCO2StorBalance[(rowoffset+1):size(dfCO2StorBalance,1),c]) + end + dfCO2StorBalance = DataFrame(dfCO2StorBalance, :auto) + CSV.write(string(path,sep,"CSC_spur_inflow_storage_balance.csv"), dfCO2StorBalance, writeheader=false) +end diff --git a/src/CSC/write_outputs/write_co2_spur_pipeline_expansion.jl b/src/CSC/write_outputs/write_co2_spur_pipeline_expansion.jl new file mode 100644 index 000000000..abf704bc4 --- /dev/null +++ b/src/CSC/write_outputs/write_co2_spur_pipeline_expansion.jl @@ -0,0 +1,51 @@ +""" +DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks +Copyright (C) 2022, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_spur_pipeline_expansion(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting co2 spur pipeline expansions +""" +function write_co2_spur_pipeline_expansion(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + L = inputs["Spur_CO2_P"] # Number of CO2 pipelines + + Existing_Trans_Cap = zeros(L) # Transmission network reinforcements in tonne/hour + transcap = zeros(L) # Transmission network reinforcements in tonne/hour + Pipes = zeros(L) # Transmission network reinforcements in tonne/hour + Fixed_Cost = zeros(L) + #Comp_Cost = zeros(L) + + + + for i in 1:L + Existing_Trans_Cap = inputs["Spur_pCO2_Pipe_Max_Flow"].*inputs["Spur_pCO2_Pipe_No_Curr"] + transcap[i] = (value.(EP[:vCO2NPipe_Spur][i]) -inputs["Spur_pCO2_Pipe_No_Curr"][i]).*inputs["Spur_pCO2_Pipe_Max_Flow"][i] + Pipes[i] = value.(EP[:vCO2NPipe_Spur][i]) + Fixed_Cost[i] = value.(EP[:eCO2NPipeNew_spur][i]) * inputs["Spur_pCAPEX_CO2_Pipe"][i] + value.(EP[:vCO2NPipe_Spur][i]) * inputs["Spur_pFixed_OM_CO2_Pipe"][i] + #Comp_Cost[i] = value.(EP[:eCO2NPipeNew][i]) * inputs["pCAPEX_Comp_CO2_Pipe"][i] + end + + dfTransCap = DataFrame( + Line = 1:L, + Existing_Trans_Capacity = convert(Array{Union{Missing,Float32}}, Existing_Trans_Cap), + New_Trans_Capacity = convert(Array{Union{Missing,Float32}}, transcap), + Total_Pipes = convert(Array{Union{Missing,Float32}}, Pipes), + Fixed_Cost_Pipes = convert(Array{Union{Missing,Float32}}, Fixed_Cost), + #Comp_Cost_pipes = convert(Array{Union{Missing,Float32}}, Comp_Cost), + ) + + CSV.write(string(path,sep,"CSC_spur_pipeline_expansion.csv"), dfTransCap) +end diff --git a/src/CSC/write_outputs/write_co2_spur_pipeline_flow.jl b/src/CSC/write_outputs/write_co2_spur_pipeline_flow.jl new file mode 100644 index 000000000..fab053500 --- /dev/null +++ b/src/CSC/write_outputs/write_co2_spur_pipeline_flow.jl @@ -0,0 +1,64 @@ +""" +DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks +Copyright (C) 2022, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_spur_pipeline_flow(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting co2 flow in the spur pipelines +""" +function write_co2_spur_pipeline_flow(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + T = inputs["T"] # Number of time steps (hours) + Z = inputs["Z"] # Number of zones + S = inputs["S"] # Number of CO2 Sites + CO2_P= inputs["Spur_CO2_P"] # Number of Spur Pipelines + CO2_Pipe_Map = inputs["CO2_Spur_Pipe_Map"] + + ## Power balance for each zone + dfCO2Pipeline = Array{Any} + rowoffset=3 + for p in 1:CO2_P + dfTemp1 = Array{Any}(nothing, T+rowoffset, 3) + dfTemp1[1,1:size(dfTemp1,2)] = ["Source_Zone_CO2_Outflow", + "Sink_Zone_CO2_Inflow", "Pipe_Level"] + + dfTemp1[2,1:size(dfTemp1,2)] = repeat([p],size(dfTemp1,2)) + + dfTemp1[3,1:size(dfTemp1,2)] = [[CO2_Pipe_Map[(CO2_Pipe_Map[!,:d] .== 1) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:zone_str][1]],[CO2_Pipe_Map[(CO2_Pipe_Map[!,:d] .== -1) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:zone_str][1]],"NA"] + + for t in 1:T + if setup["ParameterScale"]==1 + dfTemp1[t+rowoffset,1]= value.(EP[:eCO2PipeOutFlow_Spur][p,t])*ModelScalingFactor + dfTemp1[t+rowoffset,2] = - value.(EP[:eCO2PipeOutFlow_Spur][p,t])*ModelScalingFactor + dfTemp1[t+rowoffset,3] = value.(EP[:vCO2PipeLevel_Spur][p,t])*ModelScalingFactor + else + dfTemp1[t+rowoffset,1]= value.(EP[:eCO2PipeOutFlow_Spur][p,t]) + dfTemp1[t+rowoffset,2] = - value.(EP[:eCO2PipeOutFlow_Spur][p,t]) + dfTemp1[t+rowoffset,3] = value.(EP[:vCO2PipeLevel_Spur][p,t]) + end + + end + + if p==1 + dfCO2Pipeline = hcat(vcat(["", "Pipe", "Zone"], ["t$t" for t in 1:T]), dfTemp1) + else + dfCO2Pipeline = hcat(dfCO2Pipeline, dfTemp1) + end + end + + + dfCO2Pipeline = DataFrame(dfCO2Pipeline, :auto) + CSV.write(string(path,sep,"CSC_spur_pipeline_flow.csv"), dfCO2Pipeline, writeheader=false) +end diff --git a/src/CSC/write_outputs/write_co2_storage_balance.jl b/src/CSC/write_outputs/write_co2_storage_balance.jl index c35ac7a02..0ebf680ae 100644 --- a/src/CSC/write_outputs/write_co2_storage_balance.jl +++ b/src/CSC/write_outputs/write_co2_storage_balance.jl @@ -1,6 +1,6 @@ """ -DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks -Copyright (C) 2022, Massachusetts Institute of Technology +GenX: An Configurable Capacity Expansion Model +Copyright (C) 2021, Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting CO2 storage balance of resources across different zones with time. """ + function write_co2_storage_balance(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) T = inputs["T"] # Number of time steps (hours) @@ -28,9 +29,9 @@ function write_co2_storage_balance(path::AbstractString, sep::AbstractString, in dfCO2StorBalance = Array{Any} rowoffset=3 for z in 1:Z - dfTemp1 = Array{Any}(nothing, T+rowoffset, 9) - dfTemp1[1,1:size(dfTemp1,2)] = [ "Power CCS", "H2 CCS", "DAC Capture", "DAC Fuel CCS", "Biorefinery Capture","Synfuel Production Capture", "Synfuel Production Consumption", "CO2 Pipeline Import", - "CO2 Storage"] + dfTemp1 = Array{Any}(nothing, T+rowoffset, 10) + dfTemp1[1,1:size(dfTemp1,2)] = [ "Power CCS", "H2 CCS", "DAC Capture", "DAC Fuel CCS", "Biorefinery Capture","Synfuel Production Capture", "Synfuel Production Consumption", "CO2 Trunk Pipeline Import", + "CO2 Spur Pipeline Outflow", "CO2 Demand"] dfTemp1[2,1:size(dfTemp1,2)] = repeat([z],size(dfTemp1,2)) for t in 1:T @@ -52,7 +53,7 @@ function write_co2_storage_balance(path::AbstractString, sep::AbstractString, in dfTemp1[t+rowoffset,5] = 0 end - if setup["ModelLiquidFuels"] == 1 + if setup["ModelSynFuels"] == 1 dfTemp1[t+rowoffset,6] = value(EP[:eSyn_Fuels_CO2_Capture_Per_Zone_Per_Time][z,t]) dfTemp1[t+rowoffset,7] = - value(EP[:eSynFuelCO2Cons_Per_Zone_Per_Time][z,t]) else @@ -61,12 +62,23 @@ function write_co2_storage_balance(path::AbstractString, sep::AbstractString, in end if setup["ModelCO2Pipelines"] == 1 - dfTemp1[t+rowoffset,8] = value(EP[:ePipeZoneCO2Demand][t,z]) + dfTemp1[t+rowoffset,8] = value(EP[:ePipeZoneCO2Demand_Trunk][t,z]) else dfTemp1[t+rowoffset,8] = 0 end - dfTemp1[t+rowoffset,9] = - value(EP[:eCO2_Injected_per_zone][z,t]) + if setup["ModelCO2Pipelines"] == 1 + dfTemp1[t+rowoffset,9] = - value(EP[:ePipeZoneCO2Demand_Outflow_Spur][t,z]) + else + dfTemp1[t+rowoffset,9] = 0 + end + + if setup["Exogeneous_CO2_Demand"] == 1 + dfTemp1[t+rowoffset,10] = inputs["CO2_D"][t,z] + else + dfTemp1[t+rowoffset,10] = 0 + end + if setup["ParameterScale"] == 1 dfTemp1[t+rowoffset,1] = dfTemp1[t+rowoffset,1] * ModelScalingFactor @@ -78,6 +90,7 @@ function write_co2_storage_balance(path::AbstractString, sep::AbstractString, in dfTemp1[t+rowoffset,7] = dfTemp1[t+rowoffset,7] * ModelScalingFactor dfTemp1[t+rowoffset,8] = dfTemp1[t+rowoffset,8] * ModelScalingFactor dfTemp1[t+rowoffset,9] = dfTemp1[t+rowoffset,9] * ModelScalingFactor + dfTemp1[t+rowoffset,10] = dfTemp1[t+rowoffset,10] * ModelScalingFactor end # DEV NOTE: need to add terms for electricity consumption from H2 balance end @@ -91,5 +104,5 @@ function write_co2_storage_balance(path::AbstractString, sep::AbstractString, in dfCO2StorBalance[rowoffset,c]=sum(inputs["omega"].*dfCO2StorBalance[(rowoffset+1):size(dfCO2StorBalance,1),c]) end dfCO2StorBalance = DataFrame(dfCO2StorBalance, :auto) - CSV.write(string(path,sep,"Zone_CO2_storage_balance.csv"), dfCO2StorBalance, writeheader=false) + CSV.write(string(path,sep,"Zone_CO2_capture_outflow_balance.csv"), dfCO2StorBalance, writeheader=false) end diff --git a/src/CSC/write_outputs/write_co2_storage_balance_zone.jl b/src/CSC/write_outputs/write_co2_storage_balance_zone.jl index 082c3143b..93e1077b1 100644 --- a/src/CSC/write_outputs/write_co2_storage_balance_zone.jl +++ b/src/CSC/write_outputs/write_co2_storage_balance_zone.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting total CO2 storage balance across different zones. """ + function write_co2_storage_balance_zone(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) Z = inputs["Z"] # Number of zones @@ -41,7 +42,7 @@ function write_co2_storage_balance_zone(path::AbstractString, sep::AbstractStrin Biorefinery_Capture = 0 end - if setup["ModelLiquidFuels"] == 1 + if setup["ModelSynFuels"] == 1 Synfuel_Production_Capture = sum(sum(inputs["omega"].* (value.(EP[:eSyn_Fuels_CO2_Capture_Per_Zone_Per_Time])[z,:])) for z in 1:Z) Synfuel_Production_Consumption = - sum(sum(inputs["omega"].* (value.(EP[:eSynFuelCO2Cons_Per_Zone_Per_Time])[z,:])) for z in 1:Z) else @@ -50,7 +51,7 @@ function write_co2_storage_balance_zone(path::AbstractString, sep::AbstractStrin end if setup["ModelCO2Pipelines"] == 1 - CO2_Pipeline_Import = sum(sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand])[:,z])) for z in 1:Z) + CO2_Pipeline_Import = sum(sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Inflow_Spur])[:,z])) for z in 1:Z) else CO2_Pipeline_Import = 0 end @@ -102,13 +103,13 @@ function write_co2_storage_balance_zone(path::AbstractString, sep::AbstractStrin tempBiorefinery_Capture = tempBiorefinery_Capture + sum(inputs["omega"].* (value.(EP[:eBiorefinery_CO2_captured_per_zone_per_time])[z,:])) end - if setup["ModelLiquidFuels"] == 1 + if setup["ModelSynFuels"] == 1 tempSynfuel_Production_Capture = tempSynfuel_Production_Capture + sum(inputs["omega"].* (value.(EP[:eSyn_Fuels_CO2_Capture_Per_Zone_Per_Time])[z,:])) tempSynfuel_Production_Consumption = tempSynfuel_Production_Consumption - sum(inputs["omega"].* (value.(EP[:eSynFuelCO2Cons_Per_Zone_Per_Time])[z,:])) end if setup["ModelCO2Pipelines"] == 1 - tempCO2_Pipeline_Import = tempCO2_Pipeline_Import + sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand])[:,z])) + tempCO2_Pipeline_Import = tempCO2_Pipeline_Import + sum(inputs["omega"].* (value.(EP[:ePipeZoneCO2Demand_Inflow_Spur])[:,z])) end tempCO2_Storage = tempCO2_Storage - sum(inputs["omega"].* (value.(EP[:eCO2_Injected_per_zone])[z,:])) diff --git a/src/CSC/write_outputs/write_co2_storage_capacity.jl b/src/CSC/write_outputs/write_co2_storage_capacity.jl index 37e94c723..cda21bf65 100644 --- a/src/CSC/write_outputs/write_co2_storage_capacity.jl +++ b/src/CSC/write_outputs/write_co2_storage_capacity.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting CO2 storage capacity. """ + function write_co2_storage_capacity(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) # Capacity decisions dfCO2Storage = inputs["dfCO2Storage"] diff --git a/src/CSC/write_outputs/write_co2_total_injection.jl b/src/CSC/write_outputs/write_co2_total_injection.jl index 89ee68391..c203f7adc 100644 --- a/src/CSC/write_outputs/write_co2_total_injection.jl +++ b/src/CSC/write_outputs/write_co2_total_injection.jl @@ -19,6 +19,7 @@ received this license file. If not, see . Function for reporting CO2 storage injection. """ + function write_co2_total_injection(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) dfCO2Storage = inputs["dfCO2Storage"] @@ -33,13 +34,13 @@ function write_co2_total_injection(path::AbstractString, sep::AbstractString, in end dfCap = DataFrame( - Resource = inputs["CO2_STORAGE_NAME"], Zone = dfCO2Storage[!,:Zone], + Resource = inputs["CO2_STORAGE_NAME"], Site = dfCO2Storage[!,:Site], Injection_tonne_per_yr = capcapture[:], ) total = DataFrame( - Resource = "Total", Zone = "n/a", + Resource = "Total", Site = "n/a", Injection_tonne_per_yr = sum(dfCap[!,:Injection_tonne_per_yr]), ) diff --git a/src/CSC/write_outputs/write_co2_trunk_pipeline_expansion.jl b/src/CSC/write_outputs/write_co2_trunk_pipeline_expansion.jl new file mode 100644 index 000000000..c74faa231 --- /dev/null +++ b/src/CSC/write_outputs/write_co2_trunk_pipeline_expansion.jl @@ -0,0 +1,51 @@ +""" +DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks +Copyright (C) 2022, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_trunk_pipeline_expansion(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting co2 trunk pipeline expansion +""" +function write_co2_trunk_pipeline_expansion(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + L = inputs["Trunk_CO2_P"] # Number of CO2 pipelines + + Existing_Trans_Cap = zeros(L) # Transmission network reinforcements in tonne/hour + transcap = zeros(L) # Transmission network reinforcements in tonne/hour + Pipes = zeros(L) # Transmission network reinforcements in tonne/hour + Fixed_Cost = zeros(L) + #Comp_Cost = zeros(L) + + + + for i in 1:L + Existing_Trans_Cap = inputs["Trunk_pCO2_Pipe_Max_Flow"].*inputs["Trunk_pCO2_Pipe_No_Curr"] + transcap[i] = (value.(EP[:vCO2NPipe_Trunk][i]) -inputs["Trunk_pCO2_Pipe_No_Curr"][i]).*inputs["Trunk_pCO2_Pipe_Max_Flow"][i] + Pipes[i] = value.(EP[:vCO2NPipe_Trunk][i]) + Fixed_Cost[i] = value.(EP[:eCO2NPipeNew_trunk][i]) * inputs["Trunk_pCAPEX_CO2_Pipe"][i] + value.(EP[:vCO2NPipe_Trunk][i]) * inputs["Trunk_pFixed_OM_CO2_Pipe"][i] + #Comp_Cost[i] = value.(EP[:eCO2NPipeNew][i]) * inputs["pCAPEX_Comp_CO2_Pipe"][i] + end + + dfTransCap = DataFrame( + Line = 1:L, + Existing_Trans_Capacity = convert(Array{Union{Missing,Float32}}, Existing_Trans_Cap), + New_Trans_Capacity = convert(Array{Union{Missing,Float32}}, transcap), + Total_Pipes = convert(Array{Union{Missing,Float32}}, Pipes), + Fixed_Cost_Pipes = convert(Array{Union{Missing,Float32}}, Fixed_Cost), + #Comp_Cost_pipes = convert(Array{Union{Missing,Float32}}, Comp_Cost), + ) + + CSV.write(string(path,sep,"CSC_trunk_pipeline_expansion.csv"), dfTransCap) +end diff --git a/src/CSC/write_outputs/write_co2_trunk_pipeline_flow.jl b/src/CSC/write_outputs/write_co2_trunk_pipeline_flow.jl new file mode 100644 index 000000000..7e7b361df --- /dev/null +++ b/src/CSC/write_outputs/write_co2_trunk_pipeline_flow.jl @@ -0,0 +1,63 @@ +""" +DOLPHYN: Decision Optimization for Low-carbon Power and Hydrogen Networks +Copyright (C) 2022, Massachusetts Institute of Technology +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +A complete copy of the GNU General Public License v2 (GPLv2) is available +in LICENSE.txt. Users uncompressing this from an archive may not have +received this license file. If not, see . +""" + +@doc raw""" + write_co2_trunk_pipeline_flow(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + +Function for reporting co2 flow in the trunk pipelines +""" +function write_co2_trunk_pipeline_flow(path::AbstractString, sep::AbstractString, inputs::Dict, setup::Dict, EP::Model) + T = inputs["T"] # Number of time steps (hours) + Z = inputs["Z"] # Number of zones + CO2_P = inputs["Trunk_CO2_P"] # Number of Trunk Pipelines + CO2_Pipe_Map = inputs["CO2_Trunk_Pipe_Map"] + + + ## Power balance for each zone + dfCO2Pipeline = Array{Any} + rowoffset=3 + for p in 1:CO2_P + dfTemp1 = Array{Any}(nothing, T+rowoffset, 3) + dfTemp1[1,1:size(dfTemp1,2)] = ["Source_Zone_CO2_Net", + "Sink_Zone_CO2_Net", "Pipe_Level"] + + dfTemp1[2,1:size(dfTemp1,2)] = repeat([p],size(dfTemp1,2)) + + dfTemp1[3,1:size(dfTemp1,2)] = [[CO2_Pipe_Map[(CO2_Pipe_Map[!,:d] .== 1) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:zone_str][1]],[CO2_Pipe_Map[(CO2_Pipe_Map[!,:d] .== -1) .& (CO2_Pipe_Map[!,:pipe_no] .== p), :][!,:zone_str][1]],"NA"] + + for t in 1:T + if setup["ParameterScale"]==1 + dfTemp1[t+rowoffset,1]= value.(EP[:eCO2PipeFlow_Trunk_net][p,t,1])*ModelScalingFactor + dfTemp1[t+rowoffset,2] = value.(EP[:eCO2PipeFlow_Trunk_net][p,t,-1])*ModelScalingFactor + dfTemp1[t+rowoffset,3] = value.(EP[:vCO2PipeLevel_Trunk][p,t])*ModelScalingFactor + else + dfTemp1[t+rowoffset,1]= value.(EP[:eCO2PipeFlow_Trunk_net][p,t,1]) + dfTemp1[t+rowoffset,2] = value.(EP[:eCO2PipeFlow_Trunk_net][p,t,-1]) + dfTemp1[t+rowoffset,3] = value.(EP[:vCO2PipeLevel_Trunk][p,t]) + end + + end + + if p==1 + dfCO2Pipeline = hcat(vcat(["", "Pipe", "Zone"], ["t$t" for t in 1:T]), dfTemp1) + else + dfCO2Pipeline = hcat(dfCO2Pipeline, dfTemp1) + end + end + + dfCO2Pipeline = DataFrame(dfCO2Pipeline, :auto) + CSV.write(string(path,sep,"CSC_trunk_pipeline_flow.csv"), dfCO2Pipeline, writeheader=false) +end diff --git a/src/HSC/write_outputs/write_HSC_LCOH.jl b/src/HSC/write_outputs/write_HSC_LCOH.jl index 7db1a3ac6..0f64a6d10 100644 --- a/src/HSC/write_outputs/write_HSC_LCOH.jl +++ b/src/HSC/write_outputs/write_HSC_LCOH.jl @@ -135,13 +135,15 @@ function write_HSC_LCOH(path::AbstractString, sep::AbstractString, inputs::Dict, cCO2Injection = value(EP[:eVar_OM_CO2_Injection_total]) if setup["ModelCO2Pipelines"] == 1 - cCO2NetworkExpansion = value(EP[:eCCO2Pipe]) + cCO2TrunkNetworkExpansion = value(EP[:eCCO2Pipe_Trunk]) + cCO2SpurNetworkExpansion = value(EP[:eCCO2Pipe_Spur]) else - cCO2NetworkExpansion = 0 + cCO2TrunkNetworkExpansion = 0 + cCO2SpurNetworkExpansion = 0 end Blue_H2_CO2_Stor_Cost = (cCO2Injection + cCO2Stor) * Fraction_H2_CCS - Blue_H2_CO2_Pipeline_Cost = cCO2NetworkExpansion * Fraction_H2_CCS + Blue_H2_CO2_Pipeline_Cost = (cCO2TrunkNetworkExpansion + cCO2SpurNetworkExpansion) * Fraction_H2_CCS else Blue_H2_CO2_Stor_Cost = 0 @@ -324,4 +326,4 @@ function write_HSC_LCOH(path::AbstractString, sep::AbstractString, inputs::Dict, dfCost[!,Symbol("Grey_H2")] = [Grey_H2_Generation_Total, Grey_H2_Fixed_Cost_Total, Grey_H2_Var_Cost_Total, Grey_H2_Fuel_Cost_Total, Grey_H2_Electricity_Cost_Total, Grey_H2_CO2_MAC_Total, "-", "-", "-", "-", cGrey_H2_Total, Grey_H2_LCOH_Total] CSV.write(string(path,sep,"HSC_LCOH.csv"), dfCost) -end \ No newline at end of file +end diff --git a/src/configure_solver/configure_solver.jl b/src/configure_solver/configure_solver.jl index c95ec6277..d4b157c60 100755 --- a/src/configure_solver/configure_solver.jl +++ b/src/configure_solver/configure_solver.jl @@ -26,7 +26,7 @@ The "solver\_settings\_path" argument is a string which specifies the path to th """ function configure_solver(solver_name::String, solver_settings_path::String, solver::DataType=HiGHS.Optimizer) - + @show solver_name solver_name = lowercase(solver_name) solvers = Dict{String,Dict{String,Any}}( diff --git a/src/generate_model.jl b/src/generate_model.jl index 02a4b0542..3336bdd75 100644 --- a/src/generate_model.jl +++ b/src/generate_model.jl @@ -92,6 +92,10 @@ function generate_model(setup::Dict,inputs::Dict,OPTIMIZER::MOI.OptimizerWithAtt T = inputs["T"] # Number of time steps (hours) Z = inputs["Z"] # Number of zones - assumed to be same for power and hydrogen system + if setup["ModelCSC"] == 1 + S = inputs["S"] # Number of CO2 Storage Sites + end + ## Start pre-solve timer presolver_start_time = time() @@ -119,6 +123,7 @@ function generate_model(setup::Dict,inputs::Dict,OPTIMIZER::MOI.OptimizerWithAtt if setup["ModelCSC"] == 1 # Initialize CO2 Capture Balance Expression @expression(EP, eCaptured_CO2_Balance[t=1:T, z=1:Z], 0) + @expression(EP, eCO2Store_Flow_Balance[t=1:T, z=1:S], 0) # Note this is indexed by the number of CO2 Storage Sites end @@ -301,7 +306,12 @@ function generate_model(setup::Dict,inputs::Dict,OPTIMIZER::MOI.OptimizerWithAtt EP = co2_storage_investment(EP, inputs, setup) - if !isempty(inputs["CO2_STORAGE"]) + if setup["ModelCO2Pipelines"] == 1 + # model CO2 transmission via pipelines + EP = co2_pipeline(EP, inputs, setup) + end + + if !isempty(inputs["CO2_STORAGE"]) #model CO2 injection EP = co2_injection(EP, inputs, setup) end @@ -315,11 +325,6 @@ function generate_model(setup::Dict,inputs::Dict,OPTIMIZER::MOI.OptimizerWithAtt EP = co2_capture_compression(EP, inputs, setup) end - if setup["ModelCO2Pipelines"] == 1 - # model CO2 transmission via pipelines - EP = co2_pipeline(EP, inputs, setup) - end - # Direct emissions of various carbon capture sector resources EP = emissions_csc(EP, inputs,setup)