Creating Pressure Solvers
SPlisHSPlasH organizes the pressure solvers in their respective folders inside the /SPlisHSPlasH/
directory. For example DFSPH can be found inside /SPlisHSPlasH/DFSPH/
. We highly suggest the user to follow our file organization scheme. The user can also add new pressure solvers by by creating new or copying and modifying existing classes and then adding them to the build system plus additionally registering in the source code.
Note that we do not strictly distinguish the pressure solver from the simulation algorithm. Each TimeStep
class implements a whole time step including the pressure solver. The non-pressure forces are decoupled in their respective classes and only implicitly called. Thus for implementing a new pressure solver, we suggest copying the files from for example WCSPH and replacing the pressure solver by your own one. Note further, that we usually decouple data from the algorithm with the SimulationData
classes. We strongly recommend doing the same with your implementation.
Creating a new class
Again, we want to stress that copying and modifying existent methods is easier than writing a new class from scratch. However, if you want to do so, be sure to implement every abstract method inherited from TimeStep
. These include:
void step()
, the simulation step functionvoid resize()
, a method to initialize and resize any used field
Albeit being not necessary, the user may also want to override/redefine the following methods:
void init()
, the initialization method. It is important to callTimeStep::init()
inside this methodvoid reset()
, the method invoked on every reset commandvoid computeDensities()
, if the user does not want to utilize the given density computation
A minimal working example of a derive class is shown below:
TimeStepMyPressureSolver.h
#ifndef __TimeStepMyPressureSolver_h__
#define __TimeStepMyPressureSolver_h__
#include "SPlisHSPlasH/Common.h"
#include "SPlisHSPlasH/TimeStep.h"
#include "SPlisHSPlasH/SPHKernels.h"
namespace SPH
{
class TimeStepMyPressureSolver : public TimeStep
{
public:
TimeStepMyPressureSolver();
virtual ~TimeStepMyPressureSolver();
virtual void step();
virtual void resize();
};
}
#endif
TimeStepMyPressureSolve.cpp
#include "TimeStepMyPressureSolve.h"
using namespace SPH;
using namespace GenParam;
TimeStepMyPressureSolve::TimeStepMyPressureSolve() :
TimeStep()
{
[...]
}
TimeStepMyPressureSolve::~TimeStepMyPressureSolve(void)
{
[...]
}
void TimeStepMyPressureSolve::step()
{
[...]
}
void TimeStepMyPressureSolve::resize()
{
[...]
}
SPlisHSPlasH assumes your simulation method allows for operator splitting, thus usually dividing the simulation into non-pressure forces and the pressure solver plus advection. The latter is subject of the TimeStep class. It is still possible to implement these together inside your own TimeStep class, but it contradicts SPlisHSPlasH’s design principles. Since the step()
method is forwarded to the main loop by the simulation class, its purpose is to define the simulation algorithm. For guidance, we also provide a simple SPH simulation algorithm outline:
void TimeStepWCSPH::step()
{
Simulation *sim = Simulation::getCurrent();
const unsigned int nModels = sim->numberOfFluidModels();
TimeManager *tm = TimeManager::getCurrent ();
const Real h = tm->getTimeStepSize();
// 1. Perform a neighborhood search
performNeighborhoodSearch();
// 2. Compute non-pressure forces and SPH densities
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++)
{
clearAccelerations(fluidModelIndex);
computeDensities(fluidModelIndex);
}
sim->computeNonPressureForces();
// 3. Compute pressure forces
computePressureForces();
// 4. Update time step tize with CFL condition
sim->updateTimeStepSize();
// 5. Advect particles
advectParticles();
// 6. Emit and/or animate particles if necessary
sim->emitParticles();
sim->animateParticles();
// 7. Advect time
tm->setTime(tm->getTime() + h);
}
where computeDensities(...)
and clearAcceleration(...)
are already defined by the base class.
We recommend the user to split the simulation algorithm and its data into two separate classes as it is the case for our already implemented ones.
Registering the pressure solver
To add our new simulation method, we have to integrate it into the build process and the source code.
Adding to the build process
Simply add all of your class files to the CMakeLists.txt
in the /SPlisHSPlasH/
directory. We suggest creating new variables for the header and source files and adding these to the add_library()
as well as to new source_group()
calls. A possible implementation following our class file conventions would look like the following:
set(MYPRESSURESOLVER_HEADER_FILES
MyPressureSolver/SimulationDataMyPressureSolver.h
MyPressureSolver/TimeStepMyPressureSolver.h
)
set(MYPRESSURESOLVER_SOURCE_FILES
MyPressureSolver/SimulationDataMyPressureSolver.cpp
MyPressureSolver/TimeStepMyPressureSolver.cpp
)
add_library(SPlisHPlasH
[...]
${MYPRESSURESOLVER_HEADER_FILES}
${MYPRESSURESOLVER_SOURCE_FILES}
)
source_group("Header Files\\MyPressureSolver" FILES ${MYPRESSURESOLVER_HEADER_FILES})
source_group("Source Files\\MyPressureSolver" FILES ${MYPRESSURESOLVER_SOURCE_FILES})
Integration in the source code
Any timestep method and thus any pressure solver is registered in the Simulation.h
and Simulation.cpp
files, which can be found in the /SPlisHSPlasH/
directory. Adding a new method comprises of the following steps:
Adding a new enum in
SimulationMethods
Creating a new static variable
static int ENUM_SIMULATION_MYPRESSURESOLVER
for the GenericParameter system and initializing it inSimulation.cpp
Including
SPlisHSPlasH/MyPressureSolver/TimeStepMyPressureSolver.h
inSimulation.cpp
Adding a new enum value for
SIMULATION_METHOD
insideSimulation::initParameters()
using the following line:
enumParam->addEnumValue("MyPressureSolverName", ENUM_SIMULATION_MYPRESSURESOLVER);
Adding the pressure solver to
Simulation::setSimulationMethod(...)
, thus making it available for the simulation using the following:
else if (method == SimulationMethods::MyPressureSolver)
{
m_timeStep = new TimeStepMyPressureSolver();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, <desired standard SPH kernel>);
setValue(Simulation::GRAD_KERNEL_METHOD, <desired standard SPH gradient kernel>);
}
After these additions and building SPlisHSPlasH, our new pressure solver is available inside the simulation.