UNPKG

cacatoo

Version:

Building, exploring, and sharing spatially structured models

267 lines (228 loc) 7.79 kB
<!-- EXAMPLE FILE: Fitz-Nagumo --> <html> <script src="../../dist/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) --> <script src="../../lib/all.js"></script> <!-- Load other packages --> <link rel="stylesheet" href="../../style/cacatoo.css"> <!-- Set style sheet --> <script> /*-----------------------Start user-defined code ---------------------*/ /*-----------------------Start user-defined code ---------------------*/ /*-----------------------Start user-defined code ---------------------*/ var sim; // Changing sim to var to enable "save image" button //model parameters, confusingly dubbed variables //for technical reasons var a = 0.025; var e = 0.005; var b = 4.5; var dt = 2; var disruption = 0.3; var ectopic = false; let sumV; //to sum up V over the grid function cacatoo() { //setting details like simulation field size, //max run time, title, zoom scale, display //frequency, boundary conditions etc let config = { title: 'Fitzhugh Nagumo PDE', description: '', maxtime: 1000000, ncol: 150, nrow: 150, // dimensions of the grid to build wrap: [false, false], // Wrap boundary [COLS, ROWS] scale: 2, // scale of the grid (nxn pixels per grid cell bgcolour: 'white', graph_interval: 2, graph_update: 25, }; //make a model object sim = new Simulation(config); sim.makeGridmodel('Fitz'); //set the colors for the 2D displays //arguments: which variable, max value, start color, end color sim.Fitz.colourGradient('V', 100, [255, 255, 255], [0, 0, 255]); sim.Fitz.colourGradient('R', 100, [255, 255, 255], [255, 0, 0]); //2D display for the V variable & colorbar sim.createDisplay_continuous({ model: 'Fitz', property: 'V', label: 'Action potential', minval: 0, maxval: 1, decimals: 3, nticks: 3, }); //2D display for the W variable & colorbar sim.createDisplay_continuous({ model: 'Fitz', property: 'R', label: 'Refractory tissue', minval: 0, maxval: 0.3, decimals: 3, nticks: 3, }); sim.reset = function (all = true) { if (all) sim.time = 0; if (all) sim.Fitz.resetPlots(); for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { if (all) { if (x * x + y * y <= 100) sim.Fitz.grid[x][y].V = 1; else sim.Fitz.grid[x][y].V = 0.0; sim.Fitz.grid[x][y].W = 0.0; sim.Fitz.grid[x][y].A = 0.025; } else { if (x * x + y * y <= 100) sim.Fitz.grid[x][y].V = 1; } } } }; sim.reset(); sim.ectopic = function () { for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { if ( (sim.Fitz.nc - x) * (sim.Fitz.nc - x) + (sim.Fitz.nr - y) * (sim.Fitz.nr - y) <= 100 ) { sim.Fitz.grid[x][y].V = 1; } } } }; sim.defib = function () { for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { sim.Fitz.grid[x][y].V = 1; } } }; sim.ablate = function () { let range = [130,131,132] for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { if ( ((range.includes(x) && y >= 130 && x > 0)) || ((range.includes(y) && x >= 130 && y > 0)) ) sim.Fitz.grid[x][y].A = 0.35; } } }; //Defines the next-state function on a per position basis sim.Fitz.nextState = function (x, y) { //define variables V and W to store local V and W values let V = this.grid[x][y].V; let W = this.grid[x][y].W; let A = this.grid[x][y].A; //update grid stored V and W values based on ODE equations this.grid[x][y].V += dt * (-V * (V - A) * (V - 1) - W); this.grid[x][y].W += dt * (e * (V - b * W)); if (A > 0.01) this.grid[x][y].R = A > 0.2 ? A : this.grid[x][y].W; }; //this is where the overall time loop of the simulation takes place sim.Fitz.update = function () { //this is for the simulation itself, resetting part of field if (sim.time % 200 == 0) sim.reset(false); if (ectopic && sim.time % 70 == 0) sim.ectopic(); //shifting of graphs, sliding window if (sim.time > 0 && (sim.time % sim.config.graph_update) * 10 == 0) { for (let name in sim.Fitz.graphs) { let g = sim.Fitz.graphs[name]; let dat = g.data.slice(-sim.config.graph_update * 10); g.data = dat; g.g.updateOptions({ file: this.data, }); } } //defines updating mode, irrelevant here //calls the update function on all positions in grid //causing the execution of only the ODE part this.synchronous(); //now the PDE part of the equation is executed //note that only the V variable diffuses //second argument gives diffusion constant this.diffuseStates('V', 0.2); //here we compute the average value of V over the field sumV = 0; for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { sumV += this.grid[x][y].V; } } sumV /= sim.Fitz.nc * sim.Fitz.nr; if (sim.time % 100 == 0) { if (sim.time == 0) sim.log('Time, avgV\n', 'output'); let line = `${sim.time}, `; line += sumV + '\n'; sim.log(line, 'output'); } //this plots the V value in a particular position this.plotArray( ['local V'], [this.grid[this.nc - 1][0].V], ['blue'], 'Action potential at right-hand side', { valueRange: [-1, 1] } ); //this plots the field averaged V value this.plotArray(['average V'], [sumV], ['blue'], 'Average action potential'); }; //end of time loop //adding a button which when pressed sets the V //value to 1 in all positions of the field sim.addButton('Save grids', function () { sim.sectionToPNG('canvas_holder', 'grid_timepoint_'); }); sim.addButton('Save graphs', function () { sim.sectionToPNG('graph_holder', 'graph_timepoint_'); }); sim.addButton('Save output data', function () { sim.write( document.getElementById('output').textContent, `Data_${sim.time}` ); }); sim.addButton('Refresh', function () { sim.reset(); }); sim.addButton('Turn ectopic on/off', function () { ectopic = !ectopic; }); sim.addButton('Defibrillate', function () { sim.defib(); }); sim.addButton('Ablate', function () { sim.ablate(); }); //adding a button which when pressed calls the //reset function, starting a new simulation sim.addButton('Clear!', function () { for (let x = 0; x < sim.Fitz.nc; x++) { for (let y = 0; y < sim.Fitz.nr; y++) { sim.Fitz.grid[x][y].V = 1; } } }); //adding the possibility of pulling a mouse over //the V screen puts V to zero there sim.addStatebrush('Fitz', 'V', 0.0, 40); sim.addMovieButton(sim.Fitz, "Action potential",60) //this starts the whole simulation, so it calls update sim.start(); } //end overall cacatoo function /*-------------------------End user-defined code ---------------------*/ </script> <body onload="cacatoo()"> <div class="header" id="header"></div> <div class="content" id="canvas_holder"> </div> <div class="content" id="graph_holder"> </div> <div class="content" id="form_holder"> </div> <div class="content" id="output"> </div> <div class="footer" id="footer"></div> </body> </html>