DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
Mesh_Elevation_SmoothSolver.cpp
Go to the documentation of this file.
1#include "Mesh.hpp"
2#include "Geom/Quadrature.hpp"
5
6#include <omp.h>
7#include <fmt/core.h>
8#include <functional>
9#include <unordered_set>
10#include <optional>
11#include <Solver/Linear.hpp>
12
13#include "Geom/PointCloud.hpp"
14#include <nanoflann.hpp>
15
16#include "DNDS/EigenPCH.hpp"
17#ifdef DNDS_USE_SUPERLU
18# include <superlu_ddefs.h>
19#endif
20
21namespace DNDS::Geom
22{
23 // Value-semantic class: all members are value types (ssp, TTrans);
24 // = default for all special members per rule of five.
25 struct CoordPairDOF : public tCoordPair
26 {
27 CoordPairDOF() = default;
28 ~CoordPairDOF() = default;
29 CoordPairDOF(const CoordPairDOF &) = default;
31 CoordPairDOF &operator=(const CoordPairDOF &) = default;
33
35 {
36 real ret = 0;
37 for (index i = 0; i < this->father->Size(); i++)
38 ret += (*this)[i].dot(R[i]);
40 MPI::Allreduce(&ret, &retSum, 1, DNDS_MPI_REAL, MPI_SUM, this->father->getMPI().comm);
41 return retSum;
42 }
43
45 {
46 return std::sqrt(this->dot(*this));
47 }
48
50 {
51 for (index i = 0; i < this->Size(); i++)
52 (*this)[i] += R[i] * alpha;
53 }
54
56 {
57 for (index i = 0; i < this->Size(); i++)
58 (*this)[i].setConstant(v);
59 }
60
62 {
63 for (index i = 0; i < this->Size(); i++)
64 (*this)[i] = R[i];
65 }
66
68 {
69 for (index i = 0; i < this->Size(); i++)
70 (*this)[i] *= r;
71 }
72 };
73
74 struct PointCloudKDTreeCoordPair
75 {
76 tCoord ref;
77 using coord_t = real; //!< The type of each coordinate
81
82 // Must return the number of data points
83 [[nodiscard]] size_t
85 {
87 return ref->Size();
88 }
89
90 [[nodiscard]] real kdtree_get_pt(const size_t idx, const size_t dim) const
91 {
93 return ref->operator[](idx)(dim);
94 }
95
96 template <class BBOX>
97 bool kdtree_get_bbox(BBOX & /* bb */) const
98 {
99 return false;
100 }
101 };
102
103 // =================================================================
104 // Shared setup for all smooth solver variants
105 // =================================================================
106
107 /**
108 * \brief Result of the shared smooth solver setup.
109 *
110 * Populated by PrepareSmoothSolverSetup() and consumed by each
111 * smooth solver variant.
112 */
113 struct SmoothSolverSetup
114 {
115 std::unordered_set<index> nodesBoundInterpolated{};
118 };
119
120 /**
121 * \brief Common preamble for all smooth solver variants.
122 *
123 * Checks state assertions, identifies boundary-interpolated nodes,
124 * gathers boundary coordinates and displacement values, and sets up
125 * MPI ghost communication so every rank has a complete copy of the
126 * boundary interpolation data.
127 *
128 * \param mesh The mesh being smoothed (must have elevState == Elevation_O1O2,
129 * adjPrimaryState/adjFacialState/adjC2FState == Adj_PointToLocal,
130 * and nTotalMoved >= 0).
131 * \return std::nullopt if nTotalMoved == 0 (nothing to do);
132 * otherwise the populated SmoothSolverSetup.
133 */
134 static std::optional<SmoothSolverSetup> PrepareSmoothSolverSetup(
135 UnstructuredMesh &mesh)
136 {
137 DNDS_assert(mesh.elevState == Elevation_O1O2);
138 DNDS_assert(mesh.adjPrimaryState == Adj_PointToLocal);
139 DNDS_assert(mesh.cell2node.isLocal() && mesh.bnd2node.isLocal());
140 DNDS_assert(mesh.adjFacialState == Adj_PointToLocal);
141 DNDS_assert(mesh.face2cell.isLocal() && mesh.face2node.isLocal());
142 DNDS_assert(mesh.adjC2FState == Adj_PointToLocal);
143 DNDS_assert(mesh.cell2face.isLocal() && mesh.bnd2face.isLocal());
144 DNDS_assert(mesh.face2node.father);
145 DNDS_assert(mesh.nTotalMoved >= 0);
146 if (!mesh.nTotalMoved)
147 {
148 if (mesh.mpi.rank == mesh.mRank)
149 log() << "UnstructuredMesh === ElevatedNodesSolveInternalSmooth() early exit for no nodes were moved";
150 return std::nullopt;
151 }
152
153 SmoothSolverSetup setup;
154
155 // Identify boundary-interpolated nodes: those with a displacement set
156 // (coordsElevDisp != largeReal) or marked as O1 nodes (z == 2*largeReal).
157 for (index iN = 0; iN < mesh.coords.father->Size(); iN++)
158 {
159 if (mesh.coordsElevDisp[iN](0) != largeReal ||
160 mesh.coordsElevDisp[iN](2) == 2 * largeReal)
161 {
162 setup.nodesBoundInterpolated.insert(iN);
163 }
164 }
165
166 // Gather boundary coordinates and displacement values.
167 setup.boundInterpCoo.InitPair("SmoothSolverSetup::boundInterpCoo", mesh.mpi);
168 setup.boundInterpVal.InitPair("SmoothSolverSetup::boundInterpVal", mesh.mpi);
169
170 setup.boundInterpCoo.father->Resize(setup.nodesBoundInterpolated.size());
171 setup.boundInterpVal.father->Resize(setup.nodesBoundInterpolated.size());
172
173 index top{0};
174 for (auto iN : setup.nodesBoundInterpolated)
175 {
176 setup.boundInterpCoo[top] = mesh.coords[iN];
177 setup.boundInterpVal[top] =
178 (mesh.coordsElevDisp[iN](0) != largeReal)
179 ? tPoint{mesh.coordsElevDisp[iN]}
180 : tPoint::Zero();
181 top++;
182 }
183
184 // Build global ghost communication: every rank gets the full set.
185 setup.boundInterpCoo.father->createGlobalMapping();
186 index boundInterpGlobSize = setup.boundInterpCoo.father->globalSize();
187 std::vector<index> boundInterpPullIdx(boundInterpGlobSize);
188 for (index i = 0; i < boundInterpGlobSize; i++)
189 boundInterpPullIdx[i] = i;
190 setup.boundInterpCoo.TransAttach();
191 setup.boundInterpCoo.trans.createGhostMapping(boundInterpPullIdx);
192 setup.boundInterpCoo.trans.createMPITypes();
193 setup.boundInterpCoo.trans.pullOnce();
194
195 setup.boundInterpVal.TransAttach();
196 setup.boundInterpVal.trans.BorrowGGIndexing(setup.boundInterpCoo.trans);
197 setup.boundInterpVal.trans.createMPITypes();
198 setup.boundInterpVal.trans.pullOnce();
199
200 return setup;
201 }
202
204 {
206 if (!setupOpt)
207 return;
208 auto &setup = *setupOpt;
209 auto &nodesBoundInterpolated = setup.nodesBoundInterpolated;
210 auto &boundInterpCoo = setup.boundInterpCoo;
211 auto &boundInterpVal = setup.boundInterpVal;
212
213 DNDS_MPI_InsertCheck(mpi, "Got node2node");
214
215 coordsElevDisp.trans.initPersistentPull(); // only holds local nodes
216
217 if (mpi.rank == mRank)
218 log() << "RBF set: " << boundInterpCoo.son->Size() << std::endl;
219
220 using kdtree_t = nanoflann::KDTreeSingleIndexAdaptor<
221 nanoflann::L2_Simple_Adaptor<real, PointCloudKDTreeCoordPair>,
223 3,
224 index>;
225 auto coordsI = PointCloudKDTreeCoordPair(boundInterpCoo.son);
227
228 for (index iN = 0; iN < coords.father->Size(); iN++)
229 {
230 if (nodesBoundInterpolated.count(iN))
231 continue;
232
233 index nFind = elevationInfo.nSearch;
234 tPoint cooC = coords[iN];
235
236 std::vector<index> idxFound;
237 Eigen::Vector<real, Eigen::Dynamic> outDistancesSqr;
238 idxFound.resize(nFind);
239 outDistancesSqr.resize(nFind);
240
241 index nFound = bndInterpTree.knnSearch(cooC.data(), nFind, idxFound.data(), outDistancesSqr.data());
242 DNDS_assert(nFound >= 1);
243 idxFound.resize(nFound);
244 outDistancesSqr.resize(nFound);
247 coordBnd.resize(3, nFound);
248 dispBnd.resize(3, nFound);
249 for (index iF = 0; iF < nFound; iF++)
250 {
251 coordBnd(EigenAll, iF) = (*boundInterpCoo.son)[idxFound[iF]];
252 dispBnd(EigenAll, iF) = (*boundInterpVal.son)[idxFound[iF]];
253 }
254 real RMin = std::sqrt(outDistancesSqr.minCoeff());
256 dispC.setZero();
257
258 if (RMin < sqr(elevationInfo.RBFRadius * (KernelIsCompact(elevationInfo.kernel) ? 1 : 5)))
259 {
260 tPoint coordBndC = coordBnd.rowwise().mean();
265 qs.resize(3, 1);
266 qs = cooC - coordBndC;
267 tPoint dCur =
268 (RBF::RBFCPC2(coordBndRel, qs, elevationInfo.RBFRadius, elevationInfo.kernel).transpose() * coefs.topRows(coordBndRel.cols()) +
269 coefs(EigenLast - 3, EigenAll) +
270 qs.transpose() * coefs.bottomRows(3))
271 .transpose();
272 dispC = dCur;
273 }
274
276 }
277
278 coordsElevDisp.trans.startPersistentPull();
279 coordsElevDisp.trans.waitPersistentPull();
280
281 for (index iN = 0; iN < coords.father->Size(); iN++)
282 {
283 // if(dim == 2)
284 // dispO2[iN](2) = 0;
285 // if(dispO2[iN].norm() != 0)
286 // std::cout << dispO2[iN].transpose() << std::endl;
287 if (coordsElevDisp[iN](0) != largeReal)
289 }
290 coords.trans.pullOnce();
291 }
292
294 {
296 if (!setupOpt)
297 return;
298 auto &setup = *setupOpt;
299 auto &nodesBoundInterpolated = setup.nodesBoundInterpolated;
300 auto &boundInterpCoo = setup.boundInterpCoo;
301 auto &boundInterpVal = setup.boundInterpVal;
302 index boundInterpGlobSize = boundInterpCoo.father->globalSize();
303
304 // Build full pull index list (for RBF coefficient ghost mapping)
305 std::vector<index> boundInterpPullIdx(boundInterpGlobSize);
306 for (index i = 0; i < boundInterpGlobSize; i++)
308
309 DNDS_MPI_InsertCheck(mpi, "Got node2node");
310
311 coordsElevDisp.trans.initPersistentPull(); // only holds local nodes
312
313 if (mpi.rank == mRank)
314 log() << "RBF set: " << boundInterpCoo.son->Size() << std::endl;
315
316 using kdtree_t = nanoflann::KDTreeSingleIndexAdaptor<
317 nanoflann::L2_Simple_Adaptor<real, PointCloudKDTreeCoordPair>,
319 3,
320 index>;
321 using kdtree_tcoo = nanoflann::KDTreeSingleIndexAdaptor<
322 nanoflann::L2_Simple_Adaptor<real, PointCloudKDTree>,
324 3,
325 index>;
326 auto coordsI = PointCloudKDTreeCoordPair(boundInterpCoo.son);
328
330 using tScalarPair = DNDS::ArrayPair<DNDS::ParArray<real, 1>>;
331 tScalarPair boundInterpR;
332
333 boundInterpCoef.InitPair("ElevatedNodesSolveInternalSmoothV1Old::boundInterpCoef", mpi);
334 boundInterpR.InitPair("ElevatedNodesSolveInternalSmoothV1Old::boundInterpR", mpi);
335 boundInterpCoef.father->Resize(mpi.rank == mRank ? boundInterpGlobSize : 0);
336 boundInterpR.father->Resize(mpi.rank == mRank ? boundInterpGlobSize : 0);
337
338 if (mpi.rank == mRank) // that dirty work
339 {
340
341 Eigen::SparseMatrix<real> M(boundInterpGlobSize, boundInterpGlobSize);
342 M.reserve(Eigen::Vector<index, Eigen::Dynamic>::Constant(boundInterpGlobSize, elevationInfo.nSearch));
343 MatrixXR f;
344 f.resize(boundInterpGlobSize, 3);
345 for (index iN = 0; iN < boundInterpGlobSize; iN++)
346 {
347 tPoint cooC = (*boundInterpCoo.son)[iN];
348
349 index nFind = elevationInfo.nSearch;
350 std::vector<index> idxFound;
351 Eigen::Vector<real, Eigen::Dynamic> outDistancesSqr;
352 idxFound.resize(nFind);
353 outDistancesSqr.resize(nFind);
354 index nFound = bndInterpTree.knnSearch(cooC.data(), nFind, idxFound.data(), outDistancesSqr.data());
355 DNDS_assert(nFound >= 1);
356 idxFound.resize(nFound);
357 outDistancesSqr.resize(nFound);
358
359 real RMin = outDistancesSqr.minCoeff();
360 real RMax = outDistancesSqr.maxCoeff();
361 real RRBF = elevationInfo.RBFRadius * std::sqrt(RMax);
362 boundInterpR(iN, 0) = RRBF;
363 DNDS_assert(RRBF > 0);
364 Eigen::Vector<real, Eigen::Dynamic> outDists = outDistancesSqr.array().sqrt() * (1. / RRBF);
366 for (index in2n = 0; in2n < nFound; in2n++)
367 M.insert(iN, idxFound[in2n]) = fBasis(in2n);
368
369 f(iN, EigenAll) = (*boundInterpVal.son)[iN].transpose();
370 }
371
372 log() << "RBF assembled: " << boundInterpCoo.son->Size() << std::endl;
374 M.makeCompressed();
375 Eigen::SparseLU<Eigen::SparseMatrix<real>, Eigen::COLAMDOrdering<int>> LUSolver;
376 LUSolver.analyzePattern(M);
377 LUSolver.factorize(M);
378 coefs = LUSolver.solve(f);
379 for (index iN = 0; iN < boundInterpGlobSize; iN++)
380 boundInterpCoef[iN] = coefs(iN, EigenAll).transpose();
381 }
382
383 boundInterpCoef.father->createGlobalMapping();
384 boundInterpCoef.TransAttach();
385 boundInterpCoef.trans.createGhostMapping(boundInterpPullIdx);
386 boundInterpCoef.trans.createMPITypes();
387 boundInterpCoef.trans.pullOnce();
388
389 boundInterpR.TransAttach();
390 boundInterpR.trans.BorrowGGIndexing(boundInterpCoef.trans);
391 boundInterpR.trans.createMPITypes();
392 boundInterpR.trans.pullOnce();
393
394 if (mpi.rank == mRank)
395 {
396 for (index iN = 0; iN < boundInterpGlobSize; iN++)
397 {
398 }
399 }
400
402 insidePts.pts.reserve(coords.father->Size() - nodesBoundInterpolated.size());
403 std::vector<index> insideNodes;
404 insideNodes.reserve(insidePts.pts.size());
405 for (index iN = 0; iN < coords.father->Size(); iN++)
406 if (!nodesBoundInterpolated.count(iN))
407 insidePts.pts.push_back(coords[iN]), insideNodes.push_back(iN), coordsElevDisp[iN].setZero();
409 for (index iN = 0; iN < boundInterpGlobSize; iN++)
410 {
411
412 tPoint cooC = (*boundInterpCoo.son)[iN];
413#if NANOFLANN_VERSION < 0x150
414 std::vector<std::pair<DNDS::index, DNDS::real>> IndicesDists;
415#else
416 std::vector<nanoflann::ResultItem<DNDS::index, DNDS::real>> IndicesDists;
417#endif
418 IndicesDists.reserve(elevationInfo.nSearch * 5);
419 real RRBF = boundInterpR.son->operator()(iN, 0);
420#if NANOFLANN_VERSION < 0x150
421 nanoflann::SearchParams params{}; // default params
422#else
423 nanoflann::SearchParameters params{};
424#endif
425 index nFound = nodesDstTree.radiusSearch(cooC.data(), RRBF, IndicesDists, params);
426 Eigen::Vector<real, Eigen::Dynamic> outDists;
427 outDists.resize(IndicesDists.size());
428 DNDS_assert(RRBF > 0);
429 for (index i = 0; i < IndicesDists.size(); i++)
430 outDists[i] = std::sqrt(IndicesDists[i].second) / RRBF;
431
433 for (index i = 0; i < IndicesDists.size(); i++)
434 {
436 fBasis(i, 0) *
437 boundInterpCoef.son->operator[](iN);
438 }
439 }
440
441 // for (index iN = 0; iN < coords.father->Size(); iN++)
442 // {
443 // if (nodesBoundInterpolated.count(iN))
444 // continue;
445
446 // index nFind = elevationInfo.nSearch * 5; // for safety
447 // tPoint cooC = coords[iN];
448
449 // std::vector<index> idxFound;
450 // Eigen::Vector<real, Eigen::Dynamic> outDistancesSqr;
451 // idxFound.resize(nFind);
452 // outDistancesSqr.resize(nFind);
453
454 // index nFound = bndInterpTree.knnSearch(cooC.data(), nFind, idxFound.data(), outDistancesSqr.data());
455 // DNDS_assert(nFound >= 1);
456 // idxFound.resize(nFound);
457 // outDistancesSqr.resize(nFound);
458
459 // Eigen::Vector<real, Eigen::Dynamic> foundRRBFs;
460 // foundRRBFs.resize(nFound);
461 // tSmallCoords coefsC;
462 // coefsC.resize(3, nFound);
463 // for (index iF = 0; iF < nFound; iF++)
464 // foundRRBFs[iF] = (*boundInterpR.son)(idxFound[iF], 0),
465 // coefsC(EigenAll, iF) = (*boundInterpCoef.son)[idxFound[iF]].transpose();
466 // Eigen::Vector<real, Eigen::Dynamic> outDists = outDistancesSqr.array().sqrt() / foundRRBFs.array();
467 // auto fBasis = RBF::FRBFBasis(outDists, elevationInfo.kernel);
468 // tPoint dispC = coefsC * fBasis;
469 // coordsElevDisp[iN] = dispC;
470 // }
471
472 coordsElevDisp.trans.startPersistentPull();
473 coordsElevDisp.trans.waitPersistentPull();
474
475 for (index iN = nNodeO1; iN < coords.father->Size(); iN++)
476 {
477 // if(dim == 2)
478 // dispO2[iN](2) = 0;
479 // if(dispO2[iN].norm() != 0)
480 // std::cout << dispO2[iN].transpose() << std::endl;
481 if (coordsElevDisp[iN](0) != largeReal)
483 }
484 coords.trans.pullOnce();
485 }
486
488 {
490 if (!setupOpt)
491 return;
492 auto &setup = *setupOpt;
493 auto &nodesBoundInterpolated = setup.nodesBoundInterpolated;
494 auto &boundInterpCoo = setup.boundInterpCoo;
495 auto &boundInterpVal = setup.boundInterpVal;
496
497 DNDS_MPI_InsertCheck(mpi, "Got node2node");
498
499 coordsElevDisp.trans.initPersistentPull(); // only holds local nodes
500
501 index boundInterpGlobSize = boundInterpCoo.father->globalSize();
502 index boundInterpLocSize = nodesBoundInterpolated.size();
503
504 // Build full pull index list (for RBF coefficient ghost mapping)
505 std::vector<index> boundInterpPullIdx(boundInterpGlobSize);
506 for (index i = 0; i < boundInterpGlobSize; i++)
508
509 if (mpi.rank == mRank)
510 log() << "RBF set: " << boundInterpCoo.son->Size() << std::endl;
511
512 using kdtree_t = nanoflann::KDTreeSingleIndexAdaptor<
513 nanoflann::L2_Simple_Adaptor<real, PointCloudKDTreeCoordPair>,
515 3,
516 index>;
517 using kdtree_tcoo = nanoflann::KDTreeSingleIndexAdaptor<
518 nanoflann::L2_Simple_Adaptor<real, PointCloudKDTree>,
520 3,
521 index>;
522 auto coordsI = PointCloudKDTreeCoordPair(boundInterpCoo.son);
524
526 using tScalarPair = DNDS::ArrayPair<DNDS::ParArray<real, 1>>;
527 tScalarPair boundInterpR;
528 boundInterpCoef.InitPair("ElevatedNodesSolveInternalSmoothV1::boundInterpCoef", mpi);
529 boundInterpCoefRHS.InitPair("ElevatedNodesSolveInternalSmoothV1::boundInterpCoefRHS", mpi);
530 boundInterpR.InitPair("ElevatedNodesSolveInternalSmoothV1::boundInterpR", mpi);
531 boundInterpCoef.father->Resize(boundInterpCoo.father->Size());
532 boundInterpCoefRHS.father->Resize(boundInterpCoo.father->Size());
533 boundInterpR.father->Resize(boundInterpCoo.father->Size());
534 std::vector<std::vector<std::pair<index, real>>> MatC;
535 std::vector<index> boundInterpPullingIndexSolving;
536 MatC.resize(boundInterpLocSize);
537 for (index iN = 0; iN < boundInterpLocSize; iN++)
538 {
539 tPoint cooC = (*boundInterpCoo.father)[iN];
540
541 index nFind = elevationInfo.nSearch;
542 std::vector<index> idxFound;
543 Eigen::Vector<real, Eigen::Dynamic> outDistancesSqr;
544
545 /**********************************************/
546 idxFound.resize(nFind);
547 outDistancesSqr.resize(nFind);
548 index nFound = bndInterpTree.knnSearch(cooC.data(), nFind, idxFound.data(), outDistancesSqr.data());
549 DNDS_assert(nFound >= 1);
550 idxFound.resize(nFound);
551 outDistancesSqr.resize(nFound);
552
553 real RMin = std::sqrt(outDistancesSqr.minCoeff());
554 real RMax = std::sqrt(outDistancesSqr.maxCoeff());
555 real RRBF = elevationInfo.RBFRadius * RMax;
556 /***********************************************/
557 // std::vector<std::pair<DNDS::index, DNDS::real>> indicesDists;
558 // indicesDists.reserve(nFind);
559
560 // nanoflann::SearchParams searchParams;
561 // index nFound = bndInterpTree.radiusSearch(cooC.data(), elevationInfo.RBFRadius, indicesDists, searchParams);
562 // idxFound.reserve(nFound);
563 // outDistancesSqr.resize(nFound);
564 // for (auto v : indicesDists)
565 // outDistancesSqr(idxFound.size()) = v.second, idxFound.push_back(v.first);
566 // real RRBF = elevationInfo.RBFRadius;
567 /***********************************************/
568
569 boundInterpR(iN, 0) = RRBF;
570 DNDS_assert(RRBF > 0);
571 Eigen::Vector<real, Eigen::Dynamic> outDists = outDistancesSqr.array().pow(0.5 / elevationInfo.RBFPower) * (1. / std::pow(RRBF, 1. / elevationInfo.RBFPower));
573 MatC[iN].resize(nFound);
574 for (index in2n = 0; in2n < nFound; in2n++)
575 {
576 MatC[iN][in2n] = std::make_pair(index(idxFound[in2n]), real(fBasis(in2n)));
577 // idxFound[in2n] is a global indexing!
578 auto [search_good, rank, val] = boundInterpCoo.trans.pLGlobalMapping->search(idxFound[in2n]);
580 if (rank != mpi.rank)
582 }
583
584 boundInterpCoefRHS[iN] = (*boundInterpVal.father)[iN];
585 boundInterpCoef[iN].setZero(); // init val
586 // if(mpi.rank == 0)
587 // {
588 // std::cout << "mat row at " << cooC.transpose() << "R = " << RRBF << "\n";
589 // for (auto idx : idxFound)
590 // std::cout << idx << " ";
591 // std::cout << "\n"
592 // << fBasis.transpose() << "\n"
593 // << outDists.transpose() << "\n"
594 // << boundInterpCoefRHS[iN].transpose() << std::endl;
595 // }
596 }
597
598 // NOLINTNEXTLINE(readability-simplify-boolean-expr): disabled code block
599 if (false)
600 { // use superlu_dist to solve
601
602 // gridinfo_t grid;
603 // superlu_gridinit(mpi.comm, 1, mpi.size, &grid);
604 // DNDS_assert(grid.iam < mpi.size && grid.iam >= 0);
605
606 // std::vector<double> nzval, b;
607 // std::vector<int_t> colind;
608 // std::vector<int_t> rowptr;
609 // rowptr.resize(boundInterpLocSize + 1);
610 // rowptr[0] = 0;
611 // for (index i = 0; i < boundInterpLocSize; i++)
612 // rowptr[i + 1] = rowptr[i] + MatC[i].size();
613 // colind.resize(rowptr.back());
614 // nzval.resize(rowptr.back());
615 // for (index i = 0; i < boundInterpLocSize; i++)
616 // for (index i2j = 0; i2j < MatC[i].size(); i2j++)
617 // {
618 // colind[rowptr[i] + i2j] = MatC[i][i2j].first;
619 // nzval[rowptr[i] + i2j] = MatC[i][i2j].second;
620 // }
621 // b.resize(boundInterpLocSize * 3);
622 // for (index i = 0; i < boundInterpLocSize; i++)
623 // {
624 // b[boundInterpLocSize * 0 + i] = boundInterpCoefRHS[i](0);
625 // b[boundInterpLocSize * 1 + i] = boundInterpCoefRHS[i](1);
626 // b[boundInterpLocSize * 2 + i] = boundInterpCoefRHS[i](2);
627 // }
628
629 // SuperMatrix Aloc;
630 // dCreate_CompRowLoc_Matrix_dist(
631 // &Aloc, boundInterpGlobSize, boundInterpGlobSize, nzval.size(), boundInterpLocSize,
632 // boundInterpCoo.trans.pLGlobalMapping->operator()(mpi.rank, 0), nzval.data(), colind.data(), rowptr.data(),
633 // SLU_NR_loc, SLU_D, SLU_GE);
634
635 // superlu_dist_options_t options;
636 // set_default_options_dist(&options);
637 // SuperLUStat_t stat;
638 // PStatInit(&stat);
639 // dScalePermstruct_t ScalePermstruct;
640 // dScalePermstructInit(Aloc.nrow, Aloc.ncol, &ScalePermstruct);
641 // dLUstruct_t LUstruct;
642 // dLUstructInit(Aloc.ncol, &LUstruct);
643 // dSOLVEstruct_t SOLVEstruct;
644
645 // tPoint berr;
646 // berr.setZero();
647 // int info;
648
649 // pdgssvx(&options, &Aloc, &ScalePermstruct,
650 // b.data(), boundInterpLocSize, 3,
651 // &grid, &LUstruct, &SOLVEstruct, berr.data(), &stat, &info);
652 // for (index i = 0; i < boundInterpLocSize; i++)
653 // {
654 // boundInterpCoef[i](0) = b[boundInterpLocSize * 0 + i];
655 // boundInterpCoef[i](1) = b[boundInterpLocSize * 1 + i];
656 // boundInterpCoef[i](2) = b[boundInterpLocSize * 2 + i];
657 // }
658 // PStatPrint(&options, &stat, &grid);
659 // DNDS_assert(info == 0);
660
661 // PStatFree(&stat);
662 // // TODO: sanitize with valgrind!
663 // // Destroy_CompRowLoc_Matrix_dist(&Aloc); // use vector for strorage, this causes double free
664 // SUPERLU_FREE(Aloc.Store);
665 // dScalePermstructFree(&ScalePermstruct);
666 // dDestroy_LU(Aloc.ncol, &grid, &LUstruct);
667 // dLUstructFree(&LUstruct);
668 // if (options.SolveInitialized)
669 // {
670 // dSolveFinalize(&options, &SOLVEstruct);
671 // }
672
673 // superlu_gridexit(&grid);
674 }
675 // NOLINTNEXTLINE(readability-simplify-boolean-expr): forced code path
676 if (true)
677 { // use GMRES to solve
678 boundInterpCoef.father->createGlobalMapping();
679 boundInterpCoef.TransAttach();
680 boundInterpCoef.trans.createGhostMapping(boundInterpPullingIndexSolving);
681 boundInterpCoef.trans.createMPITypes();
682 boundInterpCoef.trans.initPersistentPull();
683
684 boundInterpCoefRHS.TransAttach();
685 boundInterpCoefRHS.trans.BorrowGGIndexing(boundInterpCoef.trans);
686 boundInterpCoefRHS.trans.createMPITypes();
687 boundInterpCoefRHS.trans.initPersistentPull();
688
689 for (index iN = 0; iN < boundInterpLocSize; iN++)
690 {
691 for (index in2n = 0; in2n < MatC[iN].size(); in2n++)
692 {
693 // MatC[iN][in2n].first;
694 auto [search_good, rank, val] = boundInterpCoef.trans.pLGhostMapping->search_indexAppend(MatC[iN][in2n].first);
696 DNDS_assert(val < boundInterpCoef.Size());
697 MatC[iN][in2n].first = val; // to local
698 }
699 }
700 Eigen::SparseMatrix<real> M(boundInterpLocSize, boundInterpLocSize);
701 M.reserve(Eigen::Vector<index, Eigen::Dynamic>::Constant(boundInterpLocSize, elevationInfo.nSearch));
702 for (index iN = 0; iN < boundInterpLocSize; iN++)
703 {
704 for (index in2n = 0; in2n < MatC[iN].size(); in2n++)
705 {
707 M.insert(iN, MatC[iN][in2n].first) = MatC[iN][in2n].second;
708 // ,
709 // std::cout << fmt::format("rank {}, inserting {},{}={}", mpi.rank, iN, MatC[iN][in2n].first, MatC[iN][in2n].second) << std::endl;
710 }
711 }
712 Eigen::SparseLU<Eigen::SparseMatrix<real>, Eigen::COLAMDOrdering<int>> LUSolver;
714 {
715 LUSolver.analyzePattern(M);
716 LUSolver.factorize(M); // full LU perconditioner
717 }
718
720 5,
721 [&](CoordPairDOF &v)
722 {
723 v.InitPair("ElevatedNodesSolveInternalSmoothV1::v", mpi);
724 v.father->Resize(boundInterpCoef.father->Size());
725 v.TransAttach();
726 v.trans.BorrowGGIndexing(boundInterpCoef.trans);
727 v.trans.createMPITypes();
728 v.trans.initPersistentPull();
729 });
730 boundInterpCoef.trans.startPersistentPull();
731 boundInterpCoef.trans.waitPersistentPull();
732 boundInterpCoefRHS.trans.startPersistentPull();
733 boundInterpCoefRHS.trans.waitPersistentPull();
734 gmres.solve(
736 {
737 x.trans.startPersistentPull();
738 x.trans.waitPersistentPull();
739 for (index iN = 0; iN < boundInterpLocSize; iN++)
740 {
741 Ax[iN].setZero();
742 for (index in2n = 0; in2n < MatC[iN].size(); in2n++)
743 {
744 Ax[iN] += x[MatC[iN][in2n].first] * MatC[iN][in2n].second;
745 }
746 }
747 Ax.trans.startPersistentPull();
748 Ax.trans.waitPersistentPull();
749 },
751 {
752 x.trans.startPersistentPull();
753 x.trans.waitPersistentPull();
754 Eigen::Matrix<real, Eigen::Dynamic, 3> xVal;
755 xVal.resize(boundInterpLocSize, 3);
756 for (index iN = 0; iN < boundInterpLocSize; iN++)
757 xVal(iN, EigenAll) = x[iN].transpose();
758 Eigen::Matrix<real, Eigen::Dynamic, 3> xValPrec;
760 xValPrec = LUSolver.solve(xVal);
761 for (index iN = 0; iN < boundInterpLocSize; iN++)
762 MLX[iN] = xValPrec(iN, EigenAll).transpose();
763 MLX = x;
764 MLX.trans.startPersistentPull();
765 MLX.trans.waitPersistentPull();
766 },
767 [&](CoordPairDOF &a, CoordPairDOF &b) -> real
768 {
769 return a.dot(b);
770 },
773 elevationInfo.nIter,
774 [&](int iRestart, real res, real resB)
775 {
776 if (mpi.rank == mRank)
777 log() << fmt::format("iRestart {}, res {}, resB {}", iRestart, res, resB) << std::endl;
778 return res < resB * 1e-6;
779 }
780
781 );
782 boundInterpCoef.trans.startPersistentPull();
783 boundInterpCoef.trans.waitPersistentPull();
784 }
785
786 boundInterpCoef.father->createGlobalMapping();
787 boundInterpCoef.TransAttach();
788 boundInterpCoef.trans.createGhostMapping(boundInterpPullIdx);
789 boundInterpCoef.trans.createMPITypes();
790 boundInterpCoef.trans.pullOnce();
791
792 boundInterpR.TransAttach();
793 boundInterpR.trans.BorrowGGIndexing(boundInterpCoo.trans);
794 boundInterpR.trans.createMPITypes();
795 boundInterpR.trans.pullOnce();
796
797 // if (mpi.rank == mRank)
798 // {
799 // for (index iN = 0; iN < boundInterpGlobSize; iN++)
800 // {
801 // std::cout << "pt - coef: R " << boundInterpR.son->operator()(iN, 0)
802 // << " coo " << boundInterpCoo.son->operator[](iN).transpose()
803 // << " -> " << boundInterpCoef.son->operator[](iN).transpose() << std::endl;
804 // }
805 // }
806
808 insidePts.pts.reserve(coords.father->Size() - boundInterpLocSize);
809 std::vector<index> insideNodes;
810 insideNodes.reserve(insidePts.pts.size());
811 for (index iN = 0; iN < coords.father->Size(); iN++)
812 if (!nodesBoundInterpolated.count(iN))
813 insidePts.pts.push_back(coords[iN]), insideNodes.push_back(iN), coordsElevDisp[iN].setZero();
815 for (index iN = 0; iN < boundInterpGlobSize; iN++)
816 {
817
818 tPoint cooC = (*boundInterpCoo.son)[iN];
819#if NANOFLANN_VERSION < 0x150
820 std::vector<std::pair<DNDS::index, DNDS::real>> IndicesDists;
821#else
822 std::vector<nanoflann::ResultItem<DNDS::index, DNDS::real>> IndicesDists;
823#endif
824 IndicesDists.reserve(elevationInfo.nSearch * 5);
825 real RRBF = boundInterpR.son->operator()(iN, 0);
826#if NANOFLANN_VERSION < 0x150
827 nanoflann::SearchParams params{}; // default params
828#else
829 nanoflann::SearchParameters params{};
830#endif
831 index nFound = nodesDstTree.radiusSearch(cooC.data(), RRBF, IndicesDists, params);
832 Eigen::Vector<real, Eigen::Dynamic> outDists;
833 outDists.resize(IndicesDists.size());
834 DNDS_assert(RRBF > 0);
835 for (index i = 0; i < IndicesDists.size(); i++)
836 outDists[i] = std::sqrt(IndicesDists[i].second) / std::pow(RRBF, 1. / elevationInfo.RBFPower);
837
839 for (index i = 0; i < IndicesDists.size(); i++)
840 {
842 fBasis(i, 0) *
843 boundInterpCoef.son->operator[](iN);
844 }
845 }
846
847 // for (index iN = 0; iN < coords.father->Size(); iN++)
848 // {
849 // if (nodesBoundInterpolated.count(iN))
850 // continue;
851
852 // index nFind = elevationInfo.nSearch * 5; // for safety
853 // tPoint cooC = coords[iN];
854
855 // std::vector<index> idxFound;
856 // Eigen::Vector<real, Eigen::Dynamic> outDistancesSqr;
857 // idxFound.resize(nFind);
858 // outDistancesSqr.resize(nFind);
859
860 // index nFound = bndInterpTree.knnSearch(cooC.data(), nFind, idxFound.data(), outDistancesSqr.data());
861 // DNDS_assert(nFound >= 1);
862 // idxFound.resize(nFound);
863 // outDistancesSqr.resize(nFound);
864
865 // Eigen::Vector<real, Eigen::Dynamic> foundRRBFs;
866 // foundRRBFs.resize(nFound);
867 // tSmallCoords coefsC;
868 // coefsC.resize(3, nFound);
869 // for (index iF = 0; iF < nFound; iF++)
870 // foundRRBFs[iF] = (*boundInterpR.son)(idxFound[iF], 0),
871 // coefsC(EigenAll, iF) = (*boundInterpCoef.son)[idxFound[iF]].transpose();
872 // Eigen::Vector<real, Eigen::Dynamic> outDists = outDistancesSqr.array().sqrt() / foundRRBFs.array();
873 // auto fBasis = RBF::FRBFBasis(outDists, elevationInfo.kernel);
874 // tPoint dispC = coefsC * fBasis;
875 // coordsElevDisp[iN] = dispC;
876 // }
877
878 coordsElevDisp.trans.startPersistentPull();
879 coordsElevDisp.trans.waitPersistentPull();
880
881 for (index iN = 0; iN < coords.father->Size(); iN++)
882 {
883 // if(dim == 2)
884 // dispO2[iN](2) = 0;
885 // if(dispO2[iN].norm() != 0)
886 // std::cout << dispO2[iN].transpose() << std::endl;
887 if (coordsElevDisp[iN](0) != largeReal)
889 }
890 coords.trans.pullOnce();
891 }
892
893 // ElevatedNodesSolveInternalSmoothV2 is in Mesh_Elevation_SmoothV2.cpp
894
895} // namespace DNDS::Geom
Batch of uniform-sized Eigen matrices per row, with variable batch count.
Pre-compiled-header style shim that includes the heavy Eigen headers under DNDSR's warning suppressio...
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:112
#define DNDS_MPI_InsertCheck(mpi, info)
Debug helper: barrier + print "CHECK <info> RANK r @ fn @ file:line".
Definition MPI.hpp:343
MatrixXR RBFCPC2(Tcent cent, Tx xs, real R, RBFKernelType kernel=Gaussian)
MatrixXR FRBFBasis(TIn RiXj, RBFKernelType kernel)
MatrixXR RBFInterpolateSolveCoefsNoPoly(const tSmallCoords &xs, const TF fs, real R, RBFKernelType kernel=Gaussian)
Eigen::Vector3d tPoint
Definition Geometric.hpp:9
DNDS::ArrayPair< DNDS::ArrayEigenVector< 3 > > tCoordPair
Eigen::Matrix< real, 3, Eigen::Dynamic > tSmallCoords
Definition Geometric.hpp:50
std::optional< SmoothSolverSetup > PrepareSmoothSolverSetup(UnstructuredMesh &mesh)
Common preamble for all smooth solver variants.
decltype(tCoordPair::father) tCoord
MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Allreduce.
Definition MPI.cpp:202
DNDS_CONSTANT const real UnInitReal
Sentinel "not initialised" real value (NaN). Cheap to detect with std::isnan or IsUnInitReal; survive...
Definition Defines.hpp:179
Eigen::Matrix< real, Eigen::Dynamic, Eigen::Dynamic > MatrixXR
Column-major dynamic Eigen matrix of reals (default layout).
Definition Defines.hpp:210
DNDS_CONSTANT const real largeReal
Loose upper bound (e.g., for non-dimensional limits).
Definition Defines.hpp:197
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
constexpr T sqr(const T &a)
a * a, constexpr. Works for all arithmetic types.
Definition Defines.hpp:522
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Definition Defines.hpp:110
std::ostream & log()
Return the current DNDSR log stream (either std::cout or the installed file).
Definition Defines.cpp:50
const MPI_Datatype DNDS_MPI_REAL
MPI datatype matching real (= MPI_REAL8).
Definition MPI.hpp:108
DNDS_DEVICE_CALLABLE index Size() const
Combined father + son row count.
Definition ArrayPair.hpp:33
Mutable device view onto an ArrayPair (for CUDA kernels).
ArrayPairDeviceView< B, DNDS::ArrayEigenVector< 3 > > t_deviceView
Device-view template alias: t_deviceView<DeviceBackend::CUDA> gives the mutable CUDA view type for th...
ssp< DNDS::ArrayEigenVector< 3 > > father
Owned-side array (must be resized before ghost setup).
index Size() const
Combined row count (father->Size() + son->Size()).
TTrans trans
Ghost-communication engine bound to father and son.
CoordPairDOF(const CoordPairDOF &)=default
CoordPairDOF & operator=(CoordPairDOF &&)=default
void addTo(CoordPairDOF &R, real alpha)
CoordPairDOF(CoordPairDOF &&)=default
CoordPairDOF & operator=(const CoordPairDOF &)=default
real kdtree_get_pt(const size_t idx, const size_t dim) const
std::unordered_set< index > nodesBoundInterpolated
UnstructuredMeshDeviceView< B > t_deviceView
Definition Mesh.hpp:1036
tCoordPair coordsElevDisp
only elevation
Definition Mesh.hpp:156
tCoordPair coords
reader
Definition Mesh.hpp:57
struct DNDS::Geom::UnstructuredMesh::ElevationInfo elevationInfo
int rank
This rank's 0-based index within comm (-1 until initialised).
Definition MPI.hpp:235
Eigen::Matrix< real, 5, 1 > v
tVec Ax(NCells)
tVec r(NCells)
tVec x(NCells)
tVec b(NCells)
real alpha
Eigen::Vector3d tPoint
DistributedHex3D mesh
DNDS::index idx