Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding average LCO-ccsinje and adjustment cost bugfixes #492

Merged
merged 10 commits into from
Jan 4, 2024
2 changes: 1 addition & 1 deletion .buildlibrary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ValidationKey: '221622198'
ValidationKey: '221653148'
AcceptedWarnings:
- 'Warning: package ''.*'' was built under R version'
- 'Warning: namespace ''.*'' is not available and has been replaced'
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ cff-version: 1.2.0
message: If you use this software, please cite it using the metadata from this file.
type: software
title: 'remind2: The REMIND R package (2nd generation)'
version: 1.125.1
date-released: '2023-12-07'
version: 1.125.2
date-released: '2023-12-08'
abstract: Contains the REMIND-specific routines for data and model output manipulation.
authors:
- family-names: Rodrigues
Expand Down
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Type: Package
Package: remind2
Title: The REMIND R package (2nd generation)
Version: 1.125.1
Date: 2023-12-07
Version: 1.125.2
Date: 2023-12-08
Authors@R: c(
person("Renato", "Rodrigues", , "[email protected]", role = c("aut", "cre")),
person("Lavinia", "Baumstark", role = "aut"),
Expand Down
69 changes: 51 additions & 18 deletions R/reportLCOE.R
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ reportLCOE <- function(gdx, output.type = "both"){

# read in general data (needed for average and marginal LCOE calculation)
s_twa2mwh <- readGDX(gdx,c("sm_TWa_2_MWh","s_TWa_2_MWh","s_twa2mwh"),format="first_found")
s_GtC2tCO2 <- 10^9 * readGDX(gdx,c("sm_c_2_co2","s_c_2_co2"),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)])
Expand Down Expand Up @@ -126,8 +127,9 @@ reportLCOE <- function(gdx, output.type = "both"){
p47_taxCO2eq_AggFE <- readGDX(gdx,"p47_taxCO2eq_AggFE", restore_zeros=F, react = "silent")

## variables
v_directteinv <- readGDX(gdx,name=c("v_costInvTeDir","vm_costInvTeDir","v_directteinv"),field="l",format="first_found")[,ttot,]
v_directteinv_wadj <- readGDX(gdx,name=c("o_avgAdjCostInv"),field="l",format="first_found")[,ttot,]
v_directteinv <- readGDX(gdx,name=c("v_costInvTeDir","vm_costInvTeDir","v_directteinv"),field="l",format="first_found")[,ttot,] ## Total direct Investment Cost in Timestep
#v_directteinv_wadj <- readGDX(gdx,name=c("o_avgAdjCostInv"),field="l",format="first_found")[,ttot,] ## average adjustment cost per unit installed in period
v_adjteinv <- readGDX(gdx,name=c("v_costInvTeAdj"),field="l",format="first_found")[,ttot,] ## total adjustment cost in period
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the R variable can also be called "v_costInvTeAdj"? normally I think the convention is to go with the REMIND variable name unless it is not possible.. (like v_directteinv above this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree in principle, but wanted to stick with the convention used in this part of the script. "v_costInvTeDir" turns into "v_directteinv", and I just applied the same logic. What did you mean saying "(like v_directteinv above this)"?

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")
Expand Down Expand Up @@ -179,7 +181,7 @@ reportLCOE <- function(gdx, output.type = "both"){
te_inv_annuity_wadj <- 1e+12 * te_annuity[,,te] *
mbind(
v_investcost[,ttot_before2005,te] * dimSums(vm_deltaCap[teall2rlf][,ttot_before2005,te],dim=3.2),
v_directteinv_wadj[,ttot_from2005,te]
v_adjteinv[,ttot_from2005,te] + v_directteinv[,ttot_from2005,te]
)

# average LCOE components ----
Expand Down Expand Up @@ -237,12 +239,28 @@ reportLCOE <- function(gdx, output.type = "both"){

# 2. sub-part: fuel cost ----

# fuel cost = PE price * PE demand of technology
# 2.1 primary 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)

# 2.2 second fuel cost - !only CCSinje for now!
# pm_prodCouple: Second fuel production or demand per unit output of technology. Negative values mean own consumption, positive values mean coupled product.
pm_prodCouple <- readGDX(gdx, "pm_prodCouple", restore_zeros = F)
# potential fuels and prices: not needed in this detail here, but copying approach in marginal section
fuels <- c("peoil","pegas","pecoal","peur", "pebiolc" , "pebios","pebioil",
"seel","seliqbio", "seliqfos","seliqsyn", "sesobio","sesofos","seh2","segabio" ,
"segafos","segasyn","sehe")
pm_PEPrice <- readGDX(gdx, "pm_PEPrice", restore_zeros = F)
pm_SEPrice <- readGDX(gdx, "pm_SEPrice", restore_zeros = F)
Fuel.Price <- mbind(pm_PEPrice,pm_SEPrice )[,,fuels]*1e12 # convert from trUSD2005/TWa to USD2005/TWa

# calculate secondary Fuel cost for ccsinje
te_annual_secFuel_cost <- new.magpie(getRegions(te_inv_annuity),ttot_from2005, "ccsinje" , fill=0)
te_annual_secFuel_cost[,,"ccsinje"] <- setNames(-pm_prodCouple[,,"ccsinje"] * Fuel.Price[,,"seel"] * vm_co2CCS[,,"ccsinje.1"], "ccsinje")
Copy link
Contributor

@cchrisgong cchrisgong Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am probably missing something, but why negative here? (-pm_prodCouple) oh I see nevermind it is because CCS consumes energy, would be nice to write this more explicitly (like below)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could also write a short documentation of the meanings of the three things multiplied here, I read it as
negative sign (so pm_prodCouple turns positive) * energy demand of CO2 injection (pm_prodCouple) * secondary electricity price * amount of CO2 captured (with units like below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It says in line 249 that negative values mean own consumption, positive values mean coupled product. As I want positive cost, I need to multiply.
I can adjust the line with the units if you want to have a more detailed documentation.

# units: pm_prodCouple[,,"ccsinje"] = [TWa/GtC]; Fuel.Price = [USD2005/GtC]; vm_co2CCS= [GtC]; te_annual_secFuel_cost = [USD2005]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuel price has unit USD2005/TWa, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, will adjust


# 3. sub-part: OMV cost ----

# omv cost (from pm_data) * SE production
Expand Down Expand Up @@ -308,7 +326,7 @@ reportLCOE <- function(gdx, output.type = "both"){
vm_VRE_prodSe_grid


# 7. sub-part: ccs injection cost ----
# 7. sub-part: ccs injection cost (for technologies capturing CO2) ----

# same as for storage/grid but with ccs inejection technology
# distributed to technolgies according to share of total captured co2 of ccs technology
Expand All @@ -319,8 +337,8 @@ reportLCOE <- function(gdx, output.type = "both"){

# 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_OMF_cost[,,"ccsinje"] + te_annual_secFuel_cost[,,"ccsinje"])
# distribute ccs injection cost over techs => this does not include adjustment cost incurred! => what is the reasoning behind it?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't quite understand this comment, do you mean why is adjustment cost not added as a OMF cost? What is the unit of total_ccsInj_cost?

Btw I don't quite get the formula here; shouldn't the total cost = annuitized (investment cost + other fixed costs (OMF, adjCost)) + running cost (energy cost, OMV cost, CO2 tax)... It looks like OMF and energy cost are here, but the others(?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this in the commit accidentally. The point is that only ccs injection cost without adjustment cost are distributed across technologies. I just wondered why there is no version that includes adjustment cost as these can be very substantial

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general formula for total cost should be (and is):
annuitized (investment cost + adjCost) + OMF cost (don't need to be annuitized) + running cost (energy cost, OMV cost, CO2 tax).
The ccs cost only has investment cost + OMF cost + now also energy cost
Adjustment cost are missing, that was precisely the point of my comment

te_annual_ccsInj_cost[,,teCCS] <- setNames(total_ccsInj_cost * dimSums(v_emiTeDetail[,,"cco2"][,,teCCS], dim = c(3.1,3.2,3.4), na.rm = T) /
dimSums( v_emiTeDetail[,,"cco2"], dim = 3, na.rm = T),
teCCS)
Expand Down Expand Up @@ -384,7 +402,7 @@ reportLCOE <- function(gdx, output.type = "both"){
te_annual_ccsInj_cost +
te_annual_co2_cost

# calculate total energy production
####### 10. sub-part: calculate total energy production and carbon storage #################################
# 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))),
Expand All @@ -406,21 +424,24 @@ reportLCOE <- function(gdx, output.type = "both"){
total_te_energy_usable <- total_te_energy
total_te_energy_usable[,,teVRE] <- total_te_energy[,,teVRE] - v32_storloss[,ttot_from2005,teVRE]*s_twa2mwh

# change unit of stored CO2 from GtC to tCO2
vm_co2CCS_tCO2 <- vm_co2CCS*s_GtC2tCO2



# LCOE Calculation (average) ----
LCOE.avg <- NULL
####################################################
######### 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)
# exceptions: grid and storage cost are calculate by dividing by generation after curtailment
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add another exception here that for CCS, it is divided by CO2 (or C?) stored

# convert from USD2005/MWh (or tCO2) to USD2015/MWh (or tCO2) (*1.2)
LCOE.avg <- mbind(
setNames(te_annual_inv_cost[,getYears(te_annual_fuel_cost),pe2se$all_te]/
#### Energy technologies (pe2se$all_te)
setNames(te_annual_inv_cost[,ttot_from2005,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_inv_cost_wadj[,getYears(te_annual_fuel_cost),pe2se$all_te]/
setNames(te_annual_inv_cost_wadj[,ttot_from2005,pe2se$all_te]/
total_te_energy[,,pe2se$all_te],
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te,"|supply-side", "|Investment Cost w/ Adj Cost")),
setNames(te_annual_fuel_cost[,,pe2se$all_te]/total_te_energy[,,pe2se$all_te],
Expand All @@ -445,7 +466,18 @@ LCOE.avg <- NULL
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"))
paste0("LCOE|average|",pe2se$all_enty1,"|",pe2se$all_te, "|supply-side","|Curtailment Cost")),
#### Carbon Transport and storage ("ccsinje")
setNames(te_annual_inv_cost[,ttot_from2005,"ccsinje"]/
vm_co2CCS_tCO2[,,"ccsinje.1"],
paste0("LCOx|average|","cco2_stored|","ccsinje","|supply-side", "|Investment Cost")),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of calling it LCOx, maybe something more concrete like "LCOCS" Levelized cost of carbon/Co2 storage?

Copy link
Contributor

@cchrisgong cchrisgong Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we give "cco2_stored" in the variable name? I thought usually for readability we have something more like "Captured CO2|Injected CO2 |Supply side|..." etc. not sure what the convention is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called it "cco2_stored" because the convention for the other items in this dimension is pe2se$all_te. In my understanding, "cco2" would be the remind element corresponding to that. However, it is not about cco2 (all captured co2) but only the part of it that is stored, thus the additional "_stored"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about LCOx vs LCOCS: I think that is a general question of how we want to call "LCO other than E" in this script. There are plans to also add LCO of CDR options. Do we want to give all of them their own abbreviation? Or do we want to have a generic LCOx and then filter according to the technologies, as it is the case with LCOE?

setNames(te_annual_inv_cost_wadj[,ttot_from2005,"ccsinje"]/
vm_co2CCS_tCO2[,,"ccsinje.1"],
paste0("LCOx|average|","cco2_stored|","ccsinje","|supply-side", "|Investment Cost w/ Adj Cost")),
setNames(te_annual_OMF_cost[,,"ccsinje"]/vm_co2CCS_tCO2[,,"ccsinje.1"],
paste0("LCOx|average|","cco2_stored|","ccsinje", "|supply-side","|OMF Cost")),
setNames(te_annual_secFuel_cost[,,"ccsinje"]/vm_co2CCS_tCO2[,,"ccsinje.1"],
paste0("LCOE|average|","cco2_stored|","ccsinje", "|supply-side","|Second Fuel Cost"))
)*1.2

# convert to better dimensional format
Expand All @@ -472,7 +504,8 @@ LCOE.avg <- NULL
df.lcoe.avg$cost <- sapply(df.lcoe.avg$cost, "[[", 6)

df.lcoe.avg <- df.lcoe.avg %>%
mutate( unit = "US$2015/MWh") %>%
mutate( unit = "US$2015/MWh") %>%
mutate(unit = case_when(tech=="ccsinje" & output=="cco2_stored" ~ "US$2015/tCO2", TRUE ~ unit)) %>%
select(region, period, type, output, tech, sector, unit, cost, value)

# reconvert to magpie object
Expand Down
11 changes: 9 additions & 2 deletions R/reportTechnology.R
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ reportTechnology <- function(gdx, output = NULL, regionSubsetList = NULL, t = c(

tmp <- bind_category(tmp, v_investcost, category, unit, factor, techmap)
int2ext <- get_global_mapping(category, unit, techmap)

if (CDR_mod != "off") {
unit <- "US$2005/tCO2 yr"
factor <- 1000 / 3.6
tmp <- bind_category(tmp, v_investcost, category, unit, factor, cdrmap)
int2ext <- c(int2ext, get_global_mapping(category, unit, cdrmap))
}

### Capital cost including adjustment cost ----
if (!is.null(v_adjustteinv_avg)) {
Expand All @@ -263,14 +270,14 @@ reportTechnology <- function(gdx, output = NULL, regionSubsetList = NULL, t = c(

if (tran_mod == "complex") {
unit <- "US$2005/veh"
tmp <- bind_category(tmp, v_investcost, category, unit, factor, carmap)
tmp <- bind_category(tmp, v_investcost + v_adjustteinv_avg, category, unit, factor, carmap)
int2ext <- c(int2ext, get_global_mapping(category, unit, carmap))
}

if (CDR_mod != "off") {
unit <- "US$2005/tCO2 yr"
factor <- 1000 / 3.6
tmp <- bind_category(tmp, v_investcost, category, unit, factor, cdrmap)
tmp <- bind_category(tmp, v_investcost + v_adjustteinv_avg, category, unit, factor, cdrmap)
int2ext <- c(int2ext, get_global_mapping(category, unit, cdrmap))
}

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The REMIND R package (2nd generation)

R package **remind2**, version **1.125.1**
R package **remind2**, version **1.125.2**

[![CRAN status](https://www.r-pkg.org/badges/version/remind2)](https://cran.r-project.org/package=remind2) [![R build status](https://github.com/pik-piam/remind2/workflows/check/badge.svg)](https://github.com/pik-piam/remind2/actions) [![codecov](https://codecov.io/gh/pik-piam/remind2/branch/master/graph/badge.svg)](https://app.codecov.io/gh/pik-piam/remind2) [![r-universe](https://pik-piam.r-universe.dev/badges/remind2)](https://pik-piam.r-universe.dev/builds)

Expand Down Expand Up @@ -49,7 +49,7 @@ In case of questions / problems please contact Renato Rodrigues <renato.rodrigue

To cite package **remind2** in publications use:

Rodrigues R, Baumstark L, Benke F, Dietrich J, Dirnaichner A, Führlich P, Giannousakis A, Hasse R, Hilaire J, Klein D, Koch J, Kowalczyk K, Levesque A, Malik A, Merfort A, Merfort L, Morena-Leiva S, Pehl M, Pietzcker R, Rauner S, Richters O, Rottoli M, Schötz C, Schreyer F, Siala K, Sörgel B, Spahr M, Strefler J, Verpoort P, Weigmann P (2023). _remind2: The REMIND R package (2nd generation)_. R package version 1.125.1, <URL: https://github.com/pik-piam/remind2>.
Rodrigues R, Baumstark L, Benke F, Dietrich J, Dirnaichner A, Führlich P, Giannousakis A, Hasse R, Hilaire J, Klein D, Koch J, Kowalczyk K, Levesque A, Malik A, Merfort A, Merfort L, Morena-Leiva S, Pehl M, Pietzcker R, Rauner S, Richters O, Rottoli M, Schötz C, Schreyer F, Siala K, Sörgel B, Spahr M, Strefler J, Verpoort P, Weigmann P (2023). _remind2: The REMIND R package (2nd generation)_. R package version 1.125.2, <URL: https://github.com/pik-piam/remind2>.

A BibTeX entry for LaTeX users is

Expand All @@ -58,7 +58,7 @@ A BibTeX entry for LaTeX users is
title = {remind2: The REMIND R package (2nd generation)},
author = {Renato Rodrigues and Lavinia Baumstark and Falk Benke and Jan Philipp Dietrich and Alois Dirnaichner and Pascal Führlich and Anastasis Giannousakis and Robin Hasse and Jérome Hilaire and David Klein and Johannes Koch and Katarzyna Kowalczyk and Antoine Levesque and Aman Malik and Anne Merfort and Leon Merfort and Simón Morena-Leiva and Michaja Pehl and Robert Pietzcker and Sebastian Rauner and Oliver Richters and Marianna Rottoli and Christof Schötz and Felix Schreyer and Kais Siala and Björn Sörgel and Mike Spahr and Jessica Strefler and Philipp Verpoort and Pascal Weigmann},
year = {2023},
note = {R package version 1.125.1},
note = {R package version 1.125.2},
url = {https://github.com/pik-piam/remind2},
}
```