-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathgen3sis_main.R
311 lines (266 loc) · 13.8 KB
/
gen3sis_main.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# Copyright (c) 2020, ETH Zurich
#' @title gen3sis: General Engine for Eco-Evolutionary Simulations
#' @name gen3sis
#' @description Contains an engine for spatially-explicit eco-evolutionary mechanistic models with a modular implementation and several support functions. It allows exploring the consequences of ecological and macroevolutionary processes across realistic or theoretical spatio-temporal landscapes on biodiversity patterns as a general term.
#' @references O. Hagen, B. Flück, F. Fopp, J.S. Cabral, F. Hartig, M. Pontarp, T.F. Rangel, L. Pellissier. (2021). gen3sis: A general engine for eco-evolutionary simulations of the processes that shape Earth’s biodiversity. PLoS biology
#' @details Gen3sis is implemented in a mix of R and C++ code, and wrapped into an R-package. All high-level functions that the user may interact with are written in R, and are documented via the standard R / Roxygen help files for R-packages. Runtime-critical functions are implemented in C++ and coupled to R via the Rcpp framework. Additionally, the package provides several convenience functions to generate input data, configuration files and plots, as well as tutorials in the form of vignettes that illustrate how to declare models and run simulations.
#' @seealso \code{\link{create_input_config}} \code{\link{create_input_landscape}} \code{\link{run_simulation}} \code{\link{plot_summary}}
#' @keywords programming IO iteration methods utilities
#' @concept gen3sis modeling eco-evolutionary macroevolution macroecology mechanisms
#' @examples
#' \dontrun{
#'
#' # 1. Load gen3sis and all necessary input data is set (landscape and config).
#'
#' library(gen3sis)
#'
#' # get path to example input inside package
#' datapath <- system.file(file.path("extdata", "WorldCenter"), package = "gen3sis")
#' path_config <- file.path(datapath, "config/config_worldcenter.R")
#' path_landscape <- file.path(datapath, "landscape")
#'
#' # 2. Run simulation
#'
#'sim <- run_simulation(config = path_config, landscape = path_landscape)
#'
#' # 3. Visualize the outputs
#'
#' # plot summary of entire simulation
#' plot_summary(sim)
#'
#' # plot richness at a given time-step
#' # this only works if species is saved for this time-step
#' landscape_t_150 <- readRDS(file.path(datapath,
#' "output", "config_worldcenter", "landscapes", "landscape_t_150.rds"))
#' species_t_150 <- readRDS(file.path(datapath,
#' "output", "config_worldcenter", "species", "species_t_150.rds"))
#' plot_richness(species_t_150, landscape_t_150)
#'
#' }
#' @docType package
#' @useDynLib gen3sis, .registration = TRUE
#' @importFrom Rcpp sourceCpp
#' @import Matrix
NULL
#' Run a simulation in gen3sis and return a summary object possibly saving outputs and plots to the output folder
#'
#' @details This function runs a simulation with defined landscape and config objects.
#' Possibly plot and save specified outputs as defined in the end_of_timestep_observer function inside the config object
#' @param config configuration file for the simulation or configuration object derived from a config file
#' @param landscape directory where the all_geo_hab and distance_matrices reside
#' @param output_directory directory for the simulation output
#' @param timestep_restart set the start time time-step.
#' If timestep_restart=NA (default), start at the oldest available landscape.
#' If timestep_restart="ti", start from the last available time-step.
#' If a number "x", start at time-step x (e.g. timestep_restartstart=6)
#' @param save_state save the internal state of the simulation for restarts.
#' If save_state=NA (default), do not save any internal state of the simulation.
#' If save_state="all", save all time-step.
#' If save_state="last", saves only last time-step.
#' If a vector, saves the desired time-steps (e.g. save_state=c(1,3,5))
#' @param call_observer call observer functions.
#' If call_observer="all" (default), call all time-steps.
#' If call_observer=NA, calls the start and end times.
#' If a number "X", call call_observer at x time-steps equally spaced between start and end steps.
#' For example, on a simulation with start time of 1 and end time of 20, call_observer=1 calls the observer function at time-steps 1, 11 and 20.
#' @param enable_gc enable gc in case of memory shortages
#' @param verbose integer value (i.e. 0, 1 ,2 or 3).
#' If verbose=0, no printed statement.
#' If verbose=1 (default), print time-step progress.
#' If verbose=2, enable additional progress outputs regarding current time-step.
#' If verbose=3, enable additional information from within modules
#'
#' @return a summary object containing a minimal summary on simulation and dynamics progress (alive, speciations, extinctions) as well as useful simulation data
#'
#' @importFrom utils packageVersion write.table
#'
#' @example inst/examples/run_simulation_help.R
#' @seealso \code{\link{plot_summary}} \code{\link{create_input_config}} \code{\link{create_input_landscape}}
#' @export
run_simulation <- function(config = NA,
landscape = NA,
output_directory = NA,
timestep_restart = NA,
save_state = NA,
call_observer = "all",
enable_gc = FALSE,
verbose = 1){
#----------------------------------------------------#
####### User defined variables (config.R) ############
#----------------------------------------------------#
system_time_start <- Sys.time() #Starting timer
directories <- prepare_directories(config_file = config,
input_directory = landscape,
output_directory = output_directory)
if(is.na(config)[1]){
stop("please provide either a config file or a config object")
} else if (is(config, "gen3sis_config")){
config[["directories"]] <- directories
} else if (is(config, "character")){
file.copy(config, directories$output)
config <- create_input_config(config_file = config)
config[["directories"]] <- directories
} else {
stop("this is not a known config, please provide either a config file or a config object")
}
if(!verify_config(config)){
stop("config verification failed")
}
val <- list("data" = list(),
"vars" = list(),
"config" = config)
val$config <- complete_config(val$config)
val$config$gen3sis$general$verbose <- verbose
#val$config$gen3sis$version <- "1.1"
#val$config$gen3sis$nickname <- "Quintessenced"
# #---------------------------------------------------------#
# ###### ATTRIBUTE ANCESTOR DISTRIBUTION (simulation.R) #####
# #---------------------------------------------------------#
val <- setup_inputs(val$config, val$data, val$vars)
val <- setup_variables(val$config, val$data, val$vars)
val <- setup_landscape(val$config, val$data, val$vars)
# conceptually the result of the initialization is the "end" of a previous timestep
val$data$landscape$id <- val$data$landscape$id + 1
val <- init_attribute_ancestor_distribution(val$config, val$data, val$vars)
# #---------------------------------------------------#
# ##### SIMULATION START #####
# #---------------------------------------------------#
# #---------------------------------------------------#
# ##### Init simulation #####
# #---------------------------------------------------#
val <- init_simulation(val$config, val$data, val$vars)
val <- init_summary_statistics(val$data, val$vars , val$config)
#--------------------------------------------#
######## Call observer to plot or save #######
#--------------------------------------------#
### when to call the observer
if (is.na(call_observer)){
save_steps <- c(val$config$gen3sis$general$start_time,val$config$gen3sis$general$end_time)
} else if (call_observer=="all"){
save_steps <- val$config$gen3sis$general$start_time:val$config$gen3sis$general$end_time
} else {
steps <- as.integer(call_observer) + 2
save_steps <- ceiling(seq(val$config$gen3sis$general$start_time,
val$config$gen3sis$general$end_time,
length.out = steps))
}
# # when to save the species data. +1 is added to tf for matters of timeps jumps between ti and tn
val$vars$save_steps <- save_steps
val$vars$steps <- val$config$gen3sis$general$start_time:val$config$gen3sis$general$end_time
#
#
#
if(!is.na(timestep_restart)){
val <- restore_state(val, timestep_restart)
}
for(ti in val$vars$steps){ #loop over time steps
# set to zero every new time-step!
val$vars$n_new_sp_ti <- 0
val$vars$n_ext_sp_ti <- 0
val$vars$n_sp_added_ti <- 0
# update ti inside val$vars
val$vars$ti <- ti
# #----------------------------------------#
# ######## loop setup (simulation.R) #######
# #----------------------------------------#
if(verbose>=2){
cat("loop setup \n")
}
val <- setup_landscape(val$config, val$data, val$vars)
val <- restrict_species(val$config, val$data, val$vars)
#val <- loop_setup_geo_dist_m_ti(val$config, val$data, val$vars)
val <- setup_distance_matrix(val$config, val$data, val$vars)
# #----------------------------------------------------------#
# ######## loop speciation (simulation.R) #######
# #----------------------------------------------------------#
if(verbose>=2){
cat("speciation \n")
}
val <- loop_speciation(val$config, val$data, val$vars)
# updates to take into account new species
val <- update1.n_sp.all_geo_sp_ti(val$config, val$data, val$vars)
val <- update2.n_sp_alive.geo_sp_ti(val$config, val$data, val$vars)
# #---------------------------------------------#
# ######## loop dispersal #######
# #---------------------------------------------#
if(verbose>=2){
cat("dispersal \n")
}
val <- loop_dispersal(val$config, val$data, val$vars)
# #----------------------------------------------------------#
# ######## loop evolution #######
# #----------------------------------------------------------#
if(verbose>=2){
cat("evolution \n")
}
val <- loop_evolution(val$config, val$data, val$vars)
# #--------------------------------------------------------#
# ######## loop ecology #######
# #--------------------------------------------------------#
if(verbose>=2){
cat("ecology \n")
}
val <- loop_ecology(val$config, val$data, val$vars)
# # #-------------------------------------------------------------------#
# # ######## end of time-step loop variable update (simulation.R) #######
# # #-------------------------------------------------------------------#
if(verbose>=2){
cat("end of loop updates \n")
}
#
#
# #------------------------------------------------------------------#
# ######## update loop steps variable (simulation.R) #######
# ######## !!! check with end of time-step variable update !!! #######
# #------------------------------------------------------------------#
val$vars$n_sp_alive <- sum(sapply(val$data$all_species, function(sp){ifelse(length(sp[["abundance"]]), 1, 0) }))
val$vars$n_sp <- length(val$data$all_species)
val <- update_extinction_times(val$config, val$data, val$vars)
if (verbose>=1){
cat('step =', ti, ', species alive =', val$vars$n_sp_alive, ', species total =', val$vars$n_sp, '\n')
}
if(val$vars$ti %in% val$vars$save_steps){
call_main_observer(val$data, val$vars, val$config)
}
val <- update_summary_statistics(val$data, val$vars, val$config)
save_val(val, save_state)
# abort conditions
if( val$vars$n_sp_alive >= val$config$gen3sis$general$max_number_of_species ) {
val$vars$flag <- "max_number_species"
print("max number of species reached, breaking loop")
break
}
if( val$vars$flag == "max_number_coexisting_species") {
print("max number of coexisting species reached, breaking loop")
break
}
}# close loop steps
#
if(verbose>=0 & val$vars$flag=="OK"){
cat("Simulation finished. All OK \n")
} else if(verbose>=0 & val$vars$flag=="max_number_species") {
cat("Simulation finished. Early abort due to exceeding max number of species")
} else if (verbose>=0 & val$vars$flag=="max_number_coexisting_species") {
cat("Simulation finished. Early abort due to exceeding max number of co-occuring species")
}
# #------------------------------------------------------------------#
# ######## update phylo with survival info (simulation.R) #######
# #------------------------------------------------------------------#
val <- update.phylo(val$config, val$data, val$vars)
# #------------------------------------------------------------------#
# ######## write phylogeny (internal_functions.R) #######
# #------------------------------------------------------------------#
write.table(val$data$phy, file = file.path(val$config$directories$output, "phy.txt"), sep="\t")
write_nex(phy=val$data$phy, label="species", file.path(output_location=val$config$directories$output, "phy.nex"))
# #------------------------------------------------------------------#
# ######## prepare and save summaries (internal_functions.R) #######
# #------------------------------------------------------------------#
system_time_stop <- Sys.time()
total_runtime <- difftime(system_time_stop, system_time_start, units = "hours")[[1]]
write_runtime_statisitics(val$data, val$vars, val$config, total_runtime)
sgen3sis <- make_summary(val$config, val$data, val$vars, total_runtime, save_file=TRUE)
if(verbose >= 1){
cat("Simulation runtime:", total_runtime, "hours\n")
}
return(sgen3sis)
}