R/communityModel.R

Defines functions randomEffectPriorsCateg fixedEffectPriorsCateg randomSpeciesStationEffectPriors randomEffectPriors independentEffectPriors fixedEffectPriors interceptPriors modifyInits get_cov_info summary.commOccu fit.commOccu communityModel

Documented in communityModel

#' Create a community (multi-species) occupancy model for JAGS or Nimble
#' 
#' @description 
#' Flexibly creates complete code and input data for community occupancy models for in JAGS amd Nimble, and automatically sets initial values and parameters to monitor. 
#' Supports fixed and random effects of covariates on detection and occupancy probabilities, using both continuous and categorical covariates (both site and site-occasion covariates). 
#' 
#' Optionally includes data augmentation (fully open community, or up to known maximum number of species, or no data augmentation). 
#' Allows combination of all these parameters for fast and flexible customization of community occupancy models.
#' 
#' Incidentally, the function can also be used to create  model code and input for single-species single-season occupancy models (it is the special case of the community model with only one species). 
#' Such a model will run slower than proper single-species model JAGS code due to the additional species loop, but it is possible.
#' 
#' The function returns several derived quantities, e.g. species richness, Bayesian p-values (overall and by species), Freeman-Tukey residuals for actual and simulated data (by station and total). If doing data augmentation, metacommunity size and number of unseen species are returned also. 
#'
#' @importFrom generics fit
#  @importFrom rjags jags.model coda.samples
# @importFrom coda c coda::mcmc.list mcmc
# @importFrom snowfall snowfall::sfInit snowfall::sfLibrary snowfall::sfExportAll snowfall::sfClusterSetupRNG snowfall::sfLapply snowfall::sfStop
# @importFrom nimble nimble::readBUGSmodel nimble::buildMCMC nimble::compileNimble nimble::runMCMC
#' @importFrom stats rnorm window 
#' @importFrom ggplot2 ggplot ggsave xlim geom_line facet_wrap geom_ribbon theme_bw ggtitle xlab ylab aes
#' @export
#'
#' @param data_list    list. Contains 3 slots: ylist, siteCovs, obsCovs. ylist is a list of detection histories (can be named), e.g. from \code{\link{detectionHistory}}. siteCovs is a data.frame with site covariates (optional). obsCovs is a list of site-occasion level covariates (e.g. site-occasion-specific effort, which is also returned by \code{\link{detectionHistory}}.
#' @param occuCovs  list. Up to 3 items named "fixed", "independent", and/or "ranef". Specifies fixed, independent or random effects of covariates on occupancy probability (continuous or categorical covariates). Independent effects are only supported for continuous covariates.
#' @param detCovs   list. Up to 3 items named "fixed", "independent", and/or "ranef". Specifies fixed, independent or random effects of covariates on detection probability (continuous or categorical covariates). Independent effects are only supported for continuous covariates.
#' @param detCovsObservation   list. Up to 2 items named "fixed" and/or "ranef". Specifies fixed or random effects of observation-level covariates on detection probability  (continuous or categorical covariates - categorical must be coded as character matrix)
#' @param speciesSiteRandomEffect list. Two items named "det" and "occu". If TRUE, adds a random effect of species and station. Only implemented for detection probability. 
#' @param intercepts     list. Two items named "det" and "occu" for detection and occupancy probability intercepts. Values can be "fixed" (= constant across species), "independent" (= independent estimates for each species), or "ranef" (= random effect of species on intercept).
#' @param effortCov     character. Name of list item in \code{data_list$obsCovs} which contains effort. This does not include effort as a covariate on detection probability, but only uses NA / not NA information to create binary effort and ensure detection probabilities p are 0 when there was no effort (p will be 0 whereever \code{effortCov} is NA).
#' @param richnessCategories  character. Name of categorical covariate in \code{data_list$siteCovs} for which to calculate separate richness estimates (optional). Can be useful to obtain separate richness estimates for different areas.
#' @param augmentation     If NULL, no data augmentation (only use species in \code{data_list$ylist}), otherwise named list or vector with total number of (potential) species. Names: "knownmax" or "full". Example: \code{augmentation = c(knownmax = 30)} or \code{augmentation = c(full = 30)}
#' @param modelFile   character. Text file name to save model to
#' @param nimble  logical. If TRUE, model code will be for Nimble (incompatible with JAGS). If FALSE, model code is for JAGS.
#'
#'
#' @details
#' For examples of implementation, see Vignette 5: Multi-species occupancy models.
#' 
#' Fixed effects of covariates are constant across species, whereas random effect covariates differ between species. Independent effect differ between species and are independent (there is no underlying hyperdistribution).
#' Fixed, independent and random effects are allowed for station-level detection and occupancy covariates (a.k.a. site covariates). Fixed and random effects are also allowed for station-occasion level covariates (a.k.a. observation covariates). 
#' Currently independent effects are only supported for continuous site covariates, not categorical site covariates or observation-level covariates.
#' 
#' By default, random effects will be by species. It is however possible to use categorical site covariates for grouping (continuous|categorical).
#' Furthermore, is is possible to use use nested random effects of species and another categorical site covariate (so that there is a random effect of species and an additional random effect of a categorical covariate within each species).
#' 
#' 
#' Derived quantities returned by the model are:
#'   
#'   \tabular{ll}{
#'     \code{Bpvalue} \tab Bayesian p-value (overall) \cr
#'     \code{Bpvalue_species} \tab Bayesian p-value (by species) \cr
#'     
#'     
#'     \code{Nspecies} \tab Species richness (only in JAGS model)\cr
#'     \code{Nspecies_Covariate} \tab Species richness by categorical covariate (when using \code{richnessCategories}, only in JAGS model) \cr
#'     
#'     
#'     \code{R2} \tab sum of Freeman-Tukey residuals of observed data within each species \cr
#'     \code{new.R2} \tab sum of Freeman-Tukey residuals of simulated data within each species \cr
#'     \code{R3} \tab Total sum of Freeman-Tukey residuals of observed data \cr 
#'     \code{new.R3} \tab Total sum of Freeman-Tukey residuals of simulated data \cr
#'     \emph{\code{Ntotal}} \tab Total metacommunity size (= observed species + n0) \cr
#'     \emph{\code{n0}} \tab Number of unseen species in metacommunity \cr
#'     \emph{\code{omega}} \tab Data augmentation parameter \cr
#'     \emph{\code{w}} \tab Metacommunity membership indicator for each species
#'   }
#' 
#' Quantities in \emph{italic} at the bottom are only returned in full data augmentation. \code{Nspecies} and \code{Nspecies_Covariate} are only returned in JAGS models (because Nimble models don't explicitly return latent occupancy status z).
#' 
#' 
#' 
#' @return
#' 
#' \code{commOccu} object. It is an S4 class containing all information required to run the models. See  \code{\link{commOccu-class}} for details.
#'
#' @encoding UTF-8 
#' @references 
#' Kéry, M., and J. A. Royle. "Applied hierarchical modelling in ecology - Modeling distribution, abundance and species richness using R and BUGS." Volume 1: Prelude and Static Models. Elsevier/Academic Press, 2016.
#' 
#' @section Parameter naming convention: 
#' The parameter names are assembled from building blocks. The nomenclature  is as follows:    
#'   \tabular{lll}{
#'     \bold{Name}           \tab \bold{Refers to}  \tab  \bold{Description}  \cr
#'     \bold{\code{alpha}}   \tab Submodel          \tab detection submodel  \cr
#'     \bold{\code{beta}}    \tab Submodel          \tab occupancy submode \cr
#'     \bold{\code{0}}       \tab Intercept         \tab denotes the intercepts (alpha0, beta0)  \cr
#'     \bold{\code{fixed}}   \tab Effect type       \tab fixed effects (constant across species)  \cr
#'     \bold{\code{indep}}   \tab Effect type       \tab independent effects (separate for each species)  \cr
#'     \bold{\code{ranef}}   \tab Effect type       \tab random effects (of species and/or other categorical covariates)  \cr
#'     \bold{\code{cont}}    \tab Covariate type    \tab continuous covariates \cr
#'     \bold{\code{categ}}   \tab Covariate type    \tab categorical covariates \cr
#'     \bold{\code{mean}}    \tab Hyperparameter    \tab mean of random effect \cr
#'     \bold{\code{sigma}}   \tab Hyperparameter    \tab standard deviation of random effect \cr
#'     \bold{\code{tau}}     \tab Hyperparameter    \tab precision of random effect (used internally, not returned)
#'   }
#' 
#' 
#' For example, a fixed intercept of occupancy (constant across species) is \bold{\code{beta0}}, and a fixed intercept of detection probability is \bold{\code{alpha0}}.
#' 
#' 
#' An occupancy probability intercept with a random effect of species is: 
#'   
#' \bold{\code{beta0.mean}} community mean of the occupancy probability intercept
#' 
#' \bold{\code{beta0.sigma}} standard deviation of the community occupancy probability intercept.
#' 
#' \bold{\code{beta0[1]}} occupancy probability intercept of species 1 (likewise for other species). 
#' 
#' 
#' For effects of site covariates, the pattern is:
#'   
#' \code{submodel.effectType.covariateType.CovariateName.hyperparameter}
#' 
#' For example:
#'   
#' \bold{\code{beta.ranef.cont.habitat.mean}} is the mean community effect of the continuous site covariate 'habitat' on occupancy probability.
#' 
#' \bold{\code{beta.ranef.cont.habitat[1]}} is the effect of continuous site covariate 'habitat' on occupancy probability of species 1.
#' 
#' Site-occasion covariates are denoted by ".obs" after the submodel, e.g.: 
#'   
#' \bold{\code{alpha.obs.fixed.cont.effort}} is the fixed effect of the continuous observation-level covariate 'effort' on detection probability
#' 
#' 
#' @author Juergen Niedballa
#'  
#' @examples
#' 
#' \dontrun{
# the example below fits community occupancy models to the sample data in camtrapR
# models are fit both in JAGS and Nimble
# The data set only contains 5 species and 3 stations, so the results will be nonsense. 
# It is only a technical demonstration with the camtrapR workflow
# for more complete examples, see vignette 5
#' 
#' data("camtraps")
#' 
#' # create camera operation matrix
#' camop_no_problem <- cameraOperation(CTtable      = camtraps,
#'                                     stationCol   = "Station",
#'                                     setupCol     = "Setup_date",
#'                                     retrievalCol = "Retrieval_date",
#'                                     hasProblems  = FALSE,
#'                                     dateFormat   = "dmy"
#' )
#' 
#' data("recordTableSample")
#' 
#' # make list of detection histories
#' DetHist_list <- lapply(unique(recordTableSample$Species), FUN = function(x) {
#'   detectionHistory(
#'     recordTable         = recordTableSample,
#'     camOp                = camop_no_problem,
#'     stationCol           = "Station",
#'     speciesCol           = "Species",
#'     recordDateTimeCol    = "DateTimeOriginal",
#'     species              = x,
#'     occasionLength       = 7,
#'     day1                 = "station",
#'     datesAsOccasionNames = FALSE,
#'     includeEffort        = TRUE,
#'     scaleEffort          = TRUE,
#'     timeZone             = "Asia/Kuala_Lumpur"
#'   )}
#' )
#' 
#' # assign species names to list items
#' names(DetHist_list) <- unique(recordTableSample$Species)
#' 
#' # extract detection histories (omit effort matrices)
#' ylist <- lapply(DetHist_list, FUN = function(x) x$detection_history)
#' 
#' # create some fake covariates for demonstration
#' sitecovs <- camtraps[, c(1:3)]
#' sitecovs$elevation <- c(300, 500, 600)   
#' 
#' # scale numeric covariates
#' sitecovs[, c(2:4)] <- scale(sitecovs[,-1])
#' 
#' 
#' # bundle input data for communityModel
#' data_list <- list(ylist = ylist,
#'                   siteCovs = sitecovs,
#'                   obsCovs = list(effort = DetHist_list[[1]]$effort))
#' 
#' 
#' # create community model for JAGS
#' modelfile1 <- tempfile(fileext = ".txt")
#' mod.jags <- communityModel(data_list,
#'                            occuCovs = list(fixed = "utm_y", ranef = "elevation"),
#'                            detCovsObservation = list(fixed = "effort"),
#'                            intercepts = list(det = "ranef", occu = "ranef"),
#'                            modelFile = modelfile1)
#' 
#' summary(mod.jags)
#' 
#' # fit in JAGS
#' fit.jags <- fit(mod.jags,
#'                 n.iter = 1000,
#'                 n.burnin = 500,
#'                 chains = 3)   
#' summary(fit.jags)
#' 
#' # response curves (= marginal effect plots)
#' plot_effects(mod.jags, 
#'              fit.jags, 
#'              submodel = "state")
#' plot_effects(mod.jags, 
#'              fit.jags, 
#'              submodel = "det")
#'              
#' # effect sizes plot
#' plot_coef(mod.jags, 
#'           fit.jags, 
#'           submodel = "state")
#' plot_coef(mod.jags, 
#'           fit.jags, 
#'           submodel = "det")              
#' 
#' # create community model for Nimble
#' modelfile2 <- tempfile(fileext = ".txt")
#' mod.nimble <- communityModel(data_list,
#'                              occuCovs = list(fixed = "utm_x", ranef = "utm_y"),
#'                              detCovsObservation = list(fixed = "effort"),
#'                              intercepts = list(det = "ranef", occu = "ranef"),
#'                              modelFile = modelfile2, 
#'                              nimble = TRUE)      # set nimble = TRUE
#' 
#' # load nimbleEcology package 
#' # currently necessary to do explicitly, to avoid additional package dependencies
#' require(nimbleEcology)
#' 
#' # fit uncompiled model in Nimble
#' fit.nimble.uncomp <- fit(mod.nimble, 
#'                          n.iter = 10, 
#'                          chains = 1)
#' 
#' # fit compiled model in Nimble
#' fit.nimble.comp <- fit(mod.nimble, 
#'                        n.iter = 5000, 
#'                        n.burnin = 2500,
#'                        chains = 3, 
#'                        compile = TRUE)
#' 
#' # parameter summary statistics
#' summary(fit.nimble.comp)
#' 
#' 
#' # response curves (= marginal effect plots)
#' plot_effects(mod.nimble, 
#'              fit.nimble.comp, 
#'              submodel = "state")
#' plot_effects(mod.nimble, 
#'              fit.nimble.comp, 
#'              submodel = "det")
#' 
#' # effect sizes plot
#' plot_coef(mod.nimble, 
#'           fit.nimble.comp, 
#'           submodel = "state")
#' plot_coef(mod.nimble, 
#'           fit.nimble.comp, 
#'           submodel = "det")   
#' 
#' # traceplots
#' plot(fit.nimble.comp)
#' 
#' 
#' }
#' 

communityModel <- function(data_list,
                           occuCovs = list(fixed = NULL, independent = NULL, ranef = NULL),
                           detCovs = list(fixed = NULL, ranef = NULL),
                           detCovsObservation = list(fixed = NULL, ranef = NULL),
                           speciesSiteRandomEffect = list(det = FALSE, occu = FALSE),
                           intercepts = list(det = "fixed", occu = "fixed"),
                           effortCov = "effort",
                           richnessCategories = NULL,
                           augmentation = NULL,
                           modelFile = NULL,
                           nimble = FALSE)
{   
  
  speciesIndex <- "i"      # k in AHMbook
  stationIndex <- "j"      # i in AHMbook
  occasionIndex <- "k"     # j in AHMbook
  
  # this function uses [species, station, occasion] indexing. 
  # AHM book uses: [station, occasion, species]
  
  # occupancy parameters = beta 
  # detection parameters = alpha
  # ...0 -> intercept, ...1-n -> covariates
  
  # define prior distributions to be used throughout (maybe make it an argument later)
  prior_list <- list(dnorm = "dnorm(0, 0.05)",
                     dgamma = "dgamma(0.1, 0.1)")
  
  # define inits
  inits_list <- list(
    inits_runif_mean_low = list("runif", list(1, -4.5, -1.5)),   # n_draws, min, max
    inits_runif_mean_0 = list("runif", list(1, -1, 1)), 
    inits_runif_tau  = list("runif", list(1, 0.01, 0.1)),
    inits_runif_omega = list("runif", list(1, 0, 1))
  )
  
  stopifnot(is.list(intercepts))
  stopifnot(length(intercepts) == 2)
  stopifnot(names(intercepts) %in% c("det", "occu"))
  stopifnot(intercepts$det %in% c("fixed", "ranef", "independent"))
  stopifnot(intercepts$occu %in% c("fixed", "ranef", "independent"))
  stopifnot(is.list(speciesSiteRandomEffect))
  stopifnot(is.logical(speciesSiteRandomEffect$det))
  
  if(exists("occu", speciesSiteRandomEffect)) {
    stopifnot(is.logical(speciesSiteRandomEffect$occu))
    if(speciesSiteRandomEffect$occu) stop("speciesSiteRandomEffect$occu must be FALSE")
  }
  
  
  if(!is.list(data_list)) stop("data_list must be a list")
  
  if(exists("ylist", where = data_list)) {
    if(!inherits(data_list$ylist, "list")) stop("data_list$ylist must be a list")
  } else {
    stop("data_list$ylist is missing")
  }
  
  if(exists("obsCovs", where = data_list)) {
    if(!inherits(data_list$obsCovs, "list")) stop("data_list$obsCovs must be a list")
  } else {
    stop("data_list$obsCovs is missing")
  }
  
  # data frame with information about intercepts
  covariate_info <- data.frame(param = c("intercept", "intercept"),
                           submodel = c("det", "state"),
                           covariate_type = NA,
                           covariate = NA,
                           data_type = NA,         # categ / numeric
                           is_quadratic = FALSE,
                           has_quadratic = FALSE,
                           ranef = c(ifelse(intercepts$det == "ranef", T, F), 
                                     ifelse(intercepts$occu == "ranef", T, F)), 
                           ranef_nested = FALSE,
                           ranef_cov = NA, 
                           independent = c(ifelse(intercepts$det == "independent", T, F), 
                                           ifelse(intercepts$occu == "independent", T, F)), 
                           coef = c("alpha0", "beta0"))
                           
  
  
  # keyword for nested random effects
  keyword_nested <- "+Species"
  
  # keyword for quadratic covariate effect
  keyword_quadratic <- "_squared"
  
  # parse covariates ####
  
  ## observation level covariates  ####
  if(!effortCov %in% names(data_list$obsCovs)) stop(paste0("effortCov '", effortCov, "' is not in the list of observation covariates (data_list$obsCovs)."))
  
  
  if(!is.null(unlist(detCovsObservation))) {
    covariate_info <- rbind(covariate_info, get_cov_info (cov = detCovsObservation, 
                                                          keyword_nested, 
                                                          keyword_quadratic, 
                                                          data_list, 
                                                          type = "obs", 
                                                          submodel = "det"))
    }
  
  
  obs_covariates <- data_list$obsCovs
  obs_covariates_numeric <- names(obs_covariates)[sapply(obs_covariates, is.numeric)]
  obs_covariates_categ   <- names(obs_covariates)[sapply(obs_covariates, is.character)]
  
  if(!all(names(detCovsObservation) %in% c("fixed", "ranef"))) stop("detCovsObservation can currentlly only be 'fixed' or 'ranef'")
  
  if(!is.null(detCovsObservation$fixed)) {
    for(i in detCovsObservation$fixed) {
      if(i == effortCov) next
      if(any(is.na(data_list$obsCovs[[i]]))) warning(paste("there are NAs in obsCovs:", i))
    }
  }
  
  if(!is.null(detCovsObservation$ranef)) {
    for(i in detCovsObservation$ranef) {
      if(i == effortCov) next
      if(any(is.na(data_list$obsCovs[[i]]))) warning(paste("there are NAs in obsCovs:", i))
    }
  }
  
  detCovsObservation_categ <- list(fixed = NULL, ranef = NULL)
  
  if(!is.null(detCovsObservation$fixed)) {
    if(any(!detCovsObservation$fixed %in% obs_covariates_numeric)) {
      if(!all(detCovsObservation$fixed [which(!detCovsObservation$fixed %in% obs_covariates_numeric)] %in% obs_covariates_categ)) stop(paste("Detection covariate ", paste(detCovsObservation$fixed [which(!detCovsObservation$fixed %in% obs_covariates_numeric)], collapse = ", "), 
                                                                                                                                                                     "is not a factor"))
      if(nimble == TRUE) warning("Currently categorical observation-level covariates are only supported in JAGS.\nIn Nimble they can only be used in uncompiled models (even that is experimental). Change model structure or set 'nimble = FALSE'", call. = FALSE)
      
      detCovsObservation_categ$fixed <- detCovsObservation$fixed[detCovsObservation$fixed %in% obs_covariates_categ]
      detCovsObservation$fixed       <- detCovsObservation$fixed[detCovsObservation$fixed %in% obs_covariates_numeric]
      if(length(detCovsObservation$fixed) == 0) detCovsObservation <- list(fixed = NULL, ranef = detCovsObservation$ranef)
    }
  }
  
  if(!is.null(detCovsObservation$ranef)) {
    if(any(!detCovsObservation$ranef %in% obs_covariates_numeric)) {
      if(!all(detCovsObservation$ranef [which(!detCovsObservation$ranef %in% obs_covariates_numeric)] %in% obs_covariates_categ)) stop(paste("Detection covariate ", paste(detCovsObservation$ranef [which(!detCovsObservation$ranef %in% obs_covariates_numeric)], collapse = ", "), 
                                                                                                                                                                     "is not a factor"))
      if(nimble == TRUE) warning("Currently categorical observation-level covariates are only supported in JAGS.\nIn Nimble they can only be used in uncompiled models (even that is experimental). Change model structure or set 'nimble = FALSE'", call. = FALSE)
      
      detCovsObservation_categ$ranef <- detCovsObservation$ranef[detCovsObservation$ranef %in% obs_covariates_categ]
      detCovsObservation$ranef       <- detCovsObservation$ranef[detCovsObservation$ranef %in% obs_covariates_numeric]
      if(length(detCovsObservation$ranef) == 0) detCovsObservation <- list(fixed = detCovsObservation$fixed, ranef = NULL)
    }
  }
  
  # convert character matrices to integer
  if(length(obs_covariates_categ) >= 1) {
    for(i in 1:length(obs_covariates_categ)){
      levels_tmp <- levels(as.factor(data_list$obsCovs[[obs_covariates_categ[i]]]))
      matrix_tmp <- match(data_list$obsCovs[[obs_covariates_categ[i]]], levels_tmp)
      data_list$obsCovs <- c(data_list$obsCovs, 
                             xxx = list(matrix(matrix_tmp, 
                                               nrow = dim(data_list$obsCovs[[obs_covariates_categ[i]]])[1],
                                               ncol = dim(data_list$obsCovs[[obs_covariates_categ[i]]])[2])))
      names(data_list$obsCovs)[length(data_list$obsCovs)] <- paste0(obs_covariates_categ[i], "_integer")
      attr(data_list$obsCovs[[length(data_list$obsCovs)]], "levels") <- levels_tmp
    }
  }
  
  ## site covariates ####
  
  if(exists("siteCovs", where = data_list)) {
    if(!inherits(data_list$siteCovs, "data.frame")) stop("data_list$siteCovs must be a data.frame")
  } else {
    warning("data_list$siteCovs does not exist. No site covariates available.", call. = FALSE)
  }
  
  
  # get covariate information
  if(!is.null(unlist(detCovs)))     covariate_info <- rbind(covariate_info, get_cov_info (detCovs, keyword_nested, keyword_quadratic, data_list, type = "site", submodel = "det"))
  if(!is.null(unlist(occuCovs)))    covariate_info <- rbind(covariate_info, get_cov_info (occuCovs, keyword_nested, keyword_quadratic, data_list, type = "site", submodel = "state"))
  
  
  covariates_original <- data_list$siteCovs

  # identify required covariates:
  site_covs_tmp <- unlist(c(occuCovs, detCovs, richnessCategories))
  if(length(site_covs_tmp) >= 1){
    covariatenames_needed <- unique(unlist(strsplit(site_covs_tmp, split = "|", fixed = T)))
  } else { 
    covariatenames_needed <- NULL
  }
  
  
  # remove "+Species" which indicates nested random effects
  covariatenames_needed <- gsub(keyword_nested, "", covariatenames_needed, fixed = T)
  
  # subset covariates
  covariates <- covariates_original [, colnames(covariates_original) %in% covariatenames_needed, drop = FALSE]
  
  # make a note of which covariates are in model (for summary method)
  attr(data_list$siteCovs, "in_model") <- colnames(covariates_original) %in% colnames(covariates)
  
  
  
  # identify numeric and categorical covariates
  if(ncol(covariates) != 0) {
    covariates_numeric <- names(covariates)[sapply(covariates, is.numeric)]
    covariates_categ   <- names(covariates)[sapply(covariates, is.factor)]
    covariates_char    <- names(covariates)[sapply(covariates, is.character)]
  } else {
    covariates_numeric <- NULL
    covariates_categ   <- NULL
    covariates_char    <- NULL
  }
  
  # throw error for character covariates
  
  if(length(covariates_char) >= 1) stop(paste("Covariate", covariates_char, "is character. Please convert to factor."), call. = FALSE)
    
  # paste cov1|cov2 (to check for random effects other than species, and nested random effects)
  covariates_numeric_categ <- unlist(sapply(covariates_numeric, paste, covariates_categ, sep = "|", simplify = F), use.names = F)
  covariates_numeric_categ <- c(covariates_numeric_categ, paste0(covariates_numeric_categ, keyword_nested))
  covariates_categ_categ   <- unlist(sapply(covariates_categ, paste, covariates_categ, sep = "|", simplify = F), use.names = F)
  

  if(!is.null(richnessCategories)) {
    if(length(richnessCategories) > 1) stop("richnessCategories can only have length 1, or be NULL")
    if(!richnessCategories %in% covariates_categ) stop(paste("richnessCategories", richnessCategories, "is not a categorical covariate in the covariate data frame"))
    if(nimble) warning("richnessCategories will be ignored because nimble = TRUE")
    occuIntercept_categ <- list(fixed = richnessCategories)
  } else {
    occuIntercept_categ <- list(fixed = NULL)
  }
  

  
  occuCovs_categ <- list(fixed = NULL, ranef = NULL)
  detCovs_categ  <- list(fixed = NULL, ranef = NULL)
  
  ## input checks on covariates  ####
  
  if(!is.null(occuCovs$fixed)) {
    if(any(!occuCovs$fixed %in% colnames(covariates))) {
      stop(paste(occuCovs$fixed [!occuCovs$fixed %in% colnames(covariates)], collapse = ", "), " is not in covariates")
    }
    if(any(!occuCovs$fixed %in% covariates_numeric)) {
      if(!all(occuCovs$fixed [which(!occuCovs$fixed %in% covariates_numeric)] %in% covariates_categ)) stop(paste("Occupancy covariate ", paste(occuCovs$fixed [which(!occuCovs$fixed %in% covariates_numeric)], collapse = ", "), 
                                                                                                                                         "is not a factor"))
      occuCovs_categ$fixed <- occuCovs$fixed[occuCovs$fixed %in% covariates_categ]
      occuCovs$fixed       <- occuCovs$fixed[occuCovs$fixed %in% covariates_numeric]
      if(length(occuCovs$fixed) == 0) occuCovs <- modifyList(occuCovs, list(fixed = NULL))
    }
  }
  
  if(!is.null(occuCovs$independent)) {
    if(any(!occuCovs$independent %in% colnames(covariates))) {
      stop(paste(occuCovs$independent [!occuCovs$independent %in% colnames(covariates)], collapse = ", "), " is not in covariates")
    }
    if(any(!occuCovs$independent %in% covariates_numeric)) {
      if(!all(occuCovs$independent [which(!occuCovs$independent %in% covariates_numeric)] %in% covariates_categ)) stop(paste("Occupancy covariate ", paste(occuCovs$independent [which(!occuCovs$independent %in% covariates_numeric)], collapse = ", "), 
                                                                                                                 "is not a factor"))
      
      # prohibit independent effects of categorical covariates
      if(any(occuCovs$independent %in% covariates_categ)) stop("independent effects of categorical site covariates are currently not supported")
      
      # occuCovs_categ$independent <- occuCovs$independent[occuCovs$independent %in% covariates_categ]
      # occuCovs$independent       <- occuCovs$independent[occuCovs$independent %in% covariates_numeric]
      # if(length(occuCovs$independent) == 0) occuCovs <- modifyList(occuCovs, list(independent = NULL))
    }
  }
  
  
  if(!is.null(occuCovs$ranef)) {
    if(any(occuCovs$ranef %in% covariates_categ_categ)) stop("random effects of categorical site covariates on other categorical site covariates are not supported yet (except for random effect of species)")
    
    if(any(!occuCovs$ranef %in% c(covariates_numeric, covariates_categ, covariates_numeric_categ))) {
      stop(paste(occuCovs$ranef [!occuCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ)], collapse = ", "), " is not in covariates")
    }
    if(any(!occuCovs$ranef %in% covariates_numeric)) {  
      if(!all(occuCovs$ranef [which(!occuCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ))] %in% covariates_categ)) stop(paste("Occupancy covariate ", paste(occuCovs$ranef [which(!occuCovs$ranef %in% covariates_numeric)], collapse = ", "), 
                                                                                                                                                                      "is not a factor"))
      occuCovs_categ$ranef <- c(occuCovs_categ$ranef, 
                                occuCovs$ranef[occuCovs$ranef %in% c(covariates_categ, covariates_categ_categ)])
      if(length(occuCovs_categ$ranef) == 0) occuCovs_categ$ranef <- NULL
      occuCovs$ranef       <- occuCovs$ranef[occuCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ)]
      if(length(occuCovs$ranef) == 0) occuCovs <- modifyList(occuCovs, list(ranef = NULL))
    }
  }
  
  if(!is.null(detCovs$fixed)) {
    if(any(!detCovs$fixed %in% colnames(covariates))) {
      stop(paste(detCovs$fixed [!detCovs$fixed %in% colnames(covariates)], collapse = ", "), " is not in covariates")
    }
    if(any(!detCovs$fixed %in% covariates_numeric)) {
      if(!all(detCovs$fixed [which(!detCovs$fixed %in% covariates_numeric)] %in% covariates_categ)) stop(paste("Detection covariate ", paste(detCovs$fixed [which(!detCovs$fixed %in% covariates_numeric)], collapse = ", "), 
                                                                                                                                       "is not a factor"))
      
      detCovs_categ$fixed <- detCovs$fixed[detCovs$fixed %in% covariates_categ]
      detCovs$fixed       <- detCovs$fixed[detCovs$fixed %in% covariates_numeric]
      if(length(detCovs$fixed) == 0) detCovs <- modifyList(detCovs, list(fixed = NULL))
    }
  }
  
  
  if(!is.null(detCovs$independent)) {
    if(any(!detCovs$independent %in% colnames(covariates))) {
      stop(paste(detCovs$independent [!detCovs$independent %in% colnames(covariates)], collapse = ", "), " is not in covariates")
    }
    if(any(!detCovs$independent %in% covariates_numeric)) {
      if(!all(detCovs$independent [which(!detCovs$independent %in% covariates_numeric)] %in% covariates_categ)) stop(paste("Detection covariate ", paste(detCovs$independent [which(!detCovs$independent %in% covariates_numeric)], collapse = ", "), 
                                                                                                               "is not a factor"))

      # prohibit independent effects of categorical covariates
      if(any(detCovs$independent %in% covariates_categ)) stop("independent effects of categorical site covariates are currently not supported")
      
      # detCovs_categ$independent <- detCovs$independent[detCovs$independent %in% covariates_categ]
      # detCovs$independent       <- detCovs$independent[detCovs$independent %in% covariates_numeric]
      # if(length(detCovs$independent) == 0) detCovs <- modifyList(detCovs, list(independent = NULL))
    }
  }
  
  
  if(!is.null(detCovs$ranef)) {
    
    if(any(detCovs$ranef %in% covariates_categ_categ)) stop("random effects of categorical site covariates on other categorical site covariates are not supported yet (except for random effect of species)")
    
    if(any(!detCovs$ranef %in% c(colnames(covariates), covariates_categ, covariates_numeric_categ))) {
      stop(paste(detCovs$ranef [!detCovs$ranef %in% colnames(covariates)], collapse = ", "), " is not in covariates")
    }
    
    if(any(!detCovs$ranef %in% covariates_numeric)) {
      if(!all(detCovs$ranef [which(!detCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ))] %in% covariates_categ)) stop(paste("Detection covariate ", paste(detCovs$ranef [which(!detCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ))], collapse = ", "), 
                                                                                                                                                                    "is not a factor"))
      detCovs_categ$ranef <- c(detCovs_categ$ranef, 
                               detCovs$ranef[detCovs$ranef %in% c(covariates_categ, covariates_categ_categ)])
      if(length(detCovs_categ$ranef) == 0) detCovs_categ$ranef <- NULL
      detCovs$ranef       <- detCovs$ranef[detCovs$ranef %in% c(covariates_numeric, covariates_numeric_categ)]
      if(length(detCovs$ranef) == 0) detCovs <- modifyList(detCovs, list(ranef = NULL))
    }
  }
  
  
                       
  
  # some more checks for dimensions and data types
  dim_dethist1 <- dim(data_list$ylist[[1]])
  if(!all(sapply(data_list$ylist,   FUN = function(x) all(dim(x) == dim_dethist1)))) stop("dimensions differ between items in data_list$ylist")
  if(!all(sapply(data_list$obsCovs, FUN = function(x) all(dim(x) == dim_dethist1)))) stop("dimensions differ between items in data_list$ylist and data_list$obsCovs")
  
  if(exists("siteCovs", where = data_list)) {
    if(inherits(data_list$siteCovs, "data.frame")) if(nrow(data_list$siteCovs) != dim_dethist1[1]) stop("nrow differs between data_list$siteCovs and the detection histories in data_list$ylist")
  }
  
  
  ## Data augmentation ####
  
  # check if all-0 detection histories present
  if(any(sapply(data_list$ylist, sum, na.rm = T) == 0)) {
    warning("There are all-0 detection histories. It is recommended to instead provide only detected species, and control data augmentation via 'augmentation'.")
  }
  
  
  if(!is.null(augmentation)) {
    if(length(augmentation) != 1) stop("'augmentation' must have length 1")
    if(is.null(names(augmentation))) stop("'augmentation' must be named")
    if(!names(augmentation) %in% c("maxknown", "full"))  stop("name of 'augmentation' must be either 'maxknown' or 'full'")
    augment <- names(augmentation)
    if(inherits(augmentation, "list")) augmentation <- as.vector(augmentation, mode = "numeric")
    
    if(augmentation <= length(data_list$ylist)) stop("'augmentation' must be larger than the number of detected species") 
    
    nspec <- length(data_list$ylist) # number of seen species
    
    nz <- augmentation - length(data_list$ylist)   # number of unseen species added
    data_list$ylist <- c(data_list$ylist, replicate(augmentation - length(data_list$ylist), data_list$ylist[[1]] * 0, simplify = FALSE))
    
  } else {
    augment = "none"
  }
  
  # set indices to use in loops
  speciesMax  <- "M"
  stationMax  <- "J"
  occasionMax <- "maxocc"
  
  speciesMax_value  <- length(data_list$ylist)
  stationMax_value  <- nrow(data_list$ylist[[1]])
  occasionMax_value <- ncol(data_list$ylist[[1]])
  
  
  
  # Priors  ####
  
  model_start <- "model{\n### PRIORS \n"
  
  
  ## Intercept - occupancy - hyperpriors   ####
  
  intercept_occu <- interceptPriors(effect = intercepts$occu, 
                                    type = "occupancy", 
                                    prior_list = prior_list, 
                                    inits_list = inits_list, 
                                    speciesIndex = speciesIndex,
                                    speciesMax = speciesMax,
                                    speciesMax_value = speciesMax_value)
  
  priors_intercept_occu <- intercept_occu$prior
  beta0_formula         <- intercept_occu$formula
  
  ## Intercept - detection - hyperpriors ####
  
  intercept_det <- interceptPriors(effect = intercepts$det, 
                                   type = "detection", 
                                   prior_list = prior_list, 
                                   inits_list = inits_list, 
                                   speciesIndex = speciesIndex,
                                   speciesMax = speciesMax,
                                   speciesMax_value = speciesMax_value)
  
  priors_intercept_det <- intercept_det$prior
  alpha0_formula       <- intercept_det$formula
  
  
  ##  data augmentation priors ####
  
  if(augment == "full") {
    priors_omega <- paste(
      "## Data augmentation parameter",
      "omega ~ dunif(0, 1)",
      "\n", sep = "\n"
    )
    attr(priors_omega, "params") <- c("omega")
    attr(priors_omega, "inits") <- list(omega = inits_list$inits_runif_omega)
  } else {
    priors_omega <- paste(
      "## Data augmentation parameter",
      "# < empty > ",
      "\n", sep = "\n"
    )
  }
  
  
  ## obs Cov - cont - fixed effect priors ####
  prior_obs_header_fixed <- paste("## Continuous observation-level covariates on detection - Fixed effects\n")
  
  if(!is.null(detCovsObservation$fixed)) {
    priors_obscovs_det_fixed <- list()
    
    for(obsCovs_det_index in 1:length(detCovsObservation$fixed)){
      current_cov <- detCovsObservation$fixed[obsCovs_det_index]
      
      priors_obscovs_det_fixed[[obsCovs_det_index]] <- paste(
        paste("# Covariate:", current_cov),
        paste0("alpha.obs.fixed.cont.", current_cov, " ~ ", prior_list$dnorm), 
        "\n", 
        sep = "\n")
      attr(priors_obscovs_det_fixed, "params") <- paste0("alpha.obs.fixed.cont.", detCovsObservation$fixed)
      
      inits_tmp <- vector(mode = "list", length = length(detCovsObservation$fixed))
      names(inits_tmp) <- c(paste0("alpha.obs.fixed.cont.", detCovsObservation$fixed))
      
      for(i in 1:length(inits_tmp)){
        inits_tmp[[i]] <- inits_list$inits_runif_mean_0      #list("runif", list(1))
      }
      attr(priors_obscovs_det_fixed, "inits") <- inits_tmp
      rm(inits_tmp)
    }
  } else {
    priors_obscovs_det_fixed <- list("# < empty > \n\n")
    attr(priors_obscovs_det_fixed, "params") <- NULL
  }
  
  ## obs Cov - cont - random effect priors ####
  prior_obs_header_ranef <- paste("## Continuous observation-level covariates on detection - with random effects\n")
  
  if(!is.null(detCovsObservation$ranef)){
    priors_obscovs_det_ranef <- list()
    alpha_draws_ranef_obs_cont <- list()
    
    for(obsCovs_det_index in 1:length(detCovsObservation$ranef)){
      current_cov <- detCovsObservation$ranef[obsCovs_det_index]
      priors_obscovs_det_ranef[[obsCovs_det_index]] <- paste(
        paste("# Observation Covariate:", current_cov),
        paste0("alpha.obs.ranef.cont.", current_cov, ".mean", " ~ ", prior_list$dnorm),
        paste0("alpha.obs.ranef.cont.", current_cov, ".tau", " ~ ", prior_list$dgamma),
        paste0("alpha.obs.ranef.cont.", current_cov, ".sigma", " <- sqrt(1 / alpha.obs.ranef.cont.", current_cov, ".tau)"),
        "\n", 
        sep = "\n")
      
      alpha_draws_ranef_obs_cont[[obsCovs_det_index]] <- paste("# continuous detection covariates with random effects:", 
                                                               paste0("alpha.obs.ranef.cont.", current_cov, "[", speciesIndex, "] ~ dnorm(alpha.obs.ranef.cont.", current_cov, ".mean", ", alpha.obs.ranef.cont.", current_cov, ".tau)"),
                                                               sep = "\n")
    }
    
    attr(priors_obscovs_det_ranef, "params") <- c(paste0("alpha.obs.ranef.cont.", detCovsObservation$ranef, ".mean"),
                                                  paste0("alpha.obs.ranef.cont.", detCovsObservation$ranef, ".sigma"))
    
    
    # make list of parameters
    inits_tmp <- vector(mode = "list", length = length(detCovsObservation$ranef) * 2)
    names(inits_tmp) <- c(paste0("alpha.obs.ranef.cont.", detCovsObservation$ranef, ".mean"),
                          paste0("alpha.obs.ranef.cont.", detCovsObservation$ranef, ".tau"))
    
    for(i in 1:length(inits_tmp)){
      if(endsWith(names(inits_tmp)[i], "mean")) inits_tmp[[i]] <- inits_list$inits_runif_mean_0
      if(endsWith(names(inits_tmp)[i], "tau"))  inits_tmp[[i]] <- inits_list$inits_runif_tau
    }
    attr(priors_obscovs_det_ranef, "inits") <- inits_tmp
    rm(inits_tmp)
    
    names(alpha_draws_ranef_obs_cont) <- paste0("alpha.obs.ranef.cont.", detCovsObservation$ranef)
    
  } else {
    priors_obscovs_det_ranef <- list("# < empty > \n\n")
    attr(priors_obscovs_det_ranef, "params") <- NULL
    alpha_draws_ranef_obs_cont <- "# continuous observation-level detection covariates: no random effect of species"
  }
  
  
  
  
  ## obs Cov - categ - fixed effect priors ####
  prior_obs_categ_header_fixed <- paste("## Categorical observation-level covariates on detection - Fixed effect\n")
  
  if(!is.null(detCovsObservation_categ$fixed)){
    priors_obscovs_det_categ_fixed <- list()
    
    attr(priors_obscovs_det_categ_fixed, "n_levels") <- rep(NA, times = length(detCovsObservation_categ$fixed))
    
    for(obsCovs_det_categ_index in 1:length(detCovsObservation_categ$fixed)){
      
      obsCovCat_tmp_fact <- factor(data_list$obsCovs[[detCovsObservation_categ$fixed[obsCovs_det_categ_index]]])
      
      current_cov <- detCovsObservation_categ$fixed[obsCovs_det_categ_index]
      current_cov_values <- obsCovCat_tmp_fact   #covariates[, current_cov]
      index_letter <- paste0("index_cat_fixed_obs_categ_", current_cov)
      
      priors_obscovs_det_categ_fixed[[obsCovs_det_categ_index]] <- paste(
        paste("# Covariate:", current_cov),
        paste0("alpha.obs.fixed.categ.", current_cov, "[1] <- 0"),
        paste0("for(", index_letter, " in 2:", length(levels(current_cov_values)), ") {"),
        paste0("alpha.obs.fixed.categ.", current_cov, "[", index_letter, "] ~ ", prior_list$dnorm),
        "}",
        "\n", 
        sep = "\n")
      attr(priors_obscovs_det_categ_fixed, "n_levels")[obsCovs_det_categ_index] <- length(levels(current_cov_values))
    }
    attr(priors_obscovs_det_categ_fixed, "params") <- paste0("alpha.obs.fixed.categ.", detCovsObservation_categ$fixed)
    attr(priors_obscovs_det_categ_fixed, "index_letters") <- paste0("index_cat_fixed_obs_categ_", detCovsObservation_categ$fixed)
    
    inits_tmp <- vector(mode = "list", length = length(detCovsObservation_categ$fixed))
    names(inits_tmp) <- paste0("alpha.obs.fixed.categ.", detCovsObservation_categ$fixed)
    for(n in 1:length(detCovsObservation_categ$fixed)) {
      inits_tmp[[n]][1] <- NA
      inits_tmp[[n]][2:attr(priors_obscovs_det_categ_fixed, "n_levels")[n]] <- rnorm(attr(priors_obscovs_det_categ_fixed, "n_levels")[n] - 1)
    }
    attr(priors_obscovs_det_categ_fixed, "inits") <- inits_tmp
    rm(inits_tmp)
    
  } else {
    priors_obscovs_det_categ_fixed <- list("# < empty > \n\n")
    attr(priors_obscovs_det_categ_fixed, "params") <- NULL
  }
  
  
  ## obs Cov - categ - random effect priors ####
  
  prior_obs_categ_header_ranef <- paste("## Categorical observation-level covariates on detection - with random effects\n")
  if(!is.null(detCovsObservation_categ$ranef)){
    priors_obscovs_det_categ_ranef <- list()
    alpha_draws_ranef_obs_categ <- list()
    
    attr(priors_obscovs_det_categ_ranef, "n_levels") <- rep(NA, times = length(detCovsObservation_categ$ranef))
    
    for(obsCovs_det_categ_index in 1:length(detCovsObservation_categ$ranef)){
      
      obsCovCat_tmp_fact <- factor(data_list$obsCovs[[detCovsObservation_categ$ranef[obsCovs_det_categ_index]]])
      
      current_cov <- detCovsObservation_categ$ranef[obsCovs_det_categ_index]
      current_cov_values <- obsCovCat_tmp_fact   #covariates[, current_cov]
      index_letter <- paste0("index_cat_ranef_obs_categ_", current_cov)
      
      
      
      priors_obscovs_det_categ_ranef[[obsCovs_det_categ_index]] <- paste(
        #paste("# Covariate:", current_cov),
        paste("# Community mean effects of", current_cov),
        paste0("alpha.obs.ranef.categ.", current_cov, ".mean[1] <- 0"),
        paste0("alpha.obs.ranef.categ.", current_cov, ".sigma[1] <- 0"),     # is included in random effect of intercept already
        paste0("alpha.obs.ranef.categ.", current_cov, ".tau[1] <- 0"),
        "",
        paste0("for(", index_letter, " in 2:", length(unique(current_cov_values)), ") {"),
        paste0("alpha.obs.ranef.categ.", current_cov, ".mean[", index_letter, "] ~ ", prior_list$dnorm),   
        paste0("alpha.obs.ranef.categ.", current_cov,  ".tau[", index_letter, "] ~ ", prior_list$dgamma),
        paste0("alpha.obs.ranef.categ.", current_cov,  ".sigma[", index_letter, "] <- sqrt(1 / ", "alpha.obs.ranef.categ.", current_cov,  ".tau[", index_letter, "]", ")"),
        "}",
        "",
        sep = "\n")
      attr(priors_obscovs_det_categ_ranef, "n_levels")[obsCovs_det_categ_index] <- length(unique(current_cov_values))
      
      
      alpha_draws_ranef_obs_categ[[obsCovs_det_categ_index]] <- paste("# categorical detection covariates with random effects:", 
                                                                      paste0("alpha.obs.ranef.categ.", current_cov, "[", speciesIndex, ", 1] <- 0"), 
                                                                      paste0("for (", index_letter, " in 2:",  length(unique(current_cov_values)), "){"),
                                                                      paste0("alpha.obs.ranef.categ.", current_cov, "[", speciesIndex, ", ", index_letter, "] ~ dnorm(alpha.obs.ranef.categ.", current_cov, ".mean[", index_letter, "], alpha.obs.ranef.categ.", current_cov, ".tau[", index_letter, "])"),
                                                                      "}", sep = "\n")
    }
    
    attr(priors_obscovs_det_categ_ranef, "params") <- c(paste0("alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, ".mean"),
                                                        paste0("alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, ".sigma"))
    
    attr(priors_obscovs_det_categ_ranef, "index_letters") <- paste0("index_cat_ranef_obs_", detCovsObservation_categ$ranef)
    
    inits_tmp <- vector(mode = "list", length = length(detCovsObservation_categ$ranef)  * 2)
    names(inits_tmp) <- c(paste0("alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, ".mean"),
                          paste0("alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, ".tau"))
    
    for(n in 1:length(detCovsObservation_categ$ranef)) {
      inits_tmp[[n]][1] <- NA
      inits_tmp[[n]][2:attr(priors_obscovs_det_categ_ranef, "n_levels")[n]] <- rnorm(attr(priors_obscovs_det_categ_ranef, "n_levels")[n] - 1)
      
      inits_tmp[[n + length(detCovsObservation_categ$ranef)]][1] <- NA
      inits_tmp[[n + length(detCovsObservation_categ$ranef)]][2:attr(priors_obscovs_det_categ_ranef, "n_levels")[n]] <- abs(rnorm(attr(priors_obscovs_det_categ_ranef, "n_levels")[n] - 1))
      
    }
    attr(priors_obscovs_det_categ_ranef, "inits") <- inits_tmp
    rm(inits_tmp)
    
    names(alpha_draws_ranef_obs_categ) <- paste0("alpha.obs.ranef.categ.", detCovsObservation_categ$ranef)
  } else {
    priors_obscovs_det_categ_ranef <- list("# < empty > \n\n")
    attr(priors_obscovs_det_categ_ranef, "params") <- NULL
    alpha_draws_ranef_obs_categ <- "# categorical observation covariates: no random effect of species"
  }
  
  
  
  
  
  ## det Cov - cont - fixed effect priors ####
  prior_det_header_fixed <- paste("## Continuous site covariates on detection - Fixed effects\n")
  
  priors_sitecovs_det_fixed <- fixedEffectPriors (effect_names = detCovs$fixed,
                                                  type = "detection", 
                                                  prior_list = prior_list, 
                                                  stationIndex = stationIndex)
  
  
  
  ## det Cov - cont - independent effect priors ####
  prior_det_header_indep <- paste("## Continuous site covariates on detection - Independent effects\n")
  
  priors_sitecovs_det_indep <- independentEffectPriors (effect_names = detCovs$independent,
                                                        type = "detection",
                                                        inits_list = inits_list,
                                                        prior_list = prior_list, 
                                                        stationIndex = stationIndex,
                                                        speciesIndex = speciesIndex,
                                                        speciesMax = speciesMax,
                                                        speciesMax_value = speciesMax_value)
  
  
  
  
  ## det Cov - cont - random effect priors ####
  prior_det_header_ranef <- paste("## Continuous site covariates on detection - with random effects\n")
  
  tmp <- randomEffectPriors(effect_names = detCovs$ranef, type = "detection", covariates = covariates,
                            prior_list = prior_list, inits_list = inits_list, speciesIndex = speciesIndex, stationIndex = stationIndex, 
                            speciesMaxNumber = speciesMax_value, speciesMax = speciesMax)
  priors_sitecovs_det_ranef  <- tmp[[1]]
  alpha_draws_ranef_det_cont <- tmp[[2]]
  rm(tmp)
  
  
  ## det Cov - categ- fixed effect priors ####
  prior_det_categ_header_fixed <- paste("## Categorical site covariates on detection - Fixed effect\n")
  
  priors_sitecovs_det_categ_fixed <- fixedEffectPriorsCateg(effect_names = detCovs_categ$fixed, 
                                                            covariates = covariates,
                                                            type = "detection",
                                                            prior_list = prior_list, 
                                                            stationIndex = stationIndex)
  
  
  
  ## det Cov - categ - random effect priors ####
  prior_det_categ_header_ranef <- paste("## Categorical site covariates on detection - with random effects\n")
  
  tmp <- randomEffectPriorsCateg(effect_names = detCovs_categ$ranef,
                                 covariates = covariates,
                                 type = "detection",
                                 prior_list = prior_list, 
                                 speciesIndex = speciesIndex, 
                                 stationIndex = stationIndex)
  priors_sitecovs_det_categ_ranef <- tmp[[1]]
  alpha_draws_ranef_det_categ <- tmp[[2]]
  rm(tmp)
  
  
  
  
  
  ## occu Cov - cont - fixed effect priors ####
  prior_occu_header_fixed <- paste("## Continuous site covariates on Occupancy - Fixed effects\n")
  
  priors_sitecovs_occu_fixed <- fixedEffectPriors (effect_names = occuCovs$fixed,
                                                   type = "occupancy",
                                                   prior_list = prior_list, 
                                                   stationIndex = stationIndex)
  
  
  ## occu Cov - cont - independent effect priors ####
  prior_occu_header_indep <- paste("## Continuous site covariates on Occupancy - Independent effects\n")
  
  priors_sitecovs_occu_indep <- independentEffectPriors (effect_names = occuCovs$independent,
                                                         type = "occupancy",
                                                         inits_list = inits_list,
                                                         prior_list = prior_list, 
                                                         stationIndex = stationIndex,
                                                         speciesIndex = speciesIndex,
                                                         speciesMax = speciesMax,
                                                         speciesMax_value = speciesMax_value)
  
  
  
  
  
  ## occu Cov - cont  - random effect priors ####
  prior_occu_header_ranef <- paste("## Continuous site covariates on occupancy - with random effects\n")
  tmp <- randomEffectPriors(effect_names = occuCovs$ranef,
                            type = "occupancy",
                            covariates = covariates,
                            prior_list = prior_list, 
                            inits_list = inits_list, 
                            speciesIndex = speciesIndex, 
                            stationIndex = stationIndex, 
                            speciesMaxNumber = speciesMax_value, 
                            speciesMax = speciesMax)
  priors_sitecovs_occu_ranef <- tmp[[1]]
  beta_draws_ranef_occu_cont <- tmp[[2]]
  rm(tmp)
  
  
  
  ## occu Cov - categ - fixed effect priors ####
  prior_occu_categ_header_fixed <- paste("## Categorical site covariates on Occupancy - Fixed effects\n")
  
  priors_sitecovs_occu_categ_fixed <- fixedEffectPriorsCateg(effect_names = occuCovs_categ$fixed,
                                                             covariates = covariates,
                                                             type = "occupancy", 
                                                             prior_list = prior_list,
                                                             stationIndex = stationIndex)
  
  
  
  
  ## occu Cov - categ - random effect priors ####
  prior_occu_categ_header_ranef <- paste("## Categorical site covariates on occupancy - with random effects\n")
  
  tmp <- randomEffectPriorsCateg(effect_names = occuCovs_categ$ranef,
                                 covariates = covariates,
                                 type = "occupancy", 
                                 prior_list = prior_list, 
                                 speciesIndex = speciesIndex, 
                                 stationIndex = stationIndex)
  priors_sitecovs_occu_categ_ranef <- tmp[[1]]
  beta_draws_ranef_occu_categ <- tmp[[2]]
  rm(tmp)
  
  
  
  
  
  
  # random effect of species and station on detection probability  ####
  prior_det_species_station_ranef <- "# Species-station random effect on detection probability\n"
  tmp <-  randomSpeciesStationEffectPriors(doit = speciesSiteRandomEffect$det,
                                           prior_list = prior_list, 
                                           inits_list = inits_list, 
                                           speciesIndex = speciesIndex, 
                                           stationIndex = stationIndex)
  priors_SpeciesStation_det_ranef <- tmp[[1]]
  alpha_draws_SpeciesStation_det_ranef <- tmp[[2]]
  rm(tmp)
  
  
  
  
  before_species_loop <- paste("## Draws of random effects other than species",
                               paste(unlist(alpha_draws_ranef_det_cont)[attr(alpha_draws_ranef_det_cont, "random_effect") == "not_species"]),
                               paste(unlist(beta_draws_ranef_occu_cont)[attr(beta_draws_ranef_occu_cont, "random_effect") == "not_species"]),
                               sep = "\n"
  )
  
  
  # Species loop ####
  loop1 <- paste("\n### MODEL LOOPS \n",
                 "# species loop", 
                 paste0("for (", speciesIndex, " in 1:", speciesMax, "){"),
                 if ("ranef" %in% c(intercepts$det, intercepts$occu)) {
                   paste("##  Draw species-specific random effect parameters from community distributions", 
                         "# intercepts:", sep = "\n")}, 
                 if(intercepts$occu == "ranef") {
                   #paste0("beta0[", speciesIndex, "] ~ dnorm(mean.beta0, tau.beta0)")},
                   paste0("beta0[", speciesIndex, "] ~ dnorm(beta0.mean, beta0.tau)")},
                 if(intercepts$det == "ranef") { 
                   #paste0("alpha0[", speciesIndex, "] ~ dnorm(mean.alpha0, tau.alpha0)")},
                   paste0("alpha0[", speciesIndex, "] ~ dnorm(alpha0.mean, alpha0.tau)")},
                 "",
                 if(augment == "full"){paste0("# Metacommunity membership indicator (data augmentation)\n", "w[", speciesIndex, "] ~ dbern(omega)\n")},
                 paste(unlist(alpha_draws_ranef_det_cont[attr(alpha_draws_ranef_det_cont, "random_effect") == "species"]), collapse = "\n"), 
                 "\n",
                 paste(unlist(alpha_draws_ranef_det_categ), collapse = "\n"),
                 "\n",         
                 paste(unlist(alpha_draws_ranef_obs_cont), collapse = "\n"),
                 "\n",
                 paste(unlist(alpha_draws_ranef_obs_categ), collapse = "\n"),
                 "\n",
                 paste(unlist(beta_draws_ranef_occu_cont[attr(beta_draws_ranef_occu_cont, "random_effect") == "species"]), collapse = "\n"), 
                 "\n", 
                 paste(unlist(beta_draws_ranef_occu_categ), collapse = "\n"),
                 "\n", sep = "\n")
  
  attr(loop1, "params") <- c("beta0", "alpha0", 
                             names(alpha_draws_ranef_det_cont), names(alpha_draws_ranef_det_categ), names(alpha_draws_ranef_obs_cont), names(alpha_draws_ranef_obs_categ), 
                             names(beta_draws_ranef_occu_cont), names(beta_draws_ranef_occu_categ))
  if(augment == "full"){
    attr(loop1, "params") <- c(attr(loop1, "params"), "w")
    attr(loop1, "inits") <- list(w = rep(1, length(data_list$ylist)))
  }
  
  
  
  
  ## construct formula for psi (ecological model - beta parameters)  ####
  
  beta_formula_occu_fixed <- attr(priors_sitecovs_occu_fixed, "formula")
  beta_formula_occu_indep <- attr(priors_sitecovs_occu_indep, "formula")
  beta_formula_occu_ranef <- attr(priors_sitecovs_occu_ranef, "formula")
  beta_formula_occu_categ_fixed <- attr(priors_sitecovs_occu_categ_fixed, "formula")
  beta_formula_occu_categ_ranef <- attr(priors_sitecovs_occu_categ_ranef, "formula")
  
  
  ## station loop ####
  # including formula for psi
  
  loop2 <- paste("# station loop", 
                 paste0("for (", stationIndex ," in 1:", stationMax, "){"),
                 paste("\n# Occupancy probability formula"),
                 if (nimble) {
                   paste0("logit(psi[", speciesIndex, ",", stationIndex, "]) <- ", beta0_formula, beta_formula_occu_categ_fixed, beta_formula_occu_categ_ranef, beta_formula_occu_fixed, beta_formula_occu_indep, beta_formula_occu_ranef)
                 },
                 
                 if (!nimble) {
                   paste0("logit.psi[", speciesIndex, ",", stationIndex, "] <- ", beta0_formula, beta_formula_occu_categ_fixed, beta_formula_occu_categ_ranef, beta_formula_occu_fixed, beta_formula_occu_indep, beta_formula_occu_ranef)
                 },
                 if (!nimble) {
                   paste0("psi[", speciesIndex, ",", stationIndex, "] <- exp(logit.psi[", speciesIndex, ",", stationIndex, "]) / (exp(logit.psi[", speciesIndex, ",", stationIndex, "]) + 1)")
                 },
                 
                 ifelse(!nimble, 
                        paste0("z[", speciesIndex, ",", stationIndex, "] ~ dbern(psi[", speciesIndex, ", ", stationIndex, "]", 
                               ifelse(augment == "full", paste0(" * w[", speciesIndex, "])"), ")")), 
                        ""),
                 "\n", sep = "\n"
  )
  attr(loop2, "params") <- NULL
  
  
  zin <- t(sapply(data_list$ylist, rowSums, na.rm = TRUE))  # species x Station matrix with n detections
  zin[zin>1] <- 1
  if(!nimble) attr(loop2, "inits") <- list(z = zin)
  if(nimble)  attr(loop2, "inits") <- NULL
  
  ## construct formula for p (detection model - alpha parameters)   ####
  
  alpha_formula_det_fixed <- attr(priors_sitecovs_det_fixed, "formula")
  alpha_formula_det_indep <- attr(priors_sitecovs_det_indep, "formula")
  alpha_formula_det_ranef <- attr(priors_sitecovs_det_ranef, "formula")
  alpha_formula_det_categ_fixed <- attr(priors_sitecovs_det_categ_fixed, "formula")
  alpha_formula_det_categ_ranef <- attr(priors_sitecovs_det_categ_ranef, "formula")
  
  alpha_formula_det_SpeciesStation <- attr(priors_SpeciesStation_det_ranef, "formula")
  
  
  
  if(!is.null(detCovsObservation$fixed)) {
    if(nimble) {
      alpha_formula_obs_fixed <- paste(" + alpha.obs.fixed.cont.", detCovsObservation$fixed, " * ", detCovsObservation$fixed, "[", stationIndex, ", 1:", occasionMax, "]", collapse = "", sep = "")
    } else {
      alpha_formula_obs_fixed <- paste(" + alpha.obs.fixed.cont.", detCovsObservation$fixed, " * ", detCovsObservation$fixed, "[", stationIndex, ", ", occasionIndex, "]", collapse = "", sep = "")
    }
  } else {
    alpha_formula_obs_fixed <- ""
  }
  
  
  if(!is.null(detCovsObservation$ranef)) {
    if(nimble) {
      alpha_formula_obs_ranef <- paste(" + alpha.obs.ranef.cont.", detCovsObservation$ranef, "[", speciesIndex, "] * ", detCovsObservation$ranef, "[", stationIndex, ", 1:", occasionMax, "]", collapse = "", sep = "")
    } else {
      alpha_formula_obs_ranef <- paste(" + alpha.obs.ranef.cont.", detCovsObservation$ranef, "[", speciesIndex, "] * ", detCovsObservation$ranef, "[", stationIndex, ", ", occasionIndex, "]", collapse = "", sep = "")
    }
  } else {
    alpha_formula_obs_ranef <- ""
  }
  
  
  if(!is.null(detCovsObservation_categ$fixed)) {
    if(nimble) {
      alpha_formula_obs_fixed_categ <- paste(" + alpha.obs.fixed.categ.", detCovsObservation_categ$fixed, "[index_tmp_fixed]", collapse = "", sep = "")
    } else {
      alpha_formula_obs_fixed_categ <- paste(" + alpha.obs.fixed.categ.", detCovsObservation_categ$fixed, "[", paste0(detCovsObservation_categ$fixed, "_integer"), "[", stationIndex, ", ", occasionIndex, "]", "]", collapse = "", sep = "")  
    }
  } else {
    alpha_formula_obs_fixed_categ <- ""
  }
  
  if(!is.null(detCovsObservation_categ$ranef)) {
    if(nimble){
      alpha_formula_obs_ranef_categ <- paste(" + alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, "[", speciesIndex, ", ", "index_tmp_ranef", "]", collapse = "", sep = "")
    } else {
      alpha_formula_obs_ranef_categ <- paste(" + alpha.obs.ranef.categ.", detCovsObservation_categ$ranef, "[", speciesIndex, ", ", detCovsObservation_categ$ranef, "_integer", "[", stationIndex, ", ", occasionIndex, "]", "]", collapse = "", sep = "")  
    }
  } else {
    alpha_formula_obs_ranef_categ <- ""
  }
  
  
  if(speciesSiteRandomEffect$det) {
    priors_species_station_det_ranef <- paste("ran", "[", speciesIndex, ", ", stationIndex, "] ~ dnorm(0, alpha.speciesstation.ranef.tau)", collapse = "", sep = "")
    
    alpha_species_station_ranef <- paste("ran", "[", speciesIndex, ", ", stationIndex, "]", collapse = "", sep = "")
  } else {
    alpha_species_station_ranef <- ""
  }
  
  
  ### occasion loop  ####
  
  if(!nimble){
    loop3 <- paste( alpha_draws_SpeciesStation_det_ranef,
                    "# occasion loop", 
                   paste0("for (", occasionIndex, " in 1:", occasionMax, "){"), 
                   
                   paste("# Detection probability formula"),
                   
                   
                   
                   paste0("logit.p[", speciesIndex, ",", stationIndex, ",", occasionIndex, "] <- ", alpha0_formula, alpha_formula_obs_fixed, alpha_formula_obs_ranef, 
                          alpha_formula_obs_fixed_categ, alpha_formula_obs_ranef_categ,
                          alpha_formula_det_categ_fixed, alpha_formula_det_categ_ranef, 
                          alpha_formula_det_fixed, alpha_formula_det_indep, alpha_formula_det_ranef,
                          alpha_formula_det_SpeciesStation
                          ),
                   
                   paste("\n# convert p to real scale"),
                   paste0("p[", speciesIndex, ",", stationIndex, ",", occasionIndex,"] <- exp(logit.p[", speciesIndex, ",", stationIndex, ",", occasionIndex,"]) / (1+exp(logit.p[", speciesIndex, ",", stationIndex, ",", occasionIndex,"]))"),
                   
                   paste("\n# Ensure occasions without effort have p = 0"),
                   paste0("p.eff[", speciesIndex, ",", stationIndex, ",", occasionIndex,"] <- z[", speciesIndex, ",", stationIndex, "] * p[", speciesIndex, ",", stationIndex, ",", occasionIndex,"] * effort_binary[", stationIndex, ",", occasionIndex,"]"),
                   
                   paste0("y[", speciesIndex, ",", stationIndex, ",", occasionIndex,"] ~ dbern(p.eff[", speciesIndex, ",", stationIndex, ",", occasionIndex,"])"),
                   
                   "\n### generate new data from model under consideration",
                   paste0("new.y[", speciesIndex, ",", stationIndex, ",", occasionIndex,"] ~ dbern(p.eff[", speciesIndex, ",", stationIndex, ",", occasionIndex,"])"),
                   "}   # close occasion loop", 
                   "\n", sep = "\n")
    attr(loop3, "params") <- NULL
  }
  
  if(nimble){
    
    loop3 <- paste(ifelse(!is.null(detCovsObservation_categ$fixed) , 
                          paste0("index_tmp_fixed <- ", paste0(detCovsObservation_categ$fixed, "_integer", "[", stationIndex, ", 1:", occasionMax, "]")),
                          ""),
                   ifelse(!is.null(detCovsObservation_categ$ranef), 
                          paste0("index_tmp_ranef <- ", paste0(detCovsObservation_categ$ranef, "_integer", "[", stationIndex, ", 1:", occasionMax, "]")),
                          ""),
                   paste("# Detection probability formula"),
                   paste0("logit(p[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "]) <- ", alpha0_formula, alpha_formula_obs_fixed, alpha_formula_obs_ranef, 
                          alpha_formula_obs_fixed_categ, alpha_formula_obs_ranef_categ,
                          alpha_formula_det_categ_fixed, alpha_formula_det_categ_ranef, 
                          alpha_formula_det_fixed, alpha_formula_det_indep, alpha_formula_det_ranef,
                          paste0(" * all1row[1:", occasionMax, "]")),              # this is only to ensure that dimensions match if alpha is a single value
                   
                   paste("\n# Ensure occasions without effort have p = 0"),
                   paste0("p.eff[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "] <- p[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "]", 
                          " * effort_binary[", stationIndex, ", 1:", occasionMax, "]"),
                   
                   
                   "\n### calculate probability of observed data ",
                   paste0("y[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "] ~ dOcc_v(probOcc = psi[", speciesIndex, ",", stationIndex, "], probDetect = p.eff[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "], len = ", occasionMax, ")"),
                   
                   "\n### generate new data from model under consideration",
                   paste0("new.y[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "]  <- rOcc_v(n = 1, probOcc = psi[", speciesIndex, ",", stationIndex, "], probDetect = p.eff[", speciesIndex, ",", stationIndex, ",", "1:", occasionMax, "], len = ", occasionMax, ")"), 
                   
                   "\n", sep = "\n")
    attr(loop3, "params") <- NULL
    
    
  }
  
  
  
  ## derived quantities #### 
  
  close_loop2 <-  paste( "### calculate Freeman-Tukey residuals for real and new data",
                         paste0("res[", speciesIndex, ",", stationIndex, "] <- (sqrt(sum(y[", speciesIndex, ",", stationIndex, ", 1:", occasionMax, "])) - sqrt(sum(p.eff[", speciesIndex, ",", stationIndex, ", 1:", occasionMax, "])))^2"),
                         paste0("new.res[", speciesIndex, ",", stationIndex, "] <- (sqrt(sum(new.y[", speciesIndex, ",", stationIndex, ", 1:", occasionMax, "])) - sqrt(sum(p.eff[", speciesIndex, ",", stationIndex, ", 1:", occasionMax, "])))^2"),
                         "}   # close station loop",
                         "\n", sep = "\n")
  attr(close_loop2, "params") <- NULL
  
  
  
  close_loop1a1 <- paste("### sum residuals over stations",
                         paste0("R2[", speciesIndex, "] <- sum(res[", speciesIndex, ", 1:", stationMax, "])"),
                         paste0("new.R2[", speciesIndex, "] <- sum(new.res[", speciesIndex, ", 1:", stationMax, "])"),
                         
                         "\n### species-level Bayesian p-value",
                         paste0("Bpvalue_species[", speciesIndex, "] <- R2[", speciesIndex, "] > new.R2[", speciesIndex, "]"),
                         "\n", sep = "\n")
  attr(close_loop1a1, "params") <- c("R2", "new.R2", "Bpvalue_species")
  
  if(!nimble){
    close_loop1a2 <- paste("\n### total number of occupied stations for each species",
                           paste0("Ntot[", speciesIndex, "] <- sum(z[", speciesIndex, ", 1:", stationMax, "])"),
                           "\n### species is part of community?",
                           paste0("occt[", speciesIndex ,"] <- 1 - equals(Ntot[", speciesIndex ,"],0)"),
                           "\n", sep = "\n")
  } else {
    close_loop1a2 <- "# Total number of occupied and community membership indicator are not returned if nimble = TRUE"
  }
  attr(close_loop1a2, "params") <- NULL
  
  
  
  
  
  Nstations <- list()
  
  if(!is.null(occuIntercept_categ$fixed)) {
    
    # get number of stations in each richness category
    stations_richness_categories <- table(covariates[,occuIntercept_categ$fixed])
    names(stations_richness_categories) <- paste0("J", 1:length(stations_richness_categories))
    
    for(s_occu in 1:length(unique(covariates[,occuIntercept_categ$fixed]))){
      
      current_s1 <- names(stations_richness_categories)[s_occu]
      current_s2a <- paste0(stationMax, 1:(s_occu - 1), collapse = " + ")
      current_s2b <- paste0(stationMax, 1:s_occu, collapse = " + ")
      current_s3  <- paste0(stationMax, s_occu)
      
      
      if(s_occu == 1) Nstations[[s_occu]] <- paste(paste0("### Number of stations occupied (in each level of '", richnessCategories, "')"),
                                                   paste0("Nstations[", speciesIndex , ", ", s_occu, "] <- sum(z[", speciesIndex ,", 1:", current_s1, "])/", current_s1), 
                                                   sep = "\n")
      
      if(s_occu > 1 & s_occu != length(unique(covariates[,occuIntercept_categ$fixed]))) Nstations[[s_occu]] <- paste0("Nstations[", speciesIndex , ", ", s_occu, "] <- sum(z[", speciesIndex ,", (", current_s2a, " + 1):(", current_s2b ,")]) / ", current_s3)
      
      if(s_occu == length(unique(covariates[,occuIntercept_categ$fixed]))) Nstations[[s_occu]] <- paste0("Nstations[", speciesIndex , ", ", s_occu, "] <- sum(z[", speciesIndex ,", (", current_s2a, " + 1):", stationMax, "]) / (", stationMax, " - (", current_s2a, "))")      
    }
  } else {
    Nstations[[1]] <- paste("### Number of stations occupied",
                            paste0("Nstations[", speciesIndex ,"] <- sum(z[", speciesIndex ,", 1:", stationMax,"])/", stationMax), sep = "\n")
  }
  
  if(!nimble) {
    close_loop1b <- paste(
      paste(unlist(Nstations), collapse = "\n"),
      "\n", sep = "\n")
  } else {
    close_loop1b <- "# Number of stations occupied is not returned when nimble = TRUE\n"
  }
  
  
  if(!is.null(occuIntercept_categ$fixed)) {
    occ <- paste0("occ[", speciesIndex, ", ", 1:length(unique(covariates[,occuIntercept_categ$fixed])), "]<- 1 - equals(Nstations[i,", 1:length(unique(covariates[,occuIntercept_categ$fixed])), "], 0)")
  } else {
    occ <- paste0("occ[", speciesIndex ,"] <- 1 - equals(Nstations[", speciesIndex ,"], 0)")
  }
  
  if(!nimble){
    close_loop1c <- paste(    
      paste0("### Does species occur at all or not at each station", ifelse(!is.null(richnessCategories), paste0("(within each level of '", richnessCategories, "')"), "")),
      paste(occ, collapse = "\n"),
      "}    # close species loop",
      "\n", sep = "\n")
  } else {
    close_loop1c <- paste("}    # close species loop\n")
  }
  
  finish_residuals <- paste("###sum residuals over observed species", 
                            paste0("R3 <- sum(R2[1:", speciesMax, "])"),
                            paste0("new.R3 <- sum(new.R2[1:", speciesMax, "])"),
                            paste0("Bpvalue <- R3 > new.R3"),
                            "\n", sep = "\n")
  attr(finish_residuals, "params") <- c("R3", "new.R3", "Bpvalue")
  
  
  
  if(augment == "full") {
    finish_nspecies <- paste("### total number of species", 
                             ifelse(nimble, 
                                    "# not returned if nimble = TRUE", 
                                    paste0("Nspecies <- sum(occt[1:", speciesMax , "])")),
                             "",
                             "### Total number of unobserved species",
                             paste0("n0 <- sum(w[(nspec+1):(nspec+nz)])"),
                             "",
                             "### Total metacommunity size",
                             paste0("Ntotal <- sum(w[])"),
                             "\n", sep = "\n")
    if(nimble)  attr(finish_nspecies, "params") <- c("n0", "Ntotal")
    if(!nimble) attr(finish_nspecies, "params") <- c("Nspecies", "n0", "Ntotal")
    
    
    
  } else {
    finish_nspecies <- paste("### total number of species", 
                             ifelse(nimble, 
                                    "# not returned if nimble = TRUE", 
                                    paste0("Nspecies <- sum(occt[1:", speciesMax , "])")),
                             "\n", sep = "\n")
    if(!nimble) attr(finish_nspecies, "params") <- "Nspecies"
    if(nimble)  attr(finish_nspecies, "params") <- NULL
  } 
  
  
  
  
  if(!is.null(occuIntercept_categ$fixed) & !nimble) {
    finish_nspecies_categ <- paste(paste0("### number of species (in each level of '", richnessCategories, "')"),
                                   paste0("for(s_occu in 1:", length(unique(covariates[,occuIntercept_categ$fixed])), "){"), 
                                   paste0("Nspecies_", richnessCategories, "[s_occu] <- sum(occ[1:", speciesMax , ", s_occu])"),
                                   "}",
                                   "",
                                   "}",
                                   "", sep = "\n")
    attr(finish_nspecies_categ, "params") <- paste0("Nspecies_", richnessCategories)
  } else {
    finish_nspecies_categ <- paste("}", "", sep = "\n")
  }
  
  
  
  # Creating output ####
  
  ## combine model code ####
  model_text <- list(model_start, 
                     priors_intercept_occu,
                     priors_intercept_det,
                     
                     priors_omega,
                     
                     # detection covariates (site)
                     prior_det_header_fixed,
                     unlist(priors_sitecovs_det_fixed),
                     
                     prior_det_header_indep,
                     unlist(priors_sitecovs_det_indep),
                     
                     prior_det_header_ranef,
                     unlist(priors_sitecovs_det_ranef),
                     
                     prior_det_categ_header_fixed, 
                     unlist(priors_sitecovs_det_categ_fixed),
                     
                     prior_det_categ_header_ranef,
                     unlist(priors_sitecovs_det_categ_ranef),
                     
                     # detection covariates (occasion)
                     prior_obs_header_fixed,
                     unlist(priors_obscovs_det_fixed),
                     
                     prior_obs_header_ranef,
                     unlist(priors_obscovs_det_ranef),
                     
                     prior_obs_categ_header_fixed,
                     unlist(priors_obscovs_det_categ_fixed),
                     
                     prior_obs_categ_header_ranef,
                     unlist(priors_obscovs_det_categ_ranef),
                     
                     # occupancy covariates (site)
                     prior_occu_header_fixed,
                     unlist(priors_sitecovs_occu_fixed),
                     
                     prior_occu_header_indep,
                     unlist(priors_sitecovs_occu_indep),
                     
                     prior_occu_header_ranef,
                     unlist(priors_sitecovs_occu_ranef),
                     
                     prior_occu_categ_header_fixed,
                     unlist(priors_sitecovs_occu_categ_fixed),
                     
                     prior_occu_categ_header_ranef,
                     unlist(priors_sitecovs_occu_categ_ranef),
                     
                     prior_det_species_station_ranef,
                     unlist(priors_SpeciesStation_det_ranef),
                     
                     before_species_loop,
                     
                     # model
                     loop1, 
                     loop2,
                     loop3,
                     close_loop2,
                     close_loop1a1,
                     close_loop1a2,
                     close_loop1b,
                     close_loop1c,
                     finish_residuals, 
                     finish_nspecies,
                     finish_nspecies_categ)
  
  out <- list()
  out$model <- unlist(model_text)
  
  
  ## combine parameters to monitor  ####
  out$params <- c(unlist(sapply(model_text, attr, "params")), 
                  attr(priors_sitecovs_det_fixed, "params"),
                  attr(priors_sitecovs_det_indep, "params"),
                  attr(priors_sitecovs_det_ranef, "params"),
                  attr(priors_sitecovs_det_categ_fixed, "params"),
                  attr(priors_sitecovs_det_categ_ranef, "params"),
                  attr(priors_obscovs_det_fixed, "params"),
                  attr(priors_obscovs_det_ranef, "params"),
                  attr(priors_obscovs_det_categ_fixed, "params"),
                  attr(priors_obscovs_det_categ_ranef, "params"),
                  attr(priors_sitecovs_occu_fixed, "params"),
                  attr(priors_sitecovs_occu_indep, "params"),
                  attr(priors_sitecovs_occu_ranef, "params"),
                  attr(priors_sitecovs_occu_categ_fixed, "params"),
                  attr(priors_sitecovs_occu_categ_ranef, "params"),
                  attr(priors_SpeciesStation_det_ranef, "params"))
  
  ## combine inits  ####
  inits_out_tmp <- c(do.call(c, lapply(model_text[sapply(model_text, 
                                                         FUN = function(x) !is.null(attr(x, "inits")))], 
                                       attr, "inits")),
                     attr(priors_sitecovs_det_fixed, "inits"),
                     attr(priors_sitecovs_det_indep, "inits"),
                     attr(priors_sitecovs_det_ranef, "inits"),
                     attr(priors_sitecovs_det_categ_fixed, "inits"),
                     attr(priors_sitecovs_det_categ_ranef, "inits"),
                     attr(priors_obscovs_det_fixed, "inits"),
                     attr(priors_obscovs_det_ranef, "inits"),
                     attr(priors_obscovs_det_categ_fixed, "inits"),
                     attr(priors_obscovs_det_categ_ranef, "inits"),
                     attr(priors_sitecovs_occu_fixed, "inits"),
                     attr(priors_sitecovs_occu_indep, "inits"),
                     attr(priors_sitecovs_occu_ranef, "inits"),
                     attr(priors_sitecovs_occu_categ_fixed, "inits"),
                     attr(priors_sitecovs_occu_categ_ranef, "inits"),
                     attr(priors_SpeciesStation_det_ranef, "inits"))
  
  
  inits_list     <- inits_out_tmp[sapply(inits_out_tmp, class) == "list"]
  inits_not_list <- inits_out_tmp[sapply(inits_out_tmp, class) != "list"]
  
  
  
  # make inits function
  inits_fun <- function(x){ 
    c(lapply(inits_list, FUN = function(x) do.call(x[[1]], x[[2]])),
      inits_not_list)
  }
  
  out$inits_fun <- inits_fun
  
  
  
  # prepare model data  ####
  
  # effort_binary = 1/0 indicator for whether station was active or not (to ensure p = 0 when no effort)
  effort_binary <- as.matrix(!is.na(data_list$obsCovs[[effortCov]])) * 1   # replace NA with 0, other values with 1
  
  
  # create data list for jags.model
  if(!"ylist" %in% names(data_list)) stop("Didn't find ylist in data_list")
  y <- simplify2array(data_list$ylist)
  y <- aperm(y,c(3,1,2))
  
  # assign names to data augmentation species
  if(!is.null(augmentation)) {
    if(any(dimnames(y)[[1]] == "")) {
      which_no_name <- which(dimnames(y)[[1]] == "")
      dimnames(y)[[1]][which_no_name] <- paste("species_DA", 1:length(which_no_name), sep = "_")
    }
  }
  
  data.list <- list(
    y = y,
    effort_binary = effort_binary
  )
  
  
  # add site-occasion covariates
  if(!is.null(data_list$obsCovs)) {
    data.list <- c(data.list, data_list$obsCovs[names(data_list$obsCovs) %in% unlist(detCovsObservation)])
    data.list <- c(data.list, data_list$obsCovs[names(data_list$obsCovs) %in% unlist(detCovsObservation_categ)])
    
    # check if any observation covariate is character
    #if(any(sapply(data_list$obsCovs, typeof) == "character")){
    if(!is.null(unlist(detCovsObservation_categ))) {
      
      # if yes, add the corresponding "..._integer" matrix
      data.list <- c(data.list, data_list$obsCovs[grep("_integer$", names(data_list$obsCovs))])    # this does not include original covariate
    }
    
    if(effortCov %in% unlist(detCovsObservation)) {
      if(!effortCov %in% names(data.list)) {
        data.list[effortCov] <- data_list$obsCovs[effortCov]
      }
      data.list[[effortCov]] [effort_binary == 0] <- 0   # replace effort = NA with 0 (doesn't affect calculation of p because of effort_binary)
    }
  }
  
  # add data dimensions
  list_design <- list(M = speciesMax_value,
                      J = stationMax_value,
                      maxocc = occasionMax_value)
  
  names(list_design) <- c(speciesMax, 
                          stationMax, 
                          occasionMax)
  
  data.list <- c(data.list, list_design)
  
  # add site covariates
  if(length(covariates_categ) >= 1)   data.list <- c(data.list, as.list(covariates[covariates_categ]))
  if(length(covariates_numeric) >= 1) data.list <- c(data.list, as.list(covariates[covariates_numeric]))
  
  if(!is.null(occuIntercept_categ$fixed)) {
    tmp <- table(covariates[,occuIntercept_categ$fixed])
    names(tmp) <- paste0("J", 1:length(tmp))
    data.list <- c(data.list, tmp)
  }
  
  # add data augmentation information
  if(augment == "full"){
    data.list <- c(data.list, 
                   list(nz = nz,          # number of unseen species added to the detection history list
                        nspec = nspec))   # number of seen species
  }
  
  
  # for nimble, convert site covariate factors to numeric
  # the values will then be used for indexing the factor levels, and will not be interpreted as continuous values
  if(nimble){
    data.list <- lapply(data.list, FUN = function(x) {
      if(is.factor(x)) {
        as.numeric(x)
      } else {
        x
      }
    })
    data.list$all1row <- rep(1, times = occasionMax_value)
  }
  
  
  out$model_data <- data.list
  
  # save model text file
  if(!is.null(modelFile)) {
    sink(modelFile)
    cat(out$model, fill = FALSE, sep = "")
    sink()
    if(file.exists(modelFile)) {
      message(paste("Wrote model to", modelFile))
    } else {
      message(paste("Model could not be written to", modelFile))
    }
  }
  
  # create output commOccu object
  out2 <- new("commOccu",   
              modelText = out$model,
              params    = out$params,
              inits_fun = out$inits_fun,
              data      = out$model_data,
              input     = data_list,
              nimble    = nimble,
              modelFile = ifelse(!is.null(modelFile), modelFile, "undefined"),
              covariate_info = covariate_info
              )
  return(out2)
  
}



# Define commOccu class for output ####
#' commOccu objects
#'
#' @slot modelText JAGS model code as a character vector (made up of code chunks, use cat() to print)
#' @slot params Parameters to monitor in the model runs
#' @slot inits_fun Function to create start values for the MCMC chains. It being a function ensures different values in each chain
#' @slot data List with data needed to run the model (detection & effort matrices, site covariates, number of species / stations / occasions)
#' @slot input Input data_list (unchanged)
#' @slot nimble logical indicator for whether it is a Nimble model
#' @slot modelFile Path of the text file containing the model code
#' @slot covariate_info Data frame containing information about covariates. Only used internally in plot_* and predict methods
#'
#' @note 
#' The \code{data} slot is a list of model input data. While the exact content depends on function input, it can be summarized as:
#' 
#' \tabular{ll}{
#' \code{y} \tab array of detection histories. Dimensions are: y[species, station, occasion] \cr
#' \code{effort_binary} \tab matrix of binary (1/0) survey effort. Only used to ensure p = 0 when effort = 0. Dimensions are: effort_binary[station, occasion] \cr
#' \code{site-occasion covariates} \tab The required content of data_list$obsCovs as named matrices with dimensions [station, occasion] \cr
#' \code{site covariates} \tab The required columns of data_list$siteCovs as named vectors (length = number of stations) \cr
#' \code{M} \tab Number of species \cr
#' \code{J} \tab Number of stations \cr
#' \code{maxocc} \tab Number of occasions \cr
#' }
#' 
#' For categorical site-occasion covariates, an addition matrix containing an integer representation of the character matrix with suffix "_integer" is stored in the data slot.
#' 
#' @return \code{commOccu} object
#' 
#' @export
#' 
setClass("commOccu", 
         slots = c(modelText = "character",
                   params    = "character",
                   inits_fun = "function",
                   data      = "list",
                   input     = "list",
                   nimble    = "logical",
                   modelFile = "character", 
                   #call      = "list"
                   covariate_info = "data.frame"
                   )
)







# fit method ####


fit.commOccu <- function(object,
                         n.iter = 100,
                         thin = 1,
                         n.burnin = 0,
                         n.adapt = 0,
                         cores = 1,
                         chains = 3,
                         compile = TRUE,
                         WAIC	= FALSE,
                         quiet = FALSE,
                         ...) {
  
  if(thin == 0) stop("thin can't be 0")
  if(cores > 1 & isTRUE(object@nimble))    message(paste("nimble models will not run in parallel, cores =", cores, "will be ignored."))
  if(n.adapt != 0 & isTRUE(object@nimble)) message(paste("nimble models don't use n.adapt. It will be ignored."))
  
  if(isFALSE(object@nimble)){
    
    if(!file.exists(object@modelFile)) stop(paste("modelFile not found under", object@modelFile))
    
    if(isTRUE(WAIC)) warning("WAIC is only returned in Nimble models", immediate. = TRUE)
    
    #if(isTRUE(compile)) message("'compile' is meaningless for JAGS models")
    
    # JAGS sampling function
    wrapper<-function(a){
      mod <- rjags::jags.model(file = object@modelFile, 
                               data = object@data, 
                               inits = object@inits_fun(), 
                               n.chain=1, 
                               n.adapt=n.adapt,
                               quiet = quiet)
      
      out <- rjags::coda.samples(model = mod,
                                variable.names = object@params, 
                                n.iter	= n.iter, 
                                thin = thin)
      return(out)
    }
    
    ###run JAGS
    parallel <- ifelse(cores > 1, TRUE, FALSE)
    
    #suppressMessages({
    snowfall::sfInit(parallel = parallel, 
           cpus = cores)
    snowfall::sfLibrary("rjags", character.only = TRUE)
    snowfall::sfExportAll()
    snowfall::sfClusterSetupRNG()
    out <- snowfall::sfLapply(1 : chains, wrapper)
    
    snowfall::sfStop()
   # })
    
    # posterior summaries
    out_mcmclist <- coda::mcmc.list(lapply(out, FUN = function(x) coda::mcmc(x[[1]])))
    
    if(n.burnin >= 1) {
      out2 <- window(out_mcmclist, start=(n.burnin/thin))    # start=(n.burnin/thin) + 1?
      return(out2)
    } else {
      return(out_mcmclist)
    }
    
  }
  
  
  if(isTRUE(object@nimble)) {
    
    mod <- nimble::readBUGSmodel(object@modelFile,
                                 data = object@data,
                                 inits = object@inits_fun())
    
    
    myMCMC <- nimble::buildMCMC(mod, 
                                monitors = object@params,
                                thin = thin,
                                enableWAIC = WAIC,
                                print = isFALSE(quiet))
    
    if(compile) {
      
      mod_comp <- nimble::compileNimble(mod)
      
      compMCMC <- nimble::compileNimble(myMCMC, project = mod)
      
      out_mcmclist <- nimble::runMCMC(compMCMC,
                                      niter = n.iter, 
                                      nburnin = n.burnin,
                                      thin = thin,
                                      nchains = chains, 
                                      samplesAsCodaMCMC = TRUE,
                                      WAIC = WAIC,
                                      progressBar = isFALSE(quiet),
                                      ...) 
    }
    
    if(!compile) {
      out_mcmclist <- nimble::runMCMC(myMCMC,
                                      niter = n.iter, 
                                      nburnin = n.burnin,
                                      thin = thin,
                                      nchains = chains, 
                                      samplesAsCodaMCMC = TRUE,
                                      WAIC = WAIC,
                                      progressBar = isFALSE(quiet),
                                      ...) 
    }
    
    return(out_mcmclist)
    
    
  }
}

setGeneric("fit", function(object, ...){})


#' Fit a community (multi-species) occupancy model
#'
#' Convenience function for fitting community occupancy models (defined in a commOccu object) in JAGS or Nimble.
#'
#' @param object \code{commOccu} object
#' @param n.iter number of iterations to monitor
#' @param thin thinning interval for monitors
#' @param n.burnin   burnin length
#' @param n.adapt Length of adaptive phase
#' @param cores number of cores to utilize
#' @param chains number of MCMC chains to run
#' @param compile logical. If Nimble model, compile model with \code{\link[nimble]{compileNimble}} before running model?
#' @param WAIC logical. Return WAIC (only Nimble models)
#' @param quiet if TRUE messages and progress bar will be suppressed
#' @param ...     additional arguments to pass to \code{\link[nimble]{runMCMC}} (only relevant for Nimble)
#'
#' @details 
#' Models will be fit either in JAGS or Nimble, depending on the decision made in the \code{nimble} argument in \code{\link{communityModel}}.
#' 
#' For Nimble, compilation is strongly recommended for long model runs. Uncompiled models can run extremely slow. Compilation itself can take a while also, and requires that Rtools is available on the system.
#' 
#' It is a convenience function only which hides some of the configuration options. If you require more control over model fitting, you can run all steps individually. See vignette 5 for details. 
#'  
#' @return A coda::mcmc.list
#'
#' @export
#' 
setMethod("fit", signature(object = "commOccu"), 
          fit.commOccu)



# summary method ####

summary.commOccu <- function(object, ...) {
  cat(paste0("commOccu object (for ", ifelse(object@nimble, "Nimble", "JAGS"), ")\n\n"))
  dims <- dim(object@data$y)
  cat(paste(dims[1], "species, ",
            dims[2], "stations, ", 
            dims[3], "occasions\n"))
  
  cat(paste(sum(object@data$effort_binary, na.rm = T), "occasions with effort\n"))
  
  cat(paste("Number of detections (by species):", paste(range(apply(object@data$y, 1, sum, na.rm =T)), collapse = " - "), "\n\n"))
  
  if(exists("siteCovs", object@input)){
    
    cat(paste("Available site covariates:\n", paste(colnames(object@input$siteCovs), collapse = ", "), "\n\n"))
  
    cat(paste("Used site covariates:\n", paste(colnames(object@input$siteCovs) [attr(object@input$siteCovs, "in_model")], collapse = ", "), "\n\n"))
  }
  
  if(exists("obsCovs", object@input)) {
    cat(paste("Available site-occasion covariates:\n", paste(names(object@input$obsCovs), collapse = ", "), "\n\n"))
    
    # doesn't work yet - need an indicator somewhere for what obs covs are used by model
    #cat(paste("Used site-occasion covariates:\n", paste(names(object@input$obsCovs), collapse = ", "), "\n\n"))
  }
  
  
}



#' Summarize community occupancy model
#' 
#' Gives an overview of the number of species, stations and occasions in a \code{commOccu} object. Also returns covariates. 
#' 
#' The summary method is very basic and still work in progress. 
#'
#' @param object  \code{commOccu} object
#' @param ...  currently ignored
#'
#' @return Model summary printed to console
#' @export
#'
setMethod("summary", signature(object = "commOccu"), 
          summary.commOccu)



# other helper functions

# make table with information about covariates
get_cov_info <- function(cov,
                         keyword_nested = keyword_nested,
                         keyword_quadratic = keyword_quadratic,
                         data_list = data_list,
                         type,
                         submodel) {
  
  
  if(type == "obs")  item <- "obsCovs"
  if(type == "site") item <- "siteCovs"
  
  tmp <- unlist(cov)
  
  names(tmp)[grep("fixed", names(tmp))] <- "fixed"
  names(tmp)[grep("ranef", names(tmp))] <- "ranef"
  
  if(submodel == "occu"){
    names(tmp)[grep("independent", names(tmp))] <- "independent"
  }
  
  
  
  tmp_cov1  <- sapply(strsplit(tmp, split = "|", fixed = T), FUN = function(x) x[1])
  
  if(type == "obs")  cov_type  <- sapply(data_list[[item]][tmp_cov1], typeof)
  if(type == "site") cov_type  <- sapply(data_list[[item]][,tmp_cov1, drop = FALSE], class)
  
  cov_type2 <- ifelse(cov_type %in% c("numeric", "double", "integer"), "cont", 
                      ifelse(cov_type %in% c("character", "factor"), "categ", NA))
  

  tmp2 <- data.frame(param = rep("param", times = length(tmp)),
                     submodel = rep(submodel, times = length(tmp)),
                     covariate_type = item,
                     covariate = tmp,
                     data_type = cov_type2,
                     is_quadratic = endsWith(tmp, keyword_quadratic),
                     has_quadratic = paste0(tmp, keyword_quadratic) %in% tmp,
                     ranef = ifelse(names(tmp) == "ranef", T, F), 
                     ranef_nested = grepl(keyword_nested, tmp, fixed = TRUE),
                     ranef_cov = sapply(strsplit(tmp, split = "|", fixed = TRUE), FUN = function(x) ifelse(length(x) == 2, x[2], NA)),
                     independent = ifelse(names(tmp) == "independent", T, F)
  )
  
  

  effect_type <- ifelse(tmp2$ranef, "ranef", ifelse(tmp2$independent, "indep", "fixed"))
  
  tmp2$coef <- paste0(ifelse(tmp2$submodel == "det", "alpha", "beta"), ".",
                 ifelse(tmp2$covariate_type == "obsCovs", "obs.", ""), 
                 effect_type,     #ifelse(tmp2$ranef, "ranef", "fixed"), 
                 ".",
                 tmp2$data_type, ".",
                 gsub("[|+]", "_", tmp)
                 )
  
  rownames(tmp2) <- NULL
  return(tmp2)
}



# helper functions to create priors (both detection and occupancy)  ####


# change number of random numbers created (from default = 1)
modifyInits <- function(inits, n) {
  if(!is.na(n)) {
    inits[[2]][[1]] <- as.numeric(n)
  } 
  return(inits)
}


interceptPriors <- function(effect, 
                            type,
                            prior_list,
                            inits_list, 
                            speciesIndex,
                            speciesMax,
                            speciesMax_value){
  
  
  if(type == "detection") param <- "alpha"
  if(type == "occupancy") param <- "beta"
  
  
  if(effect == "ranef"){
    
    # comenclature 2: alpha0.mean
    param_names <- list(mean  = paste0(param, "0", ".mean"), 
                        tau   = paste0(param, "0", ".tau"),
                        sigma = paste0(param, "0", ".sigma"))
    
    priors_intercept <- paste(
      paste("##", type, "intercept estimate of community (community mean)"),
      
      paste0(param_names$mean, " ~ ", prior_list$dnorm),
      paste0(param_names$tau,  " ~ ", prior_list$dgamma),
      paste0(param_names$sigma, " <- sqrt(1 / ", param_names$tau, ")"),
      
      "\n", sep = "\n")
    
    attr(priors_intercept, "params") <- c(param_names$mean, 
                                          param_names$sigma)
    
    inits_tmp <- list(mean = inits_list$inits_runif_mean_low,
                      tau  = inits_list$inits_runif_tau)
    
    names(inits_tmp) <- c(param_names$mean, 
                          param_names$tau)
    
    attr(priors_intercept, "inits") <- inits_tmp
    
    code_formula <- paste0(param, "0[", speciesIndex, "]")
  } 
  
  if(effect == "fixed") {
    priors_intercept <- paste(
      paste("##", type, "intercept estimate of community (constant across species)"),
      paste0(param, "0 ~ ", prior_list$dnorm),
      "\n", sep = "\n")
    
    inits_tmp <- list(name = inits_list$inits_runif_mean_0)
    names(inits_tmp) <- paste0(param, "0")
    attr(priors_intercept, "inits") <- inits_tmp
    code_formula <- paste0(param, "0")
  }
  
  
  if(effect == "independent") {
    priors_intercept <- paste(
      paste("##", type, "intercept estimate of community (independent between species)"),
      paste0("for(", speciesIndex, " in 1:", speciesMax, ") {"),
      paste0(param, "0[", speciesIndex, "] ~ ", prior_list$dnorm),
      "}",
      "\n", sep = "\n")
    

    # change number of random init values to create (not 1, but n_species)
    inits_tmp <- list(modifyInits(inits_list$inits_runif_mean_0, 
                                  speciesMax_value))
    
    names(inits_tmp) <- paste0(param, "0")
    
    attr(priors_intercept, "inits") <- inits_tmp
    
    code_formula <- paste0(param, "0[", speciesIndex, "]")
    
  }
  
  return(list(prior    = priors_intercept, 
              formula  = code_formula))
}




fixedEffectPriors <- function(effect_names, 
                              type,
                              prior_list, 
                              stationIndex){
  
  if(type == "detection") param <- "alpha"
  if(type == "occupancy") param <- "beta"
  
  if(!is.null(effect_names)){
    priors_list <- list()
    
    for(effect_names_index in 1:length(effect_names)){
      priors_list[[effect_names_index]] <- paste(
        paste("# Covariate:", effect_names[effect_names_index]),
        paste0(param, ".fixed.cont.", effect_names[effect_names_index], " ~ ", prior_list$dnorm), 
        "\n", 
        sep = "\n")
    }
    attr(priors_list, "params") <- paste0(param, ".fixed.cont.", effect_names)
    
    attr(priors_list, "formula") <- paste0(" + ", param, ".fixed.cont.", effect_names, " * ", effect_names, "[", stationIndex, "]", collapse = "", sep = "")
    
  } else {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    attr(priors_list, "formula") <- ""
  }
  
  return(priors_list)
}




independentEffectPriors <- function(effect_names, 
                                    type,
                                    inits_list,
                                    prior_list, 
                                    stationIndex,
                                    speciesIndex,
                                    speciesMax,
                                    speciesMax_value){
  
  if(type == "detection") param <- "alpha"
  if(type == "occupancy") param <- "beta"
  
  if(!is.null(effect_names)){
    priors_list <- list()
    
    for(effect_names_index in 1:length(effect_names)){
      priors_list[[effect_names_index]] <- paste(
        paste("# Covariate:", effect_names[effect_names_index]),
        paste0("for(", speciesIndex, " in 1:", speciesMax, ") {"),
        paste0(param, ".indep.cont.", effect_names[effect_names_index], "[", speciesIndex, "] ~ ", prior_list$dnorm), 
        "}",
        "\n", 
        sep = "\n")
    }
    attr(priors_list, "params") <- paste0(param, ".indep.cont.", effect_names)
    
    attr(priors_list, "formula") <- paste0(" + ", param, ".indep.cont.", effect_names,"[", speciesIndex, "]",  " * ", effect_names, "[", stationIndex, "]", collapse = "", sep = "")
    
    
    # make list of parameters
    inits_tmp <- vector(mode = "list", length = length(effect_names))
    names(inits_tmp) <- c(paste0(param, ".indep.cont.", effect_names))
    
    
    for(i in 1:length(inits_tmp)){
      inits_tmp[[i]] <- modifyInits(inits_list$inits_runif_mean_0, 
                                    speciesMax_value)
    }
    
    attr(priors_list, "inits") <- inits_tmp
    
    
  } else {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    attr(priors_list, "formula") <- ""
  }
  
  return(priors_list)
}


randomEffectPriors <- function(effect_names, 
                               type,
                               covariates,
                               prior_list,
                               inits_list,
                               speciesIndex, 
                               speciesMaxNumber,
                               speciesMax,
                               stationIndex, 
                               keyword_nested = "+Species"){
  
  if(type == "detection") param <- "alpha"
  if(type == "occupancy") param <- "beta"
  
  effect_names2 <- gsub("|", "_", effect_names, fixed = TRUE)
  effect_names2 <- gsub("+", "_", effect_names2, fixed = TRUE)
  
  if(!is.null(effect_names)){
    priors_list <- list()
    species_draws <- list()
    ranef_index <- vector(mode = "character")
    current_cov2 <- vector(mode = "character")
    ranef_double_all <- vector()
    
    for(effect_names_index in 1:length(effect_names)){
      current_cov <- effect_names[effect_names_index]
      if(grepl("|", current_cov, fixed = TRUE)) {
        
        # this part needs a cleanup
        
        tmp <- unlist(strsplit(current_cov, split = "|", fixed = TRUE))
        current_cov <- gsub("|", "_", current_cov, fixed = TRUE)
        current_cov2[effect_names_index] <- tmp[1]
        if(length(tmp) == 2 & endsWith(tmp[2], keyword_nested)) {
          ranef_index[effect_names_index] <- paste0(speciesIndex, ", ", gsub(keyword_nested, "", tmp[2], fixed = TRUE), "[", stationIndex, "]")  
          ranef_double <- TRUE
          ranef_double_all[effect_names_index] <- TRUE
          attr(ranef_double_all, "nlevels")[effect_names_index] <- speciesMaxNumber
          current_cov <- gsub("+", "_", current_cov, fixed = TRUE)
        } else {
          ranef_index[effect_names_index] <- paste0(tmp[2], "[", stationIndex, "]")
          ranef_double <- FALSE
          ranef_double_all[effect_names_index] <- FALSE
        }
        effect_name_print <- effect_names[effect_names_index]
      } else {
        ranef_index[effect_names_index] <- speciesIndex
        current_cov2[effect_names_index] <- current_cov
        effect_name_print <- paste(current_cov, "Species", sep = "|")
        ranef_double <- FALSE
        ranef_double_all[effect_names_index] <- FALSE
      }
      
      mean.tmp  <- paste0(param, ".ranef.cont.", current_cov, ".mean", ifelse(ranef_double, paste0("[", speciesIndex, "]"), ""))
      tau.tmp   <- paste0(param, ".ranef.cont.", current_cov, ".tau", ifelse(ranef_double, paste0("[", speciesIndex, "]"), ""))
      sigma.tmp <- paste0(param, ".ranef.cont.", current_cov, ".sigma", ifelse(ranef_double, paste0("[", speciesIndex, "]"), ""))
      
      priors_list[[effect_names_index]] <- paste(
        paste("# Covariate:", effect_name_print),
        if(ranef_double) paste0("for(", speciesIndex, " in 1:", speciesMax, "){"),
        paste0(mean.tmp, " ~ ", prior_list$dnorm),
        paste0(tau.tmp, " ~ ", prior_list$dgamma),
        paste0(sigma.tmp, " <- sqrt(1 / ", tau.tmp, ")"),
        if(ranef_double) "}",
        "\n", 
        sep = "\n")
      
      # if only species random effect
      if(ranef_index[effect_names_index] == speciesIndex) {
        species_draws[[effect_names_index]] <- paste(paste("# continuous", type, "covariate with random effects:", effect_name_print), 
                                                     paste0(param, ".ranef.cont.", current_cov, "[", ranef_index[effect_names_index], "] ~ dnorm(", mean.tmp, ", ",  tau.tmp, ")"),
                                                     sep = "\n")
      } else {   
        
        if(ranef_double) {   # two random effects (species + another)
          
          index_letter <- paste0("index_", param, "_ranef_", current_cov)
          species_draws[[effect_names_index]] <- paste(paste("# continuous", type, "covariate with random effects:", effect_name_print), 
                                                       paste0("for(", index_letter, " in 1:", length(unique(covariates[, gsub(keyword_nested, "", tmp[2], fixed = TRUE)])), ") {"),
                                                       paste0(param, ".ranef.cont.", current_cov, "[", speciesIndex, ", ", index_letter, "] ~ dnorm(", mean.tmp, ", ", tau.tmp, ")"),
                                                       "}",
                                                       sep = "\n")
          
          
        } else {    # only other random effect (not species)
          index_letter <- paste0("index_", param, "_ranef_", current_cov)
          species_draws[[effect_names_index]] <- paste(paste("# continuous", type, "covariate with random effects:", effect_name_print), 
                                                       paste0("for(", index_letter, " in 1:", length(unique(covariates[, tmp[2]])), ") {"),
                                                       paste0(param, ".ranef.cont.", current_cov, "[", index_letter, "] ~ dnorm(", mean.tmp, ", ", tau.tmp, ")"),
                                                       "}",
                                                       sep = "\n")
        }
      }
    }
    
    attr(priors_list, "params") <- c(paste0(param, ".ranef.cont.", effect_names2, ".mean"),
                                     paste0(param, ".ranef.cont.", effect_names2, ".sigma"))
    
    
    # make list of parameters
    inits_tmp <- vector(mode = "list", length = length(effect_names2) * 2)
    names(inits_tmp) <- c(paste0(param, ".ranef.cont.", effect_names2, ".mean"),
                          paste0(param, ".ranef.cont.", effect_names2, ".tau"))
    

    
    for(i in 1:length(inits_tmp)){
      if(endsWith(names(inits_tmp)[i], "mean")) if(ranef_double_all[i]){
        inits_tmp[[i]] <- lapply(attr(ranef_double_all, "nlevels"), modifyInits, inits = inits_list$inits_runif_mean_0)[[i]]
      } else {
        inits_tmp[[i]] <- inits_list$inits_runif_mean_0
      }
      
      if(endsWith(names(inits_tmp)[i], "tau"))  if(ranef_double_all[i - length(effect_names)]) {
        inits_tmp[[i]] <- lapply(attr(ranef_double_all, "nlevels"), modifyInits, inits = inits_list$inits_runif_tau)[[i - length(inits_tmp) / 2]]
      } else {
        inits_tmp[[i]] <- inits_list$inits_runif_tau
      }
    }
    attr(priors_list, "inits") <- inits_tmp
    
    attr(priors_list, "formula") <- paste(" + ", param, ".ranef.cont.", effect_names2, "[", ranef_index, "] * ", current_cov2, "[", stationIndex, "]", collapse = "", sep = "")
    
    names(species_draws) <- paste0(param, ".ranef.cont.", effect_names2)
    attr(species_draws, "random_effect") <- ifelse(ranef_index == speciesIndex | (startsWith(ranef_index, speciesIndex) & endsWith(ranef_index, paste0("[", stationIndex, "]"))),
                                                   "species", "not_species")
    
    
    ################# CHECK LINE ABOVE !!! ########
    
  } else {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    attr(priors_list, "formula") <- ""
    species_draws <- paste("# continuous", type, "covariates: no random effect of species")
  }
  
  
  return(list(priors_list, 
              species_draws)) 
}



randomSpeciesStationEffectPriors <- function(doit,
                                             prior_list, 
                                             inits_list,
                                             speciesIndex,
                                             stationIndex) {
  
  if(doit) {
  param <- "alpha"
    
  # mean.tmp  <- 0
  tau.tmp   <- "alpha.speciesstation.ranef.tau" #paste0(param, ".ranef.cont.", current_cov, ".tau", ifelse(ranef_double, paste0("[", speciesIndex, "]"), ""))
  sigma.tmp <- "alpha.speciesstation.ranef.sigma" #paste0(param, ".ranef.cont.", current_cov, ".sigma", ifelse(ranef_double, paste0("[", speciesIndex, "]"), ""))
  
  param_name_site_species <- "alpha.speciesstation.ranef"
  
  priors_list <- list()
  
  priors_list[[1]] <- paste(
    #paste("# Random Species-station effect"),
    # if(ranef_double) paste0("for(", speciesIndex, " in 1:", speciesMax, "){"),
    
    
    
    # paste0(mean.tmp, " <- ", 0#prior_list$dnorm),
    paste0(tau.tmp, " ~ ", prior_list$dgamma),
    paste0(sigma.tmp, " <- sqrt(1 / ", tau.tmp, ")"),
    # if(ranef_double) "}",
    "\n", 
    sep = "\n")
  

  attr(priors_list, "params") <- c(sigma.tmp, param_name_site_species)
  
  
  inits_tmp <- vector(mode = "list", length = 1)
  names(inits_tmp) <- "alpha.speciesstation.ranef.tau"
  inits_tmp[[1]] <- inits_list$inits_runif_tau
    
  attr(priors_list, "inits") <- inits_tmp
  
  attr(priors_list, "formula") <- #paste(" + ",  "[", ranef_index, "] * ", current_cov2, "[", stationIndex, "]", collapse = "", sep = "")
    paste(" + ",  param_name_site_species, "[", speciesIndex, ", ", stationIndex, "]", collapse = "", sep = "")
  
  
  species_draws <- paste("# Random effect of species and station on detection probability:" ,
                         paste(param_name_site_species, "[", speciesIndex, ", ", stationIndex, "] ~ dnorm(0, alpha.speciesstation.ranef.tau)", collapse = "", sep = ""),
                         "\n", 
                         sep = "\n")
# beta.ranef.cont.habitat[i] ~ dnorm(beta.ranef.cont.habitat.mean, beta.ranef.cont.habitat.tau)")
  
  } 
  
  
  if(!doit) {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    attr(priors_list, "formula") <- ""
    species_draws <- paste("# No random effect of species and station on detection probability")
  }
  
  return(list(priors_list,
              species_draws))
  }




fixedEffectPriorsCateg <- function(effect_names, 
                                   covariates, 
                                   type,
                                   prior_list, 
                                   stationIndex){
  
  if(type == "detection"){
    param <- "alpha"
    type_short <- "det"
  }
  if(type == "occupancy"){
    param <- "beta"
    type_short <- "occu"
  }
  
  
  if(!is.null(effect_names)){
    priors_list <- list()
    attr(priors_list, "n_levels") <- rep(NA, times = length(effect_names))
    
    for(effect_names_index in 1:length(effect_names)){
      
      current_cov <- effect_names[effect_names_index]
      current_cov_values <- covariates[, current_cov]
      index_letter <- paste0("index_cat_ranef_", type_short, "_", current_cov)
      
      
      if(length(unique(current_cov_values)) == 1) stop(paste(type, "covariate", effect_names[effect_names_index], "(fixed effect) has only 1 factor level."), call. = FALSE)
      
      priors_list[[effect_names_index]] <- paste(
        paste("# Covariate:", current_cov),
        paste0(param, ".fixed.categ.", current_cov, "[1] <- 0"),
        paste0("for(", index_letter, " in 2:", length(unique(current_cov_values)), ") {"),
        paste0(param, ".fixed.categ.", current_cov, "[", index_letter, "] ~ ", prior_list$dnorm),
        "}",
        "\n", 
        sep = "\n")
      attr(priors_list, "n_levels")[effect_names_index] <- length(unique(current_cov_values))
    }
    attr(priors_list, "params") <- paste0(param, ".fixed.categ.", effect_names)
    attr(priors_list, "index_letters") <- paste0("index_cat_ranef_", type_short, "_", effect_names)
    
    inits_tmp <- vector(mode = "list", length = length(effect_names))
    names(inits_tmp) <- paste0(param, ".fixed.categ.", effect_names)
    
    for(n in 1:length(effect_names)) {
      inits_tmp[[n]][1] <- NA
      inits_tmp[[n]][2:attr(priors_list, "n_levels")[n]] <- rnorm(attr(priors_list, "n_levels")[n] - 1)
    }
    attr(priors_list, "inits") <- inits_tmp
    rm(inits_tmp)
    
    attr(priors_list, "formula") <- paste(" + ", param,".fixed.categ.", effect_names, "[",   effect_names, "[", stationIndex, "]", "]", 
                                          collapse = "", sep = "")
    
  } else {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    attr(priors_list, "formula") <- ""
  }
  return(priors_list)
}


randomEffectPriorsCateg <- function(effect_names, 
                                    covariates, 
                                    type,
                                    prior_list,
                                    speciesIndex, 
                                    stationIndex){
  
  if(type == "detection"){
    param <- "alpha"
    type_short <- "det"
  }
  if(type == "occupancy"){
    param <- "beta"
    type_short <- "occu"
  }
  
  if(!is.null(effect_names)){
    priors_list <- list()
    species_draws <- list()
    
    attr(priors_list, "n_levels") <- rep(NA, times = length(effect_names))
    
    for(effect_names_index in 1:length(effect_names)){
      
      current_cov <- effect_names[effect_names_index]
      current_cov_values <- covariates[, current_cov]
      index_letter <- paste0("index_cat_ranef_", type_short, "_", current_cov)
      
      if(length(unique(current_cov_values)) == 1) stop(paste(type, "covariate", effect_names[effect_names_index], "(random effect) has only 1 factor level." ), call. = FALSE)
      
      priors_list[[effect_names_index]] <- paste(
        paste("# Community mean effects of", current_cov),
        paste0(param, ".ranef.categ.", current_cov, ".mean", "[1] <- 0"),
        paste0(param, ".ranef.categ.", current_cov, ".tau", "[1] <- 0"),
        paste0(param, ".ranef.categ.", current_cov, ".sigma", "[1] <- 0"),     # is included in random effect of intercept already, hence 0
        "",
        paste0("for(", index_letter, " in 2:", length(unique(current_cov_values)), ") {"),
        paste0(param, ".ranef.categ.", current_cov, ".mean",  "[", index_letter, "] ~ ", prior_list$dnorm),   
        paste0(param, ".ranef.categ.", current_cov, ".tau",   "[", index_letter, "] ~ ", prior_list$dgamma),
        paste0(param, ".ranef.categ.", current_cov, ".sigma", "[", index_letter, "] <- sqrt(1 / ", param, ".ranef.categ.", current_cov, ".tau", "[", index_letter, "]", ")"),
        "}",
        "",
        sep = "\n")
      attr(priors_list, "n_levels")[effect_names_index] <- length(unique(current_cov_values))
      
      if(attr(priors_list, "n_levels")[effect_names_index] == 1) stop(paste("covariate", current_cov, "has only 1 unique value:", unique(current_cov_values)), call. = F)
      
      
      species_draws[[effect_names_index]] <- paste(ifelse(effect_names_index == 1, paste("# categorical", type, "covariates with random effects:"), ""),
                                                   paste0("", param, ".ranef.categ.", current_cov, "[", speciesIndex, ", 1] <- 0"), 
                                                   paste0("for (", index_letter, " in 2:",  length(unique(current_cov_values)), "){"),
                                                   paste0("", param, ".ranef.categ.", current_cov, "[", speciesIndex, ", ", index_letter, "] ~ dnorm(", param, ".ranef.categ.", current_cov, ".mean", "[", index_letter, "], ", param, ".ranef.categ.", current_cov, ".tau", "[", index_letter, "])"),
                                                   "}", sep = "\n")
    }
    
    attr(priors_list, "params") <- c(paste0(param, ".ranef.categ.", effect_names, ".mean"),
                                     paste0(param, ".ranef.categ.", effect_names, ".sigma"))
    
    attr(priors_list, "index_letters") <- paste0("index_cat_ranef_", type_short, "_", effect_names)
    
    inits_tmp <- vector(mode = "list", length = length(effect_names)  * 2)
    names(inits_tmp) <- c(paste0(param, ".ranef.categ.", effect_names, ".mean"),
                          paste0(param, ".ranef.categ.", effect_names, ".tau"))
    
    for(n in 1:length(effect_names)) {
      inits_tmp[[n]][1] <- NA
      inits_tmp[[n]][2:attr(priors_list, "n_levels")[n]] <- rnorm(attr(priors_list, "n_levels")[n] - 1)
      
      inits_tmp[[n + length(effect_names)]][1] <- NA
      inits_tmp[[n + length(effect_names)]][2:attr(priors_list, "n_levels")[n]] <- abs(rnorm(attr(priors_list, "n_levels")[n] - 1))
      
    }
    attr(priors_list, "inits") <- inits_tmp
    rm(inits_tmp)
    
    names(species_draws) <- paste0("", param, ".ranef.categ.", effect_names)
    
    attr(priors_list, "formula") <- paste(" + ", param, ".ranef.categ.", effect_names, "[", speciesIndex, ", ", effect_names, "[", stationIndex, "]", "]", 
                                          collapse = "", sep = "")
  } else {
    priors_list <- list("# < empty > \n\n")
    attr(priors_list, "params") <- NULL
    species_draws <- "# categorical occupancy covariates: no random effect of species"
    attr(priors_list, "formula") <- ""
  }
  
  
  
  return(list(priors_list, 
              species_draws))
}

Try the camtrapR package in your browser

Any scripts or data that you put into this service are public.

camtrapR documentation built on May 11, 2022, 5:18 p.m.