bodkan/slendr

Can we handle "failed to generate child after 1 million attempts" a bit more gracefully?

bodkan opened this issue · 2 comments

Just reported by @mkiravn

The following code:

### Demonstration
### set up simulation
mating_distance <- c(0.01)
competition_distance <- c(0.2)
sigma <- 1
dispersal_function <- "brownian"
map <- world(
  xrange = c(-20, 20), # min-max longitude
  yrange = c(-20, 20), # min-max latitude
  landscape = "blank"
)

pop <- population("POP",
                  time = 1,
                  N = 100,
                  center = c(0,0),
                  radius = 20,
                  map = map,
                  mating = mating_distance,
                  competition = competition_distance,
                  dispersal_fun = dispersal_function,
                  dispersal = sigma)
# compile and run the model
model <- compile_model(
  populations = pop,
  generation_time = 1,
  sim_length = 1000, # a forward in time model in slendr needs length of the sim
  resolution = 1, # resolution in "distance units per pixel"
  # how can we control this distribution?
  path = "~/Desktop/test-model",
  overwrite = TRUE, force=T
)

# let's sample individuals in the "present"
samples <- schedule_sampling(model, times = 1000, list(pop, 5))

# simulate
slim(
  model, sampling = samples,
  sequence_length = 1, # simulate only a single locus
  recombination_rate = 0,
  method = "batch", # change to "gui" to execute the model in SLiMgui
  random_seed = 7,
  verbose = FALSE,
  coalescent_only = TRUE, # remember all individuals- not just nodes
  burnin = 0,
)

gives us this error in SLiM:

// Initial random seed:                                                                                     
2007395403233

// RunInitializeCallbacks():
SEED: 7
initializeSLiMOptions(keepPedigrees = T, dimensionality = 'xy');
initializeInteractionType(0, "xy", reciprocal=T, maxDistance=0.2);
initializeInteractionType(1, "xy", reciprocal=T, maxDistance=0.01);
initializeTreeSeq();
initializeMutationType(0, 0.5, "f", 0);
initializeGenomicElementType(1, m0, 1);
initializeGenomicElement(g1, 0, 0);
initializeMutationRate(0);
initializeRecombinationRate(0);

// Starting run at generation <start>:
1 

Generation 1: starting the simulation
Generation 1: creating POP(p0)
Generation 1: updating map for POP(p0)
Generation 1: distributing individuals of POP(p0)
ERROR (Population::EvolveSubpopulation): failed to generate child after 1 million attempts; terminating to avoid infinite loop.

Error on script line 467, character 4:

    return sim.interactionTypes[2 * subpop.id + 1].strength(individual);
    ^^^^^^
Error: Unfortunately SLiM terminated before a tree sequence was saved.
See the above for an indication of where things could have gone wrong.

SLiM exit status: 1
Message: Resource temporarily unavailable

In situations like this, the error is caused by the mating distance being too small, so individuals can't find mates.

The error from SLiM (to which we're trying to direct the user's attention by the "see the above") says ERROR (Population::EvolveSubpopulation): failed to generate child after 1 million attempts; terminating to avoid infinite loop.. This might not be super helpful to a novice user.

Maybe I can look through the log output for the instance of this exact error and report something more concrete, like "There was an issue with the specification if individual interaction. Please take a look at the SLiM output above and check the value of your mating distance."

Or are there other instances where the same error would pop up? What if there is not a single pixel where an individual could be placed? I don't think a case like this can pass through the slendr compilation phase (we're checking that some pixels are habitable).

Just need to be careful not to confuse the poor user even further.

Reporting some summary statistics of number of possible mates (e.g., mean, median, range of number of other individuals within the mating choice distance) might help?

Hey @petrelharp, thanks for the suggestion. This is a great idea.

I should definitely add an option to the slim() function for reporting some realtime statistics (debug = TRUE|FALSE?). Adding a suggestion to the error message above that the user can switch this option on to get a better idea as to what's going wrong by repeating the run with debug = TRUE would help a lot I think.