Sunday July 21, 2024
 Home | Contact | Support | JavaScript... so much power in such a few lines of code..
 JavaScript... so much power in such a few lines of code..

Quick introduction in to basic fluid (and gas) dynamics with a practical emphasis (not just theory but actually implementing and seeing things work).

Keywords: smoke, gas, dynamics, grid, simulation, real-time, animation, vortex, diffuse

Fluids are really cool and also very common - they're all around you! The human body is 65% water, and the Earth is 2/3 water.. while gasses are like fluids brother - they have a very similar set of properties - like air, smoke and clouds.

If you drop in in water or watch smoke float up into the sky - I'm sure you noticed the patterns of similarity?

Anyhow, fluid and gas dynamics are all around you! The're magical and beautiful - and you can emulate their characteristics in code.

So how do you go about simulating these things? Is it hard?

Well fluid dynamics is a big area! With lots of different models - with levels of accuracy and computation cost - however, for the most part of this tutorial I'll focus on `visually' realistic simulations that make numerical approximations. Looks and moves correctly! Also fast and we can interact with it in real-time.

Few concepts and their details - which we're going to try and include:

Diffusion: In a cup of tea, sugar molecules diffuse from areas of high concentration to low concentration, sweetening the drink evenly.
Vortex: A whirlpool forms when water rushes down a drain, creating a swirling vortex of water.
Buoyancy: A ship floats because its weight is less than the weight of water it displaces, thanks to buoyancy.
Viscosity: Honey flows slowly due to its high viscosity, resisting deformation and sticking to surfaces.

# Simple Discrate Fluid/Gas Simulation Demo

The code uses JavaScript for the fluid/gas dynamics - as it's easy to follow and can run anywhere (just need a web browser). Focus on 2D simulations - so that it doesn't get overy complex. But it should be enough to show swirly ink-like animation patterns.

The concept implementation has been broken down into key functions - each performing a specific task (like diffusion and buoyancy).

The full code is also implemented on a demo page (LINK).

The following gives the full working code (simplfied) that can run from a single file (no external libraries or resources required).

<!DOCTYPE html>
<
html lang="en">
<
<
meta charset="UTF-8">
<
meta name="viewport" content="width=device-width, initial-scale=1.0">
<
title>Fluid and Gas Dynamics Simulation XBDEV Educational Tech Demo</title>
<
style>

canvas {

border1px solid black;
}
</
style>
</
<
body>
<
canvas id="canvas" width="400" height="400"></canvas>
<
script>

// Get the canvas element and its context

const canvas document.getElementById('canvas');
const
ctx canvas.getContext('2d');

// Define the size of the grid

const resolution 2.0;
const
cols canvas.width resolution;
const
rows canvas.height resolution;

// Create arrays to hold the density, u velocity, and v velocity

let density = new Array(cols).fill(0).map(() => new Array(rows).fill(0));

let u = new Array(cols).fill(0).map(() => new Array(rows).fill(0));

let v = new Array(cols).fill(0).map(() => new Array(rows).fill(0));

// Initialize density with a letter pattern

const letter "G"// Change the letter here

ctx.font "bold 150px Arial";

ctx.fillText('X'50150);

// Convert the letter on canvas to the density array

const imageData ctx.getImageData(00canvas.widthcanvas.height);
for (
let i 0colsi++) {
for (
let j 0rowsj++) {
if (
imageData.data[(imageData.width) + (4) + 3] > 128
{

density[i][j] = 1.0;
}

}
}

// initial velcoity

for (let i 0colsi++) {
for (
let j 0rowsj++) {
const
resolution canvas.width 2;
const
resolution canvas.height 2;

// Introduce some randomness to the velocity initialization

u[i][j] = Math.cos*  cols ) * Math.sincols ) * 0.1;

v[i][j] = Math.sin16 cols ) * Math.sincols ) * 0.5;
}
}

// Function to update the simulation

function update() {

// Apply diffusion

let diff 0.01;

diffuse(diffu);

diffuse(diffv);

applyBuoyancy(densityv);

// Apply curl

curl();

// Draw the updated fields

render();

// Request animation frame for continuous update

requestAnimationFrame(update);
}

// Function to apply diffusion

function diffuse(difffield) {
const
dt 0.1// Time step

const visc 0.0001// Viscosity

const amount dt visc * (cols 2) * (rows 2) * diff;

for (
let k 010k++) { // Iterations for stability

for (let i 1cols 1i++) {
for (
let j 1rows 1j++) {

field[i][j] = (field[i][j] + amount * (

field[1][j] + field[1][j] +

field[i][1] + field[i][1])) / (amount);
}
}
}
}

// Function to apply buoyancy force

function applyBuoyancy(densityv) {
const
gravity 0.05// Strength of gravity

const buoyancyAlpha 0.1// Buoyancy coefficient for density difference

const buoyancyBeta 0.1// Buoyancy coefficient for vertical velocity

const ambientTemperature 0// Ambient temperature

for (let i 0colsi++) {
for (
let j 0rowsj++) {
const
densityDifference density[i][j] - ambientTemperature;
const
buoyancyForce = -buoyancyAlpha densityDifference buoyancyBeta v[i][j];

// Apply buoyancy force to vertical velocity

v[i][j] += buoyancyForce gravity;
}
}
}

// Function to apply curl

function curl() {
const
dt 0.1// Time step

const curlCoefficient 1.0;

for (
let i 1cols 1i++) {
for (
let j 1rows 1j++) {
const
du_dy = (u[i][1] - u[i][1]) / (resolution);
const
dv_dx = (v[1][j] - v[1][j]) / (resolution);

const
curl dv_dx du_dy;

u[i][j] += -curl dt curlCoefficient;

v[i][j] +=  curl dt curlCoefficient;
}
}
}

// Function to advect the density field
const
dt 0.1// Time step

const temp = new Array(cols).fill(0).map(() => new Array(rows).fill(0));

for (
let b=0b<7b++)
for (
let i 1cols 1i++) {
for (
let j 1rows 1j++) {

let x u[i][j] * dt resolution;

let y v[i][j] * dt resolution;

//x += (Math.random() * 2 - 1) * 0.1; // Introduce random noise in x direction
//y += (Math.random() * 2 - 1) * 0.1; // Introduce random noise in y direction

// Clamp advection position to canvas boundaries

if (0.50.5;
if (
cols 0.5cols 0.5;
if (
0.50.5;
if (
rows 0.5rows 0.5;

const
i0 Math.floor(x);
const
i1 i0 1;
const
j0 Math.floor(y);
const
j1 j0 1;

const
s1 i0;
const
s0 s1;
const
t1 j0;
const
t0 t1;

field[i][j] = s0 * (t0 field[i0][j0] + t1 field[i0][j1]) +

s1 * (t0 field[i1][j0] + t1 field[i1][j1]);
}
}
}

// Function to render the fields

function render() {

ctx.clearRect(00canvas.widthcanvas.height);

for (
let i 0colsi++) {
for (
let j 0rowsj++) {
const
resolution;
const
resolution;

// Draw density

ctx.fillStyle = `rgba(0, 0, 0, \${density[i][j]})`;

ctx.fillRect(xyresolutionresolution);

/*
// Draw velocity vectors
ctx.beginPath();
ctx.moveTo(x + resolution / 2, y + resolution / 2);
ctx.lineTo(x + resolution / 2 + u[i][j] * 10, y + resolution / 2 + v[i][j] * 10);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
ctx.stroke();
*/

}
}
}

// Start the simulation

update();
</
script>
</
body>
</
html>

# Terms/Abbreviations/Glossary

CFD - Computational Fluid Dynamics
PDEs - Partial Differential Equations
FDM - Finite Difference Methods