EduCSE Blog

Share

Verlet for Spring Damper System Simulation

Author: Win Aung Cho
Verlet 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²]

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*