← Personal Projects
C++, CUDA - university project

GPU SPH

Smoothed particle hydrodynamics written as a chain of CUDA kernels - a uniform grid for neighbour search, then density, pressure, viscosity, buoyancy and integration, with synchronisation between the passes.

LanguageFrameworkProject
C++ CUDA University project
The brief

About Smoothed Particle Hydrodynamics

The objective of this exercise was to use a combination of CUDA kernels to complete a larger task. To do so, I had to introduce synchronisation points, resolve conflicts for common memory addresses, and build optimisation structures.

The optimisation

Spatial data structure

While it would be possible to test all pairs of particles for influences, it is not very efficient. The O(n2) complexity of this approach makes it very slow for higher particle counts. To speed up the detection process, a spatial data structure is usually used. Instead of testing all possible pairs, each particle is first inserted into the spatial data structure, which then provides a way to quickly find only the particles that currently reside within a particular region.

We used a simple, uniform grid data structure, built in four steps:

The uniform grid: cells with counts, the prefix sum, and a particle's neighbours across cell boundaries
Implementation detail

The Navier-Stokes equation

Smoothed particle hydrodynamics builds upon the Navier-Stokes equation, which looks as follows:

The Navier-Stokes equation

where v is the velocity, p is the pressure, μ is the viscosity, fext are external forces, for example due to gravity, and ρm is the mass density, defined as ρm = m, where m is the mass.

SPH approaches the numerical integration of the Navier-Stokes equation by the simulation of particles. Each particle represents a sample in the space that is being simulated, and carries fluid properties such as mass, velocity, density and temperature. For our simulation, we associate each particle with a constant mass; we do not simulate temperature or more complex properties.

It must be possible to reconstruct the fluid properties for the entire space from the simulated particles. To this aim, the particle properties are smoothed out using a smoothing function W(r,h). The kernel support h can be seen as the area of influence, and r denotes the positional difference between two particles. In this exercise we use three smoothing kernels for the SPH simulation, with kernel support h equal to four times the particle radius:

The three smoothing kernels

To solve the Navier-Stokes equation it is necessary to compute the acceleration a particle experiences at a given time step (dv/dt), and to update the particle's position accordingly, considering the influence of (v * ∇)v. To this aim we have to compute the acceleration for each particle as defined on the right-hand side of the equation, which we achieve in five passes:

Then we can perform the integration step using semi-implicit Euler integration:

Semi-implicit Euler integration The integration step
More detail

The individual forces

Density. Computing the density ρi for each particle location ri is straightforward:

The density sum over neighbouring particles

where j runs over all particles. Since the kernels are 0 for ||ri - rj|| > h, we only need to consider particles that are at most h away from particle i. The spatial data structure described above limits the computations to those particles. The density has to be computed in a separate kernel launch beforehand, since particles require the surrounding density for further computations.

Pressure. The pressure p can be derived from the density ρ as p = k(ρ - ρ0), where k is a spring constant that defines how compressible a fluid behaves, and ρ0 is the natural density of the fluid, the density at which no pressure arises. Ideally we would like a high k (ideally the square of the speed of sound), but that easily leads to instabilities as it would require very small time steps. In practice we have to work with a lower k.

To compute the pressure gradients it is necessary to estimate the pressure difference between all influencing particle pairs, which can be done using the symmetric difference estimator. This results in the following formula for the velocity change due to pressure:

The velocity change due to pressure

Viscosity and dissipation. Viscosity can simply be modelled as particles transferring their velocity v to surrounding particles:

The velocity change due to viscosity

where μ is the viscosity coefficient of the fluid.

Gravity and buoyancy. Fluid particles experience a body force due to gravity g:

The gravity force

Buoyancy is an upward force opposing gravity, based on density differences inside the fluid. Depending on the fluid that surrounds a particle, the particle can either be dragged down or be spilled up. This behaviour can be modelled using the particle densities:

The buoyancy force

where b is the buoyancy coefficient and ρavg is the average surrounding density, computed by averaging the density values of all particles which lie within the kernel support h of the current particle.

And yep. If you got to here, your head probably just exploded like mine did when I first saw all of this. It was very hard and easy at the same time to implement: hard because one little accident in the formulas and nothing worked any more, and easy because all the equations were given and explained. In the end I got a great result.

Contact

Want to work together?

marc.kletz@kletzgames.com