UNPKG

cacatoo

Version:

Building, exploring, and sharing spatially structured models

292 lines (239 loc) 18.7 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) --> </head> <body> <!-- --------------------- 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</a> <a href="example_mutational_jackpot.html" id="nav__link" class="nav__link"> Mutational jackpot</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"> Tutorial 1: Cacatoo overview</a> <a href="first_project.html" class="nav__link"> Tutorial 2: Predator-prey model</a> <a href="colony_growth.html" class="nav__link"> Tutorial 3: Colony growth / ODEs</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>Colony growth and using ODEs</b> </img></h1> <center><h4><a href="first_project.html">&larr;Previous tutorial </a> </h4></center> In this tutorial, I will illustrate how you can model simple bacterial metabolism, how continuous variables can be shown on the grid, and how ODEs can be attached to grid points. Here are the steps we'll be taking: <ul> <li> <a href="#0">Simple colony growth</a></li> <li> <a href="#1">Populating the grid with custom, unique individuals</a></li> <li> <a href="#2">Displaying a continuous variable on the grid </a></li> <li> <a href="#3">Attaching ODEs to grid points</a></li> </ul> <a id="0"></a><center><h3>Simple colony growth</h3></center> From the previous tutorial, you should have a general idea of how to make a simple model. See if you can mimic a simple growing colony, like this:<br> <br><center><img style="width:50%" src="images/colony.gif"></center> <br> For my implementation of colony growth, I used the very simple code shown below:<br> <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">let randomneigh = this.randomMoore8(this,i,j).alive // Random neighbour if(this.grid[i][j].alive == 0) // If empty { if(randomneigh == 1 && this.rng.genrand_real1() < 0.5) this.grid[i][j].alive = 1 // 1 ("cell") reproduces }</font></code></pre></center> The above simulations looks like colony growth, because only the cells at the edge can grow. The limiting resource here is "empty space". However, colony growth should also be limited by nutrient concentrations, and different species may take up resources at different rates. So let's try if we can simulate this. Let's set up a new model called "growth". I have set up a 200x200 grid, and I have disabled the wrapped boundary conditions (like a petri dish, but square :D). The statecolours for 'species' can be manually defined, or simply set to 'default'. <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">let config = { title: "Colony", // The name of your cacatoo-simulation description: "", // And a description if you wish maxtime: 1000000, // How many time steps the model continues to run ncol: 200, // Number of columns (width of your grid) nrow: 200, // Number of rows (height of your grid) seed: 5, wrap: [false, false], // Wrapped boundary conditions? [COLS, ROWS] scale: 2, // Scale of the grid (nxn pixels per grid point) statecolours: {'species': { 1: "#FFFFFF", // Colours for each state. Background (0) defaults to black. 2: "red", 3: "#3030ff"}} //statecolours: { 'species': 'default' }, } sim = new Simulation(config) // Initialise the Cacatoo simulation sim.makeGridmodel("growth") // Build a new Gridmodel within the simulation called "model" </font></code></pre></center> <a id="1"></a><center><h3>Populating the grid</h3></center> Instead of populating the grid randomly (i.e. what we did in the previous tutorial with "initialGrid"), we will now set it up with some custom individuals. To do this, first define a list (array) of unique individual, and then pass that list on to the function "populateSpot" or "populateGrid": <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">let species = [{species:1,uptake_rate:0.5,internal_resources:1}, {species:2,uptake_rate:5.0,internal_resources:1}, {species:3,uptake_rate:50.0,internal_resources:1}] sim.populateSpot(sim.growth, species, [0.33,0.33,0.33], 15, config.ncol/2, config.nrow/2) // Inoculate 1 spot (middle of grid) with species. The third array sets the frequency of each species. //sim.populateGrid(sim.growth, species, [0.01,0.01,0.01]) // Alternatively, inoculate entire grid with species </font></code></pre></center> Above, I have defined three individuals that have different uptake rates. Let's add some "external resource" to the entire grid too, and set up the display for the species. <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">sim.initialGrid(sim.growth,'external_resources',1000.0,1.0) // Add 1000.0 external resources to 100% (1.0) of grid points sim.createDisplay("growth", "species", "Living cells") // Create a display in the same way we did in Tutorial 1 (display a discrete variable) </font></code></pre></center> As our three species have a different rate of nutrient uptake, which we can now use these parameters to setup nutrient exchange in the next-state function: <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">sim.growth.nextState = function (i, j) { let randomneigh = this.randomMoore8(this, i, j) // Random neighbour let this_gp = this.grid[i][j] // This cell if (this_gp.species == 0) // If empty spot { if (randomneigh.species > 1 && randomneigh.internal_resources > 50) { // Random neighbour is alive and it has enough resources this_gp.species = randomneigh.species // Empty spot becomes the parent type (reproduction) this_gp.uptake_rate = randomneigh.uptake_rate // Empty spot inherits uptake rate from the parent randomneigh.internal_resources = this_gp.internal_resources = randomneigh.internal_resources / 2 // Resources are divided between parent and offpsring } } else { if (this.rng.genrand_real1() < 0.01) { // Random death this_gp.species = 0 this_gp.uptake_rate=0.0 this_gp.internal_resources = 0 } else{ let uptake = this_gp.external_resources * (this_gp.uptake_rate/100) // Living cells can take up a fraction of available resources this_gp.internal_resources += uptake this_gp.external_resources -= uptake } } }</font></code></pre></center> <br> If all goes well, your simulation should now look like this:<br> <br><center><img style="width:50%" src="images/colony_2.gif"></center> <center><b><i> <font size=1>Again, if your simulation isn't working, check out the dev-console to see what the problem is (CTRL/CMD+SHIFT+I in Google Chrome)</font></b></i></center> <br> The simulation shows that the blue species (high uptake rate) is growing much faster than the red one, and that the white species isn't able to really grow at all! However, it would be better if we could actually see the resource concentrations, both inside and outside of cells. <a id="2"></a><center><h3>Displaying a continuous variable</h3></center> Let's add some displays for these (continuous) variables: <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">sim.createDisplay_continuous({model:"growth", property:"external_resources", label:"External resources", // Createa a display for a continuous variable (ODE state for external resources) minval:0, maxval:1000, fill:"viridis"}) sim.createDisplay_continuous({model:"growth", property:"internal_resources", label:"Internal resources", // Createa a display for a continuous variable (ODE state for external resources) minval:0, maxval:1000, fill:"viridis"})</font></code></pre></center> As you may notice, the functions above work different from "createDisplay". They only receive one argument, with a configuration-object (similar to how the simulation is initialised). This way it is much easier to setup exactly the display we want, with a custom "fill" function, a minimal cut-off (minval), a maximal cut-off, <i>etc</i>. <br><br> If all goes well, you should now see this:<br><br> <br><center><img style="width:70%" src="images/colony_3.gif"></center> <br> <hr> <br> <a id="3"></a><center><h3>Attach ODEs to grid points</h3></center> Perhaps you would prefer to use ordinary differential equations (ODEs) to solve the nutrient dynamics. Although numerically solving ODEs is a bit slower than what we did above, it is also much more accurate and allows you to study you favourite ODEs <a target="_blank" href="https://en.wikipedia.org/wiki/Outer_space">in space</a>! Attaching ODEs to your system is quite easy. Before we define the next-state rule, let's first define a set of ODEs: <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">// Define ODEs with basic resource dynamics let resource_dynamics = function (u, k) { return function (x, y) { let external = y[0] // The first variable (y[0]) is the external resource concentration, which is taken up with rate u let internal = y[1] // The second variable (y[1]) is the internal resource concentration, which is used by the cells to divide return [ -u * external, u * external - k * internal ] } } // Configuration object with initial states, parameters, and diffusion rates let ode_config = { ode_name: "resources", init_states: [1, 0], // Initial values of external and internal resources parameters: [0.0, 0.0], // u and k are set to 0.0 by default, as we will make it dependent on cell presence! diffusion_rates: [0.2, 0.0] // resources diffuse through exteral environment, but internal resources stay inside cells } // Attaches an ODE to all gridpoints with the given configuration object sim.model.attachODE(resource_dynamics, ode_config);</font></code></pre></center> At this point, your model won't really do anything different. This is because, although the grid points have ODEs within them, they are not yet being solved. Below I modify the next-state function such that i) only grid points with a cell inside have their "uptake" parameter set, ii) the internal resource variable is divided between parent and offspring update-function reproduction, and iii) all grid-points get solved. The rest should function approximately the same as our model above. <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;">sim.model.nextState = function (i, j) { let randomneigh = this.randomMoore8(this, i, j) // Random neighbour let this_gp = this.grid[i][j] // This cell if (this_gp.species == 0) // If empty { if (randomneigh.species > 1 && randomneigh.resources.state[1] > 0.5) { this_gp.species = randomneigh.species this_gp.uptake_rate = randomneigh.uptake_rate randomneigh.resources.state[1] = this.grid[i][j].resources.state[1] = randomneigh.resources.state[1] / 2 } } else { this.grid[i][j].resources.pars = [this_gp.uptake_rate, 0.5] // Living cells take up and have upkeep if (this.rng.genrand_real1() < 0.01) { this_gp.species = 0 this_gp.uptake_rate=0.0 this_gp.resources.state[1] = 0 this_gp.resources.pars = [0.0, 0.0] } } this_gp.resources.solveTimestep(0.1) // Solve a single time step with delta_t=0.1 this_gp.external_resources = this_gp.resources.state[0] if(this_gp.species > 0) this_gp.internal_resources = this.grid[i][j].resources.state[1] else this.grid[i][j].rersources = 0.0 } </font></code></pre></center> <br> And one final thing we want to do is modify the main update-loop to add diffusion of the ODE states between grid points (i.e. the external resources), and add a plot so we can see the concentrations changing:<br> <center><pre class="prettyprint lang-js"><code style="text-align:left"><font size =2 color="blue" style="font-family: monospace;"> sim.model.update = function () { this.asynchronous() // Applied as many times as it can in 1/60th of a second this.diffuseODEstates() // Diffusion of external metabolites this.plotODEstates("resources", [0, 1], [[0, 0, 0], [255, 0, 0]]) } </font></code></pre></center> <br><center><img style="width:50%" src="images/colony_4.gif"></center> <br> <br>And that wraps up this series of tutorials. The end-result of the tutorials can be found on the Github repository. If you want to learn more, start exploring the many examples available there. <br><br> <center><h4><a href="first_project.html">&larr;Previous tutorial </a> | <a href="#top">&uarr; Back to the top</a></h4></center> </div> <div id="Navigator"></div> <br class="clear"> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>