#' @export
get_interpolation_rule <- function(x) {
stopifnot(is.character(x))
if (!exists(".defInt")) load("R/sysdata.rda")
sapply(x, function(i) .defInt[[i]])
}
## constraint-class ####
#' An S4 class to represent a custom constraint.
#'
#' @name class-constraint
#'
#' @description
#' Class `constraint` is used to define custom constraints in the optimization problem.
#' `r lifecycle::badge("experimental")`
#' @inherit newConstraint details
#'
#' @md
#' @slot name `r get_slot_info("constraint", "name")`
#' @slot desc `r get_slot_info("constraint", "desc")`
#' @slot eq `r get_slot_info("constraint", "eq")`
#' @slot for.each `r get_slot_info("constraint", "for.each")`
#' @slot rhs `r get_slot_info("constraint", "rhs")`
#' @slot defVal `r get_slot_info("constraint", "defVal")`
#' @slot interpolation `r get_slot_info("constraint", "interpolation")`
#' @slot lhs `r get_slot_info("constraint", "lhs")`
#' @slot misc `r get_slot_info("constraint", "misc")`
#'
#'
#' @include class-subsidy.R
#' @family class constraint policy
#' @rdname class-constraint
#' @order 1
#' @export
#'
setClass("constraint",
representation(
name = "character",
desc = "character", # desc
eq = "factor",
for.each = "data.frame",
rhs = "data.frame",
defVal = "numeric",
interpolation = "character",
lhs = "list",
misc = "list"
# parameter= list() # For the future
),
prototype(
name = NULL,
desc = "", # desc
eq = factor("==", levels = c(">=", "<=", "==")),
for.each = data.frame(),
rhs = data.frame(),
defVal = as.numeric(NA),
interpolation = get_interpolation_rule("rhs"),
lhs = list(),
# ! Misc
misc = list()
),
S3methods = FALSE
)
setMethod("initialize", "constraint", function(.Object, ...) {
.Object
})
## summand-class #####
#' An S4 class to represent a summand in a constraint.
#'
#' @name class-summand
#'
#' @description
#' Class `summand` stores information about linear
#' terms (a multiplier and a variable)
#' in the `lhs` of the constraint class.
#' It is auto-created by `newConstraint` function
#' and is not intended to be used directly by the user.
#'
#' @md
#' @slot desc `r get_slot_info("summand", "desc")`
#' @slot variable `r get_slot_info("summand", "variable")`
#' @slot for.sum `r get_slot_info("summand", "for.sum")`
#' @slot mult `r get_slot_info("summand", "mult")`
#' @slot defVal `r get_slot_info("summand", "defVal")`
#' @slot misc `r get_slot_info("summand", "misc")`
#'
#' @family class constraint
#' @order 2
#' @rdname class-constraint
#' @export
setClass("summand",
representation(
desc = "character",
variable = "character",
for.sum = "list",
mult = "data.frame",
defVal = "numeric",
misc = "list"
# parameter= list() # !!! consider adding custom parameters
),
prototype(
desc = NULL,
variable = NULL,
for.sum = list(),
mult = data.frame(),
defVal = 1,
misc = list()
),
S3methods = FALSE
)
#' Create constraint object to add custom constraints to the model.
#'
#' @description
#' The function creates a new constraint object that can be used
#' to add custom constraints to the model.
#'
#' @details
#' Custom constraints extend the functionality of the model by adding
#' user-defined constraints to the optimization problem.
#' If the predefined constraints are not sufficient to describe the problem,
#' custom constraints can be used to add linear equlity or inequality
#' constraints to define additional relationships between the variables.
#' In many cases this can be done without writing constraints in the GAMS,
#' Julia/JuMP, Python/Pyomo, or GLPK-MathProg languages by using the
#' `constrant` class and the `newConstraint` function.
#' To define a custom constraint with the `newConstraint` function,
#' the user needs to specify the name of the constraint, the type of the
#' relation (equality, less than or equal, greater than or equal), the
#' left-hand side (LHS) terms of the statement, and the right-hand side (RHS) value.
#' The dimension of the constraint is set by the `for.each` parameter.
#' The 'lhs' terms are defined as a list of linear terms (summands).
#' Each summand consists of a variable, a multiplier, and a set of sets
#' for which the summand is defined.
#'
#'
#' @param name `r get_slot_info("constraint", "name")`
#' @param desc `r get_slot_info("constraint", "desc")`
#' @param ... named or unnamed list(s) of left-hand side (LHS)
#' linear terms (summands) to define the constraint.
#' Every summand is defined as a list with the following elements:
#' - `variable` - name of the variable in the summand.
#' - `mult` - multiplier for the variable in the summand.
#' - `for.sum` - list of sets for which the summand is defined.
#' The summands can be passed as named or unnamed lists.
#' They will be added to the `lhs` slot of the constraint object
#' as linear terms of mulipliers and variables.
#' @param eq Type of the relation ('==' default, '<=', '>=')
#' @param for.each list or data.frame with sets that define the dimension of the constraint.
#' @param rhs a numeric value, list or data frame with sets and numeric values for each constraint.
#' Note: zero values will be replaced with `replace_zerros` to avoid dropping them by the interpolation algorithms.
#' @param defVal `r get_slot_info("constraint", "defVal")`
#' @param interpolation `r get_slot_info("constraint", "interpolation")`
#' @param replace_zerros numeric value to replace zero values in `rhs` and `defVal`. Default is `1e-20`.
#'
#' @return Object of class `constraint`.
#' @family class constraint policy
#' @rdname newConstraint
#'
#' @export
newConstraint <- function(
name,
desc = "",
...,
eq = "==",
for.each = NULL,
rhs = data.frame(),
defVal = NULL,
interpolation = "inter",
replace_zerros = 1e-20
# arg = NULL
) {
obj <- new("constraint")
# stopifnot(length(eq) == 1 && eq %in% levels(obj@eq))
if (length(eq) != 1 || !(eq %in% levels(obj@eq))) {
stop(
"Unrecognized 'eq' parameter. Use one of: ",
paste0(levels(obj@eq), collapse = ", ")
)
}
obj@eq[] <- eq
# browser()
if (is.null(defVal)) {
warning("It is advisable to define 'defVal' parameter.")
if (eq == "==") {
defVal <- 0
} else if (eq == "<=") {
defVal <- Inf
} else if (eq >= ">=") {
defVal <- 0
}
}
if (is.numeric(rhs)) {
if (length(rhs) != 1) {
stop("rhs must be a single numeric value or a data.frame with sets and numeric values for each constraint.")
}
if (is.na(defVal)) defVal <- rhs
rhs <- data.frame()
}
if (!is.data.frame(rhs) && is.list(rhs)) {
tmp <- sapply(rhs, length)
if (any(tmp[1] != tmp) || is.null(names(rhs))) {
stop("Length of the list elements in 'rhs' must be identical.")
}
rhs <- as.data.frame(rhs, stringsAsFactors = FALSE)
}
if (!is.data.frame(rhs) && is.list(rhs) && length(rhs) == 1 && length(rhs[[1]]) == 1) {
if (is.na(defVal)) defVal <- rhs[[1]]
rhs <- data.frame()
}
if (is.data.frame(rhs) && ncol(rhs) == 1 && nrow(rhs) == 1) {
if (is.na(defVal)) defVal <- rhs[1, 1]
rhs <- data.frame()
}
# if (is.numeric(rhs)) {
# if (length(rhs) != 1) {
# stop("rhs must be a single numeric value or a data.frame with sets and numeric values for each constraint.")
# }
# if (is.na(defVal)) defVal <- rhs
# rhs <- data.frame()
# }
# if (!is.data.frame(rhs) && is.list(rhs)) {
# xx <- sapply(rhs, length)
# if (any(xx[1] != xx)) {
# stop("Length of the list elements in 'rhs' must be identical.")
# }
# if (xx[1] >= 1) {
# xx <- data.frame(stringsAsFactors = FALSE)
# xx[seq_len(length(rhs[[1]])), ] <- NA
# for (i in names(rhs)) xx[[i]] <- rhs[[i]]
# rhs <- xx
# }
# }
# Replace zero values with 1e-20 in rhs and defVal
if (!is.null(replace_zerros) && any(rhs$rhs == 0)) {
# warning("Zero values in 'rhs' will be replaced with '", replace_zerros, "' to avoid ignoring them by the current interpolation algorithms. Use non-zero value to avoid auto-replacement and the warning. Use 'replace_zerros = NULL' to avoid replacement.")
rhs[rhs == 0] <- replace_zerros
}
if (!is.null(replace_zerros) && !is.na(defVal) && defVal == 0) {
# warning("Zero value in 'defVal' will be replaced with '", replace_zerros, "' to avoid ignoring it by the current interpolation algorithms. Use non-zero value to avoid auto-replacement and the warning. Use 'replace_zerros = NULL' to avoid replacement.")
defVal <- replace_zerros
}
# TYPE vs SET
obj@rhs <- rhs
obj@defVal <- defVal
obj@name <- name
obj@desc <- desc
obj@interpolation <- interpolation
if (!is.null(for.each)) {
if (!is.data.frame(for.each) && is.list(for.each)) {
tmp <- data.frame(stringsAsFactors = FALSE)
fl_null <- sapply(for.each, is.null)
for (i in names(for.each)[fl_null]) {
for.each[[i]] <- NA
}
for (i in names(for.each)) {
t2 <- data.frame(for.each[[i]], stringsAsFactors = FALSE)
colnames(t2) <- i
if (ncol(tmp) == 0) {
tmp <- t2
} else {
tmp <- merge(tmp, t2)
}
}
obj@for.each <- tmp
} else if (is.data.frame(for.each)) {
obj@for.each <- for.each
} else {
stop("Unrecognized 'for.each' parameter. Failed to build the costraint.")
}
}
# for (i in seq_along(arg)) {
# obj <- addSummand(obj, arg = arg[[i]])
# }
arg <- list(...)
for (i in seq_along(arg)) {
obj <- addSummand(obj, arg = arg[[i]])
}
obj
}
#' A function to check if an object is of class `constraint`.
#'
#' @param object any R object
#'
#' @return TRUE if the object inherits class `constraint`, FALSE otherwise.
#' @export
#'
#' @family class constraint
#' @describeIn newConstraint Check if an object is a constraint.
#'
#' @examples
#' isConstraint(1)
#' isConstraint(newConstraint(""))
isConstraint <- function(object) {
inherits(object, "constraint")
}
# @describeIn newConstraint
# @family constraint
# @export
addSummand <- function(
eqt,
variable = NULL,
mult = data.frame(),
for.sum = list(),
arg) {
if (!is.null(names(arg))) {
if (any(names(arg) == "variable")) variable <- arg$variable
if (any(names(arg) == "mult")) mult <- arg$mult
if (any(names(arg) == "for.sum")) for.sum <- arg$for.sum
if (any(names(arg) == "defVal")) defVal <- arg$defVal
}
# eqt, variable, mult, for.sum, arg
st <- new("summand")
st@variable <- variable
if (!is.data.frame(mult) && is.list(mult)) {
xx <- sapply(mult, length)
if (any(xx[1] != xx)) {
stop(paste0("Wrong mult parameters "))
}
if (xx[1] >= 1) {
xx <- data.frame(stringsAsFactors = FALSE)
xx[seq_len(length(mult[[1]])), ] <- NA
for (i in names(mult)) xx[[i]] <- mult[[i]]
mult <- xx
}
}
if (is.data.frame(mult)) {
st@mult <- mult
} else {
st@defVal <- mult
}
st@for.sum <- for.sum
# browser()
if (all(names(.variable_set) != variable)) {
stop(paste0('Unknown variables "', variable, '"in summands "', eqt@name, '"'))
}
need.set <- .variable_set[[variable]]
need.set <- need.set[!(need.set %in% c(names(eqt@for.each), names(st@for.sum)))]
for (i in need.set) {
st@for.sum[i] <- list(NA)
}
if (length(st@for.sum) != 0) {
st@for.sum[sapply(st@for.sum, is.null)] <- NA
}
if (!all(names(st@mult) %in% c(names(eqt@for.each), names(st@for.sum), "value"))) {
stop(paste0('Wrong mult parameter, excessive set: "', paste0(names(st@mult)[!(names(st@mult) %in% names(st@for.sum))], collapse = '", "'), '"'))
}
names(st@defVal) <- NULL
names(st@variable) <- NULL
eqt@lhs[[length(eqt@lhs) + 1]] <- st
eqt
}
# prec <- add0_message$add0_arg$obj
# stm <- add0_message$add0_arg$app
# approxim <- add0_message$add0_arg$approxim
# Calculate do equation need additional set, and add it
.getSetEquation <- function(prec, stm, approxim) {
# browser()
# if (stm@name == "CO2_CAP") browser()
# if (grepl("CESR_", stm@name)) browser()
# if (grepl("CESR_5_2030", stm@name)) browser()
# !!! add interpolation patch here? or in the calling function? !!!
# if (nrow(stm@for.each) > 0) {
# .interpolation0(stm@rhs, parameter = "rhs", defVal = stm@defVal,
# arg = list(approxim = approxim)
# )
# temporary fix for constraints interpolation: expanding year set
# works only for year set and if no NA values in the set
if (!is.null(stm@rhs$year) && !any(is.na(stm@rhs$year))) {
stm@rhs <- interpolate_slot(stm@rhs, val = "rhs")
}
if (!is.null(stm@for.each$year) && !any(is.na(stm@for.each$year))) {
stm@for.each <- interpolate_slot(stm@for.each, val = NULL)
}
# !!! end
stop.constr <- function(x) {
stop(paste0('Constraint "', stm@name, '" error: ', x))
}
get.all.child <- function(x) {
# !!! Rewrite
unique(
c(
x,
c(
# approxim$calendar@slice_ancestry[
# approxim$calendar@slice_ancestry$parent %in% x,
# "child"]
filter(
approxim$calendar@slice_ancestry,
approxim$calendar@slice_ancestry$parent %in% x
)[["child"]]
)
)
)
}
# all.set contain all set for for.each & lhs
# Estimate is need sum for for.each
# set.map need special mapping or consist all set
all.set <- data.frame(
alias = character(), # name in equation
set = character(), # original set
for.each = logical(), # for.each, lhs
lhs.num = numeric(), # number for lhs
lead.year = logical(), # use only for year & lhs (next year)
lag.year = logical(), # use only for year & lhs (old year)
def.lhs = logical(), # not in for.each
new.map = numeric(), # need new sub set
stringsAsFactors = FALSE
)
set.map <- list()
set.map.name <- NULL # Temp vector with name for list set.map
# all.set & set.map
# for.each
old_for_each <- lapply(stm@for.each, function(x) {
if (any(is.na(x))) {
return(NULL)
}
return(unique(x))
})
names(old_for_each) <- colnames(stm@for.each)
nn <- seq_len(length(old_for_each) + sum(sapply(stm@lhs, function(x) length(.variable_set[[x@variable]]))))
all.set[seq_along(nn), ] <- NA
for (i in (1:ncol(all.set))[sapply(all.set, class) == "logical"]) {
all.set[[i]] <- FALSE
}
nn <- 0
if (length(old_for_each) > 0) {
nn <- seq_along(old_for_each)
all.set[nn, "set"] <- names(old_for_each)
all.set[nn, "alias"] <- names(old_for_each)
all.set[nn, "for.each"] <- TRUE
for.each.set <- names(old_for_each)
# Fill add.map for for.each
for (j in for.each.set) {
if (!is.null(old_for_each[[j]]) && !all(prec@set[[j]] %in% old_for_each[[j]])) {
if (any(old_for_each[[j]] %in% prec@set[[j]])) {
# warning(paste0('Set "'))
old_for_each[[j]] <-
old_for_each[[j]][old_for_each[[j]] %in% prec@set[[j]]]
}
set.map.name <- c(set.map.name, j)
set.map[[length(set.map.name)]] <- old_for_each[[j]]
all.set[nn[names(old_for_each) == j], "new.map"] <- length(set.map.name)
}
}
} else {
for.each.set <- NULL
}
# lhs
for (i in seq_along(stm@lhs)) {
# browser()
need.set <- .variable_set[[stm@lhs[[i]]@variable]]
nn <- (nn[length(nn)] + seq_along(need.set))
all.set[nn, "set"] <- need.set
all.set[nn, "alias"] <- need.set
all.set[nn, "lhs.num"] <- i
if (any(names(stm@lhs[[i]]@for.sum) == "lag.year")) {
if (all(need.set != "year")) {
stop.constr("For lag.year have to define use variable with year")
}
all.set[nn[need.set == "year"], c("lag.year", "def.lhs")] <- TRUE
}
if (any(names(stm@lhs[[i]]@for.sum) == "lead.year")) {
if (all(need.set != "year")) {
stop.constr("For lead.year have to define use variable with year")
}
all.set[nn[need.set == "year"], c("lead.year", "def.lhs")] <- TRUE
}
all.set[nn[need.set %in% names(stm@lhs[[i]]@for.sum)], "def.lhs"] <- TRUE
all.set[nn[!(need.set %in% for.each.set)], "def.lhs"] <- TRUE
# Add to set map
st <- names(stm@lhs[[i]]@for.sum)[names(stm@lhs[[i]]@for.sum) %in% need.set &
!sapply(is.na(stm@lhs[[i]]@for.sum), all)]
# Fill add.map for for.lhs
for (j in st) {
if (!is.null(stm@lhs[[i]]@for.sum[[j]]) &&
!all(prec@set[[j]] %in% stm@lhs[[i]]@for.sum[[j]]) && (j != "slice" ||
!all(prec@set[[j]] %in% get.all.child(stm@lhs[[i]]@for.sum[[j]])))) {
# check if the same set in lhs exist
fl <- FALSE
if (all(!c(all.set[nn[need.set == j], c("lead.year", "lag.year")],
recursive = TRUE
))) {
fl <- nn[(!all.set$for.each[nn] & all.set$set[nn] == j &
!is.na(all.set$new.map[nn]))]
}
add.new <- TRUE
if (any(fl)) {
for (k in all.set[fl, "new.map"]) {
if (length(stm@lhs[[i]]@for.sum[[j]]) == length(set.map[[k]]) &&
all(stm@lhs[[i]]@for.sum[[j]] %in% set.map[[k]]) && all(set.map[[k]] %in% stm@lhs[[i]]@for.sum[[j]])) {
all.set[nn[need.set == j], "new.map"] <- k
add.new <- FALSE
}
}
}
if (add.new) {
set.map.name <- c(set.map.name, j)
set.map[[length(set.map.name)]] <- stm@lhs[[i]]@for.sum[[j]]
all.set[nn[need.set == j], "new.map"] <- length(set.map.name)
}
}
}
}
# Add alias
fl <- (!all.set$for.each & all.set$def.lhs & all.set$set %in% for.each.set)
if (any(fl)) {
all.set[fl, "alias"] <- paste0(all.set[fl, "set"], "p")
}
# Need add code to reduce additional mapping
# Maaping
if (length(set.map) > 0) {
mpp <- all.set[!is.na(all.set$new.map), c("new.map", "alias")]
mpp <- mpp[!duplicated(mpp$new.map), ]
mpp <- mpp[sort(mpp$new.map, index.return = TRUE)$ix, , drop = FALSE]
new.map.name <- paste0("mCns", stm@name, "_", mpp$new.map)
new.map.name.full <- paste0(new.map.name, "(", mpp$alias, ")")
for (i in seq_along(set.map)) {
prec@parameters[[new.map.name[i]]] <-
addMultipleSet(
newParameter(new.map.name[i], set.map.name[i], "map"),
c(set.map[[i]])
)
}
# copy new.map for lhs set that define in for each
fl <- seq_len(nrow(all.set))[all.set$for.each & !is.na(all.set$new.map)]
for (i in fl) {
all.set[!all.set$for.each & !all.set$def.lhs & all.set$set == all.set$set[i], "new.map"] <- all.set$new.map[i]
}
}
if (nrow(all.set) > 0) {
st <- unique(all.set$set)
st <- st[!(st %in% names(approxim))]
for (ss in st) approxim[[ss]] <- prec@set[[ss]]
}
# Generate GAMS code with mult & rhs parameters
res <- list()
# Declaration equation in model
res$equationDeclaration2Model <- paste0("eqCns", stm@name)
# Declaration equation
if (length(old_for_each) == 0) {
res$equationDeclaration <- res$equationDeclaration2Model
} else {
res$equationDeclaration <- paste0(res$equationDeclaration2Model, "(", paste0(names(old_for_each), collapse = ", "), ")")
}
# Equation before ..
res$equation <- res$equationDeclaration
if (any(is.na(stm@for.each))) {
tmp_fe <- stm@for.each
fl_na <- colnames(stm@for.each)[apply(is.na(stm@for.each), 2, any)]
for (i in fl_na) {
tmg <- prec@parameters[[i]]@data
if (i == "year") {
tmg <- prec@parameters[["mMidMilestone"]]@data
if (any(all.set$lag.year)) {
tmg <- tmg[
!(tmg$year %in% prec@parameters[["mMilestoneFirst"]]@data$year), ,
drop = FALSE
]
}
if (any(all.set$lead.year)) {
tmg <- tmg[
tmg$year %in% prec@parameters[["mMilestoneHasNext"]]@data$year, ,
drop = FALSE
]
}
}
tmp_fe <- rbind(
merge(
select(tmp_fe, -any_of(i)),
# tmp_fe[, colnames(tmp_fe) != i, drop = FALSE],
tmg
),
tmp_fe[!is.na(tmp_fe[[i]]), , drop = FALSE]
)
tmp_fe <- tmp_fe[!duplicated(tmp_fe), , drop = FALSE]
}
stm@for.each <- tmp_fe
}
if (!is.null(stm@for.each$year)) {
stm@for.each <- stm@for.each[stm@for.each$year %in% prec@parameters[["mMidMilestone"]]@data$year, , drop = FALSE]
if (any(all.set$lag.year)) {
stm@for.each <- stm@for.each[!(stm@for.each$year %in% prec@parameters[["mMilestoneFirst"]]@data$year), , drop = FALSE]
}
if (any(all.set$lead.year)) {
stm@for.each <- stm@for.each[stm@for.each$year %in% prec@parameters[["mMilestoneHasNext"]]@data$year, , drop = FALSE]
}
}
if (nrow(stm@for.each) > 0) {
# browser()
nmn <- paste0("mCnsForEach", stm@name)
prec@parameters[[nmn]] <- .dat2par(
newParameter(nmn, colnames(stm@for.each), "map",
interpolation = stm@interpolation
),
stm@for.each
)
res$equation <- paste0(res$equation, "$", nmn, "(", paste0(colnames(stm@for.each), collapse = ", "), ")")
}
res$equation <- paste0(res$equation, ".. ")
# Add eq
res$equation <- paste0(
res$equation,
" ### ",
c("==" = "=e=", ">=" = "=g=", "<=" = "=l=")[as.character(stm@eq)],
" "
)
# Add rhs
if (nrow(stm@rhs) != 0 &&
(any(stm@rhs$rhs != 0) ||
(stm@defVal != 0 && nrow(stm@for.each) > nrow(stm@rhs)))) {
# Complicated rhs
# Generate approxim
# browser()
approxim2 <-
approxim[unique(
c(
colnames(stm@rhs)[colnames(stm@rhs) %in% names(approxim)],
"solver", "year", "calendar"
)
)]
if (any(names(approxim2) == "slice")) {
approxim2$slice <- approxim2$calendar@slice_share$slice
}
fl <- (all.set$for.each & !is.na(all.set$new.map) &
all.set$set %in% colnames(stm@rhs))
need.set <- all.set[fl, , drop = FALSE]
for (j in seq_len(nrow(need.set))) {
approxim2[[need.set[j, "set"]]] <- set.map[[need.set[j, "new.map"]]]
}
approxim2$fullsets <- approxim$fullsets
need.set0 <- for.each.set[for.each.set %in% colnames(stm@rhs)]
# browser()
xx <- newParameter(paste0("pCnsRhs", stm@name), need.set0, "numpar",
defVal = stm@defVal,
# interpolation = "back.inter.forth",
# interpolation = .defInt[["rhs"]], #!!! ToDO: add @defInt slot
interpolation = stm@interpolation,
colName = "rhs"
)
# !!! Similar interpolation for LHS is needed
# browser()
yy <- .interp_numpar(stm@rhs, "rhs", xx, approxim2)
n1 <- colnames(yy)[colnames(yy) != "value"]
# yy <- yy[
# (apply(yy[, n1, drop = FALSE], 1,
# paste0, collapse = "##") %in%
# apply(stm@for.each[, n1, drop = FALSE], 1,
# paste0, collapse = "##")
# ), , drop = FALSE]
# check:
# yy[apply(select(yy, all_of(n1)), 1, paste0, collapse = "##") %in%
# apply(select(stm@for.each, all_of(n1)), 1, paste0, collapse = "##"),]
# same using dplyr
suppressMessages({
yy <- yy |> right_join(select(stm@for.each, all_of(n1)))
})
prec@parameters[[xx@name]] <- .dat2par(xx, yy)
# Add mult
res$equation <- paste0(
res$equation, xx@name, "(",
paste0(need.set0, collapse = ", "), ")"
)
} else {
res$equation <- paste0(res$equation, stm@defVal)
}
# Add lhs
lhs_equation <- ""
# Add lhs to equation
lhs.set <- all.set[!all.set$for.each, , drop = FALSE]
for (i in seq_along(stm@lhs)) {
vrb <- stm@lhs[[i]]@variable
lhs.set2 <- lhs.set[lhs.set$lhs.num == i, ]
vrb.lhs <- .variable_mapping[[vrb]]
# Add multiple to vrb
# Add to year multiplier if lag.year | lead.year
if ((any(lhs.set2$lead.year) ||
any(lhs.set2$lag.year)) &&
(nrow(stm@lhs[[i]]@mult) == 0 ||
all(colnames(stm@lhs[[i]]@mult) != "year"))) {
if (nrow(stm@lhs[[i]]@mult) == 0) {
stm@lhs[[i]]@mult <- data.frame(
year = NA, value = stm@lhs[[i]]@defVal,
stringsAsFactors = FALSE
)
} else {
stm@lhs[[i]]@mult$year <- NA
}
}
# Add multiplier
if (nrow(stm@lhs[[i]]@mult) != 0) {
# Complicated parameter
# Generate approxim
approxim2 <- approxim[unique(c(colnames(stm@lhs[[i]]@mult)[
colnames(stm@lhs[[i]]@mult) %in% names(approxim)
], "solver", "year"))]
if (any(names(approxim2) == "slice")) {
approxim2$slice <- approxim2$calendar@slice_share$slice
}
need.set <- lhs.set2[lhs.set2$set %in% colnames(stm@lhs[[i]]@mult), "set"]
need.set2 <- lhs.set2[!is.na(lhs.set2$new.map) &
lhs.set2$set %in% colnames(stm@lhs[[i]]@mult), ]
for (j in seq_len(nrow(need.set2))) {
approxim2[[need.set2[j, "set"]]] <- set.map[[need.set2[j, "new.map"]]]
if (any(colnames(stm@lhs[[i]]@mult) %in% c(need.set, "value"))) {
if (!all(colnames(stm@lhs[[i]]@mult) %in%
c(for.each.set, need.set, "value"))) {
stop(paste0(
"There are unknown set in constraint ",
stm@name, ", mult ", i, ': "',
paste0(
colnames(stm@lhs[[i]]@mult)[
!(colnames(stm@lhs[[i]]@mult) %in% c(for.each.set, "value"))
],
collapse = '", "'
), '"'
))
}
# Add set that from for.each
nslc <- colnames(stm@lhs[[i]]@mult)[
!(colnames(stm@lhs[[i]]@mult) %in% c(need.set, "value"))
]
need.set <- c(need.set, nslc)
if (nrow(stm@for.each) > 0) {
for (j in nslc) {
approxim2[[j]] <- unique(stm@for.each[[j]])
}
} else {
for (j in nslc) {
approxim2[[j]] <- approxim[[j]]
}
if (any(nslc == "slice")) {
approxim2$slice <- approxim$calendar@slice_share$slice
}
}
}
}
approxim2$fullsets <- approxim$fullsets
# browser()
xx <- newParameter(paste0("pCnsMult", stm@name, "_", i),
need.set,
"numpar",
defVal = stm@lhs[[i]]@defVal, # !!! Check
# interpolation = "back.inter.forth"
# interpolation = .defInt[["rhs"]] # !!! Temporary fix
interpolation = stm@interpolation
)
prec@parameters[[xx@name]] <-
.dat2par(xx, .interp_numpar(stm@lhs[[i]]@mult, "value", xx, approxim2))
if (any(lhs.set2$lead.year) || any(lhs.set2$lag.year)) {
yy <- .add_dropped_zeros(prec, xx@name)
nn <- approxim$mileStoneForGrowth[as.character(yy$year)]
if (any(lhs.set2$lag.year)) nn <- (-nn)
yy$value <- (sign(yy$value) * abs(yy$value)^nn)
prec@parameters[[xx@name]] <- .dat2par(xx, yy)
}
# Add mult
vrb.lhs <- paste0(
xx@name, "(", paste0(need.set, collapse = ", "),
") * ", vrb.lhs
)
} else if (stm@lhs[[i]]@defVal != 1) {
vrb.lhs <- paste0(stm@lhs[[i]]@defVal, " * ", vrb.lhs)
}
# Replace setsname
for (j in seq_len(nrow(lhs.set2))[lhs.set2$alias != lhs.set2$set]) {
vrb.lhs <- gsub(paste0(" ", lhs.set2$set[j], " "), lhs.set2$alias[j], vrb.lhs)
}
vrb.lhs <- gsub(
"[ ]*[$][ ]*", "$",
gsub(
"[ ]*[)]", ")",
gsub(
"[ ]*[(][ ]*", "(",
gsub(
"[ ]*[,][ ]*", ", ",
vrb.lhs
)
)
)
)
# Generate data to equation
if (i != 1) lhs_equation <- paste0(lhs_equation, "+")
if (all(!lhs.set2$def.lhs)) {
lhs_equation <- paste0(lhs_equation, vrb.lhs)
} else {
lhs.set3 <- lhs.set2[lhs.set2$def.lhs, , drop = FALSE]
cnd <- NULL
if (any(!is.na(lhs.set3$new.map))) {
cnd <- c(cnd, new.map.name.full[lhs.set3$new.map[!is.na(lhs.set3$new.map)]])
}
if (any(lhs.set3$lag.year == "year")) {
cnd <- c(cnd, "mMilestoneNext(yearp, year)")
} else if (any(lhs.set3$lead.year)) {
cnd <- c(cnd, "mMilestoneNext(year, yearp)")
} else if (any(lhs.set3$set == "year")) {
cnd <- c(cnd, "mMidMilestone(year)")
}
if (any(grep("[$]", vrb.lhs))) {
tmp <- gsub(".*[$]", "", vrb.lhs)
if (substr(tmp, 1, 1) == "(") {
tmp <- substr(tmp, 2, nchar(tmp) - 1)
}
cnd <- c(cnd, tmp)
vrb.lhs <- gsub("[$].*", "", vrb.lhs)
}
# Finish for sum
if (sum(lhs.set2$def.lhs) == 1) {
lhs_equation <- paste0(lhs_equation, " sum(", lhs.set3$alias)
} else {
lhs_equation <- paste0(
lhs_equation, " sum((",
paste0(lhs.set3$alias, collapse = ", "), ")"
)
}
if (length(cnd) > 1 || any(grep("[ )]and[ (]", cnd))) {
lhs_equation <- paste0(
lhs_equation, "$(",
paste0(cnd, collapse = " and "), "), ", vrb.lhs, ")"
)
} else if (length(cnd) == 1) {
lhs_equation <- paste0(lhs_equation, "$", cnd, ", ", vrb.lhs, ")")
} else {
stop("error!")
}
}
}
res$equation <- gsub("###", lhs_equation, res$equation)
res$equation <- gsub("[+][[:blank:]]*[-]", "-", res$equation)
res$equation <- paste0(res$equation, ";")
prec@gams.equation[[stm@name]] <- res
prec
}
# .getSetEquation(prec, stm, approxim)@gams.equation
#' @export
#' @family constraint policy
#' @rdname newConstraint
newConstraintS <- function(
name,
type,
eq = "==",
rhs = 0,
for.sum = list(),
for.each = list(),
defVal = 0,
rule = NULL,
comm = NULL,
cout = TRUE,
cinp = TRUE,
aout = TRUE,
ainp = TRUE
) { # , emis = TRUE
stop.newconstr <- function(x) {
stop(paste0('Constraint "', name, '" error: ', x))
}
if (type == "tax") {
return(newTax(name, tax = rhs, comm = comm, region = for.each$region, year = for.each$year, slice = for.each$slice))
}
if (type == "subs") {
return(newSub(name, sub = rhs, comm = comm, region = for.each$region, year = for.each$year, slice = for.each$slice))
}
# if (any(grep('(share|growth)', type))) stop.newconstr(paste(type, 'have to do'))
# For wich kind of variables (capacity, newcapacity, input or output)
if (any(grep("inp", type))) {
inpout <- "Inp"
} else if (any(grep("out", type))) {
inpout <- "Out"
} else if (type == "capacity") {
inpout <- "Cap"
} else if (type == "newcapacity") {
inpout <- "NewCap"
} else if ("balance" == type) {
inpout <- "Balance"
} else {
stop.newconstr(paste0("Unknown type: ", type))
}
#
set.vec <- c(names(for.each), names(for.sum))
psb.vec <- c("sup", "stg", "tech", "imp", "expp")
psb.vec.tp <- c(
sup = "Sup", stg = "Storage", tech = "Tech",
imp = "Import", expp = "Export"
)
names(psb.vec) <- psb.vec
is.set <- psb.vec[psb.vec %in% set.vec]
if (length(is.set) > 1) {
stop.newconstr(paste0("There are more than one subsets"))
}
vec.tp <- psb.vec.tp[is.set]
if (length(is.set) == 0) {
if (all(inpout != c("Inp", "Out", "Balance"))) {
stop.newconstr(paste0("For ", type, " have to define some subsets"))
}
vrb <- paste0("v", inpout, "Tot"[inpout != "Balance"])
} else {
if (is.set == "tech" && inpout == "Inp") {
vrb <- paste0("vTech", c("", "A")[c(cinp, ainp)], "Inp")
} else if (is.set == "tech" && inpout == "Out") {
vrb <- paste0("vTech", c("Out", "AOut")[c(cout, aout)]) # , 'EmsFuel' , emis
} else if (any(type == c("newcapacity", "capacity"))) {
if (all(is.set != c("stg", "tech"))) {
stop.newconstr(paste0("For ", type, " could be define only for tech and storage"))
}
vrb <- paste0("v", vec.tp, c(capacity = "Cap", newcapacity = "NewCap")[type])
} else {
vrb <- paste0("v", vec.tp, inpout)
}
}
term <- list(for.sum = for.sum, variable = vrb[1])
arg <- list(term)
for (i in vrb[-1]) {
term$variable <- i
arg[[length(arg) + 1]] <- term
}
# To share
if (any(grep("share", type))) {
if (length(c(rhs, recursive = TRUE)) == 0) {
rhs <- (-defVal)
} else if (is.list(rhs)) {
rhs$value <- (-rhs$rhs)
rhs$rhs <- NULL
} else {
rhs <- (-rhs)
}
# for (i in seq_along(arg)) {
# arg[[i]]$mult <- rhs
# }
term <- list(
for.sum = for.sum[!(names(for.sum) %in% psb.vec)],
variable = paste0("v", inpout, "Tot"), mult = rhs
)
rhs <- 0
defVal <- 0
arg[[length(arg) + 1]] <- term
}
# To growth
if (any(grep("growth", type))) {
if (length(c(rhs, recursive = TRUE)) == 0) {
rhs <- (1 / defVal)
} else {
if (is.numeric(rhs)) {
rhs <- (1 / rhs)
} else {
rhs$value <- (1 / rhs$rhs)
rhs$rhs <- NULL
}
}
nn <- seq_along(arg)
nk <- length(arg)
for (i in nn) {
arg[[i + nk]] <- arg[[i]]
arg[[i + nk]]$mult <- rhs
arg[[i + nk]]$for.sum["lead.year"] <- list(NULL)
arg[[i + nk]]$for.sum$year <- NULL
arg[[i]]$mult <- (-1)
}
rhs <- 0
defVal <- 0
}
newConstraint(name,
eq = eq, for.each = for.each, defVal = defVal,
rhs = rhs, arg = arg
)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.