#### methods for drawing schematic representation of processes
#' Draw a schematic representation of a process
#'
#' A generic method for drawing a schematic representation of
#' processes-type classes.
#'
#' @param object The object to draw: `technology`, `storage`, `trade`, `demand`, `supply`,
#' `export`, or `import`.
#' @param ... Additional arguments passed to the specific method.
#'
#' @family draw
#' @rdname draw
#' @return displays a schematic representation of the process, returns `NULL`.
#'
#' @export
setGeneric("draw", function(obj, ...) standardGeneric("draw"))
## Constants ####
keys <- c(
"region", "year", "slice", "comm", "acomm",
# "value",
"lab_par", "lab_txt",
"tech", "group", "weather", "unit", "io", "parameter"
)
# fixing "no visible binding for global variable" warnings
utils::globalVariables(
c(
"value", "parameter", "comm", "acomm", "unit", "weather",
"lab_par", "lab_txt", "lab_waf", "lab_wcinp", "lab_wafs",
"lab_wafc", "lab_waf", "lab_wafs", "lab_par", "lab_txt",
"ioname", "iotype", "group", "waf.fx", "waf.lo", "waf.up",
"wafs.fx", "wafs.lo", "wafs.up", "wafc.fx", "wafc.lo", "wafc.up",
"ainp", "aout", "wacinp.fx", "wacinp.lo", "wacinp.up",
"wacout.fx", "wacout.lo", "wacout.up", "wacact.fx", "wacact.lo",
"wcinp.fx", "wcinp.lo", "wcinp.up", "wcout.fx", "wcout.lo", "wcout.up",
"src", "dst", "region", "year", "slice",
"cap2act", "cap2stg", "cap2use",
"io", "na.omit", "share.lo", "share.up", "share.fx",
"val_lbl", "where", "ginp2use", "desc", "x", "y"
)
)
## draw.technology ####
draw.technology <- function(obj, ...) {
# browser()
# com_tbl <- bind_rows(
# obj@input |> mutate(io = "cinp", .before = 1),
# obj@output |> mutate(io = "cout", .before = 1)
# ) |>
com_inp <- obj@input |>
mutate(io = "cinp", .before = 1) |>
rowwise() |>
mutate(
lab_txt = make_label(
comm,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
) |>
# add technology parameters
left_join(obj@ceff, by = "comm") |>
pivot_longer(
cols = matches("2"), # non-grouped-comm-params have "2" in their names
names_to = "parameter",
values_to = "value"
) |>
filter(!grepl("cact|out", parameter)) |>
group_by(io, comm) |>
filter(!is.na(value) | (all(is.na(value)) & row_number() == 1)) |>
ungroup()
com_out <- obj@output |>
mutate(io = "cout", .before = 1) |>
rowwise() |>
mutate(
lab_txt = make_label(
comm,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
) |>
# add technology parameters
left_join(obj@ceff, by = "comm") |>
pivot_longer(
cols = matches("2"), # non-grouped-comm-params have "2" in their names
names_to = "parameter",
values_to = "value"
) |>
filter(grepl("cact|out", parameter)) |>
group_by(io, comm) |>
filter((!is.na(value)) |
(all(is.na(value)) & row_number() == 1)) |>
ungroup()
com_par <- bind_rows(com_inp, com_out)
# # add technology parameters
# com_par <- com_tbl |>
# full_join(obj@ceff, by = "comm") |>
# pivot_longer(
# cols = matches("2"), # non-grouped-comm-params have "2" in their names
# names_to = "parameter",
# values_to = "value"
# ) |>
# group_by(io, comm) |>
# filter(!is.na(value) | (is.na(value) & row_number() == 1))
# parameter-labels for grouped commodities
# gcom_par <- com_par |> filter(!is.na(group))
gcom_inp <- com_inp |> filter(!is.na(group))
gcom_out <- com_out |> filter(!is.na(group))
gcom_par <- bind_rows(gcom_inp, gcom_out)
if (nrow(gcom_par) > 0) {
gcom_par <- gcom_par |>
group_by(
across(
any_of(c("comm", "acomm", "group", "unit", "io", "parameter"))
)
) |>
summarise(
val_lbl = make_label(
paste0(parameter, ":"),
# in_brackets = prettyNum(value, digits = 2),
in_brackets = format_number(value),
two_lines = if_else(all(grepl("use2cact", parameter)), TRUE, FALSE),
bracket_type = NULL
),
share_lbl = paste0(
# paste0(round(100 * min(share.lo, share.fx, na.rm = TRUE), 2), "%,",
# round(100 * max(share.up, share.fx, na.rm = TRUE), 2), "%")
paste0(
min(share.lo, share.fx, na.rm = TRUE), ",",
max(share.up, share.fx, na.rm = TRUE)
)
),
lab_par = make_label(
val_lbl,
in_brackets = if_else(all(grepl("use2cact", parameter)),
NA,
share_lbl
),
two_lines = TRUE,
bracket_type = "square"
),
# lab_use2cact = make_label(
# val_lbl,
# in_brackets = val_lbl,
# two_lines = FALSE
# ),
.groups = "drop"
) |>
rowwise() |>
mutate(
lab_par = if_else(
grepl("use2cact", parameter),
val_lbl,
lab_par
),
lab_txt = make_label(
comm,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
) |>
select(-val_lbl, -share_lbl) |>
select(-any_of(c("region", "year", "slice")))
# as.data.table()
gcom_par$lab_par
gcom_par
}
# parameter-labels for non-grouped commodities
ccom_par <- com_par |>
filter(is.na(group)) |>
group_by(across(
any_of(keys)
# any_of(c("io", "comm", "region", "year", "slice", "parameter"))
)) |>
summarise(
lab_par = make_label(
paste0(parameter, ":"),
in_brackets = value,
two_lines = FALSE
),
.groups = "drop"
) |>
select(-any_of(c("region", "year", "slice")))
ccom_par
# auxiliary inputs ####
aux_tbl <- obj@aux |>
full_join(obj@aeff, by = "acomm")
ainp <- aux_tbl |>
select(
# any_of(c("acomm", "comm", "region", "year", "slice", "unit")),
any_of(c(keys)),
matches("ainp")
) |>
rowwise() |>
mutate(
ainp = sum(abs(c_across(matches("ainp"))), na.rm = TRUE)
) |>
filter(ainp != 0) |>
# drop numeric columns columns with all NAs
select(-where(~ all(is.na(.)) & is.numeric(.)), -ainp) |>
mutate(io = "ainp", .before = 1)
ainp
# aux outputs ####
aout <- aux_tbl |>
select(
# any_of(c("acomm", "comm", "region", "year", "slice", "unit")),
any_of(c(keys)),
matches("aout")
) |>
rowwise() |>
mutate(
aout = sum(abs(c_across(matches("aout"))), na.rm = TRUE)
) |>
filter(aout != 0) |>
# drop numeric columns columns with all NAs
select(-where(~ all(is.na(.)) & is.numeric(.)), -aout) |>
mutate(io = "aout", .before = 1)
aout
# aux combined
# browser()
aux <- bind_rows(ainp, aout)
if (nrow(aux) > 0) {
aux <- aux |>
pivot_longer(
# cols = -any_of(c("io", "acomm", "comm", "region", "year", "slice", "unit")),
cols = -any_of(c(keys)),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(io, acomm, comm, unit, parameter) |>
summarise(
lab_par = make_label(
paste0(parameter, ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "keep"
) |>
mutate(
lab_txt = make_label(
acomm,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
lab_par = if_else(is.na(comm),
lab_par,
make_label(lab_par, in_brackets = comm, two_lines = TRUE)
)
) |>
ungroup()
}
aux
# weather factors
wea <- obj@weather |>
rowwise() |>
mutate(
lab_wafc = if_else(
!is.na(wafc.fx),
# create a label with the fixed value, ignore the lo and up values
make_label("wafc.fx:", in_brackets = wafc.fx, two_lines = FALSE),
# "A",
# create a label with the lo and up values
# NA
if_else(!is.na(wafc.lo) & !is.na(wafc.up),
paste0("wafc.lo: ", wafc.lo, "\n", "wafc.up: ", wafc.up),
if_else(!is.na(wafc.lo),
# create a label with the lo value
paste0("wafc.lo: ", wafc.lo),
# create a label with the up value
if_else(!is.na(wafc.up),
paste0("wafc.up: ", wafc.up),
# if all values are NA, return NA
NA_character_
)
)
)
)
) |>
select(-wafc.lo, -wafc.up, -wafc.fx) |>
mutate(
lab_waf = if_else(
!is.na(waf.fx),
make_label("waf.fx:", in_brackets = waf.fx, two_lines = FALSE),
if_else(!is.na(waf.lo) & !is.na(waf.up),
paste0("waf.lo: ", waf.lo, "\n", "waf.up: ", waf.up),
if_else(!is.na(waf.lo),
paste0("waf.lo: ", waf.lo),
if_else(!is.na(waf.up),
paste0("waf.up: ", waf.up),
NA_character_
)
)
)
)
) |>
select(-waf.lo, -waf.up, -waf.fx) |>
mutate(
lab_wafs = if_else(
!is.na(wafs.fx),
make_label("wafs.fx:", in_brackets = wafs.fx, two_lines = FALSE),
if_else(!is.na(wafs.lo) & !is.na(wafs.up),
paste0("wafs.lo: ", wafs.lo, "\n", "wafs.up: ", wafs.up),
if_else(!is.na(wafs.lo),
paste0("wafs.lo: ", wafs.lo),
if_else(!is.na(wafs.up),
paste0("wafs.up: ", wafs.up),
NA_character_
)
)
)
)
) |>
select(-wafs.lo, -wafs.up, -wafs.fx) |>
rowwise() |>
mutate(
lab_txt = if_else(
is.na(comm),
weather,
make_label(weather, in_brackets = comm, two_lines = FALSE)
),
lab_par = if_else(
all(is.na(c(lab_wafc, lab_waf, lab_wafs))),
NA_character_,
paste(na.omit(c(lab_wafc, lab_waf, lab_wafs)), collapse = "\n")
)
) |>
select(-lab_wafc, -lab_waf, -lab_wafs) |>
mutate(
io = "winp"
)
wea
geff <- obj@geff |>
pivot_longer(
cols = ginp2use,
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(across(any_of(keys))) |>
summarise(
lab_par = make_label(
paste0(parameter, ":"),
# NULL,
in_brackets = value,
two_lines = T
),
.groups = "drop"
) |>
mutate(
io = "ginp",
lab_txt = NA_character_
)
geff
# inputs to draw_process ####
grouped_com_inputs <- gcom_par |>
bind_rows(geff) |>
filter(grepl("inp", io)) |>
select(io, comm, group, parameter, lab_par) |>
rename(ioname = comm, iotype = io) |>
unique()
single_com_inputs <- ccom_par |>
filter(grepl("inp", io)) |>
select(io, comm, parameter, lab_par) |>
rename(ioname = comm, iotype = io) |>
unique()
aux_inputs <- aux |>
filter(grepl("inp", io)) |>
select(any_of(c(
"io", "acomm", "parameter", "lab_par"
))) |>
rename(ioname = acomm, iotype = io) |>
unique()
weather_factors <- wea |>
select(io, weather, lab_par) |>
rename(ioname = weather, iotype = io) |>
unique()
# outputs to draw_process ####
grouped_com_outputs <- gcom_par |>
filter(grepl("out", io)) |>
select(any_of(c(
"comm", "value", "lab_txt", "lab_par", "group", "unit",
"io", "parameter", "lab_par"
))) |>
rename(ioname = comm, iotype = io) |>
unique()
single_com_outputs <- ccom_par |>
select(any_of(c(
"comm", "value", "lab_txt", "lab_par", "group", "unit",
"io", "parameter", "lab_par"
))) |>
filter(grepl("out", io)) |>
rename(ioname = comm, iotype = io) |>
unique()
aux_outputs <- aux |>
filter(grepl("out", io)) |>
select(any_of(c(
"io", "acomm", "parameter", "lab_par"
))) |>
rename(ioname = acomm, iotype = io) |>
unique()
# arrow_labels (all inputs and outputs) ####
arrow_labels_tb <- data.table()
if (nrow(gcom_par) > 0) {
arrow_labels_tb <- rbindlist(
list(
arrow_labels_tb,
gcom_par |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = comm)
),
use.names = TRUE, fill = TRUE
)
}
if (nrow(ccom_par) > 0) {
arrow_labels_tb <- rbindlist(
list(
arrow_labels_tb,
ccom_par |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = comm)
),
use.names = TRUE, fill = TRUE
)
}
if (nrow(aux) > 0) {
arrow_labels_tb <- rbindlist(
list(
arrow_labels_tb,
aux |> select(any_of(c("acomm", "lab_txt"))) |> unique() |> rename(ioname = acomm)
),
use.names = TRUE, fill = TRUE
)
}
if (nrow(wea) > 0) {
arrow_labels_tb <- rbindlist(
list(
arrow_labels_tb,
wea |> select(any_of(c("weather", "lab_txt"))) |> unique() |> rename(ioname = weather)
),
use.names = TRUE, fill = TRUE
)
}
# arrow_labels_tb <- rbindlist(list(
# gcom_par |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = comm),
# ccom_par |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = comm),
# aux |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = acomm),
# wea |> select(any_of(c("comm", "lab_txt"))) |> unique() |> rename(ioname = weather)
# ),
# use.names = TRUE, fill = TRUE
# )
# cap2act ####
cap2act_label <- paste0("cap2act: ", obj@cap2act)
stopifnot(length(unique(arrow_labels_tb$ioname)) == nrow(arrow_labels_tb))
arrow_labels <- arrow_labels_tb$lab_txt
names(arrow_labels) <- arrow_labels_tb$ioname
stopifnot(length(arrow_labels) == nrow(arrow_labels_tb))
try(
draw_process(
process_name = obj@name,
process_desc = obj@desc,
grouped_com_inputs = grouped_com_inputs,
single_com_inputs = single_com_inputs,
aux_inputs = aux_inputs,
weather_factors = weather_factors,
grouped_com_outputs = grouped_com_outputs,
single_com_outputs = single_com_outputs,
# com_outputs = com_outputs,
aux_outputs = aux_outputs,
arrow_labels = arrow_labels,
center_label = cap2act_label,
show_iuao_labels = TRUE
)
)
} # end of draw.technology
#' Draw a schematic representation of a technology
#'
#' @export
#' @family draw technology
#' @rdname draw
#'
#' @include generics.R
#'
#' @examples
#' TECH01 <- newTechnology(
#' "TECH01",
#' desc = "Technology Description",
#' input = data.frame(
#' comm = c("COM1", "COM2", "COM5", "COM7", "COM8", "COM9"),
#' group = c("1", "1", NA, "2", "2", "2"),
#' unit = c("unit1", "unit2", "unit5", "unit7", "unit8", "unit9")
#' ),
#' output = data.frame(
#' comm = c("COM3", "COM4", "COM6"),
#' group = c("3", NA, "3"),
#' unit = c("unit3", "unit4", "unit6")
#' ),
#' group = data.frame(
#' group = c("1", "2", "3"),
#' desc = c("Group1", "Group2", "Group3"),
#' unit = "unit"
#' ),
#' aux = data.frame(
#' acomm = c("AUX1", "AUX2", "AUX3", "AUX4"),
#' unit = c("unit1", "unit2", "unit3", "unit4")
#' ),
#' region = c("R1", "R2", "R3"),
#' geff = data.frame(
#' group = c("1", "2"),
#' ginp2use = c(0.12, 0.789)
#' ),
#' ceff = data.frame(
#' comm = c("COM1", "COM2", "COM5", "COM7", "COM8", "COM9", "COM3", "COM4", "COM6"),
#' cinp2ginp = c(.1, .2, NA, .7, .8, .9, rep(NA, 3)),
#' cinp2use = c(NA, NA, .5, NA, NA, NA, rep(NA, 3)),
#' use2cact = c(rep(NA, 6), .36, .4, .36),
#' cact2cout = c(rep(NA, 6), .3, NA, .6),
#' share.lo = c(.01, .02, NA, .07, .08, .0, .03, NA, .06),
#' share.up = c(.91, .92, NA, .97, .98, 1, .83, NA, .96)
#' ),
#' aeff = data.frame(
#' acomm = c("AUX1", "AUX2", "AUX3", "AUX4"),
#' comm = c(NA, "COM1", NA, "COM3"),
#' act2ainp = c(1, NA, NA, NA),
#' cinp2aout = c(NA, 2, NA, NA),
#' cap2aout = c(NA, NA, 3, NA),
#' cout2aout = c(NA, NA, NA, 4)
#' ),
#' weather = data.frame(
#' weather = "WEATHER_CF1",
#' waf.up = .99
#' )
#' )
#' draw(TECH01)
setMethod("draw", "technology", function(obj, ...) {
draw.technology(obj, ...)
})
## draw.storage ####
draw.storage <- function(obj, ...) {
keys <- c(
"region", "year", "slice", "comm", "acomm",
# "value",
"lab_par", "lab_txt",
"tech", "group", "weather", "unit", "io", "parameter"
)
# browser()
comm <- data.frame(
comm = obj@commodity
# unit = obj@unit
) |>
cross_join(obj@seff) |>
pivot_longer(
cols = matches("eff"),
names_to = "parameter",
values_to = "value"
) |>
group_by(comm, parameter) |>
summarize(
lab_par = make_label(
paste0(parameter, ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
mutate(
group = NA_character_,
# io = "cinp",
# lab_txt = make_label(
# comm,
# in_brackets = obj@unit,
# two_lines = FALSE
# )
lab_txt = comm
# ioname = comm
)
comm
com_txt <- comm |>
select(comm, lab_txt) |>
unique()
com_inp <- comm |>
filter(grepl("inp", parameter)) |>
mutate(iotype = "cinp") |>
rename(ioname = comm)
com_out <- comm |>
filter(grepl("out", parameter)) |>
mutate(iotype = "cout") |>
rename(ioname = comm)
stg_par <- comm |>
filter(grepl("stg", parameter)) |>
mutate(cap2stg = obj@cap2stg, iotype = "stg") |>
rename(ioname = comm)
# aux
aux <- obj@aux |>
full_join(obj@aeff, by = "acomm") |>
pivot_longer(
cols = matches("2"), # non-grouped-comm-params have "2" in their names
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter, acomm, unit) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
mutate(
iotype = if_else(grepl("ainp$", parameter), "ainp", "aout"),
ioname = acomm,
lab_txt = make_label(
acomm,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
)
aux
aux_inputs <- aux |> filter(iotype == "ainp")
aux_outputs <- aux |> filter(iotype == "aout")
if (nrow(obj@weather) > 0) {
wea <- obj@weather |>
rowwise() |>
mutate(
lab_waf = if_else(
!is.na(waf.fx),
make_label("waf.fx:", in_brackets = waf.fx, two_lines = FALSE),
if_else(!is.na(waf.lo) & !is.na(waf.up),
paste0("waf.lo: ", waf.lo, "\n", "waf.up: ", waf.up),
if_else(!is.na(waf.lo),
paste0("waf.lo: ", waf.lo),
if_else(!is.na(waf.up),
paste0("waf.up: ", waf.up),
NA_character_
)
)
)
)
) |>
select(-waf.lo, -waf.up, -waf.fx) |>
mutate(
lab_wcinp = if_else(
!is.na(wcinp.fx),
make_label("wcinp.fx:", in_brackets = wcinp.fx, two_lines = FALSE),
if_else(!is.na(wcinp.lo) & !is.na(wcinp.up),
paste0("wcinp.lo: ", wcinp.lo, "\n", "wcinp.up: ", wcinp.up),
if_else(!is.na(wcinp.lo),
paste0("wcinp.lo: ", wcinp.lo),
if_else(!is.na(wcinp.up),
paste0("wcinp.up: ", wcinp.up),
NA_character_
)
)
)
)
) |>
select(-wcinp.lo, -wcinp.up, -wcinp.fx) |>
rowwise() |>
mutate(
lab_txt = if_else(
is.na(NA),
weather,
make_label(weather, in_brackets = obj@commodity, two_lines = FALSE)
),
lab_par = if_else(
all(is.na(c(lab_waf, lab_wcinp))),
NA_character_,
paste(na.omit(c(lab_waf, lab_wcinp)), collapse = "\n")
)
) |>
select(-lab_waf, -lab_wcinp) |>
rename(
ioname = weather
) |>
mutate(
iotype = "winp"
)
} else {
wea <- list(
lab_par = NULL,
lab_txt = NULL
)
}
wea
# center labels
center_labels <- c(
stg_par$lab_par,
paste0("cap2stg: ", obj@cap2stg)
) |>
paste(collapse = "\n")
# arrow_label ####
arrow_labels <- c(com_txt$lab_txt, aux$lab_txt, wea$lab_txt)
names(arrow_labels) <- c(com_txt$comm, aux$acomm, wea$ioname)
draw_process(
process_name = obj@name,
process_desc = obj@desc,
single_com_inputs = com_inp,
single_com_outputs = com_out,
aux_inputs = aux_inputs,
aux_outputs = aux_outputs,
weather_factors = wea,
# storage = stg_par,
arrow_labels = arrow_labels,
center_label = center_labels,
# show_inputs = TRUE,
# show_outputs = TRUE,
# show_aux = TRUE,
show_use_bar = FALSE,
show_act_bar = FALSE,
show_iuao_labels = FALSE
)
}
#' @export
#'
#' @family draw storage
#' @rdname draw
#'
#' @examples
#' STG_ELC <- newStorage(
#' name = "STG_ELC", # used in sets
#' desc = "Electricity storage (battery)", # for own reference
#' commodity = "ELECTRICITY", # must match the commodity name in the model
#' aux = data.frame(
#' acomm = "LITHIUM", # auxiliary commodity for battery production
#' unit = "ton" # unit of the auxiliary commodity
#' ),
#' start = data.frame(
#' start = 2020 # the first year of the process is available for installation
#' ),
#' end = data.frame(
#' end = 2030 # last year of the process is available for installation
#' ),
#' olife = data.frame(
#' olife = 20 # operational life of the storage in years
#' ),
#' seff = data.frame(
#' stgeff = 0.999, # storage efficiency
#' inpeff = 0.9, # charging efficiency
#' outeff = 0.9 # discharging efficiency
#' ),
#' aeff = data.frame(
#' acomm = "LITHIUM", # track lithium use for battery production
#' ncap2ainp = convert(4 * 250, "Wh/kg", "GWh/kt") # lithium per energy capacity
#' ),
#' af = data.frame(
#' # af.lo = 0., # lower bound for the capacity factor
#' af.up = 1. # upper bound for the capacity factor
#' ),
#' fixom = data.frame(
#' # region = "R1",
#' # year = 2020,
#' fixom = 0.9 # fixed operation and maintenance cost
#' ),
#' cap2stg = 4, # four-hours of storage
#' invcost = data.frame(
#' region = c("R1", NA), # region R1 and all other regions
#' invcost = c(1e3, 1.1e3) # investment cost in MUSD/GWh of 4-hour storage
#' ),
#' fullYear = TRUE, # full year storage cycle
#' weather = data.frame(
#' weather = "AMBIENT_TEMP", # weather factor for capacity factor
#' waf.up = 1 # affects upper boundary of capacity factor
#' # waf.lo = 0.9 # affects lower boundary of capacity factor
#' )
#' # region = c("R1", "R2", "R3"),
#' )
#' draw(STG_ELC)
#'
#' @exportMethod draw
setMethod("draw", "storage", draw.storage)
## draw.supply ####
draw.supply <- function(obj, ...) {
# keys <- c("region", "year", "slice", "comm", "acomm",
# # "value",
# "lab_par", "lab_txt",
# "tech", "group", "weather", "unit", "io", "parameter")
# browser()
if (nrow(obj@availability) == 0) {
sup_par <- data.frame(
lab_par = "",
lab_txt = "",
iotype = "cout",
ioname = obj@commodity,
group = NA_character_,
parameter = "sup"
)
} else {
sup_par <- obj@availability |>
pivot_longer(
cols = matches("ava|cost"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
# mutate(
# iotype = "cout",
# ioname = obj@commodity,
# group = NA_character_,
# lab_txt = make_label(
# obj@commodity,
# in_brackets = obj@unit,
# return_name_if_empty = TRUE,
# two_lines = FALSE
# )
# ) |>
mutate(
iotype = "cout",
ioname = obj@commodity,
group = NA_character_
) |>
group_by(ioname, iotype, group) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
.groups = "drop"
) |>
mutate(
lab_txt = make_label(
ioname,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
parameter = "sup"
)
}
arrow_labels <- make_label(
obj@commodity,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
names(arrow_labels) <- obj@commodity
# reserve
if (nrow(obj@reserve) > 0) {
res_par <- obj@reserve |>
pivot_longer(
cols = matches("res"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
mutate(
iotype = "cinp",
ioname = obj@commodity,
group = NA_character_,
lab_txt = make_label(
obj@commodity,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
) |>
mutate(
iotype = "cout",
ioname = obj@commodity,
group = NA_character_
) |>
group_by(ioname, iotype, group) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
.groups = "drop"
) |>
mutate(
lab_txt = make_label(
ioname,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
parameter = "sup"
)
res_par
} else {
res_par <- list(
lab_par = NULL,
lab_txt = NULL
)
}
draw_process(
process_name = obj@name,
process_desc = obj@desc,
single_com_outputs = sup_par,
center_label = res_par$lab_par,
arrow_labels = arrow_labels,
show_inputs = FALSE, # !!! add weather factors
show_outputs = TRUE,
show_use_bar = FALSE,
show_act_bar = FALSE,
show_iuao_labels = FALSE,
box_width = 0.25,
box_height = .4 * 1.5
)
}
#' @family draw supply
#' @rdname draw
#'
#' @export
#'
#' @examples
#' SUP_COA <- newSupply(
#' name = "SUP_COA",
#' desc = "Coal supply",
#' commodity = "COA",
#' unit = "PJ",
#' reserve = data.frame(
#' region = c("R1", "R2", "R3"),
#' res.up = c(2e5, 1e4, 3e6) # total reserves/deposits
#' ),
#' availability = data.frame(
#' region = c("R1", "R2", "R3"),
#' year = NA_integer_,
#' slice = "ANNUAL",
#' ava.up = c(1e3, 1e2, 2e2), # annual availability
#' cost = c(10, 20, 30) # cost of the resource (currency per unit)
#' ),
#' region = c("R1", "R2", "R3")
#' )
#' draw(SUP_COA)
setMethod("draw", "supply", draw.supply)
## draw.demand ####
draw.demand <- function(obj, ...) {
# browser()
dem_par <- obj@dem |>
pivot_longer(
cols = matches("dem"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
mutate(
iotype = "cinp",
ioname = obj@commodity,
group = NA_character_,
lab_txt = make_label(
obj@commodity,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
) |>
group_by(ioname, iotype, group) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
.groups = "drop"
) |>
mutate(
lab_txt = make_label(
ioname,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
parameter = "dem"
)
dem_par
arrow_labels <- make_label(
obj@commodity,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
names(arrow_labels) <- obj@commodity
draw_process(
process_name = obj@name,
process_desc = obj@desc,
single_com_inputs = dem_par,
arrow_labels = arrow_labels,
show_inputs = TRUE,
show_outputs = FALSE,
show_use_bar = FALSE,
show_act_bar = FALSE,
show_iuao_labels = FALSE,
box_width = 0.2,
box_height = .4 * 1.5
)
}
#' @family draw demand
#' @rdname draw
#'
#' @examples
#' DSTEEL <- newDemand(
#' name = "DSTEEL",
#' desc = "Steel demand",
#' commodity = "STEEL",
#' unit = "Mt",
#' dem = data.frame(
#' region = "UTOPIA", # NA for every region
#' year = c(2020, 2030, 2050),
#' slice = "ANNUAL",
#' dem = c(100, 200, 300)
#' ),
#' region = "UTOPIA", # optional, to narrow the specification of the demand
#' )
#' draw(DSTEEL)
#' @exportMethod draw
setMethod(
"draw", signature(obj = "demand"),
function(obj, ...) draw.demand(obj, ...)
)
## draw.export ####
draw.export <- function(obj, ...) {
# browser()
# key columns
# export parameters
exp_par <-
obj@exp |>
pivot_longer(
cols = matches("exp|price"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
# in_brackets = format_number(value),
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
# mutate(
# lab_par = if_else(
# is.na(lab_regions),
# lab_par,
# paste(lab_par, lab_regions, sep = " ")
# ),
# lab_par = if_else(
# is.na(lab_years),
# lab_par,
# paste(lab_par, lab_years, sep = " ")
# )
# ) |>
# select(-lab_regions, -lab_years) |>
mutate(
iotype = "cinp",
ioname = obj@commodity,
group = NA_character_,
) |>
group_by(ioname, iotype, group) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
# lab_regions = if_else(
# all(is.na(region)),
# NA_character_,
# paste0(
# # "{R(", length(unique(obj@exp$region)), "):",
# "Regions: {",
# shorten_string(
# paste0(sort(unique(obj@exp$region)), collapse = ","),
# n = 15, add_number = length(unique(obj@exp$region))),
# "}")
# ),
# lab_years = if_else(
# all(is.na(year)),
# NA_character_,
# paste0(
# "Years: [",
# shorten_string(
# paste0(range(obj@exp$year, na.rm = TRUE), collapse = ","),
# 15),
# "]")
# ),
.groups = "drop"
) |>
mutate(
lab_txt = make_label(
ioname,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
parameter = "exp"
)
exp_par
# arrow_label ####
arrow_labels <- make_label(
obj@commodity,
in_brackets = obj@unit,
two_lines = FALSE
)
names(arrow_labels) <- obj@commodity
draw_process(
process_name = obj@name,
process_desc = obj@desc,
single_com_inputs = exp_par,
arrow_labels = arrow_labels,
show_inputs = TRUE,
show_outputs = FALSE,
# show_aux = FALSE,
show_use_bar = FALSE,
show_act_bar = FALSE,
show_iuao_labels = FALSE,
box_width = 0.2,
box_height = .4 * 1.5
)
}
#' @exportMethod draw
#' @family draw export
#' @rdname draw
#'
#' @return
#' A figure with a schematic representation of the export process.
#' @export
#' @examples
#' EXPOIL <- newExport(
#' name = "EXPOIL", # used in sets
#' desc = "Oil export from the model to RoW", # for own reference
#' commodity = "OIL", # must match the commodity name in the model
#' unit = "Mtoe", # for own reference
#' exp = data.frame(
#' region = rep(c("R1", "R2"), each = 2), # export region(s)
#' year = rep(c(2020, 2050)), # export years
#' price = 500, # export price in MUSD/Mtoe (USD/t),
#' exp.up = rep(c(1e3, 1e4), each = 2), # upper bound for export in each year
#' exp.lo = rep(c(5e2, 0), each = 2) # lower bound for export in each year
#' )
#' )
#' draw(EXPOIL)
setMethod("draw", "export", draw.export)
## draw.import ####
draw.import <- function(obj, ...) {
# key columns
# browser()
# import parameters
imp_par <-
obj@imp |>
pivot_longer(
cols = matches("imp|price"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
# in_brackets = format_number(value),
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
# mutate(
# lab_par = if_else(
# is.na(lab_regions),
# lab_par,
# paste(lab_par, lab_regions, sep = " ")
# ),
# lab_par = if_else(
# is.na(lab_years),
# lab_par,
# paste(lab_par, lab_years, sep = " ")
# )
# ) |>
# select(-lab_regions, -lab_years) |>
mutate(
iotype = "cout",
ioname = obj@commodity,
group = NA_character_,
) |>
group_by(ioname, iotype, group) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
# lab_regions = if_else(
# all(is.na(region)),
# NA_character_,
# paste0(
# # "{R(", length(unique(obj@imp$region)), "):",
# "Regions: {",
# shorten_string(
# paste0(sort(unique(obj@imp$region)), collapse = ","),
# n = 15, add_number = length(unique(obj@imp$region))),
# "}")
# ),
# lab_years = if_else(
# all(is.na(year)),
# NA_character_,
# paste0(
# "Years: [",
# shorten_string(
# paste0(range(obj@imp$year, na.rm = TRUE), collapse = ","),
# 15),
# "]")
# ),
.groups = "drop"
) |>
mutate(
lab_txt = make_label(
ioname,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
),
parameter = "imp"
)
imp_par
# arrow_label ####
arrow_labels <- make_label(
obj@commodity,
in_brackets = obj@unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
names(arrow_labels) <- obj@commodity
draw_process(
process_name = obj@name,
process_desc = obj@desc,
single_com_outputs = imp_par,
arrow_labels = arrow_labels,
show_inputs = FALSE,
show_outputs = TRUE,
# show_aux = FALSE,
show_use_bar = FALSE,
show_act_bar = FALSE,
box_width = 0.2,
box_height = 0.2 * 1.5 * 2
)
}
#' @return
#' A figure with a schematic representation of the import process.
#' @exportMethod draw
#' @family draw import
#' @rdname draw
#'
#' @export
#'
#' @examples
#' IMPOIL <- newImport(
#' name = "IMPOIL", # used in sets
#' desc = "Oil import to the model to RoW", # for own reference
#' commodity = "OIL", # must match the commodity name in the model
#' unit = "Mtoe", # for own reference
#' imp = data.frame(
#' region = rep(c("R1", "R2"), each = 2), # import region(s)
#' year = rep(c(2020, 2050)), # import years
#' price = 600, # import price in MUSD/Mtoe (USD/t),
#' imp.up = rep(c(1e4, 1e6), each = 2), # upper bound for import in each year
#' imp.lo = rep(c(1e4, 1e5), each = 2) # lower bound for import in each year
#' )
#' )
#' draw(IMPOIL)
setMethod("draw", "import", draw.import)
## draw.trade ####
draw.trade <- function(obj, ...) {
arg <- list(...)
# browser()
if (!is.null(arg$region)) {
node <- arg$region
} else if (!is.null(arg$node)) {
node <- arg$node
} else {
node <- unique(obj@routes$src)[1]
}
inp_par <- obj@routes |>
filter(dst == node) |>
left_join(obj@trade, by = c("src", "dst")) |>
pivot_longer(
cols = matches("ava|eff"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(src, parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
group_by(src) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
parameter = "trade_inp",
.groups = "drop"
) |>
rowwise() |>
mutate(
comm = obj@commodity,
iotype = "cinp",
ioname = make_label(
obj@commodity,
in_brackets = src,
two_lines = F
),
group = NA_character_,
lab_txt = ioname # !!! since 'unit' is NA
)
inp_par
out_par <- obj@routes |>
filter(src == node) |>
left_join(obj@trade, by = c("src", "dst")) |>
pivot_longer(
cols = matches("ava|eff"),
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(dst, parameter) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
group_by(dst) |>
summarize(
lab_par = paste0(lab_par, collapse = "\n"),
parameter = "trade_out",
.groups = "drop"
) |>
rowwise() |>
mutate(
comm = obj@commodity,
iotype = "cout",
ioname = make_label(
obj@commodity,
in_brackets = dst,
two_lines = FALSE
),
group = NA_character_,
lab_txt = ioname # !!! since 'unit' is NA
)
out_par
# aux
aux_inp <-
obj@aux |>
full_join(obj@aeff, by = "acomm") |>
filter(dst == node) |>
pivot_longer(
cols = matches("2"), # non-grouped-comm-params have "2" in their names
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter, acomm, unit, src) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
filter(grepl("ainp", parameter)) |>
rowwise() |>
mutate(
iotype = "ainp",
ioname = paste0(acomm, ", ", src),
lab_txt = make_label(
ioname,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
)
aux_inp
aux_inp$lab_txt
aux_inp$lab_par
aux_out <-
obj@aux |>
full_join(obj@aeff, by = "acomm") |>
filter(src == node) |>
pivot_longer(
cols = matches("2"), # non-grouped-comm-params have "2" in their names
names_to = "parameter",
values_to = "value"
) |>
filter(!is.na(value)) |>
group_by(parameter, acomm, unit, dst) |>
summarize(
lab_par = make_label(
paste0(unique(parameter), ":"),
in_brackets = value,
two_lines = FALSE,
bracket_type = "square"
),
.groups = "drop"
) |>
filter(grepl("aout", parameter)) |>
# if (nrow(aux_out) > 0) {
# aux_out <- aux_out |>
rowwise() |>
mutate(
iotype = "aout",
ioname = paste0(acomm, ", ", dst),
lab_txt = make_label(
ioname,
in_brackets = unit,
return_name_if_empty = TRUE,
two_lines = FALSE
)
)
# } else {
# aux_out$lab_par <- NULL
# aux_out$lab_txt <- NULL
# }
aux_out
cap2act_label <- make_label(
"cap2act:",
in_brackets = obj@cap2act,
two_lines = FALSE,
bracket_type = "square"
)
all_nodes <- unique(obj@routes$src, obj@routes$dst)
center_label <- paste0(
cap2act_label,
"\n",
make_label(
"Nodes:",
in_brackets = paste0(all_nodes, collapse = ", "),
two_lines = FALSE,
bracket_type = "square"
)
)
arrow_labels <- c(
inp_par$lab_txt,
out_par$lab_txt,
aux_inp$lab_txt,
aux_out$lab_txt
)
names(arrow_labels) <- c(
inp_par$ioname,
out_par$ioname,
aux_inp$ioname,
aux_out$ioname
)
arrow_labels <- arrow_labels[!duplicated(arrow_labels)]
draw_process(
process_name = paste0(obj@name, ", node: ", node),
process_desc = obj@desc,
single_com_inputs = inp_par,
single_com_outputs = out_par,
aux_inputs = aux_inp,
aux_outputs = aux_out,
arrow_labels = arrow_labels,
center_label = center_label,
# show_inputs = TRUE,
# show_outputs = TRUE,
show_use_bar = FALSE,
show_act_bar = FALSE,
show_iuao_labels = FALSE,
box_width = 0.3,
box_height = .4 * 1.5
)
}
#' @param region A node to draw the trade process for.
#' `node` is an alias for `region`.
#' Default is the first node in the trade object.
#'
#' @export
#'
#' @exportMethod draw
#' @family draw trade
#' @rdname draw
#' @examples
#' PIPELINE2 <- newTrade(
#' name = "PIPELINE2",
#' desc = "Some transport pipeline",
#' commodity = "OIL",
#' routes = data.frame(
#' src = c("R1", "R1", "R2", "R3"),
#' dst = c("R2", "R3", "R3", "R2")
#' ),
#' trade = data.frame(
#' src = c("R1", "R1", "R2", "R3"),
#' dst = c("R2", "R3", "R3", "R2"),
#' teff = c(0.912, 0.913, 0.923, 0.932)
#' ),
#' aux = data.frame(
#' acomm = c("ELC", "CH4"),
#' unit = c("MWh", "kt")
#' ),
#' aeff = data.frame(
#' acomm = c("ELC", "CH4", "ELC", "CH4"),
#' src = c("R1", "R1", "R2", "R3"),
#' dst = c("R2", "R2", "R3", "R2"),
#' csrc2ainp = c(.5, NA, .3, NA),
#' cdst2ainp = c(.4, NA, .6, NA),
#' csrc2aout = c(NA, .1, NA, .2)
#' ),
#' olife = list(olife = 60)
#' )
#' draw(PIPELINE2, node = "R1")
#' draw(PIPELINE2, node = "R2")
#' draw(PIPELINE2, node = "R3")
setMethod("draw", "trade", draw.trade)
## draw.weather ####
#' An internal function to create a character string with a label
#'
#' @param name A character string with the name as the first part of the label
#' @param in_brackets A character string with the content to put in brackets
#' @param make_range A logical value to indicate if the content should be
#' formatted as a range
#' @param two_lines A logical value to indicate if the label should be in
#' two lines
#' @param return_name_if_empty A logical value to return the name if the content
#' of the brackets is NA or empty.
#' @param bracket_type A character string with the type of brackets to use,
#' one of "round", "square", "curly", "angle", or NULL
#' @noRd
#' @return A character string with the label
make_label <- function(
name,
in_brackets = NULL,
make_range = TRUE,
two_lines = FALSE,
return_name_if_empty = FALSE,
bracket_type = "round", # "round", "square", "curly", "angle", or NULL
comma = ",") {
# browser()
# if (all(is.na(in_brackets))) return(NA)
if (is.null(bracket_type)) {
bracket <- c("", "")
} else {
bracket <- switch(bracket_type,
"round" = c("(", ")"),
"square" = c("[", "]"),
"curly" = c("{", "}"),
"angle" = c("<", ">"),
"comma" = c(",", ""),
)
}
in_brackets <- in_brackets[!is.na(in_brackets)]
in_brackets <- in_brackets[in_brackets != ""]
if (is_empty(in_brackets)) {
# browser()
if (isTRUE(return_name_if_empty)) return(name)
return("")
}
if (is.numeric(in_brackets)) {
if (length(unique(in_brackets)) > 1) {
in_brackets <- paste0(
bracket[1],
format_number(min(in_brackets)),
comma,
format_number(max(in_brackets)),
bracket[2]
) |>
# format_number()
prettyNum(digits = 2, big.mark = "")
} else {
in_brackets <- unique(in_brackets) |> format_number()
}
} else {
in_brackets <- unique(in_brackets)
if (length(in_brackets) > 1) browser()
# stopifnot(length(in_brackets) == 1)
if (is.null(in_brackets) || is.na(in_brackets)) {
in_brackets <- NULL
} else {
in_brackets <- paste0(bracket[1], in_brackets, bracket[2])
}
}
if (two_lines) {
label <- paste0(name, "\n", in_brackets)
} else {
label <- paste0(name, " ", in_brackets)
}
label
}
#' Drafted function to convert an S4 object to a data frame
#'
#' @param obj An S4 object
#' @param sets A character vector with the names of the sets,
#' colnames to create in the resulting data frame.
#' Default is c("region", "year", "slice", "comm", "acomm")
#' @param verbose A logical value if to print messages
#' @noRd
en_obj2df <- function(obj, sets = NULL, verbose = FALSE) {
# browser()
if (!isS4(obj)) {
stop("Object must be an S4 class")
}
if (is.null(sets)) {
sets <- c("region", "year", "slice", "comm", "acomm")
}
# obj <- tech
slots <- slotNames(obj)
ll <- list()
for (s in slots) {
if (verbose) cat("Processing slot: ", s, "\n")
if (inherits(slot(obj, s), "data.frame")) {
ll[[s]] <- slot(obj, s) |>
pivot_by_type(sets = sets, slot_name = s)
} else if (inherits(slot(obj, s), c("character", "numeric", "logical"))) {
ll[[s]] <- data.frame(
parameter = if (is_empty(slot(obj, s))) NA else slot(obj, s)
) |>
pivot_by_type(sets = sets, slot_name = s)
} else if (inherits(slot(obj, s), "list")) {
if (length(slot(obj, s)) > 0) {
message("Skipping list slot: ", s)
}
# ll2 <-
} else {
message("Skipping slot: ", s, " of class: ", class(slot(obj, s)))
}
}
ll |>
rbindlist(use.names = TRUE, fill = TRUE) |>
select(
matches(c("slot", "parameter", sets)),
"character_val", "logical_val", "numeric_val", everything()
) |>
# filter(slot != "name") |>
mutate(
class = class(obj),
name = obj@name,
.before = 1
)
# !!! ToDO: add status column (T/F coercion success)
# !!! ToDO: process list slots (1st level at least)
}
if (F) {
x <- en_obj2df(TECH01)
}
#' An internal function to pivot a data frame by column type
#'
#' @param x data frame to pivot
#' @param sets character vector with the names of the sets, keys to keep.
#' Default is c("region", "year", "slice", "comm", "acomm")
#' @param slot_name character string with the name of the slot,
#' where the data frame comes from to add to the resulting data frame.
#' @noRd
#' @return A data frame with the pivoted data
pivot_by_type <- function(x, sets = NULL, slot_name = NULL) {
# browser()
if (is.null(sets)) {
sets <- c("region", "year", "slice", "comm", "acomm")
}
df <- data.frame()
# pivot_longer for character columns
char_df <- x |> select(any_of(sets), where(is.character))
cond <- any(sapply(select(char_df, -any_of(sets)), is.character))
if (ncol(char_df) > 0 && cond) {
char_df <- char_df |>
pivot_longer(
cols = -any_of(sets),
names_to = "parameter",
values_to = "character_val"
) |>
filter(!is.na(character_val))
# merge with existing df
if (nrow(char_df) > 0) {
if (nrow(df) > 0) {
df <- full_join(df, char_df, by = intersect(names(df), names(char_df)))
} else {
df <- char_df
}
}
} else {
char_df <- data.frame()
}
# pivot_longer for numeric columns
num_df <- x |> select(any_of(sets), where(is.numeric))
cond <- any(sapply(select(num_df, -any_of(sets)), is.numeric))
if (ncol(num_df) > 0 && cond) {
num_df <- num_df |>
pivot_longer(
cols = -any_of(sets),
names_to = "parameter",
values_to = "numeric_val"
) |>
filter(!is.na(numeric_val))
# merge with existing df
if (nrow(num_df) > 0) {
if (nrow(df) > 0) {
df <- full_join(df, num_df, by = intersect(names(df), names(num_df)))
} else {
df <- num_df
}
}
} else {
num_df <- data.frame()
}
# pivot_longer for logical columns
logical_df <- x |> select(any_of(sets), where(is.logical))
cond <- any(sapply(select(logical_df, -any_of(sets)), is.logical))
if (ncol(logical_df) > 0 && cond) {
logical_df <- logical_df |>
pivot_longer(
cols = -any_of(sets),
names_to = "parameter",
values_to = "logical_val"
) |>
filter(!is.na(logical_val))
# merge with existing df
if (nrow(logical_df) > 0) {
if (nrow(df) > 0) {
df <- full_join(df, logical_df, by = intersect(names(df), names(logical_df)))
} else {
df <- logical_df
}
}
} else {
logical_df <- data.frame()
}
x <- list(char_df, num_df, logical_df) |>
rbindlist(use.names = TRUE, fill = TRUE)
if (!is.null(slot_name)) {
x <- mutate(x, slot = slot_name, .before = 1)
}
return(x)
}
if (F) {
pivot_by_type(tech@ceff)
}
# draw_process ####
#' An internal function to draw a process
#'
#' @param process_name A character string with the name of the process
#' @param process_desc A character string with the description of the process
#' @param grouped_com_inputs A data frame with the grouped commodity inputs' labels
#' @param single_com_inputs A data frame with the single commodity inputs' labels
#' @param aux_inputs A data frame with the auxiliary inputs' labels
#' @param weather_factors A data frame with the weather factors' labels
#' @param grouped_com_outputs A data frame with the grouped commodity outputs' labels
#' @param single_com_outputs A data frame with the single commodity outputs' labels
#' @param arrow_weather_color A character string with the color of the weather arrows
#' @param arrow_labels A named character vector with the labels of the arrows
#' @param center_label A character string with the label of the cap2act arrow
#' @param box_width A numeric value with the width of the process box
#' @param box_height A numeric value with the height of the process box
#' @param box_fill A character string with the fill color of the process box
#' @param box_border A character string with the border color of the process box
#' @param box_lwd A numeric value with the line width of the process box
#' @param process_name_fontsize A numeric value with the font size of the process name
#' @param process_desc_fontsize A numeric value with the font size of the process description
#' @param arrow_comm_color A character string with the color of the commodity arrows
#' @param arrow_aux_color A character string with the color of the auxiliary arrows
#'
#' @noRd
draw_process <- function(
process_name = "Process",
process_desc = "Process Description",
# inputs
grouped_com_inputs = NULL,
single_com_inputs = NULL,
aux_inputs = NULL,
weather_factors = NULL,
# ginp2use = NULL,
# cap2act = NULL,
# outputs
grouped_com_outputs = NULL,
single_com_outputs = NULL,
# com_outputs = NULL,
aux_outputs = NULL,
# labels
arrow_labels = NULL,
center_label = NULL,
show_inputs = any(
!is.null(c(
grouped_com_inputs, single_com_inputs,
aux_inputs, weather_factors
)),
na.rm = T
),
show_outputs = any(!is.null(c(grouped_com_outputs, single_com_outputs)),
na.rm = T
),
show_use_bar = any(
!is.null(c(
grouped_com_inputs, single_com_inputs,
aux_inputs, weather_factors
)),
na.rm = T
),
show_act_bar = any(!is.null(c(grouped_com_outputs, single_com_outputs)),
na.rm = T
),
show_iuao_labels = FALSE,
show_all = NULL,
# draw parameters
box_width = 0.4,
box_height = box_width * 1.5,
arrow_length = 0.175,
box_fill = rgb(220 / 255, 230 / 255, 242 / 255),
box_border = "royalblue4",
box_lwd = 3,
process_name_fontsize = 14,
process_desc_fontsize = 10,
font_spacing = .06,
fig_background = "white",
arrow_comm_color = "red3",
arrow_aux_color = "royalblue4",
arrow_weather_color = "forestgreen") {
result <- tryCatch(
{
# browser()
if (isTRUE(show_all)) {
show_inputs <- TRUE
show_outputs <- TRUE
show_use_bar <- TRUE
show_act_bar <- TRUE
show_iuao_labels <- TRUE
}
# try(dev.off())
grid::grid.newpage()
if (!is.null(fig_background) && !is.na(fig_background)) {
grid::grid.rect(
x = 0.5, y = 0.5,
width = 1, height = 1,
gp = grid::gpar(fill = fig_background, col = NA)
)
}
# Set a viewport
vp <- grid::viewport(
width = grid::unit(1, "npc"),
height = grid::unit(1, "npc"),
just = "center"
)
grid::pushViewport(vp)
on.exit(grid::popViewport(0))
font_in_npc <- function(fontsize) {
# vp_height_in <- grid::convertUnit(unit(1, "npc"), "in", valueOnly = TRUE)
# fontsize / 72 / vp_height_in * 2
font_spacing
}
spacing_bw_titles <- 0.2 * font_in_npc(
max(process_name_fontsize, process_desc_fontsize)
)
# Process box
grid::grid.rect(
x = 0.5, y = 0.5,
width = box_width,
height = box_height,
gp = gpar(fill = box_fill, col = box_border, lty = "solid", lwd = box_lwd)
)
# Process description subtitle
# browser()
if (!is_empty(process_desc) && process_desc != "") {
txt_x <- 0.5 + box_height / 2 + spacing_bw_titles +
font_in_npc(process_desc_fontsize) / 2
# txt_x <- 0.5 + box_height / 2 + .05
grid::grid.text(
process_desc,
x = 0.5,
y = txt_x,
gp = gpar(fontsize = process_desc_fontsize, just = c("center", "bottom"))
)
} else {
txt_x <- 0.5 + box_height / 2
}
# Process label
grid::grid.text(
process_name,
x = 0.5,
y = txt_x + spacing_bw_titles *
max(process_name_fontsize, process_desc_fontsize) /
min(process_name_fontsize, process_desc_fontsize) +
font_in_npc(process_desc_fontsize) / 2,
gp = gpar(fontsize = process_name_fontsize)
)
# # Process label
# grid::grid.text(
# process_name,
# x = 0.5,
# y = 0.5 + box_height / 2 +
# 1.1 * font_in_npc(process_name_fontsize) / 2 +
# 1.2 * font_in_npc(process_desc_fontsize),
# gp = gpar(fontsize = process_name_fontsize)
# )
#
y_inp_use_act <- 0.5 + box_height / 2 - 0.03
# Inputs ####
if (show_inputs) {
# combine all inputs
inputs <- bind_rows(
grouped_com_inputs,
single_com_inputs,
aux_inputs,
weather_factors
)
# arrow colors
inputs <- inputs |>
filter(!is.na(ioname)) |>
mutate(
arrow_color = case_when(
grepl("cinp", iotype) ~ arrow_comm_color,
grepl("ainp", iotype) ~ arrow_aux_color,
grepl("winp", iotype) ~ arrow_weather_color
),
order = ifelse(grepl("winp", iotype), 1,
ifelse(grepl("ainp", iotype), 2,
ifelse(is.na(group), 3, 4)
)
)
) |>
arrange(order, desc(group), desc(ioname))
# inputs
if (is.null(inputs[["label_hjust"]])) inputs$label_hjust <- 0
if (is.null(inputs[["label_vjust"]])) inputs$label_vjust <- 0
if (is.null(inputs[["label_font"]])) inputs$label_font <- 6
if (is.null(inputs[["x"]])) inputs$x <- NA
if (is.null(inputs[["y"]])) inputs$y <- NA
n_inputs <- nrow(inputs)
inp_coords <- list() # Store coordinates where arrows touch the box
if (length(n_inputs) > 0) {
if (show_iuao_labels) {
# Add 'inp' label
grid::grid.text(
label = "inp",
x = 0.5 - box_width / 2 + 0.02,
# y = y_end + 0.02,
y = y_inp_use_act,
just = "bottom",
gp = gpar(fontsize = 8)
)
}
for (i in seq_len(n_inputs)) {
# x and y position of the input on the process box
x_pos <- 0.5 - box_width * 0.5
y_pos <- 0.5 + (i - (n_inputs + 1) / 2) * (box_height / (n_inputs + 1))
inputs$x[i] <- x_pos
inputs$y[i] <- y_pos
# draw arrow i
grid::grid.lines(
x = c(0.5 - 0.5 * box_width - arrow_length, x_pos),
y = c(y_pos, y_pos),
arrow = grid::arrow(
type = "closed", angle = 15,
length = grid::unit(0.15, "inches"),
ends = "last"
),
gp = gpar(col = inputs$arrow_color[i], lwd = 2)
)
# Add label over the arrow
grid::grid.text(
arrow_labels[inputs$ioname[i]],
x = 0.5 - box_width * 0.5 - .03,
y = y_pos + font_in_npc(10) / 2,
gp = gpar(fontsize = 10), # , col = "grey"
just = "right"
)
# combustion point
# grid::grid.points(
# x = x_pos,
# y = y_pos, pch = 16,
# gp = gpar(col = inputs$arrow_color[i], cex = 0.1)
# )
# Add label near the dot, inside the box
grid::grid.text(
inputs$lab_par[i],
x = 0.5 - box_width * 0.48 + inputs$label_hjust[i],
y = y_pos + inputs$label_vjust[i] + .00,
just = "left",
gp = gpar(fontsize = inputs$label_font[i])
)
} # end for (i in seq_len(n_inputs))
inp_arrow_spacing <- diff(inputs$y) |> mean(na.rm = TRUE)
if (is.nan(inp_arrow_spacing) | is.na(inp_arrow_spacing)) {
inp_arrow_spacing <- 0.1
}
# Draw grouping brackets for inputs
ginp <- inputs |> filter(!is.na(group))
if (nrow(ginp) > 1) {
for (g in unique(ginp$group)) {
ii <- which(ginp$group == g)
if (length(ii) == 1) {
warning("Group with only one input", ginp$ioname[ii])
next
}
y1 <- min(ginp$y[ii]) - .4 * inp_arrow_spacing
y2 <- max(ginp$y[ii]) + .4 * inp_arrow_spacing
bracket_x <- 0.5 - box_width * 0.23
# group bracket ####
grid::grid.lines(
x = c(
bracket_x,
bracket_x
),
y = c(y1, y2),
gp = gpar(lwd = 1.25, col = "red3")
)
grid::grid.lines(
x = c(
bracket_x - box_width * 0.02,
bracket_x
),
y = c(y1, y1),
gp = gpar(lwd = 1.25, col = "red3")
)
grid::grid.lines(
x = c(
bracket_x - box_width * 0.02,
bracket_x
),
y = c(y2, y2),
gp = gpar(lwd = 1.25, col = "red3")
)
# group circle ####
circle_y <- (y1 + y2) / 2
circle_x <- bracket_x
grid::grid.circle(
x = circle_x, y = circle_y, r = grid::unit(0.07, "inches"),
gp = gpar(fill = "white", col = "red3", lwd = 1.0)
)
# group number ####
grid::grid.text(
label = g,
x = circle_x,
y = circle_y,
gp = gpar(fontsize = 8)
)
# ginp2use ####
# stop()
ginp2use <- grouped_com_inputs |>
filter(group == g, parameter == "ginp2use")
if (nrow(ginp2use) == 1) {
grid::grid.text(
label = ginp2use$lab_par,
x = circle_x + 0.048,
y = circle_y,
just = "center",
gp = gpar(fontsize = 6)
)
} else if (nrow(ginp2use) > 1) {
stop("More than one ginp2use labels for group", g)
}
} # end for (g in unique(ginp$group))
# Add 'ginp' label
grid::grid.text(
label = "ginp",
x = bracket_x,
# y = y_end + 0.02,
y = y_inp_use_act,
just = "bottom",
gp = gpar(fontsize = 8)
)
} # end of groups
## "use" bracket #####
if (show_use_bar) {
cinp <- inputs |> filter(grepl("cinp", iotype))
y_use <- range(cinp$y, na.rm = TRUE) + 0.4 * inp_arrow_spacing * c(-1, 1)
use_x <- 0.5 - box_width * 0.00
grid::grid.lines(
x = c(use_x, use_x),
y = y_use,
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
grid::grid.lines(
x = c(use_x - box_width * 0.02, use_x),
y = c(y_use[1], y_use[1]),
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
grid::grid.lines(
x = c(use_x - box_width * 0.02, use_x),
y = c(y_use[2], y_use[2]),
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
## "use" label ####
if (show_iuao_labels) {
grid::grid.text(
label = "use",
x = use_x,
y = y_inp_use_act,
just = "bottom",
gp = gpar(fontsize = 8)
)
}
}
} # end of (length(n_inputs) > 0)
} # end of show_inputs
# Outputs ####
if (show_outputs) {
# browser()
# combine all outputs
outputs <- bind_rows(
grouped_com_outputs,
single_com_outputs,
aux_outputs
)
# arrow colors
outputs <- outputs |>
filter(!is.na(ioname)) |>
mutate(
arrow_color = case_when(
grepl("cout", iotype) ~ arrow_comm_color,
grepl("aout", iotype) ~ arrow_aux_color
),
order = ifelse(grepl("aout", iotype), 1,
if_else(is.na(group), 2, 3)
)
) |>
arrange(order, desc(group), desc(ioname))
if (is.null(outputs[["label_hjust"]])) outputs$label_hjust <- 0
if (is.null(outputs[["label_vjust"]])) outputs$label_vjust <- 0
if (is.null(outputs[["label_font"]])) outputs$label_font <- 6
if (is.null(outputs[["x"]])) outputs$x <- NA
if (is.null(outputs[["y"]])) outputs$y <- NA
out_coms <- outputs$ioname |> unique()
out_pars <- outputs |>
filter(grepl("out|teff", parameter) |
(is.na(group) & grepl("cinp2use|use2cact|imp|sup|trade", parameter)))
n_outputs <- length(out_coms)
# browser()
if (length(n_outputs) > 0) {
stopifnot(n_outputs == length(unique(out_pars$ioname)))
if (show_iuao_labels) {
# Add 'out' label
grid::grid.text(
label = "out",
x = 0.5 + box_width / 2 - 0.02,
y = y_inp_use_act,
just = "bottom",
gp = gpar(fontsize = 8)
)
# add 'act' label
grid::grid.text(
label = "act",
x = 0.5 + box_width * 0.22,
y = y_inp_use_act,
just = "bottom",
gp = gpar(fontsize = 8)
)
}
for (i in 1:nrow(out_pars)) {
ii <- which(outputs$ioname == out_coms[i]) # can be several parameters
# x and y position of the output on the process box
x_pos <- 0.5 + box_width * 0.5
y_pos <- 0.5 + (i - (n_outputs + 1) / 2) * (box_height / (n_outputs + 1))
outputs$x[ii] <- x_pos
outputs$y[ii] <- y_pos
# draw arrow o
# browser()
grid::grid.lines(
x = c(x_pos, 0.5 + 0.5 * box_width + arrow_length),
y = c(y_pos, y_pos),
arrow = grid::arrow(
type = "closed", angle = 15,
length = grid::unit(0.15, "inches"),
ends = "last"
),
gp = gpar(col = out_pars$arrow_color[i], lwd = 2)
)
# Add label over the arrow
grid::grid.text(
arrow_labels[out_pars$ioname[i]],
x = 0.5 + box_width * 0.5 + .02,
y = y_pos + font_in_npc(10) / 2,
gp = gpar(fontsize = 10), # , col = "grey"
just = "left"
)
# Add label near the dot, inside the box
grid::grid.text(
out_pars$lab_par[i],
x = 0.5 + box_width * 0.48 + out_pars$label_hjust[i],
y = y_pos + out_pars$label_vjust[i] + .00,
just = "right",
gp = gpar(fontsize = out_pars$label_font[i])
)
} # end for (i in seq_len(n_outputs))
out_arrow_spacing <- diff(unique(outputs$y)) |> mean(na.rm = TRUE)
# Draw grouping brackets for outputs
gout <- outputs |>
filter(!is.na(group)) |>
select(group, ioname, y, lab_txt, x, y)
if (nrow(gout) > 1) {
for (g in unique(gout$group)) {
ii <- which(gout$group == g)
if (length(ii) == 1) {
warning("Group with only one output", gout$ioname[ii])
next
}
y1 <- min(gout$y[ii], na.rm = TRUE) - .4 * out_arrow_spacing
y2 <- max(gout$y[ii], na.rm = TRUE) + .4 * out_arrow_spacing
bracket_x <- 0.5 + box_width * 0.25
# group bracket ####
grid::grid.lines(
x = c(
bracket_x,
bracket_x
),
y = c(y1, y2),
gp = gpar(lwd = 1.25, col = "red3")
)
grid::grid.lines(
x = c(
bracket_x + box_width * 0.02,
bracket_x
),
y = c(y1, y1),
gp = gpar(lwd = 1.25, col = "red3")
)
grid::grid.lines(
x = c(
bracket_x + box_width * 0.02,
bracket_x
),
y = c(y2, y2),
gp = gpar(lwd = 1.25, col = "red3")
)
# group circle ####
circle_y <- (y1 + y2) / 2
circle_x <- bracket_x
grid::grid.circle(
x = circle_x, y = circle_y, r = grid::unit(0.07, "inches"),
gp = gpar(fill = "white", col = "red3", lwd = 1.0)
)
# group number ####
grid::grid.text(
label = g,
x = circle_x,
y = circle_y,
gp = gpar(fontsize = 8)
)
}
} # end of groups
## "act" bracket ####
if (show_act_bar) {
cout <- outputs |>
filter(grepl("cout", iotype))
y_act <- range(cout$y, na.rm = TRUE) + 0.42 * out_arrow_spacing * c(-1, 1)
act_x <- 0.5 + box_width * 0.2
grid::grid.lines(
x = c(act_x, act_x),
y = y_act,
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
grid::grid.lines(
x = c(act_x + box_width * 0.02, act_x),
y = c(y_act[1], y_act[1]),
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
grid::grid.lines(
x = c(act_x + box_width * 0.02, act_x),
y = c(y_act[2], y_act[2]),
gp = gpar(lwd = 1.25, col = "red3", lty = "solid")
)
} # end of show_act_bar
## use2cact labels ####
# browser()
if (show_act_bar && show_use_bar &&
any(grepl("use2cact", outputs$parameter))) {
use2cact <- outputs |>
filter(grepl("use2cact", parameter)) |>
filter(!is.na(group))
use2cact_x <- (act_x + use_x) / 2
if (nrow(use2cact) > 0) {
for (com in unique(use2cact$ioname)) {
ii <- which(use2cact$ioname == com)
grid::grid.text(
label = use2cact$lab_par[ii],
x = use2cact_x,
y = use2cact$y[ii],
just = "center",
gp = gpar(fontsize = 6)
)
} # end of for loop
} # end of (nrow(use2cact) > 0)
} # end of show_act_bar && show_use_bar
} # end of (length(n_outputs) > 0)
} # end of show_outputs
# cap2act ####
if (!is.null(center_label)) {
grid::grid.text(
label = center_label,
x = 0.5,
y = 0.5 - box_height / 2 + 0.05,
just = "center",
gp = gpar(fontsize = 6)
)
} # end of cap2act
return(invisible(TRUE))
},
error = function(e) {
message("Error in draw_process: ", e)
try(grid::popViewport(0), silent = TRUE)
return(invisible(FALSE))
}
)
} # end of draw_process
# # dev.off()
# if (F) {
# "#DCE6F2"
# "#F0FFF0"
# "#E6E6FA"
# "#F0F8FF"
# "#228B22"
# "#FFD700"
# "#556B2F" # Dark Olive Green
# "#6B8E23"
# "#808000"
# "#556B2F"
# "#B8860B" # Dark Goldenrod
# "#DAA520"
# "#708090"
# "#2F4F4F"
# "#FF6347"
# "#FF4500"
# "#FFA07A"
# "#FFA500"
# "#FFD700"
# "#FF8C00"
# }
# Function to shorten a string
shorten_string <- function(string, n, add_number = NULL) {
if (nchar(string) > n) {
# Subtract 2 from n to account for the ".."
shortened <- substr(string, 1, n)
if (nchar(string) > n) {
if (!is.null(add_number)) {
shortened <- paste0(shortened, "..(", add_number, ")")
} else {
shortened <- paste0(shortened, "..")
}
}
return(shortened)
} else {
return(string)
}
}
# format_number <- function(x) {
# # browser()
# # if (length(x) > 1) {
# # return(sapply(x, format_number))
# # }
# # Use scientific notation if the number is larger than 100 or smaller than 0.01 (positive or negative)
# if (any(abs(x) > 100) || any(abs(x) < 0.01)) {
# return(prettyNum(format(x, scientific = TRUE), big.mark = ","))
# } else {
# return(prettyNum(format(x, nsmall = 2), big.mark = ","))
# }
# }
# x <- c(0.01224201, 1002360.1)
# Define a function for conditional formatting
# format_number <- function(x, threshold = 1e5, small_threshold = 1e-3) {
# sapply(x, function(n) {
# if (abs(n) >= threshold || (abs(n) < small_threshold && n != 0)) {
# # Use scientific notation for very large or very small numbers
# formatC(n, format = "e", digits = 3)
# } else {
# # Use fixed decimal format
# formatC(n, format = "f", digits = 3)
# }
# })
# }
# format_number <- function(x, accuracy = 3) {
# scales::label_number(accuracy = accuracy)(x)
# }
format_number <- function(x, accuracy = .01) {
sapply(x, function(value) {
if (value == 0) {
"0."
} else if (value >= 1e4) {
# For large numbers, show fewer digits with scale suffix
scales::label_scientific(accuracy = 0)(value)
} else if (value <= accuracy) {
# For smaller numbers, allow more precision
scales::label_scientific(accuracy = accuracy)(value)
} else {
scales::label_number(accuracy = accuracy, big.mark = "")(value)
}
})
}
# # Apply the function
# formatted_x <- format_numbers(x)
# print(formatted_x)
# Example usage
# numbers <- c(45.678, 12345.678, -12345.678, 0.1234, 0.009, -0.008, 1e8)
# sapply(numbers, format_number)
# print(formatted_numbers)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.