Desired Goal
My goal was to implement a system for rendering a gaseous fluid (in this case, smoke) that could simulate the microscopic behavior of a gas and render its macroscopic particles in real-time for both a 2D and 3D volume. This system solves the problem of modeling a fluid's motion without having to solve the full non-linear and incompressible Navier-Stokes equations. My system should be able to take initial density, velocity, and viscosity information and model the dispersal of a fluid from an initial inlet cell over the span of pre-determined grid lattice. The only other force affecting the expansion of the gas besides the core rules is the boundary conditions.
Results
I implemented a lattice data structure that defines a 3D space from which a fluid's motion can be modeled over a series of time steps. This 3D space is really a stack of 2D slices, each of which can be easily observed independently. The inlet cell from which the smoke originates is arbitrarily assigned, as are the initial conditions under which the smoke's motion dynamics are derived; these are density, velocity, and relaxation time (a function of viscosity). The dimensions of the lattice are also arbitrary. To model the smoke motion in a 2D space I use a lattice depth of 1, effectively limiting the fluid's degrees of movement. Typically, I model the fluid with the inlet cell set in the center of the lattice (or 2D slice). Boundary conditions are handled using Bounce-Back, which takes the packet distributions with a velocity vector pointing towards a boundary and reverses them so they instead propagate in the direction from which they originated. The following are pre-compiled models for smoke that use different lattice dimensions, and in some cases different inlet cell positions:
LBMSmoke-2D-10.exe: 2D slice with dimensions 10x10
LBMSmoke-2D-50-CNR.exe: 2D slice with dimensions 50x50 and an inlet cell in the upper left corner
LBMSmoke-2D-50-CTR.exe: 2D slice with dimensions 50x50 and an inlet cell in the center
LBMSmoke-3D-50.exe: 3D slice with dimensions 50x50x50 and a central inlet cell
Instructions: Press the 'f' key to advance the time step by 1. Use the mouse to navigate the 3D space. Left-click and drag to rotate around the central point. Middle-click and drag to zoom in and out. Right-clickk and drag to change the central point that the camera rotates around.
The following are some pictures taken from the motion renderings:
LBM-2D.png
LBM-2D-2.png
LBM-3d.png
Methods
The primary method I used is the Lattice-Boltzmann Model for Gaseous Phenomena. The LBM uses a derivative of a cellular automata structure to simulate the microscopic 'packet' behavior of a fluid and from it derive the macroscopic particle values. Two types of rules govern the movement of microscopic packets: collision and propagation. A collision operator defines how the packets move along the lattice cell edges and how they interact with other packets colliding with them on opposing vectors. The propagation rule moves the microscopic packets to the nearest neighboring cell along the approriate velocity direction.
For this project, I implemented my own lattice grid structure. The lattice itself was composed of multiple slices, where each slice was a 2D grid of cell structures. Each cell structure has its own sublattice geometry that models the edges between the cell and 19 of its 26 neighbors in 3-space. The rules for propagation and collision I took directly from the paper, along with all supporting equations. I also wrote my own basic volume renderer to display the results of the fluid motion. The paper implements a complex system using graphics hardware and texture splatting. My method draws the lattice (or a 2D slice, depending on the output settings) and renders the fluid density at cell using a color gradient to represent the density. Lower densities are represented by darker-colored cells and high densities are almost fully opaque.
Learning Experience
I learned quite a bit about why rendering a physically-accurate fluid model is so computationally difficult and what properties about fluids are important to preserve, such as conservation of mass and momentum. Complementary to this, I also discovered a novel way of approximating the same physical accuracy of fluid motion that is preserved in the Navier-Stokes equations; the Lattice-Boltzmann Method. By modeling the microscopic particles in such a way that they preserve certain key behaviors of a physically-accurate fluid, one can discretely derive the macroscopic densities with a high level of accuracy. Furthermore, by taking advantage of the computing power of contemporary graphics hardware, it is even possible to render this system in real-time.
To be sure, however, solving a discrete NS-approximating system is not a trivial problem. Maintaining a 3D connected lattice over several time step iterations proved to be very difficult initially, and even more difficult to do debug when problems arose. Using small grid volumes did not help matters, especially in cases where a problem would not propagate until the model evolved past a managable size.
In terms of project planning, I learned that you must always leave extra time for the unforeseen problems that tend to crop up. Too often during this project did I lull myself into a false sense of security with a strong run of progress only to be caught off-guard by a problem or complication that would easily negate all the progress I had previously made. Stumbling into a problem with no foreseeable solution is the worst because there is no guide on how to plan around it.
I have also decided that working by myself has some inherent downsides that I would like to avoid in the future. The most pertinent of these was the lack of having someone else to collaborate with on ideas and conceptual understanding. Although it helps to have a TA or professor that understands the background of your work, their availability cannot be taken for granted. Having a fellow colleague working in tandem with you at least broadens that availability, and even more importantly, gives you perspective on when you have encountered a problem that is indeed exceptionally difficult and requires additional help. When working by myself, I often come across stumbling blocks where the answer is not obvious, nor is the estimation of how long it might take me to grasp the concept I don't understand.
Self-Evaluation
Despite the fact that I did not accomplish everything I set out to do, I am generally happy with what I was able to achieve. It would have been nice to resolve my rendering issue with the invisible bounding planes and implementing a better rendering system in general, but the important pieces are there and I can output results that are quite satisfactory to me. If I had more time, I would have very much liked to implement the temperature field and played around with different velocity maps to see how that affected my results.
Considering that I wrote the entire underlying data structure by hand and that my approach to it was largely untested, I'm very pleased that it works so well and with a modular design that is still elegant and straight-forward.
The biggest hold-ups on my progress were the problems and implementation issues that I did not foresee ahead of time, either due to a misunderstanding on my part, or because of a syntactic issue that I could not immediately resolve. For example, I had completed my initial implementation and was outputting what I believed to be accurate results, only to go back and discover that I misread an equation and had to rewrite whole parts of the algorithm. The more annoying issues should have been avoidable, such as not getting my 3D window to draw the lattice correctly because my perspective settings were all wrong, or having linker errors in C++ that turned out to be more dependent on my knowledge (or lack thereof) of the FLTK library. These are a few of the minor examples.
My advice to anyone attempting a similar project in the future would be this: (1) Start early. Too often during the lifecycle of this project did I get drawn into a false sense of security and let my time slide only to have a very time-consuming problem crop up later on. (2) First write your data structures, followed immediately by a visual output system. Having a visual output of my numerical results would have helped me identify some problems earlier. When building a system like this that includes some very complicated equations, it is sometimes better not to rely solely on numerical results to identify potential problems.
Impact on Future Project
The project made me fully appreciate the complexity behind properly simulating fluid dynamics of motion. Not only is it conceptually difficult write a physically-accurate model, but it is also incredibly hard to debug due to the large amount of intermediate numerical data. Having no experience with any form of fluid modeler before this also added to the complexity of the project. Before I could even start working, there was a large amount of background material I had to grasp first. I am happy I undertook this project because it did allow me to study a problem that I had been interested in for some time. However, I would like my next project to revolve around a topic that I already have some prior knowledge of.