Loading...
Searching...
No Matches
SnowMPMSolver-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_SNOW_MPM_SOLVER_IMPL_HPP
12#define CUBBYFLOW_SNOW_MPM_SOLVER_IMPL_HPP
13
15
16#include <algorithm>
17#include <array>
18#include <cmath>
19#include <limits>
20#include <stdexcept>
21
22namespace CubbyFlow
23{
24template <size_t N>
35
36template <size_t N>
37struct SnowMPMSolver<N>::LinearSystemBLAS : BLAS<double, VectorND, LinearSystem>
38{
41
42 static void MVM(const System& system, const VectorND& vector,
44 {
45 system.Multiply(vector, result);
46 }
47
48 static void Residual(const System& system, const VectorND& x,
49 const VectorND& b, VectorND* result)
50 {
51 system.Multiply(x, result);
52 Base::AXPlusY(-1.0, *result, b, result);
53 }
54};
55
56template <size_t N>
58 VectorND* output) const
59{
61
62 for (size_t i = 0; i < projectedInput.GetRows(); ++i)
63 {
64 if ((*constrained)[i] != 0)
65 {
66 projectedInput[i] = 0.0;
67 }
68 }
69
71
72 solver->ApplyElasticHessian(*activeNodes, *nodeToActive, projectedInput,
73 &hessian);
74 output->Resize(input.GetRows(), 0.0);
75 output->Fill(0.0);
76
77 const auto& gridMass = solver->m_mpmSystemData->GridMass();
78
79 for (size_t slot = 0; slot < activeNodes->Length(); ++slot)
80 {
81 const double mass = gridMass((*activeNodes)[slot]);
82
83 for (size_t axis = 0; axis < N; ++axis)
84 {
85 const size_t row = slot * N + axis;
86
87 if ((*constrained)[row] == 0)
88 {
89 (*output)[row] =
91 }
92 }
93 }
94}
95
96template <size_t N>
99 const VectorType& gridOrigin, double radius,
100 double mass)
101 : Base{ radius, mass },
102 m_mpmSystemData{ std::make_shared<MPMSystemData<N>>(
104{
105 if (!std::isfinite(radius) || radius < 0.0 || !std::isfinite(mass) ||
106 mass <= 0.0)
107 {
108 throw std::invalid_argument{ "Invalid snow MPM particle parameters." };
109 }
110
111 m_mpmSystemData->SetRadius(radius);
112 m_mpmSystemData->SetMass(mass);
113 this->SetParticleSystemData(m_mpmSystemData);
114 this->SetIsUsingFixedSubTimeSteps(false);
115}
116
117template <size_t N>
118std::shared_ptr<MPMSystemData<N>> SnowMPMSolver<N>::GetMPMSystemData() const
119{
120 return m_mpmSystemData;
121}
122
123template <size_t N>
125{
126 return m_timeStepLimitScale;
127}
128
129template <size_t N>
131{
132 if (!std::isfinite(newScale) || newScale <= 0.0 || newScale > 1.0)
133 {
134 throw std::invalid_argument{
135 "Time-step limit scale must be in (0, 1]."
136 };
137 }
138
139 m_timeStepLimitScale = newScale;
140}
141
142template <size_t N>
144{
145 return m_isUsingSemiImplicit;
146}
147
148template <size_t N>
150{
151 m_isUsingSemiImplicit = isUsing;
152}
153
154template <size_t N>
156{
157 return m_maxNumberOfIterations;
158}
159
160template <size_t N>
162 unsigned int maxNumberOfIterations)
163{
164 m_maxNumberOfIterations = maxNumberOfIterations;
165}
166
167template <size_t N>
169{
170 return m_tolerance;
171}
172
173template <size_t N>
174void SnowMPMSolver<N>::SetTolerance(double tolerance)
175{
176 if (!std::isfinite(tolerance) || tolerance <= 0.0)
177 {
178 throw std::invalid_argument{
179 "Semi-implicit tolerance must be positive and finite."
180 };
181 }
182
183 m_tolerance = tolerance;
184}
185
186template <size_t N>
188{
189 return m_lastNumberOfIterations;
190}
191
192template <size_t N>
194{
195 return m_lastResidual;
196}
197
198template <size_t N>
200{
201 return m_closedDomainBoundaryFlag;
202}
203
204template <size_t N>
206{
207 m_closedDomainBoundaryFlag = flag;
208}
209
210template <size_t N>
215
216template <size_t N>
218{
219 Base::OnInitialize();
220 m_mpmSystemData->TransferFromParticlesToGrid();
221 InitializeReferenceVolumes();
222 m_maxVelocityGradient = ComputeMaxVelocityGradient();
223}
224
225template <size_t N>
227 double timeIntervalInSeconds) const
228{
229 if (!std::isfinite(timeIntervalInSeconds) || timeIntervalInSeconds <= 0.0)
230 {
231 return 1;
232 }
233
234 const auto velocities = m_mpmSystemData->Velocities();
235 const auto masses = m_mpmSystemData->ParticleMasses();
236 const auto volumes = m_mpmSystemData->InitialVolumes();
237 const auto states = m_mpmSystemData->DeformationStates();
238 const auto spacing = m_mpmSystemData->GridMass().GridSpacing();
239 double minSpacing = spacing[0];
240 double maxVelocity = 0.0;
241 double maxWaveSpeed = 0.0;
242
243 for (size_t axis = 1; axis < N; ++axis)
244 {
245 minSpacing = std::min(minSpacing, spacing[axis]);
246 }
247
248 for (size_t i = 0; i < velocities.Length(); ++i)
249 {
250 maxVelocity = std::max(maxVelocity, velocities[i].Length());
251
252 if (volumes[i] == 0.0)
253 {
254 continue;
255 }
256
257 if (!std::isfinite(volumes[i]) || volumes[i] < 0.0)
258 {
259 throw std::invalid_argument{ "Invalid snow reference volume." };
260 }
261
263 std::max(maxWaveSpeed, m_constitutiveModel.ComputeWaveSpeed(
264 states[i], masses[i] / volumes[i]));
265 }
266
267 double desiredTimeStep = std::numeric_limits<double>::infinity();
268 if (maxVelocity > 0.0)
269 {
271 }
272 if (!m_isUsingSemiImplicit && maxWaveSpeed > 0.0)
273 {
275 }
276 if (m_maxVelocityGradient > 0.0)
277 {
279 std::min(desiredTimeStep, 0.2 / m_maxVelocityGradient);
280 }
281
282 if (!std::isfinite(desiredTimeStep))
283 {
284 return 1;
285 }
286
287 const double count = std::ceil(timeIntervalInSeconds /
288 (m_timeStepLimitScale * desiredTimeStep));
289 const double maxCount =
290 static_cast<double>(std::numeric_limits<unsigned int>::max());
291
292 return static_cast<unsigned int>(std::clamp(count, 1.0, maxCount));
293}
294
295template <size_t N>
300
301template <size_t N>
303{
304 m_mpmSystemData->TransferFromParticlesToGrid();
305 InitializeReferenceVolumes();
306 UpdateGridVelocities(timeStepInSeconds);
307
308 Array1<SizeType> activeNodes;
309 Array1<ssize_t> nodeToActive;
310
311 BuildActiveNodes(&activeNodes, &nodeToActive);
312
313 Array1<uint8_t> constrained(activeNodes.Length() * N, uint8_t{ 0 });
314
315 ConstrainGridVelocities(activeNodes, nodeToActive, &constrained);
316
317 if (m_isUsingSemiImplicit)
318 {
319 SolveGridVelocities(timeStepInSeconds, activeNodes, nodeToActive,
320 constrained);
321 ConstrainGridVelocities(activeNodes, nodeToActive, nullptr);
322 }
323 else
324 {
325 m_lastNumberOfIterations = 0;
326 m_lastResidual = 0.0;
327 }
328
329 UpdateDeformation(timeStepInSeconds);
330 m_mpmSystemData->TransferFromGridToParticles();
331}
332
333template <size_t N>
335{
336 Base::OnEndAdvanceTimeStep(timeStepInSeconds);
337 ConstrainParticlesToDomain();
338}
339
340template <size_t N>
342 const Vector<ssize_t, N>& index, const SizeType& dataSize)
343{
344 SizeType result;
345
346 for (size_t axis = 0; axis < N; ++axis)
347 {
348 result[axis] = static_cast<size_t>(std::clamp<ssize_t>(
349 index[axis], 0, static_cast<ssize_t>(dataSize[axis] - 1)));
350 }
351
352 return result;
353}
354
355template <size_t N>
356void SnowMPMSolver<N>::InitializeReferenceVolumes()
357{
358 const auto positions = m_mpmSystemData->Positions();
359 const auto masses = m_mpmSystemData->ParticleMasses();
360 auto volumes = m_mpmSystemData->InitialVolumes();
361 const auto& gridMass = m_mpmSystemData->GridMass();
362 const auto dataSize = gridMass.DataSize();
363 const auto spacing = gridMass.GridSpacing();
364 const auto dataOrigin = gridMass.DataOrigin();
365 double cellVolume = 1.0;
366
367 for (size_t axis = 0; axis < N; ++axis)
368 {
370 }
371
372 if (!std::isfinite(cellVolume) || cellVolume <= 0.0)
373 {
374 throw std::invalid_argument{ "Invalid snow MPM cell volume." };
375 }
376
377 for (size_t i = 0; i < volumes.Length(); ++i)
378 {
379 if (volumes[i] != 0.0)
380 {
381 if (!std::isfinite(volumes[i]) || volumes[i] < 0.0)
382 {
383 throw std::invalid_argument{ "Invalid snow reference volume." };
384 }
385 continue;
386 }
387
388 double referenceDensity = 0.0;
391
392 for (const auto& entry : stencil)
393 {
394 referenceDensity += gridMass(ClampIndex(entry.index, dataSize)) *
395 entry.weight / cellVolume;
396 }
397
398 if (!std::isfinite(referenceDensity) || referenceDensity <= 0.0)
399 {
400 throw std::invalid_argument{ "Invalid snow reference density." };
401 }
402
404 }
405}
406
407template <size_t N>
408void SnowMPMSolver<N>::UpdateGridVelocities(double timeStepInSeconds)
409{
410 const auto positions = m_mpmSystemData->Positions();
411 const auto velocities = m_mpmSystemData->Velocities();
412 const auto masses = m_mpmSystemData->ParticleMasses();
413 const auto volumes = m_mpmSystemData->InitialVolumes();
414 const auto states = m_mpmSystemData->DeformationStates();
415 const auto& gridMass = m_mpmSystemData->GridMass();
416 auto& gridVelocities = m_mpmSystemData->GridVelocities();
417 const auto dataSize = gridMass.DataSize();
418 const auto spacing = gridMass.GridSpacing();
419 const auto dataOrigin = gridMass.DataOrigin();
420
421 for (size_t i = 0; i < positions.Length(); ++i)
422 {
423 const VectorType relativeVelocity =
424 velocities[i] - this->GetWind()->Sample(positions[i]);
425 const VectorType externalForce =
426 masses[i] * this->GetGravity() -
427 this->GetDragCoefficient() * relativeVelocity;
428 const MatrixType stress =
429 m_constitutiveModel.ComputeKirchhoffStress(states[i]);
432
433 for (const auto& entry : stencil)
434 {
435 if (entry.weight == 0.0)
436 {
437 continue;
438 }
439
440 const auto index = ClampIndex(entry.index, dataSize);
441 const double nodeMass = gridMass(index);
442 if (nodeMass > 0.0)
443 {
444 gridVelocities(index) +=
446 (entry.weight * externalForce -
447 volumes[i] * stress * entry.gradient) /
448 nodeMass;
449 }
450 }
451 }
452}
453
454template <size_t N>
455void SnowMPMSolver<N>::BuildActiveNodes(Array1<SizeType>* activeNodes,
456 Array1<ssize_t>* nodeToActive) const
457{
458 const auto& gridMass = m_mpmSystemData->GridMass();
459 const auto dataView = gridMass.DataView();
460
461 activeNodes->Clear();
462 nodeToActive->Resize(dataView.Length(), ssize_t{ -1 });
463 nodeToActive->Fill(ssize_t{ -1 });
464
465 gridMass.ForEachDataPointIndex([&gridMass, activeNodes, dataView,
466 nodeToActive](const SizeType& index) {
467 if (gridMass(index) > 0.0)
468 {
469 (*nodeToActive)[dataView.Index(index)] =
470 static_cast<ssize_t>(activeNodes->Length());
471 activeNodes->Append(index);
472 }
473 });
474}
475
476template <size_t N>
477void SnowMPMSolver<N>::ConstrainGridVelocities(
478 const Array1<SizeType>& activeNodes, const Array1<ssize_t>& nodeToActive,
479 Array1<uint8_t>* constrained)
480{
481 const auto& gridMass = m_mpmSystemData->GridMass();
482 auto& gridVelocities = m_mpmSystemData->GridVelocities();
483 const auto dataView = gridMass.DataView();
484
485 if (constrained != nullptr)
486 {
487 constrained->Resize(activeNodes.Length() * N, uint8_t{ 0 });
488 constrained->Fill(uint8_t{ 0 });
489 }
490
491 gridVelocities.ParallelForEachDataPointIndex(
492 [this, constrained, &gridMass, &nodeToActive,
493 dataView](const SizeType& index) {
494 if (gridMass(index) <= 0.0)
495 {
496 return;
497 }
498
499 const ssize_t active = nodeToActive[dataView.Index(index)];
500
501 if (active < 0)
502 {
503 return;
504 }
505
506 ConstrainGridVelocityAtNode(index, static_cast<size_t>(active),
507 constrained);
508 });
509}
510
511template <size_t N>
512void SnowMPMSolver<N>::ConstrainGridVelocityAtNode(const SizeType& index,
513 size_t active,
514 Array1<uint8_t>* constrained)
515{
516 auto& gridVelocities = m_mpmSystemData->GridVelocities();
517 VectorType velocity = gridVelocities(index);
518
519 ApplyGridColliderConstraint(index, active, constrained, &velocity);
520 ApplyGridDomainConstraint(index, active, constrained, &velocity);
521
522 gridVelocities(index) = velocity;
523}
524
525template <size_t N>
526void SnowMPMSolver<N>::ApplyGridColliderConstraint(const SizeType& index,
527 size_t active,
528 Array1<uint8_t>* constrained,
529 VectorType* velocity) const
530{
531 const auto collider = this->GetCollider();
532
533 if (collider == nullptr)
534 {
535 return;
536 }
537
538 const VectorType incoming = *velocity;
539 VectorType position =
540 m_mpmSystemData->GridVelocities().DataPosition()(index);
541
542 collider->ResolveCollision(0.0, 0.0, &position, velocity);
543
544 if (constrained != nullptr && *velocity != incoming)
545 {
546 for (size_t axis = 0; axis < N; ++axis)
547 {
548 (*constrained)[active * N + axis] = uint8_t{ 1 };
549 }
550 }
551}
552
553template <size_t N>
554void SnowMPMSolver<N>::ApplyGridDomainConstraint(const SizeType& index,
555 size_t active,
556 Array1<uint8_t>* constrained,
557 VectorType* velocity) const
558{
559 static constexpr std::array lowerFlags{ DIRECTION_LEFT, DIRECTION_DOWN,
561 static constexpr std::array upperFlags{ DIRECTION_RIGHT, DIRECTION_UP,
563 const auto dataSize = m_mpmSystemData->GridVelocities().DataSize();
564
565 for (size_t axis = 0; axis < N; ++axis)
566 {
567 const bool exceedsLower =
568 (m_closedDomainBoundaryFlag & lowerFlags[axis]) != 0 &&
569 index[axis] == 0 && (*velocity)[axis] < 0.0;
570 const bool exceedsUpper =
571 (m_closedDomainBoundaryFlag & upperFlags[axis]) != 0 &&
572 index[axis] == dataSize[axis] - 1 && (*velocity)[axis] > 0.0;
573
575 {
576 (*velocity)[axis] = 0.0;
577
578 if (constrained != nullptr)
579 {
580 (*constrained)[active * N + axis] = uint8_t{ 1 };
581 }
582 }
583 }
584}
585
586template <size_t N>
588SnowMPMSolver<N>::ComputeParticleDeformationDifferential(
589 const Stencil& stencil, const Array1<ssize_t>& nodeToActive,
590 const VectorND& input, const MatrixType& elastic) const
591{
592 const auto& gridMass = m_mpmSystemData->GridMass();
593 const auto dataSize = gridMass.DataSize();
594 const auto dataView = gridMass.DataView();
595 MatrixType result;
596
597 for (const auto& entry : stencil)
598 {
599 if (entry.weight == 0.0)
600 {
601 continue;
602 }
603
604 const SizeType index = ClampIndex(entry.index, dataSize);
605 const ssize_t active = nodeToActive[dataView.Index(index)];
606
607 if (active < 0)
608 {
609 continue;
610 }
611
612 VectorType velocityDifferential;
613
614 for (size_t axis = 0; axis < N; ++axis)
615 {
617 input[static_cast<size_t>(active) * N + axis];
618 }
619
620 for (size_t row = 0; row < N; ++row)
621 {
622 for (size_t column = 0; column < N; ++column)
623 {
624 result(row, column) +=
626 }
627 }
628 }
629
630 result *= elastic;
631 return result;
632}
633
634template <size_t N>
635void SnowMPMSolver<N>::AccumulateParticleHessian(
636 const Stencil& stencil, const Array1<ssize_t>& nodeToActive, double volume,
637 const MatrixType& elastic, const MatrixType& stressDifferential,
638 VectorND* output) const
639{
640 const auto& gridMass = m_mpmSystemData->GridMass();
641 const auto dataSize = gridMass.DataSize();
642 const auto dataView = gridMass.DataView();
643
644 for (const auto& entry : stencil)
645 {
646 if (entry.weight == 0.0)
647 {
648 continue;
649 }
650
651 const SizeType index = ClampIndex(entry.index, dataSize);
652 const ssize_t active = nodeToActive[dataView.Index(index)];
653
654 if (active < 0)
655 {
656 continue;
657 }
658
659 const VectorType contribution =
660 volume * stressDifferential * elastic.Transposed() * entry.gradient;
661
662 for (size_t axis = 0; axis < N; ++axis)
663 {
664 (*output)[static_cast<size_t>(active) * N + axis] +=
666 }
667 }
668}
669
670template <size_t N>
671void SnowMPMSolver<N>::ApplyElasticHessian(const Array1<SizeType>& activeNodes,
672 const Array1<ssize_t>& nodeToActive,
673 const VectorND& input,
674 VectorND* output) const
675{
676 const auto positions = m_mpmSystemData->Positions();
677 const auto volumes = m_mpmSystemData->InitialVolumes();
678 const auto states = m_mpmSystemData->DeformationStates();
679 const auto& gridMass = m_mpmSystemData->GridMass();
680 const auto spacing = gridMass.GridSpacing();
681 const auto dataOrigin = gridMass.DataOrigin();
682
683 output->Resize(activeNodes.Length() * N, 0.0);
684 output->Fill(0.0);
685
686 for (size_t p = 0; p < positions.Length(); ++p)
687 {
690 const MatrixType differential = ComputeParticleDeformationDifferential(
691 stencil, nodeToActive, input, states[p].elastic);
692 const MatrixType stressDifferential =
693 m_constitutiveModel.ComputeFirstPiolaStressDifferential(
695
696 AccumulateParticleHessian(stencil, nodeToActive, volumes[p],
697 states[p].elastic, stressDifferential,
698 output);
699 }
700}
701
702template <size_t N>
703VectorND SnowMPMSolver<N>::GatherActiveGridVelocities(
704 const Array1<SizeType>& activeNodes) const
705{
706 const auto& gridVelocities = m_mpmSystemData->GridVelocities();
707 VectorND result(activeNodes.Length() * N, 0.0);
708
709 for (size_t active = 0; active < activeNodes.Length(); ++active)
710 {
711 const VectorType velocity = gridVelocities(activeNodes[active]);
712
713 for (size_t axis = 0; axis < N; ++axis)
714 {
715 result[active * N + axis] = velocity[axis];
716 }
717 }
718
719 return result;
720}
721
722template <size_t N>
723VectorND SnowMPMSolver<N>::BuildSemiImplicitRightHandSide(
724 double dtSquared, const Array1<SizeType>& activeNodes,
725 const Array1<ssize_t>& nodeToActive, const Array1<uint8_t>& constrained,
726 const VectorND& velocities) const
727{
729
730 ApplyElasticHessian(activeNodes, nodeToActive, velocities, &hessian);
731
733
734 for (size_t i = 0; i < result.GetRows(); ++i)
735 {
736 if (constrained[i] == 0)
737 {
738 result[i] = -dtSquared * hessian[i];
739 }
740 }
741
742 return result;
743}
744
745template <size_t N>
746VectorND SnowMPMSolver<N>::SolveGridVelocityCorrection(
747 double dtSquared, const Array1<SizeType>& activeNodes,
748 const Array1<ssize_t>& nodeToActive, const Array1<uint8_t>& constrained,
749 const VectorND& rhs, double initialResidual)
750{
751 const LinearSystem system{ this, &activeNodes, &nodeToActive, &constrained,
752 dtSquared };
755 VectorND direction(rhs.GetRows(), 0.0);
756 VectorND product(rhs.GetRows(), 0.0);
757 VectorND image(rhs.GetRows(), 0.0);
759
760 CR<LinearSystemBLAS>(system, rhs, m_maxNumberOfIterations,
761 m_tolerance * initialResidual, &correction, &residual,
762 &direction, &product, &image,
763 &m_lastNumberOfIterations, &residualNorm);
764
765 m_lastResidual = residualNorm / initialResidual;
766 return correction;
767}
768
769template <size_t N>
770VectorND SnowMPMSolver<N>::ComputeGridVelocityUpdate(
771 double timeStepInSeconds, const Array1<SizeType>& activeNodes,
772 const Array1<ssize_t>& nodeToActive, const Array1<uint8_t>& constrained)
773{
774 const double dtSquared = timeStepInSeconds * timeStepInSeconds;
775 const VectorND vStar = GatherActiveGridVelocities(activeNodes);
776 const VectorND rhs = BuildSemiImplicitRightHandSide(
777 dtSquared, activeNodes, nodeToActive, constrained, vStar);
778 const double initialResidual = LinearSystemBLAS::L2Norm(rhs);
779
780 if (!std::isfinite(initialResidual))
781 {
782 m_lastResidual = std::numeric_limits<double>::infinity();
783 throw std::runtime_error{
784 "Semi-implicit snow solve failed to converge."
785 };
786 }
787 if (initialResidual == 0.0)
788 {
789 return vStar;
790 }
791
792 const VectorND correction =
793 SolveGridVelocityCorrection(dtSquared, activeNodes, nodeToActive,
794 constrained, rhs, initialResidual);
796
797 if (const bool isFinite = std::ranges::all_of(
798 result, [](double value) { return std::isfinite(value); });
799 !isFinite || !std::isfinite(m_lastResidual) ||
800 m_lastResidual > m_tolerance)
801 {
802 throw std::runtime_error{
803 "Semi-implicit snow solve failed to converge."
804 };
805 }
806
807 return result;
808}
809
810template <size_t N>
811void SnowMPMSolver<N>::StoreActiveGridVelocities(
812 const Array1<SizeType>& activeNodes, const VectorND& velocities)
813{
814 auto& gridVelocities = m_mpmSystemData->GridVelocities();
815
816 for (size_t active = 0; active < activeNodes.Length(); ++active)
817 {
818 VectorType velocity;
819
820 for (size_t axis = 0; axis < N; ++axis)
821 {
822 velocity[axis] = velocities[active * N + axis];
823 }
824
825 gridVelocities(activeNodes[active]) = velocity;
826 }
827}
828
829template <size_t N>
830void SnowMPMSolver<N>::SolveGridVelocities(double timeStepInSeconds,
831 const Array1<SizeType>& activeNodes,
832 const Array1<ssize_t>& nodeToActive,
833 const Array1<uint8_t>& constrained)
834{
835 auto& gridVelocities = m_mpmSystemData->GridVelocities();
836 const auto& gridVelocitiesBeforeUpdate =
837 m_mpmSystemData->GridVelocitiesBeforeUpdate();
838
839 m_lastNumberOfIterations = 0;
840 m_lastResidual = 0.0;
841
842 try
843 {
844 if (!activeNodes.IsEmpty())
845 {
846 const VectorND nextVelocities = ComputeGridVelocityUpdate(
847 timeStepInSeconds, activeNodes, nodeToActive, constrained);
848 StoreActiveGridVelocities(activeNodes, nextVelocities);
849 }
850 }
851 catch (...)
852 {
854 throw;
855 }
856}
857
858template <size_t N>
859SnowMPMSolver<N>::MatrixType SnowMPMSolver<N>::ComputeVelocityGradient(
860 size_t particleIndex) const
861{
862 const auto positions = m_mpmSystemData->Positions();
863 const auto& gridVelocities = m_mpmSystemData->GridVelocities();
864 const auto dataSize = gridVelocities.DataSize();
866 positions[particleIndex], gridVelocities.GridSpacing(),
867 gridVelocities.DataOrigin());
868 MatrixType result;
869
870 for (const auto& entry : stencil)
871 {
872 if (entry.weight != 0.0)
873 {
874 const VectorType velocity =
875 gridVelocities(ClampIndex(entry.index, dataSize));
876 for (size_t row = 0; row < N; ++row)
877 {
878 for (size_t column = 0; column < N; ++column)
879 {
880 result(row, column) +=
881 velocity[row] * entry.gradient[column];
882 }
883 }
884 }
885 }
886
887 return result;
888}
889
890template <size_t N>
891double SnowMPMSolver<N>::ComputeMaxVelocityGradient() const
892{
893 double result = 0.0;
894
895 for (size_t i = 0; i < m_mpmSystemData->NumberOfParticles(); ++i)
896 {
897 result = std::max(result, ComputeVelocityGradient(i).AbsMax());
898 }
899
900 return result;
901}
902
903template <size_t N>
904void SnowMPMSolver<N>::UpdateDeformation(double timeStepInSeconds)
905{
906 auto states = m_mpmSystemData->DeformationStates();
907 m_maxVelocityGradient = 0.0;
908
909 for (size_t i = 0; i < states.Length(); ++i)
910 {
911 const MatrixType velocityGradient = ComputeVelocityGradient(i);
912 m_maxVelocityGradient =
913 std::max(m_maxVelocityGradient, velocityGradient.AbsMax());
914 states[i] = m_constitutiveModel.Update(
915 MatrixType::MakeIdentity() + timeStepInSeconds * velocityGradient,
916 states[i]);
917 }
918}
919
920template <size_t N>
921void SnowMPMSolver<N>::ConstrainParticlesToDomain()
922{
923 static constexpr std::array lowerFlags{ DIRECTION_LEFT, DIRECTION_DOWN,
925 static constexpr std::array upperFlags{ DIRECTION_RIGHT, DIRECTION_UP,
927 const auto domain = m_mpmSystemData->GridMass().GetBoundingBox();
928 auto positions = m_mpmSystemData->Positions();
929 auto velocities = m_mpmSystemData->Velocities();
930
933 [&domain, &positions, &velocities, this](size_t i) {
934 for (size_t axis = 0; axis < N; ++axis)
935 {
936 if ((m_closedDomainBoundaryFlag & lowerFlags[axis]) != 0 &&
937 positions[i][axis] <= domain.lowerCorner[axis])
938 {
939 positions[i][axis] = domain.lowerCorner[axis];
940 velocities[i][axis] = std::max(velocities[i][axis], 0.0);
941 }
942 if ((m_closedDomainBoundaryFlag & upperFlags[axis]) != 0 &&
943 positions[i][axis] >= domain.upperCorner[axis])
944 {
945 positions[i][axis] = domain.upperCorner[axis];
946 velocities[i][axis] = std::min(velocities[i][axis], 0.0);
947 }
948 }
949 });
950}
951
952template <size_t N>
954 const SizeType& resolution)
955{
956 m_resolution = resolution;
957 return *this;
958}
959
960template <size_t N>
967
968template <size_t N>
970 const VectorType& gridOrigin)
971{
972 m_gridOrigin = gridOrigin;
973 return *this;
974}
975
976template <size_t N>
978{
979 m_radius = radius;
980 return *this;
981}
982
983template <size_t N>
985{
986 m_mass = mass;
987 return *this;
988}
989
990template <size_t N>
992{
993 return SnowMPMSolver{ m_resolution, m_gridSpacing, m_gridOrigin, m_radius,
994 m_mass };
995}
996
997template <size_t N>
998std::shared_ptr<SnowMPMSolver<N>> SnowMPMSolver<N>::Builder::MakeShared() const
999{
1000 return std::make_shared<SnowMPMSolver>(m_resolution, m_gridSpacing,
1001 m_gridOrigin, m_radius, m_mass);
1002}
1003} // namespace CubbyFlow
1004
1005#endif
static Stencil GetStencil(const Vector< double, N > &position, const Vector< double, N > &gridSpacing, const Vector< double, N > &dataOrigin)
Definition MPMSystemData-Impl.hpp:137
N-D material point method particle and grid state.
Definition MPMSystemData.hpp:72
ValueType Length() const
Definition MatrixExpression-Impl.hpp:278
MatrixTranspose< T, Rows, Cols, const Derived & > Transposed() const
Definition MatrixExpression-Impl.hpp:365
ValueType AbsMax() const
Definition MatrixExpression-Impl.hpp:162
Definition Matrix.hpp:30
void Fill(const T &val)
Definition Matrix-Impl.hpp:226
constexpr size_t GetRows() const
Definition Matrix-Impl.hpp:260
Front-end to create SnowMPMSolver objects step by step.
Definition SnowMPMSolver.hpp:216
N-D material point method solver for snow.
Definition SnowMPMSolver.hpp:43
unsigned int GetNumberOfSubTimeSteps(double timeIntervalInSeconds) const override
Returns the number of adaptive sub-time-steps.
Definition SnowMPMSolver-Impl.hpp:226
SnowMPMSolver(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)
Constructs an empty snow MPM solver.
Definition SnowMPMSolver-Impl.hpp:97
double GetTolerance() const
Returns the relative conjugate-residual tolerance.
Definition SnowMPMSolver-Impl.hpp:168
double GetTimeStepLimitScale() const
Returns the scale applied to adaptive time-step limits.
Definition SnowMPMSolver-Impl.hpp:124
void AccumulateForces(double timeStepInSeconds) override
Snow forces are integrated on the background grid.
Definition SnowMPMSolver-Impl.hpp:296
void OnEndAdvanceTimeStep(double timeStepInSeconds) override
Projects particles back into selected closed domain boundaries.
Definition SnowMPMSolver-Impl.hpp:334
unsigned int GetMaxNumberOfIterations() const
Returns the maximum conjugate-residual iteration count.
Definition SnowMPMSolver-Impl.hpp:155
Matrix< double, N, N > MatrixType
Definition SnowMPMSolver.hpp:49
void OnInitialize() override
Initializes particle-grid state before the first adaptive step query.
Definition SnowMPMSolver-Impl.hpp:217
bool GetIsUsingSemiImplicit() const
Returns whether the elastic grid update is semi-implicit.
Definition SnowMPMSolver-Impl.hpp:143
void SetClosedDomainBoundaryFlag(int flag)
Sets the closed domain boundary flag.
Definition SnowMPMSolver-Impl.hpp:205
void SetIsUsingSemiImplicit(bool isUsing)
Enables or disables the semi-implicit elastic grid update.
Definition SnowMPMSolver-Impl.hpp:149
void SetMaxNumberOfIterations(unsigned int maxNumberOfIterations)
Definition SnowMPMSolver-Impl.hpp:161
double GetLastResidual() const
Returns the relative residual from the last semi-implicit solve.
Definition SnowMPMSolver-Impl.hpp:193
std::conditional_t< N==2, ParticleSystemSolver2, ParticleSystemSolver3 > Base
Definition SnowMPMSolver.hpp:48
int GetClosedDomainBoundaryFlag() const
Returns the closed domain boundary flag.
Definition SnowMPMSolver-Impl.hpp:199
void OnBeginAdvanceTimeStep(double timeStepInSeconds) override
Advances particle-grid snow state before base particle integration.
Definition SnowMPMSolver-Impl.hpp:302
unsigned int GetLastNumberOfIterations() const
Returns the iteration count from the last semi-implicit solve.
Definition SnowMPMSolver-Impl.hpp:187
void SetTimeStepLimitScale(double newScale)
Sets the adaptive time-step scale in (0, 1].
Definition SnowMPMSolver-Impl.hpp:130
std::shared_ptr< MPMSystemData< N > > GetMPMSystemData() const
Returns the owned MPM particle and grid state.
Definition SnowMPMSolver-Impl.hpp:118
static Builder GetBuilder()
Returns a builder for SnowMPMSolver.
Definition SnowMPMSolver-Impl.hpp:211
void SetTolerance(double tolerance)
Sets the positive finite tolerance relative to the initial residual.
Definition SnowMPMSolver-Impl.hpp:174
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
VectorN< double > VectorND
Definition Matrix.hpp:742
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
Generic BLAS operator wrapper class.
Definition BLAS.hpp:29
Definition SnowMPMSolver-Impl.hpp:38
static void MVM(const System &system, const VectorND &vector, VectorND *result)
Definition SnowMPMSolver-Impl.hpp:42
static void Residual(const System &system, const VectorND &x, const VectorND &b, VectorND *result)
Definition SnowMPMSolver-Impl.hpp:48
Definition SnowMPMSolver-Impl.hpp:26
const SnowMPMSolver * solver
Definition SnowMPMSolver-Impl.hpp:27
double dtSquared
Definition SnowMPMSolver-Impl.hpp:31
const Array1< uint8_t > * constrained
Definition SnowMPMSolver-Impl.hpp:30
const Array1< ssize_t > * nodeToActive
Definition SnowMPMSolver-Impl.hpp:29
const Array1< SizeType > * activeNodes
Definition SnowMPMSolver-Impl.hpp:28
void Multiply(const VectorND &input, VectorND *output) const
Definition SnowMPMSolver-Impl.hpp:57