Loading...
Searching...
No Matches
MPMFluidSolver-Impl.hpp
Go to the documentation of this file.
1// This code is based on Jet framework.
2// Copyright (c) 2018 Doyub Kim
3// CubbyFlow is voxel-based fluid simulation engine for computer games.
4// Copyright (c) 2020 CubbyFlow Team
5// Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6// AI Part: Dongheon Cho, Minseo Kim
7// We are making my contributions/submissions to this project solely in our
8// personal capacity and are not conveying any rights to any intellectual
9// property of any third parties.
10
11#ifndef CUBBYFLOW_MPM_FLUID_SOLVER_IMPL_HPP
12#define CUBBYFLOW_MPM_FLUID_SOLVER_IMPL_HPP
13
14#include <algorithm>
15#include <array>
16#include <cmath>
17#include <limits>
18#include <stdexcept>
19
20namespace CubbyFlow
21{
22template <size_t N>
25 const VectorType& gridOrigin, double radius,
26 double mass, double targetDensity,
27 double speedOfSound, double eosExponent,
29 : Base{ radius, mass },
30 m_mpmSystemData{ std::make_shared<MPMFluidSystemData<N>>(
32 m_constitutiveModel{ targetDensity, speedOfSound, eosExponent,
34{
35 if (!std::isfinite(radius) || radius < 0.0 || !std::isfinite(mass) ||
36 mass <= 0.0)
37 {
38 throw std::invalid_argument{ "Invalid MPM fluid particle parameters." };
39 }
40
41 m_mpmSystemData->SetRadius(radius);
42 m_mpmSystemData->SetMass(mass);
43 this->SetParticleSystemData(m_mpmSystemData);
44 this->SetIsUsingFixedSubTimeSteps(false);
45}
46
47template <size_t N>
48std::shared_ptr<MPMFluidSystemData<N>> MPMFluidSolver<N>::GetMPMSystemData()
49 const
50{
51 return m_mpmSystemData;
52}
53
54template <size_t N>
56 const
57{
58 return m_constitutiveModel;
59}
60
61template <size_t N>
63{
64 return m_timeStepLimitScale;
65}
66
67template <size_t N>
69{
70 if (!std::isfinite(newScale) || newScale <= 0.0 || newScale > 1.0)
71 {
72 throw std::invalid_argument{
73 "Time-step limit scale must be in (0, 1]."
74 };
75 }
76
77 m_timeStepLimitScale = newScale;
78}
79
80template <size_t N>
82{
83 return m_closedDomainBoundaryFlag;
84}
85
86template <size_t N>
88{
89 m_closedDomainBoundaryFlag = flag;
90}
91
92template <size_t N>
97
98template <size_t N>
100{
101 Base::OnInitialize();
102 m_mpmSystemData->TransferFromParticlesToGrid();
103 InitializeReferenceVolumes();
104}
105
106template <size_t N>
108 double timeIntervalInSeconds) const
109{
110 if (!std::isfinite(timeIntervalInSeconds) || timeIntervalInSeconds <= 0.0 ||
111 m_mpmSystemData->NumberOfParticles() == 0)
112 {
113 return 1;
114 }
115
116 const auto spacing = m_mpmSystemData->GridMass().GridSpacing();
117 const auto velocities = m_mpmSystemData->Velocities();
118 double minSpacing = spacing[0];
119 double maxSpeed = 0.0;
120
121 for (size_t axis = 1; axis < N; ++axis)
122 {
123 minSpacing = std::min(minSpacing, spacing[axis]);
124 }
125
126 for (const auto& velocity : velocities)
127 {
128 for (double component : velocity)
129 {
130 if (!std::isfinite(component))
131 {
132 throw std::invalid_argument{
133 "Invalid MPM fluid particle velocity."
134 };
135 }
136 }
137
138 const double speed = velocity.Length();
139
140 if (!std::isfinite(speed))
141 {
142 throw std::invalid_argument{
143 "Invalid MPM fluid particle velocity."
144 };
145 }
146
147 maxSpeed = std::max(maxSpeed, speed);
148 }
149
150 const double desiredTimeStep =
151 minSpacing / (m_constitutiveModel.GetSpeedOfSound() + maxSpeed);
152 const double count = std::ceil(timeIntervalInSeconds /
153 (m_timeStepLimitScale * desiredTimeStep));
154 const double maxCount =
155 static_cast<double>(std::numeric_limits<unsigned int>::max());
156
157 return static_cast<unsigned int>(std::clamp(count, 1.0, maxCount));
158}
159
160template <size_t N>
165
166template <size_t N>
168{
169 m_mpmSystemData->TransferFromParticlesToGrid();
170
171 InitializeReferenceVolumes();
172 UpdateGridVelocities(timeStepInSeconds);
173 ConstrainGridVelocities();
174
175 m_mpmSystemData->TransferFromGridToParticles(timeStepInSeconds);
176}
177
178template <size_t N>
180{
181 Base::OnEndAdvanceTimeStep(timeStepInSeconds);
182 ConstrainParticlesToDomain();
183}
184
185template <size_t N>
187 const Vector<ssize_t, N>& index, const SizeType& dataSize)
188{
189 SizeType result;
190
191 for (size_t axis = 0; axis < N; ++axis)
192 {
193 result[axis] = static_cast<size_t>(std::clamp<ssize_t>(
194 index[axis], 0, static_cast<ssize_t>(dataSize[axis] - 1)));
195 }
196
197 return result;
198}
199
200template <size_t N>
201void MPMFluidSolver<N>::InitializeReferenceVolumes()
202{
203 const auto masses = m_mpmSystemData->ParticleMasses();
204 auto volumes = m_mpmSystemData->InitialVolumes();
205
206 for (size_t i = 0; i < volumes.Length(); ++i)
207 {
208 if (volumes[i] == 0.0)
209 {
210 volumes[i] = masses[i] / m_constitutiveModel.GetTargetDensity();
211 }
212 else if (!std::isfinite(volumes[i]) || volumes[i] < 0.0)
213 {
214 throw std::invalid_argument{
215 "Invalid MPM fluid reference volume."
216 };
217 }
218 }
219}
220
221template <size_t N>
222void MPMFluidSolver<N>::UpdateGridVelocities(double timeStepInSeconds)
223{
224 const auto positions = m_mpmSystemData->Positions();
225 const auto velocities = m_mpmSystemData->Velocities();
226 const auto masses = m_mpmSystemData->ParticleMasses();
227 const auto initialVolumes = m_mpmSystemData->InitialVolumes();
228 const auto volumeRatios = m_mpmSystemData->VolumeRatios();
229 const auto& gridMass = m_mpmSystemData->GridMass();
230 auto& gridVelocities = m_mpmSystemData->GridVelocities();
231 const auto dataSize = gridMass.DataSize();
232 const auto spacing = gridMass.GridSpacing();
233 const auto dataOrigin = gridMass.DataOrigin();
234 const auto isFinite = [](double value) { return std::isfinite(value); };
235
236 for (size_t i = 0; i < positions.Length(); ++i)
237 {
238 const double currentVolume = initialVolumes[i] * volumeRatios[i];
239 const double density =
240 m_constitutiveModel.ComputeDensity(masses[i], currentVolume);
241 const double pressure = m_constitutiveModel.ComputePressure(density);
242 const MatrixType stress =
243 m_constitutiveModel.ComputeCauchyStress(pressure);
244 const VectorType relativeVelocity =
245 velocities[i] - this->GetWind()->Sample(positions[i]);
246 const VectorType externalForce =
247 masses[i] * this->GetGravity() -
248 this->GetDragCoefficient() * relativeVelocity;
251
252 for (const auto& entry : stencil)
253 {
254 const auto index = ClampIndex(entry.index, dataSize);
255 const double nodeMass = gridMass(index);
256
257 if (nodeMass <= 0.0)
258 {
259 continue;
260 }
261
262 const VectorType increment =
264 (entry.weight * externalForce -
265 currentVolume * stress * entry.gradient) /
266 nodeMass;
267
268 if (!std::ranges::all_of(increment, isFinite))
269 {
270 throw std::invalid_argument{ "Non-finite MPM fluid update." };
271 }
272
273 gridVelocities(index) += increment;
274 }
275 }
276}
277
278template <size_t N>
279void MPMFluidSolver<N>::ConstrainGridVelocities()
280{
281 static constexpr std::array lowerFlags{ DIRECTION_LEFT, DIRECTION_DOWN,
283 static constexpr std::array upperFlags{ DIRECTION_RIGHT, DIRECTION_UP,
285
286 const auto& gridMass = m_mpmSystemData->GridMass();
287 auto& gridVelocities = m_mpmSystemData->GridVelocities();
288 const auto dataSize = gridVelocities.DataSize();
289
290 gridVelocities.ParallelForEachDataPointIndex(
291 [this, &gridMass, &gridVelocities, dataSize](const SizeType& index) {
292 if (gridMass(index) <= 0.0)
293 {
294 return;
295 }
296
297 VectorType velocity = gridVelocities(index);
298
299 if (const auto& collider = this->GetCollider(); collider != nullptr)
300 {
301 VectorType position = gridVelocities.DataPosition()(index);
302 collider->ResolveCollision(0.0, 0.0, &position, &velocity);
303 }
304
305 for (size_t axis = 0; axis < N; ++axis)
306 {
307 const bool exceedsLower =
308 (m_closedDomainBoundaryFlag & lowerFlags[axis]) != 0 &&
309 index[axis] == 0 && velocity[axis] < 0.0;
310 const bool exceedsUpper =
311 (m_closedDomainBoundaryFlag & upperFlags[axis]) != 0 &&
312 index[axis] == dataSize[axis] - 1 && velocity[axis] > 0.0;
313
315 {
316 velocity[axis] = 0.0;
317 }
318 }
319
320 gridVelocities(index) = velocity;
321 });
322}
323
324template <size_t N>
325void MPMFluidSolver<N>::ConstrainParticlesToDomain()
326{
327 static constexpr std::array lowerFlags{ DIRECTION_LEFT, DIRECTION_DOWN,
329 static constexpr std::array upperFlags{ DIRECTION_RIGHT, DIRECTION_UP,
331
332 const auto domain = m_mpmSystemData->GridMass().GetBoundingBox();
333 auto positions = m_mpmSystemData->Positions();
334 auto velocities = m_mpmSystemData->Velocities();
335
338 [&domain, &positions, &velocities, this](size_t i) {
339 for (size_t axis = 0; axis < N; ++axis)
340 {
341 if ((m_closedDomainBoundaryFlag & lowerFlags[axis]) != 0 &&
342 positions[i][axis] <= domain.lowerCorner[axis])
343 {
344 positions[i][axis] = domain.lowerCorner[axis];
345 velocities[i][axis] = std::max(velocities[i][axis], 0.0);
346 }
347
348 if ((m_closedDomainBoundaryFlag & upperFlags[axis]) != 0 &&
349 positions[i][axis] >= domain.upperCorner[axis])
350 {
351 positions[i][axis] = domain.upperCorner[axis];
352 velocities[i][axis] = std::min(velocities[i][axis], 0.0);
353 }
354 }
355 });
356}
357
358template <size_t N>
360 const SizeType& resolution)
361{
362 m_resolution = resolution;
363 return *this;
364}
365
366template <size_t N>
373
374template <size_t N>
376 const VectorType& gridOrigin)
377{
378 m_gridOrigin = gridOrigin;
379 return *this;
380}
381
382template <size_t N>
384 double radius)
385{
386 m_radius = radius;
387 return *this;
388}
389
390template <size_t N>
392{
393 m_mass = mass;
394 return *this;
395}
396
397template <size_t N>
399 double targetDensity)
400{
401 m_targetDensity = targetDensity;
402 return *this;
403}
404
405template <size_t N>
407 double speedOfSound)
408{
409 m_speedOfSound = speedOfSound;
410 return *this;
411}
412
413template <size_t N>
415 double eosExponent)
416{
417 m_eosExponent = eosExponent;
418 return *this;
419}
420
421template <size_t N>
425{
426 m_negativePressureScale = negativePressureScale;
427 return *this;
428}
429
430template <size_t N>
432{
433 return MPMFluidSolver{
434 m_resolution, m_gridSpacing, m_gridOrigin,
435 m_radius, m_mass, m_targetDensity,
436 m_speedOfSound, m_eosExponent, m_negativePressureScale
437 };
438}
439
440template <size_t N>
441std::shared_ptr<MPMFluidSolver<N>> MPMFluidSolver<N>::Builder::MakeShared()
442 const
443{
444 return std::make_shared<MPMFluidSolver>(
445 m_resolution, m_gridSpacing, m_gridOrigin, m_radius, m_mass,
446 m_targetDensity, m_speedOfSound, m_eosExponent,
447 m_negativePressureScale);
448}
449} // namespace CubbyFlow
450
451#endif
static Stencil GetStencil(const Vector< double, N > &position, const Vector< double, N > &gridSpacing, const Vector< double, N > &dataOrigin)
Definition MPMSystemData-Impl.hpp:138
Front-end to create MPMFluidSolver objects step by step.
Definition MPMFluidSolver.hpp:124
N-D explicit weakly compressible material point method solver.
Definition MPMFluidSolver.hpp:41
int GetClosedDomainBoundaryFlag() const
Returns the closed domain boundary flag.
Definition MPMFluidSolver-Impl.hpp:81
void SetTimeStepLimitScale(double newScale)
Sets the adaptive time-step scale in (0, 1].
Definition MPMFluidSolver-Impl.hpp:68
void OnBeginAdvanceTimeStep(double timeStepInSeconds) override
Advances particle-grid fluid state before base particle integration.
Definition MPMFluidSolver-Impl.hpp:167
const MPMFluidConstitutiveModel< N > & GetConstitutiveModel() const
Returns the immutable weakly compressible constitutive model.
Definition MPMFluidSolver-Impl.hpp:55
double GetTimeStepLimitScale() const
Returns the scale applied to adaptive time-step limits.
Definition MPMFluidSolver-Impl.hpp:62
static Builder GetBuilder()
Returns a builder for MPMFluidSolver.
Definition MPMFluidSolver-Impl.hpp:93
std::shared_ptr< MPMFluidSystemData< N > > GetMPMSystemData() const
Returns the owned MPM fluid particle and grid state.
Definition MPMFluidSolver-Impl.hpp:48
MPMFluidSolver(const SizeType &resolution=SizeType::MakeConstant(32), const VectorType &gridSpacing=VectorType::MakeConstant(1.0), const VectorType &gridOrigin=VectorType{}, double radius=1e-3, double mass=1e-3, double targetDensity=WATER_DENSITY, double speedOfSound=100.0, double eosExponent=7.0, double negativePressureScale=0.0)
Constructs an empty weakly compressible MPM fluid solver.
Definition MPMFluidSolver-Impl.hpp:23
unsigned int GetNumberOfSubTimeSteps(double timeIntervalInSeconds) const override
Returns the number of adaptive sub-time-steps.
Definition MPMFluidSolver-Impl.hpp:107
void OnEndAdvanceTimeStep(double timeStepInSeconds) override
Projects particles back into selected closed domain boundaries.
Definition MPMFluidSolver-Impl.hpp:179
void SetClosedDomainBoundaryFlag(int flag)
Sets the closed domain boundary flag.
Definition MPMFluidSolver-Impl.hpp:87
void OnInitialize() override
Initializes particle-grid state before the first adaptive step query.
Definition MPMFluidSolver-Impl.hpp:99
void AccumulateForces(double timeStepInSeconds) override
Fluid forces are integrated on the background grid.
Definition MPMFluidSolver-Impl.hpp:161
std::conditional_t< N==2, ParticleSystemSolver2, ParticleSystemSolver3 > Base
Definition MPMFluidSolver.hpp:47
N-D weakly compressible MPM fluid transfer state.
Definition MPMFluidSystemData.hpp:26
ValueType Length() const
Definition MatrixExpression-Impl.hpp:278
Definition Matrix.hpp:30
Definition pybind11Utils.hpp:22
constexpr size_t ZERO_SIZE
Zero size_t.
Definition Constants.hpp:20
constexpr int DIRECTION_UP
Up direction.
Definition Constants.hpp:323
constexpr int DIRECTION_RIGHT
Right direction.
Definition Constants.hpp:317
constexpr int DIRECTION_LEFT
Left direction.
Definition Constants.hpp:314
void ParallelFor(IndexType beginIndex, IndexType endIndex, const Function &function, ExecutionPolicy policy)
Makes a for-loop from beginIndex to endIndex in parallel.
Definition Parallel-Impl.hpp:203
constexpr int DIRECTION_BACK
Back direction.
Definition Constants.hpp:326
Matrix< T, Rows, 1 > Vector
Definition Matrix.hpp:648
constexpr int DIRECTION_FRONT
Front direction.
Definition Constants.hpp:329
constexpr int DIRECTION_DOWN
Down direction.
Definition Constants.hpp:320