Thanks for your replies to my post.
I had chosen to attach the SLiM script instead of pasting a huge amount of text, and I naively thought that all the files that I had attached in my original message were visible to you. Actually, this is the first time I post something in Google Groups ever, sorry about that. Since I don't know how to paste my graphics inline, I'll try to figure out how to share my plots here with you so that we're on the same page.
I understand the idea of having a burn-in period, but I'm not sure it should make a difference for the case I'm exploring. This is a simple tracking of the probability of fixation of a single allele (m1), whose fixation should be affected only by drift as 1) it is neutral, and 2) the neighboring mutations of the region are neutral. Taking as reference the predicted time until fixation of a single allele, in my simulations m1 allele is getting fixated earlier, which implies a smaller effective population size. In principle, the marker allele m1 of my code shouldn't be lost by mutation, as mutations in SLiM are stacked, nor be gained at any other time point except for cycle 1. As for the point of having a WF implemented as a nonWF, I just to wanted to clarify that that's exactly what I'm implementing. The calcFST() function doesn't calculate the F-statistic in the way I need, as that's a paired calculation between 2 sets of genomes, whereas I'm calculating FST for a metapopulation of more than 2 subpopulations, so I extended that function to apply it over a multiple subpopulations case.
/*
Given two haploid genomes, the number of segregating sites between the two, divided by the length of the genomic region, is pi, the average nucleotide heterozygosity, which is the standard measure for the genetic diversity in a population. The expected value of pi is 4*Ne*mu for a pure neutral model.
This is an extension of calcPairHeterozygosity function, which allows to select a specific set of mutations for such estimation of pi
*/
function (f$)calcPairHeterozygosityMut(object<Genome>$ genome1, object<Genome>$ genome2, [Ni$ start = NULL], [Ni$ end = NULL], [No<MutationType> mutationTypes=NULL],[logical$ infiniteSites = T])
{
if (community.allSpecies.length() > 1)
{
species = unique(c(genome1.individual.subpopulation.species, genome2.individual.subpopulation.species), preserveOrder=F);
if (species.length() != 1)
stop("ERROR (calcPairHeterozygosityMut()): genome1 and genome2 must belong to the same species.");
}
else
{
species = community.allSpecies;
}
if (!isNULL(mutationTypes)){
muts1 = c();
muts2 = c();
for (mtype in mutationTypes){
muts1 = c(muts1, genome1.mutations[genome1.mutations.mutationType == mtype]);
muts2 = c(muts2, genome2.mutations[genome2.mutations.mutationType == mtype]);
}
}
else{
muts1 = genome1.mutations;
muts2 = genome2.mutations;
}
length = species.chromosome.lastPosition + 1;
// handle windowing
if (!isNULL(start) & !isNULL(end))
{
if (start > end)
stop("ERROR (calcPairHeterozygosityMut()): start must be less than or equal to end.");
m1pos = muts1.position;
m2pos = muts2.position;
muts1 = muts1[(m1pos >= start) & (m1pos <= end)];
muts2 = muts2[(m2pos >= start) & (m2pos <= end)];
length = end - start + 1;
}
else if (!isNULL(start) | !isNULL(end))
{
stop("ERROR (calcPairHeterozygosityMut()): start and end must both be NULL or both be non-NULL.");
}
// do the calculation
unshared = setSymmetricDifference(muts1, muts2);
if (!infiniteSites)
unshared = unique(unshared.position, preserveOrder=F);
return size(unshared) / length;
}
/*
metapopHeterozygosity estimates the heterozygosity (pi) in a metapopulation by calculating the pi over a random sample. Note that this function normally uses a random sample of individuals, of a size supplied in the optional parameter sampleSize; but if sampleSize is equal to the number of individuals , it will instead compute pi exactly. For large metapopulations, that will of course be much slower than using a sample.
*/
function (f$)metapopHeterozygosity(o<Subpopulation> subpops, [No<MutationType>$ mt=NULL], [Ni$ start = NULL], [Ni$ end = NULL], [i$ sampleSize=100])
{
if (sampleSize >= sum(subpops.individualCount))
sampledIndividuals = subpops.individuals;
else
sampledIndividuals = sample(subpops.individuals, sampleSize, replace=F);
pi_total = 0;
for (individual in sampledIndividuals)
{
genomes = individual.genomes;
individual_pi = calcPairHeterozygosityMut(genomes[0], genomes[1], start, end, mt);
pi_total = pi_total + individual_pi;
}
return pi_total / sampleSize;
}
/*
calcMetaFST is an extension of the native calcFST function to estimate FST of a metapopulation
*/
function (f$)calcMetaFST(o<Subpopulation> subpops, [No<MutationType> mt = NULL], [Ni$ start = NULL], [Ni$ end = NULL])
{
if (subpops.length() == 0)
stop("ERROR (calcMetaFST()): subpops must be non-empty.");
if (sum(subpops.genomes.length()) == 0)
stop("ERROR (calcMetaFST()): genome vector must be non-empty.");
if (community.allSpecies.length() > 1)
{
species = unique(subpops.species, preserveOrder=F);
if (species.length() != 1)
stop("ERROR (calcMetaFST()): all genomes must belong to the same species.");
if (!isNULL(muts))
if (!all(species == muts.mutationType.species))
stop("ERROR (calcMetaFST()): all mutations must belong to the same species as the genomes.");
}
else
{
species = community.allSpecies;
}
// handle mutation type
if (isNULL(mt))
muts = species.mutations;
else {
muts = c();
for (mtype in mt){
muts = c(muts, species.mutationsOfType(mtype));
}
if (size(muts) == 0)
return 0.0;
}
// handle windowing
if (!isNULL(start) & !isNULL(end))
{
if (start > end)
stop("ERROR (calcMetaFST()): start must be less than or equal to end.");
mpos = muts.position;
muts = muts[(mpos >= start) & (mpos <= end)];
length = end - start + 1;
}
else if (!isNULL(start) | !isNULL(end))
{
stop("ERROR (calcMetaFST()): start and end must both be NULL or both be non-NULL.");
}
// do the calculation
mean_p = 0.0;
H_s = 0.0;
non_empty_demes = 0;
for (subpop in subpops)
{
if (subpop.genomes.length() == 0)
next;
non_empty_demes = non_empty_demes + 1;
ps_p = subpop.genomes.mutationFrequenciesInGenomes(muts);
mean_p = mean_p + ps_p;
H_s = H_s + 2.0 * ps_p * (1.0 - ps_p);
}
mean_p = mean_p / non_empty_demes;
H_s = H_s / non_empty_demes;
H_t = 2.0 * mean_p * (1.0 - mean_p);
meanH_s = mean(H_s);
meanH_t = mean(H_t);
if (size(meanH_s) == 0)
meanH_s = 0.0;
if (size(meanH_t) == 0)
meanH_t = 0.0;
if (meanH_s == meanH_t)
fst = 0.0;
else
fst = 1.0 - mean(H_s) / mean(H_t);
return fst;
}
// set up a simple neutral nonWF simulation
initialize() {
initializeSLiMModelType("nonWF");
defineConstant("N", dsize);
// carrying capacity for each deme
defineConstant("nd", ndemes);
// number of demes
defineConstant("mig_rate", m); // migration rate
defineConstant("meanls", K); // mean litter size for each reproduction event
defineConstant("L",100000); // chromosome length
defineConstant("A",1000); // locus where the marker mutation (m1) will land
// m1 mutation type: neutral=
initializeMutationType("m1", 0.5, "f", 0.0);
m1.convertToSubstitution = T;
m1.color = "red";
// m2 mutation type: neutral
initializeMutationType("m2", 0.5, "f", 0.0);
m2.convertToSubstitution = T;
// g1 genomic element type
initializeGenomicElementType("g1", m2, 1.0);
// uniform chromosome of length 100 kb with uniform recombination
initializeGenomicElement(g1, 0, L-1);
initializeRecombinationRate(rho*1e-10);
initializeMutationRate(mut*1e-10);
defineGlobal("m1Fixed",0);
if (exists("subdir"))
defineGlobal("parentDir", paste(dir,subdir,sep="/"));
else
defineGlobal("parentDir",dir);
}
1 first(){
sim.setValue("a_m1(t)",NULL);
}
2: first(){
sim.setValue("a_m1(t)",sum(sim.subpopulations.individuals.countOfMutationsOfType(m1))/(sum(sim.subpopulations.individualCount) * 2) + m1Fixed);
}
reproduction() {
// group competition (i.e., pick winning demes)
subpops = sim.subpopulations;
parent_demes = seqAlong(subpops);
for (pd in seqAlong(parent_demes)) {
p = parent_demes[pd];
psubpop = subpops[p];
if (psubpop.individualCount > 0){
litterSize = asInteger(round(mean(rpois(N,meanls))))*psubpop.individualCount;
parents = sample(psubpop.individuals, 2*litterSize, replace = T);
for (i in seqLen(litterSize)) {
psubpop.addCrossed(parents[2*i], parents[2*i+1]);
}
}
}
// disable this callback so it's only run once per cycle, not once per individual
self.active = 0;
}
1 early() {
// create an initial set of nd subpopulations, each with N individuals
for (i in seqLen(nd)){
sim.addSubpop(i, N);
}
// introduce marker mutation at 50% frequency
nmuts = integerDiv(sim.subpopulations.genomes.size(),2);
target = sample(sim.subpopulations.genomes, nmuts);
target.addNewDrawnMutation(m1, A);
}
// provide density-dependent selection and migration
early() {
subpops = sim.subpopulations;
inds = subpops.individuals;
// make generations non-overlapping by killing off adults
ages = inds.age;
// safe instantaneous elimination of individuals, SLiM 4 style
sim.killIndividuals(inds[ages > 0]);
inds = subpops.individuals;
//measure m1 allele frequency in newborns, i.e. exactly after killing adults
defineGlobal("m1Fixed",sum(sim.substitutions.mutationType == m1));
sim.setValue("n_m1(t)",sum(subpops.individuals.countOfMutationsOfType(m1))/(sum(subpops.individualCount) * 2) + m1Fixed);
numMigrants = rbinom(1, inds.size(), mig_rate);
if (numMigrants) {
migrants = sample(inds, numMigrants);
currentSubpopID =
migrants.subpopulation.id;
newSubpopID = rdunif(migrants.size(), 0, nd-2);
// ensure new deme is different from current one
newSubpopID = asInteger(newSubpopID < currentSubpopID) * newSubpopID
+ asInteger(newSubpopID >= currentSubpopID) * (newSubpopID + 1);
// move in bulk
for (subpop in subpops)
subpop.takeMigrants(migrants[newSubpopID ==
subpop.id]);
}
// post-migration density-dependent fitness scaling for each subpop
for (subpop in subpops)
subpop.fitnessScaling = N / subpop.individualCount;
}
late() {
defineGlobal("m1Fixed",sum(sim.substitutions.mutationType == m1));
}
1 late(){
log = community.createLogFile(paste(parentDir,paste(paste(basename,"HetFST",sep="_"),"log",sep="."),sep="/"), logInterval=5);
log.addCycle();
log.addCustomColumn("N_T(t)", "sum(sim.subpopulations.individualCount);");
log.addCustomColumn("Neutral_muts","sim.countOfMutationsOfType(m2);");
log.addCustomColumn("genome_Het(t)","metapopHeterozygosity(sim.subpopulations, m2, sampleSize=sum(sim.subpopulations.individualCount));");
log.addCustomColumn("m1_Het(t)","metapopHeterozygosity(sim.subpopulations, m1, A, A, sampleSize=sum(sim.subpopulations.individualCount));");
log.addCustomColumn("a_m1(t)","sim.getValue('a_m1(t)');"); // frequency of altruistic allele in adults
log.addCustomColumn("n_m1(t)","sim.getValue('n_m1(t)');"); // frequency of altruistic allele in newborns
log.addCustomColumn("m1(t)","sum(sim.subpopulations.individuals.countOfMutationsOfType(m1))/(sum(sim.subpopulations.individualCount) * 2) + m1Fixed;"); // frequency of altruistic allele in surviving juveniles
log.addCustomColumn("genome_Fst(t)","calcMetaFST(sim.subpopulations);");
log.addCustomColumn("m1_Fst(t)","calcMetaFST(sim.subpopulations,m1,A,A);");
}
50000 late() {
sim.outputFull(paste(parentDir,paste(paste(basename,"simFull",sep="_"),"txt",sep="."),sep="/"));
sim.outputFixedMutations(paste(parentDir,paste(paste(basename,"simFixed",sep="_"),"txt",sep="."),sep="/"));
sim.simulationFinished();
}