10#include <unordered_map>
58template <
class T,
int D>
61 using IndexType = viennahrle::Index<D>;
62 using ConstSparseIterator =
63 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
84 enum class Boundary { NONE, REACTION, AMBIENT, MASK };
86 struct BoundaryIntersection {
87 Boundary boundary = Boundary::NONE;
91 template <
class ValueType>
struct StencilPoint {
98 Vec3D<T> velocity{0., 0., 0.};
101 std::array<T, 9> strainRateTensor{};
102 std::array<T, 9> stressTensor{};
103 T vonMisesStress = 0.;
106 SmartPointer<Domain<T, D>> reactionInterface =
nullptr;
107 SmartPointer<Domain<T, D>> ambientInterface =
nullptr;
108 SmartPointer<Domain<T, D>> maskInterface =
nullptr;
109 SmartPointer<OxidationDiffusion<T, D>> diffusionField =
nullptr;
110 SmartPointer<VelocityField<T>> maskVelocityField =
nullptr;
113 int reactionSign = 1;
114 int ambientSign = -1;
117 IndexType requestedMinIndex{};
118 IndexType requestedMaxIndex{};
119 unsigned iterations = 0;
120 T residual = std::numeric_limits<T>::max();
123 unsigned lastPressureIters_ = 0;
124 T lastPressureResidual_ = 0.;
125 unsigned lastStokesIters_ = 0;
126 T lastStokesResidual_ = 0.;
127 T avgExpansionSpeed_ = 0.;
128 bool avgExpansionSpeedComputed =
false;
130 bool nodesDirty_ =
true;
131 std::array<T, D> maxVelocity_{};
132 bool useRequestedBounds =
false;
133 std::unordered_map<IndexType, std::array<T, 9>,
typename IndexType::hash>
134 deviatoricStressHistory;
137 std::vector<Vec3D<T>> previousVelocity_;
138 std::vector<T> previousPressure_;
139 bool hasPreviousSolution_ =
false;
141 static bool isFiniteVec(
const Vec3D<T> &value) {
142 for (
unsigned i = 0; i < 3; ++i)
143 if (!std::isfinite(value[i]))
148 static bool isFiniteTensor(
const std::array<T, 9> &value) {
149 for (
const auto component : value)
150 if (!std::isfinite(component))
156 std::vector<Boundary> faceBCTypes_;
157 std::vector<T> faceBCDists_;
167#ifdef VIENNALS_GPU_BICGSTAB
171 std::vector<double> pressCoeffGpu_;
172 std::vector<uint32_t> pressNeighId32_;
173 std::vector<double> actualDiagGpu_;
174 gpu::GpuBiCGSTABBuffers *gpuPressBufs_ =
nullptr;
179 std::vector<double> stokesCoeffGpu_;
180 std::vector<uint32_t> stokesNeighId32_;
181 std::vector<double> stokesDiagGpu_;
182 gpu::GpuBiCGSTABBuffers *gpuStokesBufs_ =
nullptr;
191 gpu::GpuBiCGSTABBuffers *gpuHarmonicBufs_ =
nullptr;
205 : reactionInterface(passedReactionInterface),
206 ambientInterface(passedAmbientInterface),
207 diffusionField(passedDiffusionField),
208 deformationParameters(passedDeformationParameters),
209 oxidationParameters(passedOxidationParameters) {}
211 template <
class... Args>
static auto New(Args &&...args) {
212 return SmartPointer<OxidationDeformation>::New(std::forward<Args>(args)...);
216#ifdef VIENNALS_GPU_BICGSTAB
217 gpu::freeGpuBuffers(gpuPressBufs_);
218 gpu::freeGpuBuffers(gpuStokesBufs_);
219 gpu::freeGpuBuffers(gpuHarmonicBufs_);
225 gpuPreconditioner_ = prec;
229 reactionInterface = passedInterface;
235 ambientInterface = passedInterface;
241 int passedMaskSign = 1) {
242 maskInterface = passedInterface;
243 maskSign = (passedMaskSign < 0) ? -1 : 1;
249 maskInterface =
nullptr;
256 maskVelocityField = passedVelocityField;
261 maskVelocityField =
nullptr;
267 diffusionField = passedDiffusionField;
272 oxidationParameters = passedParameters;
278 deformationParameters = passedParameters;
283 reactionSign = (passedReactionSign < 0) ? -1 : 1;
284 ambientSign = (passedAmbientSign < 0) ? -1 : 1;
289 const IndexType &passedMaxIndex) {
290 requestedMinIndex = passedMinIndex;
291 requestedMaxIndex = passedMaxIndex;
292 useRequestedBounds =
true;
298 useRequestedBounds =
false;
309 if (reactionInterface ==
nullptr || ambientInterface ==
nullptr ||
310 diffusionField ==
nullptr) {
311 Logger::getInstance()
312 .addError(
"OxidationDeformation: Missing interface or "
324 Logger::getInstance()
325 .addWarning(
"OxidationDeformation: no oxide nodes found after "
326 "buildNodes(). Verify that the reaction and ambient "
327 "level sets enclose a non-empty oxide band.")
329 hasPreviousSolution_ =
335 }
else if (hasPreviousSolution_ &&
336 previousVelocity_.size() ==
nodes.size() &&
337 previousPressure_.size() ==
nodes.size()) {
343 for (std::size_t i = 0; i <
nodes.size(); ++i) {
344 nodes[i].velocity = previousVelocity_[i];
345 nodes[i].pressure = previousPressure_[i];
349 const std::size_t nn =
nodes.size();
350 Timer<> tHarmonic, tMechanics;
357 Logger::getInstance()
358 .addTiming(
" deformation n=" + std::to_string(nn) +
" harmonic",
360 .addTiming(
" deformation n=" + std::to_string(nn) +
364 avgExpansionSpeedComputed =
false;
366 maxVelocity_.fill(
T(0));
367 for (
const auto &node :
nodes) {
368 for (
unsigned d = 0; d <
D; ++d)
369 maxVelocity_[d] = std::max(maxVelocity_[d], std::abs(node.velocity[d]));
372 for (
unsigned d = 0; d <
D; ++d)
373 maxVelocity_[d] = std::max(maxVelocity_[d], unresolvedMax[d]);
376 previousVelocity_.resize(
nodes.size());
377 previousPressure_.resize(
nodes.size());
378 for (std::size_t i = 0; i <
nodes.size(); ++i) {
379 previousVelocity_[i] =
nodes[i].velocity;
380 previousPressure_[i] =
nodes[i].pressure;
382 hasPreviousSolution_ =
true;
389 unsigned long )
final {
393 if (deformationParameters.material >= 0 &&
394 material != deformationParameters.material)
399 for (
unsigned d = 0; d <
D; ++d)
400 norm2 += velocity[d] * velocity[d];
401 if (norm2 > std::numeric_limits<T>::epsilon())
408 const Vec3D<T> &normalVector,
409 unsigned long )
final {
414 const Vec3D<T> & )
final {
415 if (deformationParameters.material >= 0 &&
416 material != deformationParameters.material)
418 return maxVelocity_[direction];
422 template <
class ValueType,
class NodeAccessor>
423 ValueType getField(
const IndexType &index, ValueType fallback,
424 NodeAccessor accessor)
const {
427 return accessor(
nodes[nodeId]);
432 return accessor(
nodes[nearby]);
435 template <
class ValueType,
class NodeAccessor>
436 ValueType getField(
const Vec3D<T> &coordinate, ValueType fallback,
437 NodeAccessor accessor)
const {
444 if constexpr (!std::is_same_v<ValueType, Vec3D<T>> &&
445 !std::is_same_v<ValueType, T>) {
447 for (
unsigned i = 0; i <
D; ++i)
448 index[i] = std::llround(coordinate[i] /
gridDelta);
449 return getField(index, fallback, accessor);
451 using IdxScalar = std::decay_t<decltype(std::declval<IndexType>()[0])>;
454 for (
unsigned d = 0; d <
D; ++d) {
456 const T c_flo = std::floor(c);
457 lo[d] =
static_cast<IdxScalar
>(c_flo);
462 T totalWeight =
T(0);
463 for (
int corner = 0; corner < (1 <<
D); ++corner) {
466 for (
unsigned d = 0; d <
D; ++d) {
467 if ((corner >> d) & 1) {
479 result = result + accessor(
nodes[nodeId]) * w;
483 if (totalWeight <
T(1e-14))
485 if (totalWeight <
T(1) -
T(1e-6))
486 result = result * (
T(1) / totalWeight);
493 return getField(coordinate, Vec3D<T>{0., 0., 0.},
494 [](
const Node &n) {
return n.velocity; });
498 return getField(index, Vec3D<T>{0., 0., 0.},
499 [](
const Node &n) {
return n.velocity; });
503 return getField(coordinate,
T(0), [](
const Node &n) {
return n.pressure; });
507 return getField(index,
T(0), [](
const Node &n) {
return n.pressure; });
511 return getField(coordinate,
T(0),
512 [](
const Node &n) {
return n.strainTrace; });
516 return getField(index,
T(0), [](
const Node &n) {
return n.strainTrace; });
520 return getField(coordinate, std::array<T, 9>{},
521 [](
const Node &n) {
return n.strainRateTensor; });
525 return getField(index, std::array<T, 9>{},
526 [](
const Node &n) {
return n.strainRateTensor; });
530 return getField(coordinate, std::array<T, 9>{},
531 [](
const Node &n) {
return n.stressTensor; });
535 return getField(index, std::array<T, 9>{},
536 [](
const Node &n) {
return n.stressTensor; });
540 return getField(coordinate,
T(0),
541 [](
const Node &n) {
return n.vonMisesStress; });
545 return getField(index,
T(0),
546 [](
const Node &n) {
return n.vonMisesStress; });
554 return std::isfinite(residual) &&
555 residual <= deformationParameters.mechanicsTolerance &&
556 std::isfinite(lastPressureResidual_) &&
557 lastPressureResidual_ <= deformationParameters.pressureTolerance &&
558 std::isfinite(lastStokesResidual_) &&
559 lastStokesResidual_ <= deformationParameters.stokesTolerance &&
563 for (
const auto &node :
nodes) {
564 if (!isFiniteVec(node.velocity) || !std::isfinite(node.pressure) ||
565 !std::isfinite(node.strainTrace) ||
566 !isFiniteTensor(node.strainRateTensor) ||
567 !isFiniteTensor(node.stressTensor) ||
568 !std::isfinite(node.vonMisesStress))
575 if (!avgExpansionSpeedComputed) {
577 avgExpansionSpeedComputed =
true;
579 return avgExpansionSpeed_;
582 for (
const auto &node :
nodes)
583 callback(node.index, node.pressure);
591 if (
nodes.empty() || ambientInterface ==
nullptr)
594 using VD =
typename PointData<T>::VectorDataType;
595 VD velocity, stressR0, stressR1, stressR2;
597 ConstSparseIterator it(ambientInterface->getDomain());
598 for (; !it.isFinished(); ++it) {
601 const IndexType idx = it.getStartIndices();
605 const auto &n =
nodes[nId];
607 isFiniteVec(n.velocity) ? n.velocity : Vec3D<T>{T(0), T(0), T(0)});
608 const auto sIt = deviatoricStressHistory.find(idx);
609 if (sIt != deviatoricStressHistory.end() &&
610 isFiniteTensor(sIt->second)) {
611 const auto &s = sIt->second;
612 stressR0.push_back({s[0], s[1], s[2]});
613 stressR1.push_back({s[3], s[4], s[5]});
614 stressR2.push_back({s[6], s[7], s[8]});
616 stressR0.push_back({
T(0),
T(0),
T(0)});
617 stressR1.push_back({
T(0),
T(0),
T(0)});
618 stressR2.push_back({
T(0),
T(0),
T(0)});
621 velocity.push_back({
T(0),
T(0),
T(0)});
622 stressR0.push_back({
T(0),
T(0),
T(0)});
623 stressR1.push_back({
T(0),
T(0),
T(0)});
624 stressR2.push_back({
T(0),
T(0),
T(0)});
628 auto &pd = ambientInterface->getPointData();
629 pd.insertReplaceVectorData(std::move(velocity),
"OxVelocity");
630 pd.insertReplaceVectorData(std::move(stressR0),
"OxStressR0");
631 pd.insertReplaceVectorData(std::move(stressR1),
"OxStressR1");
632 pd.insertReplaceVectorData(std::move(stressR2),
"OxStressR2");
639 void seedFromLevelSet() {
640 if (ambientInterface ==
nullptr ||
nodes.empty())
643 auto &pd = ambientInterface->getPointData();
644 const int vIdx = pd.getVectorDataIndex(
"OxVelocity");
645 const int r0Idx = pd.getVectorDataIndex(
"OxStressR0");
646 const int r1Idx = pd.getVectorDataIndex(
"OxStressR1");
647 const int r2Idx = pd.getVectorDataIndex(
"OxStressR2");
648 const int pIdx = pd.getScalarDataIndex(
"OxPressure");
650 const bool hasVelocity = (vIdx != -1);
651 const bool hasStress = (r0Idx != -1 && r1Idx != -1 && r2Idx != -1);
652 const bool hasPressure = (pIdx != -1);
654 if (!hasVelocity && !hasStress && !hasPressure)
657 const auto *vd = hasVelocity ? pd.getVectorData(vIdx) :
nullptr;
658 const auto *r0d = hasStress ? pd.getVectorData(r0Idx) :
nullptr;
659 const auto *r1d = hasStress ? pd.getVectorData(r1Idx) :
nullptr;
660 const auto *r2d = hasStress ? pd.getVectorData(r2Idx) :
nullptr;
661 const auto *ppd = hasPressure ? pd.getScalarData(pIdx) :
nullptr;
663 previousVelocity_.assign(
nodes.size(), Vec3D<T>{});
664 previousPressure_.assign(
nodes.size(),
T(0));
667 for (; !it.isFinished(); ++it) {
670 const auto ptId = it.getPointId();
671 const IndexType idx = it.getStartIndices();
675 if (vd && ptId <
static_cast<decltype(ptId)
>(vd->size()) &&
676 isFiniteVec((*vd)[ptId]))
677 previousVelocity_[ni] = (*vd)[ptId];
678 if (ppd && ptId <
static_cast<decltype(ptId)
>(ppd->size()) &&
679 std::isfinite((*ppd)[ptId]))
680 previousPressure_[ni] = (*ppd)[ptId];
683 if (hasStress && ptId <
static_cast<decltype(ptId)
>(r0d->size()) &&
684 ptId <
static_cast<decltype(ptId)
>(r1d->size()) &&
685 ptId <
static_cast<decltype(ptId)
>(r2d->size())) {
686 const auto &row0 = (*r0d)[ptId];
687 const auto &row1 = (*r1d)[ptId];
688 const auto &row2 = (*r2d)[ptId];
689 std::array<T, 9> s{row0[0], row0[1], row0[2], row1[0], row1[1],
690 row1[2], row2[0], row2[1], row2[2]};
691 if (isFiniteTensor(s))
692 deviatoricStressHistory[idx] = s;
696 if (hasVelocity || hasPressure) {
697 hasPreviousSolution_ =
true;
700 for (std::size_t i = 0; i <
nodes.size(); ++i) {
701 nodes[i].velocity = previousVelocity_[i];
702 nodes[i].pressure = previousPressure_[i];
707#ifdef VIENNALS_GPU_BICGSTAB
712 void buildPressureGpuGeometry() {
713 const std::size_t n =
nodes.size();
714 const T eps = std::numeric_limits<T>::epsilon();
715 pressCoeffGpu_.assign(2 *
D * n, 0.0);
716 pressNeighId32_.assign(2 *
D * n, gpu::kNoNode);
717 actualDiagGpu_.assign(n, 0.0);
719 for (std::size_t
id = 0;
id < n; ++id) {
720 if (touchesAmbient_[
id]) {
721 actualDiagGpu_[id] = 1.0;
724 for (
unsigned dir = 0; dir <
D; ++dir) {
725 const unsigned fiNeg = dir * 2u;
726 const unsigned fiPos = dir * 2u + 1u;
727 IndexType nbNeg =
nodes[id].index;
729 IndexType nbPos =
nodes[id].index;
731 const std::size_t jNeg =
733 const std::size_t jPos =
736 auto effDist = [&](
unsigned fi, std::size_t j) ->
T {
739 const Boundary bt = faceBCTypes_[fi * n + id];
740 return (bt != Boundary::NONE) ? faceBCDists_[fi * n + id] :
gridDelta;
743 const T dNeg = effDist(fiNeg, jNeg);
744 const T dPos = effDist(fiPos, jPos);
745 const T dSum = dNeg + dPos;
749 auto processFace = [&](
unsigned fi, std::size_t j,
T d) {
750 const T c =
T(2) / (d * dSum);
751 if (j !=
noNode && !touchesAmbient_[j]) {
752 pressCoeffGpu_[fi * n + id] =
static_cast<double>(c);
753 pressNeighId32_[fi * n + id] =
static_cast<uint32_t
>(j);
754 actualDiagGpu_[id] += c;
756 faceBCTypes_[fi * n +
id] == Boundary::AMBIENT) {
761 actualDiagGpu_[id] += c;
764 processFace(fiNeg, jNeg, dNeg);
765 processFace(fiPos, jPos, dPos);
767 if (actualDiagGpu_[
id] <= eps)
768 actualDiagGpu_[id] = 1.0;
777 void buildStokesGpuGeometry() {
778 const std::size_t n =
nodes.size();
779 const T eps = std::numeric_limits<T>::epsilon();
780 stokesCoeffGpu_.assign(2 *
D * n, 0.0);
781 stokesNeighId32_.assign(2 *
D * n, gpu::kNoNode);
782 stokesDiagGpu_.assign(
D * n, 0.0);
784 auto addDiag = [&](std::size_t id,
T c) {
785 for (
unsigned comp = 0; comp <
D; ++comp)
786 stokesDiagGpu_[comp * n +
id] +=
static_cast<double>(c);
789 for (std::size_t
id = 0;
id < n; ++id) {
790 for (
unsigned dir = 0; dir <
D; ++dir) {
791 const unsigned fiNeg = dir * 2u;
792 const unsigned fiPos = dir * 2u + 1u;
793 IndexType nbNeg =
nodes[id].index;
795 IndexType nbPos =
nodes[id].index;
797 const bool negInBounds =
inBounds(nbNeg);
798 const bool posInBounds =
inBounds(nbPos);
799 const std::size_t jNeg =
801 const std::size_t jPos =
806 auto stokesFaceDist = [&](
unsigned fi,
bool inb, std::size_t j) ->
T {
809 const Boundary bt = faceBCTypes_[fi * n + id];
810 return (bt != Boundary::NONE) ? faceBCDists_[fi * n + id] :
gridDelta;
813 const T dNeg = stokesFaceDist(fiNeg, negInBounds, jNeg);
814 const T dPos = stokesFaceDist(fiPos, posInBounds, jPos);
815 const T dSum = dNeg + dPos;
819 auto processFace = [&](
unsigned fi,
bool inb, std::size_t j,
T d) {
820 const T c =
T(2) / (d * dSum);
823 stokesCoeffGpu_[fi * n + id] =
static_cast<double>(c);
824 stokesNeighId32_[fi * n + id] =
static_cast<uint32_t
>(j);
831 const Boundary bt = faceBCTypes_[fi * n + id];
832 if (bt == Boundary::REACTION || bt == Boundary::MASK)
837 processFace(fiNeg, negInBounds, jNeg, dNeg);
838 processFace(fiPos, posInBounds, jPos, dPos);
840 for (
unsigned comp = 0; comp <
D; ++comp)
841 if (stokesDiagGpu_[comp * n +
id] <=
static_cast<double>(eps))
842 stokesDiagGpu_[comp * n + id] = 1.0;
859 void buildHarmonicGpuGeometry() {
860 const std::size_t n =
nodes.size();
861 harmonicCoeffGpu_.assign(2 *
D * n, 0.0);
862 harmonicDiagGpu_.assign(n, 0.0);
863 for (std::size_t
id = 0;
id < n; ++id) {
864 for (
unsigned fi = 0; fi < 2 *
D; ++fi) {
865 if (stokesNeighId32_[fi * n +
id] != gpu::kNoNode) {
867 harmonicCoeffGpu_[fi * n + id] = 1.0;
868 harmonicDiagGpu_[id] += 1.0;
875 const Boundary bt = faceBCTypes_[fi * n + id];
876 if (bt == Boundary::REACTION || bt == Boundary::MASK)
877 harmonicDiagGpu_[id] += 1.0;
880 if (harmonicDiagGpu_[
id] < 1e-10)
881 harmonicDiagGpu_[id] = 1.0;
888 void setupDeformationGpuBuffers() {
889 const std::size_t n =
nodes.size();
891 gpu::freeGpuBuffers(gpuPressBufs_);
892 gpuPressBufs_ =
nullptr;
893 gpu::freeGpuBuffers(gpuStokesBufs_);
894 gpuStokesBufs_ =
nullptr;
904 gpu::freeGpuBuffers(gpuPressBufs_);
905 gpuPressBufs_ =
nullptr;
908 gpu::allocGpuBuffers(
static_cast<uint32_t
>(n), 2 *
D, useIlu0);
909 if (!gpuPressBufs_) {
910 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
911 "pressure solver CUDA buffers could not be "
912 "allocated or the CUDA context could not be "
914 }
else if (!gpu::gpuUploadNeighborIds(
915 gpuPressBufs_, pressNeighId32_.data(), 2u *
D * n)) {
916 gpu::freeGpuBuffers(gpuPressBufs_);
917 gpuPressBufs_ =
nullptr;
918 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
919 "uploading pressure GPU neighbor IDs failed.");
920 }
else if (useIlu0 &&
921 !gpu::gpuSetupCSR(gpuPressBufs_, pressNeighId32_.data(),
922 static_cast<uint32_t
>(n), 2 *
D)) {
923 gpu::freeGpuBuffers(gpuPressBufs_);
924 gpuPressBufs_ =
nullptr;
925 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
926 "CUSPARSE setup for the pressure GPU BiCGSTAB "
931 gpu::freeGpuBuffers(gpuStokesBufs_);
932 gpuStokesBufs_ =
nullptr;
935 gpu::allocGpuBuffers(
static_cast<uint32_t
>(n), 2 *
D, useIlu0);
936 if (!gpuStokesBufs_) {
937 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
938 "Stokes solver CUDA buffers could not be "
939 "allocated or the CUDA context could not be "
941 }
else if (!gpu::gpuUploadNeighborIds(
942 gpuStokesBufs_, stokesNeighId32_.data(), 2u *
D * n)) {
943 gpu::freeGpuBuffers(gpuStokesBufs_);
944 gpuStokesBufs_ =
nullptr;
945 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
946 "uploading Stokes GPU neighbor IDs failed.");
947 }
else if (useIlu0 &&
948 !gpu::gpuSetupCSR(gpuStokesBufs_, stokesNeighId32_.data(),
949 static_cast<uint32_t
>(n), 2 *
D)) {
950 gpu::freeGpuBuffers(gpuStokesBufs_);
951 gpuStokesBufs_ =
nullptr;
952 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
953 "CUSPARSE setup for the Stokes GPU BiCGSTAB "
959 gpu::freeGpuBuffers(gpuHarmonicBufs_);
960 gpuHarmonicBufs_ =
nullptr;
963 gpu::allocGpuBuffers(
static_cast<uint32_t
>(n), 2 *
D, useIlu0);
964 if (!gpuHarmonicBufs_) {
965 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
966 "harmonic solver CUDA buffers could not be "
968 }
else if (!gpu::gpuUploadNeighborIds(
969 gpuHarmonicBufs_, stokesNeighId32_.data(), 2u *
D * n)) {
970 gpu::freeGpuBuffers(gpuHarmonicBufs_);
971 gpuHarmonicBufs_ =
nullptr;
972 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
973 "uploading harmonic GPU neighbor IDs failed.");
974 }
else if (useIlu0 &&
975 !gpu::gpuSetupCSR(gpuHarmonicBufs_, stokesNeighId32_.data(),
976 static_cast<uint32_t
>(n), 2 *
D)) {
977 gpu::freeGpuBuffers(gpuHarmonicBufs_);
978 gpuHarmonicBufs_ =
nullptr;
979 reportGpuUnavailable(
"OxidationDeformation: GPU mode was selected, but "
980 "CUSPARSE setup for the harmonic GPU BiCGSTAB "
987 if (tryGpu && gpuPressBufs_ && gpuStokesBufs_ && gpuHarmonicBufs_)
988 logDeformationBackend(
"GPU BiCGSTAB",
989 "pressure/Stokes/harmonic, preconditioner=" +
990 std::string(useIlu0 ?
"ILU0" :
"Jacobi"));
994#ifdef VIENNALS_GPU_BICGSTAB
995 static std::string gpuErrorDetail() {
996 const char *detail = gpu::gpuGetLastErrorMessage();
997 if (detail && detail[0] !=
'\0')
998 return std::string(
" Detail: ") + detail;
1006 void reportGpuUnavailable(
const std::string &message)
const {
1008 VIENNACORE_LOG_WARNING(message + gpuErrorDetail() +
1009 " Falling back to the CPU solver.");
1011 VIENNACORE_LOG_ERROR(message + gpuErrorDetail());
1016 void logDeformationBackend(
const std::string &backend,
1017 const std::string &detail)
const {
1018 if (!Logger::hasInfo())
1020 const std::string msg =
"OxidationDeformation: using " + backend +
1021 " for mechanics pressure/Stokes solves (nodes=" +
1022 std::to_string(
nodes.size()) +
1023 (detail.empty() ? std::string() :
", " + detail) +
1025 if (msg == lastLoggedBackend_)
1027 lastLoggedBackend_ = msg;
1028 Logger::getInstance().addInfo(msg).print();
1034 reactionInterface, ambientInterface, maskInterface, useRequestedBounds,
1035 requestedMinIndex, requestedMaxIndex,
1036 deformationParameters.maxGridPoints,
"OxidationDeformation");
1043 ConstSparseIterator reactionIt(reactionInterface->getDomain());
1044 ConstSparseIterator ambientIt(ambientInterface->getDomain());
1049 const T reactionPhi =
valueAt(reactionIt, index);
1050 const T ambientPhi =
valueAt(ambientIt, index);
1053 const std::size_t
id =
nodes.size();
1055 nodes.push_back({index});
1063 const std::size_t n =
nodes.size();
1064 faceBCTypes_.assign(2 *
D * n, Boundary::NONE);
1065 faceBCDists_.assign(2 *
D * n,
T(1));
1066 touchesAmbient_.assign(n, uint8_t(0));
1067 for (std::size_t
id = 0;
id < n; ++id) {
1068 const auto &node =
nodes[id];
1069 bool touchesAmbient =
false;
1070 bool touchesSolidBoundary =
false;
1071 for (
unsigned dir = 0; dir <
D; ++dir) {
1072 for (
int off : {-1, 1}) {
1073 const unsigned fi = dir * 2u + (off == 1 ? 1u : 0u);
1074 IndexType nb = node.index;
1080 faceBCTypes_[fi * n + id] = bi.boundary;
1081 faceBCDists_[fi * n + id] = bi.distance;
1082 if (bi.boundary == Boundary::AMBIENT)
1083 touchesAmbient =
true;
1084 else if (bi.boundary == Boundary::MASK ||
1085 bi.boundary == Boundary::REACTION)
1086 touchesSolidBoundary =
true;
1093 touchesAmbient_[id] =
1094 (touchesAmbient && !touchesSolidBoundary) ? uint8_t(1) : uint8_t(0);
1097#ifdef VIENNALS_GPU_BICGSTAB
1098 buildPressureGpuGeometry();
1099 buildStokesGpuGeometry();
1100 buildHarmonicGpuGeometry();
1101 setupDeformationGpuBuffers();
1104 VIENNACORE_LOG_ERROR(
"OxidationDeformation: explicit GPU mode was "
1105 "requested, but ViennaLS was built without "
1106 "VIENNALS_GPU_BICGSTAB.");
1108 VIENNACORE_LOG_WARNING(
"OxidationDeformation: GPU mode Auto was "
1109 "requested, but ViennaLS was built without "
1110 "VIENNALS_GPU_BICGSTAB. Using the CPU solver.");
1119 template <
class SolverT>
1121 const std::vector<Vec3D<SolverT>> &v,
1122 Vec3D<T> &sum)
const {
1123 const auto &node =
nodes[nodeId];
1124 sum = {
T(0),
T(0),
T(0)};
1126 const auto toT = [](
const Vec3D<SolverT> &w) -> Vec3D<T> {
1127 return {
static_cast<T>(w[0]),
static_cast<T>(w[1]),
static_cast<T>(w[2])};
1130 for (
unsigned direction = 0; direction <
D; ++direction) {
1131 for (
int offset : {-1, 1}) {
1132 IndexType neighbor = node.index;
1133 neighbor[direction] += offset;
1136 sum = sum + toT(v[nodeId]);
1140 const std::size_t neighborId =
lookupNode(neighbor);
1141 if (neighborId !=
noNode) {
1142 sum = sum + toT(v[neighborId]);
1146 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
1147 const Boundary boundary = faceBCTypes_[fi *
nodes.size() + nodeId];
1148 if (boundary == Boundary::REACTION) {
1150 }
else if (boundary == Boundary::MASK) {
1153 sum = sum + toT(v[nodeId]);
1160 template <
class SolverT>
1162 const std::vector<Vec3D<T>> &b,
1163 std::vector<Vec3D<SolverT>> &Av)
const {
1164 const T diagVal =
static_cast<T>(2 *
D);
1165#pragma omp parallel for schedule(static)
1166 for (std::size_t i = 0; i <
nodes.size(); ++i) {
1169 for (
unsigned c = 0; c <
D; ++c)
1170 Av[i][c] =
static_cast<SolverT
>(diagVal * v[i][c] - sum[c] + b[i][c]);
1182 const std::size_t n =
nodes.size();
1183 const T diagVal =
static_cast<T>(2 *
D);
1188 std::vector<Vec3D<T>> b(n);
1190 const std::vector<Vec3D<SolverT>> zeros(
1191 n, Vec3D<SolverT>{SolverT(0), SolverT(0), SolverT(0)});
1192#pragma omp parallel for schedule(static)
1193 for (std::size_t i = 0; i < n; ++i)
1198 std::vector<Vec3D<SolverT>> x(n);
1199 for (std::size_t i = 0; i < n; ++i)
1200 for (
unsigned c = 0; c <
D; ++c) {
1201 const T value =
nodes[i].velocity[c];
1202 x[i][c] =
static_cast<SolverT
>(std::isfinite(value) ? value :
T(0));
1205#ifdef VIENNALS_GPU_BICGSTAB
1206 if (gpu::gpuIsValid(gpuHarmonicBufs_)) {
1207 const std::size_t nf = 2u *
D * n;
1208 if (harmonicDiagGpu_.size() != n || harmonicCoeffGpu_.size() != nf) {
1209 VIENNACORE_LOG_ERROR(
"OxidationDeformation: harmonic GPU geometry has "
1210 "the wrong size for the current node set.");
1213 Timer<> tUpload, tSolve;
1214 std::vector<Vec3D<SolverT>> xSolved(n);
1215 unsigned maxGpuIterations = 0;
1216 double maxGpuResidual = 0.0;
1218 for (
unsigned c = 0; c <
D; ++c) {
1219 std::vector<double> bGpu(n), xGpu(n);
1220 for (std::size_t i = 0; i < n; ++i) {
1221 bGpu[i] =
static_cast<double>(b[i][c]);
1222 xGpu[i] =
static_cast<double>(x[i][c]);
1226 const bool gpuUploadOk =
1227 (c == 0) ? gpu::gpuUploadSolverArrays(
1228 gpuHarmonicBufs_, harmonicDiagGpu_.data(),
1229 bGpu.data(), harmonicCoeffGpu_.data(),
1230 static_cast<uint32_t
>(n), harmonicCoeffGpu_.size())
1231 : gpu::gpuUploadRhs(gpuHarmonicBufs_, bGpu.data(),
1232 static_cast<uint32_t
>(n));
1235 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, "
1236 "but uploading harmonic solver arrays failed." +
1240 unsigned gpuIterations = 0;
1241 double gpuResidual = 0.0;
1243 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
1244 gpuHarmonicBufs_, xGpu.data(),
1245 static_cast<double>(std::numeric_limits<SolverT>::epsilon()),
1246 deformationParameters.harmonicIterations,
1247 static_cast<double>(deformationParameters.tolerance), gpuIterations,
1251 if (!gpuConverged || !std::isfinite(gpuResidual)) {
1252 VIENNACORE_LOG_ERROR(
1253 "OxidationDeformation: harmonic GPU BiCGSTAB failed or produced "
1254 "a non-finite residual for component " +
1255 std::to_string(c) +
" (iters=" + std::to_string(gpuIterations) +
1256 ", residual=" + std::to_string(gpuResidual) +
").");
1259 maxGpuIterations = std::max(maxGpuIterations, gpuIterations);
1260 maxGpuResidual = std::max(maxGpuResidual, gpuResidual);
1261 for (std::size_t i = 0; i < n; ++i)
1262 xSolved[i][c] =
static_cast<SolverT
>(xGpu[i]);
1265 for (std::size_t i = 0; i < n; ++i)
1266 for (
unsigned c = 0; c <
D; ++c)
1267 nodes[i].velocity[c] =
static_cast<T>(xSolved[i][c]);
1268 iterations = maxGpuIterations;
1269 residual = maxGpuResidual;
1271 if (Logger::hasTiming()) {
1272 Logger::getInstance()
1273 .addTiming(
"harmonic n=" + std::to_string(n) +
1274 " iters=" + std::to_string(iterations) +
" res=" +
1275 std::to_string(residual) +
" [GPU] GPU BiCGSTAB",
1279 if (Logger::hasDebug()) {
1280 Logger::getInstance()
1281 .addTiming(
"harmonic n=" + std::to_string(n) +
" [GPU] GPU upload",
1290 const Vec3D<SolverT> zero3{SolverT(0), SolverT(0), SolverT(0)};
1291 std::vector<Vec3D<SolverT>> Ax(n);
1293 std::vector<Vec3D<SolverT>> r(n), r_hat(n);
1294 for (std::size_t i = 0; i < n; ++i)
1295 for (
unsigned c = 0; c <
D; ++c) {
1296 r[i][c] =
static_cast<SolverT
>(b[i][c] - Ax[i][c]);
1297 r_hat[i][c] = r[i][c];
1301 std::vector<Vec3D<SolverT>> pv(n, zero3), sv(n, zero3), y(n), z(n), s(n),
1303 T rho =
T(1), alpha =
T(1), omega =
T(1);
1305 auto vecDot = [&](
const std::vector<Vec3D<SolverT>> &a,
1306 const std::vector<Vec3D<SolverT>> &bv) {
1308 for (std::size_t i = 0; i < n; ++i)
1309 for (
unsigned c = 0; c <
D; ++c)
1310 sum +=
static_cast<T>(a[i][c]) *
static_cast<T>(bv[i][c]);
1314 auto vecMaxAbs = [&](
const std::vector<Vec3D<SolverT>> &vin) {
1316 for (std::size_t i = 0; i < n; ++i)
1317 for (
unsigned c = 0; c <
D; ++c)
1318 m = std::max(m, std::abs(
static_cast<T>(vin[i][c])));
1322 const T b_norm = [&] {
1324 for (std::size_t i = 0; i < n; ++i)
1325 for (
unsigned c = 0; c <
D; ++c)
1326 m = std::max(m, std::abs(b[i][c]));
1327 return (m <
T(1e-100)) ?
T(1) : m;
1330 for (; iterations < deformationParameters.harmonicIterations;
1332 const T rho_new = vecDot(r_hat, r);
1333 if (!std::isfinite(rho_new) || std::abs(rho_new) <
T(1e-100))
1335 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
1336 !std::isfinite(omega) || std::abs(omega) <
T(1e-100))
1339 const T beta = (rho_new / rho) * (alpha / omega);
1340 if (!std::isfinite(beta))
1344 for (std::size_t i = 0; i < n; ++i)
1345 for (
unsigned c = 0; c <
D; ++c)
1346 pv[i][c] =
static_cast<SolverT
>(r[i][c] +
1347 beta * (pv[i][c] - omega * sv[i][c]));
1350 for (std::size_t i = 0; i < n; ++i)
1351 for (
unsigned c = 0; c <
D; ++c)
1352 y[i][c] =
static_cast<SolverT
>(
static_cast<T>(pv[i][c]) / diagVal);
1356 const T r_hat_v = vecDot(r_hat, sv);
1357 if (!std::isfinite(r_hat_v) || std::abs(r_hat_v) <
T(1e-100))
1360 alpha = rho_new / r_hat_v;
1361 if (!std::isfinite(alpha))
1364 for (std::size_t i = 0; i < n; ++i)
1365 for (
unsigned c = 0; c <
D; ++c)
1366 s[i][c] =
static_cast<SolverT
>(r[i][c] - alpha * sv[i][c]);
1368 residual = vecMaxAbs(s);
1369 if (!std::isfinite(residual))
1371 if (residual < deformationParameters.tolerance * b_norm) {
1372 for (std::size_t i = 0; i < n; ++i)
1373 for (
unsigned c = 0; c <
D; ++c)
1374 x[i][c] =
static_cast<SolverT
>(x[i][c] + alpha * y[i][c]);
1380 for (std::size_t i = 0; i < n; ++i)
1381 for (
unsigned c = 0; c <
D; ++c)
1382 z[i][c] =
static_cast<SolverT
>(
static_cast<T>(s[i][c]) / diagVal);
1386 const T t_s = vecDot(t, s);
1387 const T t_t = vecDot(t, t);
1388 if (!std::isfinite(t_s) || !std::isfinite(t_t))
1390 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
1391 if (!std::isfinite(omega))
1394 for (std::size_t i = 0; i < n; ++i)
1395 for (
unsigned c = 0; c <
D; ++c) {
1397 static_cast<SolverT
>(x[i][c] + alpha * y[i][c] + omega * z[i][c]);
1398 r[i][c] =
static_cast<SolverT
>(s[i][c] - omega * t[i][c]);
1401 residual = vecMaxAbs(r);
1402 if (!std::isfinite(residual))
1404 if (residual < deformationParameters.tolerance * b_norm) {
1410 bool finiteSolution =
true;
1411 for (std::size_t i = 0; i < n; ++i)
1412 for (
unsigned c = 0; c <
D; ++c)
1413 if (!std::isfinite(
static_cast<T>(x[i][c])))
1414 finiteSolution =
false;
1416 if (finiteSolution) {
1417 for (std::size_t i = 0; i < n; ++i)
1418 for (
unsigned c = 0; c <
D; ++c)
1419 nodes[i].velocity[c] =
static_cast<T>(x[i][c]);
1421 residual = std::numeric_limits<T>::infinity();
1423 if (residual > deformationParameters.tolerance * b_norm)
1424 VIENNACORE_LOG_WARNING(
1425 "solveVelocity (harmonic): BiCGSTAB did not converge after " +
1426 std::to_string(iterations) +
"/" +
1427 std::to_string(deformationParameters.harmonicIterations) +
1428 " iterations (residual=" + std::to_string(residual / b_norm) +
1429 ", tolerance=" + std::to_string(deformationParameters.tolerance) +
1440 const std::size_t n =
nodes.size();
1441 std::vector<Vec3D<T>> diagV(n, Vec3D<T>{
T(0),
T(0),
T(0)});
1444 const std::vector<Vec3D<T>> zeros(n, Vec3D<T>{
T(0),
T(0),
T(0)});
1445 std::vector<Vec3D<T>> tmp(n);
1446#pragma omp parallel for schedule(static)
1447 for (std::size_t i = 0; i < n; ++i) {
1450 for (
unsigned comp = 0; comp <
D; ++comp)
1451 diagV[i][comp] = diag;
1458 T mechanicsResidual = 0.;
1476 const std::vector<Vec3D<T>> diagV =
1479 for (
unsigned iteration = 0;
1480 iteration < deformationParameters.mechanicsIterations; ++iteration) {
1488 Timer<> tStokes, tPressure;
1492 if (!std::isfinite(lastStokesResidual_)) {
1493 mechanicsResidual = std::numeric_limits<T>::infinity();
1501 if (!std::isfinite(lastPressureResidual_)) {
1502 mechanicsResidual = std::numeric_limits<T>::infinity();
1511 if (!std::isfinite(mechanicsResidual)) {
1512 mechanicsResidual = std::numeric_limits<T>::infinity();
1516 if (Logger::hasDebug())
1517 Logger::getInstance()
1519 " mechanics[" + std::to_string(iteration) +
1520 "] stokes iters=" + std::to_string(lastStokesIters_) +
1522 std::to_string(deformationParameters.stokesIterations) +
1523 " res=" + std::to_string(lastStokesResidual_),
1526 " mechanics[" + std::to_string(iteration) +
1527 "] pressure iters=" + std::to_string(lastPressureIters_) +
1529 std::to_string(deformationParameters.pressureIterations) +
1530 " res=" + std::to_string(lastPressureResidual_) +
1531 " coupling=" + std::to_string(mechanicsResidual),
1535 if (mechanicsResidual < deformationParameters.mechanicsTolerance)
1541 residual = mechanicsResidual;
1542 if (residual > deformationParameters.mechanicsTolerance)
1543 VIENNACORE_LOG_WARNING(
1544 "solveMechanics: did not converge after " +
1545 std::to_string(deformationParameters.mechanicsIterations) +
1546 " iterations (residual=" + std::to_string(residual) +
", tolerance=" +
1547 std::to_string(deformationParameters.mechanicsTolerance) +
")");
1557 const std::vector<Vec3D<T>> &diagV) {
1560 if (deformationParameters.viscosity <= std::numeric_limits<T>::epsilon())
1563 const std::size_t n =
nodes.size();
1564 const T invEta =
T(1) / deformationParameters.viscosity;
1566#pragma omp parallel for schedule(static)
1567 for (std::size_t i = 0; i < n; ++i) {
1568 for (
unsigned dir = 0; dir <
D; ++dir) {
1569 const T ai = diagV[i][dir];
1570 if (ai <= std::numeric_limits<T>::epsilon())
1576 const unsigned fi = dir * 2u;
1577 IndexType nb =
nodes[i].index;
1582 dpMinus =
nodes[j].pressure - pressureOld[j];
1586 dMinus = faceBCDists_[fi * n + i];
1597 const unsigned fi = dir * 2u + 1u;
1598 IndexType nb =
nodes[i].index;
1603 dpPlus =
nodes[j].pressure - pressureOld[j];
1607 dPlus = faceBCDists_[fi * n + i];
1615 const T dpCenter =
nodes[i].pressure - pressureOld[i];
1618 const T correction =
1619 gradDP * invEta / ai * deformationParameters.relaxation;
1620 if (std::isfinite(correction) && std::isfinite(
nodes[i].velocity[dir]))
1621 nodes[i].velocity[dir] -= correction;
1629 template <
class SolverT>
1631 const std::vector<SolverT> &p,
1632 const std::vector<T> &ambientBP,
T &diag,
1634 if (touchesAmbient_[nodeId]) {
1636 rhs = ambientBP[nodeId];
1641 for (
unsigned direction = 0; direction <
D; ++direction) {
1646 const T dSum = plus.distance + minus.distance;
1647 const T plusCoeff =
T(2) / (plus.distance * dSum);
1648 const T minusCoeff =
T(2) / (minus.distance * dSum);
1649 rhs += plusCoeff * plus.value + minusCoeff * minus.value;
1650 diag += plusCoeff + minusCoeff;
1655 template <
class SolverT>
1658 const std::vector<T> &precomputedDiag,
1659 const std::vector<T> &pBC, std::vector<SolverT> &Av)
const {
1660#pragma omp parallel for schedule(static)
1661 for (std::size_t i = 0; i <
nodes.size(); ++i) {
1664 Av[i] =
static_cast<SolverT
>(precomputedDiag[i] * v[i] - rhs + pBC[i]);
1674 const std::size_t n =
nodes.size();
1675 const T eps = std::numeric_limits<T>::epsilon();
1677 std::vector<T> divergence(n), ambientBP(n);
1678#pragma omp parallel for schedule(static)
1679 for (std::size_t i = 0; i < n; ++i) {
1684 auto warnBadPressureAssembly = [](
const std::string &stage,
1685 std::size_t nodeId,
const IndexType &idx,
1687 VIENNACORE_LOG_WARNING(
1688 "solvePressure: non-finite/overflow " + stage +
1689 " at node=" + std::to_string(nodeId) +
" index=(" +
1690 std::to_string(idx[0]) +
"," + std::to_string(idx[1]) +
1691 (
D == 3 ?
"," + std::to_string(idx[2]) : std::string()) +
1692 ") value=" + std::to_string(value));
1695 const T solverMax =
static_cast<T>(std::numeric_limits<SolverT>::max());
1696 for (std::size_t i = 0; i < n; ++i) {
1697 if (!std::isfinite(divergence[i]) ||
1698 std::abs(divergence[i]) > solverMax) {
1699 warnBadPressureAssembly(
"divergence", i,
nodes[i].index, divergence[i]);
1702 if (!std::isfinite(ambientBP[i]) || std::abs(ambientBP[i]) > solverMax) {
1703 warnBadPressureAssembly(
"ambient pressure boundary", i,
nodes[i].index,
1710 std::vector<T> diag(n), pBC(n);
1712 const std::vector<SolverT> zeros(n, SolverT(0));
1713#pragma omp parallel for schedule(static)
1714 for (std::size_t i = 0; i < n; ++i)
1718 for (std::size_t i = 0; i < n; ++i) {
1719 if (!std::isfinite(diag[i]) || std::abs(diag[i]) > solverMax) {
1720 warnBadPressureAssembly(
"pressure diagonal", i,
nodes[i].index,
1724 if (!std::isfinite(pBC[i]) || std::abs(pBC[i]) > solverMax) {
1725 warnBadPressureAssembly(
"pressure boundary rhs", i,
nodes[i].index,
1731 std::vector<T> b(n);
1733 for (std::size_t i = 0; i < n; ++i) {
1734 b[i] = pBC[i] + deformationParameters.bulkModulus * divergence[i];
1735 b_norm = std::max(b_norm, std::abs(b[i]));
1737 for (std::size_t i = 0; i < n; ++i) {
1738 if (!std::isfinite(b[i]) || std::abs(b[i]) > solverMax) {
1739 warnBadPressureAssembly(
"pressure rhs", i,
nodes[i].index, b[i]);
1743 if (b_norm <
T(1e-100))
1746 std::vector<SolverT> x(n);
1747 for (std::size_t i = 0; i < n; ++i) {
1748 T guess = touchesAmbient_[i] ? ambientBP[i] :
nodes[i].pressure;
1749 if (!std::isfinite(guess))
1750 guess = deformationParameters.ambientPressure;
1751 x[i] =
static_cast<SolverT
>(guess);
1754#ifdef VIENNALS_GPU_BICGSTAB
1755 if (gpu::gpuIsValid(gpuPressBufs_)) {
1756 const std::size_t nf = 2u *
D * n;
1757 if (actualDiagGpu_.size() != n || pressCoeffGpu_.size() != nf) {
1758 VIENNACORE_LOG_ERROR(
"OxidationDeformation: pressure GPU geometry has "
1759 "the wrong size for the current node set.");
1762 Timer<> tUpload, tSolve;
1763 std::vector<double> bGpu(n), xGpu(n);
1764 for (std::size_t i = 0; i < n; ++i) {
1765 bGpu[i] =
static_cast<double>(b[i]);
1766 xGpu[i] =
static_cast<double>(x[i]);
1770 const bool gpuUploadOk = gpu::gpuUploadSolverArrays(
1771 gpuPressBufs_, actualDiagGpu_.data(), bGpu.data(),
1772 pressCoeffGpu_.data(),
static_cast<uint32_t
>(n),
1773 pressCoeffGpu_.size());
1776 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, but "
1777 "uploading pressure solver arrays or factorizing "
1782 unsigned gpuIterations = 0;
1783 double gpuResidual = 0.0;
1785 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
1786 gpuPressBufs_, xGpu.data(),
static_cast<double>(eps),
1787 deformationParameters.pressureIterations,
1788 static_cast<double>(deformationParameters.pressureTolerance),
1789 gpuIterations, gpuResidual);
1795 if (!gpuConverged || !std::isfinite(gpuResidual)) {
1796 VIENNACORE_LOG_ERROR(
1797 "OxidationDeformation: pressure GPU BiCGSTAB failed or produced "
1798 "a non-finite residual (iters=" +
1799 std::to_string(gpuIterations) +
1800 ", residual=" + std::to_string(gpuResidual) +
").");
1804 const T beta = deformationParameters.pressureRelaxation;
1805 const T oneMinB =
T(1) - beta;
1806 for (std::size_t i = 0; i < n; ++i)
1808 oneMinB *
nodes[i].pressure + beta *
static_cast<T>(xGpu[i]);
1810 lastPressureIters_ = gpuIterations;
1811 lastPressureResidual_ = gpuResidual / b_norm;
1813 if (Logger::hasDebug()) {
1814 const std::string tag =
1815 "pressure n=" + std::to_string(n) +
1816 " iters=" + std::to_string(lastPressureIters_) +
1817 " res=" + std::to_string(lastPressureResidual_) +
" [GPU]";
1818 Logger::getInstance()
1819 .addTiming(tag +
" GPU upload", tUpload)
1820 .addTiming(tag +
" GPU BiCGSTAB", tSolve)
1842 std::vector<T> pressCoeff(2 *
D * n,
T(0));
1843 std::vector<std::size_t> pressNeighId(2 *
D * n,
noNode);
1844 std::vector<T> actualDiag(n,
T(0));
1846 for (std::size_t
id = 0;
id < n; ++id) {
1847 if (touchesAmbient_[
id]) {
1848 actualDiag[id] =
T(1);
1851 for (
unsigned dir = 0; dir <
D; ++dir) {
1852 const unsigned fiNeg = dir * 2u;
1853 const unsigned fiPos = dir * 2u + 1u;
1854 IndexType nbNeg =
nodes[id].index;
1856 IndexType nbPos =
nodes[id].index;
1858 const std::size_t jNeg =
1860 const std::size_t jPos =
1868 auto effectiveDist = [&](
unsigned fi, std::size_t j) ->
T {
1871 const Boundary bt = faceBCTypes_[fi * n + id];
1872 if (bt != Boundary::NONE)
1873 return faceBCDists_[fi * n + id];
1877 const T dNeg = effectiveDist(fiNeg, jNeg);
1878 const T dPos = effectiveDist(fiPos, jPos);
1879 const T dSum = dNeg + dPos;
1883 if (jNeg !=
noNode && !touchesAmbient_[jNeg]) {
1884 const T c =
T(2) / (dNeg * dSum);
1885 pressCoeff[fiNeg * n + id] = c;
1886 pressNeighId[fiNeg * n + id] = jNeg;
1887 actualDiag[id] += c;
1888 }
else if (jNeg !=
noNode ||
1889 faceBCTypes_[fiNeg * n +
id] == Boundary::AMBIENT) {
1894 actualDiag[id] +=
T(2) / (dNeg * dSum);
1896 if (jPos !=
noNode && !touchesAmbient_[jPos]) {
1897 const T c =
T(2) / (dPos * dSum);
1898 pressCoeff[fiPos * n + id] = c;
1899 pressNeighId[fiPos * n + id] = jPos;
1900 actualDiag[id] += c;
1901 }
else if (jPos !=
noNode ||
1902 faceBCTypes_[fiPos * n +
id] == Boundary::AMBIENT) {
1903 actualDiag[id] +=
T(2) / (dPos * dSum);
1908 if (actualDiag[
id] <= eps)
1909 actualDiag[id] =
T(1);
1933 std::vector<T> ilu_diag(n);
1934 for (std::size_t
id = 0;
id < n; ++id) {
1935 if (touchesAmbient_[
id]) {
1936 ilu_diag[id] =
T(1);
1939 ilu_diag[id] = actualDiag[id];
1940 for (
unsigned dir = 0; dir <
D; ++dir) {
1941 const unsigned fi_L = dir * 2u;
1942 const unsigned fi_U =
1944 const std::size_t j = pressNeighId[fi_L * n + id];
1945 if (j ==
noNode || ilu_diag[j] <= eps)
1954 pressCoeff[fi_L * n + id] * pressCoeff[fi_U * n + j] / ilu_diag[j];
1956 if (ilu_diag[
id] <= eps)
1957 ilu_diag[id] = actualDiag[id];
1960 auto applyIlu = [&](
const std::vector<SolverT> &in,
1961 std::vector<SolverT> &out) {
1962 std::vector<T> y(n);
1964 for (std::size_t i = 0; i < n; ++i) {
1965 T val =
static_cast<T>(in[i]);
1966 for (
unsigned dir = 0; dir <
D; ++dir) {
1967 const unsigned fi_L = dir * 2u;
1968 const std::size_t j = pressNeighId[fi_L * n + i];
1973 val += (pressCoeff[fi_L * n + i] / ilu_diag[j]) * y[j];
1978 for (std::size_t i = n; i-- > 0;) {
1980 for (
unsigned dir = 0; dir <
D; ++dir) {
1981 const unsigned fi_U = dir * 2u + 1u;
1982 const std::size_t j = pressNeighId[fi_U * n + i];
1987 val += pressCoeff[fi_U * n + i] *
static_cast<T>(out[j]);
1989 out[i] =
static_cast<SolverT
>(val / ilu_diag[i]);
1993 std::vector<SolverT> Ax(n);
1995 for (std::size_t i = 0; i < n; ++i) {
1996 if (!std::isfinite(
static_cast<T>(Ax[i])) ||
1997 std::abs(
static_cast<T>(Ax[i])) > solverMax) {
1998 warnBadPressureAssembly(
"initial pressure matvec", i,
nodes[i].index,
1999 static_cast<T>(Ax[i]));
2003 std::vector<SolverT> r(n), r_hat(n), p(n, SolverT(0)), v(n, SolverT(0)),
2004 y(n), z(n), s(n), t(n);
2005 for (std::size_t i = 0; i < n; ++i) {
2006 r[i] =
static_cast<SolverT
>(b[i] - Ax[i]);
2010 T rho =
T(1), alpha =
T(1), omega =
T(1);
2011 T pressureResidual =
T(0);
2012 unsigned pressureIter = 0;
2013 bool pressureBreakdown =
false;
2014 for (std::size_t i = 0; i < n; ++i) {
2015 const T ri =
static_cast<T>(r[i]);
2016 if (!std::isfinite(ri)) {
2017 pressureBreakdown =
true;
2020 pressureResidual = std::max(pressureResidual, std::abs(ri));
2023 for (; !pressureBreakdown &&
2024 pressureIter < deformationParameters.pressureIterations;
2027 for (std::size_t i = 0; i < n; ++i)
2028 rho_new +=
static_cast<T>(r_hat[i]) *
static_cast<T>(r[i]);
2030 if (!std::isfinite(rho_new)) {
2031 pressureBreakdown =
true;
2034 if (std::abs(rho_new) <
T(1e-100))
2036 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
2037 !std::isfinite(omega) || std::abs(omega) <
T(1e-100)) {
2038 pressureBreakdown =
true;
2042 const T beta = (rho_new / rho) * (alpha / omega);
2043 if (!std::isfinite(beta)) {
2044 pressureBreakdown =
true;
2049 for (std::size_t i = 0; i < n; ++i)
2050 p[i] =
static_cast<SolverT
>(r[i] + beta * (p[i] - omega * v[i]));
2057 for (std::size_t i = 0; i < n; ++i)
2058 r_hat_v +=
static_cast<T>(r_hat[i]) *
static_cast<T>(v[i]);
2059 if (!std::isfinite(r_hat_v)) {
2060 pressureBreakdown =
true;
2063 if (std::abs(r_hat_v) <
T(1e-100))
2066 alpha = rho_new / r_hat_v;
2067 if (!std::isfinite(alpha)) {
2068 pressureBreakdown =
true;
2072 for (std::size_t i = 0; i < n; ++i)
2073 s[i] =
static_cast<SolverT
>(r[i] - alpha * v[i]);
2075 pressureResidual =
T(0);
2076 for (std::size_t i = 0; i < n; ++i)
2078 std::max(pressureResidual, std::abs(
static_cast<T>(s[i])));
2079 if (!std::isfinite(pressureResidual)) {
2080 pressureBreakdown =
true;
2083 if (pressureResidual < deformationParameters.pressureTolerance * b_norm) {
2084 for (std::size_t i = 0; i < n; ++i)
2085 x[i] =
static_cast<SolverT
>(x[i] + alpha * y[i]);
2093 T t_s =
T(0), t_t =
T(0);
2094 for (std::size_t i = 0; i < n; ++i) {
2095 t_s +=
static_cast<T>(t[i]) *
static_cast<T>(s[i]);
2096 t_t +=
static_cast<T>(t[i]) *
static_cast<T>(t[i]);
2098 if (!std::isfinite(t_s) || !std::isfinite(t_t)) {
2099 pressureBreakdown =
true;
2102 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
2103 if (!std::isfinite(omega)) {
2104 pressureBreakdown =
true;
2108 for (std::size_t i = 0; i < n; ++i) {
2109 x[i] =
static_cast<SolverT
>(x[i] + alpha * y[i] + omega * z[i]);
2110 r[i] =
static_cast<SolverT
>(s[i] - omega * t[i]);
2113 pressureResidual =
T(0);
2114 for (std::size_t i = 0; i < n; ++i)
2116 std::max(pressureResidual, std::abs(
static_cast<T>(r[i])));
2117 if (!std::isfinite(pressureResidual)) {
2118 pressureBreakdown =
true;
2121 if (pressureResidual < deformationParameters.pressureTolerance * b_norm)
2125 if (pressureBreakdown)
2126 pressureResidual = std::numeric_limits<T>::infinity();
2128 bool finiteSolution = !pressureBreakdown;
2129 for (std::size_t i = 0; i < n; ++i)
2130 if (!std::isfinite(
static_cast<T>(x[i])))
2131 finiteSolution =
false;
2133 lastPressureIters_ = pressureIter;
2134 lastPressureResidual_ = pressureResidual / b_norm;
2135 if (finiteSolution) {
2136 const T beta = deformationParameters.pressureRelaxation;
2137 const T oneMinB =
T(1) - beta;
2138 for (std::size_t i = 0; i < n; ++i)
2140 oneMinB *
nodes[i].pressure + beta *
static_cast<T>(x[i]);
2142 lastPressureResidual_ = std::numeric_limits<T>::infinity();
2144 if (lastPressureResidual_ > deformationParameters.pressureTolerance)
2145 VIENNACORE_LOG_WARNING(
2146 "solvePressure: BiCGSTAB did not converge after " +
2147 std::to_string(lastPressureIters_) +
"/" +
2148 std::to_string(deformationParameters.pressureIterations) +
2149 " iterations (residual=" + std::to_string(lastPressureResidual_) +
2151 std::to_string(deformationParameters.pressureTolerance) +
")");
2156 template <
class SolverT>
2158 const std::vector<Vec3D<SolverT>> &v,
T &diag,
2159 Vec3D<T> &rhs)
const {
2161 rhs = {
T(0),
T(0),
T(0)};
2162 for (
unsigned direction = 0; direction <
D; ++direction) {
2165 const T dSum = plus.distance + minus.distance;
2166 const T plusCoeff =
T(2) / (plus.distance * dSum);
2167 const T minusCoeff =
T(2) / (minus.distance * dSum);
2168 rhs = rhs + plusCoeff * plus.value + minusCoeff * minus.value;
2169 diag += plusCoeff + minusCoeff;
2174 if (deformationParameters.viscosity <= std::numeric_limits<T>::epsilon())
2181 const std::size_t n =
nodes.size();
2182 const T eps = std::numeric_limits<T>::epsilon();
2185 std::vector<T> diag(n);
2186 std::vector<Vec3D<T>> vBC(n), forcing(n);
2188 const std::vector<Vec3D<SolverT>> zeros(
2189 n, Vec3D<SolverT>{SolverT(0), SolverT(0), SolverT(0)});
2190#pragma omp parallel for schedule(static)
2191 for (std::size_t i = 0; i < n; ++i) {
2198 std::vector<Vec3D<T>> b(n);
2200 for (std::size_t i = 0; i < n; ++i) {
2201 for (
unsigned c = 0; c <
D; ++c) {
2202 b[i][c] = vBC[i][c] - forcing[i][c] / deformationParameters.viscosity;
2203 b_norm = std::max(b_norm, std::abs(b[i][c]));
2206 if (b_norm <
T(1e-100))
2211 std::vector<Vec3D<SolverT>> x(n);
2214 for (std::size_t i = 0; i < n; ++i)
2215 for (
unsigned c = 0; c <
D; ++c) {
2216 const T value = vel[i][c];
2217 x[i][c] =
static_cast<SolverT
>(std::isfinite(value) ? value :
T(0));
2221#ifdef VIENNALS_GPU_BICGSTAB
2222 if (gpu::gpuIsValid(gpuStokesBufs_)) {
2223 const std::size_t nf = 2u *
D * n;
2224 if (stokesDiagGpu_.size() !=
D * n || stokesCoeffGpu_.size() != nf) {
2225 VIENNACORE_LOG_ERROR(
"OxidationDeformation: Stokes GPU geometry has "
2226 "the wrong size for the current node set.");
2229 Timer<> tUpload, tSolve;
2230 std::vector<Vec3D<SolverT>> xSolved(n);
2231 unsigned maxGpuIterations = 0;
2232 double maxGpuResidual = 0.0;
2234 for (
unsigned c = 0; c <
D; ++c) {
2235 std::vector<double> bGpu(n), xGpu(n);
2236 for (std::size_t i = 0; i < n; ++i) {
2237 bGpu[i] =
static_cast<double>(b[i][c]);
2238 xGpu[i] =
static_cast<double>(x[i][c]);
2242 const bool gpuUploadOk = gpu::gpuUploadSolverArrays(
2243 gpuStokesBufs_, stokesDiagGpu_.data() + c * n, bGpu.data(),
2244 stokesCoeffGpu_.data(),
static_cast<uint32_t
>(n),
2245 stokesCoeffGpu_.size());
2248 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, "
2249 "but uploading Stokes solver arrays failed." +
2253 unsigned gpuIterations = 0;
2254 double gpuResidual = 0.0;
2256 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
2257 gpuStokesBufs_, xGpu.data(),
static_cast<double>(eps),
2258 deformationParameters.stokesIterations,
2259 static_cast<double>(deformationParameters.stokesTolerance),
2260 gpuIterations, gpuResidual);
2263 if (!gpuConverged || !std::isfinite(gpuResidual)) {
2264 VIENNACORE_LOG_ERROR(
2265 "OxidationDeformation: Stokes GPU BiCGSTAB failed or produced "
2266 "a non-finite residual for component " +
2267 std::to_string(c) +
" (iters=" + std::to_string(gpuIterations) +
2268 ", residual=" + std::to_string(gpuResidual) +
").");
2271 maxGpuIterations = std::max(maxGpuIterations, gpuIterations);
2272 maxGpuResidual = std::max(maxGpuResidual, gpuResidual);
2273 for (std::size_t i = 0; i < n; ++i)
2274 xSolved[i][c] =
static_cast<SolverT
>(xGpu[i]);
2277 for (std::size_t i = 0; i < n; ++i)
2278 for (
unsigned c = 0; c <
D; ++c)
2279 nodes[i].velocity[c] =
static_cast<T>(xSolved[i][c]);
2281 lastStokesIters_ = maxGpuIterations;
2282 lastStokesResidual_ = maxGpuResidual / b_norm;
2284 if (Logger::hasDebug()) {
2285 const std::string tag =
"stokes n=" + std::to_string(n) +
2286 " iters=" + std::to_string(lastStokesIters_) +
2287 " res=" + std::to_string(lastStokesResidual_) +
2289 Logger::getInstance()
2290 .addTiming(tag +
" GPU upload", tUpload)
2291 .addTiming(tag +
" GPU BiCGSTAB", tSolve)
2300 auto stokesMatvec = [&](
const std::vector<Vec3D<SolverT>> &vin,
2301 std::vector<Vec3D<SolverT>> &Av) {
2302#pragma omp parallel for schedule(static)
2303 for (std::size_t i = 0; i < n; ++i) {
2307 for (
unsigned c = 0; c <
D; ++c)
2309 static_cast<SolverT
>(diag[i] * vin[i][c] - rhs[c] + vBC[i][c]);
2314 auto vecDot = [&](
const std::vector<Vec3D<SolverT>> &a,
2315 const std::vector<Vec3D<SolverT>> &bv) {
2317 for (std::size_t i = 0; i < n; ++i)
2318 for (
unsigned c = 0; c <
D; ++c) {
2319 const T av =
static_cast<T>(a[i][c]);
2320 const T bvVal =
static_cast<T>(bv[i][c]);
2321 if (!std::isfinite(av) || !std::isfinite(bvVal))
2322 return std::numeric_limits<T>::quiet_NaN();
2328 auto vecMaxAbs = [&](
const std::vector<Vec3D<SolverT>> &vin) {
2330 for (std::size_t i = 0; i < n; ++i)
2331 for (
unsigned c = 0; c <
D; ++c) {
2332 const T value =
static_cast<T>(vin[i][c]);
2333 if (!std::isfinite(value))
2334 return std::numeric_limits<T>::infinity();
2335 m = std::max(m, std::abs(value));
2341 const Vec3D<SolverT> zero3{SolverT(0), SolverT(0), SolverT(0)};
2342 std::vector<Vec3D<SolverT>> Ax(n), r(n), r_hat(n);
2343 std::vector<Vec3D<SolverT>> pv(n, zero3), sv(n, zero3), y(n), z(n), s(n),
2345 stokesMatvec(x, Ax);
2346 for (std::size_t i = 0; i < n; ++i)
2347 for (
unsigned c = 0; c <
D; ++c) {
2348 r[i][c] =
static_cast<SolverT
>(b[i][c] - Ax[i][c]);
2349 r_hat[i][c] = r[i][c];
2352 T rho =
T(1), alpha =
T(1), omega =
T(1);
2353 T velocityResidual =
T(0);
2354 unsigned stokesIter = 0;
2355 bool stokesBreakdown =
false;
2356 velocityResidual = vecMaxAbs(r);
2358 for (; stokesIter < deformationParameters.stokesIterations; ++stokesIter) {
2359 const T rho_new = vecDot(r_hat, r);
2360 if (!std::isfinite(rho_new)) {
2361 stokesBreakdown =
true;
2364 if (std::abs(rho_new) <
T(1e-100))
2366 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
2367 !std::isfinite(omega) || std::abs(omega) <
T(1e-100)) {
2368 stokesBreakdown =
true;
2372 const T beta = (rho_new / rho) * (alpha / omega);
2373 if (!std::isfinite(beta)) {
2374 stokesBreakdown =
true;
2379 for (std::size_t i = 0; i < n; ++i)
2380 for (
unsigned c = 0; c <
D; ++c)
2381 pv[i][c] =
static_cast<SolverT
>(r[i][c] +
2382 beta * (pv[i][c] - omega * sv[i][c]));
2384 for (std::size_t i = 0; i < n; ++i)
2385 for (
unsigned c = 0; c <
D; ++c) {
2386 const T pvc = pv[i][c];
2387 const T pcDiag = precondDiag[i][c];
2388 y[i][c] =
static_cast<SolverT
>((pcDiag > eps) ? pvc / pcDiag : pvc);
2391 stokesMatvec(y, sv);
2393 const T r_hat_v = vecDot(r_hat, sv);
2394 if (!std::isfinite(r_hat_v)) {
2395 stokesBreakdown =
true;
2398 if (std::abs(r_hat_v) <
T(1e-100))
2401 alpha = rho_new / r_hat_v;
2402 if (!std::isfinite(alpha)) {
2403 stokesBreakdown =
true;
2407 for (std::size_t i = 0; i < n; ++i)
2408 for (
unsigned c = 0; c <
D; ++c)
2409 s[i][c] =
static_cast<SolverT
>(r[i][c] - alpha * sv[i][c]);
2411 velocityResidual = vecMaxAbs(s);
2412 if (!std::isfinite(velocityResidual)) {
2413 stokesBreakdown =
true;
2416 if (velocityResidual < deformationParameters.stokesTolerance * b_norm) {
2417 for (std::size_t i = 0; i < n; ++i)
2418 for (
unsigned c = 0; c <
D; ++c)
2419 x[i][c] =
static_cast<SolverT
>(x[i][c] + alpha * y[i][c]);
2423 for (std::size_t i = 0; i < n; ++i)
2424 for (
unsigned c = 0; c <
D; ++c) {
2425 const T sc = s[i][c];
2426 const T pcDiag = precondDiag[i][c];
2427 z[i][c] =
static_cast<SolverT
>((pcDiag > eps) ? sc / pcDiag : sc);
2432 const T t_s = vecDot(t, s);
2433 const T t_t = vecDot(t, t);
2434 if (!std::isfinite(t_s) || !std::isfinite(t_t)) {
2435 stokesBreakdown =
true;
2438 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
2439 if (!std::isfinite(omega)) {
2440 stokesBreakdown =
true;
2444 for (std::size_t i = 0; i < n; ++i)
2445 for (
unsigned c = 0; c <
D; ++c) {
2447 static_cast<SolverT
>(x[i][c] + alpha * y[i][c] + omega * z[i][c]);
2448 r[i][c] =
static_cast<SolverT
>(s[i][c] - omega * t[i][c]);
2451 velocityResidual = vecMaxAbs(r);
2452 if (!std::isfinite(velocityResidual)) {
2453 stokesBreakdown =
true;
2456 if (velocityResidual < deformationParameters.stokesTolerance * b_norm)
2460 if (stokesBreakdown)
2461 velocityResidual = std::numeric_limits<T>::infinity();
2463 bool finiteSolution = !stokesBreakdown;
2464 for (std::size_t i = 0; i < n; ++i)
2465 for (
unsigned c = 0; c <
D; ++c)
2466 if (!std::isfinite(
static_cast<T>(x[i][c])))
2467 finiteSolution =
false;
2469 if (finiteSolution) {
2470 for (std::size_t i = 0; i < n; ++i)
2471 for (
unsigned c = 0; c <
D; ++c)
2472 nodes[i].velocity[c] =
static_cast<T>(x[i][c]);
2475 lastStokesIters_ = stokesIter;
2476 lastStokesResidual_ = finiteSolution ? velocityResidual / b_norm
2477 : std::numeric_limits<T>::infinity();
2478 if (lastStokesResidual_ > deformationParameters.stokesTolerance)
2479 VIENNACORE_LOG_WARNING(
2480 "solveStokesVelocity: BiCGSTAB did not converge after " +
2481 std::to_string(lastStokesIters_) +
"/" +
2482 std::to_string(deformationParameters.stokesIterations) +
2483 " iterations (residual=" + std::to_string(lastStokesResidual_) +
2485 std::to_string(deformationParameters.stokesTolerance) +
")");
2489 std::vector<Vec3D<T>> velocities;
2490 velocities.reserve(
nodes.size());
2491 for (
const auto &node :
nodes)
2492 velocities.push_back(node.velocity);
2497 std::vector<T> pressures;
2498 pressures.reserve(
nodes.size());
2499 for (
const auto &node :
nodes)
2500 pressures.push_back(node.pressure);
2504 template <
class SolverT>
2507 const std::vector<T> &ambientBoundaryPressure,
2508 std::size_t nodeId,
unsigned direction,
2510 const auto &node =
nodes[nodeId];
2511 IndexType neighbor = node.index;
2512 neighbor[direction] += offset;
2515 return {
static_cast<T>(pressure[nodeId]),
gridDelta};
2518 if (neighborId !=
noNode) {
2519 if (touchesAmbient_[neighborId])
2520 return {ambientBoundaryPressure[neighborId],
gridDelta};
2521 return {
static_cast<T>(pressure[neighborId]),
gridDelta};
2524 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2525 const std::size_t nn =
nodes.size();
2526 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2527 const T faceDist = faceBCDists_[fi * nn + nodeId];
2528 if (faceType == Boundary::AMBIENT)
2529 return {ambientBoundaryPressure[nodeId], faceDist};
2535 if (faceType == Boundary::REACTION)
2536 return {
static_cast<T>(pressure[nodeId]), faceDist};
2537 if (faceType == Boundary::MASK)
2539 static_cast<T>(pressure[nodeId])),
2542 return {
static_cast<T>(pressure[nodeId]),
gridDelta};
2545 template <
class SolverT>
2546 StencilPoint<Vec3D<T>>
2548 std::size_t nodeId,
unsigned direction,
2550 const auto &node =
nodes[nodeId];
2551 IndexType neighbor = node.index;
2552 neighbor[direction] += offset;
2554 const auto toT = [](
const Vec3D<SolverT> &v) -> Vec3D<T> {
2555 return {
static_cast<T>(v[0]),
static_cast<T>(v[1]),
static_cast<T>(v[2])};
2559 return {toT(velocity[nodeId]),
gridDelta};
2562 if (neighborId !=
noNode)
2563 return {toT(velocity[neighborId]),
gridDelta};
2565 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2566 const std::size_t nn =
nodes.size();
2567 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2568 const T faceDist = faceBCDists_[fi * nn + nodeId];
2569 if (faceType == Boundary::REACTION)
2571 if (faceType == Boundary::AMBIENT)
2573 faceDist, toT(velocity[nodeId])),
2575 if (faceType == Boundary::MASK)
2579 return {toT(velocity[nodeId]),
gridDelta};
2585 const auto &node =
nodes[nodeId];
2586 IndexType neighbor = node.index;
2587 neighbor[direction] += offset;
2593 if (neighborId !=
noNode)
2596 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2597 const std::size_t nn =
nodes.size();
2598 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2599 const T faceDist = faceBCDists_[fi * nn + nodeId];
2600 if (faceType == Boundary::AMBIENT)
2602 if (faceType == Boundary::REACTION)
2603 return {node.pressure, faceDist};
2604 if (faceType == Boundary::MASK)
2615 const auto &node =
nodes[nodeId];
2616 IndexType neighbor = node.index;
2617 neighbor[direction] += offset;
2623 if (neighborId !=
noNode)
2626 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2627 const std::size_t nn =
nodes.size();
2628 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2629 const T faceDist = faceBCDists_[fi * nn + nodeId];
2630 if (faceType == Boundary::REACTION)
2632 if (faceType == Boundary::AMBIENT)
2634 faceDist, node.velocity),
2636 if (faceType == Boundary::MASK)
2645 const auto count = std::min(previous.size(),
nodes.size());
2646 for (std::size_t i = 0; i < count; ++i) {
2647 for (
unsigned j = 0; j <
D; ++j) {
2648 maxChange = std::max(maxChange,
2649 std::abs(
nodes[i].velocity[j] - previous[i][j]));
2650 maxVelocity = std::max(maxVelocity, std::abs(
nodes[i].velocity[j]));
2654 if (maxVelocity <= std::numeric_limits<T>::epsilon())
2656 return maxChange / maxVelocity;
2662 const auto count = std::min(previous.size(),
nodes.size());
2663 for (std::size_t i = 0; i < count; ++i) {
2665 std::max(maxChange, std::abs(
nodes[i].pressure - previous[i]));
2666 maxPressure = std::max(maxPressure, std::abs(
nodes[i].pressure));
2669 if (maxPressure <= std::numeric_limits<T>::epsilon())
2671 return maxChange / maxPressure;
2677 const auto deviatoricRate =
2682 (relaxationTime <= std::numeric_limits<T>::epsilon())
2684 : std::exp(-deformationParameters.stressTimeStep / relaxationTime);
2686 std::array<T, 9> deviatoricStress{};
2687 for (
unsigned i = 0; i < 9; ++i) {
2688 const T viscousStress =
2689 T(2) * deformationParameters.viscosity * deviatoricRate[i];
2690 deviatoricStress[i] =
2691 decay * previousStress[i] + (
T(1) - decay) * viscousStress;
2694 return deviatoricStress;
2701 return deformationParameters.ambientPressure +
2706 int ,
T fallbackPressure)
const {
2707 return fallbackPressure;
2711 unsigned direction,
int offset,
2713 const Vec3D<T> &interiorVelocity)
const {
2714 Vec3D<T> boundaryVelocity = interiorVelocity;
2719 Vec3D<T> deviatoricTraction{0., 0., 0.};
2720 for (
unsigned component = 0; component <
D; ++component) {
2721 for (
unsigned j = 0; j <
D; ++j)
2722 deviatoricTraction[component] +=
2723 deviatoricStress[
tensorIndex(component, j)] * normal[j];
2726 for (
unsigned component = 0; component <
D; ++component) {
2727 const T normalTraction =
2728 pressure * normal[component] - deviatoricTraction[component];
2729 const T faceDerivative = normalTraction * normal[direction] /
2730 std::max(deformationParameters.viscosity,
2731 std::numeric_limits<T>::epsilon());
2732 boundaryVelocity[component] +=
2733 static_cast<T>(offset) * distance * faceDerivative;
2736 return boundaryVelocity;
2740 const Vec3D<T> &interiorVelocity)
const {
2741 if (maskVelocityField !=
nullptr) {
2742 Vec3D<T> coordinate{0., 0., 0.};
2743 for (
unsigned i = 0; i <
D; ++i)
2745 return maskVelocityField->getVectorVelocity(
2746 coordinate, deformationParameters.material, {0., 0., 0.}, 0);
2748 return {0., 0., 0.};
2752 avgExpansionSpeed_ = 0.;
2756 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2757 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2759 std::size_t count = 0;
2761 for (
const auto &node :
nodes) {
2762 bool touchesReactionBoundary =
false;
2763 for (
unsigned direction = 0; direction <
D; ++direction) {
2764 for (
int offset : {-1, 1}) {
2765 IndexType neighbor = node.index;
2766 neighbor[direction] += offset;
2774 neighbor) == Boundary::REACTION) {
2775 touchesReactionBoundary =
true;
2779 if (touchesReactionBoundary)
2783 if (touchesReactionBoundary) {
2784 Vec3D<T> coordinate{0., 0., 0.};
2785 for (
unsigned i = 0; i <
D; ++i)
2786 coordinate[i] = node.index[i] *
gridDelta;
2787 avgExpansionSpeed_ +=
2788 (oxidationParameters.expansionCoefficient -
T(1)) *
2789 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
2796 avgExpansionSpeed_ /=
static_cast<T>(count);
2800 Vec3D<T> coordinate{0., 0., 0.};
2801 for (
unsigned i = 0; i <
D; ++i)
2804 return reactionNormal(index) * (reactionSign * expansionVelocity);
2808 if (diffusionField ==
nullptr || ambientInterface ==
nullptr)
2809 return {0., 0., 0.};
2812 for (
unsigned i = 0; i <
D; ++i)
2813 index[i] = std::llround(coordinate[i] /
gridDelta);
2815 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2821 Vec3D<T> maxVelocity{0., 0., 0.};
2822 if (ambientInterface ==
nullptr || diffusionField ==
nullptr)
2825 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2826 for (; !ambientIt.isFinished(); ++ambientIt) {
2827 if (!ambientIt.isDefined())
2830 Vec3D<T> coordinate{0., 0., 0.};
2831 const auto &index = ambientIt.getStartIndices();
2832 for (
unsigned d = 0; d <
D; ++d)
2836 for (
unsigned d = 0; d <
D; ++d)
2837 maxVelocity[d] = std::max(maxVelocity[d], std::abs(velocity[d]));
2844 for (
unsigned i = 0; i <
D; ++i) {
2851 Vec3D<T> gradient{0., 0., 0.};
2852 for (
unsigned i = 0; i <
D; ++i)
2860 for (
unsigned i = 0; i <
D; ++i)
2861 forcing[i] -= stressDivergence[i];
2866 Vec3D<T> divergence{0., 0., 0.};
2867 for (
unsigned component = 0; component <
D; ++component) {
2868 for (
unsigned direction = 0; direction <
D; ++direction) {
2869 IndexType pos = index;
2870 IndexType neg = index;
2871 pos[direction] += 1;
2872 neg[direction] -= 1;
2875 divergence[component] +=
2888 const std::size_t nodeId =
lookupNode(index);
2892 std::array<T, 9> deviatoric =
nodes[nodeId].stressTensor;
2893 for (
unsigned i = 0; i < 3; ++i)
2900 return deformationParameters.ambientPressure;
2902 const std::size_t nodeId =
lookupNode(index);
2904 return deformationParameters.ambientPressure;
2905 return nodes[nodeId].pressure;
2909 return (oxidationParameters.expansionCoefficient -
T(1)) *
2910 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
2915 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2920 if (boundary == Boundary::AMBIENT) {
2921 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2924 if (boundary == Boundary::MASK && maskInterface !=
nullptr) {
2925 ConstSparseIterator maskIt(maskInterface->getDomain());
2929 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2934 const IndexType &index)
const {
2935 Vec3D<T> normal{0., 0., 0.};
2938 for (
unsigned i = 0; i <
D; ++i) {
2939 IndexType pos = index;
2940 IndexType neg = index;
2949 norm += normal[i] * normal[i];
2952 if (norm <= std::numeric_limits<T>::epsilon()) {
2953 normal = Vec3D<T>{0., 0., 0.};
2958 norm = std::sqrt(norm);
2959 for (
unsigned i = 0; i <
D; ++i)
2965#pragma omp parallel for schedule(static)
2966 for (std::size_t i = 0; i <
nodes.size(); ++i)
2973 (relaxationTime <= std::numeric_limits<T>::epsilon())
2975 : std::exp(-deformationParameters.stressTimeStep / relaxationTime);
2979 std::vector<std::pair<IndexType, std::array<T, 9>>> historyEntries(
2981#pragma omp parallel for schedule(static)
2982 for (std::size_t i = 0; i <
nodes.size(); ++i) {
2983 auto &node =
nodes[i];
2985 const auto deviatoricRate =
2989 std::array<T, 9> deviatoricStress{};
2990 for (
unsigned j = 0; j < 9; ++j) {
2991 const T viscousStress =
2992 T(2) * deformationParameters.viscosity * deviatoricRate[j];
2993 deviatoricStress[j] =
2994 decay * previousStress[j] + (
T(1) - decay) * viscousStress;
2997 node.stressTensor = deviatoricStress;
2998 for (
unsigned j = 0; j < 3; ++j)
2999 node.stressTensor[
tensorIndex(j, j)] -= node.pressure;
3002 historyEntries[i] = {node.index, deviatoricStress};
3005 std::unordered_map<IndexType, std::array<T, 9>,
typename IndexType::hash>
3007 nextHistory.reserve(
nodes.size());
3008 for (
const auto &entry : historyEntries)
3009 nextHistory[entry.first] = entry.second;
3010 deviatoricStressHistory.swap(nextHistory);
3014 std::array<T, 9> tensor{};
3015 for (
unsigned i = 0; i <
D; ++i) {
3016 for (
unsigned j = 0; j <
D; ++j) {
3025 unsigned direction)
const {
3026 const std::size_t nodeId =
lookupNode(index);
3033 minus.value[component],
nodes[nodeId].velocity[component],
3034 plus.value[component], minus.distance, plus.distance);
3038 const std::size_t nodeId =
lookupNode(index);
3045 minus.distance, plus.distance);
3050 std::array<T, 9> result = tensor;
3051 const T mean = trace /
T(3);
3052 for (
unsigned i = 0; i < 3; ++i)
3058 const auto found = deviatoricStressHistory.find(index);
3059 if (found == deviatoricStressHistory.end())
3061 return found->second;
3065 if (deformationParameters.stressRelaxationTime >
T(0))
3066 return deformationParameters.stressRelaxationTime;
3067 if (deformationParameters.shearModulus > std::numeric_limits<T>::epsilon())
3068 return deformationParameters.viscosity /
3069 deformationParameters.shearModulus;
3074 T doubleContraction = 0.;
3075 for (
unsigned i = 0; i < 3; ++i) {
3076 for (
unsigned j = 0; j < 3; ++j) {
3077 const T value =
T(0.5) * (deviatoricStress[
tensorIndex(i, j)] +
3079 doubleContraction += value * value;
3082 return std::sqrt(
T(1.5) * doubleContraction);
3087 for (
unsigned i = 0; i < 3; ++i) {
3088 for (
unsigned j = 0; j < 3; ++j)
3089 result += normal[i] * tensor[
tensorIndex(i, j)] * normal[j];
3095 ConstSparseIterator &ambientIt,
3096 ConstSparseIterator &maskIt,
3097 const IndexType &inside,
3098 const IndexType &outside)
const {
3104 ConstSparseIterator &ambientIt,
3105 ConstSparseIterator &maskIt,
3106 const IndexType &inside,
3107 const IndexType &outside)
const {
3108 const T reactionInside =
valueAt(reactionIt, inside);
3109 const T reactionOutside =
valueAt(reactionIt, outside);
3110 const T ambientInside =
valueAt(ambientIt, inside);
3111 const T ambientOutside =
valueAt(ambientIt, outside);
3115 const bool reactionCrosses =
crosses(reactionInside, reactionOutside);
3116 const bool ambientCrosses =
crosses(ambientInside, ambientOutside);
3117 const bool maskCrosses =
3118 maskInterface !=
nullptr &&
crosses(maskInside, maskOutside);
3120 if (!reactionCrosses && !ambientCrosses && !maskCrosses)
3122 if (reactionCrosses && !ambientCrosses && !maskCrosses)
3123 return {Boundary::REACTION,
3125 if (!reactionCrosses && ambientCrosses && !maskCrosses)
3127 maskInside, maskOutside,
3129 if (!reactionCrosses && !ambientCrosses && maskCrosses)
3132 const T reactionDistance =
3134 : std::numeric_limits<T>::max();
3135 const T ambientDistance =
3137 : std::numeric_limits<T>::max();
3138 const T maskDistance = maskCrosses
3140 : std::numeric_limits<T>::max();
3141 if (reactionDistance <= ambientDistance && reactionDistance <= maskDistance)
3142 return {Boundary::REACTION, reactionDistance};
3143 if (ambientDistance != std::numeric_limits<T>::max()) {
3144 const auto maskedAmbient =
3146 if (maskedAmbient.boundary == Boundary::MASK)
3147 return maskedAmbient;
3149 if (maskDistance <= ambientDistance)
3150 return {Boundary::MASK, maskDistance};
3151 return {Boundary::AMBIENT, ambientDistance};
3155 ConstSparseIterator &ambientIt,
3156 ConstSparseIterator &maskIt,
const IndexType &index,
3157 Boundary requestedBoundary)
const {
3158 for (
unsigned direction = 0; direction <
D; ++direction) {
3159 for (
int offset : {-1, 1}) {
3160 IndexType neighbor = index;
3161 neighbor[direction] += offset;
3182 constexpr T eps =
T(1e-9);
3183 return reactionSign * reactionPhi >= -eps &&
3184 ambientSign * ambientPhi >= -eps;
3188 if (maskInterface ==
nullptr)
3189 return ConstSparseIterator(reactionInterface->getDomain());
3190 return ConstSparseIterator(maskInterface->getDomain());
3193 bool isInsideMask(ConstSparseIterator &maskIt,
const IndexType &index)
const {
3194 if (maskInterface ==
nullptr)
3196 return maskSign *
valueAt(maskIt, index) >= 0.;
3200 if (maskInterface ==
nullptr)
3201 return std::numeric_limits<T>::max();
3202 return valueAt(maskIt, index);
3208 return {Boundary::MASK, distance};
3213 if (maskInterface !=
nullptr &&
3214 static_cast<T>(maskSign) * maskOutside >=
T(0))
3215 return {Boundary::MASK, distance};
3216 return {Boundary::AMBIENT, distance};
3220 if (maskInterface ==
nullptr)
3222 const T fraction = std::clamp(distance /
gridDelta,
T(0),
T(1));
3225 const T maskPhi = insidePhi + fraction * (outsidePhi - insidePhi);
3226 return static_cast<T>(maskSign) * maskPhi >=
T(0);
3231 insidePhi, outsidePhi,
3232 deformationParameters.minMechanicsBoundaryDistance,
gridDelta);
3236 T minusDistance,
T plusDistance) {
3237 const T denominator =
3238 minusDistance * plusDistance * (minusDistance + plusDistance);
3239 if (denominator <= std::numeric_limits<T>::epsilon())
3242 return (-plusDistance * plusDistance * minusValue +
3243 (plusDistance * plusDistance - minusDistance * minusDistance) *
3245 minusDistance * minusDistance * plusValue) /
3249 static constexpr unsigned tensorIndex(
unsigned row,
unsigned column) {
3250 return 3 * row + column;
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
Solves the oxidant diffusion step of the Suvorov et al. (10.1007/s10825-006-0003-z) oxidation model o...
Definition lsOxidationDiffusion.hpp:98
Common Cartesian-grid infrastructure shared by the three oxidation solver classes (diffusion,...
Definition lsOxidationSolverBase.hpp:43
static constexpr std::size_t noNode
Definition lsOxidationSolverBase.hpp:49
bool crosses(T a, T b) const
Definition lsOxidationSolverBase.hpp:63
std::size_t lookupNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:90
std::size_t linearIndex(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:96
void initNodeLookup()
Definition lsOxidationSolverBase.hpp:83
bool inBounds(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:76
std::array< std::size_t, D > strides
Definition lsOxidationSolverBase.hpp:54
T gridDelta
Definition lsOxidationSolverBase.hpp:55
std::vector< std::size_t > nodeLookupFlat
Definition lsOxidationSolverBase.hpp:50
bool initializeGridFromInterfaces(SmartPointer< Domain< T, D > > reactionInterface, SmartPointer< Domain< T, D > > ambientInterface, SmartPointer< Domain< T, D > > maskInterface, bool useRequestedBounds, const IndexType &requestedMinIndex, const IndexType &requestedMaxIndex, std::size_t maxGridPoints, const std::string &solverName)
Definition lsOxidationSolverBase.hpp:160
std::array< std::size_t, D > extents
Definition lsOxidationSolverBase.hpp:53
viennahrle::ConstSparseIterator< typename Domain< T, D >::DomainType > ConstSparseIterator
Definition lsOxidationSolverBase.hpp:46
T valueAt(ConstSparseIterator &it, const IndexType &index) const
Definition lsOxidationSolverBase.hpp:71
bool increment(IndexType &index) const
Definition lsOxidationSolverBase.hpp:105
IndexType minIndex
Definition lsOxidationSolverBase.hpp:51
viennahrle::Index< D > IndexType
Definition lsOxidationSolverBase.hpp:45
std::size_t findNearbyNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:119
IndexType maxIndex
Definition lsOxidationSolverBase.hpp:52
T levelSetCrossingDistance(T insidePhi, T outsidePhi, T minBoundaryFraction, T gridDelta)
Definition lsOxidationSolverBase.hpp:29
T clampLevelSetPhi(T v)
Clamp HRLE far-field sentinels (±DBL_MAX) to ±1 before differencing to prevent DBL_MAX² overflow that...
Definition lsOxidationSolverBase.hpp:24
Definition lsAdvect.hpp:41
GpuMode
Selects the BiCGSTAB back-end for the diffusion solve. GPU failures are reported and not silently fal...
Definition lsOxidationDiffusion.hpp:26
@ Auto
Definition lsOxidationDiffusion.hpp:34
@ Gpu
Always use GPU; fail if unavailable or unsuccessful Use the GPU when it is usable,...
Definition lsOxidationDiffusion.hpp:28
@ Cpu
Always use CPU (default).
Definition lsOxidationDiffusion.hpp:27
GpuPreconditioner
Selects the preconditioner used by the GPU BiCGSTAB solver. Jacobi matches the CPU solver's precondit...
Definition lsOxidationDiffusion.hpp:41
@ ILU0
Definition lsOxidationDiffusion.hpp:41
@ Jacobi
Definition lsOxidationDiffusion.hpp:41
Parameters for the steady oxidant diffusion model used by OxidationDiffusion.
Definition lsOxidationDiffusion.hpp:45