Created
July 21, 2024 01:24
-
-
Save entropylost/75a3ff4e0fae22a27b408968de31c5d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Note about the annotations: | |
/// [BK17] is https://animation.rwth-aachen.de/media/papers/2017-TVCG-ViscousDFSPH.pdf. | |
/// The "boundary paper" or "consistent boundaries" is https://animation.rwth-aachen.de/media/papers/84/2023-VMV-SPH_ConsistentBoundaryHandling.pdf | |
/// The original version of this file is from https://github.com/InteractiveComputerGraphics/SPlisHSPlasH/blob/4c063bed4dbb376e79f59b17391bb2b66e1207b7/SPlisHSPlasH/DFSPH/TimeStepDFSPH.cpp | |
/// Most of my notes start after a `// !!!:` which may be helpful for searching. | |
/// I also remove the _i in many places; if I refer to ρ, it usually means ρ_i, for example, if it doesn't have a different subscript. | |
#include "TimeStepDFSPH.h" | |
#include "SPlisHSPlasH/TimeManager.h" | |
#include "SPlisHSPlasH/SPHKernels.h" | |
#include "SimulationDataDFSPH.h" | |
#include <iostream> | |
#include "Utilities/Timing.h" | |
#include "Utilities/Counting.h" | |
#include "SPlisHSPlasH/Simulation.h" | |
#include "SPlisHSPlasH/BoundaryModel_Akinci2012.h" | |
#include "SPlisHSPlasH/BoundaryModel_Koschier2017.h" | |
#include "SPlisHSPlasH/BoundaryModel_Bender2019.h" | |
using namespace SPH; | |
using namespace std; | |
using namespace GenParam; | |
int TimeStepDFSPH::SOLVER_ITERATIONS_V = -1; | |
int TimeStepDFSPH::MAX_ITERATIONS_V = -1; | |
int TimeStepDFSPH::MAX_ERROR_V = -1; | |
int TimeStepDFSPH::USE_DIVERGENCE_SOLVER = -1; | |
TimeStepDFSPH::TimeStepDFSPH() : TimeStep(), | |
m_simulationData() | |
{ | |
m_simulationData.init(); | |
m_iterationsV = 0; | |
m_enableDivergenceSolver = true; | |
m_maxIterationsV = 100; | |
m_maxErrorV = static_cast<Real>(0.1); | |
// add particle fields - then they can be used for the visualization and export | |
Simulation *sim = Simulation::getCurrent(); | |
const unsigned int nModels = sim->numberOfFluidModels(); | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
// !!!: This is α / ρ * ρ_0^2 normally (the solvers additionally divide it by Δt^2. | |
// (see eq (11) in [BK17] for α; note that α is the entire fraction in the expression, not just the denominator) | |
model->addField({"factor", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real * | |
{ return &m_simulationData.getFactor(fluidModelIndex, i); }}); | |
// !!!: This is ρ* / ρ_0, not ρ* (see equation before eq (12) in [BK17]) | |
model->addField({"advected density", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real * | |
{ return &m_simulationData.getDensityAdv(fluidModelIndex, i); }}); | |
// !!!: This is actually p / ρ^2 * ρ_0 | |
model->addField({"p / rho^2", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real * | |
{ return &m_simulationData.getPressureRho2(fluidModelIndex, i); }, true}); | |
// !!!: Same for this; this is the pressure force used for the velocity divergence solve. | |
model->addField({"p_v / rho^2", FieldType::Scalar, [this, fluidModelIndex](const unsigned int i) -> Real * | |
{ return &m_simulationData.getPressureRho2_V(fluidModelIndex, i); }, true}); | |
// !!!: This is actually accurate - it's the change in v* in line 7 of Algorithm 2 / 3 in [Bk17]. | |
// It's also a^p in equation (9) of the boundary paper. | |
model->addField({"pressure acceleration", FieldType::Vector3, [this, fluidModelIndex](const unsigned int i) -> Real * | |
{ return &m_simulationData.getPressureAccel(fluidModelIndex, i)[0]; }}); | |
} | |
} | |
TimeStepDFSPH::~TimeStepDFSPH(void) | |
{ | |
// remove all particle fields | |
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("p / rho^2"); | |
model->removeFieldByName("p_v / rho^2"); | |
model->removeFieldByName("pressure acceleration"); | |
} | |
} | |
void TimeStepDFSPH::initParameters() | |
{ | |
TimeStep::initParameters(); | |
SOLVER_ITERATIONS_V = createNumericParameter("iterationsV", "Iterations (divergence)", &m_iterationsV); | |
setGroup(SOLVER_ITERATIONS_V, "Simulation|DFSPH"); | |
setDescription(SOLVER_ITERATIONS_V, "Iterations required by the divergence solver."); | |
getParameter(SOLVER_ITERATIONS_V)->setReadOnly(true); | |
MAX_ITERATIONS_V = createNumericParameter("maxIterationsV", "Max. iterations (divergence)", &m_maxIterationsV); | |
setGroup(MAX_ITERATIONS_V, "Simulation|DFSPH"); | |
setDescription(MAX_ITERATIONS_V, "Maximal number of iterations of the divergence solver."); | |
static_cast<NumericParameter<unsigned int> *>(getParameter(MAX_ITERATIONS_V))->setMinValue(1); | |
MAX_ERROR_V = createNumericParameter("maxErrorV", "Max. divergence error(%)", &m_maxErrorV); | |
setGroup(MAX_ERROR_V, "Simulation|DFSPH"); | |
setDescription(MAX_ERROR_V, "Maximal divergence error (%)."); | |
static_cast<RealParameter *>(getParameter(MAX_ERROR_V))->setMinValue(static_cast<Real>(1e-6)); | |
USE_DIVERGENCE_SOLVER = createBoolParameter("enableDivergenceSolver", "Enable divergence solver", &m_enableDivergenceSolver); | |
setGroup(USE_DIVERGENCE_SOLVER, "Simulation|DFSPH"); | |
setDescription(USE_DIVERGENCE_SOLVER, "Turn divergence solver on/off."); | |
} | |
void TimeStepDFSPH::step() | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
TimeManager *tm = TimeManager::getCurrent(); | |
const Real h = tm->getTimeStepSize(); | |
const unsigned int nModels = sim->numberOfFluidModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// search the neighbors for all particles | |
////////////////////////////////////////////////////////////////////////// | |
sim->performNeighborhoodSearch(); | |
#ifdef USE_PERFORMANCE_OPTIMIZATION | |
////////////////////////////////////////////////////////////////////////// | |
// precompute the values V_j * grad W_ij for all neighbors | |
////////////////////////////////////////////////////////////////////////// | |
START_TIMING("precomputeValues") | |
precomputeValues(); | |
STOP_TIMING_AVG | |
#endif | |
////////////////////////////////////////////////////////////////////////// | |
// compute volume/density maps boundary contribution | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
computeVolumeAndBoundaryX(); | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
computeDensityAndGradient(); | |
////////////////////////////////////////////////////////////////////////// | |
// compute densities | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) | |
computeDensities(fluidModelIndex); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute the factor alpha_i for all particles i | |
// using the equation (11) in [BK17] | |
////////////////////////////////////////////////////////////////////////// | |
START_TIMING("computeDFSPHFactor"); | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) | |
computeDFSPHFactor(fluidModelIndex); | |
STOP_TIMING_AVG; | |
////////////////////////////////////////////////////////////////////////// | |
// Perform divergence solve (see Algorithm 2 in [BK17]) | |
////////////////////////////////////////////////////////////////////////// | |
if (m_enableDivergenceSolver) | |
{ | |
START_TIMING("divergenceSolve"); | |
divergenceSolve(); | |
STOP_TIMING_AVG | |
} | |
else | |
m_iterationsV = 0; | |
////////////////////////////////////////////////////////////////////////// | |
// Reset accelerations and add gravity | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++) | |
clearAccelerations(fluidModelIndex); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute all nonpressure forces like viscosity, vorticity, ... | |
////////////////////////////////////////////////////////////////////////// | |
sim->computeNonPressureForces(); | |
////////////////////////////////////////////////////////////////////////// | |
// Update the time step size, e.g. by using a CFL condition | |
////////////////////////////////////////////////////////////////////////// | |
sim->updateTimeStepSize(); | |
////////////////////////////////////////////////////////////////////////// | |
// compute new velocities only considering non-pressure forces | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int m = 0; m < nModels; m++) | |
{ | |
FluidModel *fm = sim->getFluidModel(m); | |
const unsigned int numParticles = fm->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < (int)numParticles; i++) | |
{ | |
if (fm->getParticleState(i) == ParticleState::Active) | |
{ | |
Vector3r &vel = fm->getVelocity(i); | |
vel += h * fm->getAcceleration(i); | |
} | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Perform constant density solve (see Algorithm 3 in [BK17]) | |
////////////////////////////////////////////////////////////////////////// | |
START_TIMING("pressureSolve"); | |
pressureSolve(); | |
STOP_TIMING_AVG; | |
////////////////////////////////////////////////////////////////////////// | |
// compute final positions | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int m = 0; m < nModels; m++) | |
{ | |
FluidModel *fm = sim->getFluidModel(m); | |
const unsigned int numParticles = fm->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < (int)numParticles; i++) | |
{ | |
if (fm->getParticleState(i) == ParticleState::Active) | |
{ | |
Vector3r &xi = fm->getPosition(i); | |
const Vector3r &vi = fm->getVelocity(i); | |
xi += h * vi; | |
} | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// emit new particles and perform an animation field step | |
////////////////////////////////////////////////////////////////////////// | |
sim->emitParticles(); | |
sim->animateParticles(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute new time | |
////////////////////////////////////////////////////////////////////////// | |
tm->setTime(tm->getTime() + h); | |
} | |
void TimeStepDFSPH::pressureSolve() | |
{ | |
const Real h = TimeManager::getCurrent()->getTimeStepSize(); | |
const Real h2 = h * h; | |
const Real invH = static_cast<Real>(1.0) / h; | |
const Real invH2 = static_cast<Real>(1.0) / h2; | |
Simulation *sim = Simulation::getCurrent(); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute rho_adv | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Real density0 = model->getDensity0(); | |
const int numParticles = (int)model->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute rho_adv,i^(0) (see equation in Section 3.3 in [BK17]) | |
// using the velocities after the non-pressure forces were applied. | |
////////////////////////////////////////////////////////////////////////// | |
computeDensityAdv(fluidModelIndex, i, h, density0); | |
////////////////////////////////////////////////////////////////////////// | |
// In the end of Section 3.3 [BK17] we have to multiply the density | |
// error with the factor alpha_i divided by h^2. Hence, we multiply | |
// the factor directly by 1/h^2 here. | |
////////////////////////////////////////////////////////////////////////// | |
m_simulationData.getFactor(fluidModelIndex, i) *= invH2; | |
// !!!: ===================== | |
// factor is 1 / Δt^2 * α / ρ * ρ_0^2 | |
////////////////////////////////////////////////////////////////////////// | |
// For the warm start we use 0.5 times the old pressure value. | |
// Note: We divide the value by h^2 since we multiplied it by h^2 at the end of | |
// the last time step to make it independent of the time step size. | |
////////////////////////////////////////////////////////////////////////// | |
#ifdef USE_WARMSTART | |
if (m_simulationData.getDensityAdv(fluidModelIndex, i) > 1.0) | |
m_simulationData.getPressureRho2(fluidModelIndex, i) = static_cast<Real>(0.5) * min(m_simulationData.getPressureRho2(fluidModelIndex, i), static_cast<Real>(0.00025)) * invH2; | |
else | |
m_simulationData.getPressureRho2(fluidModelIndex, i) = 0.0; | |
#else | |
////////////////////////////////////////////////////////////////////////// | |
// If we don't use a warm start, we directly compute a pressure value | |
// by multiplying the density error with the factor. | |
////////////////////////////////////////////////////////////////////////// | |
// m_simulationData.getPressureRho2(fluidModelIndex, i) = 0.0; | |
const Real s_i = static_cast<Real>(1.0) - m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Real residuum = min(s_i, static_cast<Real>(0.0)); // r = b - A*p | |
m_simulationData.getPressureRho2(fluidModelIndex, i) = -residuum * m_simulationData.getFactor(fluidModelIndex, i); | |
#endif | |
// !!!: ===================== | |
// pressureRho2 = -(1 - densityAdv) * factor | |
// = -(1 - ρ* / ρ_0) / Δt^2 * α / ρ * ρ_0^2 | |
// = 1 / A_ii * (1 - ρ* / ρ_0) / Δt^2 * α / ρ * ρ_0^2 * ∆t / (ρ * α) | |
// = 1 / A_ii * (p_0 - ρ*) / Δt * p_0 / ρ^2 | |
// = s_i / A_ii * p_0 / ρ^2 | |
// Which is accurate since pressureRho2 has an implicit p_0 multiplier for some inane reason. | |
// Use equation (11) in consistent boundaries: | |
// A_ii = - ∆t / ρ^2 ([bottom of α in (11) [BK17]]) | |
// = - ∆t / ρ^2 * ρ / α = - ∆t / (ρ * α) | |
// p_i := p_i + ω / A_ii (s_i - ∑ A_ij * p_j), where ω = 0.5 | |
// := p_i - ω * ρ^2 / ∆t * α / ρ (s_i - ∑ A_ij * p_j) | |
// Then, | |
// p_i / ρ^2 := p_i / ρ^2 - ω / ∆t * α / ρ (s_i - ∑ A_ij * p_j) | |
} | |
} | |
} | |
m_iterations = 0; | |
////////////////////////////////////////////////////////////////////////// | |
// Start solver | |
////////////////////////////////////////////////////////////////////////// | |
Real avg_density_err = 0.0; | |
bool chk = false; | |
////////////////////////////////////////////////////////////////////////// | |
// Perform solver iterations | |
////////////////////////////////////////////////////////////////////////// | |
while ((!chk || (m_iterations < m_minIterations)) && (m_iterations < m_maxIterations)) | |
{ | |
chk = true; | |
for (unsigned int i = 0; i < nFluids; i++) | |
{ | |
FluidModel *model = sim->getFluidModel(i); | |
const Real density0 = model->getDensity0(); | |
avg_density_err = 0.0; | |
pressureSolveIteration(i, avg_density_err); | |
// Maximal allowed density fluctuation | |
const Real eta = m_maxError * static_cast<Real>(0.01) * density0; // maxError is given in percent | |
chk = chk && (avg_density_err <= eta); | |
} | |
m_iterations++; | |
} | |
INCREASE_COUNTER("DFSPH - iterations", static_cast<Real>(m_iterations)); | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
const Real density0 = model->getDensity0(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Time integration of the pressure accelerations to get new velocities | |
////////////////////////////////////////////////////////////////////////// | |
computePressureAccel(fluidModelIndex, i, density0, m_simulationData.getPressureRho2Data(), true); | |
model->getVelocity(i) += h * m_simulationData.getPressureAccel(fluidModelIndex, i); | |
} | |
} | |
} | |
#ifdef USE_WARMSTART | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Multiply by h^2, the time step size has to be removed | |
// to make the pressure value independent | |
// of the time step size | |
////////////////////////////////////////////////////////////////////////// | |
m_simulationData.getPressureRho2(fluidModelIndex, i) *= h2; | |
} | |
} | |
} | |
#endif | |
} | |
void TimeStepDFSPH::divergenceSolve() | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Init parameters | |
////////////////////////////////////////////////////////////////////////// | |
const Real h = TimeManager::getCurrent()->getTimeStepSize(); | |
const Real invH = static_cast<Real>(1.0) / h; | |
Simulation *sim = Simulation::getCurrent(); | |
const unsigned int maxIter = m_maxIterationsV; | |
const Real maxError = m_maxErrorV; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute divergence of velocity field | |
////////////////////////////////////////////////////////////////////////// | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute rho_adv,i^(0) (see equation (9) in Section 3.2 [BK17]) | |
// using the velocities after the non-pressure forces were applied. | |
////////////////////////////////////////////////////////////////////////// | |
computeDensityChange(fluidModelIndex, i, h); | |
Real densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
densityAdv = max(densityAdv, static_cast<Real>(0.0)); | |
unsigned int numNeighbors = 0; | |
for (unsigned int pid = 0; pid < sim->numberOfPointSets(); pid++) | |
numNeighbors += sim->numberOfNeighbors(fluidModelIndex, pid, i); | |
// in case of particle deficiency do not perform a divergence solve | |
if (!sim->is2DSimulation()) | |
{ | |
if (numNeighbors < 20) | |
densityAdv = 0.0; | |
} | |
else | |
{ | |
if (numNeighbors < 7) | |
densityAdv = 0.0; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// In equation (11) [BK17] we have to multiply the divergence | |
// error with the factor divided by h. Hence, we multiply the factor | |
// directly by 1/h here. | |
////////////////////////////////////////////////////////////////////////// | |
m_simulationData.getFactor(fluidModelIndex, i) *= invH; | |
////////////////////////////////////////////////////////////////////////// | |
// For the warm start we use 0.5 times the old pressure value. | |
// Divide the value by h. We multiplied it by h at the end of | |
// the last time step to make it independent of the time step size. | |
////////////////////////////////////////////////////////////////////////// | |
#ifdef USE_WARMSTART_V | |
if (densityAdv > 0.0) | |
m_simulationData.getPressureRho2_V(fluidModelIndex, i) = static_cast<Real>(0.5) * min(m_simulationData.getPressureRho2_V(fluidModelIndex, i), static_cast<Real>(0.5)) * invH; | |
else | |
m_simulationData.getPressureRho2_V(fluidModelIndex, i) = 0.0; | |
#else | |
////////////////////////////////////////////////////////////////////////// | |
// If we don't use a warm start, directly compute a pressure value | |
// by multiplying the divergence error with the factor. | |
////////////////////////////////////////////////////////////////////////// | |
m_simulationData.getPressureRho2_V(fluidModelIndex, i) = densityAdv * m_simulationData.getFactor(fluidModelIndex, i); | |
#endif | |
} | |
} | |
} | |
m_iterationsV = 0; | |
////////////////////////////////////////////////////////////////////////// | |
// Start solver | |
////////////////////////////////////////////////////////////////////////// | |
Real avg_density_err = 0.0; | |
bool chk = false; | |
////////////////////////////////////////////////////////////////////////// | |
// Perform solver iterations | |
////////////////////////////////////////////////////////////////////////// | |
while ((!chk || (m_iterationsV < 1)) && (m_iterationsV < maxIter)) | |
{ | |
chk = true; | |
for (unsigned int i = 0; i < nFluids; i++) | |
{ | |
FluidModel *model = sim->getFluidModel(i); | |
const Real density0 = model->getDensity0(); | |
avg_density_err = 0.0; | |
divergenceSolveIteration(i, avg_density_err); | |
// Maximal allowed density fluctuation | |
// use maximal density error divided by time step size | |
const Real eta = (static_cast<Real>(1.0) / h) * maxError * static_cast<Real>(0.01) * density0; // maxError is given in percent | |
chk = chk && (avg_density_err <= eta); | |
} | |
m_iterationsV++; | |
} | |
INCREASE_COUNTER("DFSPH - iterationsV", static_cast<Real>(m_iterationsV)); | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
const Real density0 = model->getDensity0(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Time integration of the pressure accelerations | |
////////////////////////////////////////////////////////////////////////// | |
computePressureAccel(fluidModelIndex, i, density0, m_simulationData.getPressureRho2VData(), true); | |
model->getVelocity(i) += h * m_simulationData.getPressureAccel(fluidModelIndex, i); | |
m_simulationData.getFactor(fluidModelIndex, i) *= h; | |
} | |
} | |
} | |
#ifdef USE_WARMSTART_V | |
for (unsigned int fluidModelIndex = 0; fluidModelIndex < nFluids; fluidModelIndex++) | |
{ | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Multiply by h, the time step size has to be removed | |
// to make the pressure value independent | |
// of the time step size | |
////////////////////////////////////////////////////////////////////////// | |
m_simulationData.getPressureRho2_V(fluidModelIndex, i) *= h; | |
} | |
} | |
} | |
#endif | |
} | |
void TimeStepDFSPH::pressureSolveIteration(const unsigned int fluidModelIndex, Real &avg_density_err) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Real density0 = model->getDensity0(); | |
const int numParticles = (int)model->numActiveParticles(); | |
if (numParticles == 0) | |
return; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
const Real h = TimeManager::getCurrent()->getTimeStepSize(); | |
const Real invH = static_cast<Real>(1.0) / h; | |
Real density_error = 0.0; | |
#pragma omp parallel default(shared) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute pressure accelerations using the current pressure values. | |
// (see Algorithm 3, line 7 in [BK17]) | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
computePressureAccel(fluidModelIndex, i, density0, m_simulationData.getPressureRho2Data()); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Update pressure values | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for reduction(+ : density_error) schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
if (model->getParticleState(i) != ParticleState::Active) | |
continue; | |
Real aij_pj = compute_aij_pj(fluidModelIndex, i); | |
aij_pj *= h * h; | |
// aij_pj = Δt Σ A_ij p_j / ρ_0 | |
////////////////////////////////////////////////////////////////////////// | |
// Compute source term: s_i = 1 - rho_adv | |
// Note: that due to our multiphase handling, the multiplier rho0 | |
// is missing here | |
////////////////////////////////////////////////////////////////////////// | |
const Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Real s_i = static_cast<Real>(1.0) - densityAdv; | |
// This "s_i" is 1 - ρ* / ρ_0 = s_i / ρ_0 * ∆t | |
////////////////////////////////////////////////////////////////////////// | |
// Update the value p/rho^2 (in [BK17] this is kappa/rho): | |
// | |
// alpha_i = -1 / (a_ii * rho_i^2) | |
// p_rho2_i = (p_i / rho_i^2) | |
// | |
// Therefore, the following lines compute the Jacobi iteration: | |
// p_i := p_i + 1/a_ii (source_term_i - a_ij * p_j) | |
////////////////////////////////////////////////////////////////////////// | |
Real &p_rho2_i = m_simulationData.getPressureRho2(fluidModelIndex, i); | |
const Real residuum = min(s_i - aij_pj, static_cast<Real>(0.0)); // r = b - A*p | |
// p_rho2_i -= residuum * m_simulationData.getFactor(fluidModelIndex, i); | |
// !!!: ===================== | |
// Using eq (11) in the boundary paper: | |
// p_i := p_i + ω / A_ii (s_i - ∑ A_ij * p_j), where ω = 0.5 | |
// := p_i - ω * ρ^2 / ∆t * α / ρ (s_i - ∑ A_ij * p_j) | |
// factor is 1 / Δt^2 * α / ρ * ρ_0^2 | |
// | |
// p_i / ρ^2 := p_i / ρ^2 - ω / ∆t * α / ρ (s_i - ∑ A_ij * p_j) | |
// p_i * ρ_0 / ρ^2 := p_i * ρ_0 / ρ^2 - ω / ∆t * α / ρ (s_i - ∑ A_ij * p_j) * ρ_0 | |
// | |
// p_rho2_i := p_rho2_i - ω * ("s_i" - aij_pj) * factor | |
// := p_rho2_i - ω * (s_i / ρ_0 * ∆t - Δt Σ A_ij p_j / ρ_0) * 1 / Δt^2 * α / ρ * ρ_0^2 | |
// := p_rho2_i - ω / Δt * α / ρ (s_i - Σ A_ij p_j) * ρ_0 | |
// | |
// Which matches with the above equation. | |
p_rho2_i = max(p_rho2_i - 0.5 * (s_i - aij_pj) * m_simulationData.getFactor(fluidModelIndex, i), 0.0); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute the sum of the density errors | |
////////////////////////////////////////////////////////////////////////// | |
density_error -= density0 * residuum; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Compute the average density error | |
////////////////////////////////////////////////////////////////////////// | |
avg_density_err = density_error / numParticles; | |
} | |
void TimeStepDFSPH::divergenceSolveIteration(const unsigned int fluidModelIndex, Real &avg_density_err) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Real density0 = model->getDensity0(); | |
const int numParticles = (int)model->numActiveParticles(); | |
if (numParticles == 0) | |
return; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
const Real h = TimeManager::getCurrent()->getTimeStepSize(); | |
const Real invH = static_cast<Real>(1.0) / h; | |
Real density_error = 0.0; | |
#pragma omp parallel default(shared) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute pressure accelerations using the current pressure values. | |
// (see Algorithm 2, line 7 in [BK17]) | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for schedule(static) | |
for (int i = 0; i < (int)numParticles; i++) | |
{ | |
computePressureAccel(fluidModelIndex, i, density0, m_simulationData.getPressureRho2VData()); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Update pressure | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for reduction(+ : density_error) schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
Real aij_pj = compute_aij_pj(fluidModelIndex, i); | |
aij_pj *= h; | |
////////////////////////////////////////////////////////////////////////// | |
// Compute source term: s_i = -d rho / dt | |
////////////////////////////////////////////////////////////////////////// | |
const Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Real s_i = -densityAdv; | |
////////////////////////////////////////////////////////////////////////// | |
// Update the value p/rho^2: | |
// | |
// alpha_i = -1 / (a_ii * rho_i^2) | |
// pv_rho2_i = (pv_i / rho_i^2) | |
// | |
// Therefore, the following line computes the Jacobi iteration: | |
// pv_i := pv_i + 1/a_ii (source_term_i - a_ij * pv_j) | |
////////////////////////////////////////////////////////////////////////// | |
Real &pv_rho2_i = m_simulationData.getPressureRho2_V(fluidModelIndex, i); | |
Real residuum = min(s_i - aij_pj, static_cast<Real>(0.0)); // r = b - A*p | |
unsigned int numNeighbors = 0; | |
for (unsigned int pid = 0; pid < sim->numberOfPointSets(); pid++) | |
numNeighbors += sim->numberOfNeighbors(fluidModelIndex, pid, i); | |
// in case of particle deficiency do not perform a divergence solve | |
if (!sim->is2DSimulation()) | |
{ | |
if (numNeighbors < 20) | |
residuum = 0.0; | |
} | |
else | |
{ | |
if (numNeighbors < 7) | |
residuum = 0.0; | |
} | |
// pv_rho2_i -= residuum * m_simulationData.getFactor(fluidModelIndex, i); | |
pv_rho2_i = max(pv_rho2_i - 0.5 * (s_i - aij_pj) * m_simulationData.getFactor(fluidModelIndex, i), 0.0); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute the sum of the divergence errors | |
////////////////////////////////////////////////////////////////////////// | |
density_error -= density0 * residuum; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// Compute the average divergence error | |
////////////////////////////////////////////////////////////////////////// | |
avg_density_err = density_error / numParticles; | |
} | |
void TimeStepDFSPH::reset() | |
{ | |
TimeStep::reset(); | |
m_simulationData.reset(); | |
m_iterations = 0; | |
m_iterationsV = 0; | |
} | |
void TimeStepDFSPH::performNeighborhoodSearchSort() | |
{ | |
m_simulationData.performNeighborhoodSearchSort(); | |
} | |
void TimeStepDFSPH::emittedParticles(FluidModel *model, const unsigned int startIndex) | |
{ | |
m_simulationData.emittedParticles(model, startIndex); | |
} | |
void TimeStepDFSPH::resize() | |
{ | |
m_simulationData.init(); | |
} | |
#ifdef USE_AVX | |
void TimeStepDFSPH::computeDFSPHFactor(const unsigned int fluidModelIndex) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Init parameters | |
////////////////////////////////////////////////////////////////////////// | |
Simulation *sim = Simulation::getCurrent(); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
#pragma omp parallel default(shared) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute pressure stiffness denominator | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute gradient dp_i/dx_j * (1/kappa) and dp_j/dx_j * (1/kappa) | |
// (see Equation (8) and the previous one [BK17]) | |
// Note: That in all quantities rho0 is missing due to our | |
// implementation of multiphase simulations. | |
////////////////////////////////////////////////////////////////////////// | |
const Vector3r &xi = model->getPosition(i); | |
Real sum_grad_p_k; | |
Vector3r grad_p_i; | |
Vector3f8 xi_avx(xi); | |
Scalarf8 sum_grad_p_k_avx(0.0f); | |
Vector3f8 grad_p_i_avx; | |
grad_p_i_avx.setZero(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors_avx_nox( | |
compute_xj(fm_neighbor, pid); | |
compute_Vj(fm_neighbor); | |
compute_Vj_gradW(); | |
sum_grad_p_k_avx += V_gradW.squaredNorm(); | |
grad_p_i_avx += V_gradW;); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors_avx( | |
const Scalarf8 V_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVolume(0), count); | |
const Vector3f8 grad_p_j = CubicKernel_AVX::gradW(xj_avx - xi_avx) * V_avx; | |
grad_p_i_avx -= grad_p_j;); | |
} | |
sum_grad_p_k = sum_grad_p_k_avx.reduce(); | |
grad_p_i[0] = grad_p_i_avx.x().reduce(); | |
grad_p_i[1] = grad_p_i_avx.y().reduce(); | |
grad_p_i[2] = grad_p_i_avx.z().reduce(); | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
grad_p_i -= gradRho;); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
const Vector3r grad_p_j = -Vj * sim->gradW(xi - xj); | |
grad_p_i -= grad_p_j;); | |
} | |
sum_grad_p_k += grad_p_i.squaredNorm(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute factor alpha_i / rho_i (see Equation (11) in [BK17]) | |
////////////////////////////////////////////////////////////////////////// | |
Real &factor = m_simulationData.getFactor(fluidModelIndex, i); | |
if (sum_grad_p_k > m_eps) | |
factor = static_cast<Real>(1.0) / (sum_grad_p_k); | |
else | |
factor = 0.0; | |
} | |
} | |
} | |
/** Compute rho_adv,i^(0) (see equation in Section 3.3 in [BK17]) | |
* using the velocities after the non-pressure forces were applied. | |
**/ | |
void TimeStepDFSPH::computeDensityAdv(const unsigned int fluidModelIndex, const unsigned int i, const Real h, const Real density0) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Real &density = model->getDensity(i); | |
Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &vi = model->getVelocity(i); | |
Real delta = 0.0; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
Scalarf8 delta_avx(0.0f); | |
const Vector3f8 xi_avx(xi); | |
Vector3f8 vi_avx(vi); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors_avx_nox( | |
compute_xj(fm_neighbor, pid); | |
compute_Vj(fm_neighbor); | |
compute_Vj_gradW(); | |
const Vector3f8 vj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &fm_neighbor->getVelocity(0), count); | |
delta_avx += (vi_avx - vj_avx).dot(V_gradW);); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors_avx( | |
const Scalarf8 Vj_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVolume(0), count); | |
const Vector3f8 vj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVelocity(0), count); | |
delta_avx += Vj_avx * (vi_avx - vj_avx).dot(CubicKernel_AVX::gradW(xi_avx - xj_avx));); | |
} | |
delta = delta_avx.reduce(); | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xi, vj); | |
delta -= (vi - vj).dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xj, vj); | |
delta += Vj * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
densityAdv = density / density0 + h * delta; | |
} | |
/** Compute rho_adv,i^(0) (see equation (9) in Section 3.2 [BK17]) | |
* using the velocities after the non-pressure forces were applied. | |
*/ | |
void TimeStepDFSPH::computeDensityChange(const unsigned int fluidModelIndex, const unsigned int i, const Real h) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &vi = model->getVelocity(i); | |
unsigned int numNeighbors = 0; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
Scalarf8 densityAdv_avx(0.0f); | |
const Vector3f8 xi_avx(xi); | |
Vector3f8 vi_avx(vi); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors_avx_nox( | |
compute_xj(fm_neighbor, pid); | |
compute_Vj(fm_neighbor); | |
compute_Vj_gradW(); | |
const Vector3f8 vj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &fm_neighbor->getVelocity(0), count); | |
densityAdv_avx += (vi_avx - vj_avx).dot(V_gradW);); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors_avx( | |
const Scalarf8 Vj_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVolume(0), count); | |
const Vector3f8 vj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVelocity(0), count); | |
densityAdv_avx += Vj_avx * (vi_avx - vj_avx).dot(CubicKernel_AVX::gradW(xi_avx - xj_avx));); | |
} | |
Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
densityAdv = densityAdv_avx.reduce(); | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xi, vj); | |
densityAdv -= (vi - vj).dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xj, vj); | |
densityAdv += Vj * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
} | |
/** Compute pressure accelerations using the current pressure values of the particles | |
*/ | |
void TimeStepDFSPH::computePressureAccel(const unsigned int fluidModelIndex, const unsigned int i, const Real density0, std::vector<std::vector<Real>> &pressure_rho2, const bool applyBoundaryForces) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
Vector3r &ai = m_simulationData.getPressureAccel(fluidModelIndex, i); | |
if (model->getParticleState(i) != ParticleState::Active) | |
return; | |
// p_rho2_i = (p_i / rho_i^2) | |
const Real p_rho2_i = pressure_rho2[fluidModelIndex][i]; | |
const Vector3r &xi = model->getPosition(i); | |
Scalarf8 p_rho2_i_avx(p_rho2_i); | |
const Vector3f8 xi_avx(xi); | |
Vector3f8 delta_ai_avx; | |
delta_ai_avx.setZero(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors_avx_nox( | |
compute_xj(fm_neighbor, pid); | |
compute_Vj(fm_neighbor); | |
compute_Vj_gradW(); | |
const Scalarf8 densityFrac_avx(fm_neighbor->getDensity0() / density0); | |
// p_rho2_j = (p_j / rho_j^2) | |
const Scalarf8 p_rho2_j_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &pressure_rho2[pid][0], count); | |
const Scalarf8 pSum = p_rho2_i_avx + densityFrac_avx * p_rho2_j_avx; | |
delta_ai_avx -= V_gradW * pSum;) | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (fabs(p_rho2_i) > m_eps) | |
{ | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
const Scalarf8 mi_avx(model->getMass(i)); | |
forall_boundary_neighbors_avx( | |
const Scalarf8 Vj_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVolume(0), count); | |
// Directly update velocities instead of storing pressure accelerations | |
const Vector3f8 a = -CubicKernel_AVX::gradW(xi_avx - xj_avx) * (Vj_avx * p_rho2_i_avx); | |
delta_ai_avx += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj_avx, -a * mi_avx, count);); | |
} | |
} | |
ai[0] = delta_ai_avx.x().reduce(); | |
ai[1] = delta_ai_avx.y().reduce(); | |
ai[2] = delta_ai_avx.z().reduce(); | |
if (fabs(p_rho2_i) > m_eps) | |
{ | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
const Vector3r a = (Real)1.0 * p_rho2_i * gradRho; | |
ai += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj, -model->getMass(i) * a);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
const Vector3r grad_p_j = -Vj * sim->gradW(xi - xj); | |
const Vector3r a = (Real)1.0 * p_rho2_i * grad_p_j; | |
ai += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj, -model->getMass(i) * a);); | |
} | |
} | |
} | |
Real TimeStepDFSPH::compute_aij_pj(const unsigned int fluidModelIndex, const unsigned int i) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute A*p which is the change of the density when applying the | |
// pressure forces. | |
// \sum_j a_ij * p_j = h^2 \sum_j V_j (a_i - a_j) * gradW_ij | |
// This is the RHS of Equation (12) in [BK17] | |
////////////////////////////////////////////////////////////////////////// | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &ai = m_simulationData.getPressureAccel(fluidModelIndex, i); | |
const Vector3f8 xi_avx(xi); | |
const Vector3f8 ai_avx(ai); | |
Scalarf8 aij_pj_avx; | |
aij_pj_avx.setZero(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors_avx_nox( | |
compute_xj(fm_neighbor, pid); | |
compute_Vj(fm_neighbor); | |
compute_Vj_gradW(); | |
const Vector3f8 aj_avx = convertVec_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &m_simulationData.getPressureAccel(pid, 0), count); | |
aij_pj_avx += (ai_avx - aj_avx).dot(V_gradW);); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors_avx( | |
const Scalarf8 Vj_avx = convert_zero(&sim->getNeighborList(fluidModelIndex, pid, i)[j], &bm_neighbor->getVolume(0), count); | |
aij_pj_avx += Vj_avx * ai_avx.dot(CubicKernel_AVX::gradW(xi_avx - xj_avx));); | |
} | |
Real aij_pj = aij_pj_avx.reduce(); | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
aij_pj -= ai.dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
aij_pj += Vj * ai.dot(sim->gradW(xi - xj));); | |
} | |
return aij_pj; | |
} | |
#else | |
void TimeStepDFSPH::computeDFSPHFactor(const unsigned int fluidModelIndex) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Init parameters | |
////////////////////////////////////////////////////////////////////////// | |
Simulation *sim = Simulation::getCurrent(); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const int numParticles = (int)model->numActiveParticles(); | |
#pragma omp parallel default(shared) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute pressure stiffness denominator | |
////////////////////////////////////////////////////////////////////////// | |
#pragma omp for schedule(static) | |
for (int i = 0; i < numParticles; i++) | |
{ | |
////////////////////////////////////////////////////////////////////////// | |
// Compute gradient dp_i/dx_j * (1/kappa) and dp_j/dx_j * (1/kappa) | |
// (see Equation (8) and the previous one [BK17]) | |
// Note: That in all quantities rho0 is missing due to our | |
// implementation of multiphase simulations. | |
////////////////////////////////////////////////////////////////////////// | |
const Vector3r &xi = model->getPosition(i); | |
Real sum_grad_p_k = 0.0; | |
Vector3r grad_p_i; | |
grad_p_i.setZero(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
// !!!: ===================== | |
// grad_p_i = Σ V * ∇W_ij | |
// sum_grad_p_k = Σ V^2 * |∇W_ij|^2 | |
forall_fluid_neighbors( | |
const Vector3r grad_p_j = -fm_neighbor->getVolume(neighborIndex) * sim->gradW(xi - xj); | |
sum_grad_p_k += grad_p_j.squaredNorm(); | |
grad_p_i -= grad_p_j;); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors( | |
const Vector3r grad_p_j = -bm_neighbor->getVolume(neighborIndex) * sim->gradW(xi - xj); | |
grad_p_i -= grad_p_j;); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
grad_p_i -= gradRho;); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
const Vector3r grad_p_j = -Vj * sim->gradW(xi - xj); | |
grad_p_i -= grad_p_j;); | |
} | |
sum_grad_p_k += grad_p_i.squaredNorm(); | |
// sum_grad_p_k = bottom of equation 11 * V^2 / m^2 = bottom / ρ_0^2 | |
////////////////////////////////////////////////////////////////////////// | |
// Compute factor as: factor_i = -1 / (a_ii * rho_i^2) | |
// where a_ii is the diagonal entry of the linear system | |
// for the pressure A * p = source term | |
////////////////////////////////////////////////////////////////////////// | |
Real &factor = m_simulationData.getFactor(fluidModelIndex, i); | |
if (sum_grad_p_k > m_eps) | |
factor = static_cast<Real>(1.0) / (sum_grad_p_k); | |
// !!!: ===================== | |
// factor = α / ρ * ρ_0^2 ≊ α * ρ_0 (assuming that the density is at rest) | |
else | |
factor = 0.0; | |
} | |
} | |
} | |
/** Compute rho_adv,i^(0) (see equation in Section 3.3 in [BK17]) | |
* using the velocities after the non-pressure forces were applied. | |
**/ | |
void TimeStepDFSPH::computeDensityAdv(const unsigned int fluidModelIndex, const unsigned int i, const Real h, const Real density0) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const Real &density = model->getDensity(i); | |
Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &vi = model->getVelocity(i); | |
Real delta = 0.0; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors( | |
const Vector3r &vj = fm_neighbor->getVelocity(neighborIndex); | |
delta += (vi - vj).dot(sim->gradW(xi - xj)); | |
// delta += fm_neighbor->getVolume(neighborIndex) * (vi - vj).dot(sim->gradW(xi - xj)); | |
); | |
// assumes that all fluid particles have the same volume | |
delta *= model->getVolume(i); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors( | |
const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); | |
delta += bm_neighbor->getVolume(neighborIndex) * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xi, vj); | |
delta -= (vi - vj).dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xj, vj); | |
delta += Vj * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
densityAdv = density / density0 + h * delta; | |
// !!!: ===================== | |
// densityAdv = ρ / ρ_0 + ∆t * ∑ V (v_i - v_j) * ∇W_ij = V / m * (ρ + ∆t * ∑ m (v_i - v_j) * ∇W_ij) = ρ* / ρ_0 | |
// Use the one before equation (12) in [BK17]. | |
} | |
/** Compute rho_adv,i^(0) (see equation (9) in Section 3.2 [BK17]) | |
* using the velocities after the non-pressure forces were applied. | |
*/ | |
void TimeStepDFSPH::computeDensityChange(const unsigned int fluidModelIndex, const unsigned int i, const Real h) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
Real &densityAdv = m_simulationData.getDensityAdv(fluidModelIndex, i); | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &vi = model->getVelocity(i); | |
densityAdv = 0.0; | |
unsigned int numNeighbors = 0; | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors( | |
const Vector3r &vj = fm_neighbor->getVelocity(neighborIndex); | |
densityAdv += (vi - vj).dot(sim->gradW(xi - xj));); | |
// assumes that all fluid particles have the same volume | |
densityAdv *= model->getVolume(i); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors( | |
const Vector3r &vj = bm_neighbor->getVelocity(neighborIndex); | |
densityAdv += bm_neighbor->getVolume(neighborIndex) * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xi, vj); | |
densityAdv -= (vi - vj).dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
Vector3r vj; | |
bm_neighbor->getPointVelocity(xj, vj); | |
densityAdv += Vj * (vi - vj).dot(sim->gradW(xi - xj));); | |
} | |
} | |
/** Compute pressure accelerations using the current pressure values of the particles | |
*/ | |
void TimeStepDFSPH::computePressureAccel(const unsigned int fluidModelIndex, const unsigned int i, const Real density0, std::vector<std::vector<Real>> &pressure_rho2, const bool applyBoundaryForces) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
Vector3r &ai = m_simulationData.getPressureAccel(fluidModelIndex, i); | |
ai.setZero(); | |
if (model->getParticleState(i) != ParticleState::Active) | |
return; | |
// p_rho2_i = (p_i / rho_i^2) | |
const Real p_rho2_i = pressure_rho2[fluidModelIndex][i]; | |
const Vector3r &xi = model->getPosition(i); | |
// !!!: ===================== | |
// From Algorithm 3, line 7 [BK17]: | |
// κ = (ρ* - ρ_0) / Δt^2 * α | |
// accel = - Δt * Σ m (κ_i / ρ_i + κ_j / ρ_j) * ∇W_ij | |
// | |
// Or just see equation (9) in the boundary paper. | |
// a_i = - Σ m (p_i / ρ_i^2 + p_j / ρ_j^2) * ∇W_ij | |
// accel = - Σ V (p_rho2_i + p_rho2_j) * ∇W_ij | |
// = - Σ m (p_rho2_i + p_rho2_j) * ∇W_ij / ρ_0 | |
// So p_rho2 = m / V * p / ρ^2 = ρ_0 * p / ρ^2 | |
// (unless the acceleration has a multiplier for some reason) | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors( | |
// p_rho2_j = (p_j / rho_j^2) | |
const Real p_rho2_j = pressure_rho2[pid][neighborIndex]; | |
// !!!: | |
// TODO: This density division thing is important! | |
const Real pSum = p_rho2_i + fm_neighbor->getDensity0() / density0 * p_rho2_j; | |
// Literally why? | |
if (fabs(pSum) > m_eps) { | |
const Vector3r grad_p_j = -fm_neighbor->getVolume(neighborIndex) * sim->gradW(xi - xj); | |
ai += pSum * grad_p_j; | |
}) | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (fabs(p_rho2_i) > m_eps) | |
{ | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors( | |
const Vector3r grad_p_j = -bm_neighbor->getVolume(neighborIndex) * sim->gradW(xi - xj); | |
const Vector3r a = (Real)1.0 * p_rho2_i * grad_p_j; | |
ai += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj, -model->getMass(i) * a);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
const Vector3r a = (Real)1.0 * p_rho2_i * gradRho; | |
ai += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj, -model->getMass(i) * a);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
const Vector3r grad_p_j = -Vj * sim->gradW(xi - xj); | |
const Vector3r a = (Real)1.0 * p_rho2_i * grad_p_j; | |
ai += a; | |
if (applyBoundaryForces) | |
bm_neighbor->addForce(xj, -model->getMass(i) * a);); | |
} | |
} | |
} | |
Real TimeStepDFSPH::compute_aij_pj(const unsigned int fluidModelIndex, const unsigned int i) | |
{ | |
Simulation *sim = Simulation::getCurrent(); | |
FluidModel *model = sim->getFluidModel(fluidModelIndex); | |
const unsigned int nFluids = sim->numberOfFluidModels(); | |
const unsigned int nBoundaries = sim->numberOfBoundaryModels(); | |
////////////////////////////////////////////////////////////////////////// | |
// Compute A*p which is the change of the density when applying the | |
// pressure forces. | |
// \sum_j a_ij * p_j = h^2 \sum_j V_j (a_i - a_j) * gradW_ij | |
// This is the RHS of Equation (12) in [BK17] | |
////////////////////////////////////////////////////////////////////////// | |
const Vector3r &xi = model->getPosition(i); | |
const Vector3r &ai = m_simulationData.getPressureAccel(fluidModelIndex, i); | |
Real aij_pj = 0.0; | |
// !!!: ===================== | |
// In the boundary paper after eq (11), we have | |
// Σ A_ij p_j = Δt ∇^2 p_i | |
// = Δt Σ m (a_i - a_j) . ∇W_ij | |
// | |
// aij_pj = Σ V (a_i - a_j) . ∇W_ij | |
// So = Σ A_ij p_j / (Δt ρ_0) | |
// | |
// Or if accel is divided by ρ_0, we have | |
// Σ A_ij p_j / (Δt ρ_0^2), but I'm pretty sure this is wrong (see the pressure solve iteration; the math fails). | |
////////////////////////////////////////////////////////////////////////// | |
// Fluid | |
////////////////////////////////////////////////////////////////////////// | |
forall_fluid_neighbors( | |
const Vector3r &aj = m_simulationData.getPressureAccel(pid, neighborIndex); | |
// aij_pj += fm_neighbor->getVolume(neighborIndex) * (ai - aj).dot(sim->gradW(xi - xj)); | |
aij_pj += (ai - aj).dot(sim->gradW(xi - xj));); | |
// assumes that all fluid particles have the same volume | |
aij_pj *= model->getVolume(i); | |
////////////////////////////////////////////////////////////////////////// | |
// Boundary | |
////////////////////////////////////////////////////////////////////////// | |
if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) | |
{ | |
forall_boundary_neighbors( | |
aij_pj += bm_neighbor->getVolume(neighborIndex) * ai.dot(sim->gradW(xi - xj));); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Koschier2017) | |
{ | |
forall_density_maps( | |
aij_pj -= ai.dot(gradRho);); | |
} | |
else if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Bender2019) | |
{ | |
forall_volume_maps( | |
aij_pj += Vj * ai.dot(sim->gradW(xi - xj));); | |
} | |
return aij_pj; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment