R/streambugs_aux.r

################################################################
#
# streambugs 1.0
# =================
#
# -------------------
# auxiliary functions
# -------------------
#
# creation:      08.09.2012
# modifications: 25.10.2017
#
################################################################

#' Construct the streambugs ODE state variable names
#'
#' Construct encoded labels of streambugs ODE state variable names from reach
#' names, habitat names, and "taxa" names for at least one of the POM, Algae, or
#' Invertebrates groups.
#'
#' @param Reaches reach names character vector; duplicates are dropped
#' @param Habitats habitats names character vector; duplicates are dropped
#' @param POM optional ("taxa") character vector of POM (particulate organic matter) group;
#'   duplicates are dropped
#'   (note: at least one "taxon" name out of all groups has to be given)
#' @param Algae optional taxa character vector of Algae group; duplicates are
#'  dropped (note: at least one taxon name out of all groups has to be given)
#' @param Invertebrates optional taxa character vector of Invertebrates group
#'  duplicates are dropped (note: at least one taxon name out of all groups has
#;  to be given)
#'
#' @return vector with state variable names in the form of
#'    \code{"Reach_Habitat_Taxon_Group"}
#'
#' @examples
#' Reaches           <- paste0("Reach",1:2)
#' Habitats          <- paste0("Hab",1:1)
#' y.names <- construct.statevariables(Reaches,Habitats,Invertebrates=c("Baetis","Ecdyonurus"))
#'
#' @export
construct.statevariables <- function(Reaches,Habitats,POM=NULL,Algae=NULL,Invertebrates=NULL)
{
  # validate that at least one taxon or POM was provided
  if ( is.null(c(POM, Algae, Invertebrates)) )
    stop("Missing at least one \"taxon\" from either group of POM, Algae, or Invertebrates")

  # encoding paste function helper
  paste.enc <- function(...) paste(..., sep="_")

  # rm duplicates
  if ( length(unique(Reaches)) != length(Reaches) ) {
    warning("Non unique reaches names - dropping duplicates")
    Reaches = unique(Reaches)
  }
  if ( length(unique(Habitats)) != length(Habitats) ) {
    warning("Non unique reaches names - dropping duplicates")
    Habitats = unique(Habitats)
  }

  # merge taxa groups
  POM.enc <- if (is.null(POM)) NULL else paste.enc(POM, "POM")
  Algae.enc <- if (is.null(Algae)) NULL else paste.enc(Algae, "Algae")
  Invertebrates.enc <- if (is.null(Invertebrates)) NULL else paste.enc(Invertebrates, "Invertebrates")
  taxa.enc <- c(POM.enc, Algae.enc, Invertebrates.enc)

  # rm duplicates
  if ( length(unique(taxa.enc)) != length(taxa.enc) ) {
    warning("Non unique taxa names within a group - dropping duplicates")
    taxa.enc = unique(taxa.enc)
  }

  # encode state variables: (reach_i, habitat_i) \times taxon_j
  n.Reaches = length(Reaches)
  n.Habitats = length(Habitats)
  n.Taxa = length(taxa.enc)
  y.names <- character(n.Reaches*n.Habitats*n.Taxa)
  for ( i in 1:n.Reaches )
    for ( j in 1:n.Habitats )
      y.names[(i-1)*n.Habitats*n.Taxa+ (j-1)*n.Taxa + 1:n.Taxa] <-
        paste.enc(Reaches[i], Habitats[j], taxa.enc)

  # sanity check: all values set
  if ( any(y.names == "") )
    stop("problem constructing state variables")

  return(y.names)
}

# extract reach names, habitat names, taxa names and optional
# group names from labels of a state vector:
# -----------------------------------------------------------

#' Decode the streambugs ODE state variable names
#'
#' Extract reach names, habitat names, taxa names and optional group names from
#' encoded labels of streambugs ODE state variable names.
#'
#' @param y.names vector with state variable names in the form of
#'    \code{"Reach_Habitat_Taxon"} or \code{"Reach_Habitat_Taxon_Group"}
#'
#' @return List with:\describe{
#'    \item{\code{$y.names}}{names of state variables (input argument)}
#'    \item{\code{$y.reaches}, \code{$y.reaches}, \code{$y.habitats},
#'      \code{$y.taxa}, and \code{$y.groups}:}{Names of, respectively, reaches,
#'      habitats, taxa, and of groups of each state variable}
#'    \item{\code{$reaches}, \code{$habitats}, \code{$taxa}, \code{$groups}:}{
#'      Unique names of, respectively, reaches, habitats, taxa, and of groups of
#'      state variables}
#'    \item{\code{$ind.fA}:}{Indices used for the areal fractions of each reach
#'      and habitat}
#'    }
#'
#' @examples
#' y.names <- c("Reach1_Hab1_Baetis_Invertebrates","Reach1_Hab1_Ecdyonurus_Invertebrates",
#'              "Reach2_Hab1_Baetis_Invertebrates", "Reach2_Hab1_Ecdyonurus_Invertebrates")
#' decode.statevarnames(y.names)
#'
#' @export
decode.statevarnames <- function(y.names)
{
  # split names of state variables:

  y.split    <- strsplit(y.names,split="_")
  y.reaches  <- rep(NA,length(y.names))
  y.habitats <- rep(NA,length(y.names))
  y.taxa     <- rep(NA,length(y.names))
  y.groups   <- rep(NA,length(y.names))
  for ( i in 1:length(y.names) )
  {
    y.reaches[i]  <- y.split[[i]][1]
    y.habitats[i] <- y.split[[i]][2]
    y.taxa[i]     <- y.split[[i]][3]
    y.groups[i]   <- y.split[[i]][4]
  }

  # determine reach, habitat, taxon and group names:

  reaches  <- unique(y.reaches[!is.na(y.reaches)])
  habitats <- unique(y.habitats[!is.na(y.habitats)])
  taxa     <- unique(y.taxa[!is.na(y.taxa)])
  groups   <- unique(y.groups[!is.na(y.groups)])

  # issue warnings:

  reaches.na <- which(is.na(y.reaches))
  if ( length(reaches.na)  > 0 )
  {
    warning("Undefined reach name(s) in component(s) of state vector: ",
            paste(reaches.na,collapse=","))
  }
  habitats.na <- which(is.na(y.habitats))
  if ( length(habitats.na)  > 0 )
  {
    warning("Undefined habitat name(s) in component(s) of state vector: ",
            paste(habitats.na,collapse=","))
  }
  taxa.na <- which(is.na(y.taxa))
  if ( length(taxa.na)  > 0 )
  {
    warning("Undefined taxon name(s) in component(s) of state vector: ",
            paste(taxa.na,collapse=","))
  }
  y.groups[is.na(y.groups)] <- ""

  # calculate indices for normalization of habitat area fractions:

  ind.fA <- list()
  for ( reach in reaches )
  {
    ind.reach  <- which(y.reaches == reach)
    habs.reach <- unique(y.habitats[ind.reach])
    ind.1sthab <- match(habs.reach,y.habitats[ind.reach])

    ind.fA[[reach]] <- list()
    ind.fA[[reach]][["ind.reach"]]  <- ind.reach
    ind.fA[[reach]][["ind.1sthab"]] <- ind.1sthab
  }

  # return splitted state variable names and reach, habibat, taxa and group names:

  return(list(y.names     = y.names,
              y.reaches   = y.reaches,
              y.habitats  = y.habitats,
              y.taxa      = y.taxa,
              y.groups    = y.groups,
              reaches     = reaches,
              habitats    = habitats,
              taxa        = taxa,
              groups      = groups,
              ind.fA      = ind.fA))
}


# check parameter vector and input list for a vector of potential names:
# ----------------------------------------------------------------------

# Check the input list and the parameter vector for occurrence of a set
# of input or parameter names. If the search is not successful use the
# default value if provided.
# Return a list with the index of the input and the value of the parameter.

get.inpind.parval <- function(names,par,inp=NA,default=NA)
{
  # define variable:

  ind <- NA
  val <- NA

  # search for inputs:

  if ( is.list(inp) )
  {
    for ( i in 1:length(names) )
    {
      ind <- match(names[i],names(inp))
      if ( !is.na(ind) ) break
    }
  }

  # search for parameter values:

  for ( i in 1:length(names) )
  {
    val <- par[names[i]]
    if ( !is.na(val) ) break
  }

  # if not available, set to default (if available):

  if ( is.na(val) ) val <- default

  # return value:

  return(list(inpind=ind,parval=val))
}


# get values of global parameters:
# --------------------------------

# The function searches for time-dependent inputs or constant values
# of global parameters with names specified in the character vector
# "par.names".
# It returns a list with a 2-column matrix of input and value indices
# in its columns for the parameter names that occur as input elements
# (values of those components will need updating as a function of time)
# and a vector of numerical values of the occrrence in the parameter
# vector or in the defaults vector.
# The function issues warnings for parameters that do neither occur
# in the input, nor in the parameter vector and do not have default
# values.

get.inpind.parval.global <- function(par.names,par,inp=NA,
                                     required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # get input indices:

  inds <- matrix(nrow=0,ncol=2)       # matrix with indices of input and
  # of values to be updated by inputs
  if ( is.list(inp) )
  {
    inpind <- match(par.names,names(inp))
    inpind <- inpind[!is.na(inpind)]
    if(length(inpind)>0)
    {
      i <- match(names(inp)[inpind],par.names)
      inds <- matrix(nrow=length(inpind),ncol=2,c(inpind,i),byrow=F)
    }
  }
  colnames(inds) <- c("inpind","i")

  # get values:

  vals <- par[par.names]
  names(vals) <- par.names

  # if not available set values to defaults (if available):

  ind.na <- which(is.na(vals))
  if ( length(ind.na) > 0 )
  {
    vals[ind.na] <- defaults[par.names[ind.na]]
  }

  # check existence of required parameters:

  for ( j in 1:length(required) )
  {
    if ( !is.na(required[j]) )
    {
      ind <- ( match(required[j],par.names) )
      if ( is.na(ind) )
      {
        warning("Global parameter \"",required[j],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      if ( is.na(vals[ind]) )
      {
        if ( nrow(inds) == 0 )
        {
          warning("Global parameter \"",required[j],
                  "\" specified to be required ",
                  "but not found in input or in parameter vector",
                  sep="")
        }
        else
        {
          if ( sum(inds[,2]==ind) == 0 )
          {
            warning("Global parameter \"",required[j],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector",
                    sep="")
          }
        }
      }
    }
  }

  # return list of matrix of input indices and vector of parameter values:

  return(list(inpinds=inds,parvals=vals))
}

get.inpind.parval.global.envcondtraits <- function(trait.names,par,inp=NA,
                                                   required=NA,defaults=NA)

  # for global parameters that are related to a trait, where we have an unknown number of classes
  # calls get.inpind.parval.global,
  # returns a named list with one entry for each trait and a vector with corresponding parameters

{

  # search par.names for trait.names

  res.trait <- list()

  for (i in 1:length(trait.names))
  {
    par.names <- character(0)

    ind1 <- grep(paste(trait.names[i],"_",sep=""),names(par))

    if(length(ind1)>0)
    {
      ind2 <- which(trait.names[i]==unlist(strsplit(names(par[ind1]),split="_")))

      par.names <- c(par.names,
                     paste(trait.names[i],unique(unlist(strsplit(names(par[ind1]),
                                                                 split="_"))[ind2+1]),
                           sep="_") )
    } else
    {
      warning("no envcond for traitclass ", trait.names[i], " found."  )
    }


    if (length(par.names)>0)
    {
      res <- get.inpind.parval.global(par.names=par.names,par=par,inp=inp,
                                      required=required,defaults=defaults)

      # sort vals and inds

      ind.order <- order(res$parvals)

      res$parvals <- res$parvals[ind.order]

      if(nrow(res$inpinds)>0)
      {
        for(j in 1:nrow(res$inpinds))
        {
          i.new <-  which(res$inpinds[j,2]==ind.order)
          res$inpinds[j,2] <- i.new
        }
      }

      # return list of matrices with input indices and values
      # of all parameters for all state variables:
    } else
    {
      inds <- matrix(nrow=0,ncol=2)
      colnames(inds) <- c("inpind","i")
      vals <- matrix(NA,nrow=0,ncol=0)
      res <- list(inpinds=inds,parvals=vals)
    }

    res.trait[[trait.names[i]]] <- res

  }

  return(res.trait)
}


# get values of reach-depedent environmental conditions:
# ------------------------------------------------------

# The function searches for time-dependent inputs or constant values
# of parameters with names given without reach identifiers in the
# character vector "par.names".
# Reach identifiers are added to the search string when searching for
# input and parameter values for state variables.
# The function returns a list with a 3-column matrix of input and matrix
# indices in its columns for the parameter names that occur as input
# elements (values of those components will need updating as a function
# of time)and a matrix of numerical values of the occrrence in the
# parameter vector or in the defaults vector. This matrix contains the
# values of all parameters (columns) for all state variables (rows).
# The function issues warnings for parameters that have been declared
# as required and do neither occur in the input, nor in the parameter
# vector and do not have default values.

get.inpind.parval.envcond.reach <- function(par.names,y.names,par,inp=NA,
                                            required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # search values:

  inds <- matrix(nrow=0,ncol=3)
  colnames(inds) <- c("inpind","i","j")
  vals <- matrix(NA,nrow=length(y.names$y.names),ncol=length(par.names))
  colnames(vals) <- par.names
  rownames(vals) <- y.names$y.names
  for ( j in 1:length(par.names) )
  {
    for ( i in 1:length(y.names$y.names) )
    {
      # define search order:

      names <- c(paste(y.names$y.reaches[i],   # Reach_Par
                       par.names[j],sep="_"),
                 par.names[j])                 # Par

      # get value:

      inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
      if ( !is.na(inppar$inpind) )
      {
        inds <- rbind(inds,c(inppar$inpind,i,j))
      }
      vals[i,j] <- inppar$parval
    }
  }

  # check existence of required parameters:

  for ( j.req in 1:length(required) )
  {
    if ( !is.na(required[j.req]) )
    {
      j <- ( match(required[j.req],par.names) )
      if ( is.na(j) )
      {
        warning("Parameter \"",required[j.req],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      for ( i in 1:length(y.names$y.names) )
      {
        if ( is.na(vals[i,j]) )
        {
          if ( nrow(inds) == 0 )
          {
            warning("Parameter \"",required[j.req],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for state variable ",i,
                    sep="")
          }
          else
          {
            if ( sum(inds[,2]==i & inds[,3]==j) == 0 )
            {
              warning("Parameter \"",required[j.req],
                      "\" specified to be required ",
                      "but not found in input or in parameter vector",
                      "for state variable ",i,
                      sep="")
            }
          }
        }
      }
    }
  }

  # return list of matrices with input indices and values
  # of all parameters for all state variables:

  return(list(inpinds=inds,parvals=vals))
}


# get values of habitat-(and reach-)depedent environmental conditions:
# --------------------------------------------------------------------

# The function searches for time-dependent inputs or constant values
# of parameters with names given without reach and habitat identifiers
# in the character vector "par.names".
# Reach and habitat identifiers are added to the search string when
# searching for input and parameter values for state variables.
# The function returns a list with a 3-column matrix of input and matrix
# indices in its columns for the parameter names that occur as input
# elements (values of those components will need updating as a function
# of time) and a matrix of numerical values of the occrrence in the
# parameter vector or in the defaults vector. This matrix contains the
# values of all parameters (columns) for all state variables (rows).
# The function issues warnings for parameters that have been declared
# as required and do neither occur in the input, nor in the parameter
# vector and do not have default values.

get.inpind.parval.envcond.habitat <- function(par.names,y.names,par,inp=NA,
                                              required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # search values:

  inds <- matrix(nrow=0,ncol=3)
  colnames(inds) <- c("inpind","i","j")
  vals <- matrix(NA,nrow=length(y.names$y.names),ncol=length(par.names))
  colnames(vals) <- par.names
  rownames(vals) <- y.names$y.names
  for ( j in 1:length(par.names) )
  {
    for ( i in 1:length(y.names$y.names) )
    {
      # define search order:

      names <- c(paste(y.names$y.reaches[i],   # Reach_Habitat_Par
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.habitats[i],  # Habitat_Par
                       par.names[j],sep="_"),
                 paste(y.names$y.reaches[i],   # Reach_Par
                       par.names[j],sep="_"),
                 par.names[j])                 # Par

      # get value:

      inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
      if ( !is.na(inppar$inpind) )
      {
        inds <- rbind(inds,c(inppar$inpind,i,j))
      }
      vals[i,j] <- inppar$parval
    }
  }

  # check existence of required parameters:

  for ( j.req in 1:length(required) )
  {
    if ( !is.na(required[j.req]) )
    {
      j <- ( match(required[j.req],par.names) )
      if ( is.na(j) )
      {
        warning("Parameter \"",required[j.req],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      for ( i in 1:length(y.names$y.names) )
      {
        if ( is.na(vals[i,j]) )
        {
          if ( nrow(inds) == 0 )
          {
            warning("Parameter \"",required[j.req],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for state variable ",i,
                    sep="")
          }
          else
          {
            if ( sum(inds[,2]==i & inds[,3]==j) == 0 )
            {
              warning("Parameter \"",required[j.req],
                      "\" specified to be required ",
                      "but not found in input or in parameter vector",
                      "for state variable ",i,
                      sep="")
            }
          }
        }
      }
    }
  }

  # normalize parameter fA:

  if ( !is.na(match("fA",par.names)) )
  {
    vals[,"fA"] <- calc.fA.norm(vals[,"fA"],y.names$ind.fA)
  }

  # return list of matrices with input indices and values
  # of all parameters for all state variables:

  return(list(inpinds=inds,parvals=vals))
}

# get values of habitat-(and reach-)depedent environmental conditions
# for a group of environmental conditions:
# ----------------------------------------

# Group.names can be specified to search for habitat specific environmental conditions
# that belong to the same group, e.g. areal fraction of different habitat types
# It automatically derives the parameter names that belong to the group and calls the
# function "get.inpind.parval.envcond.habitat"

get.inpind.parval.envcond.habitat.group <- function(group.names,y.names,par,inp=NA,
                                                    required=NA,defaults=NA)
{

  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # search par.names for trait.names

  res.group <- list()

  for (i in 1:length(group.names))
  {
    par.names <- character(0)

    ind1 <- grep(group.names[i],names(par))

    if(length(ind1)>0)
    {
      ind2 <- which(group.names[i]==unlist(strsplit(names(par[ind1]),split="_")))

      par.names <- c(par.names,
                     paste(group.names[i],unique(unlist(strsplit(names(par[ind1]),
                                                                 split="_"))[ind2+1]),
                           sep="_") )
    } else
    {
      warning("no parameters for group ", group.names[i], " found."  )
    }


    if (length(par.names)>0)
    {
      res <- get.inpind.parval.envcond.habitat(par.names=par.names,y.names,par=par,inp=inp,
                                               required=required,defaults=defaults)

      # return list of matrices with input indices and values
      # of all parameters for all state variables:

    } else
    {
      inds <- matrix(nrow=0,ncol=3)
      colnames(inds) <- c("inpind","i","j")
      vals <- matrix(NA,nrow=0,ncol=0)
      res <- list(inpinds=inds,parvals=vals)
    }

    res.group[[group.names[i]]] <- res

  }

  return(res.group)
}


# get initial conditions and inputs:
# ----------------------------------

# The function searches for time-dependent inputs or constant values
# of parameters with names given without taxon, reach and habitat
# identifiers in the character vector "par.names".
# Taxon, reach and habitat identifiers are added to the search string
# when searching for input and parameter values for state variables.

# The function returns a list with a 3-column matrix of input and matrix
# indices in its columns for the parameter names that occur as input
# elements (values of those components will need updating as a function
# of time) and a matrix of numerical values of the occurrence in the
# parameter vector or in the defaults vector. This matrix contains the
# values of all parameters (columns) for all state variables (rows).
# The function issues warnings for parameters that have been declared
# as required and do neither occur in the input, nor in the parameter
# vector and do not have default values.

get.inpind.parval.initcondinput <- function(par.names,y.names,par,inp=NA,
                                            required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # search values:

  inds <- matrix(nrow=0,ncol=3)
  colnames(inds) <- c("inpind","i","j")
  vals <- matrix(NA,nrow=length(y.names$y.names),ncol=length(par.names))
  colnames(vals) <- par.names
  rownames(vals) <- y.names$y.names
  for ( j in 1:length(par.names) )
  {
    for ( i in 1:length(y.names$y.names) )
    {
      # define search order:

      names <- c(paste(y.names$y.taxa[i],      # Taxon_Reach_Habitat_Par
                       y.names$y.reaches[i],
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.groups[i],    # Group_Reach_Habitat_Par
                       y.names$y.reaches[i],
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.taxa[i],      # Taxon_Habitat_Par
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.groups[i],    # Group_Habitat_Par
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.taxa[i],      # Taxon_Reach_Par
                       y.names$y.reaches[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.groups[i],    # Group_Reach_Par
                       y.names$y.reaches[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.taxa[i],      # Taxon_Par
                       par.names[j],sep="_"),
                 paste(y.names$y.groups[i],    # Group_Par
                       par.names[j],sep="_"),
                 paste(y.names$y.reaches[i],   # Reach_Habitat_Par
                       y.names$y.habitats[i],
                       par.names[j],sep="_"),
                 paste(y.names$y.habitats[i],  # Habitat_Par
                       par.names[j],sep="_"),
                 paste(y.names$y.reaches[i],   # Reach_Par
                       par.names[j],sep="_"),
                 par.names[j])                 # Par

      # get value:

      inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
      if ( !is.na(inppar$inpind) )
      {
        inds <- rbind(inds,c(inppar$inpind,i,j))
      }
      vals[i,j] <- inppar$parval
    }
  }

  # check existence of required parameters:

  for ( j.req in 1:length(required) )
  {
    if ( !is.na(required[j.req]) )
    {
      j <- ( match(required[j.req],par.names) )
      if ( is.na(j) )
      {
        warning("Parameter \"",required[j.req],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      for ( i in 1:length(y.names$y.names) )
      {
        if ( is.na(vals[i,j]) )
        {
          if ( nrow(inds) == 0 )
          {
            warning("Parameter \"",required[j.req],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for state variable ",i,
                    sep="")
          }
          else
          {
            if ( sum(inds[,2]==i & inds[,3]==j) == 0 )
            {
              warning("Parameter \"",required[j.req],
                      "\" specified to be required ",
                      "but not found in input or in parameter vector",
                      "for state variable ",i,
                      sep="")
            }
          }
        }
      }
    }
  }

  # return list of matrices with input indices and values
  # of all parameters for all state variables:

  return(list(inpinds=inds,parvals=vals))
}


# get taxa properties:
# --------------------

# The function searches for time-dependent inputs or constant values
# of parameters with names given without taxon and group identifiers
# in the character vector "par.names".
# Taxon and group identifiers are added to the search string
# when searching for input and parameter values for state variables.
# The function returns a list with a 2-column matrix of input and row
# indices in its columns for the parameter names that occur as input
# elements (values of those components will need updating as a function
# of time) and a matrix of numerical values of the occrrence in the
# parameter vector or in the defaults vector. This matrix contains the
# values of all parameters (columns) for all state variables (rows).
# The function issues warnings for parameters that have been declared
# as required and do neither occur in the input, nor in the parameter
# vector and do not have default values.

# The function accepts in the character vector "par.names" parameter names
# without taxon, reach and habitat specifiers and returns a list of two
# matrices with the indices of inputs and the values of all parameters,
# respectively (columns) for all state variables (rows).
# A warning is issued if a parameter that has been declared as required
# is not available for some state variables.

get.inpind.parval.taxaprop <- function(par.names,y.names,par,inp=NA,
                                       required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # search values:

  inds <- matrix(nrow=0,ncol=3)
  colnames(inds) <- c("inpind","i","j")
  vals <- matrix(NA,nrow=length(y.names$y.names),ncol=length(par.names))
  colnames(vals) <- par.names
  rownames(vals) <- y.names$y.names
  for ( j in 1:length(par.names) )
  {
    for ( i in 1:length(y.names$y.names) )
    {
      # define search order:

      names <- c(paste(y.names$y.taxa[i],      # Taxon_Par
                       par.names[j],sep="_"),
                 paste(y.names$y.groups[i],    # Group_Par
                       par.names[j],sep="_"),
                 par.names[j])                 # Par

      # get value:

      inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
      if ( !is.na(inppar$inpind) )
      {
        inds <- rbind(inds,c(inppar$inpind,i,j))
      }
      vals[i,j] <- inppar$parval
    }
  }

  # check existence of required parameters:

  for ( j.req in 1:length(required) )
  {
    if ( !is.na(required[j.req]) )
    {
      j <- ( match(required[j.req],par.names) )
      if ( is.na(j) )
      {
        warning("Parameter \"",required[j.req],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      for ( i in 1:length(y.names$y.names) )
      {
        if ( is.na(vals[i,j]) )
        {
          if ( nrow(inds) == 0 )
          {
            warning("Parameter \"",required[j.req],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for state variable ",i,
                    sep="")
          }
          else
          {
            if ( sum(inds[,2]==i & inds[,3]==j) == 0 )
            {
              warning("Parameter \"",required[j.req],
                      "\" specified to be required ",
                      "but not found in input or in parameter vector",
                      "for state variable ",i,
                      sep="")
            }
          }
        }
      }
    }
  }

  # return list of matrices with input indices and values
  # of all parameters for all state variables:

  return(list(inpinds=inds,parvals=vals))
}


# get taxa properties belonging to a specific trait:
# --------------------------------------------------

# The function accepts in the character vector "trait.names" trait names
# without taxon, reach and habitat specifiers and returns two matrices with
# the indices of inputs and the values of all parameters, respectively
# (columns) for all state variables (rows) for all traits.
# In contrast to get.parval.taxaprop we do not have to specify the name of
# the parameter but only the name of the trait the parameter belongs to.
# A warning is issued if a parameter that has been declared as required
# is not available for some state variables.

get.inpind.parval.taxaprop.traits <- function(trait.names,xval.pars=NA,y.names,par,inp=NA,
                                              required=NA,defaults=NA)
{
  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # function to split names and return the second half of it

  get2ndname <- function(fullnames)
  {
    shortnames <- rep(NA,length(fullnames))
    splitnames <- strsplit(fullnames,split="_")
    for(i in 1:length(fullnames))
    {
      shortnames[i] <- splitnames[[i]][2]
    }
    return(shortnames)
  }

  # search par.names for trait.names
  par.names <- NULL
  res.traits <- list()
  if(is.list(xval.pars))
  {
    if(length(trait.names)!=length(xval.pars)) warning("length of trait.names does not equal length of xval.pars")
  }

  for (i in 1:length(trait.names))
  {
    if(is.list(xval.pars))
    {
      if(substring(trait.names[i],1,5)!=substring(names(xval.pars[[i]]),1,5))
      {
        warning(paste("trait.names",trait.names[i],"is not fitting to xval.names",names(xval.pars[[i]])))
      }

      if (length(xval.pars[[i]][[1]]$parvals)>0)
      {
        if(is.matrix(xval.pars[[i]][[1]]$parvals))
        {
          x.val.names <- get2ndname(colnames(xval.pars[[i]][[1]]$parvals))
        }  else {
          x.val.names <- get2ndname(names(xval.pars[[i]][[1]]$parvals))
        }
        par.names <- paste(trait.names[i],x.val.names,sep="_")
      }
    } else {

      # this else is needed to extract parameters like feedingtypes in pre-prosessiong routines (e.g. for stoichiometry)

      ind1 <- grep(paste(trait.names[i],"_",sep=""),names(par))
      if(length(ind1)>0)
      {
        ind2 <- which(trait.names[i]==unlist(strsplit(names(par[ind1]),split="_")))

        par.names <- c(par.names,
                       paste(trait.names[i],unique(unlist(strsplit(names(par[ind1]),
                                                                   split="_"))[ind2+1]),
                             sep="_") )
      } else
      {
        warning("no parameters for trait ", trait.names[i], " found."  )
      }
    }
    if (length(par.names)>0)
    {
      res <- get.inpind.parval.taxaprop(par.names=par.names,y.names,par=par,inp=inp,
                                        required=required,defaults=defaults)

      # return list of matrices with input indices and values
      # of all parameters for all state variables:

    } else
    {
      inds <- matrix(nrow=0,ncol=3)
      colnames(inds) <- c("inpind","i","j")
      vals <- matrix(NA,nrow=0,ncol=0)
      res <- list(inpinds=inds,parvals=vals)
    }
    res.traits[[trait.names[i]]] <- res
  }

  return(res.traits)
}


# get stoichiometric coefficients for "taxon-based" processes:
# ------------------------------------------------------------

# Returns a list of stoichiometric vectors of processes of the type given
# by the character string "proc".
# The list elements are labelled according to the taxon labelling the process,
# the stoichiometric vectors contain stoichiometric coefficients of the taxa/
# substances indicated by their labels.

get.par.stoich.taxon <- function(proc,par)
{
  par.names <- names(par)
  par.splitted <- strsplit(par.names,split="_")
  stoich <- list()
  for ( i in 1:length(par) )
  {
    if ( par.splitted[[i]][1] == proc )
    {
      if ( length(par.splitted[[i]]) < 3 )
      {
        warning("Parameter name \"",par.names[i],"\" starts with \"",
                proc,"\""," but does not have three components")
      }
      else
      {
        if ( length(stoich[[par.splitted[[i]][2]]]) == 0 )
        {
          stoich[[par.splitted[[i]][2]]] <- numeric(0)
        }
        stoich[[par.splitted[[i]][2]]][par.splitted[[i]][3]] <- par[i]
      }
    }
  }
  return(stoich)
}


# get stoichiometric coefficients for food web processes:
# -------------------------------------------------------

# Returns a list of lists of stoichiometric vectors of food web processes
# of the type given by the character string "proc".
# The list elements are labelled according to the lead taxon labelling the
# process (typically the consumer), the list elements of the lower-level
# lists are labelled according to the second taxon characterizing the process
# (typically the food). The stoichiometric vectors contain stoichiometric
# coefficients of the taxa/substances indicated by their labels.

get.par.stoich.web <- function(proc,par)
{
  par.names <- names(par)
  par.splitted <- strsplit(par.names,split="_")
  stoich <- list()
  for ( i in 1:length(par) )
  {
    if ( par.splitted[[i]][1] == proc )
    {
      if ( length(par.splitted[[i]]) < 4 )
      {
        warning("Parameter name \"",par.names[i],"\" starts with \"",
                proc,"\""," but does not have four components")
      }
      else
      {
        if ( length(stoich[[par.splitted[[i]][2]]]) == 0 )
        {
          stoich[[par.splitted[[i]][2]]] <- list()
        }
        if ( length(stoich[[par.splitted[[i]][2]]][[par.splitted[[i]][3]]]) == 0 )
        {
          stoich[[par.splitted[[i]][2]]][[par.splitted[[i]][3]]] <- numeric(0)
        }
        stoich[[par.splitted[[i]][2]]][[par.splitted[[i]][3]]][par.splitted[[i]][4]] <- par[i]
      }
    }
  }
  return(stoich)
}


# get kinetic parameters of "taxon-based" process:
# ------------------------------------------------

# Returns a labelled vector of the parameters.

get.inpind.parval.proc1 <- function(par.names,taxon,group,reach,habitat,
                                    par,inp=NA,required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  inds <- matrix(nrow=0,ncol=2)
  colnames(inds) <- c("inpind","i")
  vals <- rep(NA,length(par.names))
  names(vals) <- par.names
  for ( j in 1:length(par.names) )
  {
    # define search order:

    names <- c(paste(taxon,                   # Taxon_Reach_Habitat_Par
                     reach,
                     habitat,
                     par.names[j],sep="_"),
               paste(group,                   # Group_Reach_Habitat_Par
                     reach,
                     habitat,
                     par.names[j],sep="_"),
               paste(taxon,                   # Taxon_Habitat_Par
                     habitat,
                     par.names[j],sep="_"),
               paste(group,                   # Group_Habitat_Par
                     habitat,
                     par.names[j],sep="_"),
               paste(taxon,                   # Taxon_Reach_Par
                     reach,
                     par.names[j],sep="_"),
               paste(group,                   # Group_Reach_Par
                     reach,
                     par.names[j],sep="_"),
               paste(taxon,                   # Taxon_Par
                     par.names[j],sep="_"),
               paste(group,                   # Group_Par
                     par.names[j],sep="_"),
               paste(reach,                   # Reach_Habitat_Par
                     habitat,
                     par.names[j],sep="_"),
               paste(habitat,                 # Habitat_Par
                     par.names[j],sep="_"),
               paste(reach,                   # Reach_Par
                     par.names[j],sep="_"),
               paste(par.names[j],sep="_"))   # Par

    # get value:

    inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
    if ( !is.na(inppar$inpind) )
    {
      inds <- rbind(inds,c(inppar$inpind,j))
    }
    vals[j] <- inppar$parval
  }

  # check existence of required parameters:

  for ( j in 1:length(required) )
  {
    if ( !is.na(required[j]) )
    {
      ind <- ( match(required[j],par.names) )
      if ( is.na(ind) )
      {
        warning("Parameter \"",required[j],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      if ( is.na(vals[ind]) )
      {
        if ( nrow(inds) == 0 )
        {
          warning("Parameter \"",required[j],
                  "\" specified to be required ",
                  "but not found in input or in parameter vector ",
                  "for taxon \"",taxon,"\" in habitat \"",habitat,
                  "\" of reach \"",reach,"\"",sep="")
        }
        else
        {
          if ( sum(inds[,2]==ind) == 0 )
          {
            warning("Parameter \"",required[j],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for taxon \"",taxon,"\" in habitat \"",habitat,
                    "\" of reach \"",reach,"\"",sep="")
          }
        }
      }
    }
  }

  return(list(inpinds=inds,parvals=vals))
}


# get kinetic parameters of food web process:
# -------------------------------------------

# Returns a list of matrixes of input indices and parameter vectors for each "food".

get.inpind.parval.proc2 <- function(par.names,taxon1,group1,taxa2,groups2,reach,habitat,
                                    par,inp=NA,required=NA,defaults=NA)
{
  par.names <- unique(par.names)

  # search values:

  par.kin <- list()
  for ( i in 1:length(taxa2) )
  {
    inds <- matrix(nrow=0,ncol=2)
    colnames(inds) <- c("inpind","i")
    vals <- rep(NA,length(par.names))
    names(vals) <- par.names
    for ( j in 1:length(par.names) )
    {
      # define search order:

      names <- c(paste(taxon1,                  # Taxon1_Taxon2_Reach_Habitat_Par
                       taxa2[i],
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Taxon2_Reach_Habitat_Par
                       taxa2[i],
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Group2_Reach_Habitat_Par
                       groups2[i],
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Group2_Reach_Habitat_Par
                       groups2[i],
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Taxon2_Habitat_Par
                       taxa2[i],
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Taxon2_Habitat_Par
                       taxa2[i],
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Group2_Habitat_Par
                       groups2[i],
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Group2_Habitat_Par
                       groups2[i],
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Taxon2_Reach_Par
                       taxa2[i],
                       reach,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Taxon2_Reach_Par
                       taxa2[i],
                       reach,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Group2_Reach_Par
                       groups2[i],
                       reach,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Group2_Reach_Par
                       groups2[i],
                       reach,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Taxon2_Par
                       taxa2[i],
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Taxon2_Par
                       taxa2[i],
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Group2_Par
                       groups2[i],
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Group2_Par
                       groups2[i],
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Reach_Habitat_Par
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Reach_Habitat_Par
                       reach,
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Habitat_Par
                       habitat,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Habitat_Par
                       habitat,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Reach_Par
                       reach,
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Reach_Par
                       reach,
                       par.names[j],sep="_"),
                 paste(taxon1,                  # Taxon1_Par
                       par.names[j],sep="_"),
                 paste(group1,                  # Group1_Par
                       par.names[j],sep="_"),
                 paste(reach,                   # Reach_Habitat_Par
                       habitat,
                       par.names[j],sep="_"),
                 paste(habitat,                 # Habitat_Par
                       par.names[j],sep="_"),
                 paste(reach,                   # Reach_Par
                       par.names[j],sep="_"),
                 paste(par.names[j],sep="_"))   # Par

      # get value:

      inppar <- get.inpind.parval(names,par,inp,default=defaults[par.names[j]])
      if ( !is.na(inppar$inpind) )
      {
        inds <- rbind(inds,c(inppar$inpind,j))
      }
      vals[j] <- inppar$parval
    }
    par.kin[[i]] <- list(inpinds=inds,parvals=vals)
  }
  names(par.kin) <- taxa2

  # check existence of required parameters:

  for ( j.req in 1:length(required) )
  {
    if ( !is.na(required[j.req]) )
    {
      j <- ( match(required[j.req],par.names) )
      if ( is.na(j) )
      {
        warning("Parameter \"",required[j.req],
                "\" specified to be required ",
                "but not listed to be searched for",
                sep="")
      }
      for ( i in 1:length(taxa2) )
      {
        if ( is.na(par.kin[[i]]$parvals[j]) )
        {
          if ( nrow(par.kin[[i]]$inpinds) == 0 )
          {
            warning("Parameter \"",required[j.req],
                    "\" specified to be required ",
                    "but not found in input or in parameter vector ",
                    "for taxon1 \"",taxon1,"\" and taxon2 \"",taxa2[i],
                    "\" in habitat \"",habitat,"\" of reach \"",reach,"\"",
                    sep="")
          }
          else
          {
            if ( sum(par.kin[[i]]$inpinds[,2]==j) == 0 )
            {
              warning("Parameter \"",required[j.req],
                      "\" specified to be required ",
                      "but not found in input or in parameter vector",
                      "for taxon1 \"",taxon1,"\" and taxon2 \"",taxa2[i],
                      "\" in habitat \"",habitat,"\" of reach \"",reach,"\"",
                      sep="")
            }
          }
        }
      }
    }
  }

  # return list of lists of matrices with input indices and
  # parameter vectors for all state variables:

  return(par.kin)
}


# function to get a structured representation of the system definition
# --------------------------------------------------------------------

#' Get system definition of the streambugs ODE model
#'
#' Get a structured representation of the streambugs ODE system definition.
#'
#' @inheritParams run.streambugs
#'
#' @return List with definition of the model including input state variables,
#'    parameters, and inputs, as well as the derived model structure including
#'    global parameters, environmental conditions of reaches and habitats,
#'    initial conditions, taxa properties, stoichiometric coefficients of all
#'    processes, and process definitions for each state variable.
#'
#' @examples
#' m <- streambugs.example.model.toy()
#' sys.def <- streambugs.get.sys.def(y.names=m$y.names, par=m$par, inp=m$inp)
#' # Get inital conditions for all state variables
#' sys.def$par.initcond$parvals
#' # Get stoichiometric coefficients of consumption within the food web
#' sys.def$par.stoich.web$Cons
#' # Get stoichiometric coefficients of death process for each taxon
#' # (transforming them into a dead organic matter "POM")
#' sys.def$par.stoich.taxon$Death
#'
#' @export
streambugs.get.sys.def <- function(y.names,par,inp=NA)
{
  # decode state variable names if the function was not already called
  # with the decoded names:

  if ( !is.list(y.names) ) y.names <- decode.statevarnames(y.names)

  # get global parameters:
  # ----------------------

  par.global <- get.inpind.parval.global(
    par.names = c("kBoltzmann",
                  "M0",
                  "ftempmax_intercept",
                  "ftempmax_curv",
                  "fcurrent_intercept",
                  "fcurrent_curv",
                  "fsapro_intercept",
                  "fsapro_curv",
                  "forgmicropoll_intercept",
                  "forgmicropoll_curv",
                  "fmicrohab_intercept",
                  "fmicrohab_curv"
    ),
    par       = par,
    inp       = inp,
    required  = c("kBoltzmann",
                  "M0",
                  "ftempmax_intercept",
                  "ftempmax_curv",
                  "fcurrent_intercept",
                  "fcurrent_curv",
                  "fsapro_intercept",
                  "fsapro_curv",
                  "forgmicropoll_intercept",
                  "forgmicropoll_curv",
                  "fmicrohab_intercept",
                  "fmicrohab_curv"

    ),
    defaults  = c(kBoltzmann              = 8.61734e-005,
                  M0                      = 1,
                  ftempmax_intercept      = 0,
                  ftempmax_curv           = 0,
                  fcurrent_intercept      = 0,
                  fcurrent_curv           = 0,
                  fsapro_intercept        = 0,
                  fsapro_curv             = 0,
                  forgmicropoll_intercept = 0,
                  forgmicropoll_curv      = 0,
                  fmicrohab_intercept     = 0,
                  fmicrohab_curv          = 0
    ))

  par.global.envtraits <- get.inpind.parval.global.envcondtraits(

    trait.names   = c("tempmaxKval",
                      "currentmsval",
                      "saprowqclassval",
                      "orgmicropollTUval"
    ),
    par=par,
    inp=inp,
    required=NA,
    defaults=NA)


  # get evironmental conditions:
  # ----------------------------

  par.envcond.reach   <- get.inpind.parval.envcond.reach(
    par.names = c("w","L"),
    y.names   = y.names,
    par       = par,
    inp       = inp,
    required  = c("w","L"))

  par.envcond.habitat <- get.inpind.parval.envcond.habitat(
    par.names = c("T",
                  "I0",
                  "fshade",
                  "CP",
                  "CN",
                  "DSusPOM",
                  "tau",
                  "taucrit",
                  "tempmaxK",
                  "currentms",
                  "orgmicropollTU",
                  "saprowqclass",
                  "fA",
                  "DFish"),
    y.names   = y.names,
    par       = par,
    inp       = inp,
    required  = c("T"),
    defaults  = c(fA=1,DFish=0))


  par.envcond.habitat.group <- get.inpind.parval.envcond.habitat.group(
    group.names = c("microhabaf"),
    y.names=y.names,
    par         = par,
    inp         = inp)

  # get initial conditions:
  # -----------------------

  par.initcond <- get.inpind.parval.initcondinput(
    par.names = "Dini",
    y.names   = y.names,
    par       = par,
    inp       = inp,
    defaults  = c(Dini=0))

  # get input:
  # ----------

  par.input <- get.inpind.parval.initcondinput(
    par.names = "Input",
    y.names   = y.names,
    par       = par,
    inp       = inp,
    defaults  = c(Input=0))

  # get taxa properties:
  # --------------------

  par.taxaprop.direct <- get.inpind.parval.taxaprop(
    par.names = c("M",
                  "Ea",
                  "b",
                  "i0",
                  "EC",
                  "fbasaltax"),
    y.names   = y.names,
    par       = par,
    inp       = inp,
    defaults  = c(b=0.75,
                  fbasaltax=1))


  # if trait.names are extended, the xval.pars have to be extended as well!

  trait.names = c("saprotolval",
                  "orgmicropolltolval",
                  "currenttolval",
                  "tempmaxtolval",
                  "microhabtolval")

  # objects which contain the names of the x-values for interpolation that are used to
  # derive the trait-tolerance values of invertebrates in the correct order

  xval.pars <- list( par.global.envtraits["saprowqclassval"],
                     par.global.envtraits["orgmicropollTUval"],
                     par.global.envtraits["currentmsval"],
                     par.global.envtraits["tempmaxKval"],
                     par.envcond.habitat.group["microhabaf"])

  par.taxaprop.traits <- get.inpind.parval.taxaprop.traits(
    trait.names = trait.names,
    xval.pars   = xval.pars,
    y.names     = y.names,
    par         = par,
    inp         = inp)


  # get process stoichiometries:
  # ----------------------------

  # taxon-based processes:

  kinparnames.taxon <- list(Miner    = c("kminer"),
                            Drift    = c("cdet"),
                            Resp     = c("fresp"),
                            Death    = c("fdeath"),
                            Prod     = c("fprod",
                                         "fgrotax",
                                         "hdens",
                                         "KI",
                                         "KP",
                                         "KN"))
  par.stoich.taxon <- list()
  for ( proc in names(kinparnames.taxon) )
  {
    par.stoich.taxon[[proc]] <- get.par.stoich.taxon(proc,par)
  }

  # food web processes:
  # -------------------

  kinparnames.web <- list(Cons     = list(taxon1 = c("fcons",
                                                     "fgrotax",
                                                     "hdens",
                                                     "Kfood",
                                                     "q"),
                                          taxa2  = c("Pref")),
                          FishPred = list(taxon1 = c("cfish",
                                                     "Kfood",
                                                     "q"),
                                          taxa2  = c("Pref")))
  par.stoich.web <- list()
  for ( proc in names(kinparnames.web) )
  {
    par.stoich.web[[proc]] <- get.par.stoich.web(proc,par)
  }

  # get kinetic parameters of processes:
  # ------------------------------------

  ind.1sthabinreach <- numeric(0)
  for ( i in 1:length(y.names$ind.fA) )
  {
    ind.1sthabinreach <-
      c(ind.1sthabinreach,
        y.names$ind.fA[[i]]$ind.reach[y.names$ind.fA[[1]]$ind.1sthab])
  }
  par.proc.taxon <- list()
  par.proc.web   <- list()
  for ( i in 1:length(y.names$y.names) )
  {
    reach   <- y.names$y.reaches[i]
    habitat <- y.names$y.habitats[i]
    taxon   <- y.names$y.taxa[i]
    group   <- y.names$y.groups[i]

    # taxon-based processes:

    par.proc.taxon[[i]] <- list()
    for ( proc in names(kinparnames.taxon) )
    {
      stoich <- par.stoich.taxon[[proc]][[taxon]]
      if ( length(stoich) > 0 )
      {
        par.proc.taxon[[i]][[proc]] <-
          get.inpind.parval.proc1(par.names = kinparnames.taxon[[proc]],
                                  taxon     = taxon,
                                  group     = group,
                                  reach     = reach,
                                  habitat   = habitat,
                                  par       = par,
                                  inp       = inp,
                                  required  = kinparnames.taxon[[proc]],
                                  defaults  = c(fgrotax=1))
        stoichmat <- matrix(NA,nrow=2,ncol=length(stoich))
        colnames(stoichmat) <- names(stoich)
        rownames(stoichmat) <- c("statevar","coeff")
        stoichmat[2,] <- stoich
        for ( j in 1:length(stoich) )
        {
          ind.statevar <- y.names$y.taxa == names(stoich)[j] &
            y.names$y.reaches == reach &
            y.names$y.habitats == habitat
          if ( sum(ind.statevar) > 1 )
          {
            warning("state vector contains multiple entries for",
                    "(Reach,Habitat,Taxon) = (",reach,",",habitat,
                    ",",names(stoich)[j],")",sep="")
          }
          else
          {
            if ( sum(ind.statevar) == 0 )
            {
              warning("state vector has no entry for",
                      "(Reach,Habitat,Taxon) = (",reach,",",habitat,
                      ",",names(stoich)[j],")",sep="")
            }
            else
            {
              stoichmat[1,j] <- which(ind.statevar)
            }
          }
        }
        par.proc.taxon[[i]][[proc]]$stoich <- stoichmat
      }
    }

    # food web processes:

    par.proc.web[[i]] <- list()
    for ( proc in names(kinparnames.web) )
    {
      taxa1 <- taxon
      if ( !is.na(match(i,ind.1sthabinreach)) ) taxa1 <- c(taxa1,"Fish")
      for ( taxon1 in taxa1 )
      {
        stoich <- par.stoich.web[[proc]][[taxon1]]
        if ( length(stoich) > 0 )
        {
          # get parameter values:

          taxa2     <- names(stoich)
          ind1      <- y.names$y.reaches == reach & y.names$y.habitats == habitat
          ind2      <- match(taxa2,y.names$y.taxa[ind1])
          groups2   <- y.names$y.groups[ind1][ind2]
          groups2   <- ifelse(is.na(groups2),"",groups2)

          proc1 <-
            get.inpind.parval.proc1(par.names = kinparnames.web[[proc]][["taxon1"]],
                                    taxon     = taxon1,
                                    group     = group,
                                    reach     = reach,
                                    habitat   = habitat,
                                    par       = par,
                                    inp       = inp,
                                    required  = kinparnames.web[[proc]][["taxon1"]],
                                    defaults  = c(fgrotax=1,q=1))
          proc2 <-
            get.inpind.parval.proc2(par.names = kinparnames.web[[proc]][["taxa2"]],
                                    taxon1    = taxon1,
                                    group1    = group,
                                    taxa2     = taxa2,
                                    groups2   = groups2,
                                    reach     = reach,
                                    habitat   = habitat,
                                    par       = par,
                                    inp       = inp,
                                    required  = kinparnames.web[[proc]][["taxa2"]],
                                    defaults  = c(Pref=1))
          par.proc.web[[i]][[proc]] <- list(inpinds = proc1$inpinds,
                                            parvals = proc1$parvals,
                                            taxa2   = proc2)
          for ( k in 1:length(taxa2) )
          {
            stoichmat <- matrix(NA,nrow=2,ncol=length(stoich[[k]]))
            colnames(stoichmat) <- names(stoich[[k]])
            rownames(stoichmat) <- c("statevar","coeff")
            stoichmat[2,] <- stoich[[k]]
            for ( j in 1:length(stoich[[k]]) )
            {
              ind.statevar <- y.names$y.taxa == names(stoich[[k]])[j] &
                y.names$y.reaches == reach &
                y.names$y.habitats == habitat
              if ( sum(ind.statevar) > 1 )
              {
                warning("state vector contains multiple entries for",
                        "(Reach,Habitat,Taxon) = (",reach,",",habitat,
                        ",",names(stoich)[j],")",sep="")
              }
              else
              {
                if ( sum(ind.statevar) == 0 )
                {
                  warning("state vector has no entry for",
                          "(Reach,Habitat,Taxon) = (",reach,",",habitat,
                          ",",names(stoich)[j],")",sep="")
                }
                else
                {
                  stoichmat[1,j] <- which(ind.statevar)
                }
              }
            }
            par.proc.web[[i]][[proc]][["taxa2"]][[taxa2[k]]]$stoich <- stoichmat
          }
        }
      }
    }
  }
  names(par.proc.taxon) <- y.names$y.names
  names(par.proc.web)   <- y.names$y.names

  # return list of system definition:
  # ---------------------------------

  return(list(y.names                   = y.names,
              par                       = par,
              inp                       = inp,
              par.global                = par.global,
              par.global.envtraits      = par.global.envtraits,
              par.envcond.reach         = par.envcond.reach,
              par.envcond.habitat       = par.envcond.habitat,
              par.envcond.habitat.group = par.envcond.habitat.group,
              par.initcond              = par.initcond,
              par.input                 = par.input,
              par.taxaprop.direct       = par.taxaprop.direct,
              par.taxaprop.traits       = par.taxaprop.traits,
              par.stoich.taxon          = par.stoich.taxon,
              par.stoich.web            = par.stoich.web,
              par.proc.taxon            = par.proc.taxon,
              par.proc.web              = par.proc.web))
}


# function to write the system definition to a file:
# --------------------------------------------------

#' Write system definition of the streambugs ODE model
#'
#' Write system definition of the streambugs ODE model into a human-readable
#' text file.
#'
#' @param sys.def system definition generated by the function
#'    \code{\link{streambugs.get.sys.def}}
#' @param file file name
#'
#' @examples
#' m <- streambugs.example.model.toy()
#' sys.def <- streambugs.get.sys.def(y.names=m$y.names, par=m$par, inp=m$inp)
#' file.name <- tempfile(m$name, fileext=".dat")
#' streambugs.write.sys.def(sys.def, file.name)
#' file.show(file.name, delete.file=TRUE)
#'
#' @export
streambugs.write.sys.def <- function(sys.def,file=NA)
{
  # local function to write typical data structures:
  # ------------------------------------------------

  write.pars <- function(pars,inp.names,main=NA)
  {
    # write header:

    if ( !is.na(main) ) cat(main,"\n",sep="")

    if ( is.vector(pars$parvals) )
    {
      # define local variables:

      vals <- pars$parvals
      inds <- pars$inpinds

      # if available. replace values by inputs:

      if ( nrow(inds) > 0 )
      {
        for ( i in 1:nrow(inds) )
        {
          vals[inds[i,2]] <- paste("input:",inp.names[inds[i,1]])
        }
      }

      # write values and inputs:

      if ( length(vals) > 0 )
      {
        for ( i in 1:length(vals) )
        {
          cat(names(vals)[i],"\t",vals[i],"\n",sep="")
        }
      }
    }
    else
    {
      if ( is.matrix(pars$parvals) )
      {
        # define local variables:

        vals <- pars$parvals
        inds <- pars$inpinds

        # if available, replace values by inputs:

        if ( nrow(inds) > 0 )
        {
          for ( i in 1:nrow(inds) )
          {
            vals[inds[i,2],inds[i,3]] <- paste("input:",inp.names[inds[i,1]])
          }
        }

        # write values and inputs:

        if ( ncol(vals) > 0 )
        {
          for ( j in 1:ncol(vals) ) cat("\t",colnames(vals)[j],sep="")
          cat("\n")
          for ( i in 1:nrow(vals) )
          {
            cat(rownames(vals)[i])
            for ( j in 1:ncol(vals) )
            {
              cat("\t",vals[i,j],sep="")
            }
            cat("\n")
          }
        }
      }
      else
      {
        cat("*** parameter format not yet implemented ***\n")
      }
    }
    cat("\n")
  }

  if ( !is.na(file) ) sink(file=file) # redirect output to file

  # header:
  # -------

  cat("=======================================================\n")
  cat("Streambugs Model Definition\n")
  cat("=======================================================\n")
  cat("\n")
  cat("-------------------------------------------------------\n")
  cat("Input to the Model\n")
  cat("-------------------------------------------------------\n")
  cat("\n")

  # write state variable names:
  # ---------------------------

  y.names <- sys.def$y.names
  cat("State Variables (",length(y.names$y.names),"):\n",sep="")
  for ( i in 1:length(y.names$y.names)) cat(y.names$y.names[i],"\n",sep="")
  cat("\n")

  # write inputs:
  # -------------

  inp <- sys.def$inp
  if ( is.list(inp) )
  {
    cat("Input Definitions (",length(inp),"):\n",sep="")
    for ( i in 1:length(inp) )
    {
      cat(names(inp)[i],"\t",nrow(inp[[i]])," data pairs\n",sep="")
    }
    cat("\n")
  }

  # write parameters:
  # -----------------

  par <- sys.def$par
  cat("Parameters (",length(par),"):\n",sep="")
  for ( i in 1:length(par) )
  {
    cat(names(par)[i],"\t",par[i],"\n",sep="")
  }
  cat("\n")

  cat("-------------------------------------------------------\n")
  cat("Derived Model Structure\n")
  cat("-------------------------------------------------------\n")
  cat("\n")

  # write global parameters:
  # ------------------------

  write.pars(sys.def$par.global,names(sys.def$inp),"Global Parameters:")

  # write global parameters related to environmental conditions and traits:
  # -----------------------------------------------------------------------

  for (i in 1:length(sys.def$par.global.envtraits))
  {
    write.pars(sys.def$par.global.envtraits[[i]],names(sys.def$inp),
               paste("Global Parameters related to environmental conditions and trait: ",names(sys.def$par.global.envtraits )[i]))
  }

  # write reach-dependent environmental conditions:
  # ----------------------------------------------

  write.pars(sys.def$par.envcond.reach,names(sys.def$inp),"Reach-Dependent Environmental Conditions:")

  # write habitat-dependent environmental conditions:
  # ------------------------------------------------

  write.pars(sys.def$par.envcond.habitat,names(sys.def$inp),"Habitat-Dependent Environmental Conditions:")

  # write habitat-dependent environmental conditions belonging to a group:
  # ----------------------------------------------------------------------

  for (i in 1:length(sys.def$par.envcond.habitat.group))
  {
    write.pars(sys.def$par.envcond.habitat.group[[i]],names(sys.def$inp),
               paste("Habitat-Dependent Environmental Conditions belonging to group:", names(sys.def$par.envcond.habitat.group)[i]))
  }

  # write initial conditons:
  # ------------------------

  write.pars(sys.def$par.initcond,names(sys.def$inp),"Initial Conditions:")

  # write input:
  # ------------

  write.pars(sys.def$par.input,names(sys.def$inp),"Input:")

  # write directly defined taxa properties:
  # ---------------------------------------

  write.pars(sys.def$par.taxaprop.direct,names(sys.def$inp),"Directly Defined Taxa Properties:")

  # write trait-derived taxa properties:
  # ------------------------------------

  for(i in 1: length(sys.def$par.taxaprop.trait))
  {
    write.pars(sys.def$par.taxaprop.trait[[i]],names(sys.def$inp),paste("Trait-Derived Taxa Properties:",names(sys.def$par.taxaprop.trait)[i]))

  }

  # write process stoichiometries of taxon-based processes:
  # -------------------------------------------------------

  par.stoich.taxon <- sys.def$par.stoich.taxon
  for( i in 1:length(par.stoich.taxon) )  # process i
  {
    if( length(par.stoich.taxon[[i]]) > 0 )
    {
      cat("Stoichiometry of Process ",names(par.stoich.taxon)[i],":\n",sep="")
      for ( j in 1:length(par.stoich.taxon[[i]]) )  # process i for organism j
      {
        for( k in 1:length(par.stoich.taxon[[i]][[j]]) )  # stoich. coeff. for subst k
        {
          cat("\t",names(par.stoich.taxon[[i]][[j]])[k],sep="")
        }
        cat("\n")
        cat("  ",names(par.stoich.taxon[[i]])[j],":",sep="")
        for( k in 1:length(par.stoich.taxon[[i]][[j]]) )
        {
          cat("\t",par.stoich.taxon[[i]][[j]][k],sep="")
        }
        cat("\n")
      }
      cat("\n")
    }
  }

  # write process stoichiometry of food web processes:
  # --------------------------------------------------

  par.stoich.web <- sys.def$par.stoich.web
  for( i in 1:length(par.stoich.web) )  # process i
  {
    if( length(par.stoich.web[[i]]) > 0 )
    {
      cat("Stoichiometry of Process ",names(par.stoich.web)[i],":\n",sep="")
      for ( j in 1:length(par.stoich.web[[i]]) )  # process i
      {
        for ( l in 1:length(par.stoich.web[[i]][[j]]) )  # process i for organisms j,l
        {
          for( k in 1:length(par.stoich.web[[i]][[j]][[l]]) )  # stoich. coeff. for subst k
          {
            cat("\t",names(par.stoich.web[[i]][[j]][[l]])[k],sep="")
          }
          cat("\n")
          cat("  ",names(par.stoich.web[[i]])[j],"-",names(par.stoich.web[[i]][[j]])[l],":",sep="")
          for( k in 1:length(par.stoich.web[[i]][[j]][[l]]) )
          {
            cat("\t",par.stoich.web[[i]][[j]][[l]][k],sep="")
          }
          cat("\n")
        }
      }
    }
    cat("\n")
  }

  # write process definitions by state variables:
  # ---------------------------------------------

  par.proc.taxon <- sys.def$par.proc.taxon
  par.proc.web   <- sys.def$par.proc.web
  cat("Process Definitions by State Variables:\n")
  for ( i in 1:length(y.names$y.names) )
  {
    var <- y.names$y.names[i]
    cat("\nState Variable ",i,":\t",var,"\n",sep="")
    if ( length(par.proc.taxon[[var]]) > 0 )
    {
      for ( j in 1:length(par.proc.taxon[[var]]) )
      {
        # define local variables:

        vals   <- par.proc.taxon[[var]][[j]]$parvals
        inds   <- par.proc.taxon[[var]][[j]]$inpinds
        stoich <- par.proc.taxon[[var]][[j]]$stoich

        # if available. replace values by inputs:

        if ( nrow(inds) > 0 )
        {
          for ( k in 1:nrow(inds) )
          {
            vals[inds[k,2]] <- paste("input:",names(inp)[inds[k,1]])
          }
        }

        # write values and inputs:

        cat("  ",names(par.proc.taxon[[var]])[j],":\n",sep="")
        cat("      Parameter:")
        for ( k in 1:length(vals) ) cat("\t",names(vals)[k],sep="")
        cat("\n")
        cat("      Value:")
        for ( k in 1:length(vals) ) cat("\t",vals[k],sep="")
        cat("\n")
        cat("      Taxon:")
        for ( k in 1:ncol(stoich) ) cat("\t",colnames(stoich)[k],sep="")
        cat("\n")
        cat("      StateVar:")
        for ( k in 1:ncol(stoich) ) cat("\t",stoich[1,k],sep="")
        cat("\n")
        cat("      StoichCoeff:")
        for ( k in 1:ncol(stoich) ) cat("\t",stoich[2,k],sep="")
        cat("\n")
      }
    }
    if ( length(par.proc.web[[var]]) > 0 )
    {
      for ( j in 1:length(par.proc.web[[var]]) )
      {
        # general process parameters:

        # define local variables:

        vals <- par.proc.web[[var]][[j]]$parvals
        inds <- par.proc.web[[var]][[j]]$inpinds

        # if available. replace values by inputs:

        if ( nrow(inds) > 0 )
        {
          for ( k in 1:nrow(inds) )
          {
            vals[inds[k,2]] <- paste("input:",names(inp)[inds[k,1]])
          }
        }

        # write values and inputs:

        cat("  ",names(par.proc.web[[var]])[j],":\n",sep="")
        cat("      Parameter:")
        for ( k in 1:length(vals) ) cat("\t",names(vals)[k],sep="")
        cat("\n")
        cat("      Value:")
        for ( k in 1:length(vals) ) cat("\t",vals[k],sep="")
        cat("\n")

        # food-specific process parameters:

        for ( k in 1:length(par.proc.web[[var]][[j]]$taxa2) )
        {
          # define local variables:

          vals   <- par.proc.web[[var]][[j]][["taxa2"]][[k]]$parvals
          inds   <- par.proc.web[[var]][[j]][["taxa2"]][[k]]$inpinds
          stoich <- par.proc.web[[var]][[j]][["taxa2"]][[k]]$stoich

          # if available. replace values by inputs:

          if ( nrow(inds) > 0 )
          {
            for ( k in 1:nrow(inds) )
            {
              vals[inds[k,2]] <- paste("input:",names(inp)[inds[k,1]])
            }
          }

          # write values and inputs:

          cat("    ",names(par.proc.web[[var]][[j]][["taxa2"]])[k],":\n",sep="")
          cat("      Parameter:")
          for ( k in 1:length(vals) ) cat("\t",names(vals)[k],sep="")
          cat("\n")
          cat("      Value:")
          for ( k in 1:length(vals) ) cat("\t",vals[k],sep="")
          cat("\n")
          cat("      Taxon:")
          for ( k in 1:ncol(stoich) ) cat("\t",colnames(stoich)[k],sep="")
          cat("\n")
          cat("      StateVar:")
          for ( k in 1:ncol(stoich) ) cat("\t",stoich[1,k],sep="")
          cat("\n")
          cat("      StoichCoeff:")
          for ( k in 1:ncol(stoich) ) cat("\t",stoich[2,k],sep="")
          cat("\n")
        }
      }
    }
  }

  if ( !is.na(file) ) sink() # terminate redirection of output
}


# function to update parameter values for a given time, t, by interpolation of inputs:
# ------------------------------------------------------------------------------------

# interpolate inputs:

interpolate.inputs <- function(inp,t)
{
  if ( !is.list(inp) ) return(NA)
  if ( length(inp) == 0 ) return(NA)
  inpvals <- rep(NA,length(inp))
  for ( i in 1:length(inp) )
  {
    inpvals[i] <- approx(x=inp[[i]][,1],y=inp[[i]][,2],xout=t,rule=2)$y
  }
  names(inpvals) <- names(inp)
  return(inpvals)
}

# update reach-dependent environmental conditions:

streambugs.update.envcond.reach <- function(par.envcond.reach,inpvals)
{
  if ( nrow(par.envcond.reach$inpinds) > 0 )
  {
    for ( i in 1:nrow(par.envcond.reach$inpinds) )
    {
      par.envcond.reach$parvals[par.envcond.reach$inpinds[i,2],
                                par.envcond.reach$inpinds[i,3]] <-
        inpvals[par.envcond.reach$inpinds[i,1]]
    }
  }
  return(par.envcond.reach$parvals)
}

# update habitat (and reach) - dependent environmental conditions:

streambugs.update.envcond.habitat <- function(par.envcond.habitat,inpvals,ind.fA)
{
  if ( nrow(par.envcond.habitat$inpinds) > 0 )
  {
    for ( i in 1:nrow(par.envcond.habitat$inpinds) )
    {
      par.envcond.habitat$parvals[par.envcond.habitat$inpinds[i,2],
                                  par.envcond.habitat$inpinds[i,3]] <-
        inpvals[par.envcond.habitat$inpinds[i,1]]
    }
  }
  # normalize parameter fA:
  if ( !is.na(match("fA",colnames(par.envcond.habitat$parvals))) )
  {
    par.envcond.habitat$parvals[,"fA"] <-
      calc.fA.norm(par.envcond.habitat$parvals[,"fA"],ind.fA)
  }
  return(par.envcond.habitat$parvals)
}

# update all:

streambugs.updatepar <- function(sys.def,t)
{
  if ( is.list(sys.def$inp) )
  {
    # interpolate inputs:
    # -------------------

    inpvals <- interpolate.inputs(sys.def$inp,t)

    # substitute interpolated inputs into parameter arrays where necessary:
    # ---------------------------------------------------------------------

    # update global parameters:

    if ( nrow(sys.def$par.global$inpinds) > 0 )
    {
      for ( i in 1:nrow(sys.def$par.global$inpinds) )
      {
        sys.def$par.global$parvals[sys.def$par.global$inpinds[i,2]] <-
          inpvals[sys.def$par.global$inpinds[i,1]]
      }
    }

    # update reach-dependent environmental conditions:

    sys.def$par.envcond.reach$parvals <-
      streambugs.update.envcond.reach(sys.def$par.envcond.reach,inpvals)

    # update habitat (and reach) - dependent environmental conditions:

    sys.def$par.envcond.habitat$parvals <-
      streambugs.update.envcond.habitat(sys.def$par.envcond.habitat,inpvals,sys.def$y.names$ind.fA)

    # update microhabitat - conditions:

    sys.def$par.envcond.habitat.group$microhabaf$parvals <-
      streambugs.update.envcond.habitat(sys.def$par.envcond.habitat.group$microhabaf,inpvals,sys.def$y.names$ind.fA)

    # update initial conditions:

    if ( nrow(sys.def$par.initcond$inpinds) > 0 )
    {
      for ( i in 1:nrow(sys.def$par.initcond$inpinds) )
      {
        sys.def$par.initcond$parvals[sys.def$par.initcond$inpinds[i,2],
                                     sys.def$par.initcond$inpinds[i,3]] <-
          inpvals[sys.def$par.initcond$inpinds[i,1]]
      }
    }

    # update inputs:

    if ( nrow(sys.def$par.input$inpinds) > 0 )
    {
      for ( i in 1:nrow(sys.def$par.input$inpinds) )
      {
        sys.def$par.input$parvals[sys.def$par.input$inpinds[i,2],
                                  sys.def$par.input$inpinds[i,3]] <-
          inpvals[sys.def$par.input$inpinds[i,1]]
      }
    }

    # update directly defined taxa properties:

    if ( nrow(sys.def$par.taxaprop.direct$inpinds) > 0 )
    {
      for ( i in 1:nrow(sys.def$par.taxaprop.direct$inpinds) )
      {
        sys.def$par.taxaprop.direct$parvals[sys.def$par.taxaprop.direct$inpinds[i,2],
                                            sys.def$par.taxaprop.direct$inpinds[i,3]] <-
          inpvals[sys.def$par.taxaprop.direct$inpinds[i,1]]
      }
    }

    # update trait-dependent taxa properties:
    for(j in 1:length(sys.def$par.taxaprop.traits))
    {
      if ( nrow(sys.def$par.taxaprop.traits[[j]]$inpinds) > 0 )
      {
        for ( i in 1:nrow(sys.def$par.taxaprop.traits[[j]]$inpinds) )
        {
          sys.def$par.taxaprop.traits[[j]]$parvals[sys.def$par.taxaprop.traits[[j]]$inpinds[i,2],  sys.def$par.taxaprop.traits[[j]]$inpinds[i,3]] <-
            inpvals[sys.def$par.taxaprop.traits[[j]]$inpinds[i,1]]
        }
      }
    }


    # par.stoich.taxon and par.stoich.web do not need updating
    # as they are are not allowed to be time-dependent

    # update kinetic parameters of taxon-based processes:

    for ( i in 1:length(sys.def$par.kin.taxon) )
    {
      if ( length(sys.def$par.kin.taxon[[i]]) > 0 )
      {
        for ( j in 1:length(sys.def$par.kin.taxon[[i]]) )
        {
          if ( nrow(sys.def$par.kin.taxon[[i]][[j]]$inpinds) > 0 )
          {
            for ( k in 1:nrow(sys.def$par.kin.taxon[[i]][[j]]$inpinds) )
            {
              sys.def$par.kin.taxon[[i]][[j]]$parvals[sys.def$par.kin.taxon$inpinds[k,2]] <-
                inpvals[sys.def$par.kin.taxon[[i]][[j]]$inpinds[k,1]]
            }
          }
        }
      }
    }

    # update kinetic parameters of food web processes:

    for ( i in 1:length(sys.def$par.kin.web) )
    {
      if ( length(sys.def$par.kin.web[[i]]) > 0 )
      {
        for ( j in 1:length(sys.def$par.kin.web[[i]]) )
        {
          if ( nrow(sys.def$par.kin.web[[i]][[j]]$inpinds) > 0 )
          {
            for ( k in 1:nrow(sys.def$par.kin.web[[i]][[j]]$inpinds) )
            {
              sys.def$par.kin.web[[i]][[j]]$parvals[sys.def$par.kin.web$inpinds[k,2]] <-
                inpvals[sys.def$par.kin.web[[i]][[j]]$inpinds[k,1]]
            }
            for ( k in 1:length(sys.def$par.kin.web[[i]][[j]]$taxa2) )
            {
              if ( nrow(sys.def$par.kin.web[[i]][[j]]$taxa2$inpinds) > 0 )
              {
                for ( l in 1:nrow(sys.def$par.kin.web[[i]][[j]]$taxa2$inpinds) )
                {
                  sys.def$par.kin.web[[i]][[j]]$taxa2[[k]]$parvals[sys.def$par.kin.web$taxa2[[k]]$inpinds[l,2],
                                                                   sys.def$par.kin.web$taxa2[[k]]$inpinds[l,3]] <-
                    inpvals[sys.def$par.kin.web[[i]][[j]]$taxa2[[k]]$inpinds[l,1]]
                }
              }
            }
          }
        }
      }
    }
  }

  return(sys.def)
}


# function to normalize habitat area fractions:
# ---------------------------------------------

calc.fA.norm <- function(fA,ind.fA)
{
  for ( i in 1:length(ind.fA) )
  {
    ind.reach  <- ind.fA[[i]][["ind.reach"]]
    ind.1sthab <- ind.fA[[i]][["ind.1sthab"]]
    fact <- 1/sum(fA[ind.reach][ind.1sthab])
    fA[ind.reach] <- fact*fA[ind.reach]
  }
  return(fA)
}

# function to calculate additional output (rates, limiting factors)
# -----------------------------------------------------------------

calculate.additional.output <- function(res,par,inp,file.add=NA,tout.add=NA)
{
  y.names <- colnames(res)[-1]
  tout <- res[,1]

  if( sum(!is.na(tout.add)) > 0 )
  {
    if( length(intersect(tout,tout.add)) < length(tout.add) )
    {
      stop("tout.add: ",tout.add," not part of tout")
    } else  tout <- tout.add
  }

  sys.def <- streambugs.get.sys.def(y.names=y.names,par=par,inp=inp)


  for (i in 1:length(tout))
  {
    sys.def.i <- streambugs.updatepar(sys.def,t=tout[i])

    res.add.i <- unlist(rhs.streambugs(t=tout[i],y=res[i,-1],parms=sys.def.i,
                                       add.out=TRUE))

    if(i==1) res.add <- res.add.i else  res.add <- rbind(res.add,res.add.i,deparse.level=0)
  }

  if(length(tout)==1) res.add <- as.matrix(t(res.add))

  res.add[,1] <- tout

  colnames(res.add)[1] <- colnames(res)[1]

  if(!is.na(file.add)) write.table(res.add,file.add,col.names=T,row.names=F,sep="\t")

  return(res.add)

}


# function to generate standard plot of streambugs results
# --------------------------------------------------------

# Note: S3-inheritance arguments consistency with generic plot function args
#       only to clean CRAN check warning; in future, if the simulation run
#       result `x` will have class "streambugs", then calling
#       `plot(res, par, inp, ...)` will invoke this method

#' Plot the results of streambugs ODE run
#'
#' Plot time series of all streambugs ODE state variables, for each reach,
#' habitat and group, resulting from the
#' \code{\link{run.streambugs}} function call.
#'
#' @param x matrix with results derived by
#'    \code{\link{run.streambugs}}
#' @param y same as \code{par} in \code{\link{run.streambugs}}
#' @param inp same as \code{inp} in \code{\link{run.streambugs}}
#' @param ... additional argument for the \code{\link[graphics]{plot}} function
#'    call
#'
#' @export
plot.streambugs <- function(x,y,inp=NA,...)
{
  res = x; par = y
  sys.def <- streambugs.get.sys.def(y.names=colnames(res)[-1],par=par,inp=inp)
  y.names <- sys.def$y.names

  par.envcond.w <- get.inpind.parval.envcond.reach(
    par.names = c("w"),
    y.names   = y.names,
    par       = par,
    inp       = inp,
    required  = c("w"))

  par.envcond.fA <- get.inpind.parval.envcond.habitat(
    par.names = c("fA"),
    y.names   = y.names,
    par       = par,
    inp       = inp,
    defaults  = c(fA=1))

  # evaluate time-dependent inputs and merge them with parameters
  # (overwriting parameters if one exists with the same name):
  # -------------------------------------------------------------

  for ( j in 1:nrow(res) )
  {
    # update (time-dependent) parameters:

    inpvals <- interpolate.inputs(inp,res[j,1])

    w  <- streambugs.update.envcond.reach(par.envcond.w,inpvals)[,"w"]
    fA <- streambugs.update.envcond.habitat(par.envcond.fA,inpvals,y.names$ind.fA)[,"fA"]

    # convert results in mass per unit length into mass per unit
    # surface area:

    res[j,-1] <- res[j,-1]/(w*fA)
  }

  # determin maxima of converted results:

  y.max <- rep(NA,length(y.names$groups))
  for ( i in 1:length(y.names$groups) )
  {
    y.max[i] <- 1.1*max(res[,1+which(y.names$y.groups==y.names$groups[i])])
  }
  names(y.max) <- y.names$groups

  # get time:

  t    <- res[,1]

  # plot output by groups:

  par.def <- par(no.readonly=TRUE)
  par(mfrow=c(length(y.names$habitats),length(y.names$groups)))
  for ( reach in y.names$reaches )
  {
    for ( habitat in y.names$habitats )
    {
      for ( group in y.names$groups )
      {
        plot(numeric(0),numeric(0),type="n",
             xlim=c(min(t),max(t)),ylim=c(0,y.max[group]),
             xlab="time [a]",ylab="biomass [gDM/m2]",
             main=paste(reach,habitat,group), ...)
        ind <- which(y.names$y.reaches  == reach &
                       y.names$y.habitats == habitat &
                       y.names$y.groups   == group )
        if ( length(ind) > 0 )
        {
          for ( k in 1:length(ind) )
          {
            lines(t,res[,1+ind[k]],lty=k)
          }
          legend(x="topleft",legend=y.names$y.taxa[ind],lty=1:length(ind))
        }
      }
    }
  }
  par(par.def)

  #write.table(res,paste("output/res_gDMperm2_",name.run,".dat",sep=""),
  #            sep="\t",row.names=F,col.names=T)

}

exp.transform <-   function(x,intercept=0,curv=0)
{
  #!if curv > 0 and intercept <1: function is curved to the right, if curv < 0 and intercept <1 function is curved to the left
  #!if curv > 0 and intercept >1: function is curved to the left,  if curv < 0 and intercept >1 function is curved to the right

  if(curv == 0)
  {
    y = intercept-(intercept-1)*x
  } else {
    y = intercept - (intercept -1) * (1 - exp(-curv * x)) / (1-exp(-curv))
  }
  return(y)
}
trybik/streambugs documentation built on May 7, 2019, 9:47 p.m.