UNPKG

cacatoo

Version:

Building, exploring, and sharing spatially structured models

420 lines (349 loc) 22.4 kB
<!DOCTYPE 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>