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;
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;
1140 const std::size_t neighborId =
lookupNode(neighbor);
1141 if (neighborId !=
noNode) {
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) {
1161 template <
class SolverT>
1163 const std::vector<Vec3D<T>> &b,
1164 std::vector<Vec3D<SolverT>> &Av)
const {
1165 const T diagVal =
static_cast<T>(2 *
D);
1166#pragma omp parallel for schedule(static)
1167 for (std::size_t i = 0; i <
nodes.size(); ++i) {
1170 for (
unsigned c = 0; c <
D; ++c)
1171 Av[i][c] =
static_cast<SolverT
>(diagVal * v[i][c] - sum[c] + b[i][c]);
1183 const std::size_t n =
nodes.size();
1184 const T diagVal =
static_cast<T>(2 *
D);
1189 std::vector<Vec3D<T>> b(n);
1191 const std::vector<Vec3D<SolverT>> zeros(
1192 n, Vec3D<SolverT>{SolverT(0), SolverT(0), SolverT(0)});
1193#pragma omp parallel for schedule(static)
1194 for (std::size_t i = 0; i < n; ++i)
1199 std::vector<Vec3D<SolverT>> x(n);
1200 for (std::size_t i = 0; i < n; ++i)
1201 for (
unsigned c = 0; c <
D; ++c) {
1202 const T value =
nodes[i].velocity[c];
1203 x[i][c] =
static_cast<SolverT
>(std::isfinite(value) ? value :
T(0));
1206#ifdef VIENNALS_GPU_BICGSTAB
1207 if (gpu::gpuIsValid(gpuHarmonicBufs_)) {
1208 const std::size_t nf = 2u *
D * n;
1209 if (harmonicDiagGpu_.size() != n || harmonicCoeffGpu_.size() != nf) {
1210 VIENNACORE_LOG_ERROR(
"OxidationDeformation: harmonic GPU geometry has "
1211 "the wrong size for the current node set.");
1214 Timer<> tUpload, tSolve;
1215 std::vector<Vec3D<SolverT>> xSolved(n);
1216 unsigned maxGpuIterations = 0;
1217 double maxGpuResidual = 0.0;
1219 for (
unsigned c = 0; c <
D; ++c) {
1220 std::vector<double> bGpu(n), xGpu(n);
1221 for (std::size_t i = 0; i < n; ++i) {
1222 bGpu[i] =
static_cast<double>(b[i][c]);
1223 xGpu[i] =
static_cast<double>(x[i][c]);
1227 const bool gpuUploadOk =
1228 (c == 0) ? gpu::gpuUploadSolverArrays(
1229 gpuHarmonicBufs_, harmonicDiagGpu_.data(),
1230 bGpu.data(), harmonicCoeffGpu_.data(),
1231 static_cast<uint32_t
>(n), harmonicCoeffGpu_.size())
1232 : gpu::gpuUploadRhs(gpuHarmonicBufs_, bGpu.data(),
1233 static_cast<uint32_t
>(n));
1236 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, "
1237 "but uploading harmonic solver arrays failed." +
1241 unsigned gpuIterations = 0;
1242 double gpuResidual = 0.0;
1244 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
1245 gpuHarmonicBufs_, xGpu.data(),
1246 static_cast<double>(std::numeric_limits<SolverT>::epsilon()),
1247 deformationParameters.harmonicIterations,
1248 static_cast<double>(deformationParameters.tolerance), gpuIterations,
1252 if (!gpuConverged || !std::isfinite(gpuResidual)) {
1253 VIENNACORE_LOG_ERROR(
1254 "OxidationDeformation: harmonic GPU BiCGSTAB failed or produced "
1255 "a non-finite residual for component " +
1256 std::to_string(c) +
" (iters=" + std::to_string(gpuIterations) +
1257 ", residual=" + std::to_string(gpuResidual) +
").");
1260 maxGpuIterations = std::max(maxGpuIterations, gpuIterations);
1261 maxGpuResidual = std::max(maxGpuResidual, gpuResidual);
1262 for (std::size_t i = 0; i < n; ++i)
1263 xSolved[i][c] =
static_cast<SolverT
>(xGpu[i]);
1266 for (std::size_t i = 0; i < n; ++i)
1267 for (
unsigned c = 0; c <
D; ++c)
1268 nodes[i].velocity[c] =
static_cast<T>(xSolved[i][c]);
1269 iterations = maxGpuIterations;
1270 residual = maxGpuResidual;
1272 if (Logger::hasTiming()) {
1273 Logger::getInstance()
1274 .addTiming(
"harmonic n=" + std::to_string(n) +
1275 " iters=" + std::to_string(iterations) +
" res=" +
1276 std::to_string(residual) +
" [GPU] GPU BiCGSTAB",
1280 if (Logger::hasDebug()) {
1281 Logger::getInstance()
1282 .addTiming(
"harmonic n=" + std::to_string(n) +
" [GPU] GPU upload",
1291 const Vec3D<SolverT> zero3{SolverT(0), SolverT(0), SolverT(0)};
1292 std::vector<Vec3D<SolverT>> Ax(n);
1294 std::vector<Vec3D<SolverT>> r(n), r_hat(n);
1295 for (std::size_t i = 0; i < n; ++i)
1296 for (
unsigned c = 0; c <
D; ++c) {
1297 r[i][c] =
static_cast<SolverT
>(b[i][c] - Ax[i][c]);
1298 r_hat[i][c] = r[i][c];
1302 std::vector<Vec3D<SolverT>> pv(n, zero3), sv(n, zero3), y(n), z(n), s(n),
1304 T rho =
T(1), alpha =
T(1), omega =
T(1);
1306 auto vecDot = [&](
const std::vector<Vec3D<SolverT>> &a,
1307 const std::vector<Vec3D<SolverT>> &bv) {
1309 for (std::size_t i = 0; i < n; ++i)
1310 for (
unsigned c = 0; c <
D; ++c)
1311 sum +=
static_cast<T>(a[i][c]) *
static_cast<T>(bv[i][c]);
1315 auto vecMaxAbs = [&](
const std::vector<Vec3D<SolverT>> &vin) {
1317 for (std::size_t i = 0; i < n; ++i)
1318 for (
unsigned c = 0; c <
D; ++c)
1319 m = std::max(m, std::abs(
static_cast<T>(vin[i][c])));
1323 const T b_norm = [&] {
1325 for (std::size_t i = 0; i < n; ++i)
1326 for (
unsigned c = 0; c <
D; ++c)
1327 m = std::max(m, std::abs(b[i][c]));
1328 return (m <
T(1e-100)) ?
T(1) : m;
1331 for (; iterations < deformationParameters.harmonicIterations;
1333 const T rho_new = vecDot(r_hat, r);
1334 if (!std::isfinite(rho_new) || std::abs(rho_new) <
T(1e-100))
1336 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
1337 !std::isfinite(omega) || std::abs(omega) <
T(1e-100))
1340 const T beta = (rho_new / rho) * (alpha / omega);
1341 if (!std::isfinite(beta))
1345 for (std::size_t i = 0; i < n; ++i)
1346 for (
unsigned c = 0; c <
D; ++c)
1347 pv[i][c] =
static_cast<SolverT
>(r[i][c] +
1348 beta * (pv[i][c] - omega * sv[i][c]));
1351 for (std::size_t i = 0; i < n; ++i)
1352 for (
unsigned c = 0; c <
D; ++c)
1353 y[i][c] =
static_cast<SolverT
>(
static_cast<T>(pv[i][c]) / diagVal);
1357 const T r_hat_v = vecDot(r_hat, sv);
1358 if (!std::isfinite(r_hat_v) || std::abs(r_hat_v) <
T(1e-100))
1361 alpha = rho_new / r_hat_v;
1362 if (!std::isfinite(alpha))
1365 for (std::size_t i = 0; i < n; ++i)
1366 for (
unsigned c = 0; c <
D; ++c)
1367 s[i][c] =
static_cast<SolverT
>(r[i][c] - alpha * sv[i][c]);
1369 residual = vecMaxAbs(s);
1370 if (!std::isfinite(residual))
1372 if (residual < deformationParameters.tolerance * b_norm) {
1373 for (std::size_t i = 0; i < n; ++i)
1374 for (
unsigned c = 0; c <
D; ++c)
1375 x[i][c] =
static_cast<SolverT
>(x[i][c] + alpha * y[i][c]);
1381 for (std::size_t i = 0; i < n; ++i)
1382 for (
unsigned c = 0; c <
D; ++c)
1383 z[i][c] =
static_cast<SolverT
>(
static_cast<T>(s[i][c]) / diagVal);
1387 const T t_s = vecDot(t, s);
1388 const T t_t = vecDot(t, t);
1389 if (!std::isfinite(t_s) || !std::isfinite(t_t))
1391 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
1392 if (!std::isfinite(omega))
1395 for (std::size_t i = 0; i < n; ++i)
1396 for (
unsigned c = 0; c <
D; ++c) {
1398 static_cast<SolverT
>(x[i][c] + alpha * y[i][c] + omega * z[i][c]);
1399 r[i][c] =
static_cast<SolverT
>(s[i][c] - omega * t[i][c]);
1402 residual = vecMaxAbs(r);
1403 if (!std::isfinite(residual))
1405 if (residual < deformationParameters.tolerance * b_norm) {
1411 bool finiteSolution =
true;
1412 for (std::size_t i = 0; i < n; ++i)
1413 for (
unsigned c = 0; c <
D; ++c)
1414 if (!std::isfinite(
static_cast<T>(x[i][c])))
1415 finiteSolution =
false;
1417 if (finiteSolution) {
1418 for (std::size_t i = 0; i < n; ++i)
1419 for (
unsigned c = 0; c <
D; ++c)
1420 nodes[i].velocity[c] =
static_cast<T>(x[i][c]);
1422 residual = std::numeric_limits<T>::infinity();
1424 if (residual > deformationParameters.tolerance * b_norm)
1425 VIENNACORE_LOG_WARNING(
1426 "solveVelocity (harmonic): BiCGSTAB did not converge after " +
1427 std::to_string(iterations) +
"/" +
1428 std::to_string(deformationParameters.harmonicIterations) +
1429 " iterations (residual=" + std::to_string(residual / b_norm) +
1430 ", tolerance=" + std::to_string(deformationParameters.tolerance) +
1441 const std::size_t n =
nodes.size();
1442 std::vector<Vec3D<T>> diagV(n, Vec3D<T>{
T(0),
T(0),
T(0)});
1445 const std::vector<Vec3D<T>> zeros(n, Vec3D<T>{
T(0),
T(0),
T(0)});
1446 std::vector<Vec3D<T>> tmp(n);
1447#pragma omp parallel for schedule(static)
1448 for (std::size_t i = 0; i < n; ++i) {
1451 for (
unsigned comp = 0; comp <
D; ++comp)
1452 diagV[i][comp] = diag;
1459 T mechanicsResidual = 0.;
1477 const std::vector<Vec3D<T>> diagV =
1480 for (
unsigned iteration = 0;
1481 iteration < deformationParameters.mechanicsIterations; ++iteration) {
1489 Timer<> tStokes, tPressure;
1493 if (!std::isfinite(lastStokesResidual_)) {
1494 mechanicsResidual = std::numeric_limits<T>::infinity();
1502 if (!std::isfinite(lastPressureResidual_)) {
1503 mechanicsResidual = std::numeric_limits<T>::infinity();
1512 if (!std::isfinite(mechanicsResidual)) {
1513 mechanicsResidual = std::numeric_limits<T>::infinity();
1517 if (Logger::hasDebug())
1518 Logger::getInstance()
1520 " mechanics[" + std::to_string(iteration) +
1521 "] stokes iters=" + std::to_string(lastStokesIters_) +
1523 std::to_string(deformationParameters.stokesIterations) +
1524 " res=" + std::to_string(lastStokesResidual_),
1527 " mechanics[" + std::to_string(iteration) +
1528 "] pressure iters=" + std::to_string(lastPressureIters_) +
1530 std::to_string(deformationParameters.pressureIterations) +
1531 " res=" + std::to_string(lastPressureResidual_) +
1532 " coupling=" + std::to_string(mechanicsResidual),
1536 if (mechanicsResidual < deformationParameters.mechanicsTolerance)
1542 residual = mechanicsResidual;
1543 if (residual > deformationParameters.mechanicsTolerance)
1544 VIENNACORE_LOG_WARNING(
1545 "solveMechanics: did not converge after " +
1546 std::to_string(deformationParameters.mechanicsIterations) +
1547 " iterations (residual=" + std::to_string(residual) +
", tolerance=" +
1548 std::to_string(deformationParameters.mechanicsTolerance) +
")");
1558 const std::vector<Vec3D<T>> &diagV) {
1561 if (deformationParameters.viscosity <= std::numeric_limits<T>::epsilon())
1564 const std::size_t n =
nodes.size();
1565 const T invEta =
T(1) / deformationParameters.viscosity;
1567#pragma omp parallel for schedule(static)
1568 for (std::size_t i = 0; i < n; ++i) {
1569 for (
unsigned dir = 0; dir <
D; ++dir) {
1570 const T ai = diagV[i][dir];
1571 if (ai <= std::numeric_limits<T>::epsilon())
1577 const unsigned fi = dir * 2u;
1578 IndexType nb =
nodes[i].index;
1583 dpMinus =
nodes[j].pressure - pressureOld[j];
1587 dMinus = faceBCDists_[fi * n + i];
1598 const unsigned fi = dir * 2u + 1u;
1599 IndexType nb =
nodes[i].index;
1604 dpPlus =
nodes[j].pressure - pressureOld[j];
1608 dPlus = faceBCDists_[fi * n + i];
1616 const T dpCenter =
nodes[i].pressure - pressureOld[i];
1619 const T correction =
1620 gradDP * invEta / ai * deformationParameters.relaxation;
1621 if (std::isfinite(correction) && std::isfinite(
nodes[i].velocity[dir]))
1622 nodes[i].velocity[dir] -= correction;
1630 template <
class SolverT>
1632 const std::vector<SolverT> &p,
1633 const std::vector<T> &ambientBP,
T &diag,
1635 if (touchesAmbient_[nodeId]) {
1637 rhs = ambientBP[nodeId];
1642 for (
unsigned direction = 0; direction <
D; ++direction) {
1647 const T dSum = plus.distance + minus.distance;
1648 const T plusCoeff =
T(2) / (plus.distance * dSum);
1649 const T minusCoeff =
T(2) / (minus.distance * dSum);
1650 rhs += plusCoeff * plus.value + minusCoeff * minus.value;
1651 diag += plusCoeff + minusCoeff;
1656 template <
class SolverT>
1659 const std::vector<T> &precomputedDiag,
1660 const std::vector<T> &pBC, std::vector<SolverT> &Av)
const {
1661#pragma omp parallel for schedule(static)
1662 for (std::size_t i = 0; i <
nodes.size(); ++i) {
1665 Av[i] =
static_cast<SolverT
>(precomputedDiag[i] * v[i] - rhs + pBC[i]);
1675 const std::size_t n =
nodes.size();
1676 const T eps = std::numeric_limits<T>::epsilon();
1678 std::vector<T> divergence(n), ambientBP(n);
1679#pragma omp parallel for schedule(static)
1680 for (std::size_t i = 0; i < n; ++i) {
1685 auto warnBadPressureAssembly = [](
const std::string &stage,
1686 std::size_t nodeId,
const IndexType &idx,
1688 VIENNACORE_LOG_WARNING(
1689 "solvePressure: non-finite/overflow " + stage +
1690 " at node=" + std::to_string(nodeId) +
" index=(" +
1691 std::to_string(idx[0]) +
"," + std::to_string(idx[1]) +
1692 (
D == 3 ?
"," + std::to_string(idx[2]) : std::string()) +
1693 ") value=" + std::to_string(value));
1696 const T solverMax =
static_cast<T>(std::numeric_limits<SolverT>::max());
1697 for (std::size_t i = 0; i < n; ++i) {
1698 if (!std::isfinite(divergence[i]) ||
1699 std::abs(divergence[i]) > solverMax) {
1700 warnBadPressureAssembly(
"divergence", i,
nodes[i].index, divergence[i]);
1703 if (!std::isfinite(ambientBP[i]) || std::abs(ambientBP[i]) > solverMax) {
1704 warnBadPressureAssembly(
"ambient pressure boundary", i,
nodes[i].index,
1711 std::vector<T> diag(n), pBC(n);
1713 const std::vector<SolverT> zeros(n, SolverT(0));
1714#pragma omp parallel for schedule(static)
1715 for (std::size_t i = 0; i < n; ++i)
1719 for (std::size_t i = 0; i < n; ++i) {
1720 if (!std::isfinite(diag[i]) || std::abs(diag[i]) > solverMax) {
1721 warnBadPressureAssembly(
"pressure diagonal", i,
nodes[i].index,
1725 if (!std::isfinite(pBC[i]) || std::abs(pBC[i]) > solverMax) {
1726 warnBadPressureAssembly(
"pressure boundary rhs", i,
nodes[i].index,
1732 std::vector<T> b(n);
1734 for (std::size_t i = 0; i < n; ++i) {
1735 b[i] = pBC[i] + deformationParameters.bulkModulus * divergence[i];
1736 b_norm = std::max(b_norm, std::abs(b[i]));
1738 for (std::size_t i = 0; i < n; ++i) {
1739 if (!std::isfinite(b[i]) || std::abs(b[i]) > solverMax) {
1740 warnBadPressureAssembly(
"pressure rhs", i,
nodes[i].index, b[i]);
1744 if (b_norm <
T(1e-100))
1747 std::vector<SolverT> x(n);
1748 for (std::size_t i = 0; i < n; ++i) {
1749 T guess = touchesAmbient_[i] ? ambientBP[i] :
nodes[i].pressure;
1750 if (!std::isfinite(guess))
1751 guess = deformationParameters.ambientPressure;
1752 x[i] =
static_cast<SolverT
>(guess);
1755#ifdef VIENNALS_GPU_BICGSTAB
1756 if (gpu::gpuIsValid(gpuPressBufs_)) {
1757 const std::size_t nf = 2u *
D * n;
1758 if (actualDiagGpu_.size() != n || pressCoeffGpu_.size() != nf) {
1759 VIENNACORE_LOG_ERROR(
"OxidationDeformation: pressure GPU geometry has "
1760 "the wrong size for the current node set.");
1763 Timer<> tUpload, tSolve;
1764 std::vector<double> bGpu(n), xGpu(n);
1765 for (std::size_t i = 0; i < n; ++i) {
1766 bGpu[i] =
static_cast<double>(b[i]);
1767 xGpu[i] =
static_cast<double>(x[i]);
1771 const bool gpuUploadOk = gpu::gpuUploadSolverArrays(
1772 gpuPressBufs_, actualDiagGpu_.data(), bGpu.data(),
1773 pressCoeffGpu_.data(),
static_cast<uint32_t
>(n),
1774 pressCoeffGpu_.size());
1777 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, but "
1778 "uploading pressure solver arrays or factorizing "
1783 unsigned gpuIterations = 0;
1784 double gpuResidual = 0.0;
1786 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
1787 gpuPressBufs_, xGpu.data(),
static_cast<double>(eps),
1788 deformationParameters.pressureIterations,
1789 static_cast<double>(deformationParameters.pressureTolerance),
1790 gpuIterations, gpuResidual);
1796 if (!gpuConverged || !std::isfinite(gpuResidual)) {
1797 VIENNACORE_LOG_ERROR(
1798 "OxidationDeformation: pressure GPU BiCGSTAB failed or produced "
1799 "a non-finite residual (iters=" +
1800 std::to_string(gpuIterations) +
1801 ", residual=" + std::to_string(gpuResidual) +
").");
1805 const T beta = deformationParameters.pressureRelaxation;
1806 const T oneMinB =
T(1) - beta;
1807 for (std::size_t i = 0; i < n; ++i)
1809 oneMinB *
nodes[i].pressure + beta *
static_cast<T>(xGpu[i]);
1811 lastPressureIters_ = gpuIterations;
1812 lastPressureResidual_ = gpuResidual / b_norm;
1814 if (Logger::hasDebug()) {
1815 const std::string tag =
1816 "pressure n=" + std::to_string(n) +
1817 " iters=" + std::to_string(lastPressureIters_) +
1818 " res=" + std::to_string(lastPressureResidual_) +
" [GPU]";
1819 Logger::getInstance()
1820 .addTiming(tag +
" GPU upload", tUpload)
1821 .addTiming(tag +
" GPU BiCGSTAB", tSolve)
1843 std::vector<T> pressCoeff(2 *
D * n,
T(0));
1844 std::vector<std::size_t> pressNeighId(2 *
D * n,
noNode);
1845 std::vector<T> actualDiag(n,
T(0));
1847 for (std::size_t
id = 0;
id < n; ++id) {
1848 if (touchesAmbient_[
id]) {
1849 actualDiag[id] =
T(1);
1852 for (
unsigned dir = 0; dir <
D; ++dir) {
1853 const unsigned fiNeg = dir * 2u;
1854 const unsigned fiPos = dir * 2u + 1u;
1855 IndexType nbNeg =
nodes[id].index;
1857 IndexType nbPos =
nodes[id].index;
1859 const std::size_t jNeg =
1861 const std::size_t jPos =
1869 auto effectiveDist = [&](
unsigned fi, std::size_t j) ->
T {
1872 const Boundary bt = faceBCTypes_[fi * n + id];
1873 if (bt != Boundary::NONE)
1874 return faceBCDists_[fi * n + id];
1878 const T dNeg = effectiveDist(fiNeg, jNeg);
1879 const T dPos = effectiveDist(fiPos, jPos);
1880 const T dSum = dNeg + dPos;
1884 if (jNeg !=
noNode && !touchesAmbient_[jNeg]) {
1885 const T c =
T(2) / (dNeg * dSum);
1886 pressCoeff[fiNeg * n + id] = c;
1887 pressNeighId[fiNeg * n + id] = jNeg;
1888 actualDiag[id] += c;
1889 }
else if (jNeg !=
noNode ||
1890 faceBCTypes_[fiNeg * n +
id] == Boundary::AMBIENT) {
1895 actualDiag[id] +=
T(2) / (dNeg * dSum);
1897 if (jPos !=
noNode && !touchesAmbient_[jPos]) {
1898 const T c =
T(2) / (dPos * dSum);
1899 pressCoeff[fiPos * n + id] = c;
1900 pressNeighId[fiPos * n + id] = jPos;
1901 actualDiag[id] += c;
1902 }
else if (jPos !=
noNode ||
1903 faceBCTypes_[fiPos * n +
id] == Boundary::AMBIENT) {
1904 actualDiag[id] +=
T(2) / (dPos * dSum);
1909 if (actualDiag[
id] <= eps)
1910 actualDiag[id] =
T(1);
1934 std::vector<T> ilu_diag(n);
1935 for (std::size_t
id = 0;
id < n; ++id) {
1936 if (touchesAmbient_[
id]) {
1937 ilu_diag[id] =
T(1);
1940 ilu_diag[id] = actualDiag[id];
1941 for (
unsigned dir = 0; dir <
D; ++dir) {
1942 const unsigned fi_L = dir * 2u;
1943 const unsigned fi_U =
1945 const std::size_t j = pressNeighId[fi_L * n + id];
1946 if (j ==
noNode || ilu_diag[j] <= eps)
1955 pressCoeff[fi_L * n + id] * pressCoeff[fi_U * n + j] / ilu_diag[j];
1957 if (ilu_diag[
id] <= eps)
1958 ilu_diag[id] = actualDiag[id];
1961 auto applyIlu = [&](
const std::vector<SolverT> &in,
1962 std::vector<SolverT> &out) {
1963 std::vector<T> y(n);
1965 for (std::size_t i = 0; i < n; ++i) {
1966 T val =
static_cast<T>(in[i]);
1967 for (
unsigned dir = 0; dir <
D; ++dir) {
1968 const unsigned fi_L = dir * 2u;
1969 const std::size_t j = pressNeighId[fi_L * n + i];
1974 val += (pressCoeff[fi_L * n + i] / ilu_diag[j]) * y[j];
1979 for (std::size_t i = n; i-- > 0;) {
1981 for (
unsigned dir = 0; dir <
D; ++dir) {
1982 const unsigned fi_U = dir * 2u + 1u;
1983 const std::size_t j = pressNeighId[fi_U * n + i];
1988 val += pressCoeff[fi_U * n + i] *
static_cast<T>(out[j]);
1990 out[i] =
static_cast<SolverT
>(val / ilu_diag[i]);
1994 std::vector<SolverT> Ax(n);
1996 for (std::size_t i = 0; i < n; ++i) {
1997 if (!std::isfinite(
static_cast<T>(Ax[i])) ||
1998 std::abs(
static_cast<T>(Ax[i])) > solverMax) {
1999 warnBadPressureAssembly(
"initial pressure matvec", i,
nodes[i].index,
2000 static_cast<T>(Ax[i]));
2004 std::vector<SolverT> r(n), r_hat(n), p(n, SolverT(0)), v(n, SolverT(0)),
2005 y(n), z(n), s(n), t(n);
2006 for (std::size_t i = 0; i < n; ++i) {
2007 r[i] =
static_cast<SolverT
>(b[i] - Ax[i]);
2011 T rho =
T(1), alpha =
T(1), omega =
T(1);
2012 T pressureResidual =
T(0);
2013 unsigned pressureIter = 0;
2014 bool pressureBreakdown =
false;
2015 for (std::size_t i = 0; i < n; ++i) {
2016 const T ri =
static_cast<T>(r[i]);
2017 if (!std::isfinite(ri)) {
2018 pressureBreakdown =
true;
2021 pressureResidual = std::max(pressureResidual, std::abs(ri));
2024 for (; !pressureBreakdown &&
2025 pressureIter < deformationParameters.pressureIterations;
2028 for (std::size_t i = 0; i < n; ++i)
2029 rho_new +=
static_cast<T>(r_hat[i]) *
static_cast<T>(r[i]);
2031 if (!std::isfinite(rho_new)) {
2032 pressureBreakdown =
true;
2035 if (std::abs(rho_new) <
T(1e-100))
2037 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
2038 !std::isfinite(omega) || std::abs(omega) <
T(1e-100)) {
2039 pressureBreakdown =
true;
2043 const T beta = (rho_new / rho) * (alpha / omega);
2044 if (!std::isfinite(beta)) {
2045 pressureBreakdown =
true;
2050 for (std::size_t i = 0; i < n; ++i)
2051 p[i] =
static_cast<SolverT
>(r[i] + beta * (p[i] - omega * v[i]));
2058 for (std::size_t i = 0; i < n; ++i)
2059 r_hat_v +=
static_cast<T>(r_hat[i]) *
static_cast<T>(v[i]);
2060 if (!std::isfinite(r_hat_v)) {
2061 pressureBreakdown =
true;
2064 if (std::abs(r_hat_v) <
T(1e-100))
2067 alpha = rho_new / r_hat_v;
2068 if (!std::isfinite(alpha)) {
2069 pressureBreakdown =
true;
2073 for (std::size_t i = 0; i < n; ++i)
2074 s[i] =
static_cast<SolverT
>(r[i] - alpha * v[i]);
2076 pressureResidual =
T(0);
2077 for (std::size_t i = 0; i < n; ++i)
2079 std::max(pressureResidual, std::abs(
static_cast<T>(s[i])));
2080 if (!std::isfinite(pressureResidual)) {
2081 pressureBreakdown =
true;
2084 if (pressureResidual < deformationParameters.pressureTolerance * b_norm) {
2085 for (std::size_t i = 0; i < n; ++i)
2086 x[i] =
static_cast<SolverT
>(x[i] + alpha * y[i]);
2094 T t_s =
T(0), t_t =
T(0);
2095 for (std::size_t i = 0; i < n; ++i) {
2096 t_s +=
static_cast<T>(t[i]) *
static_cast<T>(s[i]);
2097 t_t +=
static_cast<T>(t[i]) *
static_cast<T>(t[i]);
2099 if (!std::isfinite(t_s) || !std::isfinite(t_t)) {
2100 pressureBreakdown =
true;
2103 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
2104 if (!std::isfinite(omega)) {
2105 pressureBreakdown =
true;
2109 for (std::size_t i = 0; i < n; ++i) {
2110 x[i] =
static_cast<SolverT
>(x[i] + alpha * y[i] + omega * z[i]);
2111 r[i] =
static_cast<SolverT
>(s[i] - omega * t[i]);
2114 pressureResidual =
T(0);
2115 for (std::size_t i = 0; i < n; ++i)
2117 std::max(pressureResidual, std::abs(
static_cast<T>(r[i])));
2118 if (!std::isfinite(pressureResidual)) {
2119 pressureBreakdown =
true;
2122 if (pressureResidual < deformationParameters.pressureTolerance * b_norm)
2126 if (pressureBreakdown)
2127 pressureResidual = std::numeric_limits<T>::infinity();
2129 bool finiteSolution = !pressureBreakdown;
2130 for (std::size_t i = 0; i < n; ++i)
2131 if (!std::isfinite(
static_cast<T>(x[i])))
2132 finiteSolution =
false;
2134 lastPressureIters_ = pressureIter;
2135 lastPressureResidual_ = pressureResidual / b_norm;
2136 if (finiteSolution) {
2137 const T beta = deformationParameters.pressureRelaxation;
2138 const T oneMinB =
T(1) - beta;
2139 for (std::size_t i = 0; i < n; ++i)
2141 oneMinB *
nodes[i].pressure + beta *
static_cast<T>(x[i]);
2143 lastPressureResidual_ = std::numeric_limits<T>::infinity();
2145 if (lastPressureResidual_ > deformationParameters.pressureTolerance)
2146 VIENNACORE_LOG_WARNING(
2147 "solvePressure: BiCGSTAB did not converge after " +
2148 std::to_string(lastPressureIters_) +
"/" +
2149 std::to_string(deformationParameters.pressureIterations) +
2150 " iterations (residual=" + std::to_string(lastPressureResidual_) +
2152 std::to_string(deformationParameters.pressureTolerance) +
")");
2157 template <
class SolverT>
2159 const std::vector<Vec3D<SolverT>> &v,
T &diag,
2160 Vec3D<T> &rhs)
const {
2162 rhs = {
T(0),
T(0),
T(0)};
2163 for (
unsigned direction = 0; direction <
D; ++direction) {
2166 const T dSum = plus.distance + minus.distance;
2167 const T plusCoeff =
T(2) / (plus.distance * dSum);
2168 const T minusCoeff =
T(2) / (minus.distance * dSum);
2171 diag += plusCoeff + minusCoeff;
2176 if (deformationParameters.viscosity <= std::numeric_limits<T>::epsilon())
2183 const std::size_t n =
nodes.size();
2184 const T eps = std::numeric_limits<T>::epsilon();
2187 std::vector<T> diag(n);
2188 std::vector<Vec3D<T>> vBC(n), forcing(n);
2190 const std::vector<Vec3D<SolverT>> zeros(
2191 n, Vec3D<SolverT>{SolverT(0), SolverT(0), SolverT(0)});
2192#pragma omp parallel for schedule(static)
2193 for (std::size_t i = 0; i < n; ++i) {
2200 std::vector<Vec3D<T>> b(n);
2202 for (std::size_t i = 0; i < n; ++i) {
2203 for (
unsigned c = 0; c <
D; ++c) {
2204 b[i][c] = vBC[i][c] - forcing[i][c] / deformationParameters.viscosity;
2205 b_norm = std::max(b_norm, std::abs(b[i][c]));
2208 if (b_norm <
T(1e-100))
2213 std::vector<Vec3D<SolverT>> x(n);
2216 for (std::size_t i = 0; i < n; ++i)
2217 for (
unsigned c = 0; c <
D; ++c) {
2218 const T value = vel[i][c];
2219 x[i][c] =
static_cast<SolverT
>(std::isfinite(value) ? value :
T(0));
2223#ifdef VIENNALS_GPU_BICGSTAB
2224 if (gpu::gpuIsValid(gpuStokesBufs_)) {
2225 const std::size_t nf = 2u *
D * n;
2226 if (stokesDiagGpu_.size() !=
D * n || stokesCoeffGpu_.size() != nf) {
2227 VIENNACORE_LOG_ERROR(
"OxidationDeformation: Stokes GPU geometry has "
2228 "the wrong size for the current node set.");
2231 Timer<> tUpload, tSolve;
2232 std::vector<Vec3D<SolverT>> xSolved(n);
2233 unsigned maxGpuIterations = 0;
2234 double maxGpuResidual = 0.0;
2236 for (
unsigned c = 0; c <
D; ++c) {
2237 std::vector<double> bGpu(n), xGpu(n);
2238 for (std::size_t i = 0; i < n; ++i) {
2239 bGpu[i] =
static_cast<double>(b[i][c]);
2240 xGpu[i] =
static_cast<double>(x[i][c]);
2244 const bool gpuUploadOk = gpu::gpuUploadSolverArrays(
2245 gpuStokesBufs_, stokesDiagGpu_.data() + c * n, bGpu.data(),
2246 stokesCoeffGpu_.data(),
static_cast<uint32_t
>(n),
2247 stokesCoeffGpu_.size());
2250 VIENNACORE_LOG_ERROR(
"OxidationDeformation: GPU mode was selected, "
2251 "but uploading Stokes solver arrays failed." +
2255 unsigned gpuIterations = 0;
2256 double gpuResidual = 0.0;
2258 const bool gpuConverged = gpu::gpuSolveBiCGSTAB(
2259 gpuStokesBufs_, xGpu.data(),
static_cast<double>(eps),
2260 deformationParameters.stokesIterations,
2261 static_cast<double>(deformationParameters.stokesTolerance),
2262 gpuIterations, gpuResidual);
2265 if (!gpuConverged || !std::isfinite(gpuResidual)) {
2266 VIENNACORE_LOG_ERROR(
2267 "OxidationDeformation: Stokes GPU BiCGSTAB failed or produced "
2268 "a non-finite residual for component " +
2269 std::to_string(c) +
" (iters=" + std::to_string(gpuIterations) +
2270 ", residual=" + std::to_string(gpuResidual) +
").");
2273 maxGpuIterations = std::max(maxGpuIterations, gpuIterations);
2274 maxGpuResidual = std::max(maxGpuResidual, gpuResidual);
2275 for (std::size_t i = 0; i < n; ++i)
2276 xSolved[i][c] =
static_cast<SolverT
>(xGpu[i]);
2279 for (std::size_t i = 0; i < n; ++i)
2280 for (
unsigned c = 0; c <
D; ++c)
2281 nodes[i].velocity[c] =
static_cast<T>(xSolved[i][c]);
2283 lastStokesIters_ = maxGpuIterations;
2284 lastStokesResidual_ = maxGpuResidual / b_norm;
2286 if (Logger::hasDebug()) {
2287 const std::string tag =
"stokes n=" + std::to_string(n) +
2288 " iters=" + std::to_string(lastStokesIters_) +
2289 " res=" + std::to_string(lastStokesResidual_) +
2291 Logger::getInstance()
2292 .addTiming(tag +
" GPU upload", tUpload)
2293 .addTiming(tag +
" GPU BiCGSTAB", tSolve)
2302 auto stokesMatvec = [&](
const std::vector<Vec3D<SolverT>> &vin,
2303 std::vector<Vec3D<SolverT>> &Av) {
2304#pragma omp parallel for schedule(static)
2305 for (std::size_t i = 0; i < n; ++i) {
2309 for (
unsigned c = 0; c <
D; ++c)
2311 static_cast<SolverT
>(diag[i] * vin[i][c] - rhs[c] + vBC[i][c]);
2316 auto vecDot = [&](
const std::vector<Vec3D<SolverT>> &a,
2317 const std::vector<Vec3D<SolverT>> &bv) {
2319 for (std::size_t i = 0; i < n; ++i)
2320 for (
unsigned c = 0; c <
D; ++c) {
2321 const T av =
static_cast<T>(a[i][c]);
2322 const T bvVal =
static_cast<T>(bv[i][c]);
2323 if (!std::isfinite(av) || !std::isfinite(bvVal))
2324 return std::numeric_limits<T>::quiet_NaN();
2330 auto vecMaxAbs = [&](
const std::vector<Vec3D<SolverT>> &vin) {
2332 for (std::size_t i = 0; i < n; ++i)
2333 for (
unsigned c = 0; c <
D; ++c) {
2334 const T value =
static_cast<T>(vin[i][c]);
2335 if (!std::isfinite(value))
2336 return std::numeric_limits<T>::infinity();
2337 m = std::max(m, std::abs(value));
2343 const Vec3D<SolverT> zero3{SolverT(0), SolverT(0), SolverT(0)};
2344 std::vector<Vec3D<SolverT>> Ax(n), r(n), r_hat(n);
2345 std::vector<Vec3D<SolverT>> pv(n, zero3), sv(n, zero3), y(n), z(n), s(n),
2347 stokesMatvec(x, Ax);
2348 for (std::size_t i = 0; i < n; ++i)
2349 for (
unsigned c = 0; c <
D; ++c) {
2350 r[i][c] =
static_cast<SolverT
>(b[i][c] - Ax[i][c]);
2351 r_hat[i][c] = r[i][c];
2354 T rho =
T(1), alpha =
T(1), omega =
T(1);
2355 T velocityResidual =
T(0);
2356 unsigned stokesIter = 0;
2357 bool stokesBreakdown =
false;
2358 velocityResidual = vecMaxAbs(r);
2360 for (; stokesIter < deformationParameters.stokesIterations; ++stokesIter) {
2361 const T rho_new = vecDot(r_hat, r);
2362 if (!std::isfinite(rho_new)) {
2363 stokesBreakdown =
true;
2366 if (std::abs(rho_new) <
T(1e-100))
2368 if (!std::isfinite(rho) || !std::isfinite(alpha) ||
2369 !std::isfinite(omega) || std::abs(omega) <
T(1e-100)) {
2370 stokesBreakdown =
true;
2374 const T beta = (rho_new / rho) * (alpha / omega);
2375 if (!std::isfinite(beta)) {
2376 stokesBreakdown =
true;
2381 for (std::size_t i = 0; i < n; ++i)
2382 for (
unsigned c = 0; c <
D; ++c)
2383 pv[i][c] =
static_cast<SolverT
>(r[i][c] +
2384 beta * (pv[i][c] - omega * sv[i][c]));
2386 for (std::size_t i = 0; i < n; ++i)
2387 for (
unsigned c = 0; c <
D; ++c) {
2388 const T pvc = pv[i][c];
2389 const T pcDiag = precondDiag[i][c];
2390 y[i][c] =
static_cast<SolverT
>((pcDiag > eps) ? pvc / pcDiag : pvc);
2393 stokesMatvec(y, sv);
2395 const T r_hat_v = vecDot(r_hat, sv);
2396 if (!std::isfinite(r_hat_v)) {
2397 stokesBreakdown =
true;
2400 if (std::abs(r_hat_v) <
T(1e-100))
2403 alpha = rho_new / r_hat_v;
2404 if (!std::isfinite(alpha)) {
2405 stokesBreakdown =
true;
2409 for (std::size_t i = 0; i < n; ++i)
2410 for (
unsigned c = 0; c <
D; ++c)
2411 s[i][c] =
static_cast<SolverT
>(r[i][c] - alpha * sv[i][c]);
2413 velocityResidual = vecMaxAbs(s);
2414 if (!std::isfinite(velocityResidual)) {
2415 stokesBreakdown =
true;
2418 if (velocityResidual < deformationParameters.stokesTolerance * b_norm) {
2419 for (std::size_t i = 0; i < n; ++i)
2420 for (
unsigned c = 0; c <
D; ++c)
2421 x[i][c] =
static_cast<SolverT
>(x[i][c] + alpha * y[i][c]);
2425 for (std::size_t i = 0; i < n; ++i)
2426 for (
unsigned c = 0; c <
D; ++c) {
2427 const T sc = s[i][c];
2428 const T pcDiag = precondDiag[i][c];
2429 z[i][c] =
static_cast<SolverT
>((pcDiag > eps) ? sc / pcDiag : sc);
2434 const T t_s = vecDot(t, s);
2435 const T t_t = vecDot(t, t);
2436 if (!std::isfinite(t_s) || !std::isfinite(t_t)) {
2437 stokesBreakdown =
true;
2440 omega = (t_t >
T(1e-100)) ? t_s / t_t :
T(0);
2441 if (!std::isfinite(omega)) {
2442 stokesBreakdown =
true;
2446 for (std::size_t i = 0; i < n; ++i)
2447 for (
unsigned c = 0; c <
D; ++c) {
2449 static_cast<SolverT
>(x[i][c] + alpha * y[i][c] + omega * z[i][c]);
2450 r[i][c] =
static_cast<SolverT
>(s[i][c] - omega * t[i][c]);
2453 velocityResidual = vecMaxAbs(r);
2454 if (!std::isfinite(velocityResidual)) {
2455 stokesBreakdown =
true;
2458 if (velocityResidual < deformationParameters.stokesTolerance * b_norm)
2462 if (stokesBreakdown)
2463 velocityResidual = std::numeric_limits<T>::infinity();
2465 bool finiteSolution = !stokesBreakdown;
2466 for (std::size_t i = 0; i < n; ++i)
2467 for (
unsigned c = 0; c <
D; ++c)
2468 if (!std::isfinite(
static_cast<T>(x[i][c])))
2469 finiteSolution =
false;
2471 if (finiteSolution) {
2472 for (std::size_t i = 0; i < n; ++i)
2473 for (
unsigned c = 0; c <
D; ++c)
2474 nodes[i].velocity[c] =
static_cast<T>(x[i][c]);
2477 lastStokesIters_ = stokesIter;
2478 lastStokesResidual_ = finiteSolution ? velocityResidual / b_norm
2479 : std::numeric_limits<T>::infinity();
2480 if (lastStokesResidual_ > deformationParameters.stokesTolerance)
2481 VIENNACORE_LOG_WARNING(
2482 "solveStokesVelocity: BiCGSTAB did not converge after " +
2483 std::to_string(lastStokesIters_) +
"/" +
2484 std::to_string(deformationParameters.stokesIterations) +
2485 " iterations (residual=" + std::to_string(lastStokesResidual_) +
2487 std::to_string(deformationParameters.stokesTolerance) +
")");
2491 std::vector<Vec3D<T>> velocities;
2492 velocities.reserve(
nodes.size());
2493 for (
const auto &node :
nodes)
2494 velocities.push_back(node.velocity);
2499 std::vector<T> pressures;
2500 pressures.reserve(
nodes.size());
2501 for (
const auto &node :
nodes)
2502 pressures.push_back(node.pressure);
2506 template <
class SolverT>
2509 const std::vector<T> &ambientBoundaryPressure,
2510 std::size_t nodeId,
unsigned direction,
2512 const auto &node =
nodes[nodeId];
2513 IndexType neighbor = node.index;
2514 neighbor[direction] += offset;
2517 return {
static_cast<T>(pressure[nodeId]),
gridDelta};
2520 if (neighborId !=
noNode) {
2521 if (touchesAmbient_[neighborId])
2522 return {ambientBoundaryPressure[neighborId],
gridDelta};
2523 return {
static_cast<T>(pressure[neighborId]),
gridDelta};
2526 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2527 const std::size_t nn =
nodes.size();
2528 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2529 const T faceDist = faceBCDists_[fi * nn + nodeId];
2530 if (faceType == Boundary::AMBIENT)
2531 return {ambientBoundaryPressure[nodeId], faceDist};
2537 if (faceType == Boundary::REACTION)
2538 return {
static_cast<T>(pressure[nodeId]), faceDist};
2539 if (faceType == Boundary::MASK)
2541 static_cast<T>(pressure[nodeId])),
2544 return {
static_cast<T>(pressure[nodeId]),
gridDelta};
2547 template <
class SolverT>
2548 StencilPoint<Vec3D<T>>
2550 std::size_t nodeId,
unsigned direction,
2552 const auto &node =
nodes[nodeId];
2553 IndexType neighbor = node.index;
2554 neighbor[direction] += offset;
2556 const auto toT = [](
const Vec3D<SolverT> &v) -> Vec3D<T> {
2557 return {
static_cast<T>(v[0]),
static_cast<T>(v[1]),
static_cast<T>(v[2])};
2561 return {toT(velocity[nodeId]),
gridDelta};
2564 if (neighborId !=
noNode)
2565 return {toT(velocity[neighborId]),
gridDelta};
2567 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2568 const std::size_t nn =
nodes.size();
2569 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2570 const T faceDist = faceBCDists_[fi * nn + nodeId];
2571 if (faceType == Boundary::REACTION)
2573 if (faceType == Boundary::AMBIENT)
2575 faceDist, toT(velocity[nodeId])),
2577 if (faceType == Boundary::MASK)
2581 return {toT(velocity[nodeId]),
gridDelta};
2587 const auto &node =
nodes[nodeId];
2588 IndexType neighbor = node.index;
2589 neighbor[direction] += offset;
2595 if (neighborId !=
noNode)
2598 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2599 const std::size_t nn =
nodes.size();
2600 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2601 const T faceDist = faceBCDists_[fi * nn + nodeId];
2602 if (faceType == Boundary::AMBIENT)
2604 if (faceType == Boundary::REACTION)
2605 return {node.pressure, faceDist};
2606 if (faceType == Boundary::MASK)
2617 const auto &node =
nodes[nodeId];
2618 IndexType neighbor = node.index;
2619 neighbor[direction] += offset;
2625 if (neighborId !=
noNode)
2628 const unsigned fi = direction * 2u + (offset == 1 ? 1u : 0u);
2629 const std::size_t nn =
nodes.size();
2630 const Boundary faceType = faceBCTypes_[fi * nn + nodeId];
2631 const T faceDist = faceBCDists_[fi * nn + nodeId];
2632 if (faceType == Boundary::REACTION)
2634 if (faceType == Boundary::AMBIENT)
2636 faceDist, node.velocity),
2638 if (faceType == Boundary::MASK)
2647 const auto count = std::min(previous.size(),
nodes.size());
2648 for (std::size_t i = 0; i < count; ++i) {
2649 for (
unsigned j = 0; j <
D; ++j) {
2650 maxChange = std::max(maxChange,
2651 std::abs(
nodes[i].velocity[j] - previous[i][j]));
2652 maxVelocity = std::max(maxVelocity, std::abs(
nodes[i].velocity[j]));
2656 if (maxVelocity <= std::numeric_limits<T>::epsilon())
2658 return maxChange / maxVelocity;
2664 const auto count = std::min(previous.size(),
nodes.size());
2665 for (std::size_t i = 0; i < count; ++i) {
2667 std::max(maxChange, std::abs(
nodes[i].pressure - previous[i]));
2668 maxPressure = std::max(maxPressure, std::abs(
nodes[i].pressure));
2671 if (maxPressure <= std::numeric_limits<T>::epsilon())
2673 return maxChange / maxPressure;
2679 const auto deviatoricRate =
2684 (relaxationTime <= std::numeric_limits<T>::epsilon())
2686 : std::exp(-deformationParameters.stressTimeStep / relaxationTime);
2688 std::array<T, 9> deviatoricStress{};
2689 for (
unsigned i = 0; i < 9; ++i) {
2690 const T viscousStress =
2691 T(2) * deformationParameters.viscosity * deviatoricRate[i];
2692 deviatoricStress[i] =
2693 decay * previousStress[i] + (
T(1) - decay) * viscousStress;
2696 return deviatoricStress;
2703 return deformationParameters.ambientPressure +
2708 int ,
T fallbackPressure)
const {
2709 return fallbackPressure;
2713 unsigned direction,
int offset,
2715 const Vec3D<T> &interiorVelocity)
const {
2716 Vec3D<T> boundaryVelocity = interiorVelocity;
2721 Vec3D<T> deviatoricTraction{0., 0., 0.};
2722 for (
unsigned component = 0; component <
D; ++component) {
2723 for (
unsigned j = 0; j <
D; ++j)
2724 deviatoricTraction[component] +=
2725 deviatoricStress[
tensorIndex(component, j)] * normal[j];
2728 for (
unsigned component = 0; component <
D; ++component) {
2729 const T normalTraction =
2730 pressure * normal[component] - deviatoricTraction[component];
2731 const T faceDerivative = normalTraction * normal[direction] /
2732 std::max(deformationParameters.viscosity,
2733 std::numeric_limits<T>::epsilon());
2734 boundaryVelocity[component] +=
2735 static_cast<T>(offset) * distance * faceDerivative;
2738 return boundaryVelocity;
2742 const Vec3D<T> &interiorVelocity)
const {
2743 if (maskVelocityField !=
nullptr) {
2744 Vec3D<T> coordinate{0., 0., 0.};
2745 for (
unsigned i = 0; i <
D; ++i)
2747 return maskVelocityField->getVectorVelocity(
2748 coordinate, deformationParameters.material, {0., 0., 0.}, 0);
2750 return {0., 0., 0.};
2754 avgExpansionSpeed_ = 0.;
2758 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2759 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2761 std::size_t count = 0;
2763 for (
const auto &node :
nodes) {
2764 bool touchesReactionBoundary =
false;
2765 for (
unsigned direction = 0; direction <
D; ++direction) {
2766 for (
int offset : {-1, 1}) {
2767 IndexType neighbor = node.index;
2768 neighbor[direction] += offset;
2776 neighbor) == Boundary::REACTION) {
2777 touchesReactionBoundary =
true;
2781 if (touchesReactionBoundary)
2785 if (touchesReactionBoundary) {
2786 Vec3D<T> coordinate{0., 0., 0.};
2787 for (
unsigned i = 0; i <
D; ++i)
2788 coordinate[i] = node.index[i] *
gridDelta;
2789 avgExpansionSpeed_ +=
2790 (oxidationParameters.expansionCoefficient -
T(1)) *
2791 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
2798 avgExpansionSpeed_ /=
static_cast<T>(count);
2802 Vec3D<T> coordinate{0., 0., 0.};
2803 for (
unsigned i = 0; i <
D; ++i)
2807 reactionSign * expansionVelocity);
2811 if (diffusionField ==
nullptr || ambientInterface ==
nullptr)
2812 return {0., 0., 0.};
2815 for (
unsigned i = 0; i <
D; ++i)
2816 index[i] = std::llround(coordinate[i] /
gridDelta);
2818 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2824 Vec3D<T> maxVelocity{0., 0., 0.};
2825 if (ambientInterface ==
nullptr || diffusionField ==
nullptr)
2828 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2829 for (; !ambientIt.isFinished(); ++ambientIt) {
2830 if (!ambientIt.isDefined())
2833 Vec3D<T> coordinate{0., 0., 0.};
2834 const auto &index = ambientIt.getStartIndices();
2835 for (
unsigned d = 0; d <
D; ++d)
2839 for (
unsigned d = 0; d <
D; ++d)
2840 maxVelocity[d] = std::max(maxVelocity[d], std::abs(velocity[d]));
2847 for (
unsigned i = 0; i <
D; ++i) {
2854 Vec3D<T> gradient{0., 0., 0.};
2855 for (
unsigned i = 0; i <
D; ++i)
2863 for (
unsigned i = 0; i <
D; ++i)
2864 forcing[i] -= stressDivergence[i];
2869 Vec3D<T> divergence{0., 0., 0.};
2870 for (
unsigned component = 0; component <
D; ++component) {
2871 for (
unsigned direction = 0; direction <
D; ++direction) {
2872 IndexType pos = index;
2873 IndexType neg = index;
2874 pos[direction] += 1;
2875 neg[direction] -= 1;
2878 divergence[component] +=
2891 const std::size_t nodeId =
lookupNode(index);
2895 std::array<T, 9> deviatoric =
nodes[nodeId].stressTensor;
2896 for (
unsigned i = 0; i < 3; ++i)
2903 return deformationParameters.ambientPressure;
2905 const std::size_t nodeId =
lookupNode(index);
2907 return deformationParameters.ambientPressure;
2908 return nodes[nodeId].pressure;
2912 return (oxidationParameters.expansionCoefficient -
T(1)) *
2913 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
2918 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2923 if (boundary == Boundary::AMBIENT) {
2924 ConstSparseIterator ambientIt(ambientInterface->getDomain());
2927 if (boundary == Boundary::MASK && maskInterface !=
nullptr) {
2928 ConstSparseIterator maskIt(maskInterface->getDomain());
2932 ConstSparseIterator reactionIt(reactionInterface->getDomain());
2937 const IndexType &index)
const {
2938 Vec3D<T> normal{0., 0., 0.};
2941 for (
unsigned i = 0; i <
D; ++i) {
2942 IndexType pos = index;
2943 IndexType neg = index;
2952 norm += normal[i] * normal[i];
2955 if (norm <= std::numeric_limits<T>::epsilon()) {
2956 normal = Vec3D<T>{0., 0., 0.};
2961 norm = std::sqrt(norm);
2962 for (
unsigned i = 0; i <
D; ++i)
2968#pragma omp parallel for schedule(static)
2969 for (std::size_t i = 0; i <
nodes.size(); ++i)
2976 (relaxationTime <= std::numeric_limits<T>::epsilon())
2978 : std::exp(-deformationParameters.stressTimeStep / relaxationTime);
2982 std::vector<std::pair<IndexType, std::array<T, 9>>> historyEntries(
2984#pragma omp parallel for schedule(static)
2985 for (std::size_t i = 0; i <
nodes.size(); ++i) {
2986 auto &node =
nodes[i];
2988 const auto deviatoricRate =
2992 std::array<T, 9> deviatoricStress{};
2993 for (
unsigned j = 0; j < 9; ++j) {
2994 const T viscousStress =
2995 T(2) * deformationParameters.viscosity * deviatoricRate[j];
2996 deviatoricStress[j] =
2997 decay * previousStress[j] + (
T(1) - decay) * viscousStress;
3000 node.stressTensor = deviatoricStress;
3001 for (
unsigned j = 0; j < 3; ++j)
3002 node.stressTensor[
tensorIndex(j, j)] -= node.pressure;
3005 historyEntries[i] = {node.index, deviatoricStress};
3010 nextHistory.reserve(
nodes.size());
3011 for (
const auto &entry : historyEntries)
3012 nextHistory[entry.first] = entry.second;
3013 deviatoricStressHistory.swap(nextHistory);
3017 std::array<T, 9> tensor{};
3018 for (
unsigned i = 0; i <
D; ++i) {
3019 for (
unsigned j = 0; j <
D; ++j) {
3028 unsigned direction)
const {
3029 const std::size_t nodeId =
lookupNode(index);
3036 minus.value[component],
nodes[nodeId].velocity[component],
3037 plus.value[component], minus.distance, plus.distance);
3041 const std::size_t nodeId =
lookupNode(index);
3048 minus.distance, plus.distance);
3053 std::array<T, 9> result = tensor;
3054 const T mean = trace /
T(3);
3055 for (
unsigned i = 0; i < 3; ++i)
3061 const auto found = deviatoricStressHistory.find(index);
3062 if (found == deviatoricStressHistory.end())
3064 return found->second;
3068 if (deformationParameters.stressRelaxationTime >
T(0))
3069 return deformationParameters.stressRelaxationTime;
3070 if (deformationParameters.shearModulus > std::numeric_limits<T>::epsilon())
3071 return deformationParameters.viscosity /
3072 deformationParameters.shearModulus;
3077 T doubleContraction = 0.;
3078 for (
unsigned i = 0; i < 3; ++i) {
3079 for (
unsigned j = 0; j < 3; ++j) {
3080 const T value =
T(0.5) * (deviatoricStress[
tensorIndex(i, j)] +
3082 doubleContraction += value * value;
3085 return std::sqrt(
T(1.5) * doubleContraction);
3090 for (
unsigned i = 0; i < 3; ++i) {
3091 for (
unsigned j = 0; j < 3; ++j)
3092 result += normal[i] * tensor[
tensorIndex(i, j)] * normal[j];
3098 ConstSparseIterator &ambientIt,
3099 ConstSparseIterator &maskIt,
3100 const IndexType &inside,
3101 const IndexType &outside)
const {
3107 ConstSparseIterator &ambientIt,
3108 ConstSparseIterator &maskIt,
3109 const IndexType &inside,
3110 const IndexType &outside)
const {
3111 const T reactionInside =
valueAt(reactionIt, inside);
3112 const T reactionOutside =
valueAt(reactionIt, outside);
3113 const T ambientInside =
valueAt(ambientIt, inside);
3114 const T ambientOutside =
valueAt(ambientIt, outside);
3118 const bool reactionCrosses =
crosses(reactionInside, reactionOutside);
3119 const bool ambientCrosses =
crosses(ambientInside, ambientOutside);
3120 const bool maskCrosses =
3121 maskInterface !=
nullptr &&
crosses(maskInside, maskOutside);
3123 if (!reactionCrosses && !ambientCrosses && !maskCrosses)
3125 if (reactionCrosses && !ambientCrosses && !maskCrosses)
3126 return {Boundary::REACTION,
3128 if (!reactionCrosses && ambientCrosses && !maskCrosses)
3130 maskInside, maskOutside,
3132 if (!reactionCrosses && !ambientCrosses && maskCrosses)
3135 const T reactionDistance =
3137 : std::numeric_limits<T>::max();
3138 const T ambientDistance =
3140 : std::numeric_limits<T>::max();
3141 const T maskDistance = maskCrosses
3143 : std::numeric_limits<T>::max();
3144 if (reactionDistance <= ambientDistance && reactionDistance <= maskDistance)
3145 return {Boundary::REACTION, reactionDistance};
3146 if (ambientDistance != std::numeric_limits<T>::max()) {
3147 const auto maskedAmbient =
3149 if (maskedAmbient.boundary == Boundary::MASK)
3150 return maskedAmbient;
3152 if (maskDistance <= ambientDistance)
3153 return {Boundary::MASK, maskDistance};
3154 return {Boundary::AMBIENT, ambientDistance};
3158 ConstSparseIterator &ambientIt,
3159 ConstSparseIterator &maskIt,
const IndexType &index,
3160 Boundary requestedBoundary)
const {
3161 for (
unsigned direction = 0; direction <
D; ++direction) {
3162 for (
int offset : {-1, 1}) {
3163 IndexType neighbor = index;
3164 neighbor[direction] += offset;
3185 constexpr T eps =
T(1e-9);
3186 return reactionSign * reactionPhi >= -eps &&
3187 ambientSign * ambientPhi >= -eps;
3191 if (maskInterface ==
nullptr)
3196 bool isInsideMask(ConstSparseIterator &maskIt,
const IndexType &index)
const {
3197 if (maskInterface ==
nullptr)
3199 return maskSign *
valueAt(maskIt, index) >= 0.;
3203 if (maskInterface ==
nullptr)
3204 return std::numeric_limits<T>::max();
3205 return valueAt(maskIt, index);
3211 return {Boundary::MASK, distance};
3216 if (maskInterface !=
nullptr &&
3217 static_cast<T>(maskSign) * maskOutside >=
T(0))
3218 return {Boundary::MASK, distance};
3219 return {Boundary::AMBIENT, distance};
3223 if (maskInterface ==
nullptr)
3225 const T fraction = std::clamp(distance /
gridDelta,
T(0),
T(1));
3228 const T maskPhi = insidePhi + fraction * (outsidePhi - insidePhi);
3229 return static_cast<T>(maskSign) * maskPhi >=
T(0);
3234 insidePhi, outsidePhi,
3235 deformationParameters.minMechanicsBoundaryDistance,
gridDelta);
3239 T minusDistance,
T plusDistance) {
3240 const T denominator =
3241 minusDistance * plusDistance * (minusDistance + plusDistance);
3242 if (denominator <= std::numeric_limits<T>::epsilon())
3245 return (-plusDistance * plusDistance * minusValue +
3246 (plusDistance * plusDistance - minusDistance * minusDistance) *
3248 minusDistance * minusDistance * plusValue) /
3252 static constexpr unsigned tensorIndex(
unsigned row,
unsigned column) {
3253 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:90
static constexpr std::size_t noNode
Definition lsOxidationSolverBase.hpp:96
bool crosses(T a, T b) const
Definition lsOxidationSolverBase.hpp:110
std::size_t lookupNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:137
std::size_t linearIndex(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:143
void initNodeLookup()
Definition lsOxidationSolverBase.hpp:130
bool inBounds(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:123
std::array< std::size_t, D > strides
Definition lsOxidationSolverBase.hpp:101
T gridDelta
Definition lsOxidationSolverBase.hpp:102
std::vector< std::size_t > nodeLookupFlat
Definition lsOxidationSolverBase.hpp:97
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:207
std::array< std::size_t, D > extents
Definition lsOxidationSolverBase.hpp:100
viennahrle::ConstSparseIterator< typename Domain< T, D >::DomainType > ConstSparseIterator
Definition lsOxidationSolverBase.hpp:93
T valueAt(ConstSparseIterator &it, const IndexType &index) const
Definition lsOxidationSolverBase.hpp:118
bool increment(IndexType &index) const
Definition lsOxidationSolverBase.hpp:152
IndexType minIndex
Definition lsOxidationSolverBase.hpp:98
viennahrle::Index< D > IndexType
Definition lsOxidationSolverBase.hpp:92
std::size_t findNearbyNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:166
IndexType maxIndex
Definition lsOxidationSolverBase.hpp:99
T levelSetCrossingDistance(T insidePhi, T outsidePhi, T minBoundaryFraction, T gridDelta)
Definition lsOxidationSolverBase.hpp:76
T clampLevelSetPhi(T v)
Clamp HRLE far-field sentinels (±DBL_MAX) to ±1 before differencing to prevent DBL_MAX² overflow that...
Definition lsOxidationSolverBase.hpp:71
Vec3D< T > vecScaled(const Vec3D< T > &source, T factor)
Definition lsOxidationSolverBase.hpp:40
void vecAddTo(Vec3D< T > &target, const Vec3D< T > &source)
Definition lsOxidationSolverBase.hpp:64
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
Definition lsOxidationSolverBase.hpp:34