Verlet for Spring Damper System Simulation
Author: Win Aung ChoVerlet integration is a numerical method used to integrate Newton’s equations of motion. It is frequently used to calculate trajectories of particles in molecular dynamics simulations and computer graphics. The algorithm was first used in 1791 by Delambre and has been rediscovered many times since then, most recently by Loup Verlet in the 1960s for use in molecular dynamics.
In simple terms, Verlet physics is just a system of connected dots in stepped time domain.
Verlet's integrator is an algorithm whose purpose is to numerically integrate ordinary second-order differential equations. What makes it appealing for molecular dynamics (MD) is its invariance under time-reversal and its ability to accurately conserve the energy of the system.
There are 2 types of algorythms.
Position Verlet Algorithm
x(t+Δt)=2x(t)-x(t-Δt)+Δt²•F(x(t))/m + O[Δt⁴]
v(t)=1/(2Δt)•[x(t+Δt) - x(t-Δt)] + O[Δt²]
Velocity Verlet Algorythm
x(t+Δt)=x(t)+Δt•v(t)+1/2•Δt²•F(x(t))/m + O[Δt⁴]
v(t+Δt)=v(t)+Δt•F(x(t))/m + O[Δt²]
In simple terms, Verlet physics is just a system of connected dots in stepped time domain.
Verlet's integrator is an algorithm whose purpose is to numerically integrate ordinary second-order differential equations. What makes it appealing for molecular dynamics (MD) is its invariance under time-reversal and its ability to accurately conserve the energy of the system.
There are 2 types of algorythms.
Position Verlet Algorithm
x(t+Δt)=2x(t)-x(t-Δt)+Δt²•F(x(t))/m + O[Δt⁴]
v(t)=1/(2Δt)•[x(t+Δt) - x(t-Δt)] + O[Δt²]
Velocity Verlet Algorythm
x(t+Δt)=x(t)+Δt•v(t)+1/2•Δt²•F(x(t))/m + O[Δt⁴]
v(t+Δt)=v(t)+Δt•F(x(t))/m + O[Δt²]

Spring Damper Simulation
class SDOF { constructor() { this.rest = 15; // rest length of spring this.k = 50.0; // stiffness of spring this.c = 10.5; // damping coefficient of spring this.mass = 5.5; // lumped mass of hanger // set the initial state of the spring this.ypos = 13.1; // current position of mass this.yoldpos = 10.0; // previous position of mass this.GravityForce = 9.81 * this.mass; // force due to Earth's gravity (mass times acceleration } update() { // compute the force of the spring var y = this.ypos - this.rest; var Force = -this.k * y; // Hooke's Law // add damping to the spring force // Note: without damping to release energy spring will bounce forever var v = (this.ypos - this.yoldpos); // velocity Force += -this.c * v; // add spring damping to force // compute the acceleration of the masy var a = (Force + this.GravityForce) / this.mass; // advance time by dt using verlet integration var next_position = 2 * this.ypos - this.yoldpos + a * dt * dt; this.yoldpos = this.ypos; this.ypos = next_position; } render() { draw(this); } }

JS code object for SDOF system
Main loop for time stepping
function animate() { requestAnimationFrame(animate); // request next timestep Spring.update(); // perform verlet integration Spring.render(); // draw the system t = t + dt; }
Drawing System
function DrawThread(xc, yc, w, h, ctx) { ctx.moveTo(xc, yc); ctx.lineTo(xc + w / 2, yc + h / 4); ctx.lineTo(xc - w / 2, yc + h / 4 * 3); ctx.lineTo(xc, yc + h); } function DrawSpring(xc, yc, len, ctx) { ctx.beginPath(); ctx.moveTo(xc, yc); ctx.lineTo(xc, yc + (len * 10) * 0.30); for(i = 0; i < 8; i++) DrawThread(xc, yc + (len * 10) * (0.30 + i * 0.05), 20, (len * 10) * 0.05, ctx); ctx.moveTo(xc, yc + (len * 10) * 0.70); ctx.lineTo(xc, yc + len * 10); ctx.closePath(); ctx.stroke(); } function draw(s) { html = "Spring Length = " + s.rest.toFixed(2) + ""; html += "Dumping Coefficient = " + s.c.toFixed(2) + ""; html += "Spring Stiffness = " + s.k.toFixed(2) + ""; html += "Mass = " + s.mass.toFixed(2) + ""; html += "Gravitional Constant = " + (s.GravityForce / s.mass).toFixed(2) + ""; //html += "Deformation = " + y.toFixed(2) + ""; document.getElementById("output").innerHTML = html + "Current time = " + t.toFixed(2) + " Position of mass = " + s.ypos.toFixed(2); // clear drawing canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // draw the spring ctx.strokeStyle = "0000FF"; DrawSpring(300, 50, s.ypos, ctx); // draw the mass ctx.fillStyle = "000000"; ctx.strokeStyle = "FFFFFF"; ctx.beginPath(); ctx.arc(300, 50 + s.ypos * 10, 20, 0, 2 * Math.PI); ctx.closePath(); ctx.stroke(); ctx.fill(); // draw the ceiling ctx.fillStyle = "000000"; ctx.fillRect(0, 50, canvas.width, 5); } init(); animate();
Complete html file
<html> <body> <h1>1 Dimensional Spring Simulation</h1> <div id="output"></div> <canvas id="Canvas"></canvas> <script> var t = 0; // the current time at start var dt = 0.1; // timestep for integration var canvas, ctx; var Spring; class SDOF { constructor() { this.rest = 15; // rest length of spring this.k = 50.0; // stiffness of spring this.c = 10.5; // damping coefficient of spring this.mass = 5.5; // lumped mass of hanger // set the initial state of the spring this.ypos = 13.1; // current position of mass this.yoldpos = 10.0; // previous position of mass this.GravityForce = 9.81 * this.mass; // force due to Earth's gravity (mass times acceleration } update() { // compute the force of the spring var y = this.ypos - this.rest; var Force = -this.k * y; // Hooke's Law // add damping to the spring force // Note: without damping to release energy spring will bounce forever var v = (this.ypos - this.yoldpos); // velocity Force += -this.c * v; // add spring damping to force // compute the acceleration of the masy var a = (Force + this.GravityForce) / this.mass; // advance time by dt using verlet integration var next_position = 2 * this.ypos - this.yoldpos + a * dt * dt; this.yoldpos = this.ypos; this.ypos = next_position; } render() { draw(this); } } function init() { t = 0; dt = 0.1; // resize the drawing canvas to fill browser window canvas = document.getElementById("Canvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight / 2; ctx = canvas.getContext("2d"); Spring = new SDOF(); } // animation loop for each time step function animate() { requestAnimationFrame(animate); // request next timestep Spring.update(); // perform verlet integration Spring.render(); // draw the system t = t + dt; } function DrawThread(xc, yc, w, h, ctx) { ctx.moveTo(xc, yc); ctx.lineTo(xc + w / 2, yc + h / 4); ctx.lineTo(xc - w / 2, yc + h / 4 * 3); ctx.lineTo(xc, yc + h); } function DrawSpring(xc, yc, len, ctx) { ctx.beginPath(); ctx.moveTo(xc, yc); ctx.lineTo(xc, yc + (len * 10) * 0.30); for(i = 0; i < 8; i++) DrawThread(xc, yc + (len * 10) * (0.30 + i * 0.05), 20, (len * 10) * 0.05, ctx); ctx.moveTo(xc, yc + (len * 10) * 0.70); ctx.lineTo(xc, yc + len * 10); ctx.closePath(); ctx.stroke(); } function draw(s) { html = "Spring Length = " + s.rest.toFixed(2) + ""; html += "Dumping Coefficient = " + s.c.toFixed(2) + ""; html += "Spring Stiffness = " + s.k.toFixed(2) + ""; html += "Mass = " + s.mass.toFixed(2) + ""; html += "Gravitional Constant = " + (s.GravityForce / s.mass).toFixed(2) + ""; //html += "Deformation = " + y.toFixed(2) + ""; document.getElementById("output").innerHTML = html + "Current time = " + t.toFixed(2) + " Position of mass = " + s.ypos.toFixed(2); // clear drawing canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // draw the spring ctx.strokeStyle = "0000FF"; DrawSpring(300, 50, s.ypos, ctx); // draw the mass ctx.fillStyle = "000000"; ctx.strokeStyle = "FFFFFF"; ctx.beginPath(); ctx.arc(300, 50 + s.ypos * 10, 20, 0, 2 * Math.PI); ctx.closePath(); ctx.stroke(); ctx.fill(); // draw the ceiling ctx.fillStyle = "000000"; ctx.fillRect(0, 50, canvas.width, 5); } init(); animate(); </script> </body> </html>
Demo
Author: Win Aung Cho
12-Jun-2022 04:55:48 PM*