cacatoo
Version:
Building, exploring, and sharing spatially structured models
420 lines (349 loc) • 22.4 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cacatoo manual</title><link rel="icon" type="image/png" href="images/favicon.png" />
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/menu.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'>
<script src="scripts/jquery.js"></script>
<script src="scripts/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) -->
<script src="scripts/all.js"></script> <!-- Include other libraries (concattenated in 1 file) -->
<script src="PoS_classes.js"></script>
<script>
/*-----------------------Start user-defined code ---------------------*/
let sim;
// PARAMETERS FOR FITNESS DEFINITION
var init_es = 10 // es = essential --- if even a single one is missing = 0.0 fitness
var init_ne = 0 // ne = non-essential --- for each non-essential genes gain "non_essential_gene_boon" fitness
var init_nc = 30
var init_tra_rate = 0.85
var transposon_fitness_cost = 0.005
var non_essential_gene_boon = 0.1
// PARAMETERS FOR ECOLOGY
var death_rate = 0.02
var non = 100
// PARAMETERS FOR EVOLUTION
var gene_inactivation_rate = 0.001
var gene_deletion_rate = 0.001
var gene_duplication_rate = 0.001
var phi_mutation_rate = 0.01
// PARAMETERS FOR EDNA POOL
var degr_rate_edna = 0.02
var diff_rate_edna = 0.01
// PARAMETERS FOR TRANSPOSON DYNAMICS
var probability_TE_induced_damage = 1.0
var uptake_from_pool = 0.02
var jump_rate = 0.02
var size = 150
var scale = 2
var address_query = {}
location.search.substr(1).split("&").forEach(function (item) { address_query[item.split("=")[0]] = item.split("=")[1] })
if (address_query.size) size = Math.min(address_query.size, 500)
if (size > 300) scale = 1
else if (size > 200) scale = 2
function cacatoo() {
let config = {
title: "TEs and their host",
description: "",
maxtime: 200000,
fpsmeter: false,
ncol: size,
nrow: size, // dimensions of the grid to build
wrap: [true, true], // Wrap boundary [COLS, ROWS]
scale: scale, // scale of the grid (nxn pixels per grid cell)
graph_interval: 10,
statecolours: { alive: { 1: 'blue' } } // The background state '0' is never drawn
}
sim = new Simulation(config)
sim.makeGridmodel("TE_model");
sim.createDisplay_continuous({model:"TE_model", property:"genomesize", label:"Genome size", minval:0, maxval:100, fill:"viridis"})
sim.createDisplay_continuous({model:"TE_model", property:"T_in_genomes", label:"TEs inside genomes", minval:0, maxval:100, fill:"viridis"})
sim.createDisplay_continuous({model:"TE_model", property:"T_in_eDNA", label:"TEs in eDNA pool", minval:0, maxval:100, fill:"viridis"})
sim.TE_model.initialise = function () {
sim.initialGrid(sim.TE_model, "alive", 0)
sim.initialGrid(sim.TE_model, "genomesize", 0, undefined, 1.0)
sim.initialGrid(sim.TE_model, "T_in_eDNA", 0, undefined, 1.0)
sim.initialGrid(sim.TE_model, "T_in_genomes", 0, undefined, 1.0)
sim.max_g = 100
sim.max_t = 100
sim.TE_model.colourViridis("genomesize", sim.max_g)
sim.TE_model.colourViridis("T_in_genomes", sim.max_t)
sim.TE_model.colourViridis("T_in_eDNA", sim.max_t)
sim.mixDNApool = false
this.resetPlots()
placeCell = function (gp, init_es, init_ne, init_nc, init_tra, init_tra_rate) {
gp.alive = 1
gp.genome = new Genome()
gp.genome.initialise(init_es, init_ne, init_nc, init_tra, init_tra_rate)
gp.genomesize = Math.min(sim.max_g, gp.genome.chromosome.length) // A copy of the genome size is also stored within the grid point itself, so we can visualise it on the grid (capped at 100)
gp.fitness = gp.genome.fitness // A copy of the genomes' fitness is stored within the grid point itself, so we can use it for the "rouletteWheel" function
gp.T_in_genomes = Math.min(sim.max_t, gp.genome.nr_tra) // A copy of the genomes' fitness is stored within the grid point itself, so we can use it for the "rouletteWheel" function
}
for (let x = 0; x < sim.TE_model.nc; x++) for (let y = 0; y < sim.TE_model.nr; y++) {
this.grid[x][y].eDNA = [] // Initialise empty eDNA pool
this.grid[x][y].T_in_eDNA = 0
let size = 5
if ((Math.pow((x - sim.TE_model.nc / 2), 2) + Math.pow((y - sim.TE_model.nr / 2), 2)) < size)
placeCell(sim.TE_model.grid[x][y], init_es, init_ne, init_nc, 10, init_tra_rate)
else if ((Math.pow((x - sim.TE_model.nc / 2), 2) + Math.pow((y - sim.TE_model.nr / 2), 2)) < size * 50)
placeCell(sim.TE_model.grid[x][y], init_es, init_ne, init_nc, 0, init_tra_rate)
// else if((Math.pow((x-sim.TE_model.nc/2),2) + Math.pow((y-sim.TE_model.nr/2),2) ) < size*10 && y>sim.TE_model.nr/2)
// placeCell(sim.TE_model.grid[x][y],init_es,init_ne,0,0)
}
}
sim.TE_model.initialise() // Initialise for the first time (otherwise used for "RESET" button)
// Define the next-state function. This example is stochastic growth in a petri dish
sim.TE_model.nextState = function (i, j) {
if (this.grid[i][j].alive == 0) {
let neighbours = this.getMoore8(this, i, j, 'alive',1)
if (neighbours.length > 0) {
let winner = this.rouletteWheel(neighbours, 'fitness', non)
if (winner != undefined)
this.reproduce(i, j, winner)
}
}
else if (this.rng.genrand_real1() < death_rate || this.grid[i][j].genome.fitness == 0)
this.death_and_lysis(i, j)
else {
this.TEdynamicsI(i, j)
this.TEdynamicsII(i, j)
}
// EDNA DYNAMICS
if (this.grid[i][j].eDNA.length > 0) {
for (let k = 0; k < this.grid[i][j].eDNA.length; k++)
if (this.rng.genrand_real1() < degr_rate_edna) // degr
this.grid[i][j].eDNA.splice(k, 1)
this.grid[i][j].T_in_eDNA = Math.min(sim.max_t, this.grid[i][j].eDNA.length) // Track number of TEs for visualisation purposes
}
else
this.grid[i][j].T_in_eDNA = undefined
}
// This function is asynchronously applied to the entire grid every time step. It uses a single random number to determine both IF a DNA fragment will move,
// as well as WHERE it moves.
sim.TE_model.diffuse_eDNA = function (i, j) {
moveDNA = function (k, direction) {
let coords = sim.TE_model.moore[direction]
let target = sim.TE_model.getGridpoint(coords[0] + i, coords[1] + j)
target.eDNA.push(sim.TE_model.grid[i][j].eDNA[k])
sim.TE_model.grid[i][j].eDNA.splice(k, 1)
}
for (let k = 0; k < sim.TE_model.grid[i][j].eDNA.length; k++) {
let randomnr = sim.TE_model.rng.genrand_real1()
if (randomnr < diff_rate_edna / 4) moveDNA(k, 1)
else if (randomnr < 2 * diff_rate_edna / 4) moveDNA(k, 2)
else if (randomnr < 3 * diff_rate_edna / 4) moveDNA(k, 3)
else if (randomnr < 4 * diff_rate_edna / 4) moveDNA(k, 4)
}
}
// A custom function for copying a cell into a gp at position i,j ("reproduction")
sim.TE_model.reproduce = function (i, j, winner) {
this.grid[i][j].alive = winner.alive
this.grid[i][j].genome = winner.genome.copy(mutate = true)
this.grid[i][j].genomesize = Math.min(sim.max_g, this.grid[i][j].genome.chromosome.length)
this.grid[i][j].fitness = this.grid[i][j].genome.fitness
this.grid[i][j].T_in_genomes = Math.min(sim.max_t, this.grid[i][j].genome.nr_tra) // A copy of the genomes' fitness is stored within the grid point itself, so we can use it for the "rouletteWheel" function
}
// A custom function for killing a gp at position i,j
sim.TE_model.death_and_lysis = function (i, j) {
this.grid[i][j].alive = 0
this.grid[i][j].genomesize = undefined
this.grid[i][j].T_in_genomes = undefined
for (let p = 0; p < this.grid[i][j].genome.chromosome.length; p++) {
if (this.grid[i][j].genome.chromosome[p].type == "T")
this.grid[i][j].eDNA.push(this.grid[i][j].genome.chromosome[p])
}
}
// A custom function for TE dynamics during the lifetime of a cell
sim.TE_model.TEdynamicsI = function (i, j) {
jumping = []
for (let p = 0; p < this.grid[i][j].genome.chromosome.length; p++) {
if (this.grid[i][j].genome.chromosome[p].type == "T") {
if (this.rng.genrand_real1() < jump_rate * this.grid[i][j].genome.chromosome[p].transposition_rate)
jumping.push(this.grid[i][j].genome.chromosome[p])
}
}
for (let p = 0; p < jumping.length; p++) {
let random_pos = Math.floor(this.rng.genrand_real1() * this.grid[i][j].genome.chromosome.length)
let new_TE_copy = jumping[p].copy()
if (this.rng.genrand_real1() < probability_TE_induced_damage)
this.grid[i][j].genome.chromosome[random_pos].type = "."
this.grid[i][j].genome.chromosome.splice(random_pos, 0, new_TE_copy)
}
//if(jumping.length > 0)
this.grid[i][j].genome.calculate_fitness()
}
// A custom function for TE dynamics from the eDNA pool
sim.TE_model.TEdynamicsII = function (i, j) {
let nr_jumps = 0
for (let p = 0; p < this.grid[i][j].eDNA.length; p++) {
let randomnr = this.rng.genrand_real1()
let uptake = randomnr < uptake_from_pool
let integration = randomnr < uptake_from_pool * this.grid[i][j].eDNA[p].transposition_rate
if (uptake) {
if (integration) {
let random_pos = Math.floor(this.rng.genrand_real1() * this.grid[i][j].genome.chromosome.length)
let new_TE_copy = this.grid[i][j].eDNA[p].copy()
if (this.rng.genrand_real1() < probability_TE_induced_damage)
this.grid[i][j].genome.chromosome[random_pos].type = "."
this.grid[i][j].genome.chromosome.splice(random_pos, 0, new_TE_copy)
nr_jumps++
}
this.grid[i][j].eDNA.splice(p, 1)
}
}
if (nr_jumps > 0) this.grid[i][j].genome.calculate_fitness()
}
// Custom function to mix only the eDNA
sim.TE_model.mixeDNA = function () {
let all_eDNA_gps = [];
for (let i = 0; i < this.nc; i++)
for (let j = 0; j < this.nr; j++)
all_eDNA_gps.push(this.grid[i][j].eDNA)
all_eDNA_gps = shuffle(all_eDNA_gps, this.rng)
for (let i = 0; i < this.nc; i++)
for (let j = 0; j < this.nr; j++)
this.grid[i][j].eDNA = all_eDNA_gps.pop()
// return "Perfectly mixed the grid"
}
sim.TE_model.update = function () {
if (this.time % sim.config.graph_interval == 0) this.updateGraphs()
this.synchronous() // Applied as many times as it can in 1/60th of a second
this.apply_async(this.diffuse_eDNA)
if (sim.mixDNApool == true) this.mixeDNA()
}
sim.TE_model.updateGraphs = function () {
let num_alive = 0, gsizes = 0, hks = 0, nes = 0, non = 0, tra = 0, fitnesses = 0, sum_tra_rates = 0
for (let i = 0; i < sim.TE_model.nc; i++) for (let j = 0; j < sim.TE_model.nr; j++) {
if (this.grid[i][j].alive == 1) {
num_alive++
gsizes += this.grid[i][j].genome.chromosome.length
fitnesses += this.grid[i][j].genome.fitness
for (let p = 0; p < sim.TE_model.grid[i][j].genome.chromosome.length; p++)
switch (sim.TE_model.grid[i][j].genome.chromosome[p].type) {
case "G": hks++; break;
case "g": nes++; break;
case ".": non++; break;
case "T":
tra++;
sum_tra_rates += sim.TE_model.grid[i][j].genome.chromosome[p].transposition_rate
break;
}
}
}
this.plotArray(["Population size"],
[num_alive],
["blue"],
"Population size (nr. of living cells)")
this.plotArray(["Genome size", "Essential", "Non-essential", "Non-coding", "Transposons"],
[gsizes / num_alive, hks / num_alive, nes / num_alive, non / num_alive, tra / num_alive],
["black", "blue", "green", "grey", "red"],
"Avg genome size and composition")
this.plotArray(["Fitness"],
[fitnesses / num_alive],
["black"],
"Avg fitness")
this.plotArray(["TE jump rate (phi)"],
[sum_tra_rates / tra],
["red"],
"Avg TE jump rate (phi)")
}
// sim.addHTML("canvas_holder", "<img src=\"legend.png\">")
sim.addButton("Pause/continue", function () { sim.toggle_play() })
sim.addButton("Well-mix (all)", function () { sim.toggle_mix() })
sim.addButton("Well-mix (eDNA)", () => { sim.mixDNApool = !sim.mixDNApool })
sim.addButton("Restart", function () { sim.TE_model.initialise() })
sim.addButton("Reset", function () { location.reload(); })
sim.addHTML("form_holder", "<br>Ecological options:")
sim.addSlider("death_rate", 0.0, 1.0, 0.001, "Stochastic death rate (d)")
sim.addSlider("non", 0.0, 200.0, 1.00, "No-reproduction constant (eps)")
sim.addSlider("diff_rate_edna", 0.0, 0.25, 0.001, "eDNA diffusion rate (D)")
sim.addSlider("degr_rate_edna", 0.0, 1.0, 0.001, "eDNA degradation rate (q)")
sim.addHTML("form_holder", "<br>TE options:")
sim.addSlider("uptake_from_pool", 0.0, 1.0, 0.001, "Uptake rate from eDNA pool (u)")
sim.addSlider("jump_rate", 0.0, 1.0, 0.001, "TE jump rate (j)")
sim.addSlider("transposon_fitness_cost", 0.0, 1.0, 0.001, "TE fitness cost (c)")
sim.addSlider("probability_TE_induced_damage", 0.0, 1.0, 0.01, "TE-damage propensity (b)")
sim.addHTML("form_holder", "<br>Mutations:")
sim.addSlider("phi_mutation_rate", 0.0, 1.0, 0.001, "TE (phi) mutation rate")
sim.addSlider("gene_deletion_rate", 0.0, 0.1, 0.0001, "Gene deletion rate")
sim.addSlider("gene_duplication_rate", 0.0, 0.1, 0.0001, "Gene duplication rate")
sim.addSlider("gene_inactivation_rate", 0.0, 0.1, 0.0001, "Gene inactivation rate")
sim.addHTML("form_holder", "<br>Initial conditions (restart required!):")
sim.addSlider("init_es", 0.0, 100.0, 1, "Nr. essential genes")
sim.addSlider("init_nc", 0.0, 300.0, 1, "Nr. non-coding elements")
sim.addSlider("init_ne", 0.0, 100.0, 1, "Nr. non-essential functions")
sim.addSlider("non_essential_gene_boon", 0.0, 1.0, 0.001, "Fitness added per non-essential function")
sim.addSlider("init_tra_rate", 0.0, 1.0, 0.001, "Initial TE jump rate")
sim.start()
}
/*-------------------------End user-defined code ---------------------*/
</script>
</head>
<body onload="cacatoo()">
<!-- --------------------- START MENU. Couldnt get it to load dynamically, so this needs to be replaced in every HTML file upon changing --------------------- -->
<header class="header" id="btnNav"><buton class="header__button" id="btnNav" type="button"><i class="material-icons">menu</i></buton></header>
<nav class="nav"><div class="nav__links">
<a href="#" class="nav__head"><i id="btnNavclose" class="material-icons" style="cursor:pointer"> menu </i> Cacatoo </a>
<a href="index.html" id="nav__link" class="nav__link">Home</a>
<a href="https://github.com/bramvandijk88/cacatoo" id="nav__link" target="_blank" class="nav__link"> Source code (Github)</a>
<!-- <a href="https://replit.com/@bramvandijk88/Cacatoo-IBMs-with-examples" id="nav__link" target="_blank" class="nav__link"> Replit </a> -->
<a href="#" class="nav__head"><i class="material-icons"> play_circle_outline </i> Examples</a>
<a href="example_predator_prey.html" id="nav__link" class="nav__link"> Predator prey</a>
<a href="example_colony_growth.html" id="nav__link" class="nav__link"> Colony growth</a>
<a href="example_pheromones.html" id="nav__link" class="nav__link"> Ant pheromones</a>
<a href="example_aapjes.html" id="nav__link" class="nav__link"> Aapjes (monkeys)</a>
<a href="example_starlings.html" id="nav__link" class="nav__link"> Starlings</a> <a href="example_cooperation.html" id="nav__link" class="nav__link"> Cooperation</a>
<a href="example_TEs.html" id="nav__link" class="nav__link"> Transposon evolution</a>
<a href="examples_jsfiddle.html" id="nav__link" class="nav__link"> More examples (JSFiddle)</a>
<a href="#" class="nav__head"><i class="material-icons"> grid_on </i> How to Cacatoo </a>
<a href="overview.html" class="nav__link"> Tutorials (Blog style)</a>
<a href="list_of_options.html" class="nav__link"> All configuration options</a>
<a href="populating_the_simulation.html" class="nav__link"> Populating a simulation</a> <a href="display_and_colours.html" class="nav__link"> Display, colours, and UI</a>
<a href="neighbourhood_retrieval.html" class="nav__link"> Neighbourhood retrieval</a>
<a href="random_numbers.html" class="nav__link"> Using random numbers </a>
<a href="grid_events.html" class="nav__link"> Grid events</a>
<a href="working_with_odes.html" class="nav__link"> Working with ODEs</a>
<a href="jsdocs/index.html" id="nav__headlink" target="_blank" class="nav__headlink"><i class="material-icons">data_object </i> Full JS-Docs</a>
</div><div class="nav__overlay"></div></nav>
<script>
document.addEventListener("DOMContentLoaded", () => {
const nav = document.querySelector(".nav");
document.querySelector("#btnNav").addEventListener("click", () => {
nav.classList.add("nav--open");
});
document.querySelector(".nav__overlay").addEventListener("click", () => {
nav.classList.remove("nav--open");
});
document.querySelector("#btnNavclose").addEventListener("click", () => {
nav.classList.remove("nav--open");
});
var all_links = document.getElementsByClassName("nav__link");
var hide_menu = function() {
nav.classList.remove("nav--open");
};
for (var i = 0; i < all_links.length; i++) {
all_links[i].addEventListener('click', hide_menu, false);
}
});
</script>
<!-- --------------------- END MENU --------------------- -->
<div id="main">
<h1 class="page-title"><a href="https://github.com/bramvandijk88/cacatoo"><img src="images/elephant_cacatoo_small.png"></a> <b>Coevolution of TEs and their host</b> </img></h1>
This is a minimal version of the TE-host coevolution model (published by van Dijk <i>et al.</i>, 2021), where genome streamlining occurs as a defence mechanisms against transposable elements (TEs)
<center>
<div id="canvas_holder"></div>
<div id="graph_holder"></div>
<div id="form_holder"></div>
</center>
<br><br>
</div>
<div id="Navigator"></div>
<br class="clear">
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>