@@ -1593,6 +1593,120 @@ late() { sim.killIndividuals(p1.subsetIndividuals(minAge=1)); }
15931593
15941594 SLiMAssertScriptSuccess (test_zygosity4);
15951595
1596+
1597+ // Complex multi-trait, multi-chrom models that will (hopefully) exercise all the different code paths for
1598+ // phenotype evaluation -- callbacks, different DES combinations, neutral and non-neutral, etc.
1599+ // The intention here is that these models don't test themselves; they are stochastic and there is no
1600+ // expectation regarding their outcome. Instead, _CheckPhenotypeForTrait() cross-checks them under DEBUG.
1601+
1602+ std::string complex_multi_1 = // this is an abbreviated version of test script complex_multi_test_1.slim
1603+ R"V0G0N(
1604+ initialize() {
1605+ defineConstant("I1", 5.0);
1606+ defineConstant("I2", -5.0);
1607+ defineConstant("OPT1", 10.0);
1608+ defineConstant("OPT2", 10.0);
1609+ defineConstant("SD1", 2.0);
1610+ defineConstant("SD2", 2.0);
1611+
1612+ initializeSex();
1613+
1614+ // multiplicative traits
1615+ popgen1T = initializeTrait("popgen1T", "mul", 1.01, 1.0, 0.01, directFitnessEffect=T); // will have a mix of dominance
1616+ popgen2T = initializeTrait("popgen2T", "mul", 1.01, 1.0, 0.01, directFitnessEffect=T); // will be independent dominance
1617+ n1T = initializeTrait("n1T", "mul", NULL, NULL, NULL, directFitnessEffect=T); // neutral with direct effect
1618+ n2T = initializeTrait("n2T", "mul", NULL, NULL, NULL, directFitnessEffect=F); // neutral with no direct effect
1619+
1620+ // additive traits
1621+ quant1T = initializeTrait("quant1T", "add", I1, 0.0, 0.01, directFitnessEffect=F); // will have a mix of dominance
1622+ quant2T = initializeTrait("quant2T", "add", I2, 0.0, 0.01, directFitnessEffect=F); // will be independent dominance
1623+ n3T = initializeTrait("n3T", "add", NULL, NULL, NULL, directFitnessEffect=F); // non-neutral with no direct effect
1624+
1625+ // quant1T / quant2T will be demanded in script; popgen1T / popgen2T / n1T will be demanded because they have direct effects
1626+ // calculation of popgen2T and quant2T should be extremely efficient since they are independent dominance
1627+ // calculation of n1T should be omitted entirely; SLiM should detect that it is neutral, and not even set phenotype values
1628+ // n2T and n3T should not be demanded, and should thus never be calculated by SLiM, which we can check in script
1629+
1630+ // mutation types
1631+ initializeMutationType("m1", 0.4, "f", 0.0); // neutral for all traits
1632+
1633+ initializeMutationType("m2", 0.4, "e", 0.001); // beneficial for the popgen traits
1634+ m2.setEffectDistributionForTrait(c(n1T, n2T), "f", 0.0); // neutral DES for the neutral traits
1635+ m2.setEffectDistributionForTrait(c(quant1T, quant2T), "n", 0.0, 0.1); // unbiased normal DES for the additive traits
1636+
1637+ initializeMutationType("m3", 0.4, "g", -0.001, 1.0); // deleterious for the popgen traits
1638+ m3.setEffectDistributionForTrait(c(n1T, n2T), "f", 0.0); // neutral DES for the neutral traits
1639+ m3.setEffectDistributionForTrait(c(quant1T, quant2T), "n", 0.0, 0.1); // unbiased normal DES for the additive traits
1640+
1641+ c(m2,m3).setEffectDistributionForTrait(n3T, "n", -5.0, 0.5); // very biased and wide DES for n3T
1642+
1643+ // set up independent dominance for popgen2T and quant2T; note that setting this for m1 should be unnecessary (it is neutral)
1644+ c(m1,m2,m3).setDefaultDominanceForTrait(c(popgen2T, quant2T), NAN);
1645+
1646+ // prevent converting to substitutions for m2 and m3; once shifting the baseline offset is supported, this will not be needed
1647+ c(m2,m3).convertToSubstitution = F;
1648+
1649+ initializeGenomicElementType("g1", m1, 1.0); // neutral
1650+ initializeGenomicElementType("g2", 1:3, c(2, 1, 1)); // mixture
1651+
1652+ ids = 1:5;
1653+ symbols = c(1, 2, "X", "Y", "MT");
1654+ lengths = rdunif(5, 1e7, 2e7);
1655+ types = c("A", "A", "X", "Y", "H");
1656+ names = c("A1", "A2", "X", "Y", "MT");
1657+
1658+ for (id in ids, symbol in symbols, length in lengths, type in types, name in names)
1659+ {
1660+ initializeChromosome(id, length, type, symbol, name);
1661+ initializeMutationRate(1e-7);
1662+ initializeRecombinationRate(1e-8);
1663+
1664+ if (id == 1)
1665+ initializeGenomicElement(g1); // autosome 1 is pure neutral, using only m1
1666+ else
1667+ initializeGenomicElement(g2); // autosome 2 is a mix, using m1 / m2 / m3
1668+ }
1669+ }
1670+
1671+ // set random dominance effects for the popgen1T and quant1T traits
1672+ // other effects are generated as specified by the mutation type DES
1673+ mutation(m2) {
1674+ mut.popgen1TDominance = runif(1);
1675+ mut.quant1TDominance = runif(1);
1676+ return T;
1677+ }
1678+ mutation(m3) {
1679+ mut.popgen1TDominance = runif(1);
1680+ mut.quant1TDominance = runif(1);
1681+ return T;
1682+ }
1683+
1684+ 1 late() {
1685+ sim.addSubpop("p1", 20);
1686+ }
1687+
1688+ 1: late() {
1689+ inds = sim.subpopulations.individuals;
1690+ sim.demandPhenotype(NULL, c(sim.quant1T, sim.quant2T));
1691+ fitnessEffect_q1 = dnorm(inds.quant1T, OPT1, SD1) / dnorm(0.0, 0.0, SD1);
1692+ fitnessEffect_q2 = dnorm(inds.quant2T, OPT2, SD2) / dnorm(0.0, 0.0, SD2);
1693+ inds.fitnessScaling = fitnessEffect_q1 * fitnessEffect_q2;
1694+ }
1695+
1696+ 2: first() {
1697+ inds = sim.subpopulations.individuals;
1698+
1699+ // check that traits that do not require calculation remain uncalculated
1700+ //if (!all(isNAN(inds.n1T))) stop("n1T was calculated unnecessarily"); // the smarts for this are not yet implemented!
1701+ if (!all(isNAN(inds.n2T))) stop("n2T was calculated unnecessarily");
1702+ if (!all(isNAN(inds.n3T))) stop("n3T was calculated unnecessarily");
1703+ }
1704+
1705+ 50 late() { }
1706+ )V0G0N" ;
1707+
1708+ SLiMAssertScriptSuccess (complex_multi_1);
1709+
15961710 std::cout << " _RunMultitraitTests() done" << std::endl;
15971711}
15981712
0 commit comments