FLUIDS v.2 - A Fast, Open Source, Fluid Simulator (2009)

ZLib license. CPU & GPU simulator.
- CPU. Requires basic graphic card (GeForce 5800 or higher). Uses freeglut32.
- GPU. Requires CUDA capable card (GeForce 8800GTX or higher), CUDA 2.3 Drivers & Toolkit. Tested with Visual Studio 2008.
Windows download: fluids_v2.zip
Linux download: fluids_v2.tar.gz
(Thanks to Fariza Dian Prasetyo, Institute Technology Bandung, Indonesia for contributing CUDA build for Linux)

*NEW* FLUIDS v.3 - A Large-Scale, Open Source, Fluid Simulator has just been released!
Fluids v.3, released Dec 2012, is now availabe on the new website: http://fluids3.com

The latest version features up to 8,000,000 particles, better performance, and ease of use.

With a history in astrophysics, there is a general preception that Smoothed Particle Hydrodynamic simulations are difficult or complex. FLUIDS v.1 was developed as an easy-to-use, open source, SPH implementation which is readable, efficient, and easy to understand. In addition, it is the most efficient CPU-based implementation of SPH currently available. FLUIDS v.1 was designed to allow novice developers to begin doing research in SPH.

The reader is referred to the following instructional resources on SPH theory:

2006. Micky Kelager (DIKU, Copenhagen), Lagranian Fluid Dynamics Using Smoothed Particle Hydrodynmics

2004. Marcus Vesterlund (Umea Univ, Sweden), Simulation and Rendering of Smoothed Particle Hydrodynamics

Surface Reconstruction:
"> I've received several email about surface reconstruction of SPH particles. To facilitate discussion, and help those interested in this research, I've started a web blog page dedicated to SPH surface rendering for research and discussion. Check it out here:
Surface Reconstruction of SPH Fluids

Notes for the Novice:

- You've probably implemented a simple, fast particle system, and are thinking to yourself: "Hey, if i just put in the right inter-particle forces, these particles could maybe move like a fluid.". These notes are not meant to disuade you, but to help you understand why its not so trivial, and how to do it right.

- What follows is a brief study of how the above code is not like your typical particle system.

- First, the Navier-Stokes equations are scale sensitive, so you need to be working in proper units, at the real scales of actual fluids. In the code above, the actual simulation scale is around 1mm^3 up to 1 cm^3.

- Second, you might be thinking that the most important part is the pressure/force calculations, and that you can just plug in a trivial solution for boundary conditions -- say, for example: "Lets just reflect the Y axis if we hit a boundary". This will not only fail, it will give a simulation that explodes, because you're actually putting more energy into the system than it previously had. In other words, unlike a trivial particle system, you need to conserve energy in boundary collisions. In addition, boundaries need to give a continuous response, since a discrete test (inside/outside wall) introduces high frequency terms that are difficult to integrate. The above code shows these smooth response, energy-conserving boundary conditions.

- Third, know your parameter ranges. You say to yourself: "Well, I'll just try some different parameter values and see what happens." Unfortunately, if you're off by an order of magnitude on any parameter, then most likely the simulation will explode. This should make sense. If the time step is 10x higher.. explosion. If the internal stiffness is 10x too high, the fluid will break the integrator. So, be careful with your parameter values.
.
- Fourth, SPH fluid simulations in particular don't behave the same with small numbers of particles. You think to yourself: "Ok, so if I can't get 100 to work, I'll just try with 10 first.".. A reasonable approach for some problems, but fluid simulations become less stable with fewer particles. This is because the support radius per particle has less information to contribute to the solution.. Thus, if you scale back to 10 particles you give each particle less information to go on to compute pressure/forces in the next step. Also, with these few particles, it becomes very hard to detect if the system is moving correctly as a fluid.

- Finally, pay attention to initial conditions. The best test for stability is a resting body of water -- that is, it should just sit there. But, if your initial conditions place the particles too far apart, or too close, the liquid will rapidly collapse in on itself or expand in order to achieve rest density, possibly breaking your integrator.. Proper initial conditions, and good tests, are the best ways to debug.. The "damn break" is a common challenge, in which the left half of a volume is filled with liquid, since the behavior of the fluid in this situation is known very well (it should spash against the opposite wall, create a reflected wave, then eventually come to rest).

- The most common problem observed by novices is the "explosion", in which the system goes chaotic. There are many, simultaneous reasons that a fluid simulation will fail as they are very sensitive to many things, not just the time step. The fluid won't behave properly until all of them are working correctly. Thus, the best way to debug is not to guess where to fix, but look very carefully at the output of each step. Plot pressure/force fields, see if you notice discontinuities or incorrect values.

- And the simplest solution: Start with something that already works. Rather than roll your own, learn from the code above.

H

 

Some comments about simulation scale:

Several people have asked about the "simulation scale".

First, notice there are two scales.. The "simulation scale" is the size of the actual fluid, which must be correct in real world units. The "world scale" is the size of the graphics simulation, which may be much larger.

In our graphics world, we might want this tiny fluid to be an ocean, so we scale up the fluid to match our world. However, the fluid must simulate at the real-world, physically correct fluid scale. The simulation_scale constant is used to change between simulation scale and world scale.
      Simulation scale = 0.004
This says the fluid we render is 250 times bigger than the simulation scale. (1/.004)

The size of the simulation is based on the smoothing radius:
      Smoothing radius = 0.01 meters = 1 centimeter
So, this says, the fluid particles push on each other across a distance of 1 centimeter (in fluid scale). Beyond 1 cm, fluid particles do not interact with one another. This is quite small.. but it represents the real size of the fluid we can simulate.
So far, most fluid simulations cannot do a real fluid much larger than 10 cm ^ 3.

The cell size tells us the length of one grid cell for spatial subdivision (along one axis). I found the best cell size is two times the smoothing radius, as it allows us to search only 2x2x2 neighbors (8) instead of 3x3x3 neighbors (27).
So we have:
    Cell size = 2.0 * Smooth radius = 0.02 m (fluid scale)

This says the size of a grid cell equals 2 centimeters at the fluid scale. Now, how big is this in the graphics world scale?
The simulation scale is 0.004, so the world scale is 250 times bigger than the fluid (1/.004)
    Cell size = 0.02m * Simulation scale = 5 m (world scale)

So, while our real fluid grid has a size of 2 centimeters for each cell, the world grid has a size of 5 meters for each cell, the size at which we render. The total grid_size is the length of the simulation volume:
   Grid size = Grid max - Grid min = 30 - (-30) = 60 m (world scale)

Finally, we want to find the grid resolution, how many cells along a side:
    Grid resolution = Grid size / Cell size (world) = 60 m / 5 m = 12 cells
This says, with a 60 meter grid (along x-axis), and a 5 meter cell size, there will be 12 cells along the x-axis.

Why two scales?

Why not just simulate everything at the simulation scale, then use glScale to render the particles at the world scale.
The reason is, we might have world scale objects which we want to interact (collide) with the fluid particles.
The fluid-rigid body collisions are easiest to accomplish if our particle positions are stored at the same scale as our rendered world. Therefore, the scale change is integrated into the fluid simulator. From the API point of view, the fluid simulation presents particle positions at world scales, but internally it simulates them at the simulation scale.