#' Read in GDX and calculate LCOE reporting used in convGDX2MIF_LCOE.
#'
#' This function provides a post-processing calculation of LCOE (Levelized Cost of Energy) for energy conversion technologies in REMIND.
#' In incluldes most technologies that generate secondary energy and the distribution technologies which convert secondary energy to final energy.
#' This script calculates two different types of LCOE: average LCOE (standing system) and marginal LCOE (new plant).
#' The average LCOE reflect the total cost incurred by the technology deployment in a specific time step divided by its energy output.
#' The marginal LCOE estimate the per-unit lifetime cost of the output if the model added another capacity of that technology in the respective time step.
#'
#'
#'
#' @param gdx a GDX object as created by readGDX, or the path to a gdx
#' @param output.type string to determine which output shall be produced.
#' Can be either "average" (returns only average LCOE),
#' "marginal" (returns only marginal LCOE), "both" (returns marginal and average LCOE) and
#' and "marginal detail" (returns table to trace back how marginal LCOE are calculated).
#' @return MAgPIE object - LCOE calculated by model post-processing. Two types a) standing system LCOE b) new plant LCOE.
#' @author Felix Schreyer, Robert Pietzcker, Lavinia Baumstark
#' @seealso \code{\link{convGDX2MIF_LCOE}}
#' @examples
#'
#' \dontrun{reportLCOE(gdx)}
#'
#' @export
#' @importFrom gdx readGDX
#' @importFrom magclass new.magpie dimSums getRegions getYears getNames setNames clean_magpie dimReduce as.magpie magpie_expand
#' @importFrom dplyr %>% mutate select rename group_by ungroup right_join filter full_join arrange summarise
#' @importFrom quitte as.quitte overwrite getRegs
#' @importFrom tidyr spread gather expand fill
reportLCOE <- function(gdx, output.type = "both"){
unit <- NULL
# test whether output.type defined
if (!output.type %in% c("marginal", "average", "both", "marginal detail")) {
print("Unknown output type. Please choose either marginal, average, both or marginal detail.")
return(new.magpie(cells_and_regions = "GLO",
years = c(seq(2005,2060,5),seq(2070,2110,10),2130,2150)))
}
# check whether key variables are there
# LCOE reporting does not make sense for old gdx
# where variables are missing and model structure is different
vm_capFac <- readGDX(gdx, "vm_capFac", field = "l", restore_zeros = F)
qm_balcapture <- readGDX(gdx,"q_balcapture",field="m", restore_zeros = F)
vm_co2CCS <- readGDX(gdx, "vm_co2CCS", field = "l", restore_zeros = F)
pm_emifac <- readGDX(gdx, "pm_emiFac", field = "l", restore_zeros = F)
v32_storloss <- readGDX(gdx, "v32_storloss", field = "l")
if (is.null(vm_capFac) | is.null(qm_balcapture) | is.null(vm_co2CCS) |
is.null(pm_emifac) | is.null(v32_storloss)) {
print("The gdx file is too old for generating a LCOE reporting...returning NULL")
return(new.magpie(cells_and_regions = "GLO",
years = c(seq(2005,2060,5),seq(2070,2110,10),2130,2150)))
}
# get module realizations
module2realisation <- readGDX(gdx, "module2realisation")
#initialize output array
LCOE.out <- NULL
# read in general data
s_twa2mwh <- readGDX(gdx,c("sm_TWa_2_MWh","s_TWa_2_MWh","s_twa2mwh"),format="first_found")
ttot <- as.numeric(readGDX(gdx,"ttot"))
ttot_before2005 <- paste0("y",ttot[which(ttot <= 2000)])
ttot_from2005 <- paste0("y",ttot[which(ttot >= 2005)])
########################################################
### A) Calculation of average (standing system) LCOE ###
########################################################
if (output.type %in% c("both", "average")) {
####### read in needed data #######################
## sets
opTimeYr <- readGDX(gdx,"opTimeYr")
opTimeYr2te <- readGDX(gdx,"opTimeYr2te")
temapse <- readGDX(gdx,"en2se")
temapall <- readGDX(gdx,c("en2en","temapall"),format="first_found")
teall2rlf <- readGDX(gdx,c("te2rlf","teall2rlf"),format="first_found")
te <- readGDX(gdx,"te")
te2stor <- readGDX(gdx,"VRE2teStor")
te2grid <- readGDX(gdx,"VRE2teGrid")
teVRE <- readGDX(gdx,"teVRE")
se2fe <- readGDX(gdx,"se2fe")
pe2se <- readGDX(gdx,"pe2se")
teCCS <- readGDX(gdx,"teCCS")
teNoCCS <- readGDX(gdx,"teNoCCS")
techp <- readGDX(gdx,c("teChp","techp"),format="first_found")
teReNoBio <- readGDX(gdx,"teReNoBio")
# switches
module2realisation <- readGDX(gdx, "module2realisation")
## parameter
p_omeg <- readGDX(gdx,c("pm_omeg","p_omeg"),format="first_found")
p_omeg <- p_omeg[opTimeYr2te]
pm_ts <- readGDX(gdx,"pm_ts")
pm_data <- readGDX(gdx,"pm_data")
pm_emifac <- readGDX(gdx,"pm_emifac", restore_zeros=F) # emission factor per technology
pm_priceCO2 <- readGDX(gdx,"pm_priceCO2") # co2 price
pm_eta_conv <- readGDX(gdx,"pm_eta_conv", restore_zeros=F) # efficiency oftechnologies with time-dependent eta
pm_dataeta <- readGDX(gdx,"pm_dataeta", restore_zeros=F)# efficiency of technologies with time-independent eta
## variables
v_directteinv <- readGDX(gdx,name=c("v_costInvTeDir","vm_costInvTeDir","v_directteinv"),field="l",format="first_found")[,ttot,]
vm_capEarlyReti <- readGDX(gdx,name=c("vm_capEarlyReti"),field="l",format="first_found")[,ttot,]
vm_deltaCap <- readGDX(gdx,name=c("vm_deltaCap"),field="l",format="first_found")[,ttot,]
vm_demPe <- readGDX(gdx,name=c("vm_demPe","v_pedem"),field="l",restore_zeros=FALSE,format="first_found")
vm_prodSe <- readGDX(gdx,name=c("vm_prodSe"),field="l",restore_zeros=FALSE,format="first_found")
v_investcost <- readGDX(gdx,name=c("vm_costTeCapital","v_costTeCapital","v_investcost"),field="l",format="first_found")[,ttot,]
vm_cap <- readGDX(gdx,name=c("vm_cap"),field="l",format="first_found")
vm_prodFe <- readGDX(gdx,name=c("vm_prodFe"),field="l",restore_zeros=FALSE,format="first_found")
v_emiTeDetail <- readGDX(gdx,name=c("vm_emiTeDetail","v_emiTeDetail"),field="l",restore_zeros=FALSE,format="first_found")
## equations
qm_pebal <- readGDX(gdx,name=c("q_balPe"),field="m",format="first_found")
qm_budget <- readGDX(gdx,name=c("qm_budget"),field="m",format="first_found")
# module-specific
# amount of curtailed electricity
if (any(module2realisation[,2] == "RLDC")) {
v32_curt <- readGDX(gdx,name=c("v32_curt"),field="l",restore_zeros=FALSE,format="first_found")
} else if (any(module2realisation[,2] == "IntC")) {
v32_curt <- v32_storloss[,ttot_from2005,getNames(vm_prodSe, dim=3)]
} else{
v32_curt <- 0
}
# # sensitivites (only for investment cost sensitivity runs)
# p_costFac <- readGDX(gdx,name=c("p_costFac"),react = "silent") # sensitivity factor
# if (is.null(p_costFac)) {
# p_costFac <- new.magpie(getRegions(v_directteinv),getYears(v_directteinv),getNames(p_omeg,dim=2), fill=1)
# }
####### calculate needed parameters ###############
# calculates annuity cost:
# annuity cost = 1/ sum_t (p_omeg(t) / 1.06^t) * direct investment cost
# t is in T which is the lifetime of the technology
# direct investment cost = directteinv or for past values (before 2005) (v_investcost * deltaCap)
# annuity represents (total investment cost + interest over lifetime) distributed equally over all years of lifetime
# # quick fix for h22ch4 problem
# if (! "h22ch4" %in% magclass::getNames(p_omeg,dim=2)) {
# te <- te[te!="h22ch4"]
# }
# get a representative region
reg1 <- getRegions(vm_prodSe)[1]
te_annuity <- new.magpie("GLO",names=magclass::getNames(p_omeg,dim=2))
for(a in magclass::getNames(p_omeg[reg1,,],dim=2)){
te_annuity[,,a] <- 1/dimSums(p_omeg[reg1,,a]/1.06**as.numeric(magclass::getNames(p_omeg[reg1,,a],dim=1)),dim=3.1)
}
te_inv_annuity <- 1e+12 * te_annuity[,,te] *
mbind(
v_investcost[,ttot_before2005,te] * dimSums(vm_deltaCap[teall2rlf][,ttot_before2005,te],dim=3.2),
v_directteinv[,ttot_from2005,te]
)
########## calculation of LCOE of standing system #######
######## (old annuities included) ######################
# calculate sub-parts of "COSTS"
# LCOE calculation only for pe2se technologies so far!
####### 1. sub-part: Investment Cost #################################
# AnnualInvCost(t) = sum_(td) [annuity(td) * p_pmeg(t-td+1) * deltaT],
# where td in [t-lifetime,t]
# p_pmeg(t-td+1) = 1 for td = t (measure of how many capacities still standing from past investments)
# p_pmeg(t-td+1) = 0 for td = t-lifetime
# where t is the year for which the investment cost are calculatet and
# td are all past time steps before t back to t-lifetime of the technology
# so: annuity cost incurred over past lifetime years are are divided by current production
# (annuity cost = discounted investment cost spread over lifetime)
# take 74 tech from p_omeg, although in v_direct_in 114 in total
te_annual_inv_cost <- new.magpie(getRegions(te_inv_annuity[,ttot,]),getYears(te_inv_annuity[,ttot,]),magclass::getNames(te_inv_annuity[,ttot,]))
# loop over ttot
for(t0 in ttot){
for(a in magclass::getNames(te_inv_annuity) ) {
# all ttot before t0
t_id <- ttot[ttot<=t0]
# only the time (t_id) within the opTimeYr of a specific technology a
t_id <- t_id[t_id >= (t0 - max(as.numeric(opTimeYr2te$opTimeYr[opTimeYr2te$all_te==a]))+1)]
p_omeg_h <- new.magpie(getRegions(p_omeg),years=t_id,names=a)
for(t_id0 in t_id){
p_omeg_h[,t_id0,a] <- p_omeg[,,a][,,t0-t_id0 +1]
}
te_annual_inv_cost[,t0,a] <- dimSums(pm_ts[,t_id,] * te_inv_annuity[,t_id,a] * p_omeg_h[,t_id,a] ,dim=2)
} # a
} # t0
####### 2. sub-part: fuel cost #################################
# fuel cost = PE price * PE demand of technology
te_annual_fuel_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,magclass::getNames(te_inv_annuity), fill=0)
te_annual_fuel_cost[,,pe2se$all_te] <- setNames(1e+12 * qm_pebal[,ttot_from2005,pe2se$all_enty] / qm_budget[,ttot_from2005,] *
setNames(vm_demPe[,,pe2se$all_te], pe2se$all_enty), pe2se$all_te)
####### 3. sub-part: OMV cost #################################
# omv cost (from pm_data) * SE production
temapse.names <- apply(temapse, 1, function(x) paste0(x, collapse = '.'))
te_annual_OMV_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,magclass::getNames(te_inv_annuity), fill=0)
te_annual_OMV_cost[,,temapse$all_te] <- 1e+12 * collapseNames(pm_data[,,"omv"])[,,temapse$all_te] * setNames(vm_prodSe[,,temapse.names],temapse$all_te)
####### 4. sub-part: OMF cost #################################
# omf cost = omf (from pm_data) * investcost (trUSD/TW) * capacity
# omf in pm_data given as share of investment cost
te_annual_OMF_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,magclass::getNames(te_inv_annuity), fill=0)
te_annual_OMF_cost[,,getNames(te_inv_annuity)] <- 1e+12 * collapseNames(pm_data[,,"omf"])[,,getNames(te_inv_annuity)] * v_investcost[,ttot_from2005,getNames(te_inv_annuity)] *
dimSums(vm_cap[,ttot_from2005,], dim = 3.2)[,,getNames(te_inv_annuity)]
####### 5. sub-part: storage cost (for wind, spv, csp) #################################
# storage cost = investment cost + omf cost
# of corresponding storage technology ("storwind", "storspv", "storcsp")
# clarify: before, they used omv cost here, but storage, grid etc. does not have omv...instead, we use omf now!
te_annual_stor_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,magclass::getNames(te_inv_annuity), fill=0)
te_annual_stor_cost[,,te2stor$all_te] <- setNames(te_annual_inv_cost[,ttot_from2005,te2stor$teStor] +
te_annual_OMF_cost[,,te2stor$teStor],te2stor$all_te)
####### 6. sub-part: grid cost #################################
# same as for storage cost only with grid technologies: "gridwind", "gridspv", "gridcsp"
# only "gridwind" technology active, wind requires 1.5 * the gridwind capacities as spv and csp
grid_factor_tech <- new.magpie(names=te2grid$all_te, fill=1)
getSets(grid_factor_tech)[3] <- "all_te"
grid_factor_tech[,,"wind"] <- 1.5
te_annual_grid_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,magclass::getNames(te_inv_annuity), fill=0)
vm_VRE_prodSe_grid <- dimSums(grid_factor_tech*vm_prodSe[,,te2grid$all_te])
te_annual_grid_cost[,,te2grid$all_te] <- collapseNames(te_annual_inv_cost[,ttot_from2005,"gridwind"] +
te_annual_OMF_cost[,,"gridwind"]) *
# this multiplcative factor is added to reflect higher grid demand of wind
# see q32_limitCapTeGrid
grid_factor_tech * vm_prodSe[,,te2grid$all_te] /
vm_VRE_prodSe_grid
####### 7. sub-part: ccs injection cost #################################
# same as for storage/grid but with ccs inejection technology
# distributed to technolgies according to share of total captured co2 of ccs technology
# (annual ccsinje investment + annual ccsinje omf) * captured co2 (tech) / total captured co2 of all tech
te_annual_ccsInj_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,getNames(te_inv_annuity), fill=0)
# calculate total ccsinjection cost for all techs
total_ccsInj_cost <- dimReduce(te_annual_inv_cost[getRegions(te_annual_OMF_cost),getYears(te_annual_OMF_cost),"ccsinje"] +
te_annual_OMF_cost[,,"ccsinje"])
# distribute ccs injection cost over techs
te_annual_ccsInj_cost[,,getNames(v_emiTeDetail[,,"cco2"], dim = 3)] <- setNames(total_ccsInj_cost * v_emiTeDetail[,,"cco2"] /
dimSums( v_emiTeDetail[,,"cco2"], dim = 3),
getNames(v_emiTeDetail[,,"cco2"], dim = 3))
####### 8. sub-part: co2 cost #################################
# co2 emission cost = carbon price ($/tC) * emissions (GtC) * 1e9
te_annual_co2_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005,getNames(te_inv_annuity), fill=0)
te_annual_co2_cost[,,getNames(v_emiTeDetail[,,"co2"], dim=3)] <- setNames(pm_priceCO2[,getYears(v_emiTeDetail),] * v_emiTeDetail[,,"co2"] * 1e9,
getNames(v_emiTeDetail[,,"co2"], dim=3))
####### 9. sub-part: curtailment cost #################################
########### (only relevant for RLDC version!) ##########################
# note: this step can only be done after all the other parts have already been calcuated!
# curtailment cost = total annual cost / (production * (1-curt_share)) - total annual cost / production
# total annual cost = sum of all previous annual cost
# curt_share = share of curtailed electricity relative to total generated electricity
te_curt_cost <- new.magpie(getRegions(te_annual_fuel_cost), getYears(te_annual_fuel_cost), getNames(te_annual_fuel_cost), fill=0)
# calculate curtailment share of total generation
pe2se.seel <- pe2se[pe2se$all_enty1 == "seel", ]
curt_share <- v32_curt[,,teVRE] / vm_prodSe[,,teVRE]
# calculate total annual cost (without curtailment cost) as sum of previous parts (excl. grid and storage cost)
te_annual_total_cost_noCurt <- new.magpie(getRegions(te_annual_fuel_cost), getYears(te_annual_fuel_cost), getNames(te_annual_fuel_cost))
te_annual_total_cost_noCurt <- te_annual_inv_cost[,getYears(te_annual_fuel_cost),] +
te_annual_fuel_cost +
te_annual_OMV_cost +
te_annual_OMF_cost +
te_annual_ccsInj_cost +
te_annual_co2_cost
# calculate total energy production
# SE and FE production in MWh
total_te_energy <- new.magpie(getRegions(vm_prodSe),getYears(vm_prodSe),
c(magclass::getNames(collapseNames(vm_prodSe[temapse],collapsedim = c(1,2))),
magclass::getNames(collapseNames(vm_prodFe[se2fe],collapsedim = c(1,2)))))
total_te_energy[,,temapse$all_te] <- s_twa2mwh * setNames(vm_prodSe[,,temapse.names], temapse$all_te)
total_te_energy[,,se2fe$all_te] <- s_twa2mwh * vm_prodFe[,,se2fe$all_te]
# set total energy to NA if it is very small (< 100 MWh/yr),
# this avoids very large or infinite cost parts and weird plots
total_te_energy[total_te_energy < 100] <- NA
# calculate curtailment cost
te_curt_cost[,,teVRE] <- te_annual_total_cost_noCurt[,,teVRE] / (total_te_energy[,,teVRE] * (1-curt_share[,,teVRE])) -
te_annual_total_cost_noCurt[,,teVRE] / total_te_energy[,,teVRE]
# calculate total energy production after curtailment
total_te_energy_usable <- total_te_energy
total_te_energy_usable[,,teVRE] <- total_te_energy[,,teVRE] - v32_storloss[,ttot_from2005,teVRE]*s_twa2mwh
####################################################
######### calculate average LCOE ##################
####################################################
LCOE.avg <- NULL
# calculate standing system LCOE
# divide total cost of standing system in that time step by total generation (before curtailment) in that time step
# exception: grid and storage cost are calculate by dividing by generation after curtailment
# convert from USD2005/MWh to USD2015/MWh (*1.2)
LCOE.avg <- mbind(
setNames(te_annual_inv_cost[,getYears(te_annual_fuel_cost),pe2se$all_te]/
total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te,"|supply-side", "|Investment Cost")),
setNames(te_annual_fuel_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te,"|supply-side", "|Fuel Cost")),
setNames(te_annual_OMF_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|OMF Cost")),
setNames(te_annual_OMV_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|OMV Cost")),
### calculate VRE grid and storage cost by dividing by usable generation (after generation)
setNames(te_annual_stor_cost[,,pe2se$all_te]/total_te_energy_usable[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|Storage Cost")),
setNames(te_annual_grid_cost[,,pe2se$all_te]/total_te_energy_usable[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|Grid Cost")),
###
setNames(te_annual_ccsInj_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|CCS Cost")),
setNames(te_annual_co2_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|CO2 Cost")),
setNames(te_curt_cost[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|Curtailment Cost"))
)*1.2
# convert to better dimensional format
df.lcoe.avg <- as.quitte(LCOE.avg) %>%
select(region, period, data, value) %>%
rename(variable = data)
# extract further dimensions from variable name
df.lcoe.avg$type <- strsplit(as.character(df.lcoe.avg$variable), "\\|")
df.lcoe.avg$type <- sapply(df.lcoe.avg$type, "[[", 2)
df.lcoe.avg$output <- strsplit(as.character(df.lcoe.avg$variable), "\\|")
df.lcoe.avg$output <- sapply(df.lcoe.avg$output, "[[", 3)
df.lcoe.avg$tech <- strsplit(as.character(df.lcoe.avg$variable), "\\|")
df.lcoe.avg$tech <- sapply(df.lcoe.avg$tech, "[[", 4)
df.lcoe.avg$sector <- strsplit(as.character(df.lcoe.avg$variable), "\\|")
df.lcoe.avg$sector <- sapply(df.lcoe.avg$sector, "[[", 5)
df.lcoe.avg$cost <- strsplit(as.character(df.lcoe.avg$variable), "\\|")
df.lcoe.avg$cost <- sapply(df.lcoe.avg$cost, "[[", 6)
df.lcoe.avg <- df.lcoe.avg %>%
mutate( unit = "US$2015/MWh") %>%
select(region, period, type, output, tech, sector, unit, cost, value)
# reconvert to magpie object
LCOE.avg.out <- as.magpie(df.lcoe.avg, spatial=1, temporal=2, datacol=9)
# bind to output file
LCOE.out <- mbind(LCOE.out, LCOE.avg.out)
}
##############################################
########################################################
### B) Calculation of marginal (new plant) LCOE ########
########################################################
if (output.type %in% c("marginal", "both", "marginal detail")) {
# variable definitions for dplyr operations in the following section
region <- NULL
period <- NULL
all_te <- NULL
value <- NULL
char <- NULL
maxprod <- NULL
CapDistr <- NULL
nur <- NULL
rlf <- NULL
CAPEX <- NULL
prodSE <- NULL
AtBound <- NULL
tech <- NULL
best.rlf <- NULL
all_enty <- NULL
pomeg <- NULL
fuel.price <- NULL
pomeg.total <- NULL
eff <- NULL
OMF <- NULL
OMV <- NULL
lifetime <- NULL
disc.fac <- NULL
CapFac <- NULL
fuel.price.weighted.mean <- NULL
`Investment Cost` <- NULL
`OMF Cost` <- NULL
`OMV Cost` <- NULL
`Fuel Cost` <- NULL
`Total LCOE` <- NULL
`cost` <- NULL
`variable` <- NULL
fuel <- NULL
output <- NULL
opTimeYr <- NULL
co2.price <- NULL
emiFac <- NULL
all_enty1 <- NULL
input <- NULL
emiFac.se2fe <- NULL
co2_dem <- NULL
CO2StoreShare <- NULL
all_enty2 <- NULL
secfuel <- NULL
secfuel.prod <- NULL
grid.factor <- NULL
grid.cost <- NULL
vm_prodSeVRE <- NULL
storloss <- NULL
VREstor.cost <- NULL
all_te1 <- NULL
CCStax.cost <- NULL
co2.price.weighted.mean <- NULL
secfuel.price <- NULL
`CO2 Tax Cost` <- NULL
`CO2 Provision Cost` <- NULL
`Second Fuel Cost` <- NULL
`VRE Storage Cost` <- NULL
`Grid Cost` <- NULL
`CCS Tax Cost` <- NULL
`CCS Cost` <- NULL
type <- NULL
FlexTax <- NULL
data <- NULL
sector <- NULL
NotBuilt <- NULL
FlexPriceShare <- NULL
all_in <- NULL
FEtax <- NULL
`Flex Tax` <- NULL
`FE Tax` <- NULL
all_esty <- NULL
all_teEs <- NULL
curtShare <- NULL
`Curtailment Cost` <- NULL
maxcap <- NULL
##############################################
##### Prepare data for LCOE calculation #######
### 1. read variables, parameters sets, mappings needed from gdx
### technologies
pe2se <- readGDX(gdx,"pe2se") # pe2se technology mappings
se2se <- readGDX(gdx,"se2se") # hydrogen <--> electricity technologies
se2fe <- readGDX(gdx,"se2fe") # se2fe technology mappings
teStor <- readGDX(gdx, "teStor") # storage technologies for VREs
teGrid <- readGDX(gdx, "teGrid") # grid technologies for VREs
ccs2te <- readGDX(gdx, "ccs2te") # ccsinje technology
teReNoBio <- readGDX(gdx, "teReNoBio") # renewable technologies without biomass
teCCS <- readGDX(gdx, "teCCS") # ccs technologies
teReNoBio <- c(teReNoBio) # renewables without biomass
teVRE <- readGDX(gdx,"teVRE") # VRE technologies
# energy carriers
entyPe <- readGDX(gdx, "entyPe")
entySe <- readGDX(gdx, "entySe")
entyFe <- readGDX(gdx, "entyFe")
# conversion factors
s_twa2mwh <- readGDX(gdx,c("sm_TWa_2_MWh","s_TWa_2_MWh","s_twa2mwh"),format="first_found")
# all technologies to calculate LCOE for
te_LCOE <- c(pe2se$all_te, se2se$all_te,se2fe$all_te,"dac")
# all technologies to calculate investment and O&M LCOE for (needed for CCS, storage, grid cost)
te_LCOE_Inv <- c(te_LCOE, as.vector(teStor), as.vector(teGrid), ccs2te$all_te)
# technologies to produce SE
te_SE_gen <- c(pe2se$all_te, se2se$all_te)
# auxiliary technologies to calculate other cost parts: grid cost, storage cost, carbon capture and storage
te_aux_tech <- c( teStor, teGrid, ccs2te$all_te)
# mappings
se_gen_mapping <- rbind(pe2se, se2se)
colnames(se_gen_mapping) <- c("fuel", "output", "tech")
# all energy system technologies mapping
en2en <- readGDX(gdx, "en2en") %>%
filter(all_te %in% te_LCOE)
colnames(en2en) <- c("fuel", "output", "tech")
# VRE to storage
VRE2teStor <- readGDX(gdx, "VRE2teStor") %>%
rename(tech = all_te)
### time steps
ttot <- as.numeric(readGDX(gdx,"ttot"))
ttot_from2005 <- paste0("y",ttot[which(ttot >= 2005)])
### conversion factors
s_twa2mwh <- as.vector(readGDX(gdx,c("sm_TWa_2_MWh","s_TWa_2_MWh","s_twa2mwh"),format="first_found"))
### 2. retrieve investment and O&M cost
# investment cost
vm_costTeCapital <- readGDX(gdx, "vm_costTeCapital", field = "l", restore_zeros = F)[,ttot_from2005,te_LCOE_Inv]
df.CAPEX <- as.quitte(vm_costTeCapital) %>%
select(region, period, all_te, value) %>%
rename(tech = all_te, CAPEX = value) %>%
left_join(en2en) %>%
select(region, period, tech, fuel, output, CAPEX)
# omf cost
pm_data_omf <- readGDX(gdx, "pm_data", restore_zeros = F)[,,"omf"]
df.OMF <- as.quitte(pm_data_omf) %>%
select(region, all_te, value) %>%
rename(tech = all_te, OMF = value)
# omv cost
pm_data_omv <- readGDX(gdx, "pm_data", restore_zeros = F)[,,"omv"]
df.OMV <- as.quitte(pm_data_omv) %>%
select(region, all_te, value) %>%
rename(tech = all_te, OMV = value)
# 3. retrieve/calculate capacity factors
# capacity factor of non-renewables
vm_capFac <- readGDX(gdx, "vm_capFac", field="l", restore_zeros = F)[,ttot_from2005,]
# calculate renewable capacity factors of new plants
vm_capDistr <- readGDX(gdx, c("vm_capDistr","v_capDistr"), field = "l", restore_zeros = F)
pm_dataren <- readGDX(gdx, "pm_dataren", restore_zeros = F)
### determine capacity factor of highest free grade for renewables
# RE capacity distribution over grades
df.CapDistr <- as.quitte(vm_capDistr) %>%
select(region, all_te, period, rlf, value) %>%
rename(tech = all_te, CapDistr = value)
# max. potential of renewable production per grade
df.RE.maxprod <- as.quitte(pm_dataren[,,c("maxprod","nur")]) %>%
rename(tech = all_te) %>%
select(region, tech, rlf, char, value) %>%
spread(char, value)
# calculate marginal renewable capacity factor
df.CapFac.ren <- df.CapDistr %>%
filter(tech %in% c(teReNoBio, teVRE)) %>%
left_join(df.RE.maxprod) %>%
# maximum capacity at maxprod
mutate( maxcap = maxprod * s_twa2mwh / 8760 / nur * 1e-6 ) %>%
# filter for all grades which are still free (Capacity < 90% of capacity of max. potential)
# but not smaller than ninth grade (last grade of spv; still check all REN technologies for number of last grade)
filter( CapDistr <= 0.9 * maxcap | as.numeric(rlf) >= 9) %>%
# choose highest grade
group_by(region, period, tech) %>%
summarise(CapFac = max(nur)) %>%
ungroup()
# CapFac, merge renewble and non renewable Cap Facs
df.CapFac <- as.quitte(vm_capFac) %>%
select(region, period, all_te, value) %>%
rename(tech = all_te, CapFac = value) %>%
filter( ! tech %in% teReNoBio ) %>%
rbind(df.CapFac.ren) %>%
mutate( period = as.numeric(period))
### 4. plant lifetime and annuity factor
#discount rate
r <- 0.06
# read lifetime of tecnology
# calculate annuity factor to annuitize CAPEX and OMF (annuity factor labeled "disc.fac")
lt <- readGDX(gdx, name="fm_dataglob", restore_zeros = F)[,,"lifetime"][,,te_LCOE_Inv][,,"lifetime"]
df.lifetime <- as.quitte(lt) %>%
select(all_te, value) %>%
rename(tech = all_te, lifetime = value) %>%
mutate( disc.fac = r * (1+r)^lifetime/(-1+(1+r)^lifetime))
### note: just take LCOE investment cost formula with fix lifetime, ignoring that in REMIND capacity is drepeciating over time
### actually you would need to divide CAPEX by CapFac * 8760 * p_omeg and then add a discounting.
### Would need to discuss again how to add the discount in such formula.
### 6. retrieve fuel price
# retrieve marginal of balance equations
qm_pebal <- readGDX(gdx,name=c("q_balPe"),field="m",format="first_found")[,ttot_from2005,]
qm_sebal <- readGDX(gdx,name=c("q_balSe"),field="m",format="first_found")[,ttot_from2005,]
qm_budget <- readGDX(gdx,name=c("qm_budget"),field="m",format="first_found")[,ttot_from2005,]
qm_sebal.seel <- readGDX(gdx,name="q32_balSe",types="equations",field="m",format="first_found")[,ttot_from2005,]
# retrieve capacity distribution over lifetime (fraction of capacity still standing in that year of plant lifetime)
p_omeg <- readGDX(gdx,c("pm_omeg","p_omeg"),format="first_found")
# fuels to calculate price for
fuels <- c("peoil","pegas","pecoal","peur", "pebiolc" , "pebios","pebioil",
"seel","seliqbio", "seliqfos", "sesobio","sesofos","seh2","segabio" ,
"segafos","sehe")
# Primary Energy Price, convert from tr USD 2005/TWa to USD2015/MWh
Pe.Price <- qm_pebal[,ttot_from2005,unique(pe2se$all_enty)] / (qm_budget+1e-10)*1e12/s_twa2mwh*1.2
# Secondary Energy Electricity Price (for se2se and se2fe conversions), convert from tr USD 2005/TWa to USD2015/MWh
Se.Seel.Price <- qm_sebal.seel[,,"seel"]/(qm_budget+1e-10)*1e12/s_twa2mwh*1.2
# Secondary Energy Price (for se2se and se2fe conversions), convert from tr USD 2005/TWa to USD2015/MWh
Se.Price <- qm_sebal[,,as.vector(entySe)[as.vector(entySe) != "seel"]]/(qm_budget+1e-10)*1e12/s_twa2mwh*1.2
Fuel.Price <- mbind(Pe.Price,Se.Seel.Price, Se.Price )[,,fuels]
# Fuel price
df.Fuel.Price <- as.quitte(Fuel.Price) %>%
select(region, period, all_enty, value) %>%
rename(fuel = all_enty, fuel.price = value, opTimeYr = period) %>%
# replace zeros by last or (if not available) next value of time series (marginals are sometimes zero if there are other constriants)
mutate( fuel.price = ifelse(fuel.price == 0, NA, fuel.price)) %>%
group_by(region, fuel) %>%
tidyr::fill(fuel.price, .direction = "downup") %>%
ungroup()
### 7. retrieve carbon price
pm_priceCO2 <- readGDX(gdx, "pm_priceCO2", restore_zeros = F)
df.co2price <- as.quitte(pm_priceCO2) %>%
select(region, period, value) %>%
# where carbon price is NA, it is zero
# convert from USD2005/tC CO2 to USD2015/tCO2
mutate(value = ifelse(is.na(value), 0, value*1.2/3.66)) %>%
rename(co2.price = value, opTimeYr = period)
### 8. calculate capacity distribution weighted prices over lifetimes of plant
# capacity distribution over lifetime
df.pomeg <- as.quitte(p_omeg) %>%
rename(tech = all_te, pomeg = value) %>%
# to save run time, only take p_omeg in ten year time steps only up to lifetimes of 50 years,
# erase region dimension, pomeg same across all regions, only se generating technologies
filter(opTimeYr %in% c(1,seq(5,50,5)), region %in% getRegions(vm_costTeCapital)[1]
, tech %in% te_LCOE) %>%
select(opTimeYr, tech, pomeg)
# expanded df.omeg, with additional period dimension combined with opTimeYr dimensions,
# period will be year of building the plant,
# opTimeYr will be year within lifetime of plant (where only a certain fraction, pomeg, of capacity is still standing)
df.pomeg.expand <- df.pomeg %>%
expand(opTimeYr, period = seq(2005,2150,5)) %>%
full_join(df.pomeg) %>%
mutate( opTimeYr = as.numeric(opTimeYr)) %>%
mutate( opTimeYr = ifelse(opTimeYr > 1, opTimeYr + period, period)) %>%
left_join(en2en) %>%
arrange(tech, period, opTimeYr)
### 9. weight fuel price and carbon price with capacity density over liftime
# join fuel price to df.omeg and weigh fuel price by df.pomeg (pomeg = fraction of capacity still standing in that year of plant lifetime)
df.fuel.price.weighted <- df.Fuel.Price %>%
full_join(df.pomeg.expand) %>%
# mean of fuel prices over first 50-years of plant lifetime weighted by pomeg
group_by(region, period, fuel, tech) %>%
summarise( fuel.price.weighted.mean = sum(fuel.price * pomeg / sum(pomeg))) %>%
ungroup()
# join carbon price to df.omeg and weigh it by df.pomeg (pomeg = fraction of capacity still standing in that year of plant lifetime)
df.co2price.weighted <- df.co2price %>%
full_join(df.pomeg.expand) %>%
filter( !is.na(tech)) %>%
# mean of carbon price over first 50-years of plant lifetime weighted by pomeg
group_by(region, period, tech) %>%
summarise( co2.price.weighted.mean = sum(co2.price * pomeg / sum(pomeg))) %>%
ungroup()
# Note: Discuss whether we still need to discount fuel price/carbon price over time.
# Pomeg only refers to capacity depreciation.
# Note: Anticipated prices in NDC and BAU runs before 2025 are different.
# This calculation only anticipates prices within one scenario. No myopic view included.
### 10. get fuel conversion efficiencies
pm_eta_conv <- readGDX(gdx,"pm_eta_conv", restore_zeros=F)[,ttot_from2005,] # efficiency oftechnologies with time-independent eta
pm_dataeta <- readGDX(gdx,"pm_dataeta", restore_zeros=F)[,ttot_from2005,]# efficiency of technologies with time-dependent eta
df.eff <- as.quitte(mbind(pm_eta_conv, pm_dataeta)) %>%
rename(tech = all_te, eff = value) %>%
select(region, period, tech, eff)
### 11. get emission factors of technologies
pm_emifac <- readGDX(gdx,"pm_emifac", restore_zeros=F)[,ttot_from2005,"co2"] # co2 emission factor per technology
pm_emifac_cco2 <- readGDX(gdx,"pm_emifac", restore_zeros=F)[,ttot_from2005,"cco2"] # captured co2 emission factor per technology
df.emiFac <- as.quitte(pm_emifac) %>%
# do not need period dimension
filter(period == 2005) %>%
select(region, all_te, value) %>%
# convert from GtC CO2/TWa to tCo2/MWh
mutate(value = value / s_twa2mwh * 3.66 *1e9) %>%
rename(tech = all_te, emiFac = value) %>%
# join other technologies without emission factor -> set emission factor to zero
# (so far, only emissions of energy input captured, not of co2 input for CCU)
full_join(data.frame(tech = te_LCOE_Inv) %>%
expand(tech, region = getRegs(df.CAPEX))) %>%
mutate( emiFac = ifelse(is.na(emiFac), 0, emiFac))
# get se2fe emision factor to add to techs that produce seliqfos, segafos, sesofos
df.emifac.se2fe <- df.emiFac %>%
left_join(se2fe, by=c("tech"="all_te")) %>%
# filter for SE fossil, take diesel emifac for all liquids
filter(all_enty %in% c("seliqfos","segafos","sesofos")) %>%
filter(all_enty != "seliqfos" | all_enty1 == "fedie") %>%
rename(input = all_enty, emiFac.se2fe = emiFac) %>%
select(region, input, emiFac.se2fe) %>%
left_join(pe2se, by = c("input" = "all_enty1")) %>%
rename(tech = all_te) %>%
select(region, tech, emiFac.se2fe)
### 12. calculate CO2 capture cost
# Co2 Capture price, marginal of q_balcapture, convert from tr USD 2005/GtC to USD2015/tCO2
qm_balcapture <- readGDX(gdx,"q_balcapture",field="m", restore_zeros = F)
Co2.Capt.Price <- qm_balcapture /
(qm_budget[,getYears(qm_balcapture),]+1e-10)*1e3*1.2/3.66 ## looks weird
# # for now, just assume 50USD/tCO2 for all times and regions (half of typical 100USD/tCO2 BECCS cost)
Co2.Capt.Price[,,] <- 50
df.Co2.Capt.Price <- as.quitte(Co2.Capt.Price) %>%
rename(Co2.Capt.Price = value) %>%
select(region, period, Co2.Capt.Price)
# Note: co2 capture cost calculation needs to be checked
# set to 0 meanwhile
df.Co2.Capt.Price <- df.Co2.Capt.Price %>%
mutate( Co2.Capt.Price = 0)
# CO2 required per unit output (for CCU technologies)
if( module2realisation$`*`[module2realisation$modules=="CCU"] == "on") {
p39_co2_dem <- readGDX(gdx, c("p39_co2_dem","p39_ratioCtoH"), restore_zeros = F)[,,]
} else {
# some dummy data, only needed to create the following data frame if CCU is off
p39_co2_dem <- new.magpie(getRegions(vm_costTeCapital), getYears(vm_costTeCapital), fill = 0)
}
df.co2_dem <- as.quitte(p39_co2_dem) %>%
rename(co2_dem = value, tech = all_te) %>%
select(region, period, tech, co2_dem) %>%
# from GtC CO2/TWa(H2) to tCO2/MWh(H2)
mutate( co2_dem = co2_dem * 3.66 / s_twa2mwh * 1e9)
### 13. calculate share stored carbon from capture carbon
vm_co2CCS <- readGDX(gdx, "vm_co2CCS", field = "l", restore_zeros = F)
vm_co2capture <- readGDX(gdx, "vm_co2capture", field = "l", restore_zeros = F)
p_share_carbonCapture_stor <- (
vm_co2CCS[,,"cco2.ico2.ccsinje.1"]
/ dimSums(mselect(vm_co2capture, all_enty = "cco2"), dim = 3)
)
p_share_carbonCapture_stor[is.na(p_share_carbonCapture_stor)] <- 1
df.CO2StoreShare <- as.quitte(p_share_carbonCapture_stor) %>%
rename( CO2StoreShare = value) %>%
select(region, period, CO2StoreShare)
# Note: This is assuming that the CO2 capture to storage share stays constant over time.
# Still to do: calculate average over lifetime of plant
### 14. calculate cost of second fuel (if required)
# for technologies with coupled production
# (pm_prodCouple: negative values mean own consumption, positive values mean coupled product)
# mapping of SE technologies that require second fuel input (own consumption)
pc2te <- readGDX(gdx, "pc2te")
# second fuel production per unit output of technology
pm_prodCouple <- readGDX(gdx, "pm_prodCouple", restore_zeros = F)
# secfuel.prod (share of coupled production per unit output),
# secfuel.price (price of coupled product)
df.secfuel <- as.quitte(pm_prodCouple) %>%
rename(tech = all_te, fuel = all_enty, secfuel = all_enty2, secfuel.prod = value) %>%
select(region, tech, fuel, secfuel, secfuel.prod) %>%
right_join(df.fuel.price.weighted) %>%
rename(secfuel.price = fuel.price.weighted.mean)
# ### VRE integration cost
#
# # first calculate LCOE of VREs before storage losses (Investment Cost + OMF cost)
# # (will be done below again,
# # this is only to calculate the integration cost which are added in the end)
# # also caculate LCOE (investment and OMF cost) of grid (only gridwind used)
# # and storage technologies
# df.LCOE.VRE.preloss <- df.CAPEX %>%
# left_join(df.OMF) %>%
# left_join(df.CapFac) %>%
# left_join(df.lifetime) %>%
# filter( tech %in% c(VRE2teStor[,1], VRE2teStor[,2], "gridwind")) %>%
# # conversion from tr USD 2005/TW to USD2015/kW
# mutate(CAPEX = CAPEX *1.2 * 1e3) %>%
# # calculate annuity factor for investment cost
# mutate( disc.fac = r * (1+r)^lifetime/(-1+(1+r)^lifetime)) %>%
# # investment cost LCOE in USD/MWh
# mutate( `Investment Cost` = CAPEX * disc.fac / (CapFac*8760)*1e3) %>%
# # OMF cost LCOE in USD/MWh
# mutate( `OMF Cost` = CAPEX * OMF / (CapFac*8760)*1e3) %>%
# # calculate pre-loss VRE LCOE as investment cost + OMF cost LCOE
# mutate( `VRE LCOE preloss` = `Investment Cost` + `OMF Cost`) %>%
# select(region, period, tech, CapFac, `VRE LCOE preloss`)
#
#
#
# # calculate marginal storloss:
# # (Marginal amount of energy that is lost in storage when
# # one more MWh of 'usable seel' (='upgraded seel that is balanced by storage')
# # is produced. Unit: MWh. Can be larger than 1!)
#
# # generation share of technology
# df.shSeEl <- read.gdx(gdx, "v32_shSeEl", fields = "l") %>%
# rename(shSeEl = value, tech = all_te, period = ttot, region = all_regi)
# # usable (after storage loss) SE per technology
# df.usableSeTe <- read.gdx(gdx, "vm_usableSeTe", fields = "l") %>%
# rename(usableSeTe = value, tech = all_te, period = ttot, region = all_regi) %>%
# select(-entySe)
# # usable (after storage loss) SE
# df.usableSe <- read.gdx(gdx, "vm_usableSe", fields = "l") %>%
# rename(usableSe = value, period = ttot, region = all_regi) %>%
# select(-entySe)
# # storage exponent
# df.storexp <- read.gdx(gdx, "p32_storexp") %>%
# rename(storexp = value, tech = all_te, region = all_regi)
#
#
# # questions: !!
# ## marginal storloss of usable SE partly very high (30 for wind EUR), which unit does this have
# # it is a share, right? Or does it have energy units?
#
# # calculate marginal storloss
# df.marg.storloss <- df.LCOE.VRE.preloss %>%
# filter( tech %in% VRE2teStor[,1]) %>%
# left_join(df.shSeEl) %>%
# left_join(df.usableSeTe) %>%
# left_join(df.usableSe) %>%
# left_join(df.eff %>%
# right_join(VRE2teStor) %>%
# select(-teStor) ) %>%
# left_join(df.storexp) %>%
# mutate( loss = 1-eff) %>%
# # formula derived by RP:
# # This formula is an approximation of the marginal change of storage loss
# # that was calculated outside ReMIND by hand.
# mutate( first.term = (loss * (shSeEl^storexp) +
# (usableSeTe * shSeEl^(storexp-1) *
# (1/usableSe - usableSeTe/(usableSe^2)))) /
# ((1-eff)*shSeEl^storexp)) %>%
# mutate( second.term = ( loss^2 * shSeEl^storexp * storexp * shSeEl^(storexp-1) *
# (1/usableSe - usableSeTe/(usableSe^2))) /
# (((1-eff)*shSeEl^storexp)^2)) %>%
# mutate( marg.storloss = first.term + second.term) %>%
# mutate( marg.storloss = ifelse(loss == 0, 0, marg.storloss)) %>%
# # convert storage loss from usable SE to total SE level
# mutate(marg.storloss.SE = marg.storloss / (1+marg.storloss)) %>%
# # calculate marginal LCOE of storloss
# mutate( loss.MLCOE = `VRE LCOE preloss` * marg.storloss.SE /
# ( 1 - marg.storloss.SE)) %>%
# # calculate marginal LCOE postloss
# mutate( postloss.LCOE = `VRE LCOE preloss` /
# ( 1 - marg.storloss.SE)) %>%
# # calculate postloss - preloss LCOE -> VRE Storage Loss Cost
# mutate( storloss.MLCOE = postloss.LCOE-`VRE LCOE preloss`)
#
# # VRE grid weight: how much grid capacities the respective VRE technology needs
# df.VRE2teGrid <- data.frame(tech = c("spv","csp","wind"), GridReq = c(1,1,1.5))
#
#
# # calculate marginal integration cost (cost of building storage and grid for the marginal VRE unit)
# df.IntCost <- df.marg.storloss %>%
# select(region, period, tech, marg.storloss) %>%
# # join with MCLOE for storage and grid technologies
# # add grid capacity MLCOE
# left_join(df.LCOE.VRE.preloss %>%
# filter(tech == "gridwind") %>%
# select(-tech) %>%
# rename(gridCap.MLCOE = `VRE LCOE preloss`)) %>%
# # add storage capacity MLCOE
# left_join(df.LCOE.VRE.preloss %>%
# filter(tech %in% VRE2teStor[,2]) %>%
# rename( teStor = tech, StorCap.MLCOE = `VRE LCOE preloss`) %>%
# left_join(VRE2teStor) ) %>%
# # add grid requirement factor, current IntC impl.: wind needs 1.5*gridwind capacity than solar
# left_join(df.VRE2teGrid) %>%
# # add storage efficiencies
# left_join(df.eff %>%
# filter(tech %in% VRE2teStor[,2]) %>%
# rename(teStor = tech)) %>%
# # calculate marginal grid and storage capacity cost per unit of new VRE
# mutate( MLCOE.grid = marg.storloss / CapFac * GridReq * gridCap.MLCOE,
# MLCOE.stor = marg.storloss * eff / (1-eff) / CapFac * StorCap.MLCOE)
#
#
#
#
#
#
# # o_te_marg_storloss_usable(ttot,regi,teReNoBio) !!RP: This formula is an approximation of the marginal change of storage loss that was calculated outside ReMIND by hand.
# # = p_aux_loss(teReNoBio)
# # * (p_aux_shareseel ** p_storexp(regi,teReNoBio)
# # + (
# # p_aux_usablese_te * p_aux_shareseel ** (p_storexp(regi,teReNoBio) -1)
# # * ( 1 / p_aux_usablese - p_aux_usablese_te/(p_aux_usablese ** 2) )
# # )
# # )
# # / (1 - p_aux_loss(teReNoBio) * p_aux_shareseel ** p_storexp(regi,teReNoBio) )
# # + (
# # p_aux_loss(teReNoBio) ** 2 * p_aux_shareseel ** p_storexp(regi,teReNoBio) * p_storexp(regi,teReNoBio) * p_aux_shareseel ** (p_storexp(regi,teReNoBio) -1)
# # * ( 1 / p_aux_usablese - p_aux_usablese_te/(p_aux_usablese ** 2) )
# # )
# # / (
# # (1 - p_aux_loss(teReNoBio) * p_aux_shareseel ** p_storexp(regi,teReNoBio) )
# # ** 2
# # );
#
#
#
# #### calculate LCOE
# df.LCOE <- df.LCOE %>%
# # investment cost LCOE in USD/MWh
# mutate( `Investment Cost` = CAPEX * disc.fac / (CapFac*8760)*1e3) %>%
# # OMF cost LCOE in USD/MWh
# mutate( `OMF Cost` = CAPEX * OMF / (CapFac*8760)*1e3) %>%
# mutate( `OMV Cost` = OMV) %>%
### 15. calculate grid cost of VRE technologies
teVRE.grid <- data.frame(tech=c("spv","csp","wind"), gridtech = c("gridwind",
"gridwind","gridwind") )
p32_grid_factor <- readGDX(gdx, "p32_grid_factor")
df.gridfactor <- as.quitte(p32_grid_factor) %>%
rename(grid.factor = value) %>%
select(region, grid.factor)
df.gridcost <- df.CAPEX %>%
left_join(df.OMF) %>%
left_join(df.lifetime) %>%
left_join(df.gridfactor) %>%
filter(tech == "gridwind") %>%
rename(gridtech = tech) %>%
mutate(`Investment Cost` = CAPEX *1e12*1.2/s_twa2mwh * disc.fac) %>%
mutate(`OMF Cost` = CAPEX*1e12*1.2/s_twa2mwh * OMF) %>%
mutate(grid.cost = (`Investment Cost` + `OMF Cost`) / grid.factor) %>%
full_join(teVRE.grid) %>%
# wind requires 1.5 times the grid capacity than spv and csp
mutate(grid.cost = ifelse(tech == "wind", 1.5*grid.cost, grid.cost)) %>%
select(region, period, tech, grid.cost)
# Note: marginal grid cost calculation needs to be checked, see Robert's old version above,
# set to 0 meanwhile
df.gridcost <- df.gridcost %>%
mutate( grid.cost = 0)
# 16. VRE electricity storage cost
v32_storloss <- readGDX(gdx, "v32_storloss", field = "l")
# VRE production
vm_prodSe <- readGDX(gdx, "vm_prodSe", field = "l", restore_zeros = F)[,,c("spv","wind","csp")]
df.prodSeVRE <- as.quitte(vm_prodSe) %>%
rename(tech = all_te, vm_prodSeVRE = value) %>%
select(region, period, tech, vm_prodSeVRE)
df.storloss <- as.quitte(v32_storloss) %>%
rename(storloss = value, tech = all_te) %>%
select(region, period, tech, storloss)
# calculation following q32_limitCapTeStor
df.VREstorcost <- df.CAPEX %>%
filter(tech %in% VRE2teStor$teStor) %>%
left_join(df.OMF) %>%
left_join(df.lifetime) %>%
rename(teStor = tech) %>%
full_join(VRE2teStor) %>%
left_join(df.storloss) %>%
left_join(df.prodSeVRE) %>%
left_join(df.eff, by=c("region"="region","period"="period","teStor"="tech")) %>%
left_join(df.CapFac, by=c("region"="region","period"="period","teStor"="tech")) %>%
# cost per MWh v32_storloss
mutate(`Investment Cost` = CAPEX *1e12*1.2/s_twa2mwh * disc.fac) %>%
mutate(`OMF Cost` = CAPEX*1e12*1.2/s_twa2mwh * OMF) %>%
# cost per MWh VRE,
#assuming that for one additional unit of VRE ratio of
#v32_storloss/vm_prodSe constant
mutate(VREstor.cost = (`Investment Cost` + `OMF Cost`) /
CapFac / eff * (1-eff) * storloss / vm_prodSeVRE) %>%
mutate( VREstor.cost = ifelse(is.na(VREstor.cost), 0, VREstor.cost)) %>%
select(region, period, tech, VREstor.cost)
# Note: marginal storage cost calculation needs to be checked, see Robert's old version above,
# set to 0 meanwhile
df.VREstorcost <- df.VREstorcost %>%
mutate( VREstor.cost = 0)
### calculate Curtailment share (required for calculating Curtailment Cost later for VREs)
# curtailment share is needed to calculate curtailment cost of VREs:
# curtailment cost = LCOE(VRE) * curtshare/((1-curtShare)),
# where LCOE(VRE) is the generation LCOE of VREs, so Investment Cost + O&M Cost
if (any(module2realisation[,2] == "RLDC")) {
v32_curt <- readGDX(gdx,name=c("v32_curt"),field="l",restore_zeros=FALSE,format="first_found")
} else if (any(module2realisation[,2] == "IntC")) {
v32_curt <- v32_storloss[,ttot_from2005,getNames(vm_prodSe, dim=3)]
} else{
v32_curt <- 0
}
curt_share <- v32_curt[,,teVRE] / vm_prodSe[,,teVRE]
df.curtShare <- as.quitte(curt_share) %>%
rename(tech = all_te, curtShare = value) %>%
select(region, period, tech, curtShare)
### 17. CCS tax
# following q21_taxrevCCS
vm_co2CCS <- readGDX(gdx, "vm_co2CCS", field = "l", restore_zeros = F)
sm_ccsinjecrate <- readGDX(gdx, "sm_ccsinjecrate")
pm_dataccs <- readGDX(gdx, "pm_dataccs", restore_zeros = F)
# calculate storage share of captured CO2,
# for now take the storage share of the construction year of plant, it will not change much over time
# (if CCS, then no CCU and v_capturevalve is mostly small)
vm_co2CCS <- readGDX(gdx, "vm_co2CCS", field = "l", restore_zeros = F)
vm_co2capture <- readGDX(gdx, "vm_co2capture", field = "l", restore_zeros = F)
# calculate stored CO2 per output of capture technology (GtC/TWa)
pm_eff <- mbind(pm_eta_conv, pm_dataeta)
vm_co2CCS_m <- pm_emifac_cco2/pm_eff[,,getNames(pm_emifac_cco2, dim=3)]*collapseNames(p_share_carbonCapture_stor)
# calculate CCS tax markup following q21_taxrevCCS, convert to USD2015/MWh
CCStax <- dimReduce(pm_data_omf[,,"ccsinje"]*vm_costTeCapital[,,"ccsinje"]*vm_co2CCS_m^2/pm_dataccs[,,"quan.1"]/sm_ccsinjecrate/s_twa2mwh*1e12*1.2)
df.CCStax <- as.quitte(CCStax) %>%
rename(tech = all_te1, CCStax.cost = value) %>%
select(region, period, tech, CCStax.cost)
# Note: CCS tax still to fix, set temporarily to 0
df.CCStax <- df.CCStax %>%
mutate( CCStax.cost = 0)
### 18. CO2 Storage Cost
p_teAnnuity <- readGDX(gdx, "p_teAnnuity", restore_zeros = F)
# adding annualized capital cost and omf cost of ccsinje technology,
# multiply with vm_co2CCS_m (stored CO2 of CCS technology in GtC/TWa(output)),
# convert to USD2015/MWh
CCSCost <- vm_co2CCS_m*dimReduce(vm_costTeCapital[,,"ccsinje"]/vm_capFac[,,"ccsinje"]*(p_teAnnuity[,,"ccsinje"]+pm_data_omf[,,"ccsinje"]))*1.2*1e12/s_twa2mwh
df.CCSCost <- as.quitte(CCSCost) %>%
rename(tech = all_te, CCSCost = value) %>%
select(region, period, tech, CCSCost)
# Note: CCS cost still to fix, set temporarily to 0
df.CCSCost <- df.CCSCost %>%
mutate( CCSCost = 0)
### 19. Flexibility Tax
cm_FlexTax <- readGDX(gdx, "cm_flex_tax")
v32_flexPriceShare <- readGDX(gdx, "v32_flexPriceShare", field = "l", restore_zeros = F)
if (is.null(v32_flexPriceShare) | is.null(cm_FlexTax)) {
v32_flexPriceShare <- vm_costTeCapital
v32_flexPriceShare[,,] <- 1
} else {
if (cm_FlexTax == 0) {
v32_flexPriceShare <- vm_costTeCapital
v32_flexPriceShare[,,] <- 1
}
}
df.flexPriceShare <- as.quitte(v32_flexPriceShare) %>%
rename(tech = all_te, FlexPriceShare = value) %>%
select(region, period, tech, FlexPriceShare)
### 20. Final Energy Taxes
module2realisation <- readGDX(gdx, "module2realisation")
# read energy services types transport
esty_dyn35 <- readGDX(gdx, "esty_dyn35")
# read energy services buildings, detailed modules
esty_dyn36 <- readGDX(gdx, "esty_dyn36")
# read pffen for industry
ppfen_industry_dyn37 <- readGDX(gdx, "ppfen_industry_dyn37")
# read ppfen for buildilngs
ppfen_buildings_dyn36 <- readGDX(gdx, "ppfen_buildings_dyn36")
# read fe to pffen mapping
# relevant for simple industry and buildings realization
# and detailed industry realization)
fe2ppfEn <- readGDX(gdx, "fe2ppfEn") %>%
rename(output = all_enty)
fe2ue <- readGDX(gdx, "fe2ue") %>%
rename(output = all_enty, all_in = all_enty1) %>%
select(output, all_in)
fe2es <- readGDX(gdx, "fe2es") %>%
rename(output = all_enty) %>%
select(output, all_esty)
# FE to UE CES node for simple transport
fe2ue.transport <- readGDX(gdx, "fe2ue") %>%
rename(output = all_enty, all_in = all_enty1) %>%
select(output, all_in)
# FE to ES CES node for detailed transport
fe2es.transport <- fe2es %>%
filter(all_esty %in% as.vector(esty_dyn35) )
# FE to CES node for simple buildings
fe2ppfen.buildings <- fe2ppfEn %>%
filter(all_in %in% as.vector(ppfen_buildings_dyn36))
# FE to UE CES nodes for detailed buildings
fe2es.buildings <- fe2es %>%
filter(all_esty %in% as.vector(esty_dyn36))
# FE to CES node for simple and detailed industry
fe2ppfen.industry <- fe2ppfEn %>%
filter(all_in %in% as.vector(ppfen_industry_dyn37))
# FE to UE mapping for taxes
fe_tax_subEs36 <- readGDX(gdx, "fe_tax_subEs36")
### transport FE Tax
# transport FE tax simple module
if (any(module2realisation[,2]=="complex")) {
p21_tau_fe_tax_transport <- readGDX(gdx, "p21_tau_fe_tax_transport")[,ttot_from2005,unique(fe2ue$output)]
p21_tau_fe_sub_transport <- readGDX(gdx, "p21_tau_fe_sub_transport")[,ttot_from2005,unique(fe2ue$output)]
}
# transport FE tax EDGE-T module
if (any(module2realisation[,2]=="edge_esm")) {
p21_tau_fe_tax_transport <- readGDX(gdx, "p21_tau_fe_tax_transport")[,ttot_from2005,unique(fe2es.transport$output)]
p21_tau_fe_sub_transport <- readGDX(gdx, "p21_tau_fe_sub_transport")[,ttot_from2005,unique(fe2es.transport$output)]
}
df.FEtax.transport <- as.quitte((p21_tau_fe_tax_transport+p21_tau_fe_sub_transport)* 1.2 / s_twa2mwh * 1e12) %>%
mutate(sector = "transport") %>%
rename(FEtax = value, output = all_enty) %>%
select( region, period, output, sector, FEtax)
### stationary FE tax
# read stationary FE tax
p21_tau_fe_tax_bit_st <- readGDX(gdx, "p21_tau_fe_tax_bit_st")[,ttot_from2005,unique(c(fe2ppfen.industry$all_in, fe2ppfen.buildings$all_in))]
p21_tau_fe_tax_sub_st <- readGDX(gdx, "p21_tau_fe_sub_bit_st")[,ttot_from2005,unique(c(fe2ppfen.industry$all_in, fe2ppfen.buildings$all_in))]
# ES service tax in buildings (detailed buildings realizations)
pm_tau_fe_tax_ES_st <- readGDX(gdx, "pm_tau_fe_tax_ES_st")[,ttot_from2005,]
pm_tau_fe_sub_ES_st <- readGDX(gdx, "pm_tau_fe_sub_ES_st")[,ttot_from2005,]
#buildings FE tax
# if detailed buildings -> ES tax
if (nrow(fe2es.buildings) > 0) {
df.FEtax.buildings <- as.quitte((pm_tau_fe_tax_ES_st+pm_tau_fe_sub_ES_st)[,,unique(fe2es.buildings$all_esty)]* 1.2 / s_twa2mwh * 1e12) %>%
mutate(sector = "buildings") %>%
left_join(fe2es.buildings) %>%
rename(FEtax = value) %>%
# for now only keep one value and remove CES pffen as all
# CES pffen have the same tax for each FE in detailed modules
select( region, period, output, sector, FEtax) %>%
distinct()
} else {
# without ES tax
# for simple buildings: FE tax on all FE types
# for detailed buildings: FE tax only on electricity (other energy carriers taxed at UE level)
df.FEtax.buildings <- as.quitte((p21_tau_fe_tax_bit_st+p21_tau_fe_tax_sub_st)[,,unique(fe2ppfen.buildings$all_in)]* 1.2 / s_twa2mwh * 1e12) %>%
mutate(sector = "buildings") %>%
left_join(fe2ppfen.buildings) %>%
rename(FEtax = value) %>%
# for now only keep one value and remove CES pffen as all
# CES pffen have the same tax for each FE in detailed modules
select( region, period, output, sector, FEtax) %>%
distinct()
}
# add missing FEs with a 0, this is done such that the sectoral distinction in LCOE
# is also maintained when there no FE tax is plaed on the FE
missing.FEs <- c("fehos","fegas","fesos","feels","feh2s","fehes")
missing.FEs <- missing.FEs[! missing.FEs %in% as.vector(unique(df.FEtax.buildings$output))]
if (identical(missing.FEs, character(0))) {
missing.FEs <- NULL
}
df.missing.FE.bld <- new.magpie(getRegions(p21_tau_fe_tax_bit_st),
getYears(p21_tau_fe_tax_bit_st),
names=missing.FEs,
sets = c("region", "year", "output"),
fill = 0) %>%
as.quitte() %>%
mutate(sector = "buildings") %>%
rename( FEtax = value) %>%
select(region, period, output, sector, FEtax)
df.FEtax.buildings <- df.FEtax.buildings %>%
rbind(df.missing.FE.bld)
# for simple and detailed industry
df.FEtax.industry <- as.quitte((p21_tau_fe_tax_bit_st+p21_tau_fe_tax_sub_st)[,,unique(fe2ppfen.industry$all_in)]* 1.2 / s_twa2mwh * 1e12) %>%
mutate(sector = "industry") %>%
left_join(fe2ppfen.industry) %>%
rename(FEtax = value) %>%
# for now only keep one value and remove CES pffen as all
# CES pffen have the same tax for each FE in detailed modules
select( region, period, output, sector, FEtax) %>%
distinct()
# add missing FEs with a 0, this is done such that the sectoral distinction in LCOE
# is also maintained when there no FE tax is plaed on the FE
missing.FEs <- c("fehos","fegas","fesos","feels","feh2s","fehes")
missing.FEs <- missing.FEs[! missing.FEs %in% as.vector(unique(df.FEtax.industry$output))]
if (identical(missing.FEs, character(0))) {
missing.FEs <- NULL
}
df.missing.FE.ind <- new.magpie(getRegions(p21_tau_fe_tax_bit_st),
getYears(p21_tau_fe_tax_bit_st),
names=missing.FEs,
sets = c("region", "year", "output"),
fill = 0) %>%
as.quitte() %>%
mutate(sector = "industry") %>%
rename( FEtax = value) %>%
select(region, period, output, sector, FEtax)
df.FEtax.industry <- df.FEtax.industry %>%
rbind(df.missing.FE.ind)
df.FEtax <- df.FEtax.transport %>%
rbind(df.FEtax.buildings) %>%
rbind(df.FEtax.industry)
# Note, this should still be checked: the stationary FE tax currently
# is on the vm_cesIO (first CES level prodcution factor) and not on FE demand.
# Should be checked whether there are losses between FE and CES factor.
# This only includes taxes that are placed on final energy ( to explain FE prices).
# Not taxes on UE/ES level in the detailed buildings and industry sectors.
####################### LCOE calculation (New plant/marginal) ########################
### create table with all parmeters needed for LCOE calculation
df.LCOE <- df.CAPEX %>%
left_join(df.OMF) %>%
left_join(df.OMV) %>%
left_join(df.CapFac) %>%
left_join(df.lifetime) %>%
left_join(df.fuel.price.weighted) %>%
left_join(df.co2price.weighted) %>%
left_join(df.eff) %>%
left_join(df.emiFac) %>%
left_join(df.emifac.se2fe) %>%
left_join(df.Co2.Capt.Price) %>%
left_join(df.co2_dem) %>%
left_join(df.CO2StoreShare) %>%
left_join(df.secfuel) %>%
left_join(df.gridcost) %>%
left_join(df.VREstorcost) %>%
left_join(df.curtShare) %>%
left_join(df.CCStax) %>%
left_join(df.CCSCost) %>%
left_join(df.flexPriceShare) %>%
left_join(df.FEtax) %>%
# filter to only have LCOE technologies
filter( tech %in% c(te_LCOE))
# only retain unique region, period, tech combinations
df.LCOE <- df.LCOE %>%
unique(by=c("region", "period", "tech"))
# replace NA by 0 in certain columns
# columns where NA should be replaced by 0
col.NA.zero <- c("OMF","OMV", "co2.price.weighted.mean", "fuel.price.weighted.mean","co2_dem","emiFac.se2fe","Co2.Capt.Price",
"secfuel.prod", "secfuel.price", "grid.cost","VREstor.cost", "curtShare","CCStax.cost","CCSCost","FEtax")
df.LCOE[,col.NA.zero][is.na(df.LCOE[,col.NA.zero])] <- 0
# replace NA by 1 in certain columns
# columns where NA should be replaced by 1
col.NA.one <- c("FlexPriceShare")
df.LCOE[,col.NA.one][is.na(df.LCOE[,col.NA.one])] <- 1
# replace NA for sectors by "supply-side" (for all SE generating technologies)
col.NA.sec <- c("sector")
df.LCOE[,col.NA.sec][is.na(df.LCOE[,col.NA.sec])] <- "supply-side"
### data preparation before LCOE calculation
df.LCOE <- df.LCOE %>%
# unit conversions for CAPEX and OMV cost
# conversion from tr USD 2005/TW to USD2015/kW
mutate(CAPEX = CAPEX *1.2 * 1e3) %>%
# conversion from tr USD 2005/TWa to USD2015/MWh
mutate(OMV = OMV * 1.2 / s_twa2mwh * 1e12) %>%
# share of stored carbon from captured carbon is only relevant for CCS technologies, others -> 1
mutate( CO2StoreShare = ifelse(tech %in% teCCS, CO2StoreShare, 1)) %>%
# calculate discount factor for investment cost
mutate( disc.fac = r * (1+r)^lifetime/(-1+(1+r)^lifetime))
#### calculate LCOE
df.LCOE <- df.LCOE %>%
# investment cost LCOE in USD/MWh
mutate( `Investment Cost` = CAPEX * disc.fac / (CapFac*8760)*1e3) %>%
# OMF cost LCOE in USD/MWh
mutate( `OMF Cost` = CAPEX * OMF / (CapFac*8760)*1e3) %>%
mutate( `OMV Cost` = OMV) %>%
mutate( `Fuel Cost` = fuel.price.weighted.mean / eff) %>%
mutate( `CO2 Tax Cost` = co2.price.weighted.mean * (emiFac / eff + emiFac.se2fe) * CO2StoreShare) %>%
mutate( `CO2 Provision Cost` = Co2.Capt.Price * co2_dem) %>%
mutate( `Second Fuel Cost` = -(secfuel.prod * secfuel.price)) %>%
mutate( `VRE Storage Cost` = VREstor.cost, `Grid Cost` = grid.cost) %>%
mutate( `Curtailment Cost` = curtShare / (1-curtShare) * (`Investment Cost` + `OMF Cost` + `OMV Cost`)) %>%
mutate( `CCS Tax Cost` = CCStax.cost, `CCS Cost` = CCSCost) %>%
mutate( `Flex Tax` = -(1-FlexPriceShare) * `Fuel Cost`) %>%
mutate( `FE Tax` = FEtax) %>%
mutate( `Total LCOE` = `Investment Cost` + `OMF Cost` + `OMV Cost` + `Fuel Cost` + `CO2 Tax Cost` +
`CO2 Provision Cost` + `Second Fuel Cost` + `VRE Storage Cost` + `Grid Cost` + `CCS Tax Cost` + `Curtailment Cost` + `CCS Cost` + `Flex Tax` + `FE Tax`)
# if detailed buildings module on, calculate LCOE of fe2ue technologies
# following 36_modules/buildings/services_putty/presolve.gms
if (any(module2realisation$`*` == "services_putty")) {
p36_esCapCostImplicit <- readGDX(gdx, "p36_esCapCostImplicit", restore_zeros = F) # capital cost (incl. OMF and implicit discount rate)
t.run <- getYears(p36_esCapCostImplicit)
p36_fePrice <- readGDX(gdx, "p36_fePrice", restore_zeros = F)[,t.run,] # FE price
p36_inconvpen <- readGDX(gdx, "p36_inconvpen")[,t.run,getNames(p36_esCapCostImplicit)] # invonience penalty (on heating oil)
pm_fe2es <- readGDX(gdx, "pm_fe2es", restore_zeros = F)[,t.run,getNames(p36_esCapCostImplicit)] # fe2es efficiency
p36_logitCalibration <- readGDX(gdx, "p36_logitCalibration", restore_zeros = F)[,t.run,] # logit calibration factors (invoncenience cost for buildings technologies to match current UE)
fe2es_dyn36 <- readGDX(gdx,"fe2es_dyn36") # fe,ue,te mapping of fe2ue buildings technologies
# map FE price from FE dimension to technology dimension
p36_fePrice_ue <- p36_fePrice %>%
as.quitte() %>%
right_join(fe2es_dyn36) %>%
select(region, period, all_teEs, value) %>%
as.magpie(datacol=4)
# map ES tax to technology dimensions
ES_tax <- (pm_tau_fe_tax_ES_st+pm_tau_fe_sub_ES_st)[,t.run,fe2es.buildings$all_esty] %>%
as.quitte() %>%
right_join(fe2es_dyn36) %>%
select(region, period, all_teEs, value) %>%
as.magpie(datacol=4)
UE.LCOE.build <- new.magpie(getRegions(vm_costTeCapital), getYears(vm_costTeCapital),
c("Investment Cost (incl OMF and impl discount)",
"Fuel Cost",
"Inconvenience Cost",
"ES Taxes",
"Calibration Factor Mark-up",
"Total LCOE")) %>%
add_dimension(dim=3.2, add = "all_teEs", nm = getNames(p36_esCapCostImplicit))
### UE Buildings LCOE calculation
# always convert to USD2015/MWh
# capital cost of UE technologies including OMF and including implicit discount rate on capital
UE.LCOE.build[,t.run,"Investment Cost (incl OMF and impl discount)"] <- p36_esCapCostImplicit * 1.2 / s_twa2mwh * 1e12
# fuel cost
UE.LCOE.build[,t.run,"Fuel Cost"] <- p36_fePrice_ue / pm_fe2es * 1.2 / s_twa2mwh * 1e12
# inconvenience cost for environmental/health effects (e.g. traditional biomass, heating oil)
UE.LCOE.build[,t.run,"Inconvenience Cost"] <- p36_inconvpen / pm_fe2es * 1.2 / s_twa2mwh * 1e12
# taxes on ES level (note: these taxes are also subsumed under FE taxes in the se2fe LCOE)
UE.LCOE.build[,t.run,"ES Taxes"] <- ES_tax / pm_fe2es * 1.2 / s_twa2mwh * 1e12
# calibration factor mark-up on buildings technologies, these are other (inconvenience) cost to represent current UE shares, they fade-out over time
UE.LCOE.build[,t.run,"Calibration Factor Mark-up"] <- p36_logitCalibration * 1.2 / s_twa2mwh * 1e12
# total LCOE
UE.LCOE.build[,,"Total LCOE"] <- UE.LCOE.build[,,"Investment Cost (incl OMF and impl discount)"] +
UE.LCOE.build[,,"Fuel Cost"] +
UE.LCOE.build[,,"Inconvenience Cost"] +
UE.LCOE.build[,,"Calibration Factor Mark-up"] +
UE.LCOE.build[,,"ES Taxes"]
# add dimensions to fit to other tech LCOE
UE.LCOE.build <- UE.LCOE.build %>%
as.quitte() %>%
left_join(fe2es_dyn36) %>%
mutate( sector = "buildings", type = "marginal", unit ="US$2015/MWh", type ="marginal" ) %>%
rename(cost = data, tech = all_teEs, output = all_esty) %>%
select(region, period, type, output, tech, sector, unit, cost, value) %>%
as.magpie(datacol=9)
}
### DAC: calculate Levelized Cost of CO2 from direct air capture
# DAC energy demand per unit captured CO2 (EJ/GtC)
p33_dac_fedem <- readGDX(gdx, "p33_dac_fedem", restore_zeros = F)
LCOD <- new.magpie(getRegions(vm_costTeCapital), getYears(vm_costTeCapital),
c("Investment Cost","OMF Cost","Electricity Cost","Heat Cost","Total LCOE"))
# capital cost in trUSD2005/GtC -> convert to USD2015/tCO2
LCOD[,,"Investment Cost"] <- vm_costTeCapital[,,"dac"] * 1.2 / 3.66 /vm_capFac[,,"dac"]*p_teAnnuity[,,"dac"]*1e3
LCOD[,,"OMF Cost"] <- pm_data_omf[,,"dac"]*vm_costTeCapital[,,"dac"] * 1.2 / 3.66 /vm_capFac[,,"dac"]*1e3
# elecitricty cost (convert DAC FE demand to GJ/tCO2 and fuel price to USD/GJ)
LCOD[,,"Electricity Cost"] <- p33_dac_fedem[,,"feels"] / 3.66 * Fuel.Price[,,"seel"] / 3.66
# conversion as above, assume for now that heat is always supplied by H2
LCOD[,,"Heat Cost"] <- p33_dac_fedem[,,"feh2s"] / 3.66 * Fuel.Price[,,"seh2"] / 3.66
LCOD[,,"Total LCOE"] <- LCOD[,,"Investment Cost"]+LCOD[,,"OMF Cost"]+LCOD[,,"Electricity Cost"]+LCOD[,,"Heat Cost"]
getSets(LCOD)[3] <- "cost"
# add dimensions to fit to other tech LCOE
LCOD <- LCOD %>%
add_dimension(add = "unit", nm = "US$2015/tCO2") %>%
add_dimension(add = "tech", nm = "dac") %>%
add_dimension(add = "output", nm = "cco2") %>%
add_dimension(add = "type", nm = "marginal") %>%
add_dimension(add = "sector", dim=3.4, nm = "supply-side")
# transform marginal LCOE to mif output format
df.LCOE.out <- df.LCOE %>%
select(region, period, tech, output, sector, `Investment Cost`, `OMF Cost`, `OMV Cost`, `Fuel Cost` ,
`CO2 Tax Cost`,`CO2 Provision Cost`,`Second Fuel Cost`,`VRE Storage Cost` ,`Grid Cost`, `Curtailment Cost`,
`CCS Tax Cost`, `CCS Cost`,`Flex Tax`,`FE Tax`,`Total LCOE`) %>%
gather(cost, value, -region, -period, -tech, -output, -sector) %>%
mutate(unit = "US$2015/MWh", type="marginal") %>%
select(region, period, type, output, tech, sector, unit, cost, value)
LCOE.mar.out <- as.magpie(df.LCOE.out, spatial = 1, temporal = 2, datacol=9)
# add DAC levelized cost, buildings UE LCOE to marginal LCOE
LCOE.mar.out <- mbind(LCOE.mar.out, LCOD)
if (any(module2realisation$`*` == "services_putty")) {
LCOE.mar.out <- mbind(LCOE.mar.out, UE.LCOE.build)
}
# bind to previous calculations (if there are)
LCOE.out <- mbind(LCOE.out,LCOE.mar.out)
}
### calculate global average LCOE for region "World"
LCOE.out.inclGlobal <- new.magpie(c(getRegions(LCOE.out),"GLO"), getYears(LCOE.out), getNames(LCOE.out))
LCOE.out.inclGlobal[getRegions(LCOE.out),,] <- LCOE.out
LCOE.out.inclGlobal["GLO",,] <- dimSums(LCOE.out, dim=1) / length(getRegions(LCOE.out))
if (output.type %in% c("marginal detail")) {
return(df.LCOE)
} else {
return(LCOE.out.inclGlobal)
}
return(LCOE.out)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.