#################################################################################################################################################### # R code to simulate the dynamic model communities used in the study # "Temporal population variability in local forest communities has mixed effects on tree species richness across a latitudinal gradient" # by Fung, T., Chisholm, R.A., ..., Zimmerman, J. ##################################################################################################################################################### #################################################################################################################################### # The code below simulates the model commmunity conceptualized in Fig. S1 in the Supporting Information. # This model was used to assess bias in the two metrics of temporal population variability considered (see Appendix S2). #################################################################################################################################### # The function "model_sim_1" simulates species dynamics for model communities with no immigration and different values of the per-capita # speciation probability, as specified by the vector "nus". Each community has "J" individuals, and dynamics are simulated starting from # 1 species, for "t_max" time-steps. The first species has a fitness value that is drawn randomly from a lognormal distribution with mean 1 # and variance "A". In each time-step, with probability 1/"tau", the fitness of every species in the model is redrawn randomly from the # lognormal distribution. The simulations are initiated with the random seed number "seed". # # The output is a list with three elements: # (i) The first element "mean_S_Vec" is a vector of the mean number of species over the last "t_thres" time-steps, for each community. # (ii) The second element "mean_deltaN_Vec" is a vector of the first metric of temporal population variability (referred to as "Metric 1" in Appendix S2), # for a subsample of "J_subsample" individuals in each community. This is calculated using species dynamics in the last "t_thres" time-steps, with the # number of time-steps in a census interval defined by "t_calc_deltaN". # (iii) The third element "mean_deltaN2_Vec" is a vector of the second metric of temporal population variability (referred to as "Metric 2" in Appendix S2), # for a subsample of "J_subsample" individuals in each community. This is calculated using species dynamics in the last "t_thres" time-steps. # # This function is used to generate results to draw Figs. S2-S5 in Appendix S2. model_sim_1 <- function(seed,J,nus,A,tau,t_max,t_thres,t_calc_deltaN,J_subsample){ set.seed(seed) # "mu_lognormal" and "sigma_lognormal" are the mean and s.d. of the lognormal distribution of fitnesses. # "mu" and "sigma" are the mean and s.d. of the corresponding normal distribution. mu_lognormal = 1 sigma_lognormal = sqrt(A) mu = log((mu_lognormal^2)/sqrt((mu_lognormal^2)+(sigma_lognormal^2))) sigma = sqrt(log(1+((sigma_lognormal^2)/(mu_lognormal^2)))) n_nus = length(nus) # Create empty vectors for holding output values from the model simulations. # "mean_S_Vec" is vector with ith entry corresponding to the mean number of species over the last "t_thres" time-steps, for the ith community. # "mean_deltaN_Vec" is vector with ith entry corresponding to the first metric of temporal population variability, calculated using species dynamics over the last "t_thres" time-steps, for a subsample of "J_subsample" individuals in the ith community. # "mean_deltaN_per_N1_class_count_Mat" is a matrix with the ith row and jth column showing the number of census intervals with at least one species with initial abundance (abundance at the start of a census interval) j, # considering the last "t_thres" time-steps for a subsample of "J_subsample" individuals in the ith community. These are used to calculate entries in "mean_deltaN_per_N1_class_Mat". # "mean_deltaN_per_N1_class_Mat" is a matrix with the ith row and jth column showing the mean temporal population variability (calculated using eq. 1 in the main text) # for species with initial abundance (abundance at the start of a census interval) j, for a subsample of "J_subsample" individuals in the ith community. These are used to calculate entries in "mean_deltaN2_Vec". # "mean_deltaN2_Vec" is vector with ith entry corresponding to the second metric of temporal population variability, calculated using species dynamics over the last "t_thres" time-steps, for a subsample of "J_subsample" individuals in the ith community. mean_S_Vec = numeric(n_nus) mean_deltaN_Vec = numeric(n_nus) mean_deltaN_per_N1_class_count_Mat = matrix(0,n_nus,J) mean_deltaN_per_N1_class_Mat = matrix(0,n_nus,J) mean_deltaN2_Vec = numeric(n_nus) # Loop over the values of per-speciation probability in vector "n_nus". for(i in 1:n_nus){ # "counter1" keeps track of the number of census intervals during the last "t_thres" time-steps. counter1 = 0 nu = nus[i] cat('nu = ',nu,'\n',sep='') # "N_Vec" is the vector of species abundances (in a model community). # Start with 1 species with "J" individuals. N_Vec = rep(J,1) # NVec_calc_deltaN_start is the vector of species abundances at the start of a census interval. It is used in calculating entries in "mean_deltaN_Vec" and "mean_deltaN2_Vec". NVec_calc_deltaN_start = N_Vec # "f_Vec" is the vector of fitnesses for each species. f_Vec = rlnorm(1,meanlog=mu,sdlog=sigma) # "S" is the total number of species corresponding to "N_Vec". S = length(N_Vec) # Loop over "t_max" time-steps for ith community. for(t in 1:t_max){ if(t%%1e5==0){ cat('t = ',t,'\n',sep='') } # Redraw species fitnesses with probability 1/"tau". ran1 = runif(1) if(ran1<(1/tau)){ f_Vec = rlnorm(length(N_Vec),meanlog=mu,sdlog=sigma) } if(t==t_thres-t_calc_deltaN){ NVec_calc_deltaN_start = N_Vec } # If "t" is equal or greater to "t_thres", then update entries in "mean_S_Vec", "mean_deltaN_Vec", "mean_deltaN_per_N1_class_count_Mat" and "mean_deltaN_per_N1_class_Mat" accordingly. # After updating entries, entries in "N_Vec" and "f_Vec" corresponding to extinct species are removed. if(t>=t_thres){ mean_S_Vec[i] = mean_S_Vec[i]+S if(t%%t_calc_deltaN==0){ counter1 = counter1+1 if(J==J_subsample){ NVec_minus_NVec_calc_deltaN_start = abs(N_Vec-NVec_calc_deltaN_start) zero_ind = which(NVec_calc_deltaN_start==0) if(length(zero_ind)>0){ NVec_calc_deltaN_start = NVec_calc_deltaN_start[-zero_ind] NVec_minus_NVec_calc_deltaN_start = NVec_minus_NVec_calc_deltaN_start[-zero_ind] } mean_deltaN_Vec[i] = mean_deltaN_Vec[i]+mean(NVec_minus_NVec_calc_deltaN_start) dat1 = data.frame(NVec_calc_deltaN_start,NVec_minus_NVec_calc_deltaN_start) mean_deltaN_per_N1_class = tapply(dat1$NVec_minus_NVec_calc_deltaN_start,dat1$NVec_calc_deltaN_start,mean) names_mean_deltaN_per_N1_class = as.numeric(names(mean_deltaN_per_N1_class)) vals_mean_deltaN_per_N1_class = as.numeric(mean_deltaN_per_N1_class) mean_deltaN_per_N1_class_Mat[i,names_mean_deltaN_per_N1_class] = mean_deltaN_per_N1_class_Mat[i,names_mean_deltaN_per_N1_class]+vals_mean_deltaN_per_N1_class table_NVec_calc_deltaN_start = table(NVec_calc_deltaN_start) names_table_NVec_calc_deltaN_start = as.numeric(names(table_NVec_calc_deltaN_start)) mean_deltaN_per_N1_class_count_Mat[i,names_table_NVec_calc_deltaN_start] = mean_deltaN_per_N1_class_count_Mat[i,names_table_NVec_calc_deltaN_start]+1 }else{ NVec_calc_deltaN_start_sppID_Vec = rep(1:length(NVec_calc_deltaN_start),NVec_calc_deltaN_start) table_NVec_calc_deltaN_start_sub = table(sample(NVec_calc_deltaN_start_sppID_Vec,J_subsample,replace=FALSE)) names_NVec_calc_deltaN_start_sub = as.numeric(names(table_NVec_calc_deltaN_start_sub)) vals_NVec_calc_deltaN_start_sub = as.numeric(table_NVec_calc_deltaN_start_sub) N_Vec_sppID_Vec = rep(1:length(N_Vec),N_Vec) table_NVec_sub = table(sample(N_Vec_sppID_Vec,J_subsample,replace=FALSE)) names_NVec_sub = as.numeric(names(table_NVec_sub)) vals_NVec_sub = as.numeric(table_NVec_sub) S_sub = length(vals_NVec_calc_deltaN_start_sub) mean_S_Vec[i] = mean_S_Vec[i]+S_sub match_ind = match(names_NVec_calc_deltaN_start_sub,names_NVec_sub) match_ind_NA_ind = which(is.na(match_ind)) match_ind_noNA_ind = which(!is.na(match_ind)) NVec_minus_NVec_calc_deltaN_start_sub = numeric(length(vals_NVec_calc_deltaN_start_sub)) NVec_minus_NVec_calc_deltaN_start_sub[match_ind_noNA_ind] = abs(vals_NVec_calc_deltaN_start_sub[match_ind_noNA_ind]-vals_NVec_sub[match_ind[match_ind_noNA_ind]]) mean_deltaN_Vec[i] = mean_deltaN_Vec[i]+mean(abs(vals_NVec_calc_deltaN_start_sub[match_ind_noNA_ind]-vals_NVec_sub[match_ind[match_ind_noNA_ind]])) if(length(match_ind_NA_ind)>0){ NVec_minus_NVec_calc_deltaN_start_sub[match_ind_NA_ind] = abs(vals_NVec_calc_deltaN_start_sub[match_ind_NA_ind]) } dat1 = data.frame(vals_NVec_calc_deltaN_start_sub,NVec_minus_NVec_calc_deltaN_start_sub) mean_deltaN_per_N1_class = tapply(dat1$NVec_minus_NVec_calc_deltaN_start_sub,dat1$vals_NVec_calc_deltaN_start_sub,mean) names_mean_deltaN_per_N1_class = as.numeric(names(mean_deltaN_per_N1_class)) vals_mean_deltaN_per_N1_class = as.numeric(mean_deltaN_per_N1_class) mean_deltaN_per_N1_class_Mat[i,names_mean_deltaN_per_N1_class] = mean_deltaN_per_N1_class_Mat[i,names_mean_deltaN_per_N1_class]+vals_mean_deltaN_per_N1_class mean_deltaN_per_N1_class_count_Mat[i,vals_NVec_calc_deltaN_start_sub] = mean_deltaN_per_N1_class_count_Mat[i,vals_NVec_calc_deltaN_start_sub]+1 } # Remove entries in "N_Vec" and "f_Vec" corresponding to extinct species. extinct_ind = which(N_Vec==0) if(length(extinct_ind)>0){ N_Vec = N_Vec[-extinct_ind] f_Vec = f_Vec[-extinct_ind] } NVec_calc_deltaN_start = N_Vec } } # Randomly choose an individual to die. death_ind = sample(1:length(N_Vec),1,prob=N_Vec) N_Vec[death_ind] = N_Vec[death_ind]-1 # If a species goes extinct, update "S", "N_Vec" and "f_Vec" accordingly. if(N_Vec[death_ind]==0){ S = S-1 if(t0) N_Vec_init_census_pos = N_Vec_init_census[N_Vec_init_census_pos_ind] N_Vec_init_pos = N_Vec[N_Vec_init_census_pos_ind] NVecinitcensuspos_NVecinitpos_divide_DeltaT = abs(N_Vec_init_census_pos-N_Vec_init_pos)/DeltaT_Vec_censuses[census_sim_ind] mean_DeltaN = mean_DeltaN+mean(NVecinitcensuspos_NVecinitpos_divide_DeltaT) N_Vec_all_censuses[,census_sim_ind+1] = N_Vec census_sim_ind = census_sim_ind+1 B_census_sim_ind = Bs_tot_rarefied[census_sim_ind] D_census_sim_ind = Ds_tot_rarefied[census_sim_ind] B_over_D_census_sim_ind = Bs_over_Ds[census_sim_ind] D_sim = 0 B_sim = 0 N_Vec_init_census = N_Vec } } if(census_sim_ind<=(n_censuses-1)){ # Randomly choose an individual to die. # Update "N_Vec" accordingly. death_ind = sample(1:length(N_Vec),1,prob=N_Vec) N_Vec[death_ind] = N_Vec[death_ind]-1 D_sim = D_sim+1 # If a species goes extinct, update "S". if(N_Vec[death_ind]==0){ S = S-1 } # Number of births is determined by "B_over_D_census_sim_ind". # Randomly choose identity of newborn individuals. # Update "N_Vec" accordingly. if(D_sim==D_census_sim_ind){ n_births = B_census_sim_ind-B_sim }else{ if((B_sim/D_sim)0){ prob_birth = (N_Vec*f_vec)/sum(N_Vec*f_vec) for(o in 1:n_births){ birth_ind = sample(1:length(N_Vec),1,prob=prob_birth) N_Vec[birth_ind] = N_Vec[birth_ind]+1 } } B_sim = B_sim+n_births }else{ # After the census period, total number of individuals remains fixed at whatever value it had at the end of the census period. # Randomly choose an individual to die. # Update "N_Vec" accordingly. death_ind = sample(1:length(N_Vec),1,prob=N_Vec) N_Vec[death_ind] = N_Vec[death_ind]-1 # If a species goes extinct, update "S". if(N_Vec[death_ind]==0){ S = S-1 } # Randomly choose a replacement individual, via a local birth. # Update "N_Vec" accordingly. prob_birth = (N_Vec*f_vec)/sum(N_Vec*f_vec) birth_ind = sample(1:length(N_Vec),1,prob=prob_birth) N_Vec[birth_ind] = N_Vec[birth_ind]+1 } } mean_DeltaN = mean_DeltaN/(n_censuses-1) S_fin = S # Using "N_Vec_all_censuses", "mean_cdf" is computed. S1 = nrow(N_Vec_all_censuses) temp_cor_mat = matrix(0,S1,S1) mean_N_spp = rowMeans(N_Vec_all_censuses) sd_N_spp = sqrt(rowMeans(N_Vec_all_censuses^2)-(mean_N_spp^2)) for(i in 1:S1){ for(j in 1:S1){ Ns_sp_i = as.numeric(N_Vec_all_censuses[i,]) Ns_sp_j = as.numeric(N_Vec_all_censuses[j,]) mean_N_sp_i = mean_N_spp[i] mean_N_sp_j = mean_N_spp[j] sd_N_sp_i = sd_N_spp[i] sd_N_sp_j = sd_N_spp[j] if(max(sd_N_sp_i,sd_N_sp_j)>0){ temp_cor_ij = (1/n_censuses)*(1/(sd_N_sp_i*sd_N_sp_j))*sum((Ns_sp_i-mean_N_sp_i)*(Ns_sp_j-mean_N_sp_j)) }else{ temp_cor_ij = NA } temp_cor_mat[i,j] = temp_cor_ij } } temp_cor_mat_numeric = as.numeric(temp_cor_mat) temp_cor_mat_numeric_noNA = temp_cor_mat_numeric[which(!is.na(temp_cor_mat_numeric))] temp_cor_cdf = ecdf(temp_cor_mat_numeric_noNA) xs = seq(-1,1,length.out=1e3) mean_cdf = temp_cor_cdf(xs) return(list(mean_DeltaN,mean_cdf,S_init,S_fin)) }