Program Listing for File Simulation.cpp
↰ Return to documentation for file (SPlisHSPlasH/Simulation.cpp)
#include "Simulation.h"
#include "TimeManager.h"
#include "Utilities/Timing.h"
#include "TimeStep.h"
#include "EmitterSystem.h"
#include "SPlisHSPlasH/WCSPH/TimeStepWCSPH.h"
#include "SPlisHSPlasH/PCISPH/TimeStepPCISPH.h"
#include "SPlisHSPlasH/PBF/TimeStepPBF.h"
#include "SPlisHSPlasH/IISPH/TimeStepIISPH.h"
#include "SPlisHSPlasH/DFSPH/TimeStepDFSPH.h"
#include "SPlisHSPlasH/PF/TimeStepPF.h"
#include "SPlisHSPlasH/ICSPH/TimeStepICSPH.h"
#include "BoundaryModel_Akinci2012.h"
#include "BoundaryModel_Bender2019.h"
#include "BoundaryModel_Koschier2017.h"
using namespace SPH;
using namespace std;
using namespace GenParam;
Simulation* Simulation::current = nullptr;
int Simulation::SIM_2D = -1;
int Simulation::PARTICLE_RADIUS = -1;
int Simulation::GRAVITATION = -1;
int Simulation::CFL_METHOD = -1;
int Simulation::CFL_FACTOR = -1;
int Simulation::CFL_MIN_TIMESTEPSIZE = -1;
int Simulation::CFL_MAX_TIMESTEPSIZE = -1;
int Simulation::ENABLE_Z_SORT = -1;
int Simulation::STEPS_PER_Z_SORT = -1;
int Simulation::KERNEL_METHOD = -1;
int Simulation::GRAD_KERNEL_METHOD = -1;
int Simulation::ENUM_KERNEL_CUBIC = -1;
int Simulation::ENUM_KERNEL_WENDLANDQUINTICC2 = -1;
int Simulation::ENUM_KERNEL_POLY6 = -1;
int Simulation::ENUM_KERNEL_SPIKY = -1;
int Simulation::ENUM_KERNEL_PRECOMPUTED_CUBIC = -1;
int Simulation::ENUM_KERNEL_CUBIC_2D = -1;
int Simulation::ENUM_KERNEL_WENDLANDQUINTICC2_2D = -1;
int Simulation::ENUM_GRADKERNEL_CUBIC = -1;
int Simulation::ENUM_GRADKERNEL_WENDLANDQUINTICC2 = -1;
int Simulation::ENUM_GRADKERNEL_POLY6 = -1;
int Simulation::ENUM_GRADKERNEL_SPIKY = -1;
int Simulation::ENUM_GRADKERNEL_PRECOMPUTED_CUBIC = -1;
int Simulation::ENUM_GRADKERNEL_CUBIC_2D = -1;
int Simulation::ENUM_GRADKERNEL_WENDLANDQUINTICC2_2D = -1;
int Simulation::SIMULATION_METHOD = -1;
int Simulation::ENUM_CFL_NONE = -1;
int Simulation::ENUM_CFL_STANDARD = -1;
int Simulation::ENUM_CFL_ITER = -1;
int Simulation::ENUM_SIMULATION_WCSPH = -1;
int Simulation::ENUM_SIMULATION_PCISPH = -1;
int Simulation::ENUM_SIMULATION_PBF = -1;
int Simulation::ENUM_SIMULATION_IISPH = -1;
int Simulation::ENUM_SIMULATION_DFSPH = -1;
int Simulation::ENUM_SIMULATION_PF = -1;
int Simulation::ENUM_SIMULATION_ICSPH = -1;
int Simulation::BOUNDARY_HANDLING_METHOD = -1;
int Simulation::ENUM_AKINCI2012 = -1;
int Simulation::ENUM_KOSCHIER2017 = -1;
int Simulation::ENUM_BENDER2019 = -1;
Simulation::Simulation ()
{
m_cflMethod = 1;
m_cflFactor = 0.5;
m_cflMinTimeStepSize = static_cast<Real>(0.0001);
m_cflMaxTimeStepSize = static_cast<Real>(0.005);
m_gravitation = Vector3r(0.0, static_cast<Real>(-9.81), 0.0);
m_kernelMethod = -1;
m_gradKernelMethod = -1;
m_neighborhoodSearch = nullptr;
m_timeStep = nullptr;
m_simulationMethod = SimulationMethods::NumSimulationMethods;
m_simulationMethodChanged = NULL;
m_simulationIsInitialized = false;
m_sim2D = false;
m_enableZSort = true;
m_stepsPerZSort = 500;
m_counter = 0;
m_animationFieldSystem = new AnimationFieldSystem();
m_boundaryHandlingMethod = static_cast<int>(BoundaryHandlingMethods::Bender2019);
}
Simulation::~Simulation ()
{
#ifdef USE_DEBUG_TOOLS
delete m_debugTools;
#endif
delete m_animationFieldSystem;
delete m_timeStep;
delete m_neighborhoodSearch;
delete TimeManager::getCurrent();
for (unsigned int i = 0; i < m_fluidModels.size(); i++)
delete m_fluidModels[i];
m_fluidModels.clear();
for (unsigned int i = 0; i < m_boundaryModels.size(); i++)
delete m_boundaryModels[i];
m_boundaryModels.clear();
current = nullptr;
}
Simulation* Simulation::getCurrent ()
{
if (current == nullptr)
{
current = new Simulation ();
}
return current;
}
void Simulation::setCurrent (Simulation* tm)
{
current = tm;
}
bool Simulation::hasCurrent()
{
return (current != nullptr);
}
void Simulation::init(const Real particleRadius, const bool sim2D)
{
m_sim2D = sim2D;
initParameters();
registerNonpressureForces();
// init kernel
setParticleRadius(particleRadius);
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_PRECOMPUTED_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_PRECOMPUTED_CUBIC);
// Initialize neighborhood search
if (m_neighborhoodSearch == NULL)
#ifdef GPU_NEIGHBORHOOD_SEARCH
m_neighborhoodSearch = new NeighborhoodSearch(m_supportRadius);
#else
m_neighborhoodSearch = new NeighborhoodSearch(m_supportRadius, false);
#endif
m_neighborhoodSearch->set_radius(m_supportRadius);
}
void Simulation::deferredInit()
{
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
{
FluidModel* fm = getFluidModel(i);
fm->deferredInit();
}
}
void Simulation::initParameters()
{
ParameterObject::initParameters();
SIM_2D = createBoolParameter("sim2D", "2D Simulation", &m_sim2D);
setGroup(SIM_2D, "Simulation|Simulation");
setDescription(SIM_2D, "2D/3D simulation.");
getParameter(SIM_2D)->setReadOnly(true);
ENABLE_Z_SORT = createBoolParameter("enableZSort", "Enable z-sort", &m_enableZSort);
setGroup(ENABLE_Z_SORT, "Simulation|Simulation");
setDescription(ENABLE_Z_SORT, "Enable z-sort to improve cache hits.");
STEPS_PER_Z_SORT = createNumericParameter("stepsPerZSort", "Simulation steps per z-sort", &m_stepsPerZSort);
setGroup(STEPS_PER_Z_SORT, "Simulation|Simulation");
setDescription(STEPS_PER_Z_SORT, "Number of simulation steps which are performed before a z-sort is applied.");
static_cast<UnsignedIntParameter*>(getParameter(STEPS_PER_Z_SORT))->setMinValue(1u);
ParameterBase::GetFunc<Real> getRadiusFct = std::bind(&Simulation::getParticleRadius, this);
ParameterBase::SetFunc<Real> setRadiusFct = std::bind(&Simulation::setParticleRadius, this, std::placeholders::_1);
PARTICLE_RADIUS = createNumericParameter("particleRadius", "Particle radius", getRadiusFct, setRadiusFct);
setGroup(PARTICLE_RADIUS, "Simulation|Simulation");
setDescription(PARTICLE_RADIUS, "Radius of the fluid particles.");
getParameter(PARTICLE_RADIUS)->setReadOnly(true);
GRAVITATION = createVectorParameter("gravitation", "Gravitation", 3u, m_gravitation.data());
setGroup(GRAVITATION, "Simulation|Simulation");
setDescription(GRAVITATION, "Vector to define the gravitational acceleration.");
CFL_METHOD = createEnumParameter("cflMethod", "CFL - method", &m_cflMethod);
setGroup(CFL_METHOD, "Simulation|CFL");
setDescription(CFL_METHOD, "CFL method used for adaptive time stepping.");
EnumParameter *enumParam = static_cast<EnumParameter*>(getParameter(CFL_METHOD));
enumParam->addEnumValue("None", ENUM_CFL_NONE);
enumParam->addEnumValue("CFL", ENUM_CFL_STANDARD);
enumParam->addEnumValue("CFL - iterations", ENUM_CFL_ITER);
CFL_FACTOR = createNumericParameter("cflFactor", "CFL - factor", &m_cflFactor);
setGroup(CFL_FACTOR, "Simulation|CFL");
setDescription(CFL_FACTOR, "Factor to scale the CFL time step size.");
static_cast<RealParameter*>(getParameter(CFL_FACTOR))->setMinValue(static_cast<Real>(1e-6));
CFL_MIN_TIMESTEPSIZE = createNumericParameter("cflMinTimeStepSize", "CFL - min. time step size", &m_cflMinTimeStepSize);
setGroup(CFL_MIN_TIMESTEPSIZE, "Simulation|CFL");
setDescription(CFL_MIN_TIMESTEPSIZE, "Min. time step size.");
static_cast<RealParameter*>(getParameter(CFL_MIN_TIMESTEPSIZE))->setMinValue(static_cast<Real>(1e-9));
CFL_MAX_TIMESTEPSIZE = createNumericParameter("cflMaxTimeStepSize", "CFL - max. time step size", &m_cflMaxTimeStepSize);
setGroup(CFL_MAX_TIMESTEPSIZE, "Simulation|CFL");
setDescription(CFL_MAX_TIMESTEPSIZE, "Max. time step size.");
static_cast<RealParameter*>(getParameter(CFL_MAX_TIMESTEPSIZE))->setMinValue(static_cast<Real>(1e-6));
ParameterBase::GetFunc<int> getKernelFct = std::bind(&Simulation::getKernel, this);
ParameterBase::SetFunc<int> setKernelFct = std::bind(&Simulation::setKernel, this, std::placeholders::_1);
KERNEL_METHOD = createEnumParameter("kernel", "Kernel", getKernelFct, setKernelFct);
setGroup(KERNEL_METHOD, "Simulation|Kernel");
setDescription(KERNEL_METHOD, "Kernel function used in the SPH model.");
enumParam = static_cast<EnumParameter*>(getParameter(KERNEL_METHOD));
if (!m_sim2D)
{
enumParam->addEnumValue("Cubic spline", ENUM_KERNEL_CUBIC);
enumParam->addEnumValue("Wendland quintic C2", ENUM_KERNEL_WENDLANDQUINTICC2);
enumParam->addEnumValue("Poly6", ENUM_KERNEL_POLY6);
enumParam->addEnumValue("Spiky", ENUM_KERNEL_SPIKY);
enumParam->addEnumValue("Precomputed cubic spline", ENUM_KERNEL_PRECOMPUTED_CUBIC);
}
else
{
enumParam->addEnumValue("Cubic spline 2D", ENUM_KERNEL_CUBIC_2D);
enumParam->addEnumValue("Wendland quintic C2 2D", ENUM_KERNEL_WENDLANDQUINTICC2_2D);
}
ParameterBase::GetFunc<int> getGradKernelFct = std::bind(&Simulation::getGradKernel, this);
ParameterBase::SetFunc<int> setGradKernelFct = std::bind(&Simulation::setGradKernel, this, std::placeholders::_1);
GRAD_KERNEL_METHOD = createEnumParameter("gradKernel", "Gradient of kernel", getGradKernelFct, setGradKernelFct);
setGroup(GRAD_KERNEL_METHOD, "Simulation|Kernel");
setDescription(GRAD_KERNEL_METHOD, "Gradient of the kernel function used in the SPH model.");
enumParam = static_cast<EnumParameter*>(getParameter(GRAD_KERNEL_METHOD));
if (!m_sim2D)
{
enumParam->addEnumValue("Cubic spline", ENUM_GRADKERNEL_CUBIC);
enumParam->addEnumValue("Wendland quintic C2", ENUM_GRADKERNEL_WENDLANDQUINTICC2);
enumParam->addEnumValue("Poly6", ENUM_GRADKERNEL_POLY6);
enumParam->addEnumValue("Spiky", ENUM_GRADKERNEL_SPIKY);
enumParam->addEnumValue("Precomputed cubic spline", ENUM_GRADKERNEL_PRECOMPUTED_CUBIC);
}
else
{
enumParam->addEnumValue("Cubic spline 2D", ENUM_GRADKERNEL_CUBIC_2D);
enumParam->addEnumValue("Wendland quintic C2 2D", ENUM_GRADKERNEL_WENDLANDQUINTICC2_2D);
}
ParameterBase::GetFunc<int> getSimulationFct = std::bind(&Simulation::getSimulationMethod, this);
ParameterBase::SetFunc<int> setSimulationFct = std::bind(&Simulation::setSimulationMethod, this, std::placeholders::_1);
SIMULATION_METHOD = createEnumParameter("simulationMethod", "Simulation method", getSimulationFct, setSimulationFct);
setGroup(SIMULATION_METHOD, "Simulation|Simulation");
setDescription(SIMULATION_METHOD, "Simulation method.");
enumParam = static_cast<EnumParameter*>(getParameter(SIMULATION_METHOD));
enumParam->addEnumValue(TimeStepWCSPH::METHOD_NAME, ENUM_SIMULATION_WCSPH);
enumParam->addEnumValue(TimeStepPCISPH::METHOD_NAME, ENUM_SIMULATION_PCISPH);
enumParam->addEnumValue(TimeStepPBF::METHOD_NAME, ENUM_SIMULATION_PBF);
enumParam->addEnumValue(TimeStepIISPH::METHOD_NAME, ENUM_SIMULATION_IISPH);
enumParam->addEnumValue(TimeStepDFSPH::METHOD_NAME, ENUM_SIMULATION_DFSPH);
enumParam->addEnumValue(TimeStepPF::METHOD_NAME, ENUM_SIMULATION_PF);
enumParam->addEnumValue(TimeStepICSPH::METHOD_NAME, ENUM_SIMULATION_ICSPH);
BOUNDARY_HANDLING_METHOD = createEnumParameter("boundaryHandlingMethod", "Boundary handling method", &m_boundaryHandlingMethod);
setGroup(BOUNDARY_HANDLING_METHOD, "Simulation|Simulation");
setDescription(BOUNDARY_HANDLING_METHOD, "Boundary handling method.");
enumParam = static_cast<EnumParameter*>(getParameter(BOUNDARY_HANDLING_METHOD));
enumParam->addEnumValue("Akinci et al. 2012", ENUM_AKINCI2012);
enumParam->addEnumValue("Koschier and Bender 2017", ENUM_KOSCHIER2017);
enumParam->addEnumValue("Bender et al. 2019", ENUM_BENDER2019);
enumParam->setReadOnly(true);
}
void Simulation::setParticleRadius(Real val)
{
m_particleRadius = val;
m_supportRadius = static_cast<Real>(4.0) * m_particleRadius;
initKernels();
}
void Simulation::initKernels()
{
// init kernel
Poly6Kernel::setRadius(m_supportRadius);
SpikyKernel::setRadius(m_supportRadius);
CubicKernel::setRadius(m_supportRadius);
WendlandQuinticC2Kernel::setRadius(m_supportRadius);
PrecomputedCubicKernel::setRadius(m_supportRadius);
CohesionKernel::setRadius(m_supportRadius);
AdhesionKernel::setRadius(m_supportRadius);
CubicKernel2D::setRadius(m_supportRadius);
WendlandQuinticC2Kernel2D::setRadius(m_supportRadius);
#ifdef USE_AVX
CubicKernel_AVX::setRadius(m_supportRadius, m_sim2D);
// Poly6Kernel_AVX::setRadius(m_supportRadius);
// SpikyKernel_AVX::setRadius(m_supportRadius);
#endif
}
void Simulation::setGradKernel(int val)
{
m_gradKernelMethod = val;
if (!m_sim2D)
{
if ((m_gradKernelMethod < 0) || (m_gradKernelMethod > 4))
m_gradKernelMethod = 0;
if (m_gradKernelMethod == 0)
m_gradKernelFct = CubicKernel::gradW;
else if (m_gradKernelMethod == 1)
m_gradKernelFct = WendlandQuinticC2Kernel::gradW;
else if (m_gradKernelMethod == 2)
m_gradKernelFct = Poly6Kernel::gradW;
else if (m_gradKernelMethod == 3)
m_gradKernelFct = SpikyKernel::gradW;
else if (m_gradKernelMethod == 4)
m_gradKernelFct = Simulation::PrecomputedCubicKernel::gradW;
}
else
{
if ((m_gradKernelMethod < 0) || (m_gradKernelMethod > 1))
m_gradKernelMethod = 0;
if (m_gradKernelMethod == 0)
m_gradKernelFct = CubicKernel2D::gradW;
else if (m_gradKernelMethod == 1)
m_gradKernelFct = WendlandQuinticC2Kernel2D::gradW;
}
}
void Simulation::setKernel(int val)
{
if (val == m_kernelMethod)
return;
m_kernelMethod = val;
if (!m_sim2D)
{
if ((m_kernelMethod < 0) || (m_kernelMethod > 4))
m_kernelMethod = 0;
if (m_kernelMethod == 0)
{
m_W_zero = CubicKernel::W_zero();
m_kernelFct = CubicKernel::W;
}
else if (m_kernelMethod == 1)
{
m_W_zero = WendlandQuinticC2Kernel::W_zero();
m_kernelFct = WendlandQuinticC2Kernel::W;
}
else if (m_kernelMethod == 2)
{
m_W_zero = Poly6Kernel::W_zero();
m_kernelFct = Poly6Kernel::W;
}
else if (m_kernelMethod == 3)
{
m_W_zero = SpikyKernel::W_zero();
m_kernelFct = SpikyKernel::W;
}
else if (m_kernelMethod == 4)
{
m_W_zero = Simulation::PrecomputedCubicKernel::W_zero();
m_kernelFct = Simulation::PrecomputedCubicKernel::W;
}
}
else
{
if ((m_kernelMethod < 0) || (m_kernelMethod > 1))
m_kernelMethod = 0;
if (m_kernelMethod == 0)
{
m_W_zero = CubicKernel2D::W_zero();
m_kernelFct = CubicKernel2D::W;
}
else if (m_kernelMethod == 1)
{
m_W_zero = WendlandQuinticC2Kernel2D::W_zero();
m_kernelFct = WendlandQuinticC2Kernel2D::W;
}
}
if (getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012)
updateBoundaryVolume();
}
void Simulation::updateTimeStepSize()
{
if (m_cflMethod == 1)
updateTimeStepSizeCFL();
else if (m_cflMethod == 2)
{
Real h = TimeManager::getCurrent()->getTimeStepSize();
updateTimeStepSizeCFL();
const unsigned int iterations = m_timeStep->getNumIterations();
if (iterations == 0) // check if the method requried iterations
return;
if (iterations > 10)
h *= static_cast<Real>(0.9);
else if (iterations < 5)
h *= static_cast<Real>(1.1);
h = min(h, TimeManager::getCurrent()->getTimeStepSize());
TimeManager::getCurrent()->setTimeStepSize(h);
}
}
void Simulation::updateTimeStepSizeCFL()
{
const Real radius = m_particleRadius;
Real h = TimeManager::getCurrent()->getTimeStepSize();
Simulation *sim = Simulation::getCurrent();
const unsigned int nBoundaries = sim->numberOfBoundaryModels();
// Approximate max. position change due to current velocities
Real maxVel = 0.0;
const Real diameter = static_cast<Real>(2.0)*radius;
// fluid particles
for (unsigned int fluidModelIndex = 0; fluidModelIndex < numberOfFluidModels(); fluidModelIndex++)
{
FluidModel *fm = getFluidModel(fluidModelIndex);
const unsigned int numParticles = fm->numActiveParticles();
for (unsigned int i = 0; i < numParticles; i++)
{
const Vector3r &vel = fm->getVelocity(i);
const Vector3r &accel = fm->getAcceleration(i);
const Real velMag = (vel + accel*h).squaredNorm();
if (velMag > maxVel)
maxVel = velMag;
}
}
// boundary particles
if (getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012)
{
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
{
BoundaryModel_Akinci2012 *bm = static_cast<BoundaryModel_Akinci2012*>(getBoundaryModel(i));
if (bm->getRigidBodyObject()->isDynamic() || bm->getRigidBodyObject()->isAnimated())
{
for (unsigned int j = 0; j < bm->numberOfParticles(); j++)
{
const Vector3r &vel = bm->getVelocity(j);
const Real velMag = vel.squaredNorm();
if (velMag > maxVel)
maxVel = velMag;
}
}
}
}
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017)
{
for (unsigned int boundaryModelIndex = 0; boundaryModelIndex < numberOfBoundaryModels(); boundaryModelIndex++)
{
BoundaryModel_Koschier2017 *bm = static_cast<BoundaryModel_Koschier2017*>(getBoundaryModel(boundaryModelIndex));
if (bm->getRigidBodyObject()->isDynamic() || bm->getRigidBodyObject()->isAnimated())
{
maxVel = std::max(maxVel, bm->getMaxVel());
}
}
}
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019)
{
for (unsigned int boundaryModelIndex = 0; boundaryModelIndex < numberOfBoundaryModels(); boundaryModelIndex++)
{
BoundaryModel_Bender2019 *bm = static_cast<BoundaryModel_Bender2019*>(getBoundaryModel(boundaryModelIndex));
if (bm->getRigidBodyObject()->isDynamic() || bm->getRigidBodyObject()->isAnimated())
{
maxVel = std::max(maxVel, bm->getMaxVel());
}
}
}
// avoid division by zero
if (maxVel < static_cast<Real>(1.0e-9))
maxVel = static_cast<Real>(1.0e-9);
// Approximate max. time step size
h = m_cflFactor * static_cast<Real>(0.4) * (diameter / (sqrt(maxVel)));
h = min(h, m_cflMaxTimeStepSize);
h = max(h, m_cflMinTimeStepSize);
TimeManager::getCurrent()->setTimeStepSize(h);
}
void Simulation::computeNonPressureForces()
{
START_TIMING("computeNonPressureForces")
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
{
FluidModel *fm = getFluidModel(i);
fm->computeVorticity();
fm->computeSurfaceTension();
fm->computeViscosity();
fm->computeDragForce();
fm->computeElasticity();
fm->computeXSPH();
}
STOP_TIMING_AVG
}
void Simulation::reset()
{
// reset fluid models
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
getFluidModel(i)->reset();
// reset boundary models
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
getBoundaryModel(i)->reset();
if (getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012)
updateBoundaryVolume();
performNeighborhoodSearchSort();
if (m_timeStep)
m_timeStep->reset();
m_animationFieldSystem->reset();
m_counter = 0;
TimeManager::getCurrent()->setTime(0.0);
}
void Simulation::setSimulationMethod(const int val)
{
SimulationMethods method = static_cast<SimulationMethods>(val);
if ((method < SimulationMethods::WCSPH) || (method >= SimulationMethods::NumSimulationMethods))
method = SimulationMethods::DFSPH;
if (method == m_simulationMethod)
return;
delete m_timeStep;
m_timeStep = nullptr;
m_simulationMethod = method;
if (method == SimulationMethods::WCSPH)
{
m_timeStep = new TimeStepWCSPH();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_CUBIC);
}
else if (method == SimulationMethods::PCISPH)
{
m_timeStep = new TimeStepPCISPH();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_CUBIC);
}
else if (method == SimulationMethods::PBF)
{
m_timeStep = new TimeStepPBF();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_POLY6);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_SPIKY);
}
else if (method == SimulationMethods::IISPH)
{
m_timeStep = new TimeStepIISPH();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_CUBIC);
}
else if (method == SimulationMethods::DFSPH)
{
m_timeStep = new TimeStepDFSPH();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_PRECOMPUTED_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_PRECOMPUTED_CUBIC);
}
else if (method == SimulationMethods::PF)
{
m_timeStep = new TimeStepPF();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_PRECOMPUTED_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_PRECOMPUTED_CUBIC);
}
else if (method == SimulationMethods::ICSPH)
{
m_timeStep = new TimeStepICSPH();
m_timeStep->init();
setValue(Simulation::KERNEL_METHOD, Simulation::ENUM_KERNEL_CUBIC);
setValue(Simulation::GRAD_KERNEL_METHOD, Simulation::ENUM_GRADKERNEL_CUBIC);
}
if (m_simulationMethodChanged != nullptr)
m_simulationMethodChanged();
}
void Simulation::performNeighborhoodSearch()
{
if (zSortEnabled())
{
if (m_counter % m_stepsPerZSort == 0)
{
performNeighborhoodSearchSort();
}
m_counter++;
}
START_TIMING("neighborhood_search");
m_neighborhoodSearch->find_neighbors();
STOP_TIMING_AVG;
}
void Simulation::performNeighborhoodSearchSort()
{
if (!zSortEnabled())
return;
m_neighborhoodSearch->z_sort();
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
{
FluidModel *fm = getFluidModel(i);
fm->performNeighborhoodSearchSort();
}
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
{
BoundaryModel *bm = getBoundaryModel(i);
bm->performNeighborhoodSearchSort();
}
getTimeStep()->performNeighborhoodSearchSort();
#ifdef USE_DEBUG_TOOLS
m_debugTools->performNeighborhoodSearchSort();
#endif
}
void Simulation::setSimulationMethodChangedCallback(std::function<void()> const& callBackFct)
{
m_simulationMethodChanged = callBackFct;
}
void Simulation::setSimulationInitialized(int val)
{
m_simulationIsInitialized = val;
if (m_simulationIsInitialized)
deferredInit();
}
void Simulation::emittedParticles(FluidModel *model, const unsigned int startIndex)
{
model->emittedParticles(startIndex);
m_timeStep->emittedParticles(model, startIndex);
#ifdef USE_DEBUG_TOOLS
m_debugTools->emittedParticles(model, startIndex);
#endif
}
void Simulation::emitParticles()
{
START_TIMING("emitParticles");
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
{
FluidModel *fm = getFluidModel(i);
fm->getEmitterSystem()->step();
}
STOP_TIMING_AVG
}
void Simulation::animateParticles()
{
START_TIMING("animateParticles");
m_animationFieldSystem->step();
STOP_TIMING_AVG
}
void Simulation::addBoundaryModel(BoundaryModel *bm)
{
m_boundaryModels.push_back(bm);
}
void Simulation::addFluidModel(const std::string &id, const unsigned int nFluidParticles, Vector3r* fluidParticles, Vector3r* fluidVelocities, unsigned int* fluidObjectIds, const unsigned int nMaxEmitterParticles)
{
FluidModel *fm = new FluidModel();
fm->initModel(id, nFluidParticles, fluidParticles, fluidVelocities, fluidObjectIds, nMaxEmitterParticles);
m_fluidModels.push_back(fm);
}
void Simulation::updateBoundaryVolume()
{
if (m_neighborhoodSearch == nullptr)
return;
Simulation *sim = Simulation::getCurrent();
const unsigned int nFluids = sim->numberOfFluidModels();
// Compute value psi for boundary particles (boundary handling)
// (see Akinci et al. "Versatile rigid - fluid coupling for incompressible SPH", Siggraph 2012
// Search boundary neighborhood
// Activate only static boundaries
LOG_INFO << "Initialize boundary volume";
m_neighborhoodSearch->set_active(false);
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
{
if (!getBoundaryModel(i)->getRigidBodyObject()->isDynamic() && !getBoundaryModel(i)->getRigidBodyObject()->isAnimated())
m_neighborhoodSearch->set_active(i + nFluids, true, true);
}
//performNeighborhoodSearchSort();
m_neighborhoodSearch->find_neighbors();
// Boundary objects
for (unsigned int body = 0; body < numberOfBoundaryModels(); body++)
{
if (!getBoundaryModel(body)->getRigidBodyObject()->isDynamic() && !getBoundaryModel(body)->getRigidBodyObject()->isAnimated())
static_cast<BoundaryModel_Akinci2012*>(getBoundaryModel(body))->computeBoundaryVolume();
}
// Compute boundary psi for all dynamic bodies
for (unsigned int body = 0; body < numberOfBoundaryModels(); body++)
{
// Deactivate all
m_neighborhoodSearch->set_active(false);
// Only activate next dynamic body
if (getBoundaryModel(body)->getRigidBodyObject()->isDynamic() || getBoundaryModel(body)->getRigidBodyObject()->isAnimated())
{
m_neighborhoodSearch->set_active(body + nFluids, true, true);
m_neighborhoodSearch->find_neighbors();
static_cast<BoundaryModel_Akinci2012*>(getBoundaryModel(body))->computeBoundaryVolume();
}
}
// Activate only fluids
m_neighborhoodSearch->set_active(false);
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
{
for (unsigned int j = 0; j < numberOfFluidModels(); j++)
m_neighborhoodSearch->set_active(i, j, true);
for (unsigned int j = numberOfFluidModels(); j < m_neighborhoodSearch->point_sets().size(); j++)
m_neighborhoodSearch->set_active(i, j, true);
}
}
void SPH::Simulation::saveState(BinaryFileWriter &binWriter)
{
binWriter.write(m_W_zero);
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
getFluidModel(i)->saveState(binWriter);
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
getBoundaryModel(i)->saveState(binWriter);
m_timeStep->saveState(binWriter);
}
void SPH::Simulation::loadState(BinaryFileReader &binReader)
{
binReader.read(m_W_zero);
for (unsigned int i = 0; i < numberOfFluidModels(); i++)
getFluidModel(i)->loadState(binReader);
for (unsigned int i = 0; i < numberOfBoundaryModels(); i++)
getBoundaryModel(i)->loadState(binReader);
m_timeStep->loadState(binReader);
}