Program Listing for File Simulation.h
↰ Return to documentation for file (SPlisHSPlasH/Simulation.h)
#ifndef __Simulation_h__
#define __Simulation_h__
#include "Common.h"
#include "FluidModel.h"
#include "NonPressureForceBase.h"
#include "ParameterObject.h"
#include "NeighborhoodSearch.h"
#include "BoundaryModel.h"
#include "AnimationFieldSystem.h"
#include "Utilities/FileSystem.h"
#ifdef USE_DEBUG_TOOLS
#include "SPlisHSPlasH/Utilities/DebugTools.h"
#endif
#include <array>
#include <algorithm>
#define forall_fluid_neighbors(code) \
for (unsigned int pid = 0; pid < nFluids; pid++) \
{ \
FluidModel *fm_neighbor = sim->getFluidModelFromPointSet(pid); \
for (unsigned int j = 0; j < sim->numberOfNeighbors(fluidModelIndex, pid, i); j++) \
{ \
const unsigned int neighborIndex = sim->getNeighbor(fluidModelIndex, pid, i, j); \
const Vector3r &xj = fm_neighbor->getPosition(neighborIndex); \
code \
} \
}
#define forall_fluid_neighbors_in_same_phase(code) \
for (unsigned int j = 0; j < sim->numberOfNeighbors(fluidModelIndex, fluidModelIndex, i); j++) \
{ \
const unsigned int neighborIndex = sim->getNeighbor(fluidModelIndex, fluidModelIndex, i, j); \
const Vector3r &xj = model->getPosition(neighborIndex); \
code \
}
#define forall_boundary_neighbors(code) \
for (unsigned int pid = nFluids; pid < sim->numberOfPointSets(); pid++) \
{ \
BoundaryModel_Akinci2012 *bm_neighbor = static_cast<BoundaryModel_Akinci2012*>(sim->getBoundaryModelFromPointSet(pid)); \
for (unsigned int j = 0; j < sim->numberOfNeighbors(fluidModelIndex, pid, i); j++) \
{ \
const unsigned int neighborIndex = sim->getNeighbor(fluidModelIndex, pid, i, j); \
const Vector3r &xj = bm_neighbor->getPosition(neighborIndex); \
code \
} \
}
#define forall_density_maps(code) \
for (unsigned int pid = 0; pid < nBoundaries; pid++) \
{ \
BoundaryModel_Koschier2017 *bm_neighbor = static_cast<BoundaryModel_Koschier2017*>(sim->getBoundaryModel(pid)); \
const Real rho = bm_neighbor->getBoundaryDensity(fluidModelIndex, i); \
if (rho != 0.0) \
{ \
const Vector3r &gradRho = bm_neighbor->getBoundaryDensityGradient(fluidModelIndex, i).cast<Real>(); \
const Vector3r &xj = bm_neighbor->getBoundaryXj(fluidModelIndex, i); \
code \
} \
}
#define forall_volume_maps(code) \
for (unsigned int pid = 0; pid < nBoundaries; pid++) \
{ \
BoundaryModel_Bender2019 *bm_neighbor = static_cast<BoundaryModel_Bender2019*>(sim->getBoundaryModel(pid)); \
const Real Vj = bm_neighbor->getBoundaryVolume(fluidModelIndex, i); \
if (Vj > 0.0) \
{ \
const Vector3r &xj = bm_neighbor->getBoundaryXj(fluidModelIndex, i); \
code \
} \
}
#ifdef USE_AVX
#define forall_fluid_neighbors_avx(code) \
for (unsigned int pid = 0; pid < nFluids; pid++) \
{ \
FluidModel *fm_neighbor = sim->getFluidModelFromPointSet(pid); \
const unsigned int maxN = sim->numberOfNeighbors(fluidModelIndex, pid, i); \
for (unsigned int j = 0; j < maxN; j += 8) \
{ \
const unsigned int count = std::min(maxN - j, 8u); \
const Vector3f8 xj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &fm_neighbor->getPosition(0), count); \
code \
} \
}
#define forall_fluid_neighbors_avx_nox(code) \
unsigned int idx = 0; \
for (unsigned int pid = 0; pid < nFluids; pid++) \
{ \
FluidModel *fm_neighbor = sim->getFluidModelFromPointSet(pid); \
const unsigned int maxN = sim->numberOfNeighbors(fluidModelIndex, pid, i); \
for (unsigned int j = 0; j < maxN; j += 8) \
{ \
const unsigned int count = std::min(maxN - j, 8u); \
code \
idx++; \
} \
}
#define forall_fluid_neighbors_in_same_phase_avx(code) \
const unsigned int maxN = sim->numberOfNeighbors(fluidModelIndex, fluidModelIndex, i); \
for (unsigned int j = 0; j < maxN; j += 8) \
{ \
const unsigned int count = std::min(maxN - j, 8u); \
const Vector3f8 xj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, fluidModelIndex, i)[j], &model->getPosition(0), count); \
code \
}
#define forall_fluid_neighbors_in_same_phase_avx_nox(code) \
const unsigned int maxN = sim->numberOfNeighbors(fluidModelIndex, fluidModelIndex, i); \
for (unsigned int j = 0; j < maxN; j += 8) \
{ \
const unsigned int count = std::min(maxN - j, 8u); \
code \
}
#define forall_boundary_neighbors_avx(code) \
for (unsigned int pid = nFluids; pid < sim->numberOfPointSets(); pid++) \
{ \
BoundaryModel_Akinci2012 *bm_neighbor = static_cast<BoundaryModel_Akinci2012*>(sim->getBoundaryModelFromPointSet(pid)); \
const unsigned int maxN = sim->numberOfNeighbors(fluidModelIndex, pid, i); \
for (unsigned int j = 0; j < maxN; j += 8) \
{ \
const unsigned int count = std::min(maxN - j, 8u); \
const Vector3f8 xj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getPosition(0), count); \
code \
} \
}
#endif
namespace SPH
{
enum class SimulationMethods { WCSPH = 0, PCISPH, PBF, IISPH, DFSPH, PF, ICSPH, NumSimulationMethods };
enum class BoundaryHandlingMethods { Akinci2012 = 0, Koschier2017, Bender2019, NumSimulationMethods };
class Simulation : public GenParam::ParameterObject
{
public:
static int SIM_2D;
static int PARTICLE_RADIUS;
static int GRAVITATION;
static int CFL_METHOD;
static int CFL_FACTOR;
static int CFL_MIN_TIMESTEPSIZE;
static int CFL_MAX_TIMESTEPSIZE;
static int ENABLE_Z_SORT;
static int STEPS_PER_Z_SORT;
static int KERNEL_METHOD;
static int GRAD_KERNEL_METHOD;
static int ENUM_KERNEL_CUBIC;
static int ENUM_KERNEL_WENDLANDQUINTICC2;
static int ENUM_KERNEL_POLY6;
static int ENUM_KERNEL_SPIKY;
static int ENUM_KERNEL_PRECOMPUTED_CUBIC;
static int ENUM_KERNEL_CUBIC_2D;
static int ENUM_KERNEL_WENDLANDQUINTICC2_2D;
static int ENUM_GRADKERNEL_CUBIC;
static int ENUM_GRADKERNEL_WENDLANDQUINTICC2;
static int ENUM_GRADKERNEL_POLY6;
static int ENUM_GRADKERNEL_SPIKY;
static int ENUM_GRADKERNEL_PRECOMPUTED_CUBIC;
static int ENUM_GRADKERNEL_CUBIC_2D;
static int ENUM_GRADKERNEL_WENDLANDQUINTICC2_2D;
static int SIMULATION_METHOD;
static int ENUM_CFL_NONE;
static int ENUM_CFL_STANDARD;
static int ENUM_CFL_ITER;
static int ENUM_SIMULATION_WCSPH;
static int ENUM_SIMULATION_PCISPH;
static int ENUM_SIMULATION_PBF;
static int ENUM_SIMULATION_IISPH;
static int ENUM_SIMULATION_DFSPH;
static int ENUM_SIMULATION_PF;
static int ENUM_SIMULATION_ICSPH;
static int BOUNDARY_HANDLING_METHOD;
static int ENUM_AKINCI2012;
static int ENUM_KOSCHIER2017;
static int ENUM_BENDER2019;
typedef PrecomputedKernel<CubicKernel, 10000> PrecomputedCubicKernel;
struct NonPressureForceMethod
{
std::string m_name;
std::function<NonPressureForceBase* (FluidModel*)> m_creator;
int m_id;
};
struct FluidInfo
{
int type; // 0: block, 1: fluid model, 2: emitter
int numParticles;
AlignedBox3r box;
std::string id;
std::string samplesFile;
std::string visMeshFile;
Vector3r translation;
Matrix3r rotation;
Vector3r scale;
Vector3r initialVelocity;
Vector3r initialAngularVelocity;
unsigned char mode;
bool invert;
std::array<unsigned int, 3> resolutionSDF;
unsigned int emitter_width;
unsigned int emitter_height;
Real emitter_velocity; // emission velocity
Real emitter_emitStartTime;
Real emitter_emitEndTime;
unsigned int emitter_type;
bool hasSameParticleSampling(const FluidInfo &other)
{
if (numParticles != other.numParticles)
return false;
if ((type == 1) && (other.type == 1) && (scale == other.scale))
{
if (samplesFile == other.samplesFile)
{
std::string ext = Utilities::FileSystem::getFileExt(samplesFile);
std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
if (ext == "OBJ")
{
if (mode == other.mode)
return true;
}
else
return true;
}
}
else if ((type == 0) && (other.type == 0))
{
if (((box.max() - box.min()).isApprox(other.box.max() - other.box.min()), 1.0e-9) && (mode == other.mode))
return true;
}
return false;
}
};
protected:
std::vector<FluidModel*> m_fluidModels;
std::vector<BoundaryModel*> m_boundaryModels;
std::vector<FluidInfo> m_fluidInfos;
NeighborhoodSearch *m_neighborhoodSearch;
AnimationFieldSystem *m_animationFieldSystem;
int m_cflMethod;
Real m_cflFactor;
Real m_cflMinTimeStepSize;
Real m_cflMaxTimeStepSize;
int m_kernelMethod;
int m_gradKernelMethod;
Real m_W_zero;
Real(*m_kernelFct)(const Vector3r &);
Vector3r(*m_gradKernelFct)(const Vector3r &r);
SimulationMethods m_simulationMethod;
TimeStep *m_timeStep;
Vector3r m_gravitation;
Real m_particleRadius;
Real m_supportRadius;
bool m_sim2D;
bool m_enableZSort;
unsigned int m_stepsPerZSort;
unsigned int m_counter;
std::function<void()> m_simulationMethodChanged;
int m_boundaryHandlingMethod;
std::string m_cachePath;
bool m_useCache;
std::vector<NonPressureForceMethod> m_dragMethods;
std::vector<NonPressureForceMethod> m_elasticityMethods;
std::vector<NonPressureForceMethod> m_surfaceTensionMethods;
std::vector<NonPressureForceMethod> m_vorticityMethods;
std::vector<NonPressureForceMethod> m_viscoMethods;
bool m_simulationIsInitialized;
#ifdef USE_DEBUG_TOOLS
DebugTools* m_debugTools;
#endif
virtual void initParameters();
void registerNonpressureForces();
private:
static Simulation *current;
public:
Simulation ();
Simulation(const Simulation&) = delete;
Simulation& operator=(const Simulation&) = delete;
~Simulation ();
void init(const Real particleRadius, const bool sim2D);
void deferredInit();
void reset();
// Singleton
static Simulation* getCurrent ();
static void setCurrent (Simulation* tm);
static bool hasCurrent();
void addFluidModel(const std::string &id, const unsigned int nFluidParticles, Vector3r* fluidParticles, Vector3r* fluidVelocities, unsigned int* fluidObjectIds, const unsigned int nMaxEmitterParticles);
FluidModel *getFluidModel(const unsigned int index) { return m_fluidModels[index]; }
FluidModel *getFluidModelFromPointSet(const unsigned int pointSetIndex) { return static_cast<FluidModel*>(m_neighborhoodSearch->point_set(pointSetIndex).get_user_data()); }
const unsigned int numberOfFluidModels() const { return static_cast<unsigned int>(m_fluidModels.size()); }
void addBoundaryModel(BoundaryModel *bm);
BoundaryModel *getBoundaryModel(const unsigned int index) { return m_boundaryModels[index]; }
BoundaryModel *getBoundaryModelFromPointSet(const unsigned int pointSetIndex) { return static_cast<BoundaryModel*>(m_neighborhoodSearch->point_set(pointSetIndex).get_user_data()); }
const unsigned int numberOfBoundaryModels() const { return static_cast<unsigned int>(m_boundaryModels.size()); }
void updateBoundaryVolume();
void addFluidInfo(FluidInfo& info) { m_fluidInfos.push_back(info); }
std::vector<FluidInfo>& getFluidInfos() { return m_fluidInfos; }
FluidInfo& getFluidInfo(const unsigned int i) { return m_fluidInfos[i]; }
AnimationFieldSystem* getAnimationFieldSystem() { return m_animationFieldSystem; }
BoundaryHandlingMethods getBoundaryHandlingMethod() const { return (BoundaryHandlingMethods) m_boundaryHandlingMethod; }
void setBoundaryHandlingMethod(BoundaryHandlingMethods val) { m_boundaryHandlingMethod = (int) val; }
int getKernel() const { return m_kernelMethod; }
void setKernel(int val);
int getGradKernel() const { return m_gradKernelMethod; }
void setGradKernel(int val);
int isSimulationInitialized() const { return m_simulationIsInitialized; }
void setSimulationInitialized(int val);
FORCE_INLINE Real W_zero() const { return m_W_zero; }
FORCE_INLINE Real W(const Vector3r &r) const { return m_kernelFct(r); }
FORCE_INLINE Vector3r gradW(const Vector3r &r) { return m_gradKernelFct(r); }
int getSimulationMethod() const { return static_cast<int>(m_simulationMethod); }
void setSimulationMethod(const int val);
void setSimulationMethodChangedCallback(std::function<void()> const& callBackFct);
TimeStep *getTimeStep() { return m_timeStep; }
bool is2DSimulation() { return m_sim2D; }
bool zSortEnabled() { return m_enableZSort; }
unsigned int stepsPerZSort() { return m_stepsPerZSort; }
void initKernels();
void setParticleRadius(Real val);
Real getParticleRadius() const { return m_particleRadius; }
Real getSupportRadius() const { return m_supportRadius; }
void updateTimeStepSize();
void updateTimeStepSizeCFL();
virtual void performNeighborhoodSearch();
void performNeighborhoodSearchSort();
void computeNonPressureForces();
void animateParticles();
void emitParticles();
virtual void emittedParticles(FluidModel *model, const unsigned int startIndex);
NeighborhoodSearch* getNeighborhoodSearch() { return m_neighborhoodSearch; }
void setCachePath(const std::string& cachePath) { m_cachePath = cachePath; }
const std::string& getCachePath() const { return m_cachePath; }
void setUseCache(const bool useCache) { m_useCache = useCache; }
const bool getUseCache() const { return m_useCache; }
void saveState(BinaryFileWriter &binWriter);
void loadState(BinaryFileReader &binReader);
void addDragMethod(const std::string& name, const std::function<NonPressureForceBase* (FluidModel*)>& creator) { m_dragMethods.push_back({ name, creator, -1 }); }
std::vector<NonPressureForceMethod>& getDragMethods() { return m_dragMethods; }
void addElasticityMethod(const std::string& name, const std::function<NonPressureForceBase* (FluidModel*)>& creator) { m_elasticityMethods.push_back({ name, creator, -1 }); }
std::vector<NonPressureForceMethod>& getElasticityMethods() { return m_elasticityMethods; }
void addSurfaceTensionMethod(const std::string& name, const std::function<NonPressureForceBase* (FluidModel*)>& creator) { m_surfaceTensionMethods.push_back({ name, creator, -1 }); }
std::vector<NonPressureForceMethod>& getSurfaceTensionMethods() { return m_surfaceTensionMethods; }
void addViscosityMethod(const std::string& name, const std::function<NonPressureForceBase* (FluidModel*)>& creator) { m_viscoMethods.push_back({ name, creator, -1 }); }
std::vector<NonPressureForceMethod>& getViscosityMethods() { return m_viscoMethods; }
void addVorticityMethod(const std::string& name, const std::function<NonPressureForceBase* (FluidModel*)>& creator) { m_vorticityMethods.push_back({ name, creator, -1 }); }
std::vector<NonPressureForceMethod>& getVorticityMethods() { return m_vorticityMethods; }
#ifdef USE_DEBUG_TOOLS
DebugTools* getDebugTools() { return m_debugTools; }
void createDebugTools() { m_debugTools = new DebugTools(); m_debugTools->init(); }
#endif
FORCE_INLINE unsigned int numberOfPointSets() const
{
return static_cast<unsigned int>(m_neighborhoodSearch->n_point_sets());
}
FORCE_INLINE unsigned int numberOfNeighbors(const unsigned int pointSetIndex, const unsigned int neighborPointSetIndex, const unsigned int index) const
{
return static_cast<unsigned int>(m_neighborhoodSearch->point_set(pointSetIndex).n_neighbors(neighborPointSetIndex, index));
}
FORCE_INLINE unsigned int getNeighbor(const unsigned int pointSetIndex, const unsigned int neighborPointSetIndex, const unsigned int index, const unsigned int k) const
{
return m_neighborhoodSearch->point_set(pointSetIndex).neighbor(neighborPointSetIndex, index, k);
}
FORCE_INLINE const unsigned int * getNeighborList(const unsigned int pointSetIndex, const unsigned int neighborPointSetIndex, const unsigned int index) const
{
#ifdef GPU_NEIGHBORHOOD_SEARCH
return m_neighborhoodSearch->point_set(pointSetIndex).neighbor_list(neighborPointSetIndex, index);
#else
return m_neighborhoodSearch->point_set(pointSetIndex).neighbor_list(neighborPointSetIndex, index).data();
#endif
}
};
}
#endif