From 0d1656a0086a99205f43e204a988c52ef197cde5 Mon Sep 17 00:00:00 2001 From: Jan Bender <bender@cs.rwth-aachen.de> Date: Fri, 8 Feb 2019 09:40:26 +0100 Subject: [PATCH] - added the Implicit SPH Formulation for Incompressible Linearly Elastic Solids of Peer et al. 2017 - added Corotated SPH for deformable solids of Becker et al. 2009 - improved particle coloring --- CMakeLists.txt | 8 +- Changelog.txt | 6 + README.md | 18 +- SPlisHSPlasH/BoundaryModel.cpp | 8 +- SPlisHSPlasH/BoundaryModel.h | 18 +- SPlisHSPlasH/CMakeLists.txt | 21 +- SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp | 35 +- SPlisHSPlasH/Drag/DragForce_Gissler2017.cpp | 2 +- SPlisHSPlasH/Drag/DragForce_Macklin2014.cpp | 2 +- SPlisHSPlasH/Elasticity/ElasticityBase.cpp | 40 ++ SPlisHSPlasH/Elasticity/ElasticityBase.h | 29 + .../Elasticity/Elasticity_Becker2009.cpp | 335 +++++++++++ .../Elasticity/Elasticity_Becker2009.h | 59 ++ .../Elasticity/Elasticity_Peer2018.cpp | 562 ++++++++++++++++++ SPlisHSPlasH/Elasticity/Elasticity_Peer2018.h | 73 +++ SPlisHSPlasH/FluidModel.cpp | 106 +++- SPlisHSPlasH/FluidModel.h | 33 +- SPlisHSPlasH/IISPH/TimeStepIISPH.cpp | 31 +- SPlisHSPlasH/PBF/SimulationDataPBF.cpp | 4 +- SPlisHSPlasH/PBF/TimeStepPBF.cpp | 21 +- SPlisHSPlasH/PCISPH/SimulationDataPCISPH.cpp | 2 +- SPlisHSPlasH/PCISPH/TimeStepPCISPH.cpp | 23 +- SPlisHSPlasH/Simulation.cpp | 23 +- SPlisHSPlasH/Simulation.h | 2 +- .../SurfaceTension_Akinci2013.cpp | 7 +- .../SurfaceTension_Becker2007.cpp | 5 +- .../SurfaceTension/SurfaceTension_He2014.cpp | 12 +- SPlisHSPlasH/Utilities/MathFunctions.cpp | 231 +++++++ SPlisHSPlasH/Utilities/MathFunctions.h | 26 + SPlisHSPlasH/Utilities/SceneLoader.cpp | 29 + SPlisHSPlasH/Utilities/SceneLoader.h | 10 + .../Viscosity/Viscosity_Bender2017.cpp | 11 +- SPlisHSPlasH/Viscosity/Viscosity_Peer2015.cpp | 6 +- SPlisHSPlasH/Viscosity/Viscosity_Peer2016.cpp | 8 +- SPlisHSPlasH/Viscosity/Viscosity_Standard.cpp | 7 +- .../Viscosity/Viscosity_Takahashi2015.cpp | 10 +- .../Viscosity/Viscosity_Weiler2018.cpp | 31 +- SPlisHSPlasH/Viscosity/Viscosity_XSPH.cpp | 2 +- .../Vorticity/MicropolarModel_Bender2017.cpp | 66 +- .../Vorticity/VorticityConfinement.cpp | 4 + SPlisHSPlasH/WCSPH/TimeStepWCSPH.cpp | 19 +- {Demos => Simulators}/CMakeLists.txt | 2 +- .../Common/SimulatorBase.cpp | 397 ++++++++----- .../Common/SimulatorBase.h | 48 +- .../Common/TweakBarParameters.cpp | 45 +- .../Common/TweakBarParameters.h | 0 .../DynamicBoundarySimulator}/CMakeLists.txt | 30 +- .../PBDRigidBody.h | 0 .../PBDWrapper.cpp | 4 +- .../PositionBasedDynamicsWrapper/PBDWrapper.h | 0 .../DynamicBoundarySimulator}/main.cpp | 132 +++- .../StaticBoundarySimulator}/CMakeLists.txt | 26 +- .../StaticBoundarySimulator}/main.cpp | 130 +++- Visualization/MiniGL.cpp | 31 +- Visualization/MiniGL.h | 3 +- bin/Buckling.bat | 2 - bin/Buckling_Bender2017.bat | 2 +- bin/Buckling_Peer2015.bat | 2 +- bin/Buckling_Peer2016.bat | 2 +- bin/Buckling_Takahashi2015.bat | 2 +- bin/Buckling_Weiler2018.bat | 2 +- bin/Bunny_vs_Dragon.bat | 2 +- bin/Coiling_Bender2017.bat | 2 +- bin/Coiling_Peer2015.bat | 2 +- bin/Coiling_Peer2016.bat | 2 +- bin/Coiling_Takahashi2015.bat | 2 +- bin/Coiling_Weiler2018.bat | 2 +- bin/DamBreakModel.bat | 2 +- bin/DamBreakModelDragons.bat | 2 +- bin/DamBreakModel_2D.bat | 2 +- bin/DamBreakWithObjects.bat | 2 +- bin/DoubleDamBreak.bat | 2 +- bin/DoubleDamBreakMultiPhase.bat | 2 + bin/DoubleDamBreakTwoPhase.bat | 2 - bin/DoubleDamBreakWithSphere.bat | 2 +- bin/DragTest.bat | 2 +- bin/Emitter.bat | 2 +- bin/MultiPhaseColoring.bat | 2 + bin/Obstacle.bat | 2 +- bin/ViscousBunny.bat | 2 +- data/Scenes/DamBreakModel.json | 3 +- data/Scenes/DeformableModel.json | 55 ++ ...ase.json => DoubleDamBreakMultiPhase.json} | 0 data/Scenes/MultiPhaseColoring.json | 124 ++++ data/Scenes/Obstacle.json | 2 +- 85 files changed, 2641 insertions(+), 386 deletions(-) create mode 100644 SPlisHSPlasH/Elasticity/ElasticityBase.cpp create mode 100644 SPlisHSPlasH/Elasticity/ElasticityBase.h create mode 100644 SPlisHSPlasH/Elasticity/Elasticity_Becker2009.cpp create mode 100644 SPlisHSPlasH/Elasticity/Elasticity_Becker2009.h create mode 100644 SPlisHSPlasH/Elasticity/Elasticity_Peer2018.cpp create mode 100644 SPlisHSPlasH/Elasticity/Elasticity_Peer2018.h create mode 100644 SPlisHSPlasH/Utilities/MathFunctions.cpp create mode 100644 SPlisHSPlasH/Utilities/MathFunctions.h rename {Demos => Simulators}/CMakeLists.txt (63%) rename Demos/Common/DemoBase.cpp => Simulators/Common/SimulatorBase.cpp (70%) rename Demos/Common/DemoBase.h => Simulators/Common/SimulatorBase.h (68%) rename {Demos => Simulators}/Common/TweakBarParameters.cpp (89%) rename {Demos => Simulators}/Common/TweakBarParameters.h (100%) rename {Demos/DynamicBoundaryDemo => Simulators/DynamicBoundarySimulator}/CMakeLists.txt (75%) rename {Demos/DynamicBoundaryDemo => Simulators/DynamicBoundarySimulator}/PositionBasedDynamicsWrapper/PBDRigidBody.h (100%) rename {Demos/DynamicBoundaryDemo => Simulators/DynamicBoundarySimulator}/PositionBasedDynamicsWrapper/PBDWrapper.cpp (99%) rename {Demos/DynamicBoundaryDemo => Simulators/DynamicBoundarySimulator}/PositionBasedDynamicsWrapper/PBDWrapper.h (100%) rename {Demos/DynamicBoundaryDemo => Simulators/DynamicBoundarySimulator}/main.cpp (76%) rename {Demos/StaticBoundaryDemo => Simulators/StaticBoundarySimulator}/CMakeLists.txt (66%) rename {Demos/StaticBoundaryDemo => Simulators/StaticBoundarySimulator}/main.cpp (74%) delete mode 100644 bin/Buckling.bat create mode 100644 bin/DoubleDamBreakMultiPhase.bat delete mode 100644 bin/DoubleDamBreakTwoPhase.bat create mode 100644 bin/MultiPhaseColoring.bat create mode 100644 data/Scenes/DeformableModel.json rename data/Scenes/{DoubleDamBreakTwoPhase.json => DoubleDamBreakMultiPhase.json} (100%) create mode 100644 data/Scenes/MultiPhaseColoring.json diff --git a/CMakeLists.txt b/CMakeLists.txt index d37f379..16880c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if (NOT SPH_LIBS_ONLY) endif() add_subdirectory(extern/AntTweakBar) add_subdirectory(extern/glew) - add_subdirectory(Demos) + add_subdirectory(Simulators) add_subdirectory(Tools) add_subdirectory(Tests) endif() @@ -54,7 +54,7 @@ ExternalProject_Add( Ext_PBD PREFIX "${CMAKE_SOURCE_DIR}/extern/PositionBasedDynamics" GIT_REPOSITORY https://github.com/InteractiveComputerGraphics/PositionBasedDynamics.git - GIT_TAG "5980708692418f45a26bbd9d3318235a41312e75" + GIT_TAG "46f70c2e88ec8c8d7acc15e51c967ed35754f393" INSTALL_DIR ${ExternalInstallDir}/PositionBasedDynamics CMAKE_ARGS -DCMAKE_BUILD_TYPE=${EXT_CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=${ExternalInstallDir}/PositionBasedDynamics -DPBD_NO_DEMOS:BOOL=1 -DPBD_EXTERNALINSTALLDIR:PATH=${ExternalInstallDir} -DUSE_DOUBLE_PRECISION:BOOL=${USE_DOUBLE_PRECISION} ) @@ -64,7 +64,7 @@ ExternalProject_Add( Ext_CompactNSearch PREFIX "${CMAKE_SOURCE_DIR}/extern/CompactNSearch" GIT_REPOSITORY https://github.com/InteractiveComputerGraphics/CompactNSearch.git - GIT_TAG "4ad505f5457e34ec479994740b75767241cff468" + GIT_TAG "93724411993689ea4b0fa2daed0b295ebe1e329f" INSTALL_DIR ${ExternalInstallDir}/CompactNSearch CMAKE_ARGS -DCMAKE_BUILD_TYPE=${EXT_CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=${ExternalInstallDir}/CompactNSearch -DUSE_DOUBLE_PRECISION:BOOL=${USE_DOUBLE_PRECISION} ) @@ -74,7 +74,7 @@ ExternalProject_Add( Ext_GenericParameters PREFIX "${CMAKE_SOURCE_DIR}/extern/GenericParameters" GIT_REPOSITORY https://github.com/InteractiveComputerGraphics/GenericParameters.git - GIT_TAG "1ec904bf8555e78ae0d8ba2f9f9a395371c5d4eb" + GIT_TAG "b1ad669fac8d106515f6aa8514a03598d5766a36" INSTALL_DIR ${ExternalInstallDir}/GenericParameters CMAKE_ARGS -DCMAKE_BUILD_TYPE=${EXT_CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=${ExternalInstallDir}/GenericParameters -DGENERICPARAMETERS_NO_TESTS:BOOL=1 ) diff --git a/Changelog.txt b/Changelog.txt index 67e16c8..a9ad7af 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,4 +1,10 @@ +2.3.0 + - added the Implicit SPH Formulation for Incompressible Linearly Elastic Solids of Peer et al. 2017 + - added Corotated SPH for deformable solids of Becker et al. 2009 - SPlisHSPlasH now supports 2D simulations + - SPlisHSPlasH now has enhanced particle coloring + - partio export of arbitrary particle attributes is now supported + - renamed Static/DynamicBoundaryDemo to Static/DynamicBoundarySimulator - added documentation of file format - added colormaps - fixed race condition diff --git a/README.md b/README.md index 712c6d6..f98b401 100644 --- a/README.md +++ b/README.md @@ -34,17 +34,21 @@ Note: Please use a 64-bit target on a 64-bit operating system. 32-bit builds on ## Features SPlisHSPlasH implements: -* an open-source SPH fluid simulation +* an open-source SPH fluid simulation (2D & 3D) * several implicit pressure solvers (WCSPH, PCISPH, PBF, IISPH, DFSPH, PF) * explicit and implicit viscosity methods * current surface tension approaches * different vorticity methods +* computation of drag forces * support for multi-phase simulations +* simulation of deformable solids +* rigid-fluid coupling with static and dynamic bodies +* two-way coupling with deformable solids * fluid emitters * a json-based scene file importer * automatic surface sampling * a tool for volume sampling of closed geometries -* partio file export +* partio file export of all particle data ## Pressure Solvers @@ -102,6 +106,12 @@ The SPlisHSPlasH library implements the drag force computation of the following * Miles Macklin, Matthias Müller, Nuttapong Chentanez and Tae-Yong Kim. Unified Particle Physics for Real-Time Applications. ACM Trans. Graph., 33(4), 2014 +## Elastic Forces + +* M. Becker, M. Ihmsen, and M. Teschner. Corotated SPH for deformable solids. Proceedings of Eurographics Conference on Natural Phenomena, 2009 + +* A. Peer, C. Gissler, S. Band, and M. Teschner. An Implicit SPH Formulation for Incompressible Linearly Elastic Solids. Computer Graphics Forum, 2017 + ## Multi-Phase Fluid Simulation The SPlisHSPlasH library implements the following publication to realize multi-phase simulations: @@ -132,6 +142,8 @@ The following videos were generated using the SPlisHSPlasH library: * Markus Becker and Matthias Teschner. Weakly compressible SPH for free surface flows. In Proceedings of ACM SIGGRAPH/Eurographics Symposium on Computer Animation, 2007. Eurographics Association. +* M. Becker, M. Ihmsen, and M. Teschner. Corotated SPH for deformable solids. Proceedings of Eurographics Conference on Natural Phenomena, 2009 + * Jan Bender and Dan Koschier. Divergence-free smoothed particle hydrodynamics. In Proceedings of ACM SIGGRAPH / Eurographics Symposium on Computer Animation, 2015. ACM. * Jan Bender and Dan Koschier. Divergence-free SPH for incompressible and viscous fluids. IEEE Transactions on Visualization and Computer Graphics, 2017. @@ -154,6 +166,8 @@ The following videos were generated using the SPlisHSPlasH library: * Miles Macklin, Matthias Müller, Nuttapong Chentanez and Tae-Yong Kim. Unified Particle Physics for Real-Time Applications. ACM Trans. Graph., 33(4), 2014 +* A. Peer, C. Gissler, S. Band, and M. Teschner. An Implicit SPH Formulation for Incompressible Linearly Elastic Solids. Computer Graphics Forum, 2017 + * Andreas Peer, Markus Ihmsen, Jens Cornelis, and Matthias Teschner. An Implicit Viscosity Formulation for SPH Fluids. ACM Trans. Graph., 34(4), 2015. * Andreas Peer and Matthias Teschner. Prescribed Velocity Gradients for Highly Viscous SPH Fluids with Vorticity Diffusion. IEEE Transactions on Visualization and Computer Graphics, 2016. diff --git a/SPlisHSPlasH/BoundaryModel.cpp b/SPlisHSPlasH/BoundaryModel.cpp index 54f9352..1c07734 100644 --- a/SPlisHSPlasH/BoundaryModel.cpp +++ b/SPlisHSPlasH/BoundaryModel.cpp @@ -15,7 +15,6 @@ BoundaryModel::BoundaryModel() : m_x(), m_v(), m_V(), - m_boundaryPsi(), m_forcePerThread(), m_torquePerThread() { @@ -29,7 +28,6 @@ BoundaryModel::~BoundaryModel(void) m_x.clear(); m_v.clear(); m_V.clear(); - m_boundaryPsi.clear(); m_forcePerThread.clear(); m_torquePerThread.clear(); @@ -58,7 +56,7 @@ void BoundaryModel::reset() } } -void BoundaryModel::computeBoundaryPsi(const Real density0) +void BoundaryModel::computeBoundaryVolume() { Simulation *sim = Simulation::getCurrent(); const unsigned int nFluids = sim->numberOfFluidModels(); @@ -83,7 +81,6 @@ void BoundaryModel::computeBoundaryPsi(const Real density0) } const Real volume = static_cast<Real>(1.0) / delta; m_V[i] = volume; - m_boundaryPsi[i] = density0 * volume; } } } @@ -94,7 +91,6 @@ void BoundaryModel::initModel(RigidBodyObject *rbo, const unsigned int numBounda m_x.resize(numBoundaryParticles); m_v.resize(numBoundaryParticles); m_V.resize(numBoundaryParticles); - m_boundaryPsi.resize(numBoundaryParticles); #ifdef _OPENMP const int maxThreads = omp_get_max_threads(); @@ -114,7 +110,6 @@ void BoundaryModel::initModel(RigidBodyObject *rbo, const unsigned int numBounda m_x[i] = boundaryParticles[i]; m_v[i].setZero(); m_V[i] = 0.0; - m_boundaryPsi[i] = 0.0; } } m_rigidBody = rbo; @@ -137,7 +132,6 @@ void BoundaryModel::performNeighborhoodSearchSort() d.sort_field(&m_x[0]); d.sort_field(&m_v[0]); d.sort_field(&m_V[0]); - d.sort_field(&m_boundaryPsi[0]); m_sorted = true; } diff --git a/SPlisHSPlasH/BoundaryModel.h b/SPlisHSPlasH/BoundaryModel.h index fbf3fa5..605c858 100644 --- a/SPlisHSPlasH/BoundaryModel.h +++ b/SPlisHSPlasH/BoundaryModel.h @@ -25,7 +25,6 @@ namespace SPH std::vector<Vector3r> m_x; std::vector<Vector3r> m_v; std::vector<Real> m_V; - std::vector<Real> m_boundaryPsi; std::vector<Vector3r> m_forcePerThread; std::vector<Vector3r> m_torquePerThread; bool m_sorted; @@ -34,7 +33,7 @@ namespace SPH public: unsigned int numberOfParticles() const { return static_cast<unsigned int>(m_x.size()); } - void computeBoundaryPsi(const Real density0); + void computeBoundaryVolume(); virtual void reset(); @@ -119,21 +118,6 @@ namespace SPH { m_V[i] = val; } - - FORCE_INLINE const Real& getBoundaryPsi(const unsigned int i) const - { - return m_boundaryPsi[i]; - } - - FORCE_INLINE Real& getBoundaryPsi(const unsigned int i) - { - return m_boundaryPsi[i]; - } - - FORCE_INLINE void setBoundaryPsi(const unsigned int i, const Real &val) - { - m_boundaryPsi[i] = val; - } }; } diff --git a/SPlisHSPlasH/CMakeLists.txt b/SPlisHSPlasH/CMakeLists.txt index 5f22c96..811066a 100644 --- a/SPlisHSPlasH/CMakeLists.txt +++ b/SPlisHSPlasH/CMakeLists.txt @@ -120,13 +120,27 @@ set(DRAG_SOURCE_FILES Drag/DragForce_Macklin2014.cpp ) +set(ELASTICITY_HEADER_FILES + Elasticity/ElasticityBase.h + Elasticity/Elasticity_Becker2009.h + Elasticity/Elasticity_Peer2018.h + ) + +set(ELASTICITY_SOURCE_FILES + Elasticity/ElasticityBase.cpp + Elasticity/Elasticity_Becker2009.cpp + Elasticity/Elasticity_Peer2018.cpp + ) + set(UTILS_HEADER_FILES - Utilities/PoissonDiskSampling.h + Utilities/MathFunctions.h Utilities/MatrixFreeSolver.h + Utilities/PoissonDiskSampling.h Utilities/SceneLoader.h ) set(UTILS_SOURCE_FILES + Utilities/MathFunctions.cpp Utilities/PoissonDiskSampling.cpp Utilities/SceneLoader.cpp ) @@ -200,6 +214,9 @@ add_library(SPlisHSPlasH ${DRAG_HEADER_FILES} ${DRAG_SOURCE_FILES} + ${ELASTICITY_HEADER_FILES} + ${ELASTICITY_SOURCE_FILES} + ${UTILS_HEADER_FILES} ${UTILS_SOURCE_FILES} ) @@ -227,6 +244,8 @@ source_group("Header Files\\Vorticity" FILES ${VORTICITY_HEADER_FILES}) source_group("Source Files\\Vorticity" FILES ${VORTICITY_SOURCE_FILES}) source_group("Header Files\\Drag" FILES ${DRAG_HEADER_FILES}) source_group("Source Files\\Drag" FILES ${DRAG_SOURCE_FILES}) +source_group("Header Files\\Elasticity" FILES ${ELASTICITY_HEADER_FILES}) +source_group("Source Files\\Elasticity" FILES ${ELASTICITY_SOURCE_FILES}) source_group("Header Files\\Utils" FILES ${UTILS_HEADER_FILES}) source_group("Source Files\\Utils" FILES ${UTILS_SOURCE_FILES}) diff --git a/SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp b/SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp index 28558f8..0b98dd4 100644 --- a/SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp +++ b/SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp @@ -28,10 +28,31 @@ TimeStepDFSPH::TimeStepDFSPH() : m_enableDivergenceSolver = true; m_maxIterationsV = 100; m_maxErrorV = 0.1; + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "factor", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getFactor(fluidModelIndex, i); } }); + model->addField({ "advected density", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDensityAdv(fluidModelIndex, i); } }); + model->addField({ "kappa", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getKappa(fluidModelIndex, i); } }); + model->addField({ "kappa_v", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getKappaV(fluidModelIndex, i); } }); + } } TimeStepDFSPH::~TimeStepDFSPH(void) { + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("factor"); + model->removeFieldByName("advected density"); + model->removeFieldByName("kappa"); + model->removeFieldByName("kappa_v"); + } } void TimeStepDFSPH::initParameters() @@ -206,7 +227,7 @@ void TimeStepDFSPH::warmstartPressureSolve(const unsigned int fluidModelIndex) const Real invH2 = 1.0 / h2; Simulation *sim = Simulation::getCurrent(); FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const int numParticles = (int)model->numActiveParticles(); const unsigned int nFluids = sim->numberOfFluidModels(); @@ -290,7 +311,7 @@ void TimeStepDFSPH::pressureSolve() for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) { FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const int numParticles = (int)model->numActiveParticles(); #pragma omp parallel default(shared) { @@ -322,7 +343,7 @@ void TimeStepDFSPH::pressureSolve() for (unsigned int i = 0; i < nFluids; i++) { FluidModel *model = sim->getFluidModel(i); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); avg_density_err = 0.0; pressureSolveIteration(i, avg_density_err); @@ -358,7 +379,7 @@ void TimeStepDFSPH::pressureSolveIteration(const unsigned int fluidModelIndex, R { Simulation *sim = Simulation::getCurrent(); FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const int numParticles = (int)model->numActiveParticles(); const unsigned int nFluids = sim->numberOfFluidModels(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); @@ -441,7 +462,7 @@ void TimeStepDFSPH::warmstartDivergenceSolve(const unsigned int fluidModelIndex) const Real invH = 1.0 / h; Simulation *sim = Simulation::getCurrent(); FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const int numParticles = (int)model->numActiveParticles(); const unsigned int nFluids = sim->numberOfFluidModels(); @@ -560,7 +581,7 @@ void TimeStepDFSPH::divergenceSolve() for (unsigned int i = 0; i < nFluids; i++) { FluidModel *model = sim->getFluidModel(i); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); avg_density_err = 0.0; divergenceSolveIteration(i, avg_density_err); @@ -602,7 +623,7 @@ void TimeStepDFSPH::divergenceSolveIteration(const unsigned int fluidModelIndex, { Simulation *sim = Simulation::getCurrent(); FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const int numParticles = (int)model->numActiveParticles(); const unsigned int nFluids = sim->numberOfFluidModels(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); diff --git a/SPlisHSPlasH/Drag/DragForce_Gissler2017.cpp b/SPlisHSPlasH/Drag/DragForce_Gissler2017.cpp index 37374a9..80947b1 100644 --- a/SPlisHSPlasH/Drag/DragForce_Gissler2017.cpp +++ b/SPlisHSPlasH/Drag/DragForce_Gissler2017.cpp @@ -27,7 +27,7 @@ void DragForce_Gissler2017::step() const Real radius = sim->getValue<Real>(Simulation::PARTICLE_RADIUS); const Real diam = static_cast<Real>(2.0)*radius; static const Real pi = static_cast<Real>(M_PI); - const Real rho_l = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real rho_l = m_model->getDensity0(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; diff --git a/SPlisHSPlasH/Drag/DragForce_Macklin2014.cpp b/SPlisHSPlasH/Drag/DragForce_Macklin2014.cpp index 8f0eda2..77b72e5 100644 --- a/SPlisHSPlasH/Drag/DragForce_Macklin2014.cpp +++ b/SPlisHSPlasH/Drag/DragForce_Macklin2014.cpp @@ -14,7 +14,7 @@ DragForce_Macklin2014::~DragForce_Macklin2014(void) void DragForce_Macklin2014::step() { - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const unsigned int numParticles = m_model->numActiveParticles(); diff --git a/SPlisHSPlasH/Elasticity/ElasticityBase.cpp b/SPlisHSPlasH/Elasticity/ElasticityBase.cpp new file mode 100644 index 0000000..7426afb --- /dev/null +++ b/SPlisHSPlasH/Elasticity/ElasticityBase.cpp @@ -0,0 +1,40 @@ +#include "ElasticityBase.h" + +using namespace SPH; +using namespace GenParam; + +int ElasticityBase::YOUNGS_MODULUS = -1; +int ElasticityBase::POISSON_RATIO = -1; + + +ElasticityBase::ElasticityBase(FluidModel *model) : + NonPressureForceBase(model), + m_youngsModulus(100000.0), + m_poissonRatio(0.3) +{ +} + +ElasticityBase::~ElasticityBase(void) +{ +} + + +void ElasticityBase::initParameters() +{ + NonPressureForceBase::initParameters(); + + YOUNGS_MODULUS = createNumericParameter("youngsModulus", "Young`s modulus", &m_youngsModulus); + setGroup(YOUNGS_MODULUS, "Elasticity"); + setDescription(YOUNGS_MODULUS, "Stiffness of the elastic material"); + RealParameter* rparam = static_cast<RealParameter*>(getParameter(YOUNGS_MODULUS)); + rparam->setMinValue(0.0); + + POISSON_RATIO = createNumericParameter("poissonsRatio", "Poisson`s ratio", &m_poissonRatio); + setGroup(POISSON_RATIO, "Elasticity"); + setDescription(POISSON_RATIO, "Ratio of transversal expansion and axial compression"); + rparam = static_cast<RealParameter*>(getParameter(POISSON_RATIO)); + rparam->setMinValue(-1.0 + 1e-4); + rparam->setMaxValue(0.5 - 1e-4); +} + + diff --git a/SPlisHSPlasH/Elasticity/ElasticityBase.h b/SPlisHSPlasH/Elasticity/ElasticityBase.h new file mode 100644 index 0000000..e1eae94 --- /dev/null +++ b/SPlisHSPlasH/Elasticity/ElasticityBase.h @@ -0,0 +1,29 @@ +#ifndef __ElasticityBase_h__ +#define __ElasticityBase_h__ + +#include "SPlisHSPlasH/Common.h" +#include "SPlisHSPlasH/FluidModel.h" +#include "SPlisHSPlasH/NonPressureForceBase.h" + +namespace SPH +{ + /** \brief Base class for all elasticity methods. + */ + class ElasticityBase : public NonPressureForceBase + { + protected: + Real m_youngsModulus; + Real m_poissonRatio; + + virtual void initParameters(); + + public: + static int YOUNGS_MODULUS; + static int POISSON_RATIO; + + ElasticityBase(FluidModel *model); + virtual ~ElasticityBase(void); + }; +} + +#endif diff --git a/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.cpp b/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.cpp new file mode 100644 index 0000000..a82b07f --- /dev/null +++ b/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.cpp @@ -0,0 +1,335 @@ +#include "Elasticity_Becker2009.h" +#include "SPlisHSPlasH/Simulation.h" +#include "SPlisHSPlasH/Utilities/MathFunctions.h" + +using namespace SPH; +using namespace GenParam; + +int Elasticity_Becker2009::ALPHA = -1; + + +Elasticity_Becker2009::Elasticity_Becker2009(FluidModel *model) : + ElasticityBase(model) +{ + const unsigned int numParticles = model->numActiveParticles(); + m_restVolumes.resize(numParticles); + m_current_to_initial_index.resize(numParticles); + m_initial_to_current_index.resize(numParticles); + m_initialNeighbors.resize(numParticles); + m_rotations.resize(numParticles, Matrix3r::Identity()); + m_stress.resize(numParticles); + m_F.resize(numParticles); + m_alpha = 0.0; + + initValues(); + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "rest volume", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &m_restVolumes[i]; } }); + model->addField({ "rotation", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_rotations[i](0,0); } }); + model->addField({ "stress", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_stress[i][0]; } }); + model->addField({ "deformation gradient", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_F[i](0,0); } }); + } +} + +Elasticity_Becker2009::~Elasticity_Becker2009(void) +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("rest volume"); + model->removeFieldByName("rotation"); + model->removeFieldByName("stress"); + model->removeFieldByName("deformation gradient"); + } +} + +void Elasticity_Becker2009::initParameters() +{ + ElasticityBase::initParameters(); + + ALPHA = createNumericParameter("alpha", "Zero-energy modes suppression", &m_alpha); + setGroup(ALPHA, "Elasticity"); + setDescription(ALPHA, "Coefficent for zero-energy modes suppression method"); + RealParameter *rparam = static_cast<RealParameter*>(getParameter(ALPHA)); + rparam->setMinValue(0.0); +} + +void Elasticity_Becker2009::initValues() +{ + Simulation *sim = Simulation::getCurrent(); + sim->getNeighborhoodSearch()->find_neighbors(); + + FluidModel *model = m_model; + const unsigned int numParticles = model->numActiveParticles(); + const unsigned int fluidModelIndex = model->getPointSetIndex(); + + // Store the neighbors in the reference configurations and + // compute the volume of each particle in rest state + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + m_current_to_initial_index[i] = i; + m_initial_to_current_index[i] = i; + + // only neighbors in same phase will influence elasticity + const unsigned int numNeighbors = sim->numberOfNeighbors(fluidModelIndex, fluidModelIndex, i); + m_initialNeighbors[i].resize(numNeighbors); + for (unsigned int j = 0; j < numNeighbors; j++) + m_initialNeighbors[i][j] = sim->getNeighbor(fluidModelIndex, fluidModelIndex, i, j); + + // compute volume + Real density = model->getMass(i) * sim->W_zero(); + const Vector3r &xi = model->getPosition(i); + forall_fluid_neighbors_in_same_phase( + density += model->getMass(neighborIndex) * sim->W(xi - xj); + ) + m_restVolumes[i] = model->getMass(i) / density; + } + } +} + +void Elasticity_Becker2009::step() +{ + computeRotations(); + computeStress(); + computeForces(); +} + + +void Elasticity_Becker2009::reset() +{ + initValues(); +} + +void Elasticity_Becker2009::performNeighborhoodSearchSort() +{ + const unsigned int numPart = m_model->numActiveParticles(); + if (numPart == 0) + return; + + Simulation *sim = Simulation::getCurrent(); + auto const& d = sim->getNeighborhoodSearch()->point_set(m_model->getPointSetIndex()); + d.sort_field(&m_restVolumes[0]); + d.sort_field(&m_current_to_initial_index[0]); + + for (unsigned int i = 0; i < numPart; i++) + m_initial_to_current_index[m_current_to_initial_index[i]] = i; +} + +void Elasticity_Becker2009::computeRotations() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi = m_model->getPosition(i); + const Vector3r &xi0 = m_model->getPosition0(i0); + Matrix3r Apq; + Apq.setZero(); + + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + const Vector3r xj_xi = xj - xi; + const Vector3r xj_xi_0 = xj0 - xi0; + Apq += m_model->getMass(neighborIndex) * sim->W(xj_xi_0) * (xj_xi * xj_xi_0.transpose()); + } + +// Vector3r sigma; +// Matrix3r U, VT; +// MathFunctions::svdWithInversionHandling(Apq, sigma, U, VT); +// m_rotations[i] = U * VT; + Quaternionr q(m_rotations[i]); + MathFunctions::extractRotation(Apq, q, 10); + m_rotations[i] = q.matrix(); + } + } +} + +void Elasticity_Becker2009::computeStress() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + + // Elasticity tensor + Matrix6r C; + C.setZero(); + const Real factor = m_youngsModulus / ((1.0 + m_poissonRatio)*(1.0 - 2.0 * m_poissonRatio)); + C(0, 0) = C(1, 1) = C(2, 2) = factor * (1.0 - m_poissonRatio); + C(0, 1) = C(0, 2) = C(1, 0) = C(1, 2) = C(2, 0) = C(2, 1) = factor * (m_poissonRatio); + C(3, 3) = C(4, 4) = C(5, 5) = factor * 0.5*(1.0 - 2.0 * m_poissonRatio); + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi = m_model->getPosition(i); + const Vector3r &xi0 = m_model->getPosition0(i0); + + Matrix3r nablaU; + nablaU.setZero(); + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + + const Vector3r xj_xi = xj - xi; + const Vector3r xj_xi_0 = xj0 - xi0; + + const Vector3r uji = m_rotations[i].transpose() * xj_xi - xj_xi_0; + // subtract because kernel gradient is taken in direction of xji0 instead of xij0 + nablaU -= (m_restVolumes[neighborIndex] * uji) * sim->gradW(xj_xi_0).transpose(); + } + m_F[i] = nablaU + Matrix3r::Identity(); + + // compute Cauchy strain: epsilon = 0.5 (nabla u + nabla u^T) + Vector6r strain; + strain[0] = nablaU(0, 0); // \epsilon_{00} + strain[1] = nablaU(1, 1); // \epsilon_{11} + strain[2] = nablaU(2, 2); // \epsilon_{22} + strain[3] = 0.5 * (nablaU(0, 1) + nablaU(1, 0)); // \epsilon_{01} + strain[4] = 0.5 * (nablaU(0, 2) + nablaU(2, 0)); // \epsilon_{02} + strain[5] = 0.5 * (nablaU(1, 2) + nablaU(2, 1)); // \epsilon_{12} + + // stress = C * epsilon + m_stress[i] = C * strain; + } + } +} + +void Elasticity_Becker2009::computeForces() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi0 = m_model->getPosition0(i0); + + const size_t numNeighbors = m_initialNeighbors[i0].size(); + Vector3r fi; + fi.setZero(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + + const Vector3r xj_xi_0 = xj0 - xi0; + const Vector3r gradW0 = sim->gradW(xj_xi_0); + + const Vector3r dji = m_restVolumes[i] * gradW0; + const Vector3r dij = -m_restVolumes[neighborIndex] * gradW0; + + Vector3r sdji, sdij; + symMatTimesVec(m_stress[neighborIndex], dji, sdji); + symMatTimesVec(m_stress[i], dij, sdij); + + const Vector3r fij = -m_restVolumes[neighborIndex] * sdji; + const Vector3r fji = -m_restVolumes[i] * sdij; + + fi += m_rotations[neighborIndex] * fij - m_rotations[i] * fji; + } + fi = 0.5*fi; + + if (m_alpha != 0.0) + { + ////////////////////////////////////////////////////////////////////////// + // Ganzenm�ller, G.C. 2015. An hourglass control algorithm for Lagrangian + // Smooth Particle Hydrodynamics. Computer Methods in Applied Mechanics and + // Engineering 286, 87�106. + ////////////////////////////////////////////////////////////////////////// + Vector3r fi_hg; + fi_hg.setZero(); + const Vector3r &xi = m_model->getPosition(i); + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + + // Note: Ganzenm�ller defines xij = xj-xi + const Vector3r xi_xj = -(xi - xj); + const Real xixj_l = xi_xj.norm(); + if (xixj_l > 1.0e-6) + { + // Note: Ganzenm�ller defines xij = xj-xi + const Vector3r xi_xj_0 = -(xi0 - xj0); + const Real xixj0_l2 = xi_xj_0.squaredNorm(); + const Real W0 = sim->W(xi_xj_0); + + const Vector3r xij_i = m_F[i] * m_rotations[i] * xi_xj_0; + const Vector3r xji_j = -m_F[neighborIndex] * m_rotations[neighborIndex] * xi_xj_0; + const Vector3r epsilon_ij_i = xij_i - xi_xj; + const Vector3r epsilon_ji_j = xji_j + xi_xj; + + const Real delta_ij_i = epsilon_ij_i.dot(xi_xj) / xixj_l; + const Real delta_ji_j = -epsilon_ji_j.dot(xi_xj) / xixj_l; + + fi_hg -= m_restVolumes[neighborIndex] * W0 / xixj0_l2 * (delta_ij_i + delta_ji_j) * xi_xj / xixj_l; + } + } + fi_hg *= m_alpha * m_youngsModulus * m_restVolumes[i]; + model->getAcceleration(i) += fi_hg / model->getMass(i); + } + + // elastic acceleration + Vector3r &ai = model->getAcceleration(i); + ai += fi / model->getMass(i); + } + } +} + diff --git a/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.h b/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.h new file mode 100644 index 0000000..0d9ec4c --- /dev/null +++ b/SPlisHSPlasH/Elasticity/Elasticity_Becker2009.h @@ -0,0 +1,59 @@ +#ifndef __Elasticity_Becker2009_h__ +#define __Elasticity_Becker2009_h__ + +#include "SPlisHSPlasH/Common.h" +#include "SPlisHSPlasH/FluidModel.h" +#include "ElasticityBase.h" + +namespace SPH +{ + /** \brief This class implements the corotated SPH method for deformable solids introduced + * by Becker et al. \cite Becker:2009. + */ + class Elasticity_Becker2009 : public ElasticityBase + { + protected: + // initial particle indices, used to access their original positions + std::vector<unsigned int> m_current_to_initial_index; + std::vector<unsigned int> m_initial_to_current_index; + // initial particle neighborhood + std::vector<std::vector<unsigned int>> m_initialNeighbors; + // volumes in rest configuration + std::vector<Real> m_restVolumes; + std::vector<Matrix3r> m_rotations; + std::vector<Vector6r> m_stress; + std::vector<Matrix3r> m_F; + Real m_alpha; + + void initValues(); + void computeRotations(); + void computeStress(); + void computeForces(); + + virtual void initParameters(); + + ////////////////////////////////////////////////////////////////////////// + // multiplication of symmetric matrix, represented by a 6D vector, and a + // 3D vector + ////////////////////////////////////////////////////////////////////////// + FORCE_INLINE void symMatTimesVec(const Vector6r & M, const Vector3r & v, Vector3r &res) + { + res[0] = M[0] * v[0] + M[3] * v[1] + M[4] * v[2]; + res[1] = M[3] * v[0] + M[1] * v[1] + M[5] * v[2]; + res[2] = M[4] * v[0] + M[5] * v[1] + M[2] * v[2]; + } + + + public: + static int ALPHA; + + Elasticity_Becker2009(FluidModel *model); + virtual ~Elasticity_Becker2009(void); + + virtual void step(); + virtual void reset(); + virtual void performNeighborhoodSearchSort(); + }; +} + +#endif diff --git a/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.cpp b/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.cpp new file mode 100644 index 0000000..866a51c --- /dev/null +++ b/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.cpp @@ -0,0 +1,562 @@ +#include "Elasticity_Peer2018.h" +#include "SPlisHSPlasH/Simulation.h" +#include "SPlisHSPlasH/Utilities/MathFunctions.h" +#include "SPlisHSPlasH/TimeManager.h" +#include "Utilities/Timing.h" +#include "Utilities/Counting.h" + +using namespace SPH; +using namespace GenParam; + +int Elasticity_Peer2018::ITERATIONS = -1; +int Elasticity_Peer2018::MAX_ITERATIONS = -1; +int Elasticity_Peer2018::MAX_ERROR = -1; +int Elasticity_Peer2018::ALPHA = -1; + + +Elasticity_Peer2018::Elasticity_Peer2018(FluidModel *model) : + ElasticityBase(model) +{ + const unsigned int numParticles = model->numActiveParticles(); + m_restVolumes.resize(numParticles); + m_current_to_initial_index.resize(numParticles); + m_initial_to_current_index.resize(numParticles); + m_initialNeighbors.resize(numParticles); + m_rotations.resize(numParticles, Matrix3r::Identity()); + m_stress.resize(numParticles); + m_L.resize(numParticles); + m_F.resize(numParticles); + + m_iterations = 0; + m_maxIter = 100; + m_maxError = 1.0e-4; + m_alpha = 0.0; + + initValues(); + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "rest volume", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &m_restVolumes[i]; } }); + model->addField({ "rotation", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_rotations[i](0,0); } }); + model->addField({ "stress", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_stress[i][0]; } }); + model->addField({ "deformation gradient", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_F[i](0,0); } }); + model->addField({ "correction matrix", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_L[i](0,0); } }); + } +} + +Elasticity_Peer2018::~Elasticity_Peer2018(void) +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("rest volume"); + model->removeFieldByName("rotation"); + model->removeFieldByName("stress"); + model->removeFieldByName("deformation gradient"); + model->removeFieldByName("correction matrix"); + } +} + + +void Elasticity_Peer2018::initParameters() +{ + ElasticityBase::initParameters(); + + ITERATIONS = createNumericParameter("elasticityIterations", "Iterations", &m_iterations); + setGroup(ITERATIONS, "Elasticity"); + setDescription(ITERATIONS, "Iterations required by the elasticity solver."); + getParameter(ITERATIONS)->setReadOnly(true); + + MAX_ITERATIONS = createNumericParameter("elasticityMaxIter", "Max. iterations (elasticity)", &m_maxIter); + setGroup(MAX_ITERATIONS, "Elasticity"); + setDescription(MAX_ITERATIONS, "Coefficient for the elasticity force computation"); + static_cast<NumericParameter<unsigned int>*>(getParameter(MAX_ITERATIONS))->setMinValue(1); + + MAX_ERROR = createNumericParameter("elasticityMaxError", "Max. elasticity error", &m_maxError); + setGroup(MAX_ERROR, "Elasticity"); + setDescription(MAX_ERROR, "Coefficient for the elasticity force computation"); + RealParameter * rparam = static_cast<RealParameter*>(getParameter(MAX_ERROR)); + rparam->setMinValue(1e-7); + + ALPHA = createNumericParameter("alpha", "Zero-energy modes suppression", &m_alpha); + setGroup(ALPHA, "Elasticity"); + setDescription(ALPHA, "Coefficent for zero-energy modes suppression method"); + rparam = static_cast<RealParameter*>(getParameter(ALPHA)); + rparam->setMinValue(0.0); +} + + +void Elasticity_Peer2018::initValues() +{ + Simulation *sim = Simulation::getCurrent(); + sim->getNeighborhoodSearch()->find_neighbors(); + + FluidModel *model = m_model; + const unsigned int numParticles = model->numActiveParticles(); + const unsigned int fluidModelIndex = model->getPointSetIndex(); + + // Store the neighbors in the reference configurations and + // compute the volume of each particle in rest state + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + m_current_to_initial_index[i] = i; + m_initial_to_current_index[i] = i; + + // only neighbors in same phase will influence elasticity + const unsigned int numNeighbors = sim->numberOfNeighbors(fluidModelIndex, fluidModelIndex, i); + m_initialNeighbors[i].resize(numNeighbors); + for (unsigned int j = 0; j < numNeighbors; j++) + m_initialNeighbors[i][j] = sim->getNeighbor(fluidModelIndex, fluidModelIndex, i, j); + + // compute volume + Real density = model->getMass(i) * sim->W_zero(); + const Vector3r &xi = model->getPosition(i); + forall_fluid_neighbors_in_same_phase( + density += model->getMass(neighborIndex) * sim->W(xi - xj); + ) + m_restVolumes[i] = model->getMass(i) / density; + m_rotations[i].setIdentity(); + } + } + + computeMatrixL(); +} + + +void Elasticity_Peer2018::step() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const Real dt = TimeManager::getCurrent()->getTimeStepSize(); + + ////////////////////////////////////////////////////////////////////////// + // Init linear system solver and preconditioner + ////////////////////////////////////////////////////////////////////////// + MatrixReplacement A(3 * m_model->numActiveParticles(), matrixVecProd, (void*)this); + //m_solver.preconditioner().init(m_model->numActiveParticles(), diagonalMatrixElement, (void*)this); + + m_solver.setTolerance(m_maxError); + m_solver.setMaxIterations(m_maxIter); + m_solver.compute(A); + + VectorXr b(3 * numParticles); + VectorXr x(3 * numParticles); + VectorXr g(3 * numParticles); + + computeRotations(); + computeRHS(b); + + // warmstart + #pragma omp parallel for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + g.segment<3>(3 * i) = m_model->getVelocity(i) + dt * m_model->getAcceleration(i); + } + + ////////////////////////////////////////////////////////////////////////// + // Solve linear system + ////////////////////////////////////////////////////////////////////////// + START_TIMING("Elasticity - CG solve"); + x = m_solver.solveWithGuess(b, g); + m_iterations = (int)m_solver.iterations(); + STOP_TIMING_AVG; + INCREASE_COUNTER("Elasticity - CG iterations", static_cast<Real>(m_iterations)); + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + Vector3r &ai = m_model->getAcceleration(i); + ai += (1.0 / dt) * (x.segment<3>(3 * i) - m_model->getVelocity(i)); + } + } +} + + +void Elasticity_Peer2018::reset() +{ + initValues(); +} + +void Elasticity_Peer2018::performNeighborhoodSearchSort() +{ + const unsigned int numPart = m_model->numActiveParticles(); + if (numPart == 0) + return; + + Simulation *sim = Simulation::getCurrent(); + auto const& d = sim->getNeighborhoodSearch()->point_set(m_model->getPointSetIndex()); + d.sort_field(&m_restVolumes[0]); + d.sort_field(&m_rotations[0]); + d.sort_field(&m_current_to_initial_index[0]); + d.sort_field(&m_L[0]); + + for (unsigned int i = 0; i < numPart; i++) + m_initial_to_current_index[m_current_to_initial_index[i]] = i; +} + +void Elasticity_Peer2018::computeRotations() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi = m_model->getPosition(i); + const Vector3r &xi0 = m_model->getPosition0(i0); + Matrix3r F; + F.setZero(); + + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + const Vector3r xj_xi = xj - xi; + const Vector3r xi_xj_0 = xi0 - xj0; + const Vector3r correctedKernel = m_L[i] * sim->gradW(xi_xj_0); + F += m_restVolumes[neighborIndex] * xj_xi * correctedKernel.transpose(); + } + +// Vector3r sigma; +// Matrix3r U, VT; +// MathFunctions::svdWithInversionHandling(F, sigma, U, VT); +// m_rotations[i] = U * VT; + Quaternionr q(m_rotations[i]); + MathFunctions::extractRotation(F, q, 10); + m_rotations[i] = q.matrix(); + } + } +} + +void Elasticity_Peer2018::computeMatrixL() +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi0 = m_model->getPosition0(i0); + Matrix3r L; + L.setZero(); + + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + const Vector3r xj_xi_0 = xj0 - xi0; + const Vector3r gradW = sim->gradW(xj_xi_0); + + // minus because gradW(xij0) == -gradW(xji0) + L -= m_restVolumes[neighborIndex] * gradW * xj_xi_0.transpose(); + } + + bool invertible = false; + L.computeInverseWithCheck(m_L[i], invertible, 1e-9); + if (!invertible) + { + //MathFunctions::pseudoInverse(L, m_L[i]); + m_L[i] = Matrix3r::Zero(); + } + } + } +} + + +void Elasticity_Peer2018::computeRHS(VectorXr & rhs) +{ + Simulation *sim = Simulation::getCurrent(); + const unsigned int numParticles = m_model->numActiveParticles(); + const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + FluidModel *model = m_model; + const Real dt = TimeManager::getCurrent()->getTimeStepSize(); + + Real mu = m_youngsModulus / (static_cast<Real>(2.0) * (static_cast<Real>(1.0) + m_poissonRatio)); + Real lambda = m_youngsModulus * m_poissonRatio / ((static_cast<Real>(1.0) + m_poissonRatio) * (static_cast<Real>(1.0) - static_cast<Real>(2.0) * m_poissonRatio)); + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi = m_model->getPosition(i); + const Vector3r &xi0 = m_model->getPosition0(i0); + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // compute corotated deformation gradient (Eq. 18) + ////////////////////////////////////////////////////////////////////////// + m_F[i].setZero(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + const Vector3r xj_xi = xj - xi; + const Vector3r xi_xj_0 = xi0 - xj0; + const Vector3r correctedRotatedKernel = m_rotations[i] * m_L[i] * sim->gradW(xi_xj_0); + m_F[i] += m_restVolumes[neighborIndex] * xj_xi * correctedRotatedKernel.transpose(); + } + + ////////////////////////////////////////////////////////////////////////// + // compute Cauchy strain: epsilon = 0.5 (F + F^T) - I + ////////////////////////////////////////////////////////////////////////// + Vector6r strain; + strain[0] = m_F[i](0, 0) - 1.0; // \epsilon_{00} + strain[1] = m_F[i](1, 1) - 1.0; // \epsilon_{11} + strain[2] = m_F[i](2, 2) - 1.0; // \epsilon_{22} + strain[3] = 0.5 * (m_F[i](0, 1) + m_F[i](1, 0)); // \epsilon_{01} + strain[4] = 0.5 * (m_F[i](0, 2) + m_F[i](2, 0)); // \epsilon_{02} + strain[5] = 0.5 * (m_F[i](1, 2) + m_F[i](2, 1)); // \epsilon_{12} + + ////////////////////////////////////////////////////////////////////////// + // First Piola Kirchhoff stress = 2 mu epsilon + lambda trace(epsilon) I + ////////////////////////////////////////////////////////////////////////// + const Real trace = strain[0] + strain[1] + strain[2]; + const Real ltrace = lambda*trace; + m_stress[i] = strain * 2.0*mu; + m_stress[i][0] += ltrace; + m_stress[i][1] += ltrace; + m_stress[i][2] += ltrace; + } + } + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = m_current_to_initial_index[i]; + const Vector3r &xi0 = m_model->getPosition0(i0); + const size_t numNeighbors = m_initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Compute elastic force + ////////////////////////////////////////////////////////////////////////// + Vector3r force; + force.setZero(); + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + const Vector3r xi_xj_0 = xi0 - xj0; + const Vector3r correctedRotatedKernel_i = m_rotations[i] * m_L[i] * sim->gradW(xi_xj_0); + const Vector3r correctedRotatedKernel_j = -m_rotations[neighborIndex] * m_L[neighborIndex] * sim->gradW(xi_xj_0); + Vector3r PWi, PWj; + symMatTimesVec(m_stress[i], correctedRotatedKernel_i, PWi); + symMatTimesVec(m_stress[neighborIndex], correctedRotatedKernel_j, PWj); + force += m_restVolumes[i] * m_restVolumes[neighborIndex] * (PWi - PWj); + } + + if (m_alpha != 0.0) + { + ////////////////////////////////////////////////////////////////////////// + // Ganzenm�ller, G.C. 2015. An hourglass control algorithm for Lagrangian + // Smooth Particle Hydrodynamics. Computer Methods in Applied Mechanics and + // Engineering 286, 87�106. + ////////////////////////////////////////////////////////////////////////// + Vector3r fi_hg; + fi_hg.setZero(); + const Vector3r &xi = m_model->getPosition(i); + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = m_initial_to_current_index[m_initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = m_initialNeighbors[i0][j]; + + const Vector3r &xj = model->getPosition(neighborIndex); + const Vector3r &xj0 = m_model->getPosition0(neighborIndex0); + + // Note: Ganzenm�ller defines xij = xj-xi + const Vector3r xi_xj = -(xi - xj); + const Real xixj_l = xi_xj.norm(); + if (xixj_l > 1.0e-6) + { + // Note: Ganzenm�ller defines xij = xj-xi + const Vector3r xi_xj_0 = -(xi0 - xj0); + const Real xixj0_l2 = xi_xj_0.squaredNorm(); + const Real W0 = sim->W(xi_xj_0); + + const Vector3r xij_i = m_F[i] * m_rotations[i] * xi_xj_0; + const Vector3r xji_j = -m_F[neighborIndex] * m_rotations[neighborIndex] * xi_xj_0; + const Vector3r epsilon_ij_i = xij_i - xi_xj; + const Vector3r epsilon_ji_j = xji_j + xi_xj; + + const Real delta_ij_i = epsilon_ij_i.dot(xi_xj) / xixj_l; + const Real delta_ji_j = -epsilon_ji_j.dot(xi_xj) / xixj_l; + + fi_hg -= m_restVolumes[neighborIndex] * W0 / xixj0_l2 * (delta_ij_i + delta_ji_j) * xi_xj / xixj_l; + } + } + fi_hg *= m_alpha * m_youngsModulus * m_restVolumes[i]; + model->getAcceleration(i) += fi_hg / model->getMass(i); + } + + rhs.segment<3>(3 * i) = model->getVelocity(i) + dt * (model->getAcceleration(i) + 1.0 / model->getMass(i) * force); + } + } +} + +void Elasticity_Peer2018::matrixVecProd(const Real* vec, Real *result, void *userData) +{ + Simulation *sim = Simulation::getCurrent(); + Elasticity_Peer2018 * elasticity = static_cast<Elasticity_Peer2018*>(userData); + FluidModel *model = elasticity->getModel(); + const unsigned int numParticles = model->numActiveParticles(); + const Real dt = TimeManager::getCurrent()->getTimeStepSize(); + + const auto ¤t_to_initial_index = elasticity->m_current_to_initial_index; + const auto &initial_to_current_index = elasticity->m_initial_to_current_index; + const auto &initialNeighbors = elasticity->m_initialNeighbors; + const auto &restVolumes = elasticity->m_restVolumes; + const auto &rotations = elasticity->m_rotations; + const auto &L = elasticity->m_L; + auto &stress = elasticity->m_stress; + const Real youngsModulus = elasticity->m_youngsModulus; + const Real poissonRatio = elasticity->m_poissonRatio; + + Real mu = youngsModulus / (static_cast<Real>(2.0) * (static_cast<Real>(1.0) + poissonRatio)); + Real lambda = youngsModulus * poissonRatio / ((static_cast<Real>(1.0) + poissonRatio) * (static_cast<Real>(1.0) - static_cast<Real>(2.0) * poissonRatio)); + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = current_to_initial_index[i]; + const Vector3r &pi = Eigen::Map<const Vector3r>(&vec[3 * i], 3); + const Vector3r &xi0 = model->getPosition0(i0); + const size_t numNeighbors = initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // compute corotated deformation gradient (Eq. 18) + ////////////////////////////////////////////////////////////////////////// + Matrix3r nablaU; + nablaU.setZero(); + + ////////////////////////////////////////////////////////////////////////// + // Fluid + ////////////////////////////////////////////////////////////////////////// + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = initial_to_current_index[initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = initialNeighbors[i0][j]; + + const Vector3r &pj = Eigen::Map<const Vector3r>(&vec[3 * neighborIndex], 3); + const Vector3r &xj0 = model->getPosition0(neighborIndex0); + const Vector3r pj_pi = pj - pi; + const Vector3r xi_xj_0 = xi0 - xj0; + const Vector3r correctedRotatedKernel = rotations[i] * L[i] * sim->gradW(xi_xj_0); + nablaU += restVolumes[neighborIndex] * pj_pi * correctedRotatedKernel.transpose(); + } + nablaU *= dt; + + ////////////////////////////////////////////////////////////////////////// + // compute Cauchy strain: epsilon = 0.5 (nablaU + nablaU^T) + ////////////////////////////////////////////////////////////////////////// + Vector6r strain; + strain[0] = nablaU(0, 0); // \epsilon_{00} + strain[1] = nablaU(1, 1); // \epsilon_{11} + strain[2] = nablaU(2, 2); // \epsilon_{22} + strain[3] = 0.5 * (nablaU(0, 1) + nablaU(1, 0)); // \epsilon_{01} + strain[4] = 0.5 * (nablaU(0, 2) + nablaU(2, 0)); // \epsilon_{02} + strain[5] = 0.5 * (nablaU(1, 2) + nablaU(2, 1)); // \epsilon_{12} + + ////////////////////////////////////////////////////////////////////////// + // First Piola Kirchhoff stress = 2 mu epsilon + lambda trace(epsilon) I + ////////////////////////////////////////////////////////////////////////// + const Real trace = strain[0] + strain[1] + strain[2]; + const Real ltrace = lambda*trace; + stress[i] = strain * 2.0*mu; + stress[i][0] += ltrace; + stress[i][1] += ltrace; + stress[i][2] += ltrace; + } + } + + #pragma omp parallel default(shared) + { + #pragma omp for schedule(static) + for (int i = 0; i < (int)numParticles; i++) + { + const unsigned int i0 = current_to_initial_index[i]; + const Vector3r &xi0 = model->getPosition0(i0); + const size_t numNeighbors = initialNeighbors[i0].size(); + + ////////////////////////////////////////////////////////////////////////// + // Compute elastic force + ////////////////////////////////////////////////////////////////////////// + Vector3r force; + force.setZero(); + for (unsigned int j = 0; j < numNeighbors; j++) + { + const unsigned int neighborIndex = initial_to_current_index[initialNeighbors[i0][j]]; + // get initial neighbor index considering the current particle order + const unsigned int neighborIndex0 = initialNeighbors[i0][j]; + + const Vector3r &xj0 = model->getPosition0(neighborIndex0); + const Vector3r xi_xj_0 = xi0 - xj0; + const Vector3r correctedRotatedKernel_i = rotations[i] * L[i] * sim->gradW(xi_xj_0); + const Vector3r correctedRotatedKernel_j = -rotations[neighborIndex] * L[neighborIndex] * sim->gradW(xi_xj_0); + Vector3r PWi, PWj; + elasticity->symMatTimesVec(stress[i], correctedRotatedKernel_i, PWi); + elasticity->symMatTimesVec(stress[neighborIndex], correctedRotatedKernel_j, PWj); + force += restVolumes[i] * restVolumes[neighborIndex] * (PWi - PWj); + } + + const Real factor = dt / model->getMass(i); + result[3 * i] = vec[3 * i] - factor * force[0]; + result[3 * i + 1] = vec[3 * i + 1] - factor * force[1]; + result[3 * i + 2] = vec[3 * i + 2] - factor * force[2]; + } + } +} diff --git a/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.h b/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.h new file mode 100644 index 0000000..b167df4 --- /dev/null +++ b/SPlisHSPlasH/Elasticity/Elasticity_Peer2018.h @@ -0,0 +1,73 @@ +#ifndef __Elasticity_Peer2018_h__ +#define __Elasticity_Peer2018_h__ + +#include "SPlisHSPlasH/Common.h" +#include "SPlisHSPlasH/FluidModel.h" +#include "ElasticityBase.h" +#include "SPlisHSPlasH/Utilities/MatrixFreeSolver.h" + +namespace SPH +{ + /** \brief This class implements the implicit SPH formulation for + * incompressible linearly elastic solids introduced + * by Peer et al. \cite PGBT17. + */ + class Elasticity_Peer2018 : public ElasticityBase + { + protected: + typedef Eigen::ConjugateGradient<MatrixReplacement, Eigen::Lower | Eigen::Upper, Eigen::IdentityPreconditioner> Solver; + + // initial particle indices, used to access their original positions + std::vector<unsigned int> m_current_to_initial_index; + std::vector<unsigned int> m_initial_to_current_index; + // initial particle neighborhood + std::vector<std::vector<unsigned int>> m_initialNeighbors; + // volumes in rest configuration + std::vector<Real> m_restVolumes; + std::vector<Matrix3r> m_rotations; + std::vector<Vector6r> m_stress; + std::vector<Matrix3r> m_L; + std::vector<Matrix3r> m_F; + unsigned int m_iterations; + unsigned int m_maxIter; + Real m_maxError; + Real m_alpha; + Solver m_solver; + + void initValues(); + void computeMatrixL(); + void computeRotations(); + void computeRHS(VectorXr & rhs); + + virtual void initParameters(); + + ////////////////////////////////////////////////////////////////////////// + // multiplication of symmetric matrix, represented by a 6D vector, and a + // 3D vector + ////////////////////////////////////////////////////////////////////////// + FORCE_INLINE void symMatTimesVec(const Vector6r & M, const Vector3r & v, Vector3r &res) + { + res[0] = M[0] * v[0] + M[3] * v[1] + M[4] * v[2]; + res[1] = M[3] * v[0] + M[1] * v[1] + M[5] * v[2]; + res[2] = M[4] * v[0] + M[5] * v[1] + M[2] * v[2]; + } + + + public: + static int ITERATIONS; + static int MAX_ITERATIONS; + static int MAX_ERROR; + static int ALPHA; + + Elasticity_Peer2018(FluidModel *model); + virtual ~Elasticity_Peer2018(void); + + virtual void step(); + virtual void reset(); + virtual void performNeighborhoodSearchSort(); + + static void matrixVecProd(const Real* vec, Real *result, void *userData); + }; +} + +#endif diff --git a/SPlisHSPlasH/FluidModel.cpp b/SPlisHSPlasH/FluidModel.cpp index 6aed189..8cad952 100644 --- a/SPlisHSPlasH/FluidModel.cpp +++ b/SPlisHSPlasH/FluidModel.cpp @@ -25,6 +25,9 @@ #include "Vorticity/MicropolarModel_Bender2017.h" #include "Drag/DragForce_Gissler2017.h" #include "Drag/DragForce_Macklin2014.h" +#include "Elasticity/ElasticityBase.h" +#include "Elasticity/Elasticity_Becker2009.h" +#include "Elasticity/Elasticity_Peer2018.h" using namespace SPH; @@ -37,6 +40,7 @@ int FluidModel::DRAG_METHOD = -1; int FluidModel::SURFACE_TENSION_METHOD = -1; int FluidModel::VISCOSITY_METHOD = -1; int FluidModel::VORTICITY_METHOD = -1; +int FluidModel::ELASTICITY_METHOD = -1; int FluidModel::ENUM_DRAG_NONE = -1; int FluidModel::ENUM_DRAG_MACKLIN2014 = -1; int FluidModel::ENUM_DRAG_GISSLER2017 = -1; @@ -55,6 +59,9 @@ int FluidModel::ENUM_VISCOSITY_WEILER2018 = -1; int FluidModel::ENUM_VORTICITY_NONE = -1; int FluidModel::ENUM_VORTICITY_MICROPOLAR = -1; int FluidModel::ENUM_VORTICITY_VC = -1; +int FluidModel::ENUM_ELASTICITY_NONE = -1; +int FluidModel::ENUM_ELASTICITY_BECKER2009 = -1; +int FluidModel::ENUM_ELASTICITY_PEER2018 = -1; FluidModel::FluidModel() : @@ -82,15 +89,27 @@ FluidModel::FluidModel() : m_surfaceTensionMethodChanged = nullptr; m_viscosityMethodChanged = nullptr; m_vorticityMethodChanged = nullptr; + m_elasticityMethod = ElasticityMethods::None; + m_elasticity = nullptr; + m_elasticityMethodChanged = nullptr; + + addField({ "position", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &getPosition(i)[0]; } }); + addField({ "velocity", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &getVelocity(i)[0]; } }); + addField({ "density", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &getDensity(i); } }); } FluidModel::~FluidModel(void) { + removeFieldByName("position"); + removeFieldByName("velocity"); + removeFieldByName("density"); + delete m_emitterSystem; delete m_surfaceTension; delete m_drag; delete m_vorticity; delete m_viscosity; + delete m_elasticity; releaseFluidParticles(); } @@ -171,6 +190,16 @@ void FluidModel::initParameters() enumParam->addEnumValue("None", ENUM_VORTICITY_NONE); enumParam->addEnumValue("Micropolar model", ENUM_VORTICITY_MICROPOLAR); enumParam->addEnumValue("Vorticity confinement", ENUM_VORTICITY_VC); + + ParameterBase::GetFunc<int> getElasticityFct = std::bind(&FluidModel::getElasticityMethod, this); + ParameterBase::SetFunc<int> setElasticityFct = std::bind(&FluidModel::setElasticityMethod, this, std::placeholders::_1); + ELASTICITY_METHOD = createEnumParameter("elasticityMethod", "Elasticity method", getElasticityFct, setElasticityFct); + setGroup(ELASTICITY_METHOD, "Elasticity"); + setDescription(ELASTICITY_METHOD, "Method to compute elastic forces."); + enumParam = static_cast<EnumParameter*>(getParameter(ELASTICITY_METHOD)); + enumParam->addEnumValue("None", ENUM_ELASTICITY_NONE); + enumParam->addEnumValue("Becker et al. 2009", ENUM_ELASTICITY_BECKER2009); + enumParam->addEnumValue("Peer et al. 2018", ENUM_ELASTICITY_PEER2018); } @@ -201,6 +230,8 @@ void FluidModel::reset() m_vorticity->reset(); if (m_drag) m_drag->reset(); + if (m_elasticity) + m_elasticity->reset(); m_emitterSystem->reset(); } @@ -305,15 +336,29 @@ void FluidModel::performNeighborhoodSearchSort() m_vorticity->performNeighborhoodSearchSort(); if (m_drag) m_drag->performNeighborhoodSearchSort(); + if (m_elasticity) + m_elasticity->performNeighborhoodSearchSort(); } void SPH::FluidModel::setDensity0(const Real v) { m_density0 = v; initMasses(); - Simulation::getCurrent()->updateBoundaryPsi(); } +const SPH::FieldDescription & SPH::FluidModel::getField(const std::string &name) +{ + unsigned int index = 0; + for (auto i = 0; i < m_fields.size(); i++) + { + if (m_fields[i].name == name) + { + index = i; + break; + } + } + return m_fields[index]; +} void FluidModel::setNumActiveParticles(const unsigned int num) { @@ -345,6 +390,11 @@ void FluidModel::setVorticityMethodChangedCallback(std::function<void()> const& m_vorticityMethodChanged = callBackFct; } +void FluidModel::setElasticityMethodChangedCallback(std::function<void()> const& callBackFct) +{ + m_elasticityMethodChanged = callBackFct; +} + void FluidModel::computeSurfaceTension() { if (m_surfaceTension) @@ -369,6 +419,12 @@ void FluidModel::computeDragForce() m_drag->step(); } +void FluidModel::computeElasticity() +{ + if (m_elasticity) + m_elasticity->step(); +} + void FluidModel::emittedParticles(const unsigned int startIndex) { if (m_viscosity) @@ -379,6 +435,8 @@ void FluidModel::emittedParticles(const unsigned int startIndex) m_vorticity->emittedParticles(startIndex); if (m_drag) m_drag->emittedParticles(startIndex); + if (m_elasticity) + m_elasticity->emittedParticles(startIndex); } void FluidModel::setSurfaceTensionMethod(const int val) @@ -495,3 +553,49 @@ void FluidModel::setDragMethod(const int val) if (m_dragMethodChanged != nullptr) m_dragMethodChanged(); } + +void FluidModel::setElasticityMethod(const int val) +{ + ElasticityMethods em = static_cast<ElasticityMethods>(val); + if ((em < ElasticityMethods::None) || (em >= ElasticityMethods::NumElasticityMethods)) + em = ElasticityMethods::None; + + if (em == m_elasticityMethod) + return; + + delete m_elasticity; + m_elasticity = nullptr; + + m_elasticityMethod = em; + + if (m_elasticityMethod == ElasticityMethods::Becker2009) + m_elasticity = new Elasticity_Becker2009(this); + else if (m_elasticityMethod == ElasticityMethods::Peer2018) + m_elasticity = new Elasticity_Peer2018(this); + + if (m_elasticity != nullptr) + m_elasticity->init(); + + if (m_elasticityMethodChanged != nullptr) + m_elasticityMethodChanged(); +} + + +void FluidModel::addField(const FieldDescription &field) +{ + m_fields.push_back(field); + std::sort(m_fields.begin(), m_fields.end(), [](FieldDescription &i, FieldDescription &j) -> bool { return (i.name < j.name); }); +} + +void FluidModel::removeFieldByName(const std::string &fieldName) +{ + for (auto it = m_fields.begin(); it != m_fields.end(); it++) + { + if (it->name == fieldName) + { + m_fields.erase(it); + break; + } + } +} + diff --git a/SPlisHSPlasH/FluidModel.h b/SPlisHSPlasH/FluidModel.h index e9f1b05..b8588c2 100644 --- a/SPlisHSPlasH/FluidModel.h +++ b/SPlisHSPlasH/FluidModel.h @@ -15,12 +15,23 @@ namespace SPH class SurfaceTensionBase; class VorticityBase; class DragBase; + class ElasticityBase; class EmitterSystem; + enum FieldType { Scalar = 0, Vector3, Vector6, Matrix3, Matrix6 }; + struct FieldDescription + { + std::string name; + FieldType type; + // getFct(particleIndex) + std::function<Real*(const unsigned int)> getFct; + }; + enum class SurfaceTensionMethods { None = 0, Becker2007, Akinci2013, He2014, NumSurfaceTensionMethods }; enum class ViscosityMethods { None = 0, Standard, XSPH, Bender2017, Peer2015, Peer2016, Takahashi2015, Weiler2018, NumViscosityMethods }; enum class VorticityMethods { None = 0, Micropolar, VorticityConfinement, NumVorticityMethods }; enum class DragMethods { None = 0, Macklin2014, Gissler2017, NumDragMethods }; + enum class ElasticityMethods { None = 0, Becker2009, Peer2018, NumElasticityMethods }; /** \brief The fluid model stores the particle and simulation information */ @@ -35,6 +46,7 @@ namespace SPH static int SURFACE_TENSION_METHOD; static int VISCOSITY_METHOD; static int VORTICITY_METHOD; + static int ELASTICITY_METHOD; static int ENUM_DRAG_NONE; static int ENUM_DRAG_MACKLIN2014; @@ -58,7 +70,10 @@ namespace SPH static int ENUM_VORTICITY_MICROPOLAR; static int ENUM_VORTICITY_VC; - + static int ENUM_ELASTICITY_NONE; + static int ENUM_ELASTICITY_BECKER2009; + static int ENUM_ELASTICITY_PEER2018; + FluidModel(); virtual ~FluidModel(); @@ -89,11 +104,15 @@ namespace SPH VorticityBase *m_vorticity; DragMethods m_dragMethod; DragBase *m_drag; + ElasticityMethods m_elasticityMethod; + ElasticityBase *m_elasticity; + std::vector<FieldDescription> m_fields; std::function<void()> m_dragMethodChanged; std::function<void()> m_surfaceTensionMethodChanged; std::function<void()> m_viscosityMethodChanged; std::function<void()> m_vorticityMethodChanged; + std::function<void()> m_elasticityMethodChanged; Real m_density0; unsigned int m_pointSetIndex; @@ -120,6 +139,13 @@ namespace SPH unsigned int getPointSetIndex() const { return m_pointSetIndex; } + void addField(const FieldDescription &field); + const std::vector<FieldDescription> &getFields() { return m_fields; } + const FieldDescription &getField(const unsigned int i) { return m_fields[i]; } + const FieldDescription &getField(const std::string &name); + const unsigned int numberOfFields() { return static_cast<unsigned int>(m_fields.size()); } + void removeFieldByName(const std::string &fieldName); + void setNumActiveParticles(const unsigned int num); unsigned int numberOfParticles() const { return static_cast<unsigned int>(m_x.size()); } @@ -147,21 +173,26 @@ namespace SPH void setVorticityMethod(const int val); int getDragMethod() const { return static_cast<int>(m_dragMethod); } void setDragMethod(const int val); + int getElasticityMethod() const { return static_cast<int>(m_elasticityMethod); } + void setElasticityMethod(const int val); SurfaceTensionBase *getSurfaceTensionBase() { return m_surfaceTension; } ViscosityBase *getViscosityBase() { return m_viscosity; } VorticityBase *getVorticityBase() { return m_vorticity; } DragBase *getDragBase() { return m_drag; } + ElasticityBase *getElasticityBase() { return m_elasticity; } void setDragMethodChangedCallback(std::function<void()> const& callBackFct); void setSurfaceMethodChangedCallback(std::function<void()> const& callBackFct); void setViscosityMethodChangedCallback(std::function<void()> const& callBackFct); void setVorticityMethodChangedCallback(std::function<void()> const& callBackFct); + void setElasticityMethodChangedCallback(std::function<void()> const& callBackFct); void computeSurfaceTension(); void computeViscosity(); void computeVorticity(); void computeDragForce(); + void computeElasticity(); FORCE_INLINE Vector3r &getPosition0(const unsigned int i) diff --git a/SPlisHSPlasH/IISPH/TimeStepIISPH.cpp b/SPlisHSPlasH/IISPH/TimeStepIISPH.cpp index efae128..1c97210 100644 --- a/SPlisHSPlasH/IISPH/TimeStepIISPH.cpp +++ b/SPlisHSPlasH/IISPH/TimeStepIISPH.cpp @@ -14,10 +14,37 @@ TimeStepIISPH::TimeStepIISPH() : { m_simulationData.init(); m_counter = 0; + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "a_ii", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getAii(fluidModelIndex, i); } }); + model->addField({ "d_ii", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDii(fluidModelIndex, i)[0]; } }); + model->addField({ "d_ij*p_j", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDij_pj(fluidModelIndex, i)[0]; } }); + model->addField({ "pressure", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressure(fluidModelIndex, i); } }); + //model->addField({ "last pressure", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getLastPressure(fluidModelIndex, i); } }); + model->addField({ "advected density", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDensityAdv(fluidModelIndex, i); } }); + model->addField({ "pressure acceleration", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressureAccel(fluidModelIndex, i)[0]; } }); + } } TimeStepIISPH::~TimeStepIISPH(void) { + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("a_ii"); + model->removeFieldByName("d_ii"); + model->removeFieldByName("d_ij*p_j"); + model->removeFieldByName("pressure"); + //model->removeFieldByName("last pressure"); + model->removeFieldByName("advected density"); + model->removeFieldByName("pressure acceleration"); + } } void TimeStepIISPH::step() @@ -187,7 +214,7 @@ void TimeStepIISPH::pressureSolve() for (unsigned int i = 0; i < nFluids; i++) { FluidModel *model = sim->getFluidModel(i); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); avg_density_err = 0.0; pressureSolveIteration(i, avg_density_err); @@ -208,7 +235,7 @@ void TimeStepIISPH::pressureSolveIteration(const unsigned int fluidModelIndex, R const unsigned int numParticles = model->numActiveParticles(); const unsigned int nFluids = sim->numberOfFluidModels(); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); const Real h2 = h*h; const Real omega = 0.5; diff --git a/SPlisHSPlasH/PBF/SimulationDataPBF.cpp b/SPlisHSPlasH/PBF/SimulationDataPBF.cpp index c297103..5d6baaa 100644 --- a/SPlisHSPlasH/PBF/SimulationDataPBF.cpp +++ b/SPlisHSPlasH/PBF/SimulationDataPBF.cpp @@ -67,8 +67,8 @@ void SimulationDataPBF::reset() { m_deltaX[i][j].setZero(); m_lambda[i][j] = 0.0; - getLastPosition(i, j) = fm->getPosition(i); - getOldPosition(i, j) = fm->getPosition(i); + getLastPosition(i, j) = fm->getPosition(j); + getOldPosition(i, j) = fm->getPosition(j); } } } diff --git a/SPlisHSPlasH/PBF/TimeStepPBF.cpp b/SPlisHSPlasH/PBF/TimeStepPBF.cpp index c0327e0..e6f2b4f 100644 --- a/SPlisHSPlasH/PBF/TimeStepPBF.cpp +++ b/SPlisHSPlasH/PBF/TimeStepPBF.cpp @@ -23,10 +23,27 @@ TimeStepPBF::TimeStepPBF() : m_simulationData.init(); m_counter = 0; m_velocityUpdateMethod = 0; + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "lambda", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getLambda(fluidModelIndex, i); } }); + model->addField({ "deltaX", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDeltaX(fluidModelIndex, i)[0]; } }); + } } TimeStepPBF::~TimeStepPBF(void) { + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("lambda"); + model->removeFieldByName("deltaX"); + } } void TimeStepPBF::initParameters() @@ -153,7 +170,7 @@ void TimeStepPBF::pressureSolve() for (unsigned int i = 0; i < nFluids; i++) { FluidModel *model = sim->getFluidModel(i); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); avg_density_err = 0.0; pressureSolveIteration(i, avg_density_err); @@ -178,7 +195,7 @@ void TimeStepPBF::pressureSolveIteration(const unsigned int fluidModelIndex, Rea const unsigned int nFluids = sim->numberOfFluidModels(); const Real eps = 1.0e-6; - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); #pragma omp parallel default(shared) { diff --git a/SPlisHSPlasH/PCISPH/SimulationDataPCISPH.cpp b/SPlisHSPlasH/PCISPH/SimulationDataPCISPH.cpp index c95f348..4e6377a 100644 --- a/SPlisHSPlasH/PCISPH/SimulationDataPCISPH.cpp +++ b/SPlisHSPlasH/PCISPH/SimulationDataPCISPH.cpp @@ -46,7 +46,7 @@ void SimulationDataPCISPH::init() // Find prototype particle // => particle with max. fluid neighbors - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); unsigned int index = 0; unsigned int maxNeighbors = 0; diff --git a/SPlisHSPlasH/PCISPH/TimeStepPCISPH.cpp b/SPlisHSPlasH/PCISPH/TimeStepPCISPH.cpp index 90f485f..961df08 100644 --- a/SPlisHSPlasH/PCISPH/TimeStepPCISPH.cpp +++ b/SPlisHSPlasH/PCISPH/TimeStepPCISPH.cpp @@ -14,10 +14,29 @@ TimeStepPCISPH::TimeStepPCISPH() : { m_simulationData.init(); m_counter = 0; + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "pressure", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressure(fluidModelIndex, i); } }); + model->addField({ "advected density", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getDensityAdv(fluidModelIndex, i); } }); + model->addField({ "pressure acceleration", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressureAccel(fluidModelIndex, i)[0]; } }); + } } TimeStepPCISPH::~TimeStepPCISPH(void) { + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("pressure"); + model->removeFieldByName("advected density"); + model->removeFieldByName("pressure acceleration"); + } } void TimeStepPCISPH::step() @@ -101,7 +120,7 @@ void TimeStepPCISPH::pressureSolve() for (unsigned int i = 0; i < nFluids; i++) { FluidModel *model = sim->getFluidModel(i); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); avg_density_err = 0.0; pressureSolveIteration(i, avg_density_err); @@ -120,7 +139,7 @@ void TimeStepPCISPH::pressureSolveIteration(const unsigned int fluidModelIndex, Simulation *sim = Simulation::getCurrent(); FluidModel *model = sim->getFluidModel(fluidModelIndex); const int numParticles = (int)model->numActiveParticles(); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); const Real h2 = h*h; const Real invH2 = static_cast<Real>(1.0) / h2; diff --git a/SPlisHSPlasH/Simulation.cpp b/SPlisHSPlasH/Simulation.cpp index 2d2df9f..728e617 100644 --- a/SPlisHSPlasH/Simulation.cpp +++ b/SPlisHSPlasH/Simulation.cpp @@ -63,7 +63,7 @@ Simulation::Simulation () m_neighborhoodSearch = nullptr; m_timeStep = nullptr; - m_simulationMethod = SimulationMethods::WCSPH; + m_simulationMethod = SimulationMethods::NumSimulationMethods; m_simulationMethodChanged = NULL; m_sim2D = false; @@ -113,7 +113,8 @@ void Simulation::init(const Real particleRadius, const bool sim2D) // init kernel setParticleRadius(particleRadius); - setSimulationMethod(static_cast<int>(SimulationMethods::DFSPH)); + 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) @@ -316,7 +317,7 @@ void Simulation::setKernel(int val) m_kernelFct = WendlandQuinticC2Kernel2D::W; } } - updateBoundaryPsi(); + updateBoundaryVolume(); } void Simulation::updateTimeStepSize() @@ -396,6 +397,7 @@ void Simulation::computeNonPressureForces() fm->computeViscosity(); fm->computeVorticity(); fm->computeDragForce(); + fm->computeElasticity(); } STOP_TIMING_AVG } @@ -409,7 +411,7 @@ void Simulation::reset() // reset boundary models for (unsigned int i = 0; i < numberOfBoundaryModels(); i++) getBoundaryModel(i)->reset(); - updateBoundaryPsi(); + updateBoundaryVolume(); if (m_timeStep) m_timeStep->reset(); @@ -543,14 +545,11 @@ void Simulation::addFluidModel(const std::string &id, const unsigned int nFluidP } -void Simulation::updateBoundaryPsi() +void Simulation::updateBoundaryVolume() { if (m_neighborhoodSearch == nullptr) return; - // ToDo - const Real density0 = 1000.0; - Simulation *sim = Simulation::getCurrent(); const unsigned int nFluids = sim->numberOfFluidModels(); @@ -562,7 +561,7 @@ void Simulation::updateBoundaryPsi() // Search boundary neighborhood // Activate only static boundaries - LOG_INFO << "Initialize boundary psi"; + LOG_INFO << "Initialize boundary volume"; m_neighborhoodSearch->set_active(false); for (unsigned int i = 0; i < numberOfBoundaryModels(); i++) { @@ -576,7 +575,7 @@ void Simulation::updateBoundaryPsi() for (unsigned int body = 0; body < numberOfBoundaryModels(); body++) { if (!getBoundaryModel(body)->getRigidBodyObject()->isDynamic()) - getBoundaryModel(body)->computeBoundaryPsi(density0); + getBoundaryModel(body)->computeBoundaryVolume(); } ////////////////////////////////////////////////////////////////////////// @@ -592,7 +591,7 @@ void Simulation::updateBoundaryPsi() { m_neighborhoodSearch->set_active(body + nFluids, true, true); m_neighborhoodSearch->find_neighbors(); - getBoundaryModel(body)->computeBoundaryPsi(density0); + getBoundaryModel(body)->computeBoundaryVolume(); } } @@ -605,4 +604,4 @@ void Simulation::updateBoundaryPsi() for (unsigned int j = numberOfFluidModels(); j < m_neighborhoodSearch->point_sets().size(); j++) m_neighborhoodSearch->set_active(i, j, true); } -} \ No newline at end of file +} diff --git a/SPlisHSPlasH/Simulation.h b/SPlisHSPlasH/Simulation.h index f535b52..6045692 100644 --- a/SPlisHSPlasH/Simulation.h +++ b/SPlisHSPlasH/Simulation.h @@ -145,7 +145,7 @@ namespace SPH 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 updateBoundaryPsi(); + void updateBoundaryVolume(); int getKernel() const { return m_kernelMethod; } void setKernel(int val); diff --git a/SPlisHSPlasH/SurfaceTension/SurfaceTension_Akinci2013.cpp b/SPlisHSPlasH/SurfaceTension/SurfaceTension_Akinci2013.cpp index 78a8649..cc28fec 100644 --- a/SPlisHSPlasH/SurfaceTension/SurfaceTension_Akinci2013.cpp +++ b/SPlisHSPlasH/SurfaceTension/SurfaceTension_Akinci2013.cpp @@ -8,10 +8,13 @@ SurfaceTension_Akinci2013::SurfaceTension_Akinci2013(FluidModel *model) : SurfaceTensionBase(model) { m_normals.resize(model->numParticles(), Vector3r::Zero()); + + model->addField({ "normal", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_normals[i][0]; } }); } SurfaceTension_Akinci2013::~SurfaceTension_Akinci2013(void) { + m_model->removeFieldByName("normal"); m_normals.clear(); } @@ -51,7 +54,7 @@ void SurfaceTension_Akinci2013::computeNormals() void SurfaceTension_Akinci2013::step() { Simulation *sim = Simulation::getCurrent(); - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const Real supportRadius = sim->getSupportRadius(); const unsigned int numParticles = m_model->numActiveParticles(); const Real k = m_surfaceTension; @@ -108,7 +111,7 @@ void SurfaceTension_Akinci2013::step() if (length2 > 1.0e-9) { xixj = ((Real) 1.0 / sqrt(length2)) * xixj; - ai -= k * bm_neighbor->getBoundaryPsi(neighborIndex) * xixj * AdhesionKernel::W(xi - xj); + ai -= k * density0 * bm_neighbor->getVolume(neighborIndex) * xixj * AdhesionKernel::W(xi - xj); } ) } diff --git a/SPlisHSPlasH/SurfaceTension/SurfaceTension_Becker2007.cpp b/SPlisHSPlasH/SurfaceTension/SurfaceTension_Becker2007.cpp index 88e9b8f..40614bb 100644 --- a/SPlisHSPlasH/SurfaceTension/SurfaceTension_Becker2007.cpp +++ b/SPlisHSPlasH/SurfaceTension/SurfaceTension_Becker2007.cpp @@ -24,6 +24,7 @@ void SurfaceTension_Becker2007::step() const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; + const Real density0 = model->getDensity0(); // Compute forces #pragma omp parallel default(shared) @@ -53,9 +54,9 @@ void SurfaceTension_Becker2007::step() const Vector3r xixj = xi - xj; const Real r2 = xixj.dot(xixj); if (r2 > diameter2) - ai -= k / m_model->getMass(i) * bm_neighbor->getBoundaryPsi(neighborIndex) * (xi - xj) * sim->W(xi - xj); + ai -= k / m_model->getMass(i) * density0 * bm_neighbor->getVolume(neighborIndex) * (xi - xj) * sim->W(xi - xj); else - ai -= k / m_model->getMass(i) * bm_neighbor->getBoundaryPsi(neighborIndex) * (xi - xj) * sim->W(Vector3r(diameter, 0.0, 0.0)); + ai -= k / m_model->getMass(i) * density0 * bm_neighbor->getVolume(neighborIndex) * (xi - xj) * sim->W(Vector3r(diameter, 0.0, 0.0)); ) } } diff --git a/SPlisHSPlasH/SurfaceTension/SurfaceTension_He2014.cpp b/SPlisHSPlasH/SurfaceTension/SurfaceTension_He2014.cpp index 628b337..cb36373 100644 --- a/SPlisHSPlasH/SurfaceTension/SurfaceTension_He2014.cpp +++ b/SPlisHSPlasH/SurfaceTension/SurfaceTension_He2014.cpp @@ -9,10 +9,16 @@ SurfaceTension_He2014::SurfaceTension_He2014(FluidModel *model) : { m_color.resize(model->numParticles(), 0.0); m_gradC2.resize(model->numParticles(), 0.0); + + model->addField({ "color", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &m_color[i]; } }); + model->addField({ "gradC2", FieldType::Scalar, [&](const unsigned int i) -> Real* { return &m_gradC2[i]; } }); } SurfaceTension_He2014::~SurfaceTension_He2014(void) { + m_model->removeFieldByName("color"); + m_model->removeFieldByName("gradC2"); + m_color.clear(); m_gradC2.clear(); } @@ -23,7 +29,7 @@ void SurfaceTension_He2014::step() Simulation *sim = Simulation::getCurrent(); const unsigned int numParticles = m_model->numActiveParticles(); const Real k = m_surfaceTension; - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; @@ -50,7 +56,7 @@ void SurfaceTension_He2014::step() // Boundary ////////////////////////////////////////////////////////////////////////// forall_boundary_neighbors( - ci += bm_neighbor->getBoundaryPsi(neighborIndex) / density0 * sim->W(xi - xj); + ci += bm_neighbor->getVolume(neighborIndex) * sim->W(xi - xj); ) } } @@ -104,7 +110,7 @@ void SurfaceTension_He2014::step() // Boundary ////////////////////////////////////////////////////////////////////////// forall_boundary_neighbors( - ai += factor*bm_neighbor->getBoundaryPsi(neighborIndex) / density0 * gradC2_i * sim->gradW(xi - xj); + ai += factor* bm_neighbor->getVolume(neighborIndex) * gradC2_i * sim->gradW(xi - xj); ) } } diff --git a/SPlisHSPlasH/Utilities/MathFunctions.cpp b/SPlisHSPlasH/Utilities/MathFunctions.cpp new file mode 100644 index 0000000..273d770 --- /dev/null +++ b/SPlisHSPlasH/Utilities/MathFunctions.cpp @@ -0,0 +1,231 @@ +#include "MathFunctions.h" +#include <cfloat> + +using namespace SPH; + + +// ---------------------------------------------------------------------------------------------- +void MathFunctions::extractRotation(const Matrix3r &A, Quaternionr &q, const unsigned int maxIter) +{ + for (unsigned int iter = 0; iter < maxIter; iter++) + { + Matrix3r R = q.matrix(); + Vector3r omega = (R.col(0).cross(A.col(0)) + R.col(1).cross(A.col(1)) + R.col(2).cross(A.col(2))) * + (1.0 / fabs(R.col(0).dot(A.col(0)) + R.col(1).dot(A.col(1)) + R.col(2).dot(A.col(2)) + 1.0e-9)); + Real w = omega.norm(); + if (w < 1.0e-9) + break; + q = Quaternionr(AngleAxisr(w, (1.0 / w) * omega)) * q; + q.normalize(); + } +} + +void MathFunctions::pseudoInverse(const Matrix3r &a, Matrix3r &res) +{ + const Real epsilon = std::numeric_limits<Real>::epsilon(); + const Eigen::JacobiSVD<Matrix3r> svd(a, Eigen::ComputeFullU | Eigen::ComputeFullV); + const Real tolerance = epsilon * std::max(a.cols(), a.rows()) * svd.singularValues().array().abs()(0); + res = svd.matrixV() * (svd.singularValues().array().abs() > tolerance).select(svd.singularValues().array().inverse(), 0).matrix().asDiagonal() * svd.matrixU().adjoint(); +} + + +/** Perform a singular value decomposition of matrix A: A = U * sigma * V^T. +* This function returns two proper rotation matrices U and V^T which do not +* contain a reflection. Reflections are corrected by the inversion handling +* proposed by Irving et al. 2004. +*/ +void MathFunctions::svdWithInversionHandling(const Matrix3r &A, Vector3r &sigma, Matrix3r &U, Matrix3r &VT) +{ + + Matrix3r AT_A, V; + AT_A = A.transpose() * A; + + Vector3r S; + + // Eigen decomposition of A^T * A + eigenDecomposition(AT_A, V, S); + + // Detect if V is a reflection . + // Make a rotation out of it by multiplying one column with -1. + const Real detV = V.determinant(); + if (detV < 0.0) + { + Real minLambda = REAL_MAX; + unsigned char pos = 0; + for (unsigned char l = 0; l < 3; l++) + { + if (S[l] < minLambda) + { + pos = l; + minLambda = S[l]; + } + } + V(0, pos) = -V(0, pos); + V(1, pos) = -V(1, pos); + V(2, pos) = -V(2, pos); + } + + if (S[0] < 0.0) S[0] = 0.0; // safety for sqrt + if (S[1] < 0.0) S[1] = 0.0; + if (S[2] < 0.0) S[2] = 0.0; + + sigma[0] = sqrt(S[0]); + sigma[1] = sqrt(S[1]); + sigma[2] = sqrt(S[2]); + + VT = V.transpose(); + + // + // Check for values of hatF near zero + // + unsigned char chk = 0; + unsigned char pos = 0; + for (unsigned char l = 0; l < 3; l++) + { + if (fabs(sigma[l]) < 1.0e-4) + { + pos = l; + chk++; + } + } + + if (chk > 0) + { + if (chk > 1) + { + U.setIdentity(); + } + else + { + U = A * V; + for (unsigned char l = 0; l < 3; l++) + { + if (l != pos) + { + for (unsigned char m = 0; m < 3; m++) + { + U(m, l) *= static_cast<Real>(1.0) / sigma[l]; + } + } + } + + Vector3r v[2]; + unsigned char index = 0; + for (unsigned char l = 0; l < 3; l++) + { + if (l != pos) + { + v[index++] = Vector3r(U(0, l), U(1, l), U(2, l)); + } + } + Vector3r vec = v[0].cross(v[1]); + vec.normalize(); + U(0, pos) = vec[0]; + U(1, pos) = vec[1]; + U(2, pos) = vec[2]; + } + } + else + { + Vector3r sigmaInv(static_cast<Real>(1.0) / sigma[0], static_cast<Real>(1.0) / sigma[1], static_cast<Real>(1.0) / sigma[2]); + U = A * V; + for (unsigned char l = 0; l < 3; l++) + { + for (unsigned char m = 0; m < 3; m++) + { + U(m, l) *= sigmaInv[l]; + } + } + } + + const Real detU = U.determinant(); + + // U is a reflection => inversion + if (detU < 0.0) + { + //std::cout << "Inversion!\n"; + Real minLambda = REAL_MAX; + unsigned char pos = 0; + for (unsigned char l = 0; l < 3; l++) + { + if (sigma[l] < minLambda) + { + pos = l; + minLambda = sigma[l]; + } + } + + // invert values of smallest singular value + sigma[pos] = -sigma[pos]; + U(0, pos) = -U(0, pos); + U(1, pos) = -U(1, pos); + U(2, pos) = -U(2, pos); + } +} + +// ---------------------------------------------------------------------------------------------- +void MathFunctions::eigenDecomposition(const Matrix3r &A, Matrix3r &eigenVecs, Vector3r &eigenVals) +{ + const int numJacobiIterations = 10; + const Real epsilon = static_cast<Real>(1e-15); + + Matrix3r D = A; + + // only for symmetric matrices! + eigenVecs.setIdentity(); // unit matrix + int iter = 0; + while (iter < numJacobiIterations) { // 3 off diagonal elements + // find off diagonal element with maximum modulus + int p, q; + Real a, max; + max = fabs(D(0, 1)); + p = 0; q = 1; + a = fabs(D(0, 2)); + if (a > max) { p = 0; q = 2; max = a; } + a = fabs(D(1, 2)); + if (a > max) { p = 1; q = 2; max = a; } + // all small enough -> done + if (max < epsilon) break; + // rotate matrix with respect to that element + jacobiRotate(D, eigenVecs, p, q); + iter++; + } + eigenVals[0] = D(0, 0); + eigenVals[1] = D(1, 1); + eigenVals[2] = D(2, 2); +} + +// ---------------------------------------------------------------------------------------------- +void MathFunctions::jacobiRotate(Matrix3r &A, Matrix3r &R, int p, int q) +{ + // rotates A through phi in pq-plane to set A(p,q) = 0 + // rotation stored in R whose columns are eigenvectors of A + if (A(p, q) == 0.0) + return; + + Real d = (A(p, p) - A(q, q)) / (static_cast<Real>(2.0)*A(p, q)); + Real t = static_cast<Real>(1.0) / (fabs(d) + sqrt(d*d + static_cast<Real>(1.0))); + if (d < 0.0) t = -t; + Real c = static_cast<Real>(1.0) / sqrt(t*t + 1); + Real s = t*c; + A(p, p) += t*A(p, q); + A(q, q) -= t*A(p, q); + A(p, q) = A(q, p) = 0.0; + // transform A + int k; + for (k = 0; k < 3; k++) { + if (k != p && k != q) { + Real Akp = c*A(k, p) + s*A(k, q); + Real Akq = -s*A(k, p) + c*A(k, q); + A(k, p) = A(p, k) = Akp; + A(k, q) = A(q, k) = Akq; + } + } + // store rotation in R + for (k = 0; k < 3; k++) { + Real Rkp = c*R(k, p) + s*R(k, q); + Real Rkq = -s*R(k, p) + c*R(k, q); + R(k, p) = Rkp; + R(k, q) = Rkq; + } +} diff --git a/SPlisHSPlasH/Utilities/MathFunctions.h b/SPlisHSPlasH/Utilities/MathFunctions.h new file mode 100644 index 0000000..1491d0e --- /dev/null +++ b/SPlisHSPlasH/Utilities/MathFunctions.h @@ -0,0 +1,26 @@ +#ifndef MATH_FUNCTIONS_H +#define MATH_FUNCTIONS_H + +#include "SPlisHSPlasH/Common.h" + +// ------------------------------------------------------------------------------------ +namespace SPH +{ + class MathFunctions + { + public: + /** Implementation of the paper: \n + * Matthias M�ller, Jan Bender, Nuttapong Chentanez and Miles Macklin, + * "A Robust Method to Extract the Rotational Part of Deformations", + * ACM SIGGRAPH Motion in Games, 2016 + */ + static void extractRotation(const Matrix3r &A, Quaternionr &q, const unsigned int maxIter); + + static void pseudoInverse(const Matrix3r &a, Matrix3r &res); + static void svdWithInversionHandling(const Matrix3r &A, Vector3r &sigma, Matrix3r &U, Matrix3r &VT); + static void eigenDecomposition(const Matrix3r &A, Matrix3r &eigenVecs, Vector3r &eigenVals); + static void jacobiRotate(Matrix3r &A, Matrix3r &R, int p, int q); + }; +} + +#endif \ No newline at end of file diff --git a/SPlisHSPlasH/Utilities/SceneLoader.cpp b/SPlisHSPlasH/Utilities/SceneLoader.cpp index f804ebd..5749952 100644 --- a/SPlisHSPlasH/Utilities/SceneLoader.cpp +++ b/SPlisHSPlasH/Utilities/SceneLoader.cpp @@ -328,6 +328,35 @@ void SceneLoader::readParameterObject(const std::string &key, ParameterObject *p static_cast<VectorParameter<Real>*>(paramBase)->setValue(val.data()); } } + else if (paramBase->getType() == ParameterBase::STRING) + { + std::string val; + if (readValue(config[paramBase->getName()], val)) + static_cast<StringParameter*>(paramBase)->setValue(val); + } } } } + +Utilities::SceneLoader::ColoringData Utilities::SceneLoader::readColoringInfo(const std::string &key) +{ + ColoringData data; + data.colorField = "velocity"; + data.colorMapType = 0; + data.minVal = 0.0; + data.maxVal = 5.0; + + ////////////////////////////////////////////////////////////////////////// + // read configuration + ////////////////////////////////////////////////////////////////////////// + if (m_jsonData.find(key) != m_jsonData.end()) + { + nlohmann::json config = m_jsonData[key]; +# + readValue(config["renderMinValue"], data.minVal); + readValue(config["renderMaxValue"], data.maxVal); + readValue(config["colorField"], data.colorField); + readValue(config["colorMapType"], data.colorMapType); + } + return data; +} diff --git a/SPlisHSPlasH/Utilities/SceneLoader.h b/SPlisHSPlasH/Utilities/SceneLoader.h index 6b33483..ee46b84 100644 --- a/SPlisHSPlasH/Utilities/SceneLoader.h +++ b/SPlisHSPlasH/Utilities/SceneLoader.h @@ -82,6 +82,15 @@ namespace Utilities Real timeStepSize; }; + /** \brief Struct to store particle coloring information */ + struct ColoringData + { + std::string colorField; + unsigned int colorMapType; + Real minVal; + Real maxVal; + }; + void readScene(const char *fileName, Scene &scene); @@ -148,6 +157,7 @@ namespace Utilities } void readParameterObject(const std::string &key, GenParam::ParameterObject *paramObj); + ColoringData readColoringInfo(const std::string &key); }; template <> diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Bender2017.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Bender2017.cpp index 7a1287c..d6934ca 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Bender2017.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Bender2017.cpp @@ -21,10 +21,18 @@ Viscosity_Bender2017::Viscosity_Bender2017(FluidModel *model) : m_iterations = 0; m_maxIter = 50; m_maxError = 0.01; + + model->addField({ "target strain rate", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_targetStrainRate[i][0]; } }); + model->addField({ "viscosity factor", FieldType::Matrix6, [&](const unsigned int i) -> Real* { return &m_viscosityFactor[i](0,0); } }); + model->addField({ "viscosity lambda", FieldType::Vector6, [&](const unsigned int i) -> Real* { return &m_viscosityLambda[i][0]; } }); } Viscosity_Bender2017::~Viscosity_Bender2017(void) { + m_model->removeFieldByName("target strain rate"); + m_model->removeFieldByName("viscosity factor"); + m_model->removeFieldByName("viscosity lambda"); + m_targetStrainRate.clear(); m_viscosityFactor.clear(); m_viscosityLambda.clear(); @@ -61,6 +69,7 @@ void Viscosity_Bender2017::step() const unsigned int maxIter = m_maxIter; const Real maxError = m_maxError; const Real maxError2 = maxError*maxError; + const Real density0 = model->getDensity0(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); @@ -187,7 +196,7 @@ void Viscosity_Bender2017::step() ////////////////////////////////////////////////////////////////////////// forall_boundary_neighbors( const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); - ai -= invH * 0.1 * m_viscosity * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) * (vi - vj)* sim->W(xi - xj); + ai -= invH * 0.1 * m_viscosity * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * (vi - vj)* sim->W(xi - xj); ) } } diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Peer2015.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Peer2015.cpp index 0409c6b..9e1a8fc 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Peer2015.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Peer2015.cpp @@ -20,10 +20,14 @@ Viscosity_Peer2015::Viscosity_Peer2015(FluidModel *model) : m_iterations = 0; m_maxIter = 50; m_maxError = 0.01; + + model->addField({ "target nablaV", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_targetNablaV[i](0,0); } }); } Viscosity_Peer2015::~Viscosity_Peer2015(void) { + m_model->removeFieldByName("target nablaV"); + m_targetNablaV.clear(); } @@ -88,7 +92,7 @@ void Viscosity_Peer2015::step() Simulation *sim = Simulation::getCurrent(); const int numParticles = (int) m_model->numActiveParticles(); const Real viscosity = static_cast<Real>(1.0) - m_viscosity; - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Peer2016.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Peer2016.cpp index 98adad2..9971302 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Peer2016.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Peer2016.cpp @@ -27,10 +27,16 @@ Viscosity_Peer2016::Viscosity_Peer2016(FluidModel *model) : m_maxErrorV = 0.01; m_maxIterOmega = 50; m_maxErrorOmega = 0.01; + + model->addField({ "target nablaV", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_targetNablaV[i](0,0); } }); + model->addField({ "omega (visco)", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_omega[i][0]; } }); } Viscosity_Peer2016::~Viscosity_Peer2016(void) { + m_model->removeFieldByName("target nablaV"); + m_model->removeFieldByName("omega (visco)"); + m_targetNablaV.clear(); m_omega.clear(); } @@ -174,7 +180,7 @@ void Viscosity_Peer2016::step() Simulation *sim = Simulation::getCurrent(); const int numParticles = (int) m_model->numActiveParticles(); const Real viscosity = static_cast<Real>(1.0) - m_viscosity; - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Standard.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Standard.cpp index d37b411..7297e4f 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Standard.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Standard.cpp @@ -21,6 +21,9 @@ void Viscosity_Standard::step() const Real h2 = h*h; const unsigned int nFluids = sim->numberOfFluidModels(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + Real d = 10.0; + if (sim->is2DSimulation()) + d = 6.0; #pragma omp parallel default(shared) { @@ -41,7 +44,7 @@ void Viscosity_Standard::step() // Viscosity const Real density_j = fm_neighbor->getDensity(neighborIndex); const Vector3r xixj = xi - xj; - ai += 10.0 * m_viscosity * (fm_neighbor->getMass(neighborIndex) / density_j) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * sim->gradW(xi - xj); + ai += d * m_viscosity * (fm_neighbor->getMass(neighborIndex) / density_j) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * sim->gradW(xi - xj); ) //////////////////////////////////////////////////////////////////////////// @@ -56,7 +59,7 @@ void Viscosity_Standard::step() // const Vector3r &xj = bm_neighbor->getPosition(neighborIndex); // const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); // const Vector3r xixj = xi - xj; - // ai += 10.0 * viscosity * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) * (vi) * (xixj.dot(sim->gradW(xi - xj))) / (xixj.squaredNorm() + 0.01*h2); + // ai += d * viscosity * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * (vi) * (xixj.dot(sim->gradW(xi - xj))) / (xixj.squaredNorm() + 0.01*h2); // } //} } diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Takahashi2015.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Takahashi2015.cpp index 2c51518..a682464 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Takahashi2015.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Takahashi2015.cpp @@ -20,10 +20,16 @@ Viscosity_Takahashi2015::Viscosity_Takahashi2015(FluidModel *model) : m_maxIter = 100; m_maxError = 0.01; m_iterations = 0; + + model->addField({ "viscous stress", FieldType::Matrix3, [&](const unsigned int i) -> Real* { return &m_viscousStress[i](0,0); } }); + model->addField({ "accel (visco)", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_accel[i][0]; } }); } Viscosity_Takahashi2015::~Viscosity_Takahashi2015(void) { + m_model->removeFieldByName("viscous stress"); + m_model->removeFieldByName("accel (visco)"); + m_viscousStress.clear(); m_accel.clear(); } @@ -132,7 +138,7 @@ void Viscosity_Takahashi2015::step() { Simulation *sim = Simulation::getCurrent(); const int numParticles = (int) m_model->numActiveParticles(); - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); const unsigned int nFluids = sim->numberOfFluidModels(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); @@ -208,7 +214,7 @@ void Viscosity_Takahashi2015::step() const unsigned int neighborIndex = sim->getNeighbor(fluidModelIndex, pid, i, j); const Vector3r &xj = bm_neighbor->getPosition(neighborIndex); const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); - ai -= invH * 0.1 * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) * (vi - vj)* sim->W(xi - xj); + ai -= invH * 0.1 * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * (vi - vj)* sim->W(xi - xj); } } } diff --git a/SPlisHSPlasH/Viscosity/Viscosity_Weiler2018.cpp b/SPlisHSPlasH/Viscosity/Viscosity_Weiler2018.cpp index c20d28b..620a1ad 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_Weiler2018.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_Weiler2018.cpp @@ -21,10 +21,14 @@ Viscosity_Weiler2018::Viscosity_Weiler2018(FluidModel *model) : m_boundaryViscosity = 0.0; m_vDiff.resize(model->numParticles(), Vector3r::Zero()); + + model->addField({ "velocity difference", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_vDiff[i][0]; } }); } Viscosity_Weiler2018::~Viscosity_Weiler2018(void) { + m_model->removeFieldByName("velocity difference"); + m_vDiff.clear(); } @@ -69,6 +73,11 @@ void Viscosity_Weiler2018::matrixVecProd(const Real* vec, Real *result, void *us const Real dt = TimeManager::getCurrent()->getTimeStepSize(); const Real mu = visco->m_viscosity; const Real mub = visco->m_boundaryViscosity; + const Real density0 = model->getDensity0(); + + Real d = 10.0; + if (sim->is2DSimulation()) + d = 6.0; #pragma omp parallel default(shared) { @@ -91,7 +100,7 @@ void Viscosity_Weiler2018::matrixVecProd(const Real* vec, Real *result, void *us const Vector3r &vj = Eigen::Map<const Vector3r>(&vec[3 * neighborIndex]); const Vector3r xixj = xi - xj; - ai += 10.0 * mu * (model->getMass(neighborIndex) / density_j) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * gradW; + ai += d * mu * (model->getMass(neighborIndex) / density_j) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * gradW; ) ////////////////////////////////////////////////////////////////////////// @@ -102,7 +111,7 @@ void Viscosity_Weiler2018::matrixVecProd(const Real* vec, Real *result, void *us const Vector3r gradW = sim->gradW(xi - xj); const Vector3r xixj = xi - xj; - ai += 10.0 * mub * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * gradW; + ai += d * mub * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * (vi - vj).dot(xixj) / (xixj.squaredNorm() + 0.01*h2) * gradW; ) result[3 * i] = vec[3 * i] - dt / density_i*ai[0]; @@ -121,7 +130,11 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Matrix3r FluidModel *model = visco->getModel(); const unsigned int nFluids = sim->numberOfFluidModels(); const unsigned int fluidModelIndex = model->getPointSetIndex(); + const Real density0 = model->getDensity0(); + Real d = 10.0; + if (sim->is2DSimulation()) + d = 6.0; const Real h = sim->getSupportRadius(); const Real h2 = h*h; @@ -142,7 +155,7 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Matrix3r const Real density_j = model->getDensity(neighborIndex); const Vector3r gradW = sim->gradW(xi - xj); const Vector3r xixj = xi - xj; - result += 10.0 * mu * (model->getMass(neighborIndex) / density_j) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); + result += d * mu * (model->getMass(neighborIndex) / density_j) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); ) ////////////////////////////////////////////////////////////////////////// @@ -153,7 +166,7 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Matrix3r const Vector3r gradW = sim->gradW(xi - xj); const Vector3r xixj = xi - xj; - result += 10.0 * mub * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); + result += d * mub * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); ) result = Matrix3r::Identity() - (dt / density_i) * result; } @@ -173,7 +186,11 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Vector3r const Real mub = visco->m_boundaryViscosity; const unsigned int nFluids = sim->numberOfFluidModels(); const unsigned int fluidModelIndex = m_model->getPointSetIndex(); + const Real density0 = model->getDensity0(); + Real d = 10.0; + if (sim->is2DSimulation()) + d = 6.0; const Real density_i = model->getDensity(i); @@ -188,7 +205,7 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Vector3r const Real density_j = model->getDensity(neighborIndex); const Vector3r gradW = model->gradW(xi - xj); const Vector3r xixj = xi - xj; - Matrix3r r = 10.0 * mu * (model->getMass(neighborIndex) / density_j) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); + Matrix3r r = d * mu * (model->getMass(neighborIndex) / density_j) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); result += r.diagonal(); ) @@ -200,7 +217,7 @@ void Viscosity_Weiler2018::diagonalMatrixElement(const unsigned int i, Vector3r const Vector3r gradW = model->gradW(xi - xj); const Vector3r xixj = xi - xj; - Matrix3r r = 10.0 * mub * (model->getBoundaryPsi(pid, neighborIndex) / density_i) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); + Matrix3r r = d * mub * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) / (xixj.squaredNorm() + 0.01*h2) * (gradW * xixj.transpose()); result += r.diagonal(); ) result = Vector3r::Ones() - (dt / density_i) * result; @@ -215,7 +232,7 @@ void Viscosity_Weiler2018::step() // prevent solver from running with a zero-length vector if (numParticles == 0) return; - const Real density0 = m_model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = m_model->getDensity0(); const Real h = TimeManager::getCurrent()->getTimeStepSize(); ////////////////////////////////////////////////////////////////////////// diff --git a/SPlisHSPlasH/Viscosity/Viscosity_XSPH.cpp b/SPlisHSPlasH/Viscosity/Viscosity_XSPH.cpp index 4fd861d..890dec0 100644 --- a/SPlisHSPlasH/Viscosity/Viscosity_XSPH.cpp +++ b/SPlisHSPlasH/Viscosity/Viscosity_XSPH.cpp @@ -63,7 +63,7 @@ void Viscosity_XSPH::step() // const unsigned int neighborIndex = sim->getNeighbor(pid, i, j); // const Vector3r &xj = bm_neighbor->getPosition(neighborIndex); // const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); - // ai -= invH * viscosity * (bm_neighbor->getBoundaryPsi(neighborIndex) / density_i) * (vi)* sim->W(xi - xj); + // ai -= invH * viscosity * bm_neighbor->getVolume(neighborIndex) * (vi)* sim->W(xi - xj); // } //} } diff --git a/SPlisHSPlasH/Vorticity/MicropolarModel_Bender2017.cpp b/SPlisHSPlasH/Vorticity/MicropolarModel_Bender2017.cpp index cabfa16..2ed123c 100644 --- a/SPlisHSPlasH/Vorticity/MicropolarModel_Bender2017.cpp +++ b/SPlisHSPlasH/Vorticity/MicropolarModel_Bender2017.cpp @@ -17,10 +17,14 @@ MicropolarModel_Bender2017::MicropolarModel_Bender2017(FluidModel *model) : m_angularAcceleration.resize(model->numParticles(), Vector3r::Zero()); m_inertiaInverse = 0.5; m_viscosityOmega = 0.1; + + model->addField({ "angular velocity", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_omega[i][0]; } }); } MicropolarModel_Bender2017::~MicropolarModel_Bender2017(void) { + m_model->removeFieldByName("angular velocity"); + m_omega.clear(); m_angularAcceleration.clear(); } @@ -51,13 +55,21 @@ void MicropolarModel_Bender2017::step() const unsigned int fluidModelIndex = m_model->getPointSetIndex(); const unsigned int nFluids = sim->numberOfFluidModels(); FluidModel *model = m_model; + const Real density0 = model->getDensity0(); - const Real h = TimeManager::getCurrent()->getTimeStepSize(); - const Real invH = static_cast<Real>(1.0) / h; + const Real dt = TimeManager::getCurrent()->getTimeStepSize(); + const Real invDt = static_cast<Real>(1.0) / dt; const Real nu_t = m_vorticityCoeff; const Real zeta = m_viscosityOmega; + const Real h = sim->getSupportRadius(); + const Real h2 = h*h; + + Real d = 10.0; + if (sim->is2DSimulation()) + d = 6.0; + #pragma omp parallel default(shared) { #pragma omp for schedule(static) @@ -86,13 +98,47 @@ void MicropolarModel_Bender2017::step() const Vector3r omegaij = omegai - omegaj; const Vector3r gradW = sim->gradW(xij); - // XSPH for angular velocity field - angAcceli -= invH * m_inertiaInverse * zeta * (m_model->getMass(neighborIndex) / density_j) * omegaij * sim->W(xij); - - // symmetric curl - ai -= nu_t * density_i * m_model->getMass(neighborIndex) * ((omegai / density_i2 + omegaj / density_j2).cross(gradW)); - angAcceli -= nu_t * density_i * m_inertiaInverse * (m_model->getMass(neighborIndex) * (vi / density_i2 + vj / density_j2).cross(gradW)); - ) + // XSPH for angular velocity field + angAcceli -= invDt * m_inertiaInverse * zeta * (m_model->getMass(neighborIndex) / density_j) * omegaij * sim->W(xij); + + //// Viscosity + //angAcceli += d * m_inertiaInverse * zeta * (m_model->getMass(neighborIndex) / density_i) * omegaij.dot(xij) / (xij.squaredNorm() + 0.01*h2) * gradW; + + // difference curl + ai += nu_t * 1.0/density_i * m_model->getMass(neighborIndex) * (omegaij.cross(gradW)); + angAcceli += nu_t * 1.0/density_i * m_inertiaInverse * (m_model->getMass(neighborIndex) * (vi - vj).cross(gradW)); + +// // symmetric curl +// ai -= nu_t * density_i * m_model->getMass(neighborIndex) * ((omegai / density_i2 + omegaj / density_j2).cross(gradW)); +// angAcceli -= nu_t * density_i * m_inertiaInverse * (m_model->getMass(neighborIndex) * (vi / density_i2 + vj / density_j2).cross(gradW)); + ); + + ////////////////////////////////////////////////////////////////////////// + // Boundary + ////////////////////////////////////////////////////////////////////////// + forall_boundary_neighbors( + const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); + const Vector3r &omegaj = Vector3r::Zero();//m_omega[neighborIndex]; + + // Viscosity + const Vector3r xij = xi - xj; + const Vector3r omegaij = omegai - omegaj; + const Vector3r gradW = sim->gradW(xij); + + // XSPH for angular velocity field + //angAcceli -= invDt * m_inertiaInverse * zeta * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * omegaij * sim->W(xij); + + // Viscosity + //angAcceli += d * m_inertiaInverse * zeta * (density0 * bm_neighbor->getVolume(neighborIndex) / density_i) * omegaij.dot(xij) / (xij.squaredNorm() + 0.01*h2) * gradW; + + // difference curl + ai += nu_t * 1.0 / density_i * density0 * bm_neighbor->getVolume(neighborIndex) * (omegaij.cross(gradW)); + angAcceli += nu_t * 1.0 / density_i * m_inertiaInverse * (density0 * bm_neighbor->getVolume(neighborIndex) * (vi - vj).cross(gradW)); + +// // symmetric curl +// ai -= nu_t * density_i * density0 * bm_neighbor->getVolume(neighborIndex) * ((omegai / density_i2 + omegaj / density_i2).cross(gradW)); +// angAcceli -= nu_t * density_i * m_inertiaInverse * (density0 * bm_neighbor->getVolume(neighborIndex) * (vi / density_i2 + vj / density_i2).cross(gradW)); + ); angAcceli -= 2.0 * m_inertiaInverse * nu_t * omegai; } } @@ -102,7 +148,7 @@ void MicropolarModel_Bender2017::step() #pragma omp for schedule(static) for (int i = 0; i < (int)numParticles; i++) { - m_omega[i] += h*m_angularAcceleration[i]; + m_omega[i] += dt*m_angularAcceleration[i]; } } } diff --git a/SPlisHSPlasH/Vorticity/VorticityConfinement.cpp b/SPlisHSPlasH/Vorticity/VorticityConfinement.cpp index 79df01d..5fcaccc 100644 --- a/SPlisHSPlasH/Vorticity/VorticityConfinement.cpp +++ b/SPlisHSPlasH/Vorticity/VorticityConfinement.cpp @@ -10,10 +10,14 @@ VorticityConfinement::VorticityConfinement(FluidModel *model) : { m_omega.resize(model->numParticles(), Vector3r::Zero()); m_normOmega.resize(model->numParticles(), 0.0); + + model->addField({ "angular velocity", FieldType::Vector3, [&](const unsigned int i) -> Real* { return &m_omega[i][0]; } }); } VorticityConfinement::~VorticityConfinement(void) { + m_model->removeFieldByName("angular velocity"); + m_omega.clear(); m_normOmega.clear(); } diff --git a/SPlisHSPlasH/WCSPH/TimeStepWCSPH.cpp b/SPlisHSPlasH/WCSPH/TimeStepWCSPH.cpp index 85e31b5..ebc16cd 100644 --- a/SPlisHSPlasH/WCSPH/TimeStepWCSPH.cpp +++ b/SPlisHSPlasH/WCSPH/TimeStepWCSPH.cpp @@ -22,10 +22,27 @@ TimeStepWCSPH::TimeStepWCSPH() : m_stiffness = 50.0; m_exponent = 7.0; + + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->addField({ "pressure", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressure(fluidModelIndex, i); } }); + model->addField({ "pressure acceleration", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real* { return &m_simulationData.getPressureAccel(fluidModelIndex, i)[0]; } }); + } } TimeStepWCSPH::~TimeStepWCSPH(void) { + Simulation *sim = Simulation::getCurrent(); + const unsigned int nModels = sim->numberOfFluidModels(); + for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) + { + FluidModel *model = sim->getFluidModel(fluidModelIndex); + model->removeFieldByName("pressure"); + model->removeFieldByName("pressure acceleration"); + } } void TimeStepWCSPH::initParameters() @@ -65,7 +82,7 @@ void TimeStepWCSPH::step() for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) { FluidModel *model = sim->getFluidModel(fluidModelIndex); - const Real density0 = model->getValue<Real>(FluidModel::DENSITY0); + const Real density0 = model->getDensity0(); #pragma omp parallel default(shared) { #pragma omp for schedule(static) diff --git a/Demos/CMakeLists.txt b/Simulators/CMakeLists.txt similarity index 63% rename from Demos/CMakeLists.txt rename to Simulators/CMakeLists.txt index e66ca18..6e30108 100644 --- a/Demos/CMakeLists.txt +++ b/Simulators/CMakeLists.txt @@ -1,5 +1,5 @@ include(${PROJECT_PATH}/Visualization/CMakeLists.txt) add_definitions(-DPBD_DATA_PATH="../data") -subdirs(DynamicBoundaryDemo StaticBoundaryDemo) +subdirs(DynamicBoundarySimulator StaticBoundarySimulator) diff --git a/Demos/Common/DemoBase.cpp b/Simulators/Common/SimulatorBase.cpp similarity index 70% rename from Demos/Common/DemoBase.cpp rename to Simulators/Common/SimulatorBase.cpp index 931b256..d4638eb 100644 --- a/Demos/Common/DemoBase.cpp +++ b/Simulators/Common/SimulatorBase.cpp @@ -1,4 +1,4 @@ -#include "DemoBase.h" +#include "SimulatorBase.h" #include "Visualization/MiniGL.h" #include "SPlisHSPlasH/Utilities/SceneLoader.h" #include "Utilities/FileSystem.h" @@ -18,6 +18,7 @@ #include "Utilities/SystemInfo.h" #include "Visualization/colormaps/colormap_jet.h" #include "Visualization/colormaps/colormap_plasma.h" +#include "extern/partio/src/lib/Partio.h" INIT_LOGGING INIT_TIMING @@ -28,40 +29,30 @@ using namespace std; using namespace GenParam; using namespace Utilities; -int DemoBase::PAUSE = -1; -int DemoBase::PAUSE_AT = -1; -int DemoBase::STOP_AT = -1; -int DemoBase::NUM_STEPS_PER_RENDER = -1; -int DemoBase::PARTIO_EXPORT = -1; -int DemoBase::PARTIO_EXPORT_FPS = -1; -int DemoBase::RENDER_MIN_VALUE = -1; -int DemoBase::RENDER_MAX_VALUE = -1; -int DemoBase::RENDER_WALLS = -1; -int DemoBase::RENDER_COLOR_FIELD = -1; -int DemoBase::RENDER_COLOR_MAP_TYPE = -1; -int DemoBase::ENUM_WALLS_NONE = -1; -int DemoBase::ENUM_WALLS_PARTICLES_ALL = -1; -int DemoBase::ENUM_WALLS_PARTICLES_NO_WALLS = -1; -int DemoBase::ENUM_WALLS_GEOMETRY_ALL = -1; -int DemoBase::ENUM_WALLS_GEOMETRY_NO_WALLS = -1; -int DemoBase::ENUM_RENDER_NONE = -1; -int DemoBase::ENUM_RENDER_VELOCITY = -1; -int DemoBase::ENUM_RENDER_ANGULAR_VELOCITY = -1; -int DemoBase::ENUM_RENDER_DENSITY = -1; -int DemoBase::ENUM_COLORMAP_NONE = -1; -int DemoBase::ENUM_COLORMAP_JET = -1; -int DemoBase::ENUM_COLORMAP_PLASMA = -1; +int SimulatorBase::PAUSE = -1; +int SimulatorBase::PAUSE_AT = -1; +int SimulatorBase::STOP_AT = -1; +int SimulatorBase::NUM_STEPS_PER_RENDER = -1; +int SimulatorBase::PARTIO_EXPORT = -1; +int SimulatorBase::PARTIO_EXPORT_FPS = -1; +int SimulatorBase::PARTIO_EXPORT_ATTRIBUTES = -1; +int SimulatorBase::RENDER_WALLS = -1; +int SimulatorBase::ENUM_WALLS_NONE = -1; +int SimulatorBase::ENUM_WALLS_PARTICLES_ALL = -1; +int SimulatorBase::ENUM_WALLS_PARTICLES_NO_WALLS = -1; +int SimulatorBase::ENUM_WALLS_GEOMETRY_ALL = -1; +int SimulatorBase::ENUM_WALLS_GEOMETRY_NO_WALLS = -1; +int SimulatorBase::ENUM_RENDER_NONE = -1; +int SimulatorBase::ENUM_RENDER_VELOCITY = -1; +int SimulatorBase::ENUM_RENDER_ANGULAR_VELOCITY = -1; +int SimulatorBase::ENUM_RENDER_DENSITY = -1; -DemoBase::DemoBase() +SimulatorBase::SimulatorBase() { Utilities::logger.addSink(unique_ptr<Utilities::ConsoleSink>(new Utilities::ConsoleSink(Utilities::LogLevel::INFO))); m_numberOfStepsPerRenderUpdate = 4; - m_colorField = 1; - setColorMapType(0); - m_renderMinValue = 0.0; - m_renderMaxValue = 5.0; m_sceneFile = ""; m_renderWalls = 4; m_doPause = true; @@ -72,17 +63,22 @@ DemoBase::DemoBase() m_framesPerSecond = 25; m_nextFrameTime = 0.0; m_frameCounter = 1; + m_colorField.resize(1, "velocity"); + m_colorMapType.resize(1, 0); + m_renderMinValue.resize(1, 0.0); + m_renderMaxValue.resize(1, 5.0); + m_partioAttributes = "velocity"; #ifdef DL_OUTPUT m_nextTiming = 1.0; #endif } -DemoBase::~DemoBase() +SimulatorBase::~SimulatorBase() { } -void DemoBase::initParameters() +void SimulatorBase::initParameters() { ParameterObject::initParameters(); @@ -104,37 +100,10 @@ void DemoBase::initParameters() setDescription(NUM_STEPS_PER_RENDER, "Number of simulation steps per rendered frame."); static_cast<NumericParameter<unsigned int>*>(getParameter(NUM_STEPS_PER_RENDER))->setMinValue(1); - RENDER_MIN_VALUE = createNumericParameter("renderMinValue", "Min. value (shader)", &m_renderMinValue); - setGroup(RENDER_MIN_VALUE, "Visualization"); - setDescription(RENDER_MIN_VALUE, "Minimal value used for color-coding the color field in the rendering process."); - - RENDER_MAX_VALUE = createNumericParameter("renderMaxValue", "Max. value (shader)", &m_renderMaxValue); - setGroup(RENDER_MAX_VALUE, "Visualization"); - setDescription(RENDER_MAX_VALUE, "Maximal value used for color-coding the color field in the rendering process."); - - RENDER_COLOR_FIELD = createEnumParameter("colorField", "Color field", &m_colorField); - setGroup(RENDER_COLOR_FIELD, "Visualization"); - setDescription(RENDER_COLOR_FIELD, "Choose vector or scalar field for particle coloring."); - EnumParameter *enumParam = static_cast<EnumParameter*>(getParameter(RENDER_COLOR_FIELD)); - enumParam->addEnumValue("None", ENUM_RENDER_NONE); - enumParam->addEnumValue("Velocity", ENUM_RENDER_VELOCITY); - enumParam->addEnumValue("Angular velocity (micropolar model)", ENUM_RENDER_ANGULAR_VELOCITY); - enumParam->addEnumValue("Density", ENUM_RENDER_DENSITY); - - ParameterBase::GetFunc<int> getColorMapTypeFct = std::bind(&DemoBase::getColorMapType, this); - ParameterBase::SetFunc<int> setColorMapTypeFct = std::bind(&DemoBase::setColorMapType, this, std::placeholders::_1); - RENDER_COLOR_MAP_TYPE = createEnumParameter("colorMapType", "Color map", getColorMapTypeFct, setColorMapTypeFct); - setGroup(RENDER_COLOR_MAP_TYPE, "Visualization"); - setDescription(RENDER_COLOR_MAP_TYPE, "Choose a color map."); - enumParam = static_cast<EnumParameter*>(getParameter(RENDER_COLOR_MAP_TYPE)); - enumParam->addEnumValue("None", ENUM_COLORMAP_NONE); - enumParam->addEnumValue("Jet", ENUM_COLORMAP_JET); - enumParam->addEnumValue("Plasma", ENUM_COLORMAP_PLASMA); - RENDER_WALLS = createEnumParameter("renderWalls", "Render walls", &m_renderWalls); setGroup(RENDER_WALLS, "Visualization"); setDescription(RENDER_WALLS, "Make walls visible/invisible."); - enumParam = static_cast<EnumParameter*>(getParameter(RENDER_WALLS)); + EnumParameter *enumParam = static_cast<EnumParameter*>(getParameter(RENDER_WALLS)); enumParam->addEnumValue("None", ENUM_WALLS_NONE); enumParam->addEnumValue("Particles (all)", ENUM_WALLS_PARTICLES_ALL); enumParam->addEnumValue("Particles (no walls)", ENUM_WALLS_PARTICLES_NO_WALLS); @@ -148,9 +117,14 @@ void DemoBase::initParameters() PARTIO_EXPORT_FPS = createNumericParameter("partioFPS", "Export FPS", &m_framesPerSecond); setGroup(PARTIO_EXPORT_FPS, "Export"); setDescription(PARTIO_EXPORT_FPS, "Frame rate of partio export."); + + PARTIO_EXPORT_ATTRIBUTES = createStringParameter("partioAttributes", "Export attributes", &m_partioAttributes); + getParameter(PARTIO_EXPORT_ATTRIBUTES)->setReadOnly(true); + setGroup(PARTIO_EXPORT_ATTRIBUTES, "Export"); + setDescription(PARTIO_EXPORT_ATTRIBUTES, "Attributes that are exported in the partio files (except id and position)."); } -void DemoBase::init(int argc, char **argv, const char *demoName) +void SimulatorBase::init(int argc, char **argv, const char *simName) { initParameters(); m_exePath = FileSystem::getProgramPath(); @@ -199,18 +173,22 @@ void DemoBase::init(int argc, char **argv, const char *demoName) return; // OpenGL - MiniGL::init(argc, argv, 1280, 960, 0, 0, demoName); + MiniGL::init(argc, argv, 1280, 960, 0, 0, simName); MiniGL::initLights(); MiniGL::getOpenGLVersion(m_context_major_version, m_context_minor_version); - MiniGL::setViewport(40.0, 0.1f, 500.0, Vector3r(0.0, 3.0, 8.0), Vector3r(0.0, 0.0, 0.0)); + const bool sim2D = getScene().sim2D; + if (sim2D) + MiniGL::setViewport(40.0, 0.1f, 500.0, Vector3r(0.0, 0.0, 8.0), Vector3r(0.0, 0.0, 0.0)); + else + MiniGL::setViewport(40.0, 0.1f, 500.0, Vector3r(0.0, 3.0, 8.0), Vector3r(0.0, 0.0, 0.0)); MiniGL::setSelectionFunc(selection, this); - MiniGL::addKeyFunc('i', std::bind(&DemoBase::particleInfo, this)); + MiniGL::addKeyFunc('i', std::bind(&SimulatorBase::particleInfo, this)); if (MiniGL::checkOpenGLVersion(3, 3)) initShaders(); } -void DemoBase::cleanup() +void SimulatorBase::cleanup() { for (unsigned int i = 0; i < m_scene.boundaryModels.size(); i++) delete m_scene.boundaryModels[i]; @@ -229,7 +207,7 @@ void DemoBase::cleanup() m_scene.emitters.clear(); } -void DemoBase::initShaders() +void SimulatorBase::initShaders() { string vertFile = getExePath() + "/resources/shaders/vs_points_vector.glsl"; string fragFile = getExePath() + "/resources/shaders/fs_points.glsl"; @@ -305,7 +283,7 @@ void DemoBase::initShaders() } -void DemoBase::meshShaderBegin(const float *col) +void SimulatorBase::meshShaderBegin(const float *col) { m_meshShader.begin(); glUniform1f(m_meshShader.getUniform("shininess"), 5.0f); @@ -320,12 +298,12 @@ void DemoBase::meshShaderBegin(const float *col) glUniform3fv(m_meshShader.getUniform("surface_color"), 1, col); } -void DemoBase::meshShaderEnd() +void SimulatorBase::meshShaderEnd() { m_meshShader.end(); } -void DemoBase::pointShaderBegin(Shader *shader, const float *col, const bool useTexture) +void SimulatorBase::pointShaderBegin(Shader *shader, const float *col, const Real minVal, const Real maxVal, const bool useTexture, float const* color_map) { shader->begin(); @@ -334,16 +312,16 @@ void DemoBase::pointShaderBegin(Shader *shader, const float *col, const bool use const Real radius = Simulation::getCurrent()->getValue<Real>(Simulation::PARTICLE_RADIUS); glUniform1f(shader->getUniform("viewport_width"), (float)viewport[2]); glUniform1f(shader->getUniform("radius"), (float)radius); - glUniform1f(shader->getUniform("min_scalar"), (GLfloat)m_renderMinValue); - glUniform1f(shader->getUniform("max_scalar"), (GLfloat)m_renderMaxValue); + glUniform1f(shader->getUniform("min_scalar"), (GLfloat)minVal); + glUniform1f(shader->getUniform("max_scalar"), (GLfloat)maxVal); glUniform3fv(shader->getUniform("color"), 1, col); if (useTexture) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_1D, m_textureMap); - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, m_colorMapLength, 0, GL_RGB, GL_FLOAT, - reinterpret_cast<float const*>(m_colorMapBuffer)); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 256u, 0, GL_RGB, GL_FLOAT, color_map); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -367,13 +345,13 @@ void DemoBase::pointShaderBegin(Shader *shader, const float *col, const bool use glPointParameterf(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); } -void DemoBase::pointShaderEnd(Shader *shader, const bool useTexture) +void SimulatorBase::pointShaderEnd(Shader *shader, const bool useTexture) { glBindTexture(GL_TEXTURE_1D, 0); shader->end(); } -void DemoBase::readParameters() +void SimulatorBase::readParameters() { m_sceneLoader->readParameterObject("Configuration", this); m_sceneLoader->readParameterObject("Configuration", Simulation::getCurrent()); @@ -389,11 +367,18 @@ void DemoBase::readParameters() m_sceneLoader->readParameterObject(key, (ParameterObject*) model->getSurfaceTensionBase()); m_sceneLoader->readParameterObject(key, (ParameterObject*) model->getViscosityBase()); m_sceneLoader->readParameterObject(key, (ParameterObject*) model->getVorticityBase()); + m_sceneLoader->readParameterObject(key, (ParameterObject*) model->getElasticityBase()); + + SceneLoader::ColoringData colorData = m_sceneLoader->readColoringInfo(key); + setColorField(i, colorData.colorField); + setColorMapType(i, colorData.colorMapType); + setRenderMinValue(i, colorData.minVal); + setRenderMaxValue(i, colorData.maxVal); } } -void DemoBase::buildModel() +void SimulatorBase::buildModel() { TimeManager::getCurrent()->setTimeStepSize(m_scene.timeStepSize); @@ -403,7 +388,8 @@ void DemoBase::buildModel() Simulation *sim = Simulation::getCurrent(); - sim->getTimeStep()->resize(); + if (sim->getTimeStep()) + sim->getTimeStep()->resize(); if (!sim->is2DSimulation()) { @@ -418,7 +404,7 @@ void DemoBase::buildModel() } -void DemoBase::initFluidData() +void SimulatorBase::initFluidData() { LOG_INFO << "Initialize fluid particles"; @@ -430,7 +416,7 @@ void DemoBase::initFluidData() std::map<std::string, unsigned int> fluidIDs; unsigned int index = 0; for (unsigned int i = 0; i < m_scene.fluidBlocks.size(); i++) - { + { if (fluidIDs.find(m_scene.fluidBlocks[i]->id) == fluidIDs.end()) fluidIDs[m_scene.fluidBlocks[i]->id] = index++; } @@ -482,10 +468,15 @@ void DemoBase::initFluidData() nParticles += (unsigned int)fluidParticles[index].size(); } + m_colorField.resize(sim->numberOfFluidModels(), "velocity"); + m_colorMapType.resize(sim->numberOfFluidModels(), 0); + m_renderMinValue.resize(sim->numberOfFluidModels(), 0.0); + m_renderMaxValue.resize(sim->numberOfFluidModels(), 5.0); + LOG_INFO << "Number of fluid particles: " << nParticles; } -void DemoBase::createEmitters() +void SimulatorBase::createEmitters() { Simulation *sim = Simulation::getCurrent(); @@ -531,7 +522,7 @@ void DemoBase::createEmitters() } -void DemoBase::createFluidBlocks(std::map<std::string, unsigned int> &fluidIDs, std::vector<std::vector<Vector3r>> &fluidParticles, std::vector<std::vector<Vector3r>> &fluidVelocities) +void SimulatorBase::createFluidBlocks(std::map<std::string, unsigned int> &fluidIDs, std::vector<std::vector<Vector3r>> &fluidParticles, std::vector<std::vector<Vector3r>> &fluidVelocities) { for (unsigned int i = 0; i < m_scene.fluidBlocks.size(); i++) { @@ -611,9 +602,11 @@ void DemoBase::createFluidBlocks(std::map<std::string, unsigned int> &fluidIDs, } } -void DemoBase::renderFluid(FluidModel *model, float *fluidColor) +void SimulatorBase::renderFluid(const unsigned int fluidModelIndex, float *fluidColor) { // Draw simulation model + Simulation *sim = Simulation::getCurrent(); + FluidModel *model = sim->getFluidModel(fluidModelIndex); const unsigned int nParticles = model->numActiveParticles(); if (nParticles == 0) return; @@ -625,8 +618,7 @@ void DemoBase::renderFluid(FluidModel *model, float *fluidColor) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, speccolor); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0); glColor3fv(surfaceColor); - - Simulation *sim = Simulation::getCurrent(); + const Real supportRadius = sim->getSupportRadius(); Real vmax = static_cast<Real>(0.4*2.0)*supportRadius / TimeManager::getCurrent()->getTimeStepSize(); Real vmin = 0.0; @@ -635,56 +627,60 @@ void DemoBase::renderFluid(FluidModel *model, float *fluidColor) { Shader *shader_vector = &m_shader_vector_map; Shader *shader_scalar = &m_shader_scalar_map; - if (m_colorMapType == ENUM_COLORMAP_NONE) + float const *color_map = nullptr; + if (m_colorMapType[fluidModelIndex] == 1) + color_map = reinterpret_cast<float const*>(colormap_jet); + else if (m_colorMapType[fluidModelIndex] == 2) + color_map = reinterpret_cast<float const*>(colormap_plasma); + + if (m_colorMapType[fluidModelIndex] == 0) { shader_vector = &m_shader_vector; shader_scalar = &m_shader_scalar; } - if ((m_colorField == ENUM_RENDER_VELOCITY) || (m_colorField == ENUM_RENDER_ANGULAR_VELOCITY)) - pointShaderBegin(shader_vector, &fluidColor[0], true); - else if (m_colorField == ENUM_RENDER_DENSITY) - pointShaderBegin(shader_scalar, &fluidColor[0], true); - else - pointShaderBegin(shader_scalar, &fluidColor[0], false); + const FieldDescription *field = nullptr; + const std::string &colorFieldName = m_colorField[fluidModelIndex]; + field = &model->getField(colorFieldName); + + + if (field == nullptr) + pointShaderBegin(shader_scalar, &fluidColor[0], m_renderMinValue[fluidModelIndex], m_renderMaxValue[fluidModelIndex], false); + else if (field->type == FieldType::Vector3) + pointShaderBegin(shader_vector, &fluidColor[0], m_renderMinValue[fluidModelIndex], m_renderMaxValue[fluidModelIndex], true, color_map); + else if (field->type == FieldType::Scalar) + pointShaderBegin(shader_scalar, &fluidColor[0], m_renderMinValue[fluidModelIndex], m_renderMaxValue[fluidModelIndex], true, color_map); if (model->numActiveParticles() > 0) { glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_REAL, GL_FALSE, 0, &model->getPosition(0)); - - if (m_colorField == ENUM_RENDER_VELOCITY) - { - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_REAL, GL_FALSE, 0, &model->getVelocity(0)); - } - else if ((m_colorField == ENUM_RENDER_ANGULAR_VELOCITY) && ((VorticityMethods)model->getVorticityMethod() == VorticityMethods::Micropolar)) - { - glEnableVertexAttribArray(1); - float fluidColor2[4] = { 0.3f, 0.9f, 0.5f, 1.0f }; - glUniform3fv(shader_vector->getUniform("color"), 1, fluidColor2); - glVertexAttribPointer(1, 3, GL_REAL, GL_FALSE, 0, &((MicropolarModel_Bender2017*)model->getVorticityBase())->getAngularVelocity(0)[0]); - } - else if (m_colorField == ENUM_RENDER_DENSITY) + + if (field != nullptr) { - glEnableVertexAttribArray(1); - float fluidColor2[4] = { 0.9f, 0.3f, 0.3f, 1.0f }; - glUniform3fv(shader_scalar->getUniform("color"), 1, fluidColor2); - glVertexAttribPointer(1, 1, GL_REAL, GL_FALSE, 0, &model->getDensity(0)); - } + if (field->type == FieldType::Vector3) + { + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_REAL, GL_FALSE, 0, field->getFct(0)); + } + else if (field->type == FieldType::Scalar) + { + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 1, GL_REAL, GL_FALSE, 0, field->getFct(0)); + } + } glDrawArrays(GL_POINTS, 0, model->numActiveParticles()); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); } - if ((m_colorField == ENUM_RENDER_VELOCITY) || (m_colorField == ENUM_RENDER_ANGULAR_VELOCITY)) + if (field == nullptr) + pointShaderEnd(shader_scalar, false); + else if (field->type == FieldType::Vector3) pointShaderEnd(shader_vector, true); - else if (m_colorField == ENUM_RENDER_DENSITY) + else if (field->type == FieldType::Scalar) pointShaderEnd(shader_scalar, true); - else - pointShaderEnd(shader_scalar, false); - } else { @@ -711,7 +707,7 @@ void DemoBase::renderFluid(FluidModel *model, float *fluidColor) const unsigned int fluidIndex = model->getPointSetIndex(); if (MiniGL::checkOpenGLVersion(3, 3)) { - pointShaderBegin(&m_shader_scalar, &red[0]); + pointShaderBegin(&m_shader_scalar, &red[0], m_renderMinValue[fluidModelIndex], m_renderMaxValue[fluidModelIndex]); if ((getSelectedParticles().size() > 0) && ((getSelectedParticles()[fluidIndex].size() > 0))) { const Real radius = sim->getValue<Real>(Simulation::PARTICLE_RADIUS); @@ -745,9 +741,9 @@ void DemoBase::renderFluid(FluidModel *model, float *fluidColor) } -void DemoBase::mouseMove(int x, int y, void *clientData) +void SimulatorBase::mouseMove(int x, int y, void *clientData) { - DemoBase *base = (DemoBase*)clientData; + SimulatorBase *base = (SimulatorBase*)clientData; Simulation *sim = Simulation::getCurrent(); Vector3r mousePos; @@ -768,9 +764,9 @@ void DemoBase::mouseMove(int x, int y, void *clientData) base->m_oldMousePos = mousePos; } -void DemoBase::selection(const Eigen::Vector2i &start, const Eigen::Vector2i &end, void *clientData) +void SimulatorBase::selection(const Eigen::Vector2i &start, const Eigen::Vector2i &end, void *clientData) { - DemoBase *base = (DemoBase*)clientData; + SimulatorBase *base = (SimulatorBase*)clientData; Simulation *sim = Simulation::getCurrent(); base->m_selectedParticles.resize(sim->numberOfFluidModels()); bool selected = false; @@ -803,24 +799,59 @@ void DemoBase::selection(const Eigen::Vector2i &start, const Eigen::Vector2i &en } } -void DemoBase::particleInfo() +void SimulatorBase::particleInfo() { Simulation *sim = Simulation::getCurrent(); + const int maxWidth = 25; for (unsigned int i = 0; i < sim->numberOfFluidModels(); i++) { FluidModel *model = sim->getFluidModel(i); + if (m_selectedParticles[i].size() > 0) + { + LOG_INFO << "---------------------------------------------------------------------------"; + LOG_INFO << model->getId(); + LOG_INFO << "---------------------------------------------------------------------------"; + } for (unsigned int j = 0; j < m_selectedParticles[i].size(); j++) { unsigned int index = m_selectedParticles[i][j]; - LOG_INFO << index; - LOG_INFO << "x: " << model->getPosition(index).transpose(); - LOG_INFO << "v: " << model->getVelocity(index).transpose(); - LOG_INFO << "density: " << model->getDensity(index); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << "Index:" << index; + for (unsigned int k = 0; k < model->numberOfFields(); k++) + { + const FieldDescription &field = model->getField(k); + if (field.type == Scalar) + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << field.name + ":" << *field.getFct(index); + else if (field.type == Vector3) + { + Eigen::Map<Vector3r> vec(field.getFct(index)); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << field.name + ":" << vec.transpose(); + } + else if (field.type == Vector6) + { + Eigen::Map<Vector6r> vec(field.getFct(index)); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << field.name + ":" << vec.transpose(); + } + else if (field.type == Matrix3) + { + Eigen::Map<Matrix3r> mat(field.getFct(index)); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << field.name + ":" << mat.row(0); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << " " << mat.row(1); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << " " << mat.row(2); + } + else if (field.type == Matrix6) + { + Eigen::Map<Matrix6r> mat(field.getFct(index)); + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << field.name + ":" << mat.row(0); + for (unsigned int k = 1; k < 6; k++) + LOG_INFO << std::left << std::setw(maxWidth) << std::setfill(' ') << " " << mat.row(k); + } + } + LOG_INFO << "---------------------------------------------------------------------------\n"; } } } -void DemoBase::partioExport() +void SimulatorBase::partioExport() { std::string exportPath = FileSystem::normalizePath(m_outputPath + "/partio"); FileSystem::makeDirs(exportPath); @@ -833,11 +864,107 @@ void DemoBase::partioExport() fileName = fileName + "_" + model->getId() + "_" + std::to_string(m_frameCounter) + ".bgeo"; std::string exportFileName = FileSystem::normalizePath(exportPath + "/" + fileName); - PartioReaderWriter::writeParticles(exportFileName, model->numActiveParticles(), &model->getPosition(0), &model->getVelocity(0), 0.0); + writeParticles(exportFileName, model); } } -void DemoBase::step() + +void SimulatorBase::writeParticles(const std::string &fileName, FluidModel *model) +{ + Partio::ParticlesDataMutable& particleData = *Partio::create(); + Partio::ParticleAttribute posAttr = particleData.addAttribute("position", Partio::VECTOR, 3); + Partio::ParticleAttribute idAttr = particleData.addAttribute("id", Partio::INT, 1); + + // add attributes + std::vector<std::string> attributes; + StringTools::tokenize(m_partioAttributes, attributes, ";"); + + std::map<unsigned int, int> attrMap; + std::map<unsigned int, Partio::ParticleAttribute> partioAttrMap; + for (unsigned int i = 0; i < attributes.size(); i++) + { + // position is exported anyway + if (attributes[i] == "position") + { + attrMap[i] = -1; + continue; + } + + bool found = false; + for (unsigned int j = 0; j < model->numberOfFields(); j++) + { + const FieldDescription &field = model->getField(j); + if (field.name == attributes[i]) + { + found = true; + if (field.type == Scalar) + { + attrMap[i] = j; + partioAttrMap[i] = particleData.addAttribute(attributes[i].c_str(), Partio::FLOAT, 1); + } + else if (field.type == Vector3) + { + attrMap[i] = j; + partioAttrMap[i] = particleData.addAttribute(attributes[i].c_str(), Partio::VECTOR, 3); + } + else + { + attrMap[i] = -1; + LOG_WARN << "Only scalar and vector fields are currently supported by the partio exporter."; + } + break; + } + } + if (!found) + { + attrMap[i] = -1; + LOG_WARN << "Unknown field cannot be exported in partio file: " << attributes[i]; + } + } + + const unsigned int numParticles = model->numActiveParticles(); + + for (unsigned int i = 0; i < numParticles; i++) + { + Partio::ParticleIndex index = particleData.addParticle(); + float* pos = particleData.dataWrite<float>(posAttr, index); + int* id = particleData.dataWrite<int>(idAttr, index); + + const Vector3r &x = model->getPosition(i); + pos[0] = (float)x[0]; + pos[1] = (float)x[1]; + pos[2] = (float)x[2]; + + id[0] = i; + + for (unsigned int j = 0; j < attributes.size(); j++) + { + const int fieldIndex = attrMap[j]; + if (fieldIndex != -1) + { + const FieldDescription &field = model->getField(fieldIndex); + if (field.type == FieldType::Scalar) + { + float* val = particleData.dataWrite<float>(partioAttrMap[j], index); + *val = (float) *field.getFct(i); + } + else if (field.type == FieldType::Vector3) + { + float* val = particleData.dataWrite<float>(partioAttrMap[j], index); + Eigen::Map<Vector3r> vec(field.getFct(i)); + val[0] = (float)vec[0]; + val[1] = (float)vec[1]; + val[2] = (float)vec[2]; + } + } + } + } + + Partio::write(fileName.c_str(), particleData, true); + particleData.release(); +} + +void SimulatorBase::step() { if (m_enablePartioExport) { @@ -862,7 +989,7 @@ void DemoBase::step() #endif } -void DemoBase::reset() +void SimulatorBase::reset() { //TimeManager::getCurrent()->setTimeStepSize(m_scene.timeStepSize); m_nextFrameTime = 0.0; @@ -872,17 +999,3 @@ void DemoBase::reset() #endif } -void DemoBase::setColorMapType(const int v) -{ - m_colorMapType = v; - if (m_colorMapType == ENUM_COLORMAP_JET) - { - m_colorMapBuffer = colormap_jet[0]; - m_colorMapLength = 256u; - } - else if (m_colorMapType == ENUM_COLORMAP_PLASMA) - { - m_colorMapBuffer = colormap_plasma[0]; - m_colorMapLength = 256u; - } -} diff --git a/Demos/Common/DemoBase.h b/Simulators/Common/SimulatorBase.h similarity index 68% rename from Demos/Common/DemoBase.h rename to Simulators/Common/SimulatorBase.h index 4d6ea9b..cb0dd4a 100644 --- a/Demos/Common/DemoBase.h +++ b/Simulators/Common/SimulatorBase.h @@ -1,5 +1,5 @@ -#ifndef __DemoBase_h__ -#define __DemoBase_h__ +#ifndef __SimulatorBase_h__ +#define __SimulatorBase_h__ #include "SPlisHSPlasH/Common.h" #include "SPlisHSPlasH/Utilities/SceneLoader.h" @@ -11,7 +11,7 @@ namespace SPH { - class DemoBase : public GenParam::ParameterObject + class SimulatorBase : public GenParam::ParameterObject { public: struct SimulationMethod @@ -38,20 +38,21 @@ namespace SPH Shader m_meshShader; GLuint m_textureMap; int m_renderWalls; - int m_colorField; bool m_doPause; Real m_pauseAt; Real m_stopAt; bool m_enablePartioExport; unsigned int m_framesPerSecond; - Real m_renderMaxValue; - Real m_renderMinValue; + std::string m_partioAttributes; Vector3r m_oldMousePos; std::vector<std::vector<unsigned int>> m_selectedParticles; std::unique_ptr<Utilities::SceneLoader> m_sceneLoader; Real m_nextFrameTime; unsigned int m_frameCounter; - int m_colorMapType; + std::vector<std::string> m_colorField; + std::vector<int> m_colorMapType; + std::vector<Real> m_renderMaxValue; + std::vector<Real> m_renderMinValue; float const* m_colorMapBuffer; unsigned int m_colorMapLength; #ifdef DL_OUTPUT @@ -76,12 +77,9 @@ namespace SPH static int NUM_STEPS_PER_RENDER; static int PARTIO_EXPORT; static int PARTIO_EXPORT_FPS; - static int RENDER_MIN_VALUE; - static int RENDER_MAX_VALUE; + static int PARTIO_EXPORT_ATTRIBUTES; static int RENDER_WALLS; - static int RENDER_COLOR_FIELD; - static int RENDER_COLOR_MAP_TYPE; - + static int ENUM_WALLS_NONE; static int ENUM_WALLS_PARTICLES_ALL; static int ENUM_WALLS_PARTICLES_NO_WALLS; @@ -93,21 +91,18 @@ namespace SPH static int ENUM_RENDER_ANGULAR_VELOCITY; static int ENUM_RENDER_DENSITY; - static int ENUM_COLORMAP_NONE; - static int ENUM_COLORMAP_JET; - static int ENUM_COLORMAP_PLASMA; - - DemoBase(); - virtual ~DemoBase(); + SimulatorBase(); + virtual ~SimulatorBase(); - void init(int argc, char **argv, const char *demoName); + void init(int argc, char **argv, const char *simName); void buildModel(); void cleanup(); - void renderFluid(FluidModel *model, float *fluidColor); + void renderFluid(const unsigned int fluidModelIndex, float *fluidColor); void readParameters(); void partioExport(); + void writeParticles(const std::string &fileName, FluidModel *model); void step(); void reset(); @@ -124,7 +119,7 @@ namespace SPH Shader& getMeshShader() { return m_meshShader; } void meshShaderBegin(const float *col); void meshShaderEnd(); - void pointShaderBegin(Shader *shader, const float *col, const bool useTexture = false); + void pointShaderBegin(Shader *shader, const float *col, const Real minVal, const Real maxVal, const bool useTexture = false, float const* color_map = nullptr); void pointShaderEnd(Shader *shader, const bool useTexture = false); Utilities::SceneLoader::Scene& getScene() { return m_scene; } @@ -132,8 +127,15 @@ namespace SPH bool getUseParticleCaching() const { return m_useParticleCaching; } void setUseParticleCaching(bool val) { m_useParticleCaching = val; } - int getColorMapType() const { return m_colorMapType; } - void setColorMapType(const int v); + const std::string& getColorField(const unsigned int fluidModelIndex) { return m_colorField[fluidModelIndex]; } + void setColorField(const unsigned int fluidModelIndex, const std::string& fieldName) { m_colorField[fluidModelIndex] = fieldName; } + + int getColorMapType(const unsigned int fluidModelIndex) const { return m_colorMapType[fluidModelIndex]; } + void setColorMapType(const unsigned int fluidModelIndex, int val) { m_colorMapType[fluidModelIndex] = val; } + Real getRenderMaxValue(const unsigned int fluidModelIndex) const { return m_renderMaxValue[fluidModelIndex]; } + void setRenderMaxValue(const unsigned int fluidModelIndex, Real val) { m_renderMaxValue[fluidModelIndex] = val; } + Real getRenderMinValue(const unsigned int fluidModelIndex) const { return m_renderMinValue[fluidModelIndex]; } + void setRenderMinValue(const unsigned int fluidModelIndex, Real val) { m_renderMinValue[fluidModelIndex] = val; } }; } diff --git a/Demos/Common/TweakBarParameters.cpp b/Simulators/Common/TweakBarParameters.cpp similarity index 89% rename from Demos/Common/TweakBarParameters.cpp rename to Simulators/Common/TweakBarParameters.cpp index fae8234..c51607d 100644 --- a/Demos/Common/TweakBarParameters.cpp +++ b/Simulators/Common/TweakBarParameters.cpp @@ -130,25 +130,11 @@ void TweakBarParameters::createParameterObjectGUI(ParameterObject *paramObj) TwAddVarCB(MiniGL::getTweakBar(), varName, TW_TYPE_DIR3R, setParameterValue, getParameterValue, m_params[m_params.size() - 1].get(), str); } } - -// else if ((desc->type == IBDS::IBDS_STRING_PARAMETER) || (desc->type == IBDS::IBDS_FILEPATH_PARAMETER)) -// { -// //const bool &defaultValue = paramObj->getParameterDefaultValue<bool>(j); -// char str[400]; -// char tmp.c_str()[20]; -// if (desc->readOnly) -// sprintf(tmp.c_str(), "%s", "readonly=true"); -// else -// sprintf(tmp.c_str(), "%s", "readonly=false"); -// -// // Defining new unique var name -// char varName[500]; -// sprintf(varName, "%s::%s", objName.c_str(), desc->fullName.c_str()); -// -// sprintf(str, " label='%s' help='%s' group='%s' %s ", desc->label.c_str(), desc->description.c_str(), objName.c_str(), tmp.c_str()); -// TwAddVarCB(MiniGL::getTwBar(), varName, TW_TYPE_CDSTRING, setParameterValue, getParameterValue, &m_params[m_params.size() - 1], str); -// } -// + else if (paramBase->getType() == ParameterBase::STRING) + { + sprintf(str, " label='%s' help='%s' group='%s' %s ", paramBase->getLabel().c_str(), paramBase->getDescription().c_str(), paramBase->getGroup().c_str(), tmp.c_str()); + TwAddVarCB(MiniGL::getTweakBar(), varName, TW_TYPE_STDSTRING, setParameterValue, getParameterValue, m_params[m_params.size() - 1].get(), str); + } } } @@ -232,11 +218,11 @@ void TW_CALL TweakBarParameters::setParameterValue(const void *value, void *clie static_cast<VectorParameter<Real>*>(paramBase)->setValue(val); } } -// else if ((desc->type == IBDS::IBDS_STRING_PARAMETER) || (desc->type == IBDS::IBDS_FILEPATH_PARAMETER)) -// { -// const std::string val = std::string(*(const char **)(value)); -// paramObj->setParameter<std::string>(desc->id, val); -// } + else if (paramBase->getType() == ParameterBase::STRING) + { + const std::string *srcPtr = static_cast<const std::string *>(value); + static_cast<StringParameter*>(paramBase)->setValue(*srcPtr); + } } void TW_CALL TweakBarParameters::getParameterValue(void *value, void *clientData) @@ -300,10 +286,11 @@ void TW_CALL TweakBarParameters::getParameterValue(void *value, void *clientData ((Real*)value)[2] = val[2]; } } -// else if ((desc->type == IBDS::IBDS_STRING_PARAMETER) || (desc->type == IBDS::IBDS_FILEPATH_PARAMETER)) -// { -// const std::string &val = paramObj->getParameter<std::string>(desc->id); -// *(const char **)(value) = val.c_str(); -// } + else if (paramBase->getType() == ParameterBase::STRING) + { + const std::string &val = static_cast<StringParameter*>(paramBase)->getValue(); + std::string *destPtr = static_cast<std::string *>(value); + TwCopyStdStringToLibrary(*destPtr, val); + } } diff --git a/Demos/Common/TweakBarParameters.h b/Simulators/Common/TweakBarParameters.h similarity index 100% rename from Demos/Common/TweakBarParameters.h rename to Simulators/Common/TweakBarParameters.h diff --git a/Demos/DynamicBoundaryDemo/CMakeLists.txt b/Simulators/DynamicBoundarySimulator/CMakeLists.txt similarity index 75% rename from Demos/DynamicBoundaryDemo/CMakeLists.txt rename to Simulators/DynamicBoundarySimulator/CMakeLists.txt index 69d8e65..cd7e6a4 100644 --- a/Demos/DynamicBoundaryDemo/CMakeLists.txt +++ b/Simulators/DynamicBoundarySimulator/CMakeLists.txt @@ -64,13 +64,13 @@ set(SIMULATION_LINK_LIBRARIES ${SIMULATION_LINK_LIBRARIES} set(SIMULATION_DEPENDENCIES Ext_PBD ${SIMULATION_DEPENDENCIES}) link_directories(${PROJECT_PATH}/extern/install/PositionBasedDynamics/lib) -add_executable(DynamicBoundaryDemo +add_executable(DynamicBoundarySimulator main.cpp - ${PROJECT_PATH}/Demos/Common/DemoBase.cpp - ${PROJECT_PATH}/Demos/Common/DemoBase.h - ${PROJECT_PATH}/Demos/Common/TweakBarParameters.cpp - ${PROJECT_PATH}/Demos/Common/TweakBarParameters.h + ${PROJECT_PATH}/Simulators/Common/SimulatorBase.cpp + ${PROJECT_PATH}/Simulators/Common/SimulatorBase.h + ${PROJECT_PATH}/Simulators/Common/TweakBarParameters.cpp + ${PROJECT_PATH}/Simulators/Common/TweakBarParameters.h ${VIS_FILES} @@ -87,23 +87,23 @@ add_definitions(-DTW_NO_LIB_PRAGMA -DTW_STATIC) include_directories(${PROJECT_PATH}/extern/freeglut/include) include_directories(${PROJECT_PATH}/extern/glew/include) -set_target_properties(DynamicBoundaryDemo PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) -set_target_properties(DynamicBoundaryDemo PROPERTIES RELWITHDEBINFO_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX}) -set_target_properties(DynamicBoundaryDemo PROPERTIES MINSIZEREL_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX}) -add_dependencies(DynamicBoundaryDemo ${SIMULATION_DEPENDENCIES}) -target_link_libraries(DynamicBoundaryDemo ${SIMULATION_LINK_LIBRARIES}) +set_target_properties(DynamicBoundarySimulator PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) +set_target_properties(DynamicBoundarySimulator PROPERTIES RELWITHDEBINFO_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX}) +set_target_properties(DynamicBoundarySimulator PROPERTIES MINSIZEREL_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX}) +add_dependencies(DynamicBoundarySimulator ${SIMULATION_DEPENDENCIES}) +target_link_libraries(DynamicBoundarySimulator ${SIMULATION_LINK_LIBRARIES}) VIS_SOURCE_GROUPS() source_group("Header Files\\PBD" FILES ${PBDWRAPPER_HEADER_FILES}) source_group("Source Files\\PBD" FILES ${PBDWRAPPER_SOURCE_FILES}) -set_target_properties(DynamicBoundaryDemo PROPERTIES FOLDER "Demos") +set_target_properties(DynamicBoundarySimulator PROPERTIES FOLDER "Simulators") -add_custom_command(TARGET DynamicBoundaryDemo PRE_BUILD +add_custom_command(TARGET DynamicBoundarySimulator PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory - ${PROJECT_PATH}/data/shaders $<TARGET_FILE_DIR:DynamicBoundaryDemo>/resources/shaders) + ${PROJECT_PATH}/data/shaders $<TARGET_FILE_DIR:DynamicBoundarySimulator>/resources/shaders) -add_custom_command(TARGET DynamicBoundaryDemo PRE_BUILD +add_custom_command(TARGET DynamicBoundarySimulator PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory - ${PROJECT_PATH}/extern/install/PositionBasedDynamics/include/data/shaders $<TARGET_FILE_DIR:DynamicBoundaryDemo>/resources/pbd_shaders) + ${PROJECT_PATH}/extern/install/PositionBasedDynamics/include/data/shaders $<TARGET_FILE_DIR:DynamicBoundarySimulator>/resources/pbd_shaders) diff --git a/Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDRigidBody.h b/Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDRigidBody.h similarity index 100% rename from Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDRigidBody.h rename to Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDRigidBody.h diff --git a/Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDWrapper.cpp b/Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDWrapper.cpp similarity index 99% rename from Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDWrapper.cpp rename to Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDWrapper.cpp index 34843b8..9241792 100644 --- a/Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDWrapper.cpp +++ b/Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDWrapper.cpp @@ -10,10 +10,10 @@ #include "Utilities/Timing.h" #include "Utilities/FileSystem.h" #include "GL/freeglut_ext.h" -#include "Demos/Visualization/Shader.h" +#include "Visualization/Shader.h" #include "Simulation/TimeManager.h" #include "Simulation/Simulation.h" -#include "Demos/Visualization/Visualization.h" +#include "Visualization/Visualization.h" #include "Simulation/TimeStepController.h" diff --git a/Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDWrapper.h b/Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDWrapper.h similarity index 100% rename from Demos/DynamicBoundaryDemo/PositionBasedDynamicsWrapper/PBDWrapper.h rename to Simulators/DynamicBoundarySimulator/PositionBasedDynamicsWrapper/PBDWrapper.h diff --git a/Demos/DynamicBoundaryDemo/main.cpp b/Simulators/DynamicBoundarySimulator/main.cpp similarity index 76% rename from Demos/DynamicBoundaryDemo/main.cpp rename to Simulators/DynamicBoundarySimulator/main.cpp index dc0d3ea..47b99ca 100644 --- a/Demos/DynamicBoundaryDemo/main.cpp +++ b/Simulators/DynamicBoundarySimulator/main.cpp @@ -11,13 +11,13 @@ #include "Utilities/OBJLoader.h" #include "SPlisHSPlasH/Utilities/PoissonDiskSampling.h" #include "PositionBasedDynamicsWrapper/PBDWrapper.h" -#include "Demos/Common/DemoBase.h" +#include "Simulators/Common/SimulatorBase.h" #include "Utilities/FileSystem.h" #include "Utilities/Version.h" #include "Utilities/SystemInfo.h" #include "Utilities/Counting.h" #include "SPlisHSPlasH/Simulation.h" -#include "Demos/Common/TweakBarParameters.h" +#include "Simulators/Common/TweakBarParameters.h" #include "SPlisHSPlasH/TimeStep.h" #include "GL/freeglut_ext.h" @@ -44,18 +44,27 @@ void updateBoundaryParticles(const bool forceUpdate); void updateBoundaryForces(); void TW_CALL setCurrentFluidModel(const void *value, void *clientData); void TW_CALL getCurrentFluidModel(void *value, void *clientData); - -DemoBase *base; +void TW_CALL setColorField(const void *value, void *clientData); +void TW_CALL getColorField(void *value, void *clientData); +void TW_CALL setRenderMaxValue(const void *value, void *clientData); +void TW_CALL getRenderMaxValue(void *value, void *clientData); +void TW_CALL setRenderMinValue(const void *value, void *clientData); +void TW_CALL getRenderMinValue(void *value, void *clientData); +void TW_CALL setColorMapType(const void *value, void *clientData); +void TW_CALL getColorMapType(void *value, void *clientData); + +SimulatorBase *base; PBDWrapper pbdWrapper; unsigned int currentFluidModel = 0; +std::vector<std::string> colorFieldNames; // main int main( int argc, char **argv ) { REPORT_MEMORY_LEAKS; - base = new DemoBase(); - base->init(argc, argv, "DynamicBoundaryDemo"); + base = new SimulatorBase(); + base->init(argc, argv, "DynamicBoundarySimulator"); Simulation *sim = Simulation::getCurrent(); sim->init(base->getScene().particleRadius, base->getScene().sim2D); @@ -83,6 +92,7 @@ int main( int argc, char **argv ) model->setSurfaceMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getSurfaceTensionBase()); }); model->setViscosityMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getViscosityBase()); }); model->setVorticityMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getVorticityBase()); }); + model->setElasticityMethodChangedCallback([&]() { reset(); initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getElasticityBase()); }); } initParameters(); @@ -141,16 +151,46 @@ void initParameters() // show GUI only for currently selected fluid model unsigned int i = currentFluidModel; FluidModel *model = sim->getFluidModel(currentFluidModel); + + colorFieldNames.clear(); + colorFieldNames.resize(model->numberOfFields()); + TwType enumType = TwDefineEnum("ColorFieldEnum", NULL, 0); + std::ostringstream oss; + for (unsigned int j = 0; j < model->numberOfFields(); j++) + { + const FieldDescription &field = model->getField(j); + if ((field.type == FieldType::Scalar) || (field.type == FieldType::Vector3)) + { + if (j != 0) + oss << ", "; + oss << j << " {" << field.name.c_str() << "}"; + colorFieldNames[j] = field.name; + } + } + std::string enumStr = " label='Color field' enum='" + oss.str() + "' group='" + model->getId() + "'"; + enumStr = enumStr + " help='Choose vector or scalar field for particle coloring.'"; + TwAddVarCB(MiniGL::getTweakBar(), "ColorField", enumType, setColorField, getColorField, nullptr, enumStr.c_str()); + + TwType enumType2 = TwDefineEnum("ColorMapTypeEnum", NULL, 0); + std::string str = " label='Color map' enum='0 {None}, 1 {Jet}, 2 {Plasma}' group='" + model->getId() + "' help='Choose a color map.'"; + TwAddVarCB(MiniGL::getTweakBar(), "ColorMapType", enumType2, setColorMapType, getColorMapType, nullptr, str.c_str()); + str = " label='Min. value (shader)' step=0.001 precision=3 group='" + model->getId() + "' help='Minimal value used for color-coding the color field in the rendering process.'"; + TwAddVarCB(MiniGL::getTweakBar(), "RenderMinValue", TW_TYPE_REAL, setRenderMinValue, getRenderMinValue, nullptr, str.c_str()); + str = " label='Max. value (shader)' step=0.001 precision=3 group='" + model->getId() + "' help='Maximal value used for color-coding the color field in the rendering process.'"; + TwAddVarCB(MiniGL::getTweakBar(), "RenderMaxValue", TW_TYPE_REAL, setRenderMaxValue, getRenderMaxValue, nullptr, str.c_str()); + TweakBarParameters::createParameterObjectGUI(model); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getDragBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getSurfaceTensionBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getViscosityBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getVorticityBase()); + TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getElasticityBase()); TwDefine((std::string("TweakBar/FluidModel group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/'Drag force' group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/'Surface tension' group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/Viscosity group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/Vorticity group='") + model->getId() + "'").c_str()); + TwDefine((std::string("TweakBar/'Elasticity' group='") + model->getId() + "'").c_str()); pbdWrapper.initGUI(); } @@ -177,21 +217,21 @@ void reset() void timeStep () { - const Real stopAt = base->getValue<Real>(DemoBase::STOP_AT); + const Real stopAt = base->getValue<Real>(SimulatorBase::STOP_AT); if ((stopAt > 0.0) && (stopAt < TimeManager::getCurrent()->getTime())) glutLeaveMainLoop(); - const Real pauseAt = base->getValue<Real>(DemoBase::PAUSE_AT); + const Real pauseAt = base->getValue<Real>(SimulatorBase::PAUSE_AT); if ((pauseAt > 0.0) && (pauseAt < TimeManager::getCurrent()->getTime())) - base->setValue(DemoBase::PAUSE, true); + base->setValue(SimulatorBase::PAUSE, true); - if (base->getValue<bool>(DemoBase::PAUSE)) + if (base->getValue<bool>(SimulatorBase::PAUSE)) return; // Simulation code Simulation *sim = Simulation::getCurrent(); const bool sim2D = sim->is2DSimulation(); - const unsigned int numSteps = base->getValue<unsigned int>(DemoBase::NUM_STEPS_PER_RENDER); + const unsigned int numSteps = base->getValue<unsigned int>(SimulatorBase::NUM_STEPS_PER_RENDER); for (unsigned int i = 0; i < numSteps; i++) { START_TIMING("SimStep"); @@ -235,7 +275,7 @@ void renderBoundary() Shader &shader = base->getShaderScalar(); Shader &meshShader = base->getMeshShader(); SceneLoader::Scene &scene = base->getScene(); - const int renderWalls = base->getValue<int>(DemoBase::RENDER_WALLS); + const int renderWalls = base->getValue<int>(SimulatorBase::RENDER_WALLS); GLint context_major_version = base->getContextMajorVersion(); if ((renderWalls == 1) || (renderWalls == 2)) @@ -286,7 +326,11 @@ void renderBoundary() void render() { float gridColor[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - MiniGL::drawGrid(gridColor); + const bool sim2D = Simulation::getCurrent()->is2DSimulation(); + if (sim2D) + MiniGL::drawGrid_xy(gridColor); + else + MiniGL::drawGrid_xz(gridColor); MiniGL::coordinateSystem(); MiniGL::drawTime(TimeManager::getCurrent()->getTime()); @@ -294,11 +338,9 @@ void render() Simulation *sim = Simulation::getCurrent(); for (unsigned int i = 0; i < sim->numberOfFluidModels(); i++) { - FluidModel *model = sim->getFluidModel(i); - float fluidColor[4] = { 0.3f, 0.5f, 0.9f, 1.0f }; MiniGL::hsvToRgb(0.61f - 0.1f*i, 0.66f, 0.9f, fluidColor); - base->renderFluid(model, fluidColor); + base->renderFluid(i, fluidColor); } renderBoundary(); @@ -309,7 +351,7 @@ void render() PBD::SimulationModel &model = pbdWrapper.getSimulationModel(); PBD::SimulationModel::RigidBodyVector &rb = model.getRigidBodies(); - const int renderWalls = base->getValue<int>(DemoBase::RENDER_WALLS); + const int renderWalls = base->getValue<int>(SimulatorBase::RENDER_WALLS); SceneLoader::Scene &scene = base->getScene(); if ((renderWalls == 3) || (renderWalls == 4)) { @@ -425,7 +467,7 @@ void initBoundaryData() } Simulation::getCurrent()->addBoundaryModel(rb, static_cast<unsigned int>(boundaryParticles.size()), &boundaryParticles[0]); } - Simulation::getCurrent()->updateBoundaryPsi(); + Simulation::getCurrent()->updateBoundaryVolume(); updateBoundaryParticles(true); } @@ -488,3 +530,57 @@ void TW_CALL getCurrentFluidModel(void *value, void *clientData) { *(unsigned int *)(value) = *((unsigned int*)clientData); } + +void TW_CALL setColorField(const void *value, void *clientData) +{ + const unsigned int val = *(const unsigned int *)(value); + base->setColorField(currentFluidModel, colorFieldNames[val]); +} + +void TW_CALL getColorField(void *value, void *clientData) +{ + const std::string &fieldName = base->getColorField(currentFluidModel); + unsigned int index = 0; + for (auto i = 0; i < colorFieldNames.size(); i++) + { + if (colorFieldNames[i] == fieldName) + { + index = i; + break; + } + } + *(unsigned int *)(value) = index; +} + +void TW_CALL setRenderMaxValue(const void *value, void *clientData) +{ + const Real val = *(const Real *)(value); + base->setRenderMaxValue(currentFluidModel, val); +} + +void TW_CALL getRenderMaxValue(void *value, void *clientData) +{ + *(Real *)(value) = base->getRenderMaxValue(currentFluidModel); +} + +void TW_CALL setRenderMinValue(const void *value, void *clientData) +{ + const Real val = *(const Real *)(value); + base->setRenderMinValue(currentFluidModel, val); +} + +void TW_CALL getRenderMinValue(void *value, void *clientData) +{ + *(Real *)(value) = base->getRenderMinValue(currentFluidModel); +} + +void TW_CALL setColorMapType(const void *value, void *clientData) +{ + const unsigned int val = *(const unsigned int *)(value); + base->setColorMapType(currentFluidModel, val); +} + +void TW_CALL getColorMapType(void *value, void *clientData) +{ + *(unsigned int *)(value) = base->getColorMapType(currentFluidModel); +} diff --git a/Demos/StaticBoundaryDemo/CMakeLists.txt b/Simulators/StaticBoundarySimulator/CMakeLists.txt similarity index 66% rename from Demos/StaticBoundaryDemo/CMakeLists.txt rename to Simulators/StaticBoundarySimulator/CMakeLists.txt index 0b00f8c..c1e597e 100644 --- a/Demos/StaticBoundaryDemo/CMakeLists.txt +++ b/Simulators/StaticBoundarySimulator/CMakeLists.txt @@ -35,13 +35,13 @@ include_directories(${PROJECT_PATH}/extern/install/GenericParameters/include) set(SIMULATION_DEPENDENCIES ${SIMULATION_DEPENDENCIES} Ext_GenericParameters) -add_executable(StaticBoundaryDemo +add_executable(StaticBoundarySimulator main.cpp - ${PROJECT_PATH}/Demos/Common/DemoBase.cpp - ${PROJECT_PATH}/Demos/Common/DemoBase.h - ${PROJECT_PATH}/Demos/Common/TweakBarParameters.cpp - ${PROJECT_PATH}/Demos/Common/TweakBarParameters.h + ${PROJECT_PATH}/Simulators/Common/SimulatorBase.cpp + ${PROJECT_PATH}/Simulators/Common/SimulatorBase.h + ${PROJECT_PATH}/Simulators/Common/TweakBarParameters.cpp + ${PROJECT_PATH}/Simulators/Common/TweakBarParameters.h ${VIS_FILES} ) @@ -55,15 +55,15 @@ add_definitions(-DTW_NO_LIB_PRAGMA -DTW_STATIC) include_directories(${PROJECT_PATH}/extern/freeglut/include) include_directories(${PROJECT_PATH}/extern/glew/include) -set_target_properties(StaticBoundaryDemo PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) -set_target_properties(StaticBoundaryDemo PROPERTIES RELWITHDEBINFO_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX}) -set_target_properties(StaticBoundaryDemo PROPERTIES MINSIZEREL_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX}) -add_dependencies(StaticBoundaryDemo ${SIMULATION_DEPENDENCIES}) -target_link_libraries(StaticBoundaryDemo ${SIMULATION_LINK_LIBRARIES}) +set_target_properties(StaticBoundarySimulator PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) +set_target_properties(StaticBoundarySimulator PROPERTIES RELWITHDEBINFO_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX}) +set_target_properties(StaticBoundarySimulator PROPERTIES MINSIZEREL_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX}) +add_dependencies(StaticBoundarySimulator ${SIMULATION_DEPENDENCIES}) +target_link_libraries(StaticBoundarySimulator ${SIMULATION_LINK_LIBRARIES}) VIS_SOURCE_GROUPS() -set_target_properties(StaticBoundaryDemo PROPERTIES FOLDER "Demos") +set_target_properties(StaticBoundarySimulator PROPERTIES FOLDER "Simulators") -add_custom_command(TARGET StaticBoundaryDemo PRE_BUILD +add_custom_command(TARGET StaticBoundarySimulator PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory - ${PROJECT_PATH}/data/shaders $<TARGET_FILE_DIR:StaticBoundaryDemo>/resources/shaders) + ${PROJECT_PATH}/data/shaders $<TARGET_FILE_DIR:StaticBoundarySimulator>/resources/shaders) diff --git a/Demos/StaticBoundaryDemo/main.cpp b/Simulators/StaticBoundarySimulator/main.cpp similarity index 74% rename from Demos/StaticBoundaryDemo/main.cpp rename to Simulators/StaticBoundarySimulator/main.cpp index eddcb6e..1670585 100644 --- a/Demos/StaticBoundaryDemo/main.cpp +++ b/Simulators/StaticBoundarySimulator/main.cpp @@ -10,14 +10,14 @@ #include "SPlisHSPlasH/StaticRigidBody.h" #include "Utilities/OBJLoader.h" #include "SPlisHSPlasH/Utilities/PoissonDiskSampling.h" -#include "Demos/Common/DemoBase.h" +#include "Simulators/Common/SimulatorBase.h" #include "Utilities/FileSystem.h" #include "Utilities/Version.h" #include <fstream> #include "Utilities/Logger.h" #include "Utilities/Counting.h" #include "SPlisHSPlasH/Simulation.h" -#include "Demos/Common/TweakBarParameters.h" +#include "Simulators/Common/TweakBarParameters.h" #include "GL/freeglut_ext.h" // Enable memory leak detection @@ -42,17 +42,26 @@ void initParameters(); void loadObj(const std::string &filename, TriangleMesh &geo, const Vector3r &scale); void TW_CALL setCurrentFluidModel(const void *value, void *clientData); void TW_CALL getCurrentFluidModel(void *value, void *clientData); - -DemoBase *base; +void TW_CALL setColorField(const void *value, void *clientData); +void TW_CALL getColorField(void *value, void *clientData); +void TW_CALL setRenderMaxValue(const void *value, void *clientData); +void TW_CALL getRenderMaxValue(void *value, void *clientData); +void TW_CALL setRenderMinValue(const void *value, void *clientData); +void TW_CALL getRenderMinValue(void *value, void *clientData); +void TW_CALL setColorMapType(const void *value, void *clientData); +void TW_CALL getColorMapType(void *value, void *clientData); + +SimulatorBase *base; unsigned int currentFluidModel = 0; +std::vector<std::string> colorFieldNames; // main int main( int argc, char **argv ) { REPORT_MEMORY_LEAKS; - base = new DemoBase(); - base->init(argc, argv, "StaticBoundaryDemo"); + base = new SimulatorBase(); + base->init(argc, argv, "StaticBoundarySimulator"); Simulation *sim = Simulation::getCurrent(); sim->init(base->getScene().particleRadius, base->getScene().sim2D); @@ -74,6 +83,7 @@ int main( int argc, char **argv ) model->setSurfaceMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getSurfaceTensionBase()); }); model->setViscosityMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getViscosityBase()); }); model->setVorticityMethodChangedCallback([&]() { initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getVorticityBase()); }); + model->setElasticityMethodChangedCallback([&]() { reset(); initParameters(); base->getSceneLoader()->readParameterObject(key, (ParameterObject*)model->getElasticityBase()); }); } initParameters(); @@ -130,16 +140,46 @@ void initParameters() // show GUI only for currently selected fluid model unsigned int i = currentFluidModel; FluidModel *model = sim->getFluidModel(currentFluidModel); + + colorFieldNames.clear(); + colorFieldNames.resize(model->numberOfFields()); + TwType enumType = TwDefineEnum("ColorFieldEnum", NULL, 0); + std::ostringstream oss; + for (unsigned int j = 0; j < model->numberOfFields(); j++) + { + const FieldDescription &field = model->getField(j); + if ((field.type == FieldType::Scalar) || (field.type == FieldType::Vector3)) + { + if (j != 0) + oss << ", "; + oss << j << " {" << field.name.c_str() << "}"; + colorFieldNames[j] = field.name; + } + } + std::string enumStr = " label='Color field' enum='" + oss.str() + "' group='" + model->getId() + "'"; + enumStr = enumStr + " help='Choose vector or scalar field for particle coloring.'"; + TwAddVarCB(MiniGL::getTweakBar(), "ColorField", enumType, setColorField, getColorField, nullptr, enumStr.c_str()); + + TwType enumType2 = TwDefineEnum("ColorMapTypeEnum", NULL, 0); + std::string str = " label='Color map' enum='0 {None}, 1 {Jet}, 2 {Plasma}' group='" + model->getId() + "' help='Choose a color map.'"; + TwAddVarCB(MiniGL::getTweakBar(), "ColorMapType", enumType2, setColorMapType, getColorMapType, nullptr, str.c_str()); + str = " label='Min. value (shader)' step=0.001 precision=3 group='" + model->getId() + "' help='Minimal value used for color-coding the color field in the rendering process.'"; + TwAddVarCB(MiniGL::getTweakBar(), "RenderMinValue", TW_TYPE_REAL, setRenderMinValue, getRenderMinValue, nullptr, str.c_str()); + str = " label='Max. value (shader)' step=0.001 precision=3 group='" + model->getId() + "' help='Maximal value used for color-coding the color field in the rendering process.'"; + TwAddVarCB(MiniGL::getTweakBar(), "RenderMaxValue", TW_TYPE_REAL, setRenderMaxValue, getRenderMaxValue, nullptr, str.c_str()); + TweakBarParameters::createParameterObjectGUI(model); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getDragBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getSurfaceTensionBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getViscosityBase()); TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getVorticityBase()); + TweakBarParameters::createParameterObjectGUI((GenParam::ParameterObject*) model->getElasticityBase()); TwDefine((std::string("TweakBar/FluidModel group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/'Drag force' group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/'Surface tension' group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/Viscosity group='") + model->getId() + "'").c_str()); TwDefine((std::string("TweakBar/Vorticity group='") + model->getId() + "'").c_str()); + TwDefine((std::string("TweakBar/'Elasticity' group='") + model->getId() + "'").c_str()); } void reset() @@ -157,21 +197,21 @@ void reset() void timeStep () { - const Real stopAt = base->getValue<Real>(DemoBase::STOP_AT); + const Real stopAt = base->getValue<Real>(SimulatorBase::STOP_AT); if ((stopAt > 0.0) && (stopAt < TimeManager::getCurrent()->getTime())) glutLeaveMainLoop(); - const Real pauseAt = base->getValue<Real>(DemoBase::PAUSE_AT); + const Real pauseAt = base->getValue<Real>(SimulatorBase::PAUSE_AT); if ((pauseAt > 0.0) && (pauseAt < TimeManager::getCurrent()->getTime())) - base->setValue(DemoBase::PAUSE, true); + base->setValue(SimulatorBase::PAUSE, true); - if (base->getValue<bool>(DemoBase::PAUSE)) + if (base->getValue<bool>(SimulatorBase::PAUSE)) return; // Simulation code Simulation *sim = Simulation::getCurrent(); const bool sim2D = sim->is2DSimulation(); - const unsigned int numSteps = base->getValue<unsigned int>(DemoBase::NUM_STEPS_PER_RENDER); + const unsigned int numSteps = base->getValue<unsigned int>(SimulatorBase::NUM_STEPS_PER_RENDER); for (unsigned int i = 0; i < numSteps; i++) { START_TIMING("SimStep"); @@ -201,7 +241,11 @@ void timeStep () void render() { float gridColor[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - MiniGL::drawGrid(gridColor); + const bool sim2D = Simulation::getCurrent()->is2DSimulation(); + if (sim2D) + MiniGL::drawGrid_xy(gridColor); + else + MiniGL::drawGrid_xz(gridColor); MiniGL::coordinateSystem(); MiniGL::drawTime(TimeManager::getCurrent()->getTime()); @@ -209,11 +253,9 @@ void render() Simulation *sim = Simulation::getCurrent(); for (unsigned int i = 0; i < sim->numberOfFluidModels(); i++) { - FluidModel *model = sim->getFluidModel(i); - float fluidColor[4] = { 0.3f, 0.5f, 0.9f, 1.0f }; MiniGL::hsvToRgb(0.61f-0.1f*i, 0.66f, 0.9f, fluidColor); - base->renderFluid(model, fluidColor); + base->renderFluid(i, fluidColor); } renderBoundary(); } @@ -224,7 +266,7 @@ void renderBoundary() Shader &shader = base->getShaderScalar(); Shader &meshShader = base->getMeshShader(); SceneLoader::Scene &scene = base->getScene(); - const int renderWalls = base->getValue<int>(DemoBase::RENDER_WALLS); + const int renderWalls = base->getValue<int>(SimulatorBase::RENDER_WALLS); GLint context_major_version = base->getContextMajorVersion(); if ((renderWalls == 1) || (renderWalls == 2)) @@ -412,7 +454,7 @@ void initBoundaryData() Simulation::getCurrent()->addBoundaryModel(rb, static_cast<unsigned int>(boundaryParticles.size()), &boundaryParticles[0]); } - Simulation::getCurrent()->updateBoundaryPsi(); + Simulation::getCurrent()->updateBoundaryVolume(); } void TW_CALL setCurrentFluidModel(const void *value, void *clientData) @@ -427,3 +469,57 @@ void TW_CALL getCurrentFluidModel(void *value, void *clientData) { *(unsigned int *)(value) = *((unsigned int*)clientData); } + +void TW_CALL setColorField(const void *value, void *clientData) +{ + const unsigned int val = *(const unsigned int *)(value); + base->setColorField(currentFluidModel, colorFieldNames[val]); +} + +void TW_CALL getColorField(void *value, void *clientData) +{ + const std::string &fieldName = base->getColorField(currentFluidModel); + unsigned int index = 0; + for (auto i = 0; i < colorFieldNames.size(); i++) + { + if (colorFieldNames[i] == fieldName) + { + index = i; + break; + } + } + *(unsigned int *)(value) = index; +} + +void TW_CALL setRenderMaxValue(const void *value, void *clientData) +{ + const Real val = *(const Real *)(value); + base->setRenderMaxValue(currentFluidModel, val); +} + +void TW_CALL getRenderMaxValue(void *value, void *clientData) +{ + *(Real *)(value) = base->getRenderMaxValue(currentFluidModel); +} + +void TW_CALL setRenderMinValue(const void *value, void *clientData) +{ + const Real val = *(const Real *)(value); + base->setRenderMinValue(currentFluidModel, val); +} + +void TW_CALL getRenderMinValue(void *value, void *clientData) +{ + *(Real *)(value) = base->getRenderMinValue(currentFluidModel); +} + +void TW_CALL setColorMapType(const void *value, void *clientData) +{ + const unsigned int val = *(const unsigned int *)(value); + base->setColorMapType(currentFluidModel, val); +} + +void TW_CALL getColorMapType(void *value, void *clientData) +{ + *(unsigned int *)(value) = base->getColorMapType(currentFluidModel); +} diff --git a/Visualization/MiniGL.cpp b/Visualization/MiniGL.cpp index 31b7147..f9580e6 100644 --- a/Visualization/MiniGL.cpp +++ b/Visualization/MiniGL.cpp @@ -393,7 +393,7 @@ void MiniGL::drawTetrahedron(const Vector3r &a, const Vector3r &b, const Vector3 drawTriangle(b, c, d, normal4, color); } -void MiniGL::drawGrid(float *color) +void MiniGL::drawGrid_xz(float *color) { float speccolor[4] = { 1.0, 1.0, 1.0, 1.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); @@ -422,6 +422,35 @@ void MiniGL::drawGrid(float *color) glEnd(); } +void MiniGL::drawGrid_xy(float *color) +{ + float speccolor[4] = { 1.0, 1.0, 1.0, 1.0 }; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, speccolor); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0); + + const int size = 5; + + glBegin(GL_LINES); + for (int i = -size; i <= size; i++) + { + glVertex3f((float)i, (float)-size, 0.0f ); + glVertex3f((float)i, (float)size, 0.0f); + glVertex3f((float)-size, (float)i, 0.0f); + glVertex3f((float)size, (float)i, 0.0f); + } + glEnd(); + + glLineWidth(3.0f); + glBegin(GL_LINES); + glVertex3f((float)-size, 0.0f, 0.0f); + glVertex3f((float)size, 0.0f, 0.0f); + glVertex3f(0.0f, (float)-size, 0.0f); + glVertex3f(0.0f, (float)size, 0.0f); + glEnd(); +} + void MiniGL::setViewport(float pfovy, float pznear, float pzfar, const Vector3r &peyepoint, const Vector3r &plookat) { fovy = pfovy; diff --git a/Visualization/MiniGL.h b/Visualization/MiniGL.h index 70bcdde..2f65fd2 100644 --- a/Visualization/MiniGL.h +++ b/Visualization/MiniGL.h @@ -129,7 +129,8 @@ namespace SPH */ static void drawTetrahedron(const Vector3r &a, const Vector3r &b, const Vector3r &c, const Vector3r &d, float *color); static void drawTriangle (const Vector3r &a, const Vector3r &b, const Vector3r &c, const Vector3r &norm, float *color); - static void drawGrid(float *color); + static void drawGrid_xz(float *color); + static void drawGrid_xy(float *color); static void drawBitmapText (float x, float y, const char *str, int strLength, float *color); static void drawStrokeText(const Real x, const Real y, const Real z, float scale, const char *str, int strLength, float *color); static void drawStrokeText (const Vector3r &pos, float scale, const char *str, int strLength, float *color); diff --git a/bin/Buckling.bat b/bin/Buckling.bat deleted file mode 100644 index 5b028a7..0000000 --- a/bin/Buckling.bat +++ /dev/null @@ -1,2 +0,0 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel.json -pause \ No newline at end of file diff --git a/bin/Buckling_Bender2017.bat b/bin/Buckling_Bender2017.bat index 61f762a..44b5c2d 100644 --- a/bin/Buckling_Bender2017.bat +++ b/bin/Buckling_Bender2017.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel_Bender2017.json +.\StaticBoundarySimulator.exe ../data/scenes/BucklingModel_Bender2017.json pause \ No newline at end of file diff --git a/bin/Buckling_Peer2015.bat b/bin/Buckling_Peer2015.bat index 95a9e75..1fedbfc 100644 --- a/bin/Buckling_Peer2015.bat +++ b/bin/Buckling_Peer2015.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel_Peer2015.json +.\StaticBoundarySimulator.exe ../data/scenes/BucklingModel_Peer2015.json pause \ No newline at end of file diff --git a/bin/Buckling_Peer2016.bat b/bin/Buckling_Peer2016.bat index 4734a82..1ede812 100644 --- a/bin/Buckling_Peer2016.bat +++ b/bin/Buckling_Peer2016.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel_Peer2016.json +.\StaticBoundarySimulator.exe ../data/scenes/BucklingModel_Peer2016.json pause \ No newline at end of file diff --git a/bin/Buckling_Takahashi2015.bat b/bin/Buckling_Takahashi2015.bat index e6cb2c4..f439ae6 100644 --- a/bin/Buckling_Takahashi2015.bat +++ b/bin/Buckling_Takahashi2015.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel_Takahashi2015.json +.\StaticBoundarySimulator.exe ../data/scenes/BucklingModel_Takahashi2015.json pause \ No newline at end of file diff --git a/bin/Buckling_Weiler2018.bat b/bin/Buckling_Weiler2018.bat index 26b5d1d..d314731 100644 --- a/bin/Buckling_Weiler2018.bat +++ b/bin/Buckling_Weiler2018.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/BucklingModel_Weiler2018.json +.\StaticBoundarySimulator.exe ../data/scenes/BucklingModel_Weiler2018.json pause \ No newline at end of file diff --git a/bin/Bunny_vs_Dragon.bat b/bin/Bunny_vs_Dragon.bat index c995168..cfce186 100644 --- a/bin/Bunny_vs_Dragon.bat +++ b/bin/Bunny_vs_Dragon.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/Bunny_vs_Dragon.json +.\StaticBoundarySimulator.exe ../data/scenes/Bunny_vs_Dragon.json pause \ No newline at end of file diff --git a/bin/Coiling_Bender2017.bat b/bin/Coiling_Bender2017.bat index 489785c..cf2cc7a 100644 --- a/bin/Coiling_Bender2017.bat +++ b/bin/Coiling_Bender2017.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/CoilingModel_Bender2017.json +.\StaticBoundarySimulator.exe ../data/scenes/CoilingModel_Bender2017.json pause \ No newline at end of file diff --git a/bin/Coiling_Peer2015.bat b/bin/Coiling_Peer2015.bat index 467569c..a78a454 100644 --- a/bin/Coiling_Peer2015.bat +++ b/bin/Coiling_Peer2015.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/CoilingModel_Peer2015.json +.\StaticBoundarySimulator.exe ../data/scenes/CoilingModel_Peer2015.json pause \ No newline at end of file diff --git a/bin/Coiling_Peer2016.bat b/bin/Coiling_Peer2016.bat index b851695..2523f2a 100644 --- a/bin/Coiling_Peer2016.bat +++ b/bin/Coiling_Peer2016.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/CoilingModel_Peer2016.json +.\StaticBoundarySimulator.exe ../data/scenes/CoilingModel_Peer2016.json pause \ No newline at end of file diff --git a/bin/Coiling_Takahashi2015.bat b/bin/Coiling_Takahashi2015.bat index 3c44b1d..23f88f2 100644 --- a/bin/Coiling_Takahashi2015.bat +++ b/bin/Coiling_Takahashi2015.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/CoilingModel_Takahashi2015.json +.\StaticBoundarySimulator.exe ../data/scenes/CoilingModel_Takahashi2015.json pause \ No newline at end of file diff --git a/bin/Coiling_Weiler2018.bat b/bin/Coiling_Weiler2018.bat index c5967c5..0242a0d 100644 --- a/bin/Coiling_Weiler2018.bat +++ b/bin/Coiling_Weiler2018.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/CoilingModel_Weiler2018.json +.\StaticBoundarySimulator.exe ../data/scenes/CoilingModel_Weiler2018.json pause \ No newline at end of file diff --git a/bin/DamBreakModel.bat b/bin/DamBreakModel.bat index 488e258..d0ad0b2 100644 --- a/bin/DamBreakModel.bat +++ b/bin/DamBreakModel.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DamBreakModel.json +.\StaticBoundarySimulator.exe ../data/scenes/DamBreakModel.json pause \ No newline at end of file diff --git a/bin/DamBreakModelDragons.bat b/bin/DamBreakModelDragons.bat index c2e81dd..feba9c0 100644 --- a/bin/DamBreakModelDragons.bat +++ b/bin/DamBreakModelDragons.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DamBreakModelDragons.json +.\StaticBoundarySimulator.exe ../data/scenes/DamBreakModelDragons.json pause \ No newline at end of file diff --git a/bin/DamBreakModel_2D.bat b/bin/DamBreakModel_2D.bat index f110733..bc9163a 100644 --- a/bin/DamBreakModel_2D.bat +++ b/bin/DamBreakModel_2D.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DamBreakModel_2D.json +.\StaticBoundarySimulator.exe ../data/scenes/DamBreakModel_2D.json pause \ No newline at end of file diff --git a/bin/DamBreakWithObjects.bat b/bin/DamBreakWithObjects.bat index 91913cf..853eb2b 100644 --- a/bin/DamBreakWithObjects.bat +++ b/bin/DamBreakWithObjects.bat @@ -1,2 +1,2 @@ -.\DynamicBoundaryDemo.exe ../data/scenes/DamBreakWithObjects.json +.\DynamicBoundarySimulator.exe ../data/scenes/DamBreakWithObjects.json pause \ No newline at end of file diff --git a/bin/DoubleDamBreak.bat b/bin/DoubleDamBreak.bat index f834a05..c1adff8 100644 --- a/bin/DoubleDamBreak.bat +++ b/bin/DoubleDamBreak.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DoubleDamBreak.json +.\StaticBoundarySimulator.exe ../data/scenes/DoubleDamBreak.json pause \ No newline at end of file diff --git a/bin/DoubleDamBreakMultiPhase.bat b/bin/DoubleDamBreakMultiPhase.bat new file mode 100644 index 0000000..16c8658 --- /dev/null +++ b/bin/DoubleDamBreakMultiPhase.bat @@ -0,0 +1,2 @@ +.\StaticBoundarySimulator.exe ../data/scenes/DoubleDamBreakMultiPhase.json +pause \ No newline at end of file diff --git a/bin/DoubleDamBreakTwoPhase.bat b/bin/DoubleDamBreakTwoPhase.bat deleted file mode 100644 index a8fb926..0000000 --- a/bin/DoubleDamBreakTwoPhase.bat +++ /dev/null @@ -1,2 +0,0 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DoubleDamBreakTwoPhase.json -pause \ No newline at end of file diff --git a/bin/DoubleDamBreakWithSphere.bat b/bin/DoubleDamBreakWithSphere.bat index b9d5833..0fec86e 100644 --- a/bin/DoubleDamBreakWithSphere.bat +++ b/bin/DoubleDamBreakWithSphere.bat @@ -1,2 +1,2 @@ -.\DynamicBoundaryDemo.exe ../data/scenes/DoubleDamBreakWithSphere.json +.\DynamicBoundarySimulator.exe ../data/scenes/DoubleDamBreakWithSphere.json pause \ No newline at end of file diff --git a/bin/DragTest.bat b/bin/DragTest.bat index 91b795b..b22289e 100644 --- a/bin/DragTest.bat +++ b/bin/DragTest.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/DragTest.json +.\StaticBoundarySimulator.exe ../data/scenes/DragTest.json pause diff --git a/bin/Emitter.bat b/bin/Emitter.bat index 472a7d2..2360991 100644 --- a/bin/Emitter.bat +++ b/bin/Emitter.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/Emitter.json +.\StaticBoundarySimulator.exe ../data/scenes/Emitter.json pause \ No newline at end of file diff --git a/bin/MultiPhaseColoring.bat b/bin/MultiPhaseColoring.bat new file mode 100644 index 0000000..ce96122 --- /dev/null +++ b/bin/MultiPhaseColoring.bat @@ -0,0 +1,2 @@ +.\StaticBoundarySimulator.exe ../data/scenes/MultiPhaseColoring.json +pause \ No newline at end of file diff --git a/bin/Obstacle.bat b/bin/Obstacle.bat index a74854c..5a2e33e 100644 --- a/bin/Obstacle.bat +++ b/bin/Obstacle.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/Obstacle.json +.\StaticBoundarySimulator.exe ../data/scenes/Obstacle.json pause \ No newline at end of file diff --git a/bin/ViscousBunny.bat b/bin/ViscousBunny.bat index 2fcd3d9..0ad070e 100644 --- a/bin/ViscousBunny.bat +++ b/bin/ViscousBunny.bat @@ -1,2 +1,2 @@ -.\StaticBoundaryDemo.exe ../data/scenes/ViscousBunny.json +.\StaticBoundarySimulator.exe ../data/scenes/ViscousBunny.json pause \ No newline at end of file diff --git a/data/Scenes/DamBreakModel.json b/data/Scenes/DamBreakModel.json index 1236a39..6899760 100644 --- a/data/Scenes/DamBreakModel.json +++ b/data/Scenes/DamBreakModel.json @@ -16,7 +16,8 @@ "stiffness": 50000, "exponent": 7, "velocityUpdateMethod": 0, - "enableDivergenceSolver": true + "enableDivergenceSolver": true, + "partioAttributes": "density;velocity" }, "Fluid": { diff --git a/data/Scenes/DeformableModel.json b/data/Scenes/DeformableModel.json new file mode 100644 index 0000000..249dbce --- /dev/null +++ b/data/Scenes/DeformableModel.json @@ -0,0 +1,55 @@ +{ + "Configuration": + { + "imteStepSize": 0.001, + "particleRadius": 0.025, + "numberOfStepsPerRenderUpdate": 2, + "density0": 1000, + "simulationMethod": 4, + "gravitation": [0,-9.81,0], + "cflMethod": 0, + "cflFactor": 1, + "cflMaxTimeStepSize": 0.005, + "maxIterations": 100, + "maxError": 0.01, + "maxIterationsV": 100, + "maxErrorV": 0.1, + "stiffness": 50000, + "exponent": 7, + "velocityUpdateMethod": 0, + "enableDivergenceSolver": true + }, + "Fluid": + { + "elasticityMethod": 2, + "viscosity": 0.01, + "viscosityMethod": 1, + "surfaceTension": 0.02, + "surfaceTensionMethod": 0, + "youngsModulus": 250000.0, + "poissonsRatio": 0.33, + "alpha": 0.1 + }, + "RigidBodies": [ + { + "geometryFile": "../models/UnitBox.obj", + "translation": [0,2.5,0], + "rotationAxis": [1, 0, 0], + "rotationAngle": 0, + "scale": [3.1, 5, 3.1], + "color": [0.1, 0.4, 0.6, 1.0], + "isDynamic": false, + "isWall": true + } + ], + "FluidBlocks": [ + { + "denseMode": 0, + "start": [-0.25, 0.5, -0.25], + "end": [0.25, 1.0, 0.25] + } + ] +} + + + diff --git a/data/Scenes/DoubleDamBreakTwoPhase.json b/data/Scenes/DoubleDamBreakMultiPhase.json similarity index 100% rename from data/Scenes/DoubleDamBreakTwoPhase.json rename to data/Scenes/DoubleDamBreakMultiPhase.json diff --git a/data/Scenes/MultiPhaseColoring.json b/data/Scenes/MultiPhaseColoring.json new file mode 100644 index 0000000..f70743d --- /dev/null +++ b/data/Scenes/MultiPhaseColoring.json @@ -0,0 +1,124 @@ +{ + "Configuration": + { + "timeStepSize": 0.001, + "numberOfStepsPerRenderUpdate": 2, + "particleRadius": 0.025, + "simulationMethod": 4, + "gravitation": [0.0,-9.81,0], + "cflMethod": 1, + "cflFactor": 0.5, + "cflMaxTimeStepSize": 0.005, + "maxIterations": 100, + "maxError": 0.01, + "maxIterationsV": 100, + "maxErrorV": 0.1, + "stiffness": 5000, + "exponent": 1, + "velocityUpdateMethod": 0, + "enableDivergenceSolver": true + }, + "first_fluid": + { + "density0": 1000, + "viscosity": 0.01, + "viscosityBoundary": 5000, + "viscosityMethod": 1, + "viscoMaxIter": 200, + "viscoMaxError": 0.05, + "maxEmitterParticles": 6000, + "colorField": "velocity", + "colorMapType": 1, + "renderMinValue": 0.01, + "renderMaxValue": 6.0 + }, + "second_fluid": + { + "density0": 100, + "viscosity": 0.01, + "viscosityMethod": 1, + "vorticityMethod": 1, + "vorticity": 0.1, + "colorField": "angular velocity", + "colorMapType": 2, + "renderMinValue": 0.01, + "renderMaxValue": 2.0 + }, + "third_fluid": + { + "density0": 50, + "viscosity": 0.015, + "maxEmitterParticles": 1000, + "emitterReuseParticles": false, + "emitterBoxMin": [-4.0,-1.0,-4.0], + "emitterBoxMax": [0.0,4,4.0], + "colorField": "position", + "colorMapType": 1, + "renderMinValue": 0.0, + "renderMaxValue": 2.0 + }, + "RigidBodies": [ + { + "geometryFile": "../models/UnitBox.obj", + "translation": [0,2,0], + "rotationAxis": [1, 0, 0], + "rotationAngle": 0, + "scale": [2.1, 4, 2.1], + "color": [0.1, 0.4, 0.6, 1.0], + "isDynamic": false, + "isWall": true, + "restitution" : 0.6, + "friction" : 0.0, + "collisionObjectType": 2, + "collisionObjectScale": [2.1, 4, 2.1], + "invertSDF": true + } + ], + "Comment": [ + { + "id": 1, + "geometryFile": "../models/torus.obj", + "isDynamic": 1, + "density": 50, + "translation": [0.4,1.0,0.0], + "rotationAxis": [0, 0, 1], + "rotationAngle": 1.0, + "scale": [0.2, 0.2, 0.2], + "restitution" : 0.6, + "friction" : 0.2, + "color": [0.5, 0.2, 0.6, 1.0], + "collisionObjectType": 4, + "collisionObjectScale": [0.2, 0.1, 0.2] + } + ], + "FluidBlocks": [ + { + "id": "first_fluid", + "denseMode": 0, + "start": [-1.0, 0.0, -1.0], + "end": [-0.3, 0.75, -0.3] + }, + { + "id": "second_fluid", + "denseMode": 0, + "start": [0.3, 0.0, 0.3], + "end": [1.0, 1.75, 1.0] + } + ], + "Emitters": [ + { + "id": "third_fluid", + "width": 8, + "height": 3, + "translation": [-0.2,0.75,0.0], + "direction": [1, 0, 0], + "velocity": [2, 0, 0.0], + "comment" : "The initial velocity should be at least 2*particleRadius*emitsPerSecond.", + "emitsPerSecond": 40, + "type": 0 + } + ] +} + + + diff --git a/data/Scenes/Obstacle.json b/data/Scenes/Obstacle.json index a4e4a9c..3577324 100644 --- a/data/Scenes/Obstacle.json +++ b/data/Scenes/Obstacle.json @@ -25,7 +25,7 @@ "vorticityMethod": 1, "viscosity": 0.01, "vorticity": 0.15, - "viscosityOmega": 0.1, + "viscosityOmega": 0.5, "inertiaInverse": 0.5, "viscosityMethod": 1, "surfaceTension": 0.02, -- GitLab