#' Builds a Coupled Scenario
#'
#' Merge a named list of \code{\link{sdScenarioClass}} objects in to a unique
#' coupled scenario object with all the variables named with the prefix
#' 'ModelId.' followed by the original variable name. The 'ModelId' is the name
#' of each element in the \code{scenarios} list and must be the same name of the
#' component ID that will use it.
#' Use the arguments to define the coupled scenario simulation time sequence
#' (the arguments \code{from}, \code{to} and \code{by} must be present to define
#' the time sequence) and the integrator method to be used in the simulations
#' (or the default 'lsoda' will be used).
#'
#' @param coupledScenarioId A string with the coupled scenario ID. If missing a
#' default timestamp ID will be created.
#' @param scenarios A named list of not empty \code{\link{sdScenarioClass}}
#' objects and/or character strings with a sdScenario XML or EXCEL file name.
#'
#' When passing a scenario in a EXCEL file it should follow the default format
#' described in the \code{\link{sdLoadScenario}} documentation.
#' All the scenario elements of this list must be named with the model ID that
#' will use it.
#' @param from The starting value of the time sequence. Numeric of
#' length 1.
#' @param to The end (maximal) value of the time sequence. Numeric of length 1.
#' @param by number: increment of the time sequence.
#' @param method The integrator to be used in the simulation,
#' a string ("lsoda", "lsode", "lsodes","lsodar","vode", "daspk", "euler",
#' "rk4", "ode23", "ode45", "radau", "bdf", "bdf_d", "adams", "impAdams" or
#' "impAdams_d"). Default value is "lsoda".
#'
#' When running with support to events the given method must
#' be one of the following routines, which have root-finding capability:
#' \code{\link[deSolve]{lsoda}}, \code{\link[deSolve]{lsode}} or
#' \code{\link[deSolve]{radau}}; If the given method is different from any of
#' these three routines the simulator will run with the default method
#' "lsoda".
#'
#' See the \code{\link[deSolve]{ode}} and the \code{\link[deSolve]{events}}
#' details section for more information.
#' @param timeSeriesDirectory The directory where time series inputs are stored
#' (when passing the time series inputs via external text files).
#' @param varNames logical: if \code{TRUE} the return value is a list with two
#' elements - the \code{coupledScenario} containing the built coupled
#' scenario object and the \code{varNames} containing a list of all the
#' \code{scenarios} variables names; if \code{FALSE} the return value is just
#' the coupled scenario object, the default.
#' @return Either the coupled scenario object with all variables named with
#' the prefix 'ModelId.' followed by the original variable name or, if
#' \code{varNames} is \code{TRUE}, a list with two
#' elements - the \code{coupledScenario} containing the built coupled
#' scenario object and the \code{varNames} containing a list of all the
#' \code{scenarios} variables names.
#' @examples
#' ## Let's build a coupled scenario for the Lotka-Volterra example model
#' # presented in the help('sdCoupledModel')
#'
#' # create the Prey model variables and scenario
#' stPrey <- list(P = 1)
#' parsPrey <- list(rG = 1.0,
#' K = 10)
#' inpPrey <- list(IngestC = 0)
#'
#' preyScen <- sdScenario(scenarioId = "preyScen",
#' state = stPrey,
#' parameter = parsPrey,
#' input = inpPrey)
#'
#' # create the Consumer model variables and scenario
#' stConsumer <- list(C = 2)
#' parsConsumer <- list(rI = 0.2,
#' rM = 0.2 ,
#' AE = 0.5)
#' inpConsumer <- list(P = 0)
#'
#' consumerScen <- sdScenario(scenarioId = "consumerScen",
#' state = stConsumer,
#' parameter = parsConsumer,
#' input = inpConsumer)
#'
#' ## Create the coupled scenario and print it
#' coupledLvScen <- sdBuildCoupledScenario(
#' coupledScenarioId = "LVcoupled",
#' scenarios = c(Prey = preyScen,
#' Consumer = consumerScen),
#' method = "lsoda",
#' from = 0,
#' to = 200,
#' by = 1)
#' print(coupledLvScen)
#' ## this scenario can be used to simulate the coupled Lotka-Volterra Model
#' # in a different environment by setting different values for the variables
#' # and passing it via the argument 'scenario' to the sdSimulate function
sdBuildCoupledScenario = function(coupledScenarioId = NULL,
scenarios = NULL,
from = NULL,
to = NULL,
by = NULL,
method = c("lsoda", "lsode", "lsodes", "lsodar",
"vode", "daspk", "euler", "rk4",
"ode23", "ode45", "radau", "bdf",
"bdf_d", "adams", "impAdams",
"impAdams_d"),
timeSeriesDirectory = "",
varNames = FALSE)
{
if (!is.null(from) && !is.null(to) && !is.null(by))
times <- list(from = from, to = to, by = by)
else
times <- NULL
componentsId <- names(scenarios)
if (is.null(componentsId) || any(componentsId == ""))
sdCoupledModelMsg$sdBuildCoupledScenario1()
# build default scenario concatanating the components default scenario
stComponents <- list()
ctComponents <- list()
inpComponents <- list()
parComponents <- list()
swComponents <- list()
unitComponents <- list()
descriptionComponents <- list()
interpolComponents <- list()
modelVarNames <- list()
# trim the components names
componentsId <- gsub(" ", "", componentsId, fixed = TRUE)
for (modelId in componentsId)
{
scenComponent <- scenarios[[modelId]]
if (is.character(scenComponent))
scenComponent <- sdLoadScenario(file = scenComponent,
timeSeriesDirectory = timeSeriesDirectory)
if (!inherits(scenComponent, "sdScenarioClass"))
{
sdCoupledModelMsg$sdBuildCoupledScenario2(scenComponent, modelId)
next
}
if (length(scenComponent$state) > 0)
stComponents[[modelId]] <- scenComponent$state
if (length(scenComponent$constant) > 0)
ctComponents[[modelId]] <- scenComponent$constant
if (length(scenComponent$input) > 0)
inpComponents[[modelId]] <- scenComponent$input[
!(names(scenComponent$input) %in% c("interpolation_", "fun_"))]
if (length(scenComponent$input$interpolation_) > 0)
interpolComponents[[modelId]] <-
scenComponent$input$interpolation_
if (length(scenComponent$parameter) > 0)
parComponents[[modelId]] <- scenComponent$parameter
if (length(scenComponent$switch) > 0)
swComponents[[modelId]] <- scenComponent$switch
if (length(scenComponent$unit) > 0)
unitComponents[[modelId]] <- scenComponent$unit
if (length(scenComponent$description) > 0)
descriptionComponents[[modelId]] <-
scenComponent$description
modelVarNames[[modelId]] <- c(
names(stComponents[[modelId]]),
names(ctComponents[[modelId]]),
names(inpComponents[[modelId]]),
names(parComponents[[modelId]]),
names(swComponents[[modelId]]))
}
coupledScenario <- sdScenario(
scenarioId = coupledScenarioId,
method = method[[1]],
times = times,
state = unlist(stComponents, recursive = FALSE),
constant = unlist(ctComponents, recursive = FALSE),
input = unlist(inpComponents, recursive = FALSE),
interpolation = as.list(unlist(interpolComponents)),
parameter = unlist(parComponents, recursive = FALSE),
switch = unlist(swComponents, recursive = FALSE),
unit = unlist(unitComponents, recursive = FALSE),
description = unlist(descriptionComponents, recursive = FALSE),
timeSeriesDirectory = timeSeriesDirectory)
if (!varNames)
return(coupledScenario)
else
return(list(coupledScenario = coupledScenario, varNames = modelVarNames))
}
#' Class Representation of a Coupled System Dynamics Model
#'
#' Represents a coupled system dynamics model made up of instanced
#' \code{\link{sdModelClass}}, \code{\link{sdStaticModelClass}} and/or
#' \code{\link{sdCoupledModelClass}} components and a list of connections that
#' define the flow of information between components.
#' The \code{connections} list determines loops of information feedback and
#' circular causality for conceptualizing the structure of a complex system and
#' for communicating model-based insights.
#' The complex system is solved by integrating all the coupled system components
#' simultaneously as one, updating the connections at each time step.
#'
#' To create an object use the constructor \code{\link{sdCoupledModel}}.
#'
#' To load a model from a XML file use the \code{\link{sdLoadModel}} function.
#'
#' To simulate a model in different scenarios use the \code{\link{sdSimulate}}
#' function. Make sure to build the default coupled scenario before
#' running the simulation or some computation time will be spent running the
#' \code{\link{sdBuildCoupledScenario}} function to build it.
#'
#' The object fields not declared in this documentation are automatically
#' generated by the \code{$buildCoupledModel} method to be used by the package
#' during simulations, e.g. the lists with all the components functions,
#' the vectors representing the connections (eqConnections and stConnections)
#' and the list with the components variables indexes (indexComponents).
#'
#' @field coupledModelId A string with the coupled model identification. Any
#' non-word character will be removed and the result will be converted to a
#' valid name (see \code{\link[base]{make.names}}).
#' @field coupledModelDescription A string with the coupled model description.
#' @field components A list of \code{\link{sdModelClass}},
#' \code{\link{sdStaticModelClass}} and/or \code{\link{sdCoupledModelClass}}
#' objects.
#'
#' The models must have different \code{ID}'s that will be used as unique
#' identifiers, otherwise only the last model added with the same ID will be
#' kept.
#' @field connections A list of vectors that specifies the connections.
#'
#' Each vector represents a connection, it must have 5 elements and be defined
#' as:
#'
#' c(ID, Component 1, Input 1, Component 2, Output 2).
#'
#' Where ID: is the
#' connection identification; Component 1: is the identification of the receiver
#' component; Input 1: is the name of the input variable from the receiver
#' component (component 1); Component 2: is the identification of the sender
#' component; and Output 2: is the name of the connected state variable or,
#' auxiliary or algebraic, equation with prefix st$, aux$ or eq$, respectively,
#' indicating the output type from the sender component (Component 2), e.g.
#' st$<varName>, aux$<eqName> or eq$<eqName>.
#' @field defaultCoupledScenario The default coupled scenario, a
#' \code{\link{sdScenarioClass}} object. Uses the method
#' \code{$buildCoupledModel} to automatically build it by merging the
#' components default scenarios. See \code{\link{sdBuildCoupledScenario}} for
#' more information about how these merge occures.
#' @section Methods Definition:
#' \describe{
#' \item{\code{$initialize(coupledModelId = NULL,
#' coupledModelDescription = NULL, components = NULL, connections = NULL)}}{
#' Class constructor. Define the base components and connections of the coupled
#' system dynamics model.
#' The \code{coupledModelId} is mandatory, if missing a default timestamp ID
#' will be created.
#'
#' \strong{Arguments}
#'
#' \emph{See the Fields section above for the arguments descriptions.}
#' }
#'
#' \item{\code{$print()}}{A short print of the object fields.}
#'
#' \item{\code{$addComponent(...)}}{Add extra components to the coupled system
#' dynamics model.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{...}{A character string with a model XML file name or a
#' \code{\link{sdModelClass}}, \code{\link{sdStaticModelClass}} and/or
#' \code{\link{sdCoupledModelClass}} objects already initialized. The
#' models must have different \code{ID}'s to be used as unique
#' identifiers. Only one component by ID is stored.}}}
#'
#' \item{\code{$removeComponent(...)}}{Remove the specified components from the
#' coupled system dynamics model and all connections involving them.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{...}{Character objects containing the model IDs to be removed. If
#' missing all components will be removed.}
#' }}
#'
#' \item{\code{$addConnection(...)}}{Add extra connections to the
#' coupled system dynamics model.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{...}{Connection vectors with 5 character elements. See more details in
#' the connections field section above.}
#' }}
#'
#' \item{\code{$removeConnection(...)}}{Remove the specified connections
#' from the model \code{connections} list.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{...}{Character objects containing the connections ID's to be removed.
#' If missing all the connections will be removed.}
#' }}
#'
#' \item{\code{$buildCoupledModel(from = NULL, to = NULL, by = NULL,
#' method = NULL, timeSeriesDirectory = "")}}{Build the default coupled scenario
#' by merging the
#' components default scenarios (see \code{\link{sdBuildCoupledScenario}}),
#' initialize the coupled auxiliary and/or algebraic equations, build the
#' vectors representing the connections (\code{eqConnections} and
#' \code{stConnections}) and the list with the components variables indexes
#' (\code{indexComponents}). The arguments \code{from}, \code{to} and \code{by}
#' must be present to define the simulation time sequence.
#'
#' If the components have support to events and no component defined an event
#' function the simulation will stop in the first root, otherwise the
#' corresponding event function will be trigged.
#'
#' Use regular expressions to update the components equations with the name
#' of the coupled scenario variables.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{from}{The starting value of the time sequence. Numeric of length 1.}
#' \item{to}{The end (maximal) value of the time sequence. Numeric of length 1.}
#' \item{by}{number: increment of the time sequence.}
#' \item{method}{The integrator to be used in the simulation,
#' a string ("lsoda", "lsode", "lsodes","lsodar","vode", "daspk", "euler",
#' "rk4", "ode23", "ode45", "radau", "bdf", "bdf_d", "adams", "impAdams" or
#' "impAdams_d"). Default value is "lsoda".
#'
#' When running with support to events the given method must
#' be one of the following routines, which have root-finding capability:
#' \code{\link[deSolve]{lsoda}}, \code{\link[deSolve]{lsode}} or
#' \code{\link[deSolve]{radau}}; If the given method is different from any of
#' these three routines the simulator will run with the default method
#' "lsoda".
#'
#' See the \code{\link[deSolve]{ode}} and the \code{\link[deSolve]{events}}
#' details section for more information.}
#' \item{timeSeriesDirectory}{The directory where time series inputs are stored
#' (when passing the time series inputs via external text files).}
#' }
#' }
#'
#' \item{\code{$isBuilt()}}{Test if the coupled model was already built and not
#' modified. Return a logical object.
#' }
#'
#' \item{\code{$verifyModel(scenario = NULL, verbose = FALSE,
#' timeSeriesDirectory = "")}}{Execute the first step of the coupled model
#' simulation in the default scenario or merged with a given one.
#' Check for possible incorrect return values and variables in the components
#' definitions and warn the user.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{scenario}{A coupled scenario object (see
#' \code{\link{sdBuildCoupledScenario}}), or a list of sdScenario objects and/or
#' character strings with a sdScenario XML or EXCEL file name - the
#' elements of this list must be named with the model ID that represent's it.
#' If missing validate the model using the default scenario.}
#' \item{verbose}{Logical: If \code{TRUE} provides additional details as to what
#' the computer is doing. Default = \code{FALSE}.}
#' \item{timeSeriesDirectory}{The directory where time series inputs are stored
#' (when passing the time series inputs via external text files).}
#' }}
#'
#' \item{\code{$saveToXml(file = "sdCoupledModel.xml")}}{Save the components
#' and the connections in a XML file.
#'
#' \strong{Arguments}
#'
#' \describe{
#' \item{file}{A character object naming the file to save to. The file extension
#' must be included in the file name, e.g. '.xml'.}
#' }}
#' }
#' @examples
#' # The Lotka-Volterra consumer-prey model implemented as a coupled model with
#' # the components being the population models of a prey and a consumer and a
#' # static model for the environment capacity
#'
#' # set the time sequence and use the default method
#' times <- list(from = 0, to = 200, by = 1)
#'
#' # Prey model variables and ode function
#' stPrey <- list(P = 1)
#' parsPrey <- list(rG = 1.0)
#' inpPrey <- list(ingestC = 0,
#' envCapacity = 1)
#' auxPrey <- list(GrowthP = "par$rG * st$P * inp$envCapacity")
#' descriptionsPrey <- list(P = "population of preys",
#' rG = "growth rate of prey",
#' ingestC = "Consumer ingestion",
#' envCapacity = "available environment capacity")
#'
#' LVodePrey <- function(t, st, ct, par, inp, sw, aux)
#' {
#' dP <- aux$GrowthP - inp$ingestC
#'
#' return(list(c(dP)))
#' }
#'
#' # create the component prey model
#' prey <- sdModel(modelId = "Prey",
#' defaultScenario = sdScenario(scenarioId = "preyScen",
#' times = times,
#' state = stPrey,
#' parameter = parsPrey,
#' input = inpPrey,
#' description = descriptionsPrey),
#' aux = auxPrey,
#' DifferentialEquations = LVodePrey)
#'
#' # Consumer model variables and ode function
#' stConsumer <- list(C = 2)
#' parsConsumer <- list(rI = 0.2,
#' rM = 0.2 ,
#' aE = 0.5)
#' inpConsumer <- list(P = 0)
#' auxConsumer <- list(ingestC = "par$rI * inp$P * st$C",
#' "mortC <- par$rM * st$C")
#' descriptionsConsumer <- list(C = "population of consumer",
#' rM = "mortality rate of consumer",
#' aE = "assimilation efficiency",
#' rI = "rate of ingestion",
#' P = "population of preys")
#'
#' LVodeConsumer <- function(t, st, ct, par, inp, sw, aux)
#' {
#' dC <- aux$ingestC * par$aE - aux$mortC
#'
#' return(list(c(dC)))
#' }
#'
#' # create the component consumer model
#' consumer <- sdModel(modelId = "Consumer",
#' defaultScenario = sdScenario(
#' scenarioId = "consumerScen",
#' times = times,
#' state = stConsumer,
#' parameter = parsConsumer,
#' input = inpConsumer,
#' description = descriptionsConsumer),
#' aux = auxConsumer,
#' DifferentialEquations = LVodeConsumer)
#'
#' # Environment model variables and algebraic equations
#' parEnv <- data.frame(Variable = c("K"),
#' Value = c(10),
#' Description =c("carrying capacity"))
#' inpEnv <- data.frame(Variable = c("P"),
#' Value = c(1),
#' Description = c("Population size"))
#' eqEnvironment <- list(regulatingCapacity = "1 - inp$P/par$K")
#'
#' # create the component environment capacity model
#' environmentCap <- sdStaticModel(staticModelId = "Environment",
#' defaultScenario = sdScenario(
#' scenarioId = "EnvironmentScen",
#' parameter = parEnv,
#' input = inpEnv,
#' times = times),
#' equations = eqEnvironment)
#'
#' # create the coupled model connections list
#' # conP: inform the consumer model about the amount of preys;
#' # conIngestC: inform the prey model about the consumer ingestion;
#' # conPEnv: inform the environment model about the amount of preys and
#' # conEnvCapacity: inform the prey model about the available environment capacity
#' lvConnections <- list(
#' c("conP", "Consumer", "P", "Prey", "st$P"),
#' c("conIngestC", "Prey", "ingestC", "Consumer", "aux$ingestC"),
#' c("conPEnv", "Environment", "P", "Prey", "st$P"),
#' c("conEnvCapacity", "Prey", "envCapacity", "Environment", "eq$regulatingCapacity"))
#'
#' # Create the Lotka-Volterra coupled model
#' coupledLV <- sdCoupledModel(coupledModelId = "LVCoupled",
#' components = c(prey, consumer, environmentCap),
#' connections = lvConnections,
#' coupledModelDescription =
#' "Lotka-Volterra Equations implemented as a coupled model")
#' # build the coupled model and validate it
#' coupledLV$buildCoupledModel(from = 0,
#' to = 200,
#' by = 1)
#' coupledLV$verifyModel(verbose = TRUE)
#'
#' # simulate the coupled model and plot the results
#' outclv <- sdSimulate(model = coupledLV)
#' outclv$plot("Prey.P Environment.regulatingCapacity", "Prey.P Consumer.C",
#' main = c("Coupled Prey Regulated by Environment Capacity",
#' "Coupled Prey and Consumer by Lotka-Volterra"),
#' multipleYAxis = TRUE)
sdCoupledModelClass <- R6::R6Class(
classname = "sdCoupledModelClass",
public = list(
# Class Public Methods
initialize = function(coupledModelId = NULL,
coupledModelDescription = NULL,
components = NULL,
connections = NULL)
{
if (!missing(coupledModelId) && !is.null(coupledModelId))
self$coupledModelId <- coupledModelId
else
self$coupledModelId <- NULL
if (!missing(components) && !is.null(components))
{
if (is.list(components))
{
for (model in components)
self$addComponent(model)
}
else
self$addComponent(components)
}
if (!missing(connections) && !is.null(connections))
{
if (is.list(connections))
{
for (con in connections)
self$addConnection(con)
}
else
self$addConnection(connections)
}
if (!missing(coupledModelDescription) &&
!is.null(coupledModelDescription))
self$coupledModelDescription <- coupledModelDescription
private$pcoupledEnv <- new.env(parent = baseenv())
},
print = function()
{
cat("\n<sdCoupledModel ID = ",
private$pcoupledModelId,
">\n",
sep = "")
cat(indent("$coupledModelDescription", indent = 4), sep = "\n")
cat(indent(private$pcoupledModelDescription, indent = 4), sep = "\n")
cat("\n")
cat(indent("$components", indent = 4), sep = "\n")
cat(indent(private$pcomponentsId, indent = 4), sep = "\n")
cat("\n")
cat(indent("$connections", indent = 4), sep = "\n")
cat(indent(capture.output(private$pconnections), indent = 4), sep = "\n")
cat("\n")
cat(indent("$defaultCoupledScenario", indent = 4), sep = "\n")
if (private$flagBuild)
cat(indent(capture.output(private$pdefaultCoupledScenario), indent = 4),
sep = "\n")
else
cat(indent("Not built.", indent = 4), sep = "\n")
cat("\n")
},
addComponent = function(...)
{
components <- list(...)
for (model in components)
{
if (is.character(model))
model <- sdLoadModel(file = model)
# only add sdModel's or sdStaticModel's
if (inherits(model, "sdModelClass"))
{
id <- model$modelId
# check if id already exist in components id
if (any(grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)) ||
id %in% private$pcomponentsId)
{
sdCoupledModelMsg$addComponent1(private$pcoupledModelId, id)
self$removeComponent(c(private$pcomponentsId[
grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)], id))
}
else if (id == private$pcoupledModelId)
{ # check if id is equal the coupled model id
sdCoupledModelMsg$addComponent0(private$pcoupledModelId, id)
next()
}
private$pcomponentsId <- c(private$pcomponentsId, id)
private$pcomponents[[id]] <- model$clone(deep = TRUE)
private$pcomponentsClass[[id]] <- class(model)[[1]]
private$pcomponentsEquations[[id]] <- model$DifferentialEquations
private$pcomponentsInitVars[[id]] <- model$InitVars
private$pcomponentsPostProcessVars[[id]] <- model$PostProcessVars
if (is.function(model$RootSpecification) ||
is.numeric(model$RootSpecification))
{
private$pcomponentsRootSpecification[[id]] <-
model$RootSpecification
private$pcomponentsEventFunction[[id]] <- model$EventFunction
}
else if (is.data.frame(model$RootSpecification))
{
rootdf <- model$RootSpecification
rootdf$var <- paste0(id, ".", as.character(rootdf$var))
# convert the columns for errors prevention
rootdf$method <- lapply(as.character(rootdf$method),
type.convert, as.is = TRUE)
rootdf$time <- as.numeric(rootdf$time)
rootdf$value <- as.numeric(rootdf$value)
private$pcomponentsRootSpecification[[id]] <- rootdf
private$pcomponentsEventFunction[[id]] <- rootdf
}
private$pcomponentsAux[[id]] <- model$aux
private$pcomponentsGlobal[[id]] <- model$GlobalFunctions
}
else if (inherits(model, "sdStaticModelClass"))
{
id <- model$staticModelId
if (any(grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)) ||
id %in% private$pcomponentsId)
{
sdCoupledModelMsg$addComponent1(private$pcoupledModelId, id)
self$removeComponent(c(private$pcomponentsId[
grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)], id))
}
else if (id == private$pcoupledModelId)
{
sdCoupledModelMsg$addComponent0(private$pcoupledModelId, id)
next()
}
private$pcomponentsId <- c(private$pcomponentsId, id)
private$pcomponents[[id]] <- model$clone(deep = TRUE)
private$pcomponentsClass[[id]] <- class(model)[[1]]
private$pcomponentsInitVars[[id]] <- model$InitVars
private$pcomponentsAux[[id]] <- model$equations
private$pcomponentsGlobal[[id]] <- model$GlobalFunctions
}
else if (inherits(model, "sdCoupledModelClass"))
{
id <- model$coupledModelId
if (any(grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)) ||
id %in% private$pcomponentsId)
{
sdCoupledModelMsg$addComponent1(private$pcoupledModelId, id)
self$removeComponent(c(private$pcomponentsId[
grepl(pattern = paste0("^",id,"."),
x = private$pcomponentsId, perl = TRUE)], id))
}
else if (id == private$pcoupledModelId)
{
sdCoupledModelMsg$addComponent0(private$pcoupledModelId, id)
next()
}
# add the components with the coupled model id as prefix
for (j in seq_along(model$components))
{
comp <- model$components[[j]]$clone(deep = TRUE)
if (!is.null(comp$modelId))
comp$modelId <- paste0(model$coupledModelId, ".", comp$modelId)
else if(!is.null(comp$staticModelId))
comp$staticModelId <- paste0(model$coupledModelId, ".",
comp$staticModelId)
self$addComponent(comp)
}
# add the connections
for (con in model$connections)
{
con[[2]] <- paste0(model$coupledModelId, ".", con[[2]])
con[[4]] <- paste0(model$coupledModelId, ".", con[[4]])
# private$pconnections[[con[[1]]]] <- con
self$addConnection(con)
}
}
else
sdCoupledModelMsg$addComponent2(private$pcoupledModelId,
typeof(model))
}
# merge the components auxs
private$pcomponentsAux <- lapply(unlist(private$pcomponentsAux,
recursive = FALSE),
as.expression)
private$flagBuild <- private$flagVerify <- FALSE
invisible()
},
removeComponent = function(...)
{
componentName <- gsub(" ", "", unlist(list(...)), fixed = TRUE)
# remove the models from all lists
private$pcomponentsId <- private$pcomponentsId[
!(private$pcomponentsId %in% componentName)]
private$pcomponents <- private$pcomponents[
!(names(private$pcomponents) %in% componentName)]
private$pcomponentsEquations <- private$pcomponentsEquations[
!(names(private$pcomponentsEquations) %in% componentName)]
private$pcomponentsInitVars <- private$pcomponentsInitVars[
!(names(private$pcomponentsInitVars) %in% componentName)]
private$pcomponentsPostProcessVars <- private$pcomponentsPostProcessVars[
!(names(private$pcomponentsPostProcessVars) %in% componentName)]
private$pcomponentsRootSpecification <-
private$pcomponentsRootSpecification[
!(names(private$pcomponentsRootSpecification) %in% componentName)]
private$pcomponentsEventFunction <- private$pcomponentsEventFunction[
!(names(private$pcomponentsEventFunction) %in% componentName)]
for (model in componentName)
private$pcomponentsAux <- private$pcomponentsAux[
!grepl(paste0("^", model, "\\."),
names(private$pcomponentsAux))]
private$pcomponentsGlobal <- private$pcomponentsGlobal[
!(names(private$pcomponentsGlobal) %in% componentName)]
# remove the connections involving the removed model
connections <- private$pconnections
if (!is.null(connections) && length(connections) > 0)
{
for (model in componentName)
{
remCon <- sapply(1:length(connections), function(i)
{
if (model %in% connections[[i]])
return(connections[[i]][[1]])
})
if (length(remCon) > 0)
self$removeConnection(remCon)
}
}
private$flagBuild <- private$flagVerify <- FALSE
},
addConnection = function(...)
{
connections <- list(...)
for (con in connections)
{
if (length(con) != 5)
sdCoupledModelMsg$addConnection1(private$pcoupledModelId, con)
else if (!grepl(pattern = "^(aux\\$|st\\$|eq\\$)", x = con[[5]],
perl = TRUE))
sdCoupledModelMsg$addConnection2(private$pcoupledModelId, con)
else
{
# add connection
if (con[[1]] %in% names(private$pconnections))
sdCoupledModelMsg$addConnection3(private$pcoupledModelId, con[[1]])
private$pconnections[[con[[1]]]] <- con
}
}
private$flagBuild <- private$flagVerify <- FALSE
},
removeConnection = function(...)
# ... = cons ids
{
connectionsId <- unlist(list(...))
auxComponents <- private$pcomponentsAux
# refactor the aux lists removing the connection dependency
for (conId in connectionsId)
{
con <- private$pconnections[[conId]]
if (!is.null(con))
{
id <- con[[1]]
m1 <- con[[2]]
in1 <- con[[3]]
m2 <- con[[4]]
out2 <- strsplit(con[[5]], "$", fixed = TRUE)[[1]]
# remove the aux connection from the equations list using regex
if (out2[[1]] %in% c("aux", "eq"))
{
auxComponents[grepl(paste0("^", m1, "\\."),
names(auxComponents))] <-
lapply(auxComponents[grepl(paste0("^", m1, "\\."),
names(auxComponents))], function(x)
{
gsub(pattern = paste0(out2[[1]], "\\$", m2, ".", out2[[2]]),
replacement = paste0("inp$", m1, ".", in1),
x = toString(as.expression(x)), perl = TRUE)
})
private$auxCon <- private$auxCon[names(private$auxCon)
!= paste0(m1, ".", in1)]
}
else
private$stCon <- private$stCon[names(private$stCon)
!= paste0(m1, ".", in1)]
}
}
# refactor the auxliary list without sorting
private$pcomponentsAux <- sdInitEquations(auxComponents, eqName = "NULL")
# remove the connections with id in the connecitonId vector
private$pconnections <- private$pconnections[!(names(private$pconnections)
%in% connectionsId)]
private$flagVerify <- private$flagBuild <- FALSE
},
verifyModel = function(scenario = NULL, verbose = FALSE,
timeSeriesDirectory = "")
{
if (!self$isBuilt)
sdCoupledModelMsg$verifyModel1(private$pcoupledModelId)
# simulate the coupled model first time step
# Get components functions
componentsEquations <- private$pcomponentsEquations
namesCompEqs <- names(private$pcomponentsEquations)
componentsInitVars <- private$pcomponentsInitVars
componentsRootSpecification <- private$pcomponentsRootSpecification
componentsEventFunction <- private$pcomponentsEventFunction
eq <- aux <- private$pcomponentsAux
componentsId <- private$pcomponentsId
iComps <- private$pindexComponents
# get the model scenario
defaultScenario <- self$defaultCoupledScenario$clone(deep = TRUE)
# overwrite default variables with the given scenario values
if (!is.null(scenario))
{
# convert list of scenarios to coupled scenario
if (is.list(scenario))
scenario <- sdBuildCoupledScenario(coupledScenarioId = "coupledScen",
scenarios = scenario)
else if (is.character(scenario))
scenario <- sdLoadScenario(file = scenario)
if (inherits(scenario, "sdScenarioClass"))
{
if (length(scenario$state) > 0)
defaultScenario$addState(scenario$state, verbose = verbose)
if (length(scenario$constant) > 0)
defaultScenario$addConstant(scenario$constant, verbose = verbose)
if (length(scenario$input) > 0)
defaultScenario$addInput(
scenario$input[!(names(scenario$input) %in% c("interpolation_",
"fun_"))],
interpolation = scenario$input[["interpolation_"]],
verbose = verbose)
if (length(scenario$parameter) > 0)
defaultScenario$addParameter(scenario$parameter, verbose = verbose)
if (length(scenario$switch) > 0)
defaultScenario$addSwitch(scenario$switch, verbose = verbose)
if (!is.null(scenario$times))
defaultScenario$times <- scenario$times
if (!is.null(scenario$method))
defaultScenario$method <- scenario$method
}
else
sdCoupledModelMsg$verifyModel2(private$pcoupledModelId,
typeof(scenario))
}
# get model variables
st <- defaultScenario$state
ct <- defaultScenario$constant
par <- defaultScenario$parameter
inp <- defaultScenario$input
sw <- defaultScenario$switch
times <- defaultScenario$times
# Run the model Init Vars
modelInit <- list()
for (modelId in names(componentsInitVars))
{
# run the init vars
if (!is.null(componentsInitVars[[modelId]]))
{
if (inherits(private$pcomponents[[modelId]], "sdModelClass"))
modelInitVars <- componentsInitVars[[modelId]](
st = st[iComps$st[[modelId]]],
ct = ct[iComps$ct[[modelId]]],
par = par[iComps$par[[modelId]]],
inp = inp[iComps$inp[[modelId]]],
sw = sw[iComps$sw[[modelId]]],
aux = aux[iComps$aux[[modelId]]])
else if (inherits(private$pcomponents[[modelId]],
"sdStaticModelClass"))
modelInitVars <- componentsInitVars[[modelId]](
ct = ct[iComps$ct[[modelId]]],
par = par[iComps$par[[modelId]]],
inp = inp[iComps$inp[[modelId]]],
sw = sw[iComps$sw[[modelId]]],
eq = aux[iComps$aux[[modelId]]])
#concatenate the initVars return
modelInit$st <- c(modelInit$st, modelInitVars$st)
modelInit$ct <- c(modelInit$ct, modelInitVars$ct)
modelInit$par <- c(modelInit$par, modelInitVars$par)
modelInit$inp <- c(modelInit$inp, modelInitVars$inp[
!(names(modelInitVars$inp) %in% c("interpolation_", "fun_"))])
modelInit$sw <- c(modelInit$sw, modelInitVars$sw)
}
}
if (length(modelInit) > 0)
{
st <- MergeLists(modelInit$st, st, "coupledState")
ct <- MergeLists(modelInit$ct, ct, "coupledConstant")
par <- MergeLists(modelInit$par, par, "coupledParameter")
inp <- MergeLists(modelInit$inp, inp, "coupledInput")
sw <- MergeLists(modelInit$sw, sw, "coupledSwitch")
}
# get the connections list
stCon <- private$stCon
auxCon <- private$auxCon
# get the aux connections index
conAux <- match(unlist(auxCon, use.names = FALSE), names(aux))
conAuxInps <- match(names(auxCon), names(inp))
# get the connected st index and the connected inp index
conSt <- match(unlist(stCon, use.names = FALSE), names(st))
conStInps <- match(names(stCon), names(inp))
if (!is.null(times) && length(unlist(times)) > 0)
{
t <- times[[1]]
}
else
{
sdCoupledModelMsg$verifyModel3(private$pcoupledModelId)
t <- 0
}
# evaluate time series varibles
# compute the time series in the inputs
for (var in names(inp$fun_))
{
if (!is.null(inp$fun_[[var]]))
inp[[var]] <- inp$fun_[[var]](t)
else
inp[[var]] <- NULL
}
# make the connections using the transformation vector
inp[conStInps] <- st[conSt]
auxenv <- new.env()
appendEnv(auxenv, private$pcoupledEnv)
# evaluate the auxiliary variables and update the aux list
for (auxVar in names(aux))
{
eq[[auxVar]] <- aux[[auxVar]] <- tryCatch(
{
eval(aux[[auxVar]],
envir = auxenv)
},
error = function(e)
{
sdCoupledModelMsg$verifyModel4(private$pcoupledModelId, auxVar, e)
invisible(numeric(0))
})
if (is.null(aux[[auxVar]]) || is.na(aux[[auxVar]]) ||
length(aux[[auxVar]]) == 0 || is.infinite(aux[[auxVar]]))
sdCoupledModelMsg$verifyModel5(private$pcoupledModelId, aux, auxVar)
}
# make the aux connection
inp[conAuxInps] <- aux[conAux]
if (length(private$pcomponentsEquations) > 0) # at least one atomic model components
{
# Create new environment to store variables that will be validated
# and set the coupled env as its parent environment
env <- new.env(parent = private$pcoupledEnv)
lsVars <- list()
# replace original function with test function
for (i in 1:length(componentsEquations))
{
mId <- namesCompEqs[[i]]
lsVars[[mId]] <- c()
bodyStr <- as.character(body(componentsEquations[[i]]))
bodyStr <- gsub("(?<!<)<-", "<<-", bodyStr, perl = TRUE)
bodyStr <- gsub("->(?!>)", "->>", bodyStr, perl = TRUE)
bodyStr <- paste(bodyStr[2:length(bodyStr)], collapse = "\n")
body(componentsEquations[[i]]) <-
parse(text = paste("{", bodyStr, "}", sep = "\n"))
lsVars[[mId]] <- c(lsVars[[mId]],
all.vars(body(componentsEquations[[i]])))
environment(componentsEquations[[i]]) <- env
}
# Create test variables in env
for (var in unlist(lsVars, use.names = FALSE))
assign(var, "\\0", envir = env)
# stores the model definitions result
dState <- c()
dAux <- list()
# run the components model definitions
dState <- vector("numeric", length = length(st))
names(dState) <- names(st)
for (i in seq_along(namesCompEqs))
{
# run the model definition
mDef <- tryCatch(
{
componentsEquations[[i]](t = t,
st = st[iComps$st[[namesCompEqs[[i]]]]],
ct = ct[iComps$ct[[namesCompEqs[[i]]]]],
par = par[iComps$par[[namesCompEqs[[i]]]]],
inp = inp[iComps$inp[[namesCompEqs[[i]]]]],
sw = sw[iComps$sw[[namesCompEqs[[i]]]]],
aux = aux[iComps$aux[[namesCompEqs[[i]]]]])
},
error = function(e)
{
sdCoupledModelMsg$verifyModel6(private$pcoupledModelId,
namesCompEqs[[i]], e)
return(invisible(NULL))
})
if (length(unlist(mDef[[1]])) == 0)
mDef[[1]] <- c()
# concatenate the states derivatives
dState[iComps$st[[namesCompEqs[[i]]]]] <- mDef[[1]]
# concatenate the global auxiliary values
mAux <- mDef[-1]
if (length(mAux) > 0)
# there is auxiliary values
{
names(mAux)[names(mAux) == ""] <- "noname"
names(mAux) <- paste0(namesCompEqs[[i]], ".", names(mAux))
dAux <- append(dAux, mAux)
}
}
res <- list(c(dState), dAux, aux)
# Display warnings if any variables during the Model Definition
# execution are NULL, numeric(0), Inf or NA
for (modelId in namesCompEqs)
{
for (x in lsVars[[modelId]])
{
var <- mget(x, envir = environment(componentsEquations[[modelId]]),
ifnotfound = "\\1", inherits = TRUE)[[1]]
if (is.function(var) || is.environment(var))
{
# do nothing
}
else if ( (is.list(var) && length(var) > 0) ||
( is.vector(var) && length(var) > 1 ))
{
xUnlist <- unlist(var, recursive = TRUE)
for (i in 1:length(xUnlist))
{
if (is.function(xUnlist[[i]]) || is.environment(xUnlist[[i]]) ||
is.language(xUnlist[[i]]))
next # do nothing
else if (is.null(xUnlist[[i]]))
sdCoupledModelMsg$verifyModel7(private$pcoupledModelId, modelId,
names(xUnlist)[[i]], x, "NULL")
else if (length(xUnlist[[i]]) == 0 && is.numeric(xUnlist[[i]]))
sdCoupledModelMsg$verifyModel7(private$pcoupledModelId, modelId,
names(xUnlist)[[i]], x,
"numeric(0)")
else if (is.na(xUnlist[[i]]))
sdCoupledModelMsg$verifyModel7(private$pcoupledModelId, modelId,
names(xUnlist)[[i]], x, "NA")
else if (is.infinite(xUnlist[[i]]))
sdCoupledModelMsg$verifyModel7(private$pcoupledModelId, modelId,
names(xUnlist)[[i]], x, "Inf")
}
}
else if (x %in% c('st', 'ct', 'par', 'inp', 'sw', 'aux'))
next # do nothing if an arg is empty
else if (is.null(unlist(var)))
sdCoupledModelMsg$verifyModel8(private$pcoupledModelId, modelId, x,
"NULL")
else if (length(var) == 0 && is.numeric(var))
sdCoupledModelMsg$verifyModel8(private$pcoupledModelId, modelId, x,
"numeric(0)")
else if (is.na(unlist(var)))
sdCoupledModelMsg$verifyModel8(private$pcoupledModelId, modelId, x,
"NA")
else if (is.infinite(unlist(var)))
sdCoupledModelMsg$verifyModel8(private$pcoupledModelId, modelId, x,
"Inf")
}
}
# Check the return of Model Definition contains invalid values
if (is.list(res))
{
dRes <- res[[1]]
if (!is.numeric(dRes))
sdCoupledModelMsg$verifyModel9(private$pcoupledModelId, typeof(dRes))
if (length(dRes) != length(st))
sdCoupledModelMsg$verifyModel10(private$pcoupledModelId, dRes,
length(st))
}
else
sdCoupledModelMsg$verifyModel11(private$pcoupledModelId, typeof(res))
}
if (verbose)
sdCoupledModelMsg$verifyModel12(private$pcoupledModelId)
private$flagVerify <- TRUE
},
buildCoupledModel = function(from = NULL,
to = NULL,
by = NULL,
method = c("lsoda", "lsode",
"lsodes", "lsodar",
"vode", "daspk",
"euler", "rk4", "ode23",
"ode45", "radau",
"bdf", "bdf_d", "adams",
"impAdams", "impAdams_d"),
timeSeriesDirectory = "")
{
if (length(private$pcomponentsId) == 0)
sdCoupledModelMsg$buildCoupledModel1(private$pcoupledModelId)
# build default scenario concatanating the components default scenario
componentsIds <- private$pcomponentsId
scenComponents <- list()
auxsNames <- list()
for (modelId in componentsIds)
{
if (!is.null(private$pcomponents[[modelId]]$defaultScenario))
scenComponents[[modelId]] <-
private$pcomponents[[modelId]]$defaultScenario$clone()
}
defaultCoupledScenarioVars <- sdBuildCoupledScenario(
coupledScenarioId = "Default",
scenarios = scenComponents,
method = method[[1]],
from = from,
to = to,
by = by,
varNames = TRUE,
timeSeriesDirectory = timeSeriesDirectory)
private$pdefaultCoupledScenario <-
defaultCoupledScenarioVars$coupledScenario
# build the components list of indexes
indexComponents <- list(st = list(), ct = list(), inp = list(),
par = list(), ts = list(), sw = list(),
aux = list())
namesSt <- names(defaultCoupledScenarioVars$coupledScenario$state)
namesCt <- names(defaultCoupledScenarioVars$coupledScenario$constant)
namesInp <- names(defaultCoupledScenarioVars$coupledScenario$input)
namesTs <- names(defaultCoupledScenarioVars$coupledScenario$input$fun_)
namesPar <- names(defaultCoupledScenarioVars$coupledScenario$parameter)
namesSw <- names(defaultCoupledScenarioVars$coupledScenario$switch)
coupledEnv <- private$pcoupledEnv
# clean coupledEnv
rm(list = ls(coupledEnv), envir = coupledEnv)
# get all the components variables names and substitute in the components
# functions for the coupled names (concatenated with the model ID)
# reset the enviromnet of all components functions with the coupled env
componentsVarNames <- defaultCoupledScenarioVars$varNames
for (modelId in componentsIds)
{
componentsVarNames[[modelId]] <- c(
componentsVarNames[[modelId]],
gsub(pattern = paste0("^",modelId,"\\."), replacement = "",
x = names(private$pcomponentsAux)[
grepl(paste0("^", modelId, "\\."),
names(private$pcomponentsAux))]),
names(private$pcomponentsGlobal[[modelId]]))
if (is.function(private$pcomponentsEquations[[modelId]]))
{
private$pcomponentsEquations[[modelId]] <-
replaceCoupledVarsNames(
func = private$pcomponentsEquations[[modelId]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsEquations[[modelId]]) <-
coupledEnv
}
if (is.function(private$pcomponentsInitVars[[modelId]]))
{
private$pcomponentsInitVars[[modelId]] <-
replaceCoupledVarsNames(
func = private$pcomponentsInitVars[[modelId]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsInitVars[[modelId]]) <-
coupledEnv
}
if (is.function(private$pcomponentsPostProcessVars[[modelId]]))
{
private$pcomponentsPostProcessVars[[modelId]] <-
replaceCoupledVarsNames(
func = private$pcomponentsPostProcessVars[[modelId]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsPostProcessVars[[modelId]]) <-
coupledEnv
}
if (is.function(private$pcomponentsRootSpecification[[modelId]]))
{
private$pcomponentsRootSpecification[[modelId]] <-
replaceCoupledVarsNames(
func = private$pcomponentsRootSpecification[[modelId]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsRootSpecification[[modelId]]) <-
coupledEnv
}
if (is.function(private$pcomponentsEventFunction[[modelId]]))
{
private$pcomponentsEventFunction[[modelId]] <-
replaceCoupledVarsNames(
func = private$pcomponentsEventFunction[[modelId]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsEventFunction[[modelId]]) <-
coupledEnv
}
private$pcomponentsAux[grepl(paste0("^", modelId, "\\."),
names(private$pcomponentsAux))] <-
replaceCoupledVarsNames(
listExp = private$pcomponentsAux[
grepl(paste0("^", modelId, "\\."),
names(private$pcomponentsAux))],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
if (length(private$pcomponentsGlobal[[modelId]]) > 0)
{
for (i in 1:length(private$pcomponentsGlobal[[modelId]]))
{
if (is.function(private$pcomponentsGlobal[[modelId]][[i]]))
{
private$pcomponentsGlobal[[modelId]][[i]] <-
replaceCoupledVarsNames(
func = private$pcomponentsGlobal[[modelId]][[i]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
environment(private$pcomponentsGlobal[[modelId]][[i]]) <-
coupledEnv
}
else if (is.expression(private$pcomponentsGlobal[[modelId]][[i]]))
private$pcomponentsGlobal[[modelId]][[i]] <-
replaceCoupledVarsNames(
listExp = private$pcomponentsGlobal[[modelId]][[i]],
componentsVarNames = componentsVarNames[[modelId]],
modelId = modelId)
}
}
# stop if components are empty
if (length(private$pcomponentsEquations) == 0 &&
length(private$pcomponentsAux) == 0)
sdCoupledModelMsg$buildCoupledModel0(private$pcoupledModelId)
# set each component variables indexes
indexComponents$st[[modelId]] <- match(
namesSt[grepl(paste0("^", modelId, "\\."), namesSt)], namesSt)
indexComponents$ct[[modelId]] <- match(
namesCt[grepl(paste0("^", modelId, "\\."), namesCt)], namesCt)
indexComponents$inp[[modelId]] <- match(
namesInp[grepl(paste0("^", modelId, "\\."), namesInp)], namesInp)
indexComponents$ts[[modelId]] <- match(
namesTs[grepl(paste0("^", modelId, "\\."), namesTs)], namesTs)
indexComponents$par[[modelId]] <- match(
namesPar[grepl(paste0("^", modelId, "\\."), namesPar)], namesPar)
indexComponents$sw[[modelId]] <- match(
namesSw[grepl(paste0("^", modelId, "\\."), namesSw)], namesSw)
}
# add the global function to the coupled env
appendEnv(coupledEnv,
as.list(unlist(private$pcomponentsGlobal,
recursive = FALSE)))
stComponents <- defaultCoupledScenarioVars$coupledScenario$state
inpComponents <- defaultCoupledScenarioVars$coupledScenario$input
auxComponents <- private$pcomponentsAux
# build the auxliary eq and state vars connections list
eqConnections <- list()
stConnections <- list()
# Build the transformation vectors from the given connections
connections <- private$pconnections
if (!is.null(connections) && length(connections) > 0)
{
for (con in connections)
{
id <- con[[1]]
m1 <- con[[2]]
in1 <- con[[3]]
m2 <- con[[4]]
out2 <- strsplit(con[[5]], "$", fixed = TRUE)[[1]]
if (!(m1 %in% componentsIds))
sdCoupledModelMsg$buildCoupledModel2(private$pcoupledModelId, m1,
id)
else if (!(m2 %in% componentsIds))
sdCoupledModelMsg$buildCoupledModel2(private$pcoupledModelId, m2,
id)
else if (!(paste0(m1, ".", in1) %in% names(inpComponents)))
sdCoupledModelMsg$buildCoupledModel3(private$pcoupledModelId,
"input", in1, m1, id)
else if (!(out2[[1]] %in% c("aux", "st", "eq")))
sdCoupledModelMsg$buildCoupledModel4(private$pcoupledModelId, m2,
id)
else if (out2[[1]] == "st" && !(paste0(m2, ".", out2[[2]]) %in%
names(stComponents)))
sdCoupledModelMsg$buildCoupledModel3(private$pcoupledModelId,
"state", out2[[2]], m2, id)
else if (out2[[1]] %in% c("aux", "eq") &&
!(paste0(m2, ".", out2[[2]]) %in% names(auxComponents)))
sdCoupledModelMsg$buildCoupledModel3(private$pcoupledModelId,
"equation", out2[[2]], m2, id)
else
{
# check connections unit
u1 <- defaultCoupledScenarioVars$coupledScenario$unit[[
paste0(m1, ".", in1)]]
u2 <- defaultCoupledScenarioVars$coupledScenario$unit[[
paste0(m2, ".", out2[[2]])]]
if (!is.null(u1) &&
!is.null(u2) && toupper(u1) != toupper(u2))
sdCoupledModelMsg$buildCoupledModel5(private$pcoupledModelId, in1,
u1, m1, out2, u2, m2, id)
else # make connection
{
if (out2[[1]] == "st")
{
if (!is.null(stConnections[[paste0(m1, ".", in1)]]))
sdCoupledModelMsg$buildCoupledModel6(private$pcoupledModelId,
m1, in1,
"state variable")
stConnections[paste0(m1, ".", in1)] <- paste0(m2,".", out2[[2]])
}
else if (out2[[1]] %in% c("aux","eq"))
{
if (!is.null(eqConnections[[paste0(m1, ".", in1)]]))
sdCoupledModelMsg$buildCoupledModel6(private$pcoupledModelId,
m1, in1,
"equation")
eqConnections[paste0(m1, ".", in1)] <- paste0(m2, ".",
out2[[2]])
# substitute the connected inp <- aux
auxComponents <- lapply(auxComponents, function(x)
{
gsub(pattern = paste0("inp\\$", m1, ".", in1,
"|inp\\[(\\'|\\\")", m1, ".", in1,
"\b(\\'|\\\")\\]|inp\\[\\[(\\'|\\\")",
m1, ".", in1, "\b(\\'|\\\")\\]\\]"),
replacement = paste0(out2[[1]], "$",
m2, ".", out2[[2]]),
x = toString(as.expression(x)), perl = TRUE)
})
}
}
}
}
}
# sort the auxliary equations and the connection matrix
auxComponents <- sdInitEquations(auxComponents)
private$pcomponentsAux <- auxComponents
# set index list for the equations
namesAux <- names(auxComponents)
for (modelId in componentsIds)
indexComponents$aux[[modelId]] <- match(
namesAux[grepl(paste0("^", modelId, "\\."), namesAux)], namesAux)
# set the index list
private$pindexComponents <- indexComponents
private$auxCon <- eqConnections
private$stCon <- stConnections
private$flagBuild <- TRUE
},
saveToXml = function(file = "sdCoupledModel.xml")
{
# save model to XML
doc = XML::newXMLDoc()
rootsdCoupledModel <- XML::newXMLNode("sdCoupledModel", doc = doc)
lCoupledModel <-
list(
coupledModelId = private$pcoupledModelId,
coupledModelDescription = private$pcoupledModelDescription,
connections = private$pconnections
)
ListToXML(rootsdCoupledModel, lCoupledModel)
# add the components
components <- XML::newXMLNode("components")
componentsXML <- lapply(private$pcomponents, function(x)
{
x$saveToXml()
})
XML::addChildren(components, kids = componentsXML)
XML::addChildren(rootsdCoupledModel, kids = list(components))
if (!missing(file))
cat(XML::saveXML(doc, encoding = "UTF-8",
prefix = xmlPrefix(),
indent = TRUE), file = file)
return(invisible(rootsdCoupledModel))
}
),
active = list(
coupledModelId = function(coupledModelId)
{
if (missing(coupledModelId))
return(private$pcoupledModelId)
else if (is.null(coupledModelId))
{
coupledModelId <- gsub("\\s", "", paste("coupledModel", Sys.Date()),
perl = TRUE)
sdCoupledModelMsg$coupledModelId1(coupledModelId)
}
else if (!is.character(coupledModelId))
{
coupledModelId <- gsub("\\s", "", paste("coupledModel", Sys.Date()),
perl = TRUE)
sdCoupledModelMsg$coupledModelId2(coupledModelId)
}
private[["pcoupledModelId"]] <- make.names(gsub("\\s", "",
coupledModelId,
perl = TRUE))
},
coupledModelDescription = function(coupledModelDescription)
{
if (missing(coupledModelDescription))
return(private$pcoupledModelDescription)
# set description
else if (is.character(coupledModelDescription))
private$pcoupledModelDescription <- coupledModelDescription
else
sdCoupledModelMsg$coupledModelDescription(private$pcoupledModelId)
},
defaultCoupledScenario = function()
{
if (private$flagBuild)
return(private$pdefaultCoupledScenario)
else
sdCoupledModelMsg$defaultCoupledScenario(private$pcoupledModelId)
},
components = function()
{
return(private$pcomponents)
},
componentsId = function()
{
return(private$pcomponentsId)
},
componentsClass = function()
{
return(private$pcomponentsClass)
},
componentsEquations = function()
{
return(private$pcomponentsEquations)
},
componentsInitVars = function()
{
return(private$pcomponentsInitVars)
},
componentsPostProcessVars = function()
{
return(private$pcomponentsPostProcessVars)
},
componentsRootSpecification = function()
{
return(private$pcomponentsRootSpecification)
},
componentsEventFunction = function()
{
return(private$pcomponentsEventFunction)
},
componentsAux = function()
{
return(private$pcomponentsAux)
},
componentsGlobal = function()
{
return(private$pcomponentsGlobal)
},
connections = function()
{
return(private$pconnections)
},
eqConnections = function()
{
# check if the matrix exist - flagBuild = TRUE
if (private$flagBuild)
return(private$auxCon)
else
sdCoupledModelMsg$connectionsList(private$pcoupledModelId,
"equations")
return(invisible(NULL))
},
stConnections = function()
{
# check if the matrix exist - flagBuild = TRUE
if (private$flagBuild)
return(private$stCon)
else
sdCoupledModelMsg$connectionsList(private$pcoupledModelId,
"state")
return(invisible(NULL))
},
indexComponents = function()
{
# check if the matrix exist - flagBuild = TRUE
if (private$flagBuild)
return(private$pindexComponents)
else
sdCoupledModelMsg$indexComponents(private$pcoupledModelId)
return(invisible(NULL))
},
isBuilt = function()
{
return(private$flagBuild)
},
modelEnv = function()
{
return(private$pcoupledEnv)
},
isVerified = function()
{
return(private$flagVerify)
}
),
private = list(
# Class Private Atributes
pcoupledModelId = NULL,
pcoupledModelDescription = NULL,
pdefaultCoupledScenario = NULL,
pindexComponents = NULL,
pconnections = list(),
auxCon = NULL,
stCon = NULL,
flagBuild = FALSE,
flagVerify = FALSE,
pcomponents = list(),
pcomponentsId = list(),
pcomponentsClass = list(),
pcomponentsEquations = list(),
pcomponentsInitVars = list(),
pcomponentsPostProcessVars = list(),
pcomponentsRootSpecification = list(),
pcomponentsEventFunction = list(),
pcomponentsAux = list(),
pcomponentsGlobal = list(),
pcoupledEnv = NULL
)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.