Interactive LBM Simulation - 2D

Fluid-Dynamics Interactive Lattice-Boltzmann Physics Simulation

This is an interactive simulation of the Boltzmann equation in 2D. For the theory behind the simulation see the previous posts 1D Lattice Boltzmann Theory and Extending the LBM to 2D and 3D. As for the 1D simulation it is written in Rust, compiled to Wasm and uses some JavaScript glue to handle the interactivity & plotting.

Move the mouse over the canvas below…

0 fps

(Apologies, for some reason the simulation display does not work in Firefox.)

Moving the mouse imparts a localized velocity to the fluid which matches the direction you’re moving the mouse. Moving faster imparts a correspondingly faster velocity.

If you move the mouse fast enough, or in a circular motion, you can see the fluid form vortices. Then then stretch, move & merge with the flow, eventually dissipating.

Changing the slider updates the value of $\tau$, and thus the viscosity. For higher values, the fluid will slow down faster. The simulation has periodic boundary-conditions.

The drop-down menu can be used to display different properties of the fluid. The most interesting is the so-called vorticity external link , which is the default, but you can also choose to look at the density fluctuations or the velocity field itself.

Because the velocity field is a 2D vector field it can be tricky to represent with just colours. Here I’ve taken the approach of mapping the direction of the fluid motion to the hue of the colour, and the speed to the saturation. This means that the greyer the fluid, the slower it is moving, and the more technicolour, the faster. This is quite a common approach for displaying functions of complex numbers, but I’m not convinced it works well for a dynamic field such as we have in fluid dynamics. Visualising flow-fields is an interesting topic in itself, which perhaps I’ll write about at some point.

Performance

I added the FPS meter to the simulation to check its performance. The simulation is not huge, so I had hoped that using Rust+Wasm would mean I could achieve reasonable performance while keeping the code structure very simple. Unfortunately it does seem quite slow. On my plugged-in laptop I originally saw ~20 FPS; unacceptably slow. The vast majority of the time spent is inside the call to my Wasm function, which actually does the calculations, not in the JavaScript part that does the rendering. There are some obvious optimizations and improvements that can be made, and I may try implementing some of them. However, I’ll have to take care to keep the code both readable and relatively dimension & velocity-set agnostic.

Edit 2024/09/30:

I have tried a couple of the most obvious optimisations and I’m now seeing ~55 FPS on my laptop. Still not amazingly fast but quite an improvement. The optimizations were

  1. Pre-compute the offset indicies with periodicity for the streaming, and read them from memory.
  2. Split the main loop over cells into two; one which handles the streaming and one for calculating the macroscopic properties and local equilibrium.
  3. Explicitly write out the D2Q9 equilibrium calculations for each of the 9 components.

Despite pre-computing meaning we need to read from an extra array, it seems to help markedly over computing the offsets on-the-fly. Splitting the loop lets us make the streaming downstream rather than upstream, which lets us read contiguously but write discontiguously. It’s not obvious that this way should be faster but in practice it is. The third optimisation avoids needlessly calculating many factors which will ultimately turn out to be zero.

Edit 2024/12/20:

Revisiting this, I have implemented an in-place streaming algorithm which avoids the needs for two arrays to store the distribution functions. This has the added benefit of improved memory locality since we are reading and writing to the same memory, rather than reading from one (for streaming & collision) and storing to another. The specific in-place streaming algorithm I have implemented is the ‘AA-pattern’; it is described nicely in this paper external link .

This has given a further boost from ~55 FPS to ~78 FPS on my laptop. (Note, your browser may limit to 60 FPS depending on your screen refresh rate.) In fact the boost is even slightly bigger since for technical reasons I had to increase the number of timesteps per frame from 15 to 16 because I needed an even number. This means we are performing almost 7% more calculations per frame and have increased the frame-rate. For this reason it’s better to consdier the ‘lattice updates per second’ which has increased from ~18 Mlups to ~28 Mlups; quite a nice increase!

Extending the LBM to 2D and 3D
Order Statistics