cacatoo
Version:
Building, exploring, and sharing spatially structured models
267 lines (228 loc) • 7.79 kB
HTML
<!--
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>