16#include <unordered_map>
21#include <Eigen/SparseCore>
22#include <Eigen/SparseLU>
23#include <amgcl/adapter/crs_tuple.hpp>
24#include <amgcl/amg.hpp>
25#include <amgcl/backend/builtin.hpp>
26#include <amgcl/coarsening/smoothed_aggregation.hpp>
27#include <amgcl/make_solver.hpp>
28#include <amgcl/preconditioner/schur_pressure_correction.hpp>
29#include <amgcl/relaxation/as_preconditioner.hpp>
30#include <amgcl/relaxation/ilu0.hpp>
31#include <amgcl/relaxation/spai0.hpp>
32#include <amgcl/solver/fgmres.hpp>
33#include <amgcl/solver/preonly.hpp>
110template <
class T,
int D>
113 using IndexType = viennahrle::Index<D>;
114 using ConstSparseIterator =
115 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
136 enum class Boundary { NONE, REACTION, AMBIENT, ANCHOR };
138 struct BoundaryIntersection {
139 Boundary boundary = Boundary::NONE;
145 Vec3D<T> velocity{0., 0., 0.};
148 std::array<T, 9> strainRateTensor{};
149 std::array<T, 9> stressTensor{};
150 T vonMisesStress = 0.;
151 bool nitride =
false;
154 SmartPointer<Domain<T, D>> reactionInterface =
nullptr;
155 SmartPointer<Domain<T, D>> ambientInterface =
nullptr;
156 SmartPointer<Domain<T, D>> maskInterface =
nullptr;
157 SmartPointer<OxidationDiffusion<T, D>> diffusionField =
nullptr;
160 int reactionSign = 1;
161 int ambientSign = -1;
164 IndexType requestedMinIndex{};
165 IndexType requestedMaxIndex{};
166 unsigned iterations = 0;
167 T residual = std::numeric_limits<T>::max();
170 T lastContinuityResidual_ = 0.;
171 T lastStokesResidual_ = 0.;
172 T avgExpansionSpeed_ = 0.;
173 bool avgExpansionSpeedComputed =
false;
175 bool nodesDirty_ =
true;
176 std::array<T, D> maxVelocity_{};
177 bool useRequestedBounds =
false;
179 deviatoricStressHistory;
181 std::vector<T> oxideEta_;
184 std::vector<T> nitrideEta_;
190 std::vector<std::array<T, 9>> nitrideStartNode_;
191 std::vector<std::array<T, 9>> nitrideStressEnd_;
192 std::vector<std::array<T, 9>> nitrideStressAdvected_;
193 bool nitrideStressAdvectedValid_ =
false;
197 std::vector<std::array<T, 9>> oxideStressEnd_;
198 std::vector<std::array<T, 9>> oxideStressAdvected_;
199 bool oxideStressAdvectedValid_ =
false;
204 std::vector<Vec3D<T>> reactionVelocity_;
205 std::vector<std::array<T, 9>> startStress_;
206 bool boundaryCacheValid_ =
false;
208 static bool isFiniteVec(
const Vec3D<T> &value) {
209 for (
unsigned i = 0; i < 3; ++i)
210 if (!std::isfinite(value[i]))
215 static bool isFiniteTensor(
const std::array<T, 9> &value) {
216 for (
const auto component : value)
217 if (!std::isfinite(component))
223 std::vector<Boundary> faceBCTypes_;
224 std::vector<T> faceBCDists_;
230#ifdef VIENNALS_GPU_BICGSTAB
240 : reactionInterface(passedReactionInterface),
241 ambientInterface(passedAmbientInterface),
242 diffusionField(passedDiffusionField),
243 deformationParameters(passedDeformationParameters),
244 oxidationParameters(passedOxidationParameters) {}
246 template <
class... Args>
static auto New(Args &&...args) {
247 return SmartPointer<OxidationDeformation>::New(std::forward<Args>(args)...);
251 reactionInterface = passedInterface;
257 ambientInterface = passedInterface;
263 int passedMaskSign = 1) {
264 maskInterface = passedInterface;
265 maskSign = (passedMaskSign < 0) ? -1 : 1;
271 maskInterface =
nullptr;
278 diffusionField = passedDiffusionField;
283 oxidationParameters = passedParameters;
289 deformationParameters = passedParameters;
306 reactionSign = (passedReactionSign < 0) ? -1 : 1;
307 ambientSign = (passedAmbientSign < 0) ? -1 : 1;
312 const IndexType &passedMaxIndex) {
313 requestedMinIndex = passedMinIndex;
314 requestedMaxIndex = passedMaxIndex;
315 useRequestedBounds =
true;
321 useRequestedBounds =
false;
333 return deformationParameters.nitrideYoungModulus > 0. &&
334 maskInterface !=
nullptr;
337 return std::max(
T(deformationParameters.stressTimeStep),
338 std::numeric_limits<T>::min());
341 return std::clamp(
T(deformationParameters.nitridePoissonRatio),
T(-0.95),
345 return T(deformationParameters.nitrideYoungModulus) /
350 return T(deformationParameters.nitrideYoungModulus) * nu /
351 ((
T(1) + nu) * (
T(1) -
T(2) * nu));
359 return deformationParameters.nitrideViscosity > 0.;
363 deformationParameters.nitrideShearActivationVolume > 0.;
374 std::numeric_limits<T>::min() *
T(1e10));
379 :
T(deformationParameters.nitrideViscosity);
391 twoEta =
T(2) * etaImpl;
393 T(2) * etaImpl /
T(3);
397 const auto it = nitrideStressStart_.find(index);
398 return it == nitrideStressStart_.end() ? std::array<T, 9>{} : it->second;
401 static const std::array<T, 9> zero{};
402 return i < nitrideStartNode_.size() ? nitrideStartNode_[i] : zero;
406 ConstSparseIterator &reactionIt, ConstSparseIterator &maskIt,
407 const IndexType &inside,
const IndexType &outside)
const {
410 if (
crosses(maskInside, maskOutside))
412 const T reactionInside =
valueAt(reactionIt, inside);
413 const T reactionOutside =
valueAt(reactionIt, outside);
414 if (
crosses(reactionInside, reactionOutside))
415 return {Boundary::ANCHOR,
425 auto &node =
nodes[i];
431 for (
unsigned d = 0; d <
D; ++d)
433 std::array<T, 9> end = start;
435 for (
unsigned r = 0; r < 3; ++r) {
436 for (
unsigned c = 0; c < 3; ++c)
438 dt *
T(2) * mu * node.strainRateTensor[
tensorIndex(r, c)];
443 const T mean = (start[0] + start[4] + start[8]) /
T(3);
444 T twoEta, volumetric;
446 for (
unsigned r = 0; r < 3; ++r) {
447 for (
unsigned c = 0; c < 3; ++c)
451 end[
tensorIndex(r, r)] += (
T(1) - decay) * mean + volumetric * trace;
454 if (i < nitrideStressEnd_.size())
455 nitrideStressEnd_[i] = end;
456 nitrideStressAdvectedValid_ =
false;
457 node.stressTensor = end;
458 const T meanStress = (end[0] + end[4] + end[8]) /
T(3);
460 for (
unsigned d = 0; d < 3; ++d)
476 std::vector<std::array<T, 9>> &out,
bool nitride)
const {
477 const std::size_t n =
nodes.size();
482#pragma omp parallel for schedule(static)
483 for (std::size_t i = 0; i < n; ++i) {
484 if (
nodes[i].nitride != nitride)
486 std::array<T, D> base{};
487 std::array<T, D> frac{};
488 std::array<T, D> departure{};
489 for (
unsigned d = 0; d <
D; ++d) {
490 const T x =
static_cast<T>(
nodes[i].index[d]) -
493 base[d] = std::floor(x);
494 frac[d] = x - base[d];
496 if (!nitride &&
macPhiRAt(departure) <
T(0)) {
497 out[i] = std::array<T, 9>{};
500 std::array<T, 9> sum{};
502 for (
unsigned corner = 0; corner < (1u <<
D); ++corner) {
505 for (
unsigned d = 0; d <
D; ++d) {
506 const bool up = (corner >> d) & 1u;
507 idx[d] =
static_cast<viennahrle::IndexType
>(base[d]) + (up ? 1 : 0);
508 weight *= up ? frac[d] : (
T(1) - frac[d]);
515 for (
unsigned k = 0; k < 9; ++k)
516 sum[k] += weight * end[j][k];
519 if (weightSum >
T(1e-6))
520 for (
unsigned k = 0; k < 9; ++k)
521 out[i][k] = sum[k] / weightSum;
525 advectStress(nitrideStressEnd_, nitrideStressAdvected_,
true);
526 nitrideStressAdvectedValid_ =
true;
529 advectStress(oxideStressEnd_, oxideStressAdvected_,
false);
530 oxideStressAdvectedValid_ =
true;
536 const std::vector<std::array<T, 9>> &field,
bool nitride,
537 std::array<T, 9> &out)
const {
538 if (field.size() !=
nodes.size())
540 std::array<T, 9> sum{};
542 auto add = [&](
const IndexType &nb) {
546 for (
unsigned k = 0; k < 9; ++k)
547 sum[k] += field[j][k];
550 for (
unsigned d = 0; d <
D; ++d)
551 for (
int off : {-1, 1}) {
557 unsigned corners = 1;
558 for (
unsigned d = 0; d <
D; ++d)
560 for (
unsigned code = 0; code < corners; ++code) {
562 unsigned rest = code;
564 for (
unsigned d = 0; d <
D; ++d) {
565 const int off =
static_cast<int>(rest % 3) - 1;
568 centre = centre && off == 0;
576 for (
unsigned k = 0; k < 9; ++k)
577 out[k] = sum[k] /
static_cast<T>(count);
586 if (maskInterface ==
nullptr ||
nodes.empty())
588 if (!nitrideStressAdvectedValid_)
590 using VD =
typename PointData<T>::VectorDataType;
591 VD hist0, hist1, hist2, vel, out0, out1, out2;
592 ConstSparseIterator it(maskInterface->getDomain());
593 for (; !it.isFinished(); ++it) {
596 const IndexType idx = it.getStartIndices();
599 j < nitrideStressAdvected_.size()) {
600 const auto &h = nitrideStressAdvected_[j];
601 const auto &e = nitrideStressEnd_[j];
602 hist0.push_back({h[0], h[1], h[2]});
603 hist1.push_back({h[3], h[4], h[5]});
604 hist2.push_back({h[6], h[7], h[8]});
605 vel.push_back(isFiniteVec(
nodes[j].velocity)
607 : Vec3D<T>{
T(0),
T(0),
T(0)});
608 out0.push_back({e[0], e[1], e[2]});
609 out1.push_back({e[3], e[4], e[5]});
610 out2.push_back({e[6], e[7], e[8]});
616 std::array<T, 9> old{};
619 hist0.push_back({old[0], old[1], old[2]});
620 hist1.push_back({old[3], old[4], old[5]});
621 hist2.push_back({old[6], old[7], old[8]});
622 vel.push_back({
T(0),
T(0),
T(0)});
623 out0.push_back({
T(0),
T(0),
T(0)});
624 out1.push_back({
T(0),
T(0),
T(0)});
625 out2.push_back({
T(0),
T(0),
T(0)});
628 auto &pd = maskInterface->getPointData();
629 pd.insertReplaceVectorData(std::move(hist0),
"NitStressR0");
630 pd.insertReplaceVectorData(std::move(hist1),
"NitStressR1");
631 pd.insertReplaceVectorData(std::move(hist2),
"NitStressR2");
632 pd.insertReplaceVectorData(std::move(vel),
"MaskVelocity");
633 pd.insertReplaceVectorData(std::move(out0),
"MaskStressR0");
634 pd.insertReplaceVectorData(std::move(out1),
"MaskStressR1");
635 pd.insertReplaceVectorData(std::move(out2),
"MaskStressR2");
639 nitrideStressStart_.clear();
642 auto &pd = maskInterface->getPointData();
643 const int r0 = pd.getVectorDataIndex(
"NitStressR0");
644 const int r1 = pd.getVectorDataIndex(
"NitStressR1");
645 const int r2 = pd.getVectorDataIndex(
"NitStressR2");
646 if (r0 == -1 || r1 == -1 || r2 == -1)
648 const auto *d0 = pd.getVectorData(r0);
649 const auto *d1 = pd.getVectorData(r1);
650 const auto *d2 = pd.getVectorData(r2);
651 ConstSparseIterator it(maskInterface->getDomain());
652 for (; !it.isFinished(); ++it) {
655 const auto id = it.getPointId();
656 if (
id >=
static_cast<decltype(
id)
>(d0->size()) ||
657 id >=
static_cast<decltype(
id)
>(d1->size()) ||
658 id >=
static_cast<decltype(
id)
>(d2->size()))
660 const auto &a = (*d0)[id];
661 const auto &b = (*d1)[id];
662 const auto &c = (*d2)[id];
663 std::array<T, 9> st{a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]};
664 if (isFiniteTensor(st))
665 nitrideStressStart_[it.getStartIndices()] = st;
668 std::vector<std::pair<IndexType, std::array<T, 9>>> fills;
669 for (
const auto &node :
nodes) {
670 if (!node.nitride || nitrideStressStart_.count(node.index))
672 std::array<T, 9> sum{};
674 for (
unsigned d = 0; d <
D; ++d)
675 for (
int off : {-1, 1}) {
676 IndexType nb = node.index;
678 const auto found = nitrideStressStart_.find(nb);
679 if (found == nitrideStressStart_.end())
681 for (
unsigned k = 0; k < 9; ++k)
682 sum[k] += found->second[k];
687 for (
unsigned k = 0; k < 9; ++k)
688 sum[k] /=
static_cast<T>(count);
689 fills.push_back({node.index, sum});
691 for (
const auto &fill : fills)
692 nitrideStressStart_[fill.first] = fill.second;
697 if (reactionInterface ==
nullptr || ambientInterface ==
nullptr ||
698 diffusionField ==
nullptr) {
699 Logger::getInstance()
700 .addError(
"OxidationDeformation: Missing interface or "
706 VIENNACORE_LOG_ERROR(
707 "OxidationDeformation: a mask interface needs the monolithic "
708 "nitride mechanics (nitrideYoungModulus > 0).");
716 Logger::getInstance()
717 .addWarning(
"OxidationDeformation: no oxide nodes found after "
718 "buildNodes(). Verify that the reaction and ambient "
719 "level sets enclose a non-empty oxide band.")
728 const std::size_t nn =
nodes.size();
734 boundaryCacheValid_ =
false;
735 Logger::getInstance()
736 .addTiming(
" deformation n=" + std::to_string(nn) +
740 avgExpansionSpeedComputed =
false;
742 maxVelocity_.fill(
T(0));
743 for (
const auto &node :
nodes) {
744 for (
unsigned d = 0; d <
D; ++d)
745 maxVelocity_[d] = std::max(maxVelocity_[d], std::abs(node.velocity[d]));
753 for (
unsigned d = 0; d <
D; ++d)
754 maxVelocity_[d] = std::max(maxVelocity_[d], unresolvedMax[d]);
762 unsigned long )
final {
766 if (deformationParameters.material >= 0 &&
767 material != deformationParameters.material)
772 for (
unsigned d = 0; d <
D; ++d)
773 norm2 += velocity[d] * velocity[d];
774 bool resolved = norm2 > std::numeric_limits<T>::epsilon();
788 for (
unsigned d = 0; d <
D; ++d)
789 index[d] = std::llround(coordinate[d] /
gridDelta);
791 index, [](std::size_t) {
return true; }, velocity);
801 const Vec3D<T> &normalVector,
802 unsigned long )
final {
807 const Vec3D<T> & )
final {
808 if (deformationParameters.material >= 0 &&
809 material != deformationParameters.material)
811 return maxVelocity_[direction];
815 template <
class ValueType,
class NodeAccessor>
816 ValueType getField(
const IndexType &index, ValueType fallback,
817 NodeAccessor accessor)
const {
820 return accessor(
nodes[nodeId]);
825 return accessor(
nodes[nearby]);
828 template <
class ValueType,
class NodeAccessor>
829 ValueType getField(
const Vec3D<T> &coordinate, ValueType fallback,
830 NodeAccessor accessor)
const {
837 if constexpr (!std::is_same_v<ValueType, Vec3D<T>> &&
838 !std::is_same_v<ValueType, T>) {
840 for (
unsigned i = 0; i <
D; ++i)
841 index[i] = std::llround(coordinate[i] /
gridDelta);
842 return getField(index, fallback, accessor);
844 using IdxScalar = std::decay_t<decltype(std::declval<IndexType>()[0])>;
847 for (
unsigned d = 0; d <
D; ++d) {
849 const T c_flo = std::floor(c);
850 lo[d] =
static_cast<IdxScalar
>(c_flo);
855 T totalWeight =
T(0);
856 for (
int corner = 0; corner < (1 <<
D); ++corner) {
859 for (
unsigned d = 0; d <
D; ++d) {
860 if ((corner >> d) & 1) {
872 result = result + accessor(
nodes[nodeId]) * w;
876 if (totalWeight <
T(1e-14))
878 if (totalWeight <
T(1) -
T(1e-6))
879 result = result * (
T(1) / totalWeight);
886 return getField(coordinate, Vec3D<T>{0., 0., 0.},
887 [](
const Node &n) {
return n.velocity; });
899 for (
unsigned d = 0; d <
D; ++d)
900 norm2 += velocity[d] * velocity[d];
901 if (norm2 > std::numeric_limits<T>::epsilon())
904 for (
unsigned d = 0; d <
D; ++d)
905 index[d] = std::llround(coordinate[d] /
gridDelta);
907 index, [](std::size_t) {
return true; }, velocity))
914 template <
class Accept>
916 Vec3D<T> &velocity)
const {
920 velocity = Vec3D<T>{
T(0),
T(0),
T(0)};
921 for (
const auto j : nearby)
922 for (
unsigned d = 0; d <
D; ++d)
923 velocity[d] +=
nodes[j].velocity[d] /
static_cast<T>(nearby.size());
928 return getField(index, Vec3D<T>{0., 0., 0.},
929 [](
const Node &n) {
return n.velocity; });
942 using IdxScalar = std::decay_t<decltype(std::declval<IndexType>()[0])>;
945 for (
unsigned d = 0; d <
D; ++d) {
947 const T cFloor = std::floor(c);
948 lo[d] =
static_cast<IdxScalar
>(cFloor);
949 frac[d] = c - cFloor;
951 Vec3D<T> result{
T(0),
T(0),
T(0)};
952 T totalWeight =
T(0);
953 for (
int corner = 0; corner < (1 <<
D); ++corner) {
956 for (
unsigned d = 0; d <
D; ++d) {
957 if ((corner >> d) & 1) {
969 for (
unsigned d = 0; d <
D; ++d)
970 result[d] += w *
nodes[j].velocity[d];
973 if (totalWeight >=
T(1e-14)) {
974 for (
unsigned d = 0; d <
D; ++d)
975 result[d] /= totalWeight;
979 for (
unsigned d = 0; d <
D; ++d)
980 index[d] = std::llround(coordinate[d] /
gridDelta);
982 index, [&](std::size_t j) {
return nodes[j].nitride == nitride; },
985 return nitride ? Vec3D<T>{
T(0),
T(0),
T(0)}
990 return getField(coordinate,
T(0), [](
const Node &n) {
return n.pressure; });
994 return getField(index,
T(0), [](
const Node &n) {
return n.pressure; });
998 return getField(coordinate,
T(0),
999 [](
const Node &n) {
return n.strainTrace; });
1003 return getField(index,
T(0), [](
const Node &n) {
return n.strainTrace; });
1007 return getField(coordinate, std::array<T, 9>{},
1008 [](
const Node &n) {
return n.strainRateTensor; });
1012 return getField(index, std::array<T, 9>{},
1013 [](
const Node &n) {
return n.strainRateTensor; });
1017 return getField(coordinate, std::array<T, 9>{},
1018 [](
const Node &n) {
return n.stressTensor; });
1022 return getField(index, std::array<T, 9>{},
1023 [](
const Node &n) {
return n.stressTensor; });
1027 return getField(coordinate,
T(0),
1028 [](
const Node &n) {
return n.vonMisesStress; });
1032 return getField(index,
T(0),
1033 [](
const Node &n) {
return n.vonMisesStress; });
1047 return std::isfinite(residual) &&
1048 residual <= deformationParameters.mechanicsTolerance &&
1049 std::isfinite(lastStokesResidual_) &&
1053 for (
const auto &node :
nodes) {
1054 if (!isFiniteVec(node.velocity) || !std::isfinite(node.pressure) ||
1055 !std::isfinite(node.strainTrace) ||
1056 !isFiniteTensor(node.strainRateTensor) ||
1057 !isFiniteTensor(node.stressTensor) ||
1058 !std::isfinite(node.vonMisesStress))
1065 if (!avgExpansionSpeedComputed) {
1067 avgExpansionSpeedComputed =
true;
1069 return avgExpansionSpeed_;
1073 for (
const auto &node :
nodes)
1074 callback(node.index, node.pressure, node.stressTensor);
1082 if (
nodes.empty() || ambientInterface ==
nullptr)
1086 using VD =
typename PointData<T>::VectorDataType;
1087 VD velocity, stressR0, stressR1, stressR2;
1092 if (!oxideStressAdvectedValid_)
1095 ConstSparseIterator it(ambientInterface->getDomain());
1096 for (; !it.isFinished(); ++it) {
1097 if (!it.isDefined())
1099 const IndexType idx = it.getStartIndices();
1102 velocity.push_back((nId !=
noNode && isFiniteVec(
nodes[nId].velocity))
1103 ?
nodes[nId].velocity
1104 : Vec3D<T>{
T(0),
T(0),
T(0)});
1105 std::array<T, 9> stress{};
1107 nId < oxideStressAdvected_.size())
1108 stress = oxideStressAdvected_[nId];
1113 if (!isFiniteTensor(stress))
1115 stressR0.push_back({stress[0], stress[1], stress[2]});
1116 stressR1.push_back({stress[3], stress[4], stress[5]});
1117 stressR2.push_back({stress[6], stress[7], stress[8]});
1120 auto &pd = ambientInterface->getPointData();
1121 pd.insertReplaceVectorData(std::move(velocity),
"OxVelocity");
1122 pd.insertReplaceVectorData(std::move(stressR0),
"OxStressR0");
1123 pd.insertReplaceVectorData(std::move(stressR1),
"OxStressR1");
1124 pd.insertReplaceVectorData(std::move(stressR2),
"OxStressR2");
1126 typename PointData<T>::ScalarDataType factor;
1127 for (ConstSparseIterator vit(ambientInterface->getDomain());
1128 !vit.isFinished(); ++vit) {
1129 if (!vit.isDefined())
1131 const std::size_t nId =
lookupNode(vit.getStartIndices());
1132 factor.push_back((nId !=
noNode && !
nodes[nId].nitride)
1134 T(deformationParameters.viscosity)
1137 pd.insertReplaceScalarData(std::move(factor),
"OxViscosity");
1140 typename PointData<T>::ScalarDataType pressure;
1141 for (ConstSparseIterator pit(ambientInterface->getDomain());
1142 !pit.isFinished(); ++pit) {
1143 if (!pit.isDefined())
1145 const std::size_t nId =
lookupNode(pit.getStartIndices());
1146 pressure.push_back((nId !=
noNode && !
nodes[nId].nitride &&
1147 std::isfinite(
nodes[nId].pressure))
1148 ?
nodes[nId].pressure
1151 pd.insertReplaceScalarData(std::move(pressure),
"OxMechanicsPressure");
1162 void seedFromLevelSet() {
1163 if (ambientInterface ==
nullptr ||
nodes.empty())
1167 auto &pd = ambientInterface->getPointData();
1168 const int r0Idx = pd.getVectorDataIndex(
"OxStressR0");
1169 const int r1Idx = pd.getVectorDataIndex(
"OxStressR1");
1170 const int r2Idx = pd.getVectorDataIndex(
"OxStressR2");
1171 if (r0Idx == -1 || r1Idx == -1 || r2Idx == -1)
1173 const auto *r0d = pd.getVectorData(r0Idx);
1174 const auto *r1d = pd.getVectorData(r1Idx);
1175 const auto *r2d = pd.getVectorData(r2Idx);
1178 for (; !it.isFinished(); ++it) {
1179 if (!it.isDefined())
1181 const auto ptId = it.getPointId();
1182 const IndexType idx = it.getStartIndices();
1183 if (ptId <
static_cast<decltype(ptId)
>(r0d->size()) &&
1184 ptId <
static_cast<decltype(ptId)
>(r1d->size()) &&
1185 ptId <
static_cast<decltype(ptId)
>(r2d->size())) {
1186 const auto &row0 = (*r0d)[ptId];
1187 const auto &row1 = (*r1d)[ptId];
1188 const auto &row2 = (*r2d)[ptId];
1189 std::array<T, 9> s{row0[0], row0[1], row0[2], row1[0], row1[1],
1190 row1[2], row2[0], row2[1], row2[2]};
1191 if (isFiniteTensor(s))
1192 deviatoricStressHistory[idx] = s;
1200 reactionInterface, ambientInterface, maskInterface, useRequestedBounds,
1201 requestedMinIndex, requestedMaxIndex,
1202 deformationParameters.maxGridPoints,
"OxidationDeformation");
1210 ConstSparseIterator reactionIt(reactionInterface->getDomain());
1211 ConstSparseIterator ambientIt(ambientInterface->getDomain());
1217 const T reactionPhi =
valueAt(reactionIt, index);
1218 const T ambientPhi =
valueAt(ambientIt, index);
1221 const std::size_t
id =
nodes.size();
1223 nodes.push_back({index});
1224 }
else if (mono && inMask &&
1225 static_cast<T>(reactionSign) * reactionPhi >=
T(-1e-9)) {
1227 const std::size_t
id =
nodes.size();
1229 nodes.push_back({index});
1230 nodes.back().nitride =
true;
1238 const std::size_t n =
nodes.size();
1239 faceBCTypes_.assign(2 *
D * n, Boundary::NONE);
1240 faceBCDists_.assign(2 *
D * n,
T(1));
1241 for (std::size_t
id = 0;
id < n; ++id) {
1242 const auto &node =
nodes[id];
1243 for (
unsigned dir = 0; dir <
D; ++dir) {
1244 for (
int off : {-1, 1}) {
1245 const unsigned fi = dir * 2u + (off == 1 ? 1u : 0u);
1246 IndexType nb = node.index;
1252 BoundaryIntersection bi;
1260 faceBCTypes_[fi * n + id] = bi.boundary;
1261 faceBCDists_[fi * n + id] = bi.distance;
1266 nitrideStressEnd_.assign(n, std::array<T, 9>{});
1267 nitrideStressAdvectedValid_ =
false;
1268 oxideStressEnd_.assign(n, std::array<T, 9>{});
1269 oxideStressAdvectedValid_ =
false;
1312 std::vector<std::pair<std::int64_t, T>>
t;
1315 for (
const auto &kv : o.
t)
1316 t.emplace_back(kv.first, s * kv.second);
1322 r.
t.reserve(
t.size());
1323 for (
const auto &kv :
t)
1324 r.
t.emplace_back(kv.first, s * kv.second);
1330 static constexpr unsigned macPairs =
static_cast<unsigned>(
D * (
D - 1) / 2);
1334 return (
D == 2) ? 0u : (c == 0 ? (e == 1 ? 0u : 1u) : 2u);
1337 if (
D == 2 || pair == 0) {
1340 }
else if (pair == 1) {
1355 std::array<std::vector<std::int64_t>,
D>
1359 std::unordered_map<std::size_t, T>
1366 std::vector<std::pair<unsigned, IndexType>>
1393 for (
unsigned d = 0; d <
D; ++d)
1402 return it ==
macPhiR_.end() ?
T(1) : it->second;
1408 std::array<T, D> base{}, frac{};
1409 for (
unsigned d = 0; d <
D; ++d) {
1410 base[d] = std::floor(x[d]);
1411 frac[d] = x[d] - base[d];
1414 for (
unsigned corner = 0; corner < (1u <<
D); ++corner) {
1417 for (
unsigned d = 0; d <
D; ++d) {
1418 const bool up = (corner >> d) & 1u;
1419 idx[d] =
static_cast<viennahrle::IndexType
>(base[d]) + (up ? 1 : 0);
1420 w *= up ? frac[d] : (
T(1) - frac[d]);
1423 value += w * std::clamp(
macPhiR(idx),
T(-1),
T(1));
1443 std::int64_t
macFaceId(
unsigned d,
const IndexType &a)
const {
1444 const std::size_t ja =
macNode(a);
1449 const std::size_t jb =
macNode(b);
1459 for (
unsigned e = 0; e <
D; ++e) {
1498 bool nitride =
false;
1499 for (
const auto &q : {a, b}) {
1500 const std::size_t j =
macNode(q);
1503 if (
nodes[j].nitride) {
1508 if (v[0] !=
T(0) || v[1] !=
T(0) || (
D == 3 && v[2] !=
T(0))) {
1513 if (boundaryCacheValid_ && !(nitride && count == 0)) {
1514 Vec3D<T> p{
T(0),
T(0),
T(0)};
1515 for (
unsigned e = 0; e <
D; ++e)
1516 p[e] = (
static_cast<T>(a[e]) + (e == d ?
T(0.5) :
T(0)) +
1517 theta *
static_cast<T>(ghost[e] - a[e])) *
1519 const auto front = diffusionField->frontVelocityAt(p);
1521 return static_cast<T>(reactionSign) *
1522 (oxidationParameters.expansionCoefficient -
T(1)) * front.speed *
1526 return sum /
static_cast<T>(count);
1529 for (
const auto &q : {a, b}) {
1530 const std::size_t j =
macNode(q);
1541 const IndexType unreflected = a;
1544 const std::int64_t
id =
macFaceId(d, a);
1546 r.
t.emplace_back(
id, sign);
1551 if (partner ==
nullptr)
1553 IndexType pa = *partner;
1556 for (
unsigned e = 0; e <
D; ++e)
1557 inBox = inBox && a[e] == unreflected[e] && pa[e] == (*partner)[e];
1558 const std::int64_t pid =
macFaceId(d, pa);
1561 pe.
t.emplace_back(pid, psign);
1566 if (phiP >
T(0) && phiP - phiF >
T(0))
1567 theta = phiP / (phiP - phiF);
1568 const T thMin = std::clamp(
1569 T(deformationParameters.minMechanicsBoundaryDistance),
T(0),
T(1));
1570 theta = std::clamp(theta, thMin,
T(1));
1583 T theta, std::int64_t pid)
const {
1586 int axis = -1, step = 0;
1587 for (
unsigned e = 0; e <
D; ++e) {
1588 const auto delta = a[e] - pa[e];
1591 if (axis != -1 || (delta != 1 && delta != -1)) {
1595 axis =
static_cast<int>(e);
1596 step =
static_cast<int>(delta);
1599 slot =
static_cast<std::size_t
>(pid) * 2 *
D +
1600 2 *
static_cast<std::size_t
>(axis) + (step > 0 ? 1 : 0);
1605 if (state.load(std::memory_order_acquire) == 2)
1608 std::uint8_t empty = 0;
1609 if (state.compare_exchange_strong(empty, 1, std::memory_order_acq_rel)) {
1611 state.store(2, std::memory_order_release);
1619 const std::size_t slots =
static_cast<std::size_t
>(
macNVel_) * 2 *
D;
1622 macGhostState_ = std::make_unique<std::atomic<std::uint8_t>[]>(slots);
1625 for (std::size_t k = 0; k < slots; ++k)
1634 const IndexType hi = q;
1642 IndexType ae = a, ac = a;
1655 return it == m.end() ? -1 : it->second;
1664 for (
unsigned k : {c, e}) {
1678 T macPsi(
bool nitride,
const IndexType &a)
const {
1681 return it == m.end() ? std::numeric_limits<T>::quiet_NaN() : it->second;
1687 const T c =
macPsi(nitride, x);
1690 if (std::isfinite(c))
1691 for (
int sg : {-1, 1}) {
1694 const T v =
macPsi(nitride, y);
1695 if (std::isfinite(v)) {
1696 sum += (v - c) *
static_cast<T>(sg);
1700 return cnt ? sum /
static_cast<T>(cnt)
1701 : std::numeric_limits<T>::quiet_NaN();
1706 for (
unsigned e = 0; e <
D; ++e)
1707 norm += grad[e] * grad[e];
1708 std::array<T, 3> n{
T(0),
T(0),
T(0)};
1709 if (norm <=
T(1e-12))
1711 norm = std::sqrt(norm);
1712 for (
unsigned e = 0; e <
D; ++e)
1713 n[e] = -grad[e] / norm;
1721 const IndexType &q)
const {
1723 for (
unsigned e = 0; e <
D; ++e)
1726 const T step =
static_cast<T>(q[dq] - s[dq]);
1727 std::array<T, 3> g{
T(0),
T(0),
T(0)};
1728 g[dq] = (
macPsi(nitride, q) -
macPsi(nitride, s)) * step;
1729 if (!std::isfinite(g[dq])) {
1735 for (
unsigned e = 0; e <
D; ++e) {
1740 for (
const auto &x : {s, q}) {
1742 if (std::isfinite(slope)) {
1747 g[e] = cnt ? sum /
static_cast<T>(cnt) :
T(0);
1750 if (n[0] ==
T(0) && n[1] ==
T(0) && n[2] ==
T(0))
1757 const IndexType &a)
const {
1759 bool defined =
true;
1760 auto edges = [&](
unsigned along,
unsigned across) {
1763 for (
int b = 0; b <= 1; ++b) {
1769 if (std::isfinite(diff)) {
1774 defined = defined && cnt > 0;
1775 return cnt ? sum /
static_cast<T>(cnt) :
T(0);
1777 std::array<T, 3> g{
T(0),
T(0),
T(0)};
1781 return {
T(0),
T(0),
T(0)};
1782 if constexpr (
D == 3) {
1783 const unsigned k = 3 - c - e;
1786 for (
int bc = 0; bc <= 1; ++bc)
1787 for (
int be = 0; be <= 1; ++be) {
1792 if (std::isfinite(slope)) {
1797 g[k] = cnt ? sum /
static_cast<T>(cnt) :
T(0);
1805 const std::array<T, 3> &n,
unsigned a,
1807 auto P = [&](
unsigned i,
unsigned j) {
1808 return (i == j ?
T(1) :
T(0)) - n[i] * n[j];
1811 for (
unsigned c = 0; c <
D; ++c)
1812 for (
unsigned e = 0; e <
D; ++e) {
1813 const T w = P(a, c) * P(e, b);
1824 std::array<MacLin, 9> S{};
1825 for (
unsigned c = 0; c <
D; ++c)
1827 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
1832 for (
int dc = -1; dc <= 0; ++dc)
1833 for (
int de = -1; de <= 0; ++de) {
1846 sum.
scaled(
T(1) /
static_cast<T>(cnt));
1860 const std::size_t kk =
static_cast<std::size_t
>(k);
1867 if (n[0] ==
T(0) && n[1] ==
T(0) && n[2] ==
T(0))
1871 for (
int bc = 0; bc <= 1; ++bc)
1872 for (
int be = 0; be <= 1; ++be) {
1883 r.
add(proj,
T(1) /
static_cast<T>(cnt));
1891 const std::uint8_t kq =
macKind(q);
1892 const std::size_t js =
macNode(s);
1893 auto oxideLike = [&](std::size_t jp,
const IndexType &at) {
1902 auto nitrideLike = [&](std::size_t jp,
const IndexType &at) {
1904 T twoEta, volumetric;
1907 for (
unsigned c = 0; c <
D; ++c)
1915 return kq ==
macOxide ? oxideLike(jq, q) : nitrideLike(jq, q);
1923 for (
unsigned e = 0; e <
D; ++e)
1925 fi = e * 2u + (q[e] > s[e] ? 1u : 0u);
1926 const std::size_t n =
nodes.size();
1927 T theta = faceBCDists_[fi * n + js] /
gridDelta;
1928 if (faceBCTypes_[fi * n + js] != Boundary::AMBIENT)
1930 theta = std::clamp(theta,
T(1e-3),
T(1));
1938 return nodes[js].nitride ? nitrideLike(js, q) : oxideLike(js, q);
1944 if (
nodes[j].nitride) {
1949 const T mean = (start[0] + start[4] + start[8]) /
T(3);
1950 return decay * start[
tensorIndex(d, d)] + (
T(1) - decay) * mean;
1957 const std::size_t n =
nodes.size();
1958 for (
unsigned d = 0; d <
D; ++d) {
1967 ConstSparseIterator reactionIt(reactionInterface->getDomain());
1968 ConstSparseIterator ambientIt(ambientInterface->getDomain());
1970 auto put = [&](
const IndexType &q) {
1977 auto defined = [&](ConstSparseIterator &it) {
1979 return it.isDefined() ? it.getValue()
1980 : std::numeric_limits<T>::quiet_NaN();
1982 macPsiA_[li] =
static_cast<T>(ambientSign) * defined(ambientIt);
1983 if (maskInterface !=
nullptr)
1984 macPsiM_[li] =
static_cast<T>(maskSign) * defined(maskIt);
1986 for (std::size_t i = 0; i < n; ++i) {
1988 for (
unsigned d = 0; d <
D; ++d)
1990 for (
unsigned code = 0; code < span; ++code) {
1991 IndexType q =
nodes[i].index;
1992 unsigned rest = code;
1993 for (
unsigned d = 0; d <
D; ++d) {
1994 q[d] +=
static_cast<int>(rest % 5) - 2;
2002 std::int64_t count = 0;
2003 for (
unsigned d = 0; d <
D; ++d) {
2004 for (std::size_t i = 0; i < n; ++i) {
2005 for (
int side = 0; side < 2; ++side) {
2006 IndexType a =
nodes[i].index;
2017 const std::size_t other =
macNode(side == 0 ? a : b);
2018 std::int64_t existing = -1;
2022 if (existing >= 0) {
2040 bool gasContact =
false;
2041 bool hasNitride =
false;
2042 for (std::size_t i = 0; i < n; ++i) {
2043 if (
nodes[i].nitride) {
2048 for (
unsigned fi = 0; fi < 2 *
D; ++fi)
2050 gasContact || faceBCTypes_[fi * n + i] == Boundary::AMBIENT;
2057 for (std::size_t i = 0; i < n; ++i)
2058 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
2061 for (
int dc = -1; dc <= 0; ++dc)
2062 for (
int de = -1; de <= 0; ++de) {
2063 IndexType lo =
nodes[i].index;
2073 m[li] =
static_cast<std::int64_t
>(
macCorners_.size());
2086 for (std::size_t k = 0; k < nk; ++k) {
2090 int oxide = 0, nitride = 0, gas = 0;
2091 for (
int bc = 0; bc <= 1; ++bc)
2092 for (
int be = 0; be <= 1; ++be) {
2104 macX_.assign(
static_cast<std::size_t
>(
macN_),
T(0));
2115 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
2127 for (
int bc = 0; bc <= 1; ++bc)
2128 for (
int be = 0; be <= 1; ++be) {
2132 const std::size_t j =
macNode(q);
2145 if (tau0 <= std::numeric_limits<T>::epsilon())
2147 const T tau = tau0 *
macCornerEta_[k] /
T(deformationParameters.viscosity);
2148 return std::exp(-
T(deformationParameters.stressTimeStep) / tau);
2153#pragma omp parallel for schedule(static)
2154 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
2160 std::numeric_limits<T>::min() *
T(1e10));
2164 for (
int bc = 0; bc <= 1; ++bc)
2165 for (
int be = 0; be <= 1; ++be) {
2171 inv +=
T(1) / etaImpl;
2174 inv +=
T(1) / etaImplN;
2193 const std::size_t n =
nodes.size();
2194 const std::int64_t N =
macN_;
2195 std::vector<MacLin> rows(
static_cast<std::size_t
>(N));
2197#pragma omp parallel for schedule(dynamic, 64)
2198 for (std::size_t i = 0; i < n; ++i) {
2199 for (
unsigned d = 0; d <
D; ++d)
2200 for (
int side = 0; side < 2; ++side) {
2201 const std::int64_t
id =
2205 IndexType a =
nodes[i].index;
2209 const std::size_t ja =
macNode(a);
2210 if (side == 0 && ja !=
noNode)
2216 for (
unsigned e = 0; e <
D; ++e) {
2228#pragma omp parallel for schedule(static)
2229 for (std::size_t i = 0; i < n; ++i) {
2230 const std::int64_t pid =
macPid_[i];
2234 for (
unsigned d = 0; d <
D; ++d)
2236 rows[
static_cast<std::size_t
>(pid)] = std::move(r);
2239 for (std::size_t i = 0; i < n; ++i)
2243 g.
c = -
T(deformationParameters.ambientPressure) / etaRef;
2244 rows[
static_cast<std::size_t
>(
macPid_[i])] = g;
2248 if constexpr (
D == 3)
2262 typedef amgcl::make_solver<
2264 amgcl::relaxation::ilu0>,
2265 amgcl::solver::preonly<MacAmgPrecondBackend>>
2267 typedef amgcl::make_solver<
2269 amgcl::relaxation::spai0>,
2270 amgcl::solver::preonly<MacAmgPrecondBackend>>
2272 typedef amgcl::preconditioner::schur_pressure_correction<
MacAmgUSolver,
2308 const std::size_t N = rows.size();
2314 const auto byColumn = [](
const auto &a,
const auto &b) {
2315 return a.first < b.first;
2317#pragma omp parallel for schedule(dynamic, 256)
2318 for (std::size_t i = 0; i < N; ++i) {
2319 auto &t = rows[i].t;
2320 std::sort(t.begin(), t.end(), byColumn);
2321 ptrdiff_t count = 0;
2322 for (std::size_t q = 0; q < t.size();) {
2323 const std::int64_t c = t[q].first;
2325 for (; q < t.size() && t[q].first == c; ++q)
2326 v +=
static_cast<double>(t[q].second);
2331 rhs[i] = -
static_cast<double>(rows[i].c);
2333 for (std::size_t i = 0; i < N; ++i)
2334 ptr[i + 1] += ptr[i];
2335 col.resize(
static_cast<std::size_t
>(ptr[N]));
2336 val.resize(
static_cast<std::size_t
>(ptr[N]));
2337#pragma omp parallel for schedule(dynamic, 256)
2338 for (std::size_t i = 0; i < N; ++i) {
2339 auto &t = rows[i].t;
2340 auto k =
static_cast<std::size_t
>(ptr[i]);
2341 for (std::size_t q = 0; q < t.size();) {
2342 const std::int64_t c = t[q].first;
2344 for (; q < t.size() && t[q].first == c; ++q)
2345 v +=
static_cast<double>(t[q].second);
2347 col[k] =
static_cast<ptrdiff_t
>(c);
2351 std::vector<std::pair<std::int64_t, T>>().swap(t);
2373 const std::vector<ptrdiff_t> &col,
2374 const std::vector<double> &val,
2375 const std::vector<double> &rhs,
bool fresh,
2377 const std::size_t N = rhs.size();
2383 for (std::size_t i = 0; i < N; ++i)
2384 for (ptrdiff_t q = ptr[i]; q < ptr[i + 1]; ++q)
2385 rs[i] = std::max(rs[i], std::abs(val[q]));
2387 v = v > 0. ? 1. / v : 1.;
2388 for (std::size_t i = 0; i < N; ++i)
2389 for (ptrdiff_t q = ptr[i]; q < ptr[i + 1]; ++q)
2390 cs[col[q]] = std::max(cs[col[q]], std::abs(rs[i] * val[q]));
2392 v = v > 0. ? 1. / v : 1.;
2396 sval.resize(val.size());
2399#pragma omp parallel for schedule(static)
2400 for (std::size_t i = 0; i < N; ++i) {
2401 for (ptrdiff_t q = ptr[i]; q < ptr[i + 1]; ++q)
2402 sval[q] = rs[i] * val[q] * cs[col[q]];
2403 srhs[i] = rs[i] * rhs[i];
2404 y[i] = i <
macX_.size() && std::isfinite(
macX_[i])
2405 ?
static_cast<double>(
macX_[i]) / cs[i]
2408 const ptrdiff_t n =
static_cast<ptrdiff_t
>(N);
2409 auto A = std::tie(n, ptr, col, sval);
2410 std::size_t iters = 0;
2412 const unsigned maxIterations =
2414 const double target =
2421#ifdef VIENNALS_GPU_BICGSTAB
2423 static_assert(
sizeof(ptrdiff_t) ==
sizeof(std::int64_t));
2424 unsigned gpuIters = 0;
2425 if (!gpu::gpuMacSolve(
2426 macGpu_, n,
reinterpret_cast<const std::int64_t *
>(ptr.data()),
2427 reinterpret_cast<const std::int64_t *
>(col.data()), sval.data(),
2428 srhs.data(), y.data(), maxIterations, target, gpuIters, err)) {
2430 VIENNACORE_LOG_ERROR(
2432 "OxidationDeformation: the GPU mechanics solve failed: ") +
2433 gpu::gpuGetLastErrorMessage());
2441 macAmgS_->prm.maxiter = maxIterations;
2443 std::tie(iters, err) = (*macAmgS_)(A, *
macAmgP_, srhs, y);
2444 }
catch (
const std::exception &e) {
2446 VIENNACORE_LOG_WARNING(
2447 std::string(
"OxidationDeformation: AMGCL failed: ") + e.what());
2454 std::vector<double> x(N);
2455 for (std::size_t i = 0; i < N; ++i)
2456 x[i] = cs[i] * y[i];
2457 for (std::size_t i = 0; i < N; ++i)
2458 if (!std::isfinite(x[i]))
2462 backward =
static_cast<T>(err);
2463 if (!(err < 1e-6)) {
2465 VIENNACORE_LOG_WARNING(
2466 "OxidationDeformation: AMGCL did not converge (iters=" +
2467 std::to_string(iters) +
", residual=" + std::to_string(err) +
")");
2471 for (std::size_t i = 0; i < N; ++i)
2472 macX_[i] =
static_cast<T>(x[i]);
2480#ifdef VIENNALS_GPU_BICGSTAB
2489 const std::vector<ptrdiff_t> &col,
2490 const std::vector<double> &sval,
2491 const std::vector<double> &cs) {
2492 const std::size_t N = cs.size();
2497 std::vector<double> B(
static_cast<std::size_t
>(
macNVel_) *
D, 0.);
2498 for (std::size_t i = 0; i <
nodes.size(); ++i)
2499 for (
unsigned d = 0; d <
D; ++d)
2502 B[
static_cast<std::size_t
>(id) *
D + d] = 1. / cs[
id];
2503#ifdef VIENNALS_GPU_BICGSTAB
2506 static_cast<std::int64_t
>(N),
2507 reinterpret_cast<const std::int64_t *
>(ptr.data()),
2508 reinterpret_cast<const std::int64_t *
>(col.data()), sval.data(),
2509 static_cast<std::int64_t
>(
macNVel_),
static_cast<int>(
D), B.data());
2512 const std::string message =
2514 "OxidationDeformation: GPU mode was selected, but the GPU "
2515 "mechanics setup failed: ") +
2516 gpu::gpuGetLastErrorMessage();
2518 VIENNACORE_LOG_ERROR(message);
2521 VIENNACORE_LOG_WARNING(message +
" Falling back to the CPU solver.");
2526 typename MacAmgPrecond::params prm;
2527 prm.pmask.assign(N, 0);
2528 for (std::size_t i =
static_cast<std::size_t
>(
macNVel_); i < N; ++i)
2531 prm.approx_schur =
true;
2533 auto &nullspace = prm.usolver.precond.coarsening.nullspace;
2534 nullspace.cols =
static_cast<int>(
D);
2535 nullspace.B = std::move(B);
2536 const ptrdiff_t n =
static_cast<ptrdiff_t
>(N);
2538 std::make_unique<MacAmgPrecond>(std::tie(n, ptr, col, sval), prm);
2539 typename MacAmgKrylov::params sprm;
2541 macAmgS_ = std::make_unique<MacAmgKrylov>(N, sprm);
2542 }
catch (
const std::exception &e) {
2544 VIENNACORE_LOG_WARNING(
2545 std::string(
"OxidationDeformation: AMGCL failed: ") + e.what());
2552 Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>>>
2567 const Eigen::VectorXd &b,
T &backward) {
2568 const Eigen::Index N = A.rows();
2569 const auto precondition = [&](
const Eigen::VectorXd &v) {
2570 return Eigen::VectorXd(
2574 Eigen::VectorXd x = Eigen::VectorXd::Zero(N);
2575 if (
macX_.size() ==
static_cast<std::size_t
>(N))
2576 for (Eigen::Index k = 0; k < N; ++k)
2577 x[k] =
static_cast<double>(
macX_[
static_cast<std::size_t
>(k)]);
2578 const double bnorm = b.norm();
2581 constexpr int m = 30;
2582 constexpr double tol = 1e-10;
2583 Eigen::VectorXd r = b - A * x;
2584 const double beta = r.norm();
2585 if (beta > tol * bnorm) {
2586 std::vector<Eigen::VectorXd> V, Z;
2587 Eigen::MatrixXd H = Eigen::MatrixXd::Zero(m + 1, m);
2588 Eigen::VectorXd g = Eigen::VectorXd::Zero(m + 1);
2589 std::array<double, m> c{}, s{};
2590 V.push_back(r / beta);
2593 bool converged =
false;
2594 for (
int j = 0; j < m; ++j) {
2595 Z.push_back(precondition(V[j]));
2596 Eigen::VectorXd w = A * Z[j];
2597 for (
int i = 0; i <= j; ++i) {
2598 H(i, j) = w.dot(V[i]);
2599 w -= H(i, j) * V[i];
2601 const double hNext = w.norm();
2602 H(j + 1, j) = hNext;
2603 for (
int i = 0; i < j; ++i) {
2604 const double t = c[i] * H(i, j) + s[i] * H(i + 1, j);
2605 H(i + 1, j) = -s[i] * H(i, j) + c[i] * H(i + 1, j);
2608 const double den = std::hypot(H(j, j), H(j + 1, j));
2611 c[j] = H(j, j) / den;
2612 s[j] = H(j + 1, j) / den;
2615 g[j + 1] = -s[j] * g[j];
2618 if (std::abs(g[j + 1]) <= tol * bnorm || hNext == 0.) {
2622 V.push_back(w / hNext);
2626 const Eigen::VectorXd y =
2627 H.topLeftCorner(k, k).triangularView<Eigen::Upper>().solve(g.head(k));
2628 for (
int i = 0; i < k; ++i)
2631 const Eigen::VectorXd res = b - A * x;
2632 if (!x.allFinite() || !(res.norm() <= 10. * tol * bnorm))
2634 const Eigen::VectorXd terms = A.cwiseAbs() * x.cwiseAbs() + b.cwiseAbs();
2636 for (Eigen::Index k = 0; k < N; ++k)
2639 std::max(backward,
static_cast<T>(std::abs(res[k]) / terms[k]));
2642 macX_.assign(
static_cast<std::size_t
>(N),
T(0));
2643 for (Eigen::Index k = 0; k < N; ++k)
2644 macX_[
static_cast<std::size_t
>(k)] =
static_cast<T>(x[k]);
2650 const Eigen::Index N =
static_cast<Eigen::Index
>(rows.size());
2651 std::vector<Eigen::Triplet<double>> trip;
2652 std::size_t nnz = 0;
2653 for (
const auto &r : rows)
2656 Eigen::VectorXd rhs(N);
2657 for (Eigen::Index k = 0; k < N; ++k) {
2658 for (
const auto &kv : rows[
static_cast<std::size_t
>(k)].t)
2659 trip.emplace_back(
static_cast<int>(k),
static_cast<int>(kv.first),
2660 static_cast<double>(kv.second));
2661 rhs[k] = -
static_cast<double>(rows[
static_cast<std::size_t
>(k)].c);
2663 Eigen::SparseMatrix<double> A(N, N);
2664 A.setFromTriplets(trip.begin(), trip.end());
2666 Eigen::VectorXd rs = Eigen::VectorXd::Zero(N),
2667 cs = Eigen::VectorXd::Zero(N);
2668 for (Eigen::Index k = 0; k < A.outerSize(); ++k)
2669 for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
2670 rs[it.row()] = std::max(rs[it.row()], std::abs(it.value()));
2671 for (Eigen::Index k = 0; k < N; ++k)
2672 rs[k] = rs[k] > 0. ? 1. / rs[k] : 1.;
2673 for (Eigen::Index k = 0; k < A.outerSize(); ++k)
2674 for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
2676 std::max(cs[it.col()], std::abs(rs[it.row()] * it.value()));
2677 for (Eigen::Index k = 0; k < N; ++k)
2678 cs[k] = cs[k] > 0. ? 1. / cs[k] : 1.;
2679 Eigen::SparseMatrix<double> As = rs.asDiagonal() * A * cs.asDiagonal();
2680 As.makeCompressed();
2684 macLU_ = std::make_unique<Eigen::SparseLU<Eigen::SparseMatrix<double>,
2685 Eigen::COLAMDOrdering<int>>>();
2686 macLU_->analyzePattern(As);
2698 if (lu.info() != Eigen::Success) {
2699 VIENNACORE_LOG_WARNING(
2700 "OxidationDeformation: MAC factorisation failed: " +
2701 lu.lastErrorMessage());
2710 cs.asDiagonal() * Eigen::VectorXd(lu.solve(rs.asDiagonal() * rhs));
2711 for (
int pass = 0; pass < 2; ++pass) {
2712 const Eigen::VectorXd r = rhs - A * x;
2713 x += cs.asDiagonal() * Eigen::VectorXd(lu.solve(rs.asDiagonal() * r));
2715 const Eigen::VectorXd res = rhs - A * x;
2716 const Eigen::VectorXd terms = A.cwiseAbs() * x.cwiseAbs() + rhs.cwiseAbs();
2718 for (Eigen::Index k = 0; k < N; ++k)
2721 std::max(backward,
static_cast<T>(std::abs(res[k]) / terms[k]));
2724 macX_.assign(
static_cast<std::size_t
>(N),
T(0));
2725 for (Eigen::Index k = 0; k < N; ++k)
2726 macX_[
static_cast<std::size_t
>(k)] =
static_cast<T>(x[k]);
2732 for (
const auto &kv : e.
t)
2733 v += kv.second *
macX_[
static_cast<std::size_t
>(kv.first)];
2740 std::vector<T> &cornerRate)
const {
2741 const std::size_t n =
nodes.size();
2742 nodeRate.assign(n, std::array<T, 9>{});
2744#pragma omp parallel for schedule(static)
2745 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
2750#pragma omp parallel for schedule(static)
2751 for (std::size_t i = 0; i < n; ++i) {
2752 auto &R = nodeRate[i];
2753 for (
unsigned d = 0; d <
D; ++d)
2755 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
2760 for (
int dc = -1; dc <= 0; ++dc)
2761 for (
int de = -1; de <= 0; ++de) {
2762 IndexType lo =
nodes[i].index;
2769 sum += sign * cornerRate[
static_cast<std::size_t
>(k)];
2772 const T g = cnt ?
T(0.5) * sum /
static_cast<T>(cnt) :
T(0);
2787 constexpr int m = 5;
2788 const std::size_t L = u.size();
2791 if (
static_cast<int>(
macAAu_.size()) > m + 1) {
2795 const std::size_t h =
macAAu_.size();
2797 auto fnorm = [&](std::size_t j) {
2799 for (std::size_t k = 0; k < L; ++k) {
2803 return std::sqrt(sq);
2805 if (h >= 2 && fnorm(h - 1) >
T(2) * fnorm(h - 2)) {
2809 const std::size_t hh =
macAAu_.size();
2811 for (std::size_t k = 0; k < L; ++k)
2812 u[k] =
T(0.5) * (u[k] + g[k]);
2816 const std::size_t q = hh - 1;
2817 std::vector<std::vector<T>> dF(q, std::vector<T>(L));
2818 std::vector<T> fl(L);
2819 for (std::size_t k = 0; k < L; ++k)
2821 for (std::size_t j = 0; j < q; ++j)
2822 for (std::size_t k = 0; k < L; ++k)
2824 std::vector<T> M(q * q,
T(0)), rhs(q,
T(0)), gamma(q,
T(0));
2825 for (std::size_t a = 0; a < q; ++a) {
2826 for (std::size_t b = 0; b < q; ++b) {
2828 for (std::size_t k = 0; k < L; ++k)
2829 sum += dF[a][k] * dF[b][k];
2833 for (std::size_t k = 0; k < L; ++k)
2834 sum += dF[a][k] * fl[k];
2836 M[a * q + a] *=
T(1) +
T(1e-10);
2839 for (std::size_t c = 0; c < q; ++c) {
2840 std::size_t piv = c;
2841 for (std::size_t r = c + 1; r < q; ++r)
2842 if (std::abs(M[r * q + c]) > std::abs(M[piv * q + c]))
2844 if (std::abs(M[piv * q + c]) <
T(1e-300))
2847 for (std::size_t k = 0; k < q; ++k)
2848 std::swap(M[c * q + k], M[piv * q + k]);
2849 std::swap(rhs[c], rhs[piv]);
2851 for (std::size_t r = c + 1; r < q; ++r) {
2852 const T f = M[r * q + c] / M[c * q + c];
2853 for (std::size_t k = c; k < q; ++k)
2854 M[r * q + k] -= f * M[c * q + k];
2855 rhs[r] -= f * rhs[c];
2858 for (std::size_t c = q; c-- > 0;) {
2860 for (std::size_t k = c + 1; k < q; ++k)
2861 sum -= M[c * q + k] * gamma[k];
2862 gamma[c] = std::abs(M[c * q + c]) >
T(1e-300) ? sum / M[c * q + c] :
T(0);
2865 for (std::size_t k = 0; k < L; ++k) {
2867 for (std::size_t j = 0; j < q; ++j)
2874 const std::vector<T> &cornerRate) {
2877 if (!oxide && !nitride)
2879 const std::size_t n =
nodes.size();
2880 std::vector<T> locNode(n,
T(0)), locCorner(
macCorners_.size(),
T(0)),
2883#pragma omp parallel for schedule(dynamic, 64)
2884 for (std::size_t i = 0; i < n; ++i) {
2885 if (
nodes[i].nitride ? !nitride : !oxide)
2887 std::array<T, 9> start =
2889 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
2894 for (
int dc = -1; dc <= 0; ++dc)
2895 for (
int de = -1; de <= 0; ++de) {
2896 IndexType lo =
nodes[i].index;
2907 cnt ? sum /
static_cast<T>(cnt) :
T(0);
2909 const auto &R = nodeRate[i];
2911 for (
unsigned d = 0; d <
D; ++d)
2913 std::array<T, 9> dev = R;
2914 for (
unsigned d = 0; d < 3; ++d)
2921 auto cornerViscosity = [&](std::size_t k,
bool ofNitride) {
2925 std::array<T, 9> start{}, rate{};
2927 for (
int bc = 0; bc <= 1; ++bc)
2928 for (
int be = 0; be <= 1; ++be) {
2932 const std::size_t j =
macNode(q);
2937 for (
unsigned d = 0; d < 3; ++d) {
2945 for (
unsigned d = 0; d < 3; ++d) {
2951 T(0.5) * cornerRate[k];
2953 for (
unsigned d = 0; d <
D; ++d)
2955 for (
unsigned d = 0; d < 3; ++d)
2960#pragma omp parallel for schedule(dynamic, 64)
2961 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
2965 locCorner[k] = cornerViscosity(k,
false);
2967 locCornerN[k] = cornerViscosity(k,
true);
2970 std::vector<T> u, g;
2971 for (std::size_t i = 0; i < n; ++i)
2972 if (locNode[i] >
T(0)) {
2973 u.push_back(std::log(
nodes[i].nitride ? nitrideEta_[i] : oxideEta_[i]));
2974 g.push_back(std::log(locNode[i]));
2976 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
2977 if (locCorner[k] >
T(0)) {
2979 g.push_back(std::log(locCorner[k]));
2981 if (locCornerN[k] >
T(0)) {
2983 g.push_back(std::log(locCornerN[k]));
2987 const T hi = std::log(
T(deformationParameters.viscosity));
2988 const T lo = hi + std::log(
T(1e-6));
2989 const T hiN = std::log(std::max(
T(deformationParameters.nitrideViscosity),
2990 std::numeric_limits<T>::min()));
2991 const T loN = hiN + std::log(
T(1e-6));
2993 for (std::size_t i = 0; i < n; ++i)
2994 if (locNode[i] >
T(0)) {
2995 if (
nodes[i].nitride)
2996 nitrideEta_[i] = std::exp(std::clamp(u[q++], loN, hiN));
2998 oxideEta_[i] = std::exp(std::clamp(u[q++], lo, hi));
3000 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
3001 if (locCorner[k] >
T(0))
3003 if (locCornerN[k] >
T(0))
3013 const T eta0 =
T(deformationParameters.viscosity);
3014 const T etaN0 =
T(deformationParameters.nitrideViscosity);
3015 const T oxideVolume =
T(deformationParameters.shearActivationVolume);
3016 const T nitrideVolume =
3017 T(deformationParameters.nitrideShearActivationVolume);
3018 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
3021 std::array<T, 9> s{};
3037 const std::vector<T> &cornerRate,
T etaRef) {
3038 const std::size_t n =
nodes.size();
3039 if (oxideStressEnd_.size() != n)
3040 oxideStressEnd_.assign(n, std::array<T, 9>{});
3041 oxideStressAdvectedValid_ =
false;
3044#pragma omp parallel for schedule(dynamic, 64)
3045 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
3054#pragma omp parallel for schedule(dynamic, 64)
3055 for (std::size_t i = 0; i < n; ++i) {
3056 auto &node =
nodes[i];
3057 for (
unsigned d = 0; d <
D; ++d) {
3058 IndexType lo = node.index;
3060 const IndexType hi = node.index;
3064 node.pressure =
macPid_[i] >= 0
3067 node.strainRateTensor = nodeRate[i];
3069 for (
unsigned d = 0; d <
D; ++d)
3071 node.strainTrace = tr;
3073 std::array<T, 9> shear{};
3074 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
3079 for (
int dc = -1; dc <= 0; ++dc)
3080 for (
int de = -1; de <= 0; ++de) {
3081 IndexType lo = node.index;
3092 cnt ? sum /
static_cast<T>(cnt) :
T(0);
3096 auto &end = nitrideStressEnd_[i];
3097 for (
unsigned c = 0; c < 3; ++c)
3098 for (
unsigned e = 0; e < 3; ++e)
3101 node.stressTensor = end;
3106 const auto &start = startStress_[i];
3107 std::array<T, 9> end = shear;
3108 for (
unsigned d = 0; d < 3; ++d)
3111 (
T(1) - decay) *
T(2) * eta *
3113 oxideStressEnd_[i] = end;
3114 node.stressTensor = end;
3115 for (
unsigned d = 0; d < 3; ++d)
3116 node.stressTensor[
tensorIndex(d, d)] -= node.pressure;
3117 std::array<T, 9> dev = end;
3118 const T mean = (end[0] + end[4] + end[8]) /
T(3);
3119 for (
unsigned d = 0; d < 3; ++d)
3125 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
3141 ~GhostOff() { on =
false; }
3143 nitrideStartNode_.clear();
3145 nitrideStartNode_.assign(
nodes.size(), std::array<T, 9>{});
3146 for (std::size_t i = 0; i <
nodes.size(); ++i)
3147 if (
nodes[i].nitride)
3150 if (nitrideStressEnd_.size() !=
nodes.size())
3151 nitrideStressEnd_.assign(
nodes.size(), std::array<T, 9>{});
3157 const bool nonlinear =
3160 oxideEta_.size() ==
nodes.size() &&
3161 nitrideEta_.size() ==
nodes.size() &&
3168 if (oxideEta_.size() !=
nodes.size())
3169 oxideEta_.assign(
nodes.size(),
T(deformationParameters.viscosity));
3170 if (nitrideEta_.size() !=
nodes.size())
3171 nitrideEta_.assign(
nodes.size(),
3172 T(deformationParameters.nitrideViscosity));
3174 std::vector<std::array<T, 9>> nodeRate;
3175 std::vector<T> cornerRate;
3176 std::vector<T> previous;
3177 T change = std::numeric_limits<T>::infinity();
3180 const unsigned maxIt =
3181 std::max(1u, deformationParameters.mechanicsIterations);
3184 for (it = 1; it <= maxIt; ++it) {
3193 T dmax =
T(0), vmax =
T(0);
3194 for (std::int64_t k = 0; k <
macNVel_; ++k) {
3195 const std::size_t kk =
static_cast<std::size_t
>(k);
3196 vmax = std::max(vmax, std::abs(
macX_[kk]));
3197 if (kk < previous.size())
3198 dmax = std::max(dmax, std::abs(
macX_[kk] - previous[kk]));
3200 change = vmax >
T(0) ? dmax / vmax : dmax;
3204 (it > 1 && change <
T(deformationParameters.mechanicsTolerance)))
3209 residual = std::numeric_limits<T>::infinity();
3210 lastStokesResidual_ = std::numeric_limits<T>::infinity();
3215 residual = nonlinear ? change :
T(0);
3220 lastStokesResidual_ = backward;
3222 T sumDiv =
T(0), cellVolume =
T(1);
3223 for (
unsigned d = 0; d <
D; ++d)
3225 for (std::size_t i = 0; i <
nodes.size(); ++i)
3226 if (!
nodes[i].nitride)
3227 sumDiv += std::abs(
nodes[i].strainTrace);
3229 lastContinuityResidual_ = inflow > std::numeric_limits<T>::epsilon()
3230 ? sumDiv * cellVolume / inflow
3231 : sumDiv * cellVolume;
3234 macWarmVersion_ = residual <= deformationParameters.mechanicsTolerance
3236 : std::numeric_limits<std::size_t>::max();
3237 if (residual > deformationParameters.mechanicsTolerance)
3238 VIENNACORE_LOG_WARNING(
3239 "solveMechanics: viscosity iteration did not converge after " +
3240 std::to_string(it - 1) +
3241 " iterations (change=" + std::to_string(residual) +
", tolerance=" +
3242 std::to_string(deformationParameters.mechanicsTolerance) +
")");
3256 for (std::size_t k = 0; k <
macCorners_.size(); ++k) {
3262 Vec3D<T> v{
T(0),
T(0),
T(0)};
3264 for (
int bc = 0; bc <= 1; ++bc)
3265 for (
int be = 0; be <= 1; ++be) {
3269 const std::size_t j =
macNode(q);
3272 for (
unsigned d = 0; d <
D; ++d)
3273 v[d] +=
nodes[j].velocity[d];
3278 std::array<T, D> base{}, frac{}, departure{};
3279 for (
unsigned d = 0; d <
D; ++d) {
3280 const T x =
static_cast<T>(lo[d]) - v[d] / cnt * dt /
gridDelta;
3281 base[d] = std::floor(x);
3282 frac[d] = x - base[d];
3284 departure[d] = x + (d == c || d == e ?
T(0.5) :
T(0));
3288 auto &slot = out.try_emplace(lo,
macNoHistory()).first->second;
3292 T sum =
T(0), wsum =
T(0);
3293 for (
unsigned corner = 0; corner < (1u <<
D); ++corner) {
3296 for (
unsigned d = 0; d <
D; ++d) {
3297 const bool up = (corner >> d) & 1u;
3298 idx[d] =
static_cast<viennahrle::IndexType
>(base[d]) + (up ? 1 : 0);
3299 w *= up ? frac[d] : (
T(1) - frac[d]);
3313 auto &slot = out.try_emplace(lo,
macNoHistory()).first->second;
3339 point[c] -=
static_cast<int>(k & 1u);
3340 point[e] -=
static_cast<int>(k >> 1);
3346 auto store = [&](SmartPointer<Domain<T, D>> levelSet,
3347 const std::string &name) {
3348 using VD =
typename PointData<T>::VectorDataType;
3349 std::array<VD, 4> data;
3350 ConstSparseIterator it(levelSet->getDomain());
3351 for (; !it.isFinished(); ++it) {
3352 if (!it.isDefined())
3354 const IndexType point = it.getStartIndices();
3355 for (
unsigned k = 0; k < 4; ++k) {
3357 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
3361 value[pair] = found->second[pair];
3363 data[k].push_back(value);
3366 for (
unsigned k = 0; k < 4; ++k)
3367 levelSet->getPointData().insertReplaceVectorData(
3368 std::move(data[k]), name + std::to_string(k));
3370 store(ambientInterface,
"OxCornerShear");
3372 store(maskInterface,
"NitCornerShear");
3377 auto load = [&](SmartPointer<Domain<T, D>> levelSet,
3378 const std::string &name) {
3379 auto &pd = levelSet->getPointData();
3380 for (
unsigned k = 0; k < 4; ++k) {
3381 const int index = pd.getVectorDataIndex(name + std::to_string(k));
3384 const auto *data = pd.getVectorData(index);
3385 ConstSparseIterator it(levelSet->getDomain());
3386 for (; !it.isFinished(); ++it) {
3387 if (!it.isDefined())
3389 const auto id = it.getPointId();
3390 if (
id >=
static_cast<decltype(
id)
>(data->size()))
3392 const auto &v = (*data)[id];
3393 for (
unsigned pair = 0; pair <
macPairs; ++pair) {
3402 slot[pair] = v[pair];
3407 load(ambientInterface,
"OxCornerShear");
3408 if (maskInterface !=
nullptr)
3409 load(maskInterface,
"NitCornerShear");
3415 const std::size_t n =
nodes.size();
3417 for (
unsigned d = 0; d + 1 <
D; ++d)
3420#pragma omp parallel for reduction(+ : inflow) schedule(static)
3421 for (std::size_t i = 0; i < n; ++i)
3422 for (
unsigned fi = 0; fi < 2 *
D; ++fi)
3423 if (faceBCTypes_[fi * n + i] == Boundary::REACTION) {
3425 inflow += std::abs(v[fi / 2]) * faceArea;
3435 const std::size_t n =
nodes.size();
3438 T maxDiv =
T(0), sumSq =
T(0), net =
T(0);
3439 T pMin = std::numeric_limits<T>::max();
3440 T pMax = std::numeric_limits<T>::lowest();
3441 std::size_t count = 0;
3442 for (std::size_t i = 0; i < n; ++i) {
3443 if (
nodes[i].nitride)
3445 const T p =
nodes[i].pressure;
3446 if (std::isfinite(p)) {
3447 pMin = std::min(pMin, p);
3448 pMax = std::max(pMax, p);
3450 const T div =
nodes[i].strainTrace;
3451 if (!std::isfinite(div))
3453 maxDiv = std::max(maxDiv, std::abs(div));
3458 T cellVolume =
T(1);
3459 for (
unsigned d = 0; d <
D; ++d)
3465 buffer,
sizeof(buffer),
3466 "OxidationDeformation: mechanics iterations=%u "
3467 "residual=%.3e continuity=%.3e |div v|max=%.3e rms=%.3e /hr "
3468 "netVolumeRate=%.4e "
3469 "um^%d/hr p=[%.3f, %.3f] GPa",
3470 iterationsUsed,
static_cast<double>(residual),
3471 static_cast<double>(lastContinuityResidual_),
3472 static_cast<double>(maxDiv),
3473 static_cast<double>(count ? std::sqrt(sumSq /
T(count)) :
T(0)),
3474 static_cast<double>(net * cellVolume),
D,
3475 static_cast<double>(pMin *
T(1e-9)),
3476 static_cast<double>(pMax *
T(1e-9)));
3477 VIENNACORE_LOG_INFO(std::string(buffer));
3481 avgExpansionSpeed_ = 0.;
3485 ConstSparseIterator reactionIt(reactionInterface->getDomain());
3486 ConstSparseIterator ambientIt(ambientInterface->getDomain());
3488 std::size_t count = 0;
3490 for (
const auto &node :
nodes) {
3491 bool touchesReactionBoundary =
false;
3492 for (
unsigned direction = 0; direction <
D; ++direction) {
3493 for (
int offset : {-1, 1}) {
3494 IndexType neighbor = node.index;
3495 neighbor[direction] += offset;
3503 neighbor) == Boundary::REACTION) {
3504 touchesReactionBoundary =
true;
3508 if (touchesReactionBoundary)
3512 if (touchesReactionBoundary) {
3513 Vec3D<T> coordinate{0., 0., 0.};
3514 for (
unsigned i = 0; i <
D; ++i)
3515 coordinate[i] = node.index[i] *
gridDelta;
3516 avgExpansionSpeed_ +=
3517 (oxidationParameters.expansionCoefficient -
T(1)) *
3518 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
3525 avgExpansionSpeed_ /=
static_cast<T>(count);
3529 boundaryCacheValid_ =
false;
3530 const std::size_t n =
nodes.size();
3531 reactionVelocity_.assign(n, Vec3D<T>{
T(0),
T(0),
T(0)});
3532 startStress_.assign(n, std::array<T, 9>{});
3533 if (diffusionField !=
nullptr)
3534 diffusionField->getScalarVelocity({
T(0),
T(0),
T(0)}, 0,
3535 {
T(0),
T(0),
T(0)}, 0);
3536#pragma omp parallel for schedule(static)
3537 for (std::size_t i = 0; i < n; ++i) {
3538 bool reaction =
false;
3539 for (
unsigned fi = 0; fi < 2 *
D; ++fi)
3540 reaction = reaction || faceBCTypes_[fi * n + i] == Boundary::REACTION;
3545 boundaryCacheValid_ =
true;
3549 return boundaryCacheValid_ ? reactionVelocity_[i]
3554 if (boundaryCacheValid_) {
3557 return startStress_[i];
3563 Vec3D<T> coordinate{0., 0., 0.};
3564 for (
unsigned i = 0; i <
D; ++i)
3568 reactionSign * expansionVelocity);
3572 if (diffusionField ==
nullptr || ambientInterface ==
nullptr)
3573 return {0., 0., 0.};
3576 for (
unsigned i = 0; i <
D; ++i)
3577 index[i] = std::llround(coordinate[i] /
gridDelta);
3579 ConstSparseIterator ambientIt(ambientInterface->getDomain());
3585 Vec3D<T> maxVelocity{0., 0., 0.};
3586 if (ambientInterface ==
nullptr || diffusionField ==
nullptr)
3593 std::vector<IndexType> points;
3594 for (ConstSparseIterator ambientIt(ambientInterface->getDomain());
3595 !ambientIt.isFinished(); ++ambientIt)
3596 if (ambientIt.isDefined())
3597 points.push_back(ambientIt.getStartIndices());
3600 diffusionField->getScalarVelocity({
T(0),
T(0),
T(0)}, 0,
3601 {
T(0),
T(0),
T(0)}, 0);
3604 Vec3D<T> local{0., 0., 0.};
3605#pragma omp for schedule(dynamic, 64) nowait
3606 for (std::size_t k = 0; k < points.size(); ++k) {
3607 Vec3D<T> coordinate{0., 0., 0.};
3608 for (
unsigned d = 0; d <
D; ++d)
3609 coordinate[d] = points[k][d] *
gridDelta;
3612 for (
unsigned d = 0; d <
D; ++d)
3613 local[d] = std::max(local[d], std::abs(velocity[d]));
3616 for (
unsigned d = 0; d <
D; ++d)
3617 maxVelocity[d] = std::max(maxVelocity[d], local[d]);
3623 return (oxidationParameters.expansionCoefficient -
T(1)) *
3624 std::abs(diffusionField->getScalarVelocity(coordinate, 0,
3629 ConstSparseIterator reactionIt(reactionInterface->getDomain());
3634 const IndexType &index)
const {
3636 levelSetIt, index, [&](
const IndexType &q,
bool &ok) {
3641 for (
unsigned i = 0; i <
D; ++i)
3642 norm += normal[i] * normal[i];
3644 if (norm <= std::numeric_limits<T>::epsilon()) {
3645 normal = Vec3D<T>{0., 0., 0.};
3650 norm = std::sqrt(norm);
3651 for (
unsigned i = 0; i <
D; ++i)
3657 const auto found = deviatoricStressHistory.find(index);
3658 if (found == deviatoricStressHistory.end())
3660 return found->second;
3666 if (tau <= std::numeric_limits<T>::epsilon())
3668 return std::exp(-
T(deformationParameters.stressTimeStep) / tau);
3674 return std::max(
T(deformationParameters.viscosity) *
3676 std::numeric_limits<T>::min() *
T(1e10));
3680 return deformationParameters.shearActivationVolume > 0.;
3688 :
T(deformationParameters.viscosity);
3695 if (tau0 <= std::numeric_limits<T>::epsilon())
3697 const T tau = tau0 *
nodeViscosity(i) /
T(deformationParameters.viscosity);
3698 return std::exp(-
T(deformationParameters.stressTimeStep) / tau);
3705 std::numeric_limits<T>::min() *
T(1e10));
3712 for (
unsigned k = 0; k < 9; ++k)
3713 j2 += deviatoric[k] * deviatoric[k];
3714 const T shear = std::sqrt(
T(0.5) * j2);
3716 T(1.380649e-23) * std::max(
T(oxidationParameters.temperature),
T(1));
3717 const T x = shear * volume / (
T(2) * kT);
3718 constexpr T floor =
T(1e-6);
3723 return std::max(x / std::sinh(x), floor);
3728 const T eta0 =
T(deformationParameters.viscosity);
3729 oxideEta_.assign(
nodes.size(), eta0);
3730 for (std::size_t i = 0; i <
nodes.size(); ++i)
3731 if (!
nodes[i].nitride)
3734 T(deformationParameters.shearActivationVolume));
3739 const T eta0 =
T(deformationParameters.nitrideViscosity);
3740 nitrideEta_.assign(
nodes.size(), eta0);
3741 for (std::size_t i = 0; i <
nodes.size(); ++i)
3742 if (
nodes[i].nitride)
3746 T(deformationParameters.nitrideShearActivationVolume));
3749 static std::array<T, 9>
deviatorOf(
const std::array<T, 9> &stress) {
3751 const T mean = (stress[0] + stress[4] + stress[8]) /
T(3);
3752 for (
unsigned d = 0; d < 3; ++d)
3753 s[d * 3 + d] -= mean;
3765 const std::array<T, 9> &deviatoricRate)
const {
3767 T(deformationParameters.viscosity),
3769 T(deformationParameters.shearActivationVolume));
3773 const std::array<T, 9> &deviatoricRate)
const {
3774 const T eta0 =
T(deformationParameters.nitrideViscosity);
3776 start, deviatoricRate, eta0, eta0 /
nitrideMu(),
3777 T(deformationParameters.nitrideShearActivationVolume));
3780 const std::array<T, 9> &deviatoricRate,
T eta0,
T tau0,
3782 const T dt =
T(deformationParameters.stressTimeStep);
3783 const auto residual = [&](
T logEta) {
3784 const T eta = std::exp(logEta);
3785 const T decay = (tau0 <= std::numeric_limits<T>::epsilon())
3787 : std::exp(-dt / (tau0 * eta / eta0));
3788 std::array<T, 9> end{};
3789 for (
unsigned k = 0; k < 9; ++k)
3791 decay * start[k] + (
T(1) - decay) *
T(2) * eta * deviatoricRate[k];
3792 return logEta - std::log(eta0 *
eyringFactor(end, volume));
3794 const T hi = std::log(eta0);
3795 const T rHi = residual(hi);
3798 const T lo = hi + std::log(
T(1e-6));
3799 const T rLo = residual(lo);
3801 return std::exp(lo);
3804 return std::exp(
bracketedRoot(residual, lo, rLo, hi, rHi,
T(0.5e-10)));
3811 template <
class Function>
3813 T c = b, fc = fb, d = b - a, e = d;
3814 for (
int iteration = 0; iteration < 100; ++iteration) {
3815 if ((fb >
T(0) && fc >
T(0)) || (fb <
T(0) && fc <
T(0))) {
3820 if (std::abs(fc) < std::abs(fb)) {
3828 const T tol =
T(2) * std::numeric_limits<T>::epsilon() * std::abs(b) +
3830 const T m =
T(0.5) * (c - b);
3831 if (std::abs(m) <= tol || fb ==
T(0))
3833 if (std::abs(e) >= tol && std::abs(fa) > std::abs(fb)) {
3834 const T s = fb / fa;
3840 const T qa = fa / fc, r = fb / fc;
3841 p = s * (
T(2) * m * qa * (qa - r) - (b - a) * (r -
T(1)));
3842 q = (qa -
T(1)) * (r -
T(1)) * (s -
T(1));
3849 std::min(
T(3) * m * q - std::abs(tol * q), std::abs(e * q))) {
3862 b += std::abs(d) > tol ? d : (m >
T(0) ? tol : -tol);
3869 if (deformationParameters.stressRelaxationTime >
T(0))
3870 return deformationParameters.stressRelaxationTime;
3871 if (deformationParameters.shearModulus > std::numeric_limits<T>::epsilon())
3872 return deformationParameters.viscosity /
3873 deformationParameters.shearModulus;
3878 T doubleContraction = 0.;
3879 for (
unsigned i = 0; i < 3; ++i) {
3880 for (
unsigned j = 0; j < 3; ++j) {
3881 const T value =
T(0.5) * (deviatoricStress[
tensorIndex(i, j)] +
3883 doubleContraction += value * value;
3886 return std::sqrt(
T(1.5) * doubleContraction);
3890 ConstSparseIterator &ambientIt,
3891 ConstSparseIterator &maskIt,
3892 const IndexType &inside,
3893 const IndexType &outside)
const {
3899 ConstSparseIterator &ambientIt,
3900 ConstSparseIterator &maskIt,
3901 const IndexType &inside,
3902 const IndexType &outside)
const {
3903 const T reactionInside =
valueAt(reactionIt, inside);
3904 const T reactionOutside =
valueAt(reactionIt, outside);
3905 const T ambientInside =
valueAt(ambientIt, inside);
3906 const T ambientOutside =
valueAt(ambientIt, outside);
3910 const bool reactionCrosses =
crosses(reactionInside, reactionOutside);
3911 const bool ambientCrosses =
crosses(ambientInside, ambientOutside);
3912 const bool maskCrosses =
3913 maskInterface !=
nullptr &&
crosses(maskInside, maskOutside);
3915 if (!reactionCrosses && !ambientCrosses && !maskCrosses)
3918 const T reactionDistance =
3920 : std::numeric_limits<T>::max();
3921 const T ambientDistance =
3923 : std::numeric_limits<T>::max();
3924 const T maskDistance = maskCrosses
3926 : std::numeric_limits<T>::max();
3927 if (reactionCrosses && reactionDistance <= ambientDistance &&
3928 reactionDistance <= maskDistance)
3929 return {Boundary::REACTION, reactionDistance};
3933 if (ambientCrosses &&
3935 return {Boundary::AMBIENT, ambientDistance};
3936 return {Boundary::AMBIENT, std::min(ambientDistance, maskDistance)};
3945 constexpr T eps =
T(1e-9);
3946 return reactionSign * reactionPhi >= -eps &&
3947 ambientSign * ambientPhi >= -eps;
3951 if (maskInterface ==
nullptr)
3952 return ConstSparseIterator(reactionInterface->getDomain());
3953 return ConstSparseIterator(maskInterface->getDomain());
3956 bool isInsideMask(ConstSparseIterator &maskIt,
const IndexType &index)
const {
3957 if (maskInterface ==
nullptr)
3959 return maskSign *
valueAt(maskIt, index) >= 0.;
3963 if (maskInterface ==
nullptr)
3964 return std::numeric_limits<T>::max();
3965 return valueAt(maskIt, index);
3971 (maskInterface !=
nullptr &&
3972 static_cast<T>(maskSign) * maskOutside >=
T(0));
3976 if (maskInterface ==
nullptr)
3978 const T fraction = std::clamp(distance /
gridDelta,
T(0),
T(1));
3981 const T maskPhi = insidePhi + fraction * (outsidePhi - insidePhi);
3982 return static_cast<T>(maskSign) * maskPhi >=
T(0);
3987 insidePhi, outsidePhi,
3988 deformationParameters.minMechanicsBoundaryDistance,
gridDelta);
3991 static constexpr unsigned tensorIndex(
unsigned row,
unsigned column) {
3992 return 3 * row + column;
3998template <
class T,
int D>
4000 SmartPointer<OxidationDeformation<T, D>> field_;
4001 bool nitride_ =
false;
4006 : field_(field), nitride_(nitride) {}
4008 template <
class... Args>
static auto New(Args &&...args) {
4009 return SmartPointer<OxidationMaterialVelocity>::New(
4010 std::forward<Args>(args)...);
4015 unsigned long )
final {
4016 return field_->getMaterialVelocity(coordinate, nitride_);
4020 const Vec3D<T> ¢ralDifferences)
final {
4021 return field_->getDissipationAlpha(direction, material, centralDifferences);
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
constexpr T gridDelta
Definition OxidationNitride.cpp:35
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:126
T getDissipationAlpha(int direction, int material, const Vec3D< T > ¢ralDifferences) final
If lsLocalLaxFriedrichsAnalytical is used as the spatial discretization scheme, this is called to pro...
Definition lsOxidationDeformation.hpp:4019
Vec3D< T > getVectorVelocity(const Vec3D< T > &coordinate, int, const Vec3D< T > &, unsigned long) final
Like getScalarVelocity, but returns a velocity value for each cartesian direction.
Definition lsOxidationDeformation.hpp:4013
OxidationMaterialVelocity(SmartPointer< OxidationDeformation< T, D > > field, bool nitride)
Definition lsOxidationDeformation.hpp:4004
static auto New(Args &&...args)
Definition lsOxidationDeformation.hpp:4008
Common Cartesian-grid infrastructure shared by the oxidation solver classes (diffusion,...
Definition lsOxidationSolverBase.hpp:100
static constexpr std::size_t noNode
Definition lsOxidationSolverBase.hpp:106
bool crosses(T a, T b) const
Definition lsOxidationSolverBase.hpp:147
std::size_t lookupNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:218
std::size_t linearIndex(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:224
void initNodeLookup()
Definition lsOxidationSolverBase.hpp:211
bool inBounds(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:204
std::array< std::size_t, D > strides
Definition lsOxidationSolverBase.hpp:111
T gridDelta
Definition lsOxidationSolverBase.hpp:112
std::vector< std::size_t > nodeLookupFlat
Definition lsOxidationSolverBase.hpp:107
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:334
std::array< std::size_t, D > extents
Definition lsOxidationSolverBase.hpp:110
viennahrle::ConstSparseIterator< typename Domain< T, D >::DomainType > ConstSparseIterator
Definition lsOxidationSolverBase.hpp:103
std::vector< std::size_t > findNearbyNodes(const IndexType &index, Accept accept) const
Definition lsOxidationSolverBase.hpp:294
T valueAt(ConstSparseIterator &it, const IndexType &index) const
Definition lsOxidationSolverBase.hpp:155
bool increment(IndexType &index) const
Definition lsOxidationSolverBase.hpp:233
IndexType minIndex
Definition lsOxidationSolverBase.hpp:108
viennahrle::Index< D > IndexType
Definition lsOxidationSolverBase.hpp:102
std::size_t findNearbyNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:247
Vec3D< T > definedLevelSetGradient(ConstSparseIterator &it, const IndexType &index, OnGrid onGrid) const
Definition lsOxidationSolverBase.hpp:170
IndexType maxIndex
Definition lsOxidationSolverBase.hpp:109
T levelSetCrossingDistance(T insidePhi, T outsidePhi, T minBoundaryFraction, T gridDelta)
Definition lsOxidationSolverBase.hpp:86
T clampLevelSetPhi(T v)
Clamp HRLE far-field sentinels (±DBL_MAX) to ±1 before differencing to prevent DBL_MAX² overflow that...
Definition lsOxidationSolverBase.hpp:81
Vec3D< T > vecScaled(const Vec3D< T > &source, T factor)
Definition lsOxidationSolverBase.hpp:50
Definition lsAdvect.hpp:41
GpuMode
Where the large linear solves of the oxidation solvers run: the diffusion solve and the 3-D mechanics...
Definition lsOxidationDiffusion.hpp:43
@ Gpu
Always use GPU; fail if unavailable or unsuccessful Use the GPU when it is usable,...
Definition lsOxidationDiffusion.hpp:45
@ Cpu
Always use CPU (default).
Definition lsOxidationDiffusion.hpp:44
Parameters for the steady oxidant diffusion model used by OxidationDiffusion.
Definition lsOxidationDiffusion.hpp:56
Definition lsOxidationSolverBase.hpp:44