Week 2: So It Begins!
Hello everyone, welcome to Week 2 of my blog!
A little recap of what I did this week before I jump into the details… I read a bunch of papers on the Lattice Boltzmann Model and started on writing code.
Concept of a lattice grid: There is a lattice grid (hence the name Lattice Boltzmann Method) that particle distributions float around on. For my program, I used a 400×100 grid in the x and y direction. The top/bottom and left/right bounds of the lattice grid are the walls and inlet/outlet of the channel respectively. The figure below is a zoomed in view of one lattice node. At each node, there are 9 velocity vectors (denoted with e in the figure); 8 pointing to neighboring nodes and 1 zero vector. In each of these 9 directions, there is a particle distribution function, which basically describes the probability of a particle having that particular velocity. If you’re trying to imagine each of these little particles as water droplets, this might not make too much sense, but just let go of that thought and roll with the idea of many particle distributions moving around a lattice grid (this is LBM’s main difference with the Navier-Stokes solvers). We’ll see later that equations can turn these discrete particle distributions and velocity unit vectors into the macroscopic velocity and density of a fluid that we’re more used to seeing. But for now, just imagine that if most of your particle distributions have the velocity vector e1, pointing to the right, you’ll end up with a fluid that has a macroscopic velocity in the general rightward direction as well.
Shown below is a D2Q9 lattice grid. LBM models are typically implemented on a DnQm lattice, meaning n dimensions and m velocity directions. A 2D, 9-directional lattice was used, D2Q9. A velocity unit vector ei is assigned for each direction in the nine-velocity model. LBM can also be implemented in 3d, with D3Q15, D3Q19, and D3Q27 being the most common models.
Algorithm overview: The fluid’s motion is divided into two main steps: streaming and colliding. A flowchart that summarizes the whole thing:
Streaming is when the particle distributions at every node “stream” to their neighboring nodes. For example, referring back to the figure above of a lattice node, the particles that have velocity e2, lets call that particle distribution f2, will move to the lattice node above this one. Every particle on every lattice node of the grid will move in the direction described by its velocity (the particles with 0 velocity will stay at that node). We’ll talk about what happens at the boundaries a little later.
The collision step describes what happens when particles have streamed in from the 8 different directions and “collide” with each other at a node. Another idea that is involved is the equilibrium distribution. When particles arrive at a new node after streaming, they will redistribute themselves and arrive at this equilibrium distribution. The relaxation time is the time that it takes to “relax” to equilibrium. In the code snippet shown in the next section, you’ll see relaxation time as a parameter, relating to Reynolds number. The Reynolds number isn’t so relevant in the rest of the code, but it essentially tells us if we except to see laminar (low Re) or turbulent flow (high Re) in our simulation.
Python implementation: I used a 3d list to hold the particle distributions, two dimensions for the x and y position on the lattice grid and the third for the direction of the velocity. For example f[200,50,2] would describe the particle distribution at x=200 y=50 with velocity e2. The streaming step in Python is made super simple with the NumPy library, specifically the function np.roll, which “rolls” vectors in the direction you direct it to. The collision step is not as simple, but we’ll talk about that more next week. Below is a screenshot of my code including some parameters with comments explaining the meaning. The lattice speeds and weights are what was described with the figure earlier in this post (except in the code you’ll see cx and cy as the unit velocity vector split up into the x and y directions). Something additional to note that will be relevant later is each direction is assigned a weight, and the weights of all nine directions add up to one. We don’t really need to worry about the initial conditions yet, but basically it initializes the particle distributions for the whole lattice, kept in the 3d list named f.
Thank you for reading! Next week, I’ll continue working on my code and will post a blog update of the progress.
Kruger T., et al, The Lattice Bolzmann Method Principles and Practice, Springer Cham, 2017
Mohamad, A. A. Lattice Boltzmann Method Fundamentals and Engineering Applications with Computer Codes, Springer, London, UK, 2011
Succi, Sauro. (2003). The Lattice Boltzmann Equation – For Fluid Dynamics and Beyond. 10.1016/S0997-7546(02)00005-5.
Chen, S., Mart ́ınez, D., Mei, R. (1996). On boundary conditions in lattice Boltzmann methods. Physics of Fluids, 8(9), 2527–2536.
Chen, S., Wang, Z., Shan, X., Doolean, G. Lattice Boltzmann computational fluid dynamics in three dimensions. J Stat Phys 68, 379–400 (1992).
Frisch U., Hasslacher B., Pomeau Y. Lattice Gas Automata for the Navier-Stokes Equation. Phys- ical Review Letters, vol. 56, no. 14, April 1986, p. 1505.
Boltzmann, L. Lectures on Gas Theory; Courier Corporation: Chelmsford, MA, USA, 2012.