Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

local adaptation dir #36

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added local_adaptation/LAimg_greyscale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions local_adaptation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Local Adaptation NWF

The goal of this script is to serve as a baseline for exploration of local adaptation by quantative traits.

This is a simulation in which local adaptation is achieved through QTL mutations arising in a 20kb region of the 1e6bp genomic element, where we model *only* the additive effects of the QTLs. Neutral mutations may be overlaid for downstream analysis (say, using msprime), as they are not modeled in this example. Here, we borrow ideas from section `16.11` of the SLiM manual `v. 4.2.2` and combine new ideas presented in the rest of this repository.

`LAimg_greyscale.png` is the example greyscale image used in the current script, but this can be easily exchanged for another greyscale image. Greyscale is recommended.

An interesting aspect to this script is how we approach fitness scaling. Notice that we first measure local density, like in other examples, but then we also construct phenotypes and fitness effects from QTLs. The phenotypes of individuals are calculated as the sum of the `m2` mutations (QTL). So then, we have these two elements of fitness scaling:

first in line 77:
```
inds.fitnessScaling = 1 / (1 + RHO * competition);
Jrodriguez216 marked this conversation as resolved.
Show resolved Hide resolved
```
Where fitness is impacted by local density (competition). Competition here is *not* based on phenotype, and all individuals compete equally with other individuals in a given distance, regardless of phenotype.

and second in line 88:
```
inds.fitnessScaling = inds.fitnessScaling * qtl_fit;
```
where qtl_fit is defined as:
```
qtl_fit = dnorm(phenotype, optimum, SK);
qtl_opt = dnorm(0.0, 0.0, SK);
qtl_fit = qtl_fit/qtl_opt;
```
We rescale fitness by global max fitness so that the value for an individual at the optimum is `1.0`.

Additionally, we save parameters such as `phenotypes`, `optima`, and `ids`, along with all default parameters in the metadata of the output tree sequence. This can easily be modified in line 121.

One example of how we've used these values is by taking the difference between phenotype and optimum, for each individual, and averaging across individuals for a quantitative measure of local adaptation. We've then varied the default parameters to see how this measure of local adaptation shifts (ex. with a higher sigma_D, there is a higher difference in optimum & pheno).
146 changes: 146 additions & 0 deletions local_adaptation/localadaptation_nonWF.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
initialize() {

initializeSLiMModelType("nonWF");
initializeSLiMOptions(keepPedigrees=T, dimensionality="xy");
//initializeTreeSeq();

defaults = Dictionary(
"SEED", getSeed(),
"SD", 0.5, // sigma_D, dispersal distance
"SX", 0.3, // sigma_C, competition distance parameter
"SM", 0.3, // sigma_M, mate choice distance parameter
"SK", 0.7, // sigma_K, standard deviation of the fitness function
"K", 100, // carrying capacity per unit area
"M", 1e-8, // mutation rate
"LIFETIME", 4, // average life span
"WIDTH", 5.0, // width of the simulated area
"HEIGHT", 5.0, // height of the simulated area
"RUNTIME", 10000, // total number of ticks to run the simulation for
"env_image", "./LAimg_greyscale.png", // image representing environmental gradient
"R", 1e-8 // recombination rate
);

// Set up parameters with a user-defined function
setupParams(defaults);

initializeMutationRate(c(0.0, M, 0.0), c(4e5 - 1, 6e5 - 1, 1e6 - 1));
initializeMutationType("m1", 0.0, "f", 0.0); // neutral
initializeMutationType("m2", 0.5, "n", 0.0, 0.05); // QTL
m2.convertToSubstitution = F;

// Set up constants that depend on externally defined parameters
defineConstant("DEFAULTS", defaults);
defineConstant("FECUN", 1 / LIFETIME);
defineConstant("RHO", FECUN / ((1 + FECUN) * K));
defineConstant("PARAMS", defaults);

initializeGenomicElementType("g0", m2, 1); // id, mutationtype, proportion
initializeGenomicElementType("g1", m2, 1); // id, mutationtype, proportion
initializeGenomicElement(g0, 0, 4e5 - 1);
initializeGenomicElement(g1, 4e5, 6e5 - 1);
initializeGenomicElement(g0, 6e5, 1e6 - 1);
initializeRecombinationRate(R);

// competition
initializeInteractionType(1, "xy", reciprocal=T, maxDistance=SX * 3);
i1.setInteractionFunction("n", 1.0, SX);

// mate choice
initializeInteractionType(2, "xy", reciprocal=T, maxDistance=SM * 3);
i2.setInteractionFunction("n", 1.0, SM);
}

mutationEffect(m2) { return 1.0; }
1 first() {
sim.addSubpop("p1", asInteger(K * WIDTH * HEIGHT));
p1.setSpatialBounds(c(0.0, 0.0, WIDTH, HEIGHT));
p1.individuals.setSpatialPosition(p1.pointUniform(p1.individualCount));
p1.individuals.z = 0.0;
mapImage = Image(env_image);

// mapImage.floatK for greyscale images; greyscale is recommended.
map = p1.defineSpatialMap("map1", "xy", 1.0 - mapImage.floatK,
valueRange=c(0.0, 1.0), colors=c("red", "yellow"))
.rescale(-1, 1);
defineConstant("OPTIMUM", map);
}

early() {
// Disperse offspring
offspring = p1.subsetIndividuals(maxAge=0);
p1.deviatePositions(offspring, "reprising", INF, "n", SD);

// Measure local density and use it for density regulation
i1.evaluate(p1);
inds = sim.subpopulations.individuals;
competition = i1.localPopulationDensity(inds);
inds.fitnessScaling = 1 / (1 + RHO * competition);

// construct phenotypes and fitness effects from QTLs
phenotypes = inds.sumOfMutationsOfType(m2);
locations = inds.spatialPosition;
optima = OPTIMUM.mapValue(locations);

qtl_fit = dnorm(phenotypes, optima, SK);
qtl_opt = dnorm(0.0, 0.0, SK);
qtl_fit = qtl_fit/qtl_opt;

inds.fitnessScaling = inds.fitnessScaling * qtl_fit;

// color individuals according to phenotype
inds.color = OPTIMUM.mapColor(phenotypes);

if (p1.individualCount == 0) {
catn("Population went extinct! Ending the simulation.");
sim.simulationFinished();
}
}
Jrodriguez216 marked this conversation as resolved.
Show resolved Hide resolved

first() {
// preparation for the reproduction() callback
i2.evaluate(p1);
}

reproduction() {
litterSize = rpois(1, FECUN);
if (litterSize > 0){
mate = i2.drawByStrength(individual, 1);
if (mate.size())
subpop.addCrossed(individual, mate, count=litterSize);
}
}

RUNTIME late() {
// assign values to be preserved
inds = sortBy(sim.subpopulations.individuals, "pedigreeID");
phenotypes = inds.sumOfMutationsOfType(m2);
locations = inds.spatialPosition;
optima = OPTIMUM.mapValue(locations);

// record values and pedigree IDs in metadata
metadataDict = Dictionary("optima", optima, "phenotypes", phenotypes, "ids", inds.pedigreeID, "defaults", DEFAULTS);
//sim.treeSeqOutput("./localadaptationSLiM.trees", metadata=metadataDict);
sim.simulationFinished();
}

function (void)setupParams(object<Dictionary>$ defaults)
Jrodriguez216 marked this conversation as resolved.
Show resolved Hide resolved
{
if (!exists("PARAMFILE")) defineConstant("PARAMFILE", "./params.json");
if (!exists("OUTDIR")) defineConstant("OUTDIR", ".");
defaults.addKeysAndValuesFrom(Dictionary("PARAMFILE", PARAMFILE, "OUTDIR", OUTDIR));

if (fileExists(PARAMFILE)) {
defaults.addKeysAndValuesFrom(Dictionary(readFile(PARAMFILE)));
defaults.setValue("READ_FROM_PARAMFILE", PARAMFILE);
}

defaults.setValue("OUTBASE", OUTDIR + "/out_" + defaults.getValue("SEED"));
defaults.setValue("OUTPATH", defaults.getValue("OUTBASE") + ".trees");

for (k in defaults.allKeys) {
if (!exists(k))
defineConstant(k, defaults.getValue(k));
else
defaults.setValue(k, executeLambda(k + ";"));
}
}