I recently came across this fluid dynamics simulation and have been trying to read up on the lattice Boltzmann method in order to better understand it. Most of the code there is pretty transparent, so between just reading the code and reading Wikipedia I have pretty much figured what everything does... except for the kinematic calculations in the core ``collide` function.
I've been able to figure out that the factors of 1/9, 4/9, and 1/36 are related to the lengths of the vectors connecting cell centers along different lattice directions, and I can even find resources that explain what different factors to use for different lattice types (with a D2Q9 lattice being used in this code). But I haven't been able to find anything that explains how you go from the generic vector equation which defines the collision step of the Lattice Boltzmann Algorithm to the specific nine lines of implementation arithmetic shown below. In particular, where do those factors of 3, 1.5, and 4.5 comes from?
The code used in the linked web page (with minor edits to remove free variables and improve readability) is as follows:
function collide(viscosity) { // kinematic viscosity coefficient in natural units
var omega = 1 / (3*viscosity + 0.5); // reciprocal of relaxation time
for (var y=1; y<ydim-1; y++) {
for (var x=1; x<xdim-1; x++) {
var i = x + y*xdim; // array index for this lattice site
var thisrho = n0[i]+nN[i]+nS[i]+nE[i]+nW[i]+nNW[i]+nNE[i]+nSW[i]+nSE[i];
// macroscopic horizontal velocity
var thisux = (nE[i]+nNE[i]+nSE[i]-nW[i]-nNW[i]-nSW[i])/thisrho;
// macroscopic vertical velocity
var thisuy = (nN[i]+nNE[i]+nNW[i]-nS[i]-nSE[i]-nSW[i])/thisrho;
var one9thrho = thisrho / 9;
var one36thrho = thisrho / 36;
var ux3 = 3 * thisux;
var uy3 = 3 * thisuy;
var ux2 = thisux * thisux;
var uy2 = thisuy * thisuy;
var uxuy2 = 2 * thisux * thisuy;
var u2 = ux2 + uy2;
var u215 = 1.5 * u2;
n0[i] += omega * (4/9*thisrho * (1 - u215) - n0[i]);
nE[i] += omega * ( one9thrho * (1 + ux3 + 4.5*ux2 - u215) - nE[i]);
nW[i] += omega * ( one9thrho * (1 - ux3 + 4.5*ux2 - u215) - nW[i]);
nN[i] += omega * ( one9thrho * (1 + uy3 + 4.5*uy2 - u215) - nN[i]);
nS[i] += omega * ( one9thrho * (1 - uy3 + 4.5*uy2 - u215) - nS[i]);
nNE[i] += omega * ( one36thrho * (1 + ux3 + uy3 + 4.5*(u2+uxuy2) - u215) - nNE[i]);
nSE[i] += omega * ( one36thrho * (1 + ux3 - uy3 + 4.5*(u2-uxuy2) - u215) - nSE[i]);
nNW[i] += omega * ( one36thrho * (1 - ux3 + uy3 + 4.5*(u2-uxuy2) - u215) - nNW[i]);
nSW[i] += omega * ( one36thrho * (1 - ux3 - uy3 + 4.5*(u2+uxuy2) - u215) - nSW[i]);
}
}
}