Skip to content

Instantly share code, notes, and snippets.

@newby-jay
Last active July 15, 2019 11:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save newby-jay/9bb1a3eca1eb5c0ab08c to your computer and use it in GitHub Desktop.
Save newby-jay/9bb1a3eca1eb5c0ab08c to your computer and use it in GitHub Desktop.
Noisy limit cycle animation

Effects of moderate noise on a limit cycle oscillator: Counterrotation and bistability (Phys. Rev. Lett., 2014)

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]},
tex: {extensions: ["color.js"]}});
</script>
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG">
</script>
<style>
body {height: 100%;width: 100%;}
#animation {
position: absolute;
top: 50px;
left: 200px;
}
#svgAxes {
position: absolute;
top: 50px;
left: 200px;
stroke: #000;
font: 10px sans-serif;
}
.axis {
stroke: none;
fill: #000;
}
.axis line, .axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
#slider {
position: absolute;
top: 300px;
left: 550px;
width: 150px;
background: none;
font: 10px sans-serif;
}
#sliderLabel {
position: absolute;
top: 260px;
left: 575px;
font: 30px serif;
}
#ylabel {
position: absolute;
top: 150px;
left: 170px;
font: 30px serif;
}
#xlabel {
position: absolute;
top: 330px;
left: 350px;
font: 30px serif;
}
#equations {
position: absolute;
top: 370px;
left: 170px;
}
</style>
<body>
<canvas id="animation" width="250" height="250"></canvas>
<svg id="svgAxes" width="250" height="250"></svg>
<span id="xlabel" touch-action="none"><em>x</em></span>
<span id="ylabel" touch-action="none"><em>y</em></span>
<span id="sliderLabel">&sigma; = <span id="sliderValue"></span></span>
<div style="text-align: center">
<input type="range" id="slider" value="10" min="0" max="50">
</div>
<div id="equations">
\[\begin{align}
dX &= \left[\gamma\left(1-X^2 - Y^2\right)X - \left(1 - \gamma c\left(1 - \sqrt{X^2 + Y^2}\right)^{2}\right)Y\right]dt + \sigma dW_x \\
dY &= \left[\gamma\left(1-X^2 - Y^2\right)Y + \left(1 - \gamma c\left(1 - \sqrt{X^2 + Y^2}\right)^{2}\right)X\right]dt + \sigma dW_y
\end{align}\]
</div>
<script type="text/javascript" src="http://iop.io/js/vendor/d3.v3.min.js"></script>
<script src="lc_anim.js"></script>
</body>
</html>
(function anim() {
////////////////////
//// simulation ////
////////////////////
var gamma = 50,
c = 25,
dt = 0.004,
x = 1,
y = 0,
rn = d3.random.normal(0, Math.sqrt(dt));
function evolve_simulation() {
for (var i=0; i<2; i++) {
var rho = Math.sqrt(x*x + y*y),
r = rho - 1,
a = gamma*(1 - rho*rho),
b = -gamma*c*r*r + 1;
x += (x*a - b*y)*dt + sigma*rn();
y += (y*a + b*x)*dt + sigma*rn();
}
}
/////////////////////////
//// animation setup ////
/////////////////////////
var width = 250,
height = width,
mw = 30,
cNa = "#F24533", // orange 242,69,51
cK = "#0080FF", // blue 0,128,255
linecolor = d3.interpolateRgb(cK, cNa);
var g = d3.select("#animation").node().getContext("2d");
g.fillStyle = "rgba(255, 255, 255, 0.05)"; // for fading curves
g.strokeStyle = linecolor(0);
g.lineWidth = 4;
var svg = d3.select("#svgAxes")
// .attr("width", width)
// .attr("height", height)
.style("margin-bottom", mw + "px")
.style("margin-top", mw + "px"),
//// limit cycle ////
circ_trans = (width - 2*mw)/2 + mw;
svg.append("g").attr("transform", "translate(" + mw + ", "+ -mw + ")")
.append("circle")
.attr("r", (width-2*mw)/2.4)
.attr("transform", "translate(" + circ_trans + "," + circ_trans +")")
.style("fill", "none").style("opacity", 0.3);
//// axes ////
scx = d3.scale.linear()
.domain([-1.2, 1.2])
.range([mw, width - 1.01*mw]),
scy = d3.scale.linear()
.domain([-1.2, 1.2])
.range([height - mw, mw]),
xyline = d3.svg.line()
.x(function(d, i) { return scx(d.x); })
.y(function(d, i) { return scy(d.y); }),
svg.append("g").attr("class", "axis")
.attr("transform", "translate(" + mw + "," + (height-mw) + ")")
.call(d3.svg.axis().scale(scx).orient("bottom").tickValues([-1, 0, 1]));
svg.append("g").attr("class", "axis")
.attr("transform", "translate(" + mw + "," + (-mw) + ")")
.call(d3.svg.axis().scale(scy).orient("left").tickValues([-1, 0, 1]));
//// simulation trajectory ///
var drawFlag = true,
frameRate = 20;
d3.timer(function () {if (drawFlag) {draw();}}, frameRate);
g.globalCompositeOperation = "source-over";
function draw() {
g.fillRect(0, 0, width, height); // fades all existing curves by a set amount determined by fillStyle above
g.beginPath();
g.moveTo(scx(x)+mw, scy(y));
for (var i=0; i<5; i++) {evolve_simulation();}
g.lineTo(scx(x)+mw, scy(y));
g.stroke();
}
//// events ////
var slider = document.querySelector("#slider"),
sigma = slider.value/100;
document.getElementById("sliderValue").innerHTML = sigma.toPrecision(2);
slider.addEventListener("input",
function () {
sigma = slider.value/100;
document.getElementById("sliderValue").innerHTML = sigma.toPrecision(2);
g.strokeStyle = linecolor(sigma/0.5);
}, false);
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment