preCICE v3.2.0
Loading...
Searching...
No Matches
PetRadialBasisFctMapping.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef PRECICE_NO_PETSC
3
4#include <iterator>
5#include <map>
6#include <numeric>
7#include <vector>
8
10#include "logging/LogMacros.hpp"
13#include "math/math.hpp"
14#include "precice/impl/versions.hpp"
15#include "profiling/Event.hpp"
16#include "utils/IntraComm.hpp"
17#include "utils/Petsc.hpp"
18#include "utils/assertion.hpp"
19
21
22namespace precice::mapping {
23
24namespace {
25// VecChop was deprecated in PETSc 3.20 and is to be replaced by VecFilter
26inline PetscErrorCode PRECICE_VecFilter(Vec v, PetscReal tol)
27{
28#if ((PETSC_MAJOR > 3) || (PETSC_MAJOR == 3 && PETSC_MINOR >= 20))
29 return VecFilter(v, tol);
30#else
31 return VecChop(v, tol);
32#endif
33}
34} // namespace
35
36namespace tests {
37class PetRadialBasisFctMappingTest; // Forward declaration to friend the class
38}
39
50template <typename RADIAL_BASIS_FUNCTION_T>
51class PetRadialBasisFctMapping : public RadialBasisFctBaseMapping<RADIAL_BASIS_FUNCTION_T> {
52public:
66 Mapping::Constraint constraint,
67 int dimensions,
68 const RADIAL_BASIS_FUNCTION_T &function,
69 std::array<bool, 3> deadAxis,
70 double solverRtol = 1e-9,
71 Polynomial polynomial = Polynomial::SEPARATE);
72
75
77 void computeMapping() final override;
78
80 void clear() final override;
81
83 std::string getName() const final override;
84
85private:
87 void mapConservative(const time::Sample &inData, Eigen::VectorXd &outData) final override;
88
90 void mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) final override;
91
93 using VertexData = std::vector<std::vector<std::pair<int, double>>>;
94
95 mutable logging::Logger _log{"mapping::PetRadialBasisFctMapping"};
96
99
102
105
108
111
114
117
120
121 const double _solverRtol;
122
125
127 bool useRescaling = true;
128
131
134
137
139 void printMappingInfo(int dim) const;
140
143
146
148
155 void loadInitialGuessForDim(int dimension, int allDimensions, petsc::Vector &destination);
156
163 void storeInitialGuessForDim(int dimension, int allDimensions, petsc::Vector &source);
164};
165
166// --------------------------------------------------- HEADER IMPLEMENTATIONS
167
168template <typename RADIAL_BASIS_FUNCTION_T>
170 Mapping::Constraint constraint,
171 int dimensions,
172 const RADIAL_BASIS_FUNCTION_T &function,
173 std::array<bool, 3> deadAxis,
174 double solverRtol,
175 Polynomial polynomial)
176 : RadialBasisFctBaseMapping<RADIAL_BASIS_FUNCTION_T>(constraint, dimensions, function, deadAxis, Mapping::InitialGuessRequirement::Required),
177 _matrixC("C"),
178 _matrixQ("Q"),
179 _matrixA("A"),
180 _matrixV("V"),
181 _solver("Coefficient Solver"),
182 _QRsolver("QR Solver"),
183 _AOmapping(nullptr),
184 _solverRtol(solverRtol),
185 _polynomial(polynomial),
186 _commState(utils::Parallel::current())
187{
190 localPolyparams = (_commState->rank() > 0) ? 0 : polyparams;
191}
192
193template <typename RADIAL_BASIS_FUNCTION_T>
198
199template <typename RADIAL_BASIS_FUNCTION_T>
201{
203 precice::profiling::Event e("map.pet.computeMapping.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
204 precice::profiling::Event ePreCompute("map.pet.preComputeMapping.From" + this->input()->getName() + "To" + this->output()->getName());
205
206 clear();
207
208 PRECICE_DEBUG_IF(_polynomial == Polynomial::ON, "Using integrated polynomial.");
209 PRECICE_DEBUG_IF(_polynomial == Polynomial::OFF, "Using no polynomial.");
210 PRECICE_DEBUG_IF(_polynomial == Polynomial::SEPARATE, "Using separated polynomial.");
211
212 PRECICE_ASSERT(this->input()->getDimensions() == this->output()->getDimensions(),
213 this->input()->getDimensions(), this->output()->getDimensions());
214 int const dimensions = this->input()->getDimensions();
215 mesh::PtrMesh inMesh;
216 mesh::PtrMesh outMesh;
218 inMesh = this->output();
219 outMesh = this->input();
220 } else {
221 inMesh = this->input();
222 outMesh = this->output();
223 }
224
225 // Indizes that are used to build the Petsc AO mapping
226 std::vector<PetscInt> myIndizes;
227
228 // Indizes for Q^T, holding the polynomial
229 if (_commState->rank() <= 0) // Rank 0 or not in IntraComm mode
230 for (size_t i = 0; i < polyparams; i++)
231 myIndizes.push_back(i); // polyparams reside in the first rows (which are always on rank 0)
232
233 // Indizes for the vertices with polyparams offset
234 for (const mesh::Vertex &v : inMesh->vertices())
235 if (v.isOwner())
236 myIndizes.push_back(v.getGlobalIndex() + polyparams);
237
238 auto n = myIndizes.size(); // polyparams, if on rank 0, are included here
239
240 auto outputSize = outMesh->nVertices();
241
242 PetscErrorCode ierr = 0;
243 PRECICE_DEBUG("inMesh->nVertices() = {}", inMesh->nVertices());
244 PRECICE_DEBUG("outMesh->nVertices() = {}", outMesh->nVertices());
245 ePreCompute.stop();
246
247 precice::profiling::Event eCreateMatrices("map.pet.createMatrices.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
248
249 // Matrix C: Symmetric, sparse matrix with n x n local size.
250 _matrixC.init(n, n, PETSC_DETERMINE, PETSC_DETERMINE, MATSBAIJ);
251 PRECICE_DEBUG("Set matrix C to local size {0} x {0}", n);
252 ierr = MatSetOption(_matrixC, MAT_SYMMETRIC, PETSC_TRUE);
253 CHKERRV(ierr);
254 ierr = MatSetOption(_matrixC, MAT_SYMMETRY_ETERNAL, PETSC_TRUE);
255 CHKERRV(ierr);
256
257 // Matrix Q: Dense, holds the input mesh for the polynomial if set to SEPARATE. Zero size otherwise
258 _matrixQ.init(n, PETSC_DETERMINE, PETSC_DETERMINE, sepPolyparams, MATDENSE);
259 PRECICE_DEBUG("Set matrix Q to local size {} x {}", n, sepPolyparams);
260
261 // Matrix V: Dense, holds the output mesh for polynomial if set to SEPARATE. Zero size otherwise
262 _matrixV.init(outputSize, PETSC_DETERMINE, PETSC_DETERMINE, sepPolyparams, MATDENSE);
263 PRECICE_DEBUG("Set matrix V to local size {} x {}", outputSize, sepPolyparams);
264
265 // Matrix A: Sparse matrix with outputSize x n local size.
266 _matrixA.init(outputSize, n, PETSC_DETERMINE, PETSC_DETERMINE, MATAIJ);
267 PRECICE_DEBUG("Set matrix A to local size {} x {}", outputSize, n);
268
269 eCreateMatrices.stop();
270 precice::profiling::Event eAO("map.pet.AO.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
271
272 auto const ownerRangeABegin = _matrixA.ownerRange().first;
273 auto const ownerRangeAEnd = _matrixA.ownerRange().second;
274
275 // A mapping from globalIndex -> local col/row
276 ierr = AOCreateMapping(_commState->comm, myIndizes.size(), myIndizes.data(), nullptr, &_AOmapping);
277 CHKERRV(ierr);
278
279 eAO.stop();
280
281 Eigen::VectorXd distance(dimensions);
282
283 // We do preallocating of the matrices C and A. That means we traverse the input data once, just
284 // to know where we have entries in the sparse matrix. This information petsc can use to
285 // preallocate the matrix. In the second phase we actually fill the matrix.
286
287 // Stores col -> value for each row;
288 VertexData vertexData = bgPreallocationMatrixC(inMesh);
289
290 // -- BEGIN FILL LOOP FOR MATRIX C --
291 PRECICE_DEBUG("Begin filling matrix C");
292 precice::profiling::Event eFillC("map.pet.fillC.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
293
294 // We collect entries for each row and set them blockwise using MatSetValues.
295 PetscInt const idxSize = std::max(_matrixC.getSize().second, _matrixQ.getSize().second);
296 std::vector<PetscInt> colIdx(idxSize); // holds the columns indices of the entries
297 std::vector<PetscScalar> rowVals(idxSize); // holds the values of the entries
298
299 int preallocRow = 0;
300 PetscInt row = _matrixC.ownerRange().first + localPolyparams;
301 for (const mesh::Vertex &inVertex : inMesh->vertices()) {
302 if (not inVertex.isOwner())
303 continue;
304
305 PetscInt colNum = 0; // holds the number of non-zero columns in current row
306
307 // -- SETS THE POLYNOMIAL PART OF THE MATRIX --
309 colIdx[colNum] = colNum;
310 rowVals[colNum++] = 1;
311
312 for (int dim = 0; dim < dimensions; dim++) {
313 if (not this->_deadAxis[dim]) {
314 colIdx[colNum] = colNum;
315 rowVals[colNum++] = inVertex.coord(dim);
316 }
317 }
318
319 // cols are always the first ones for the polynomial, no need to translate
321 ierr = MatSetValues(_matrixC, colNum, colIdx.data(), 1, &row, rowVals.data(), INSERT_VALUES);
322 CHKERRV(ierr);
323 } else if (_polynomial == Polynomial::SEPARATE) {
324 ierr = MatSetValues(_matrixQ, 1, &row, colNum, colIdx.data(), rowVals.data(), INSERT_VALUES);
325 CHKERRV(ierr);
326 }
327 colNum = 0;
328 }
329
330 // -- SETS THE COEFFICIENTS --
331 {
332 auto const &rowVertices = vertexData[preallocRow];
333 for (const auto &vertex : rowVertices) {
334 rowVals[colNum] = this->_basisFunction.evaluate(vertex.second);
335 colIdx[colNum++] = vertex.first;
336 }
337 ++preallocRow;
338 }
339 ierr = AOApplicationToPetsc(_AOmapping, colNum, colIdx.data());
340 CHKERRV(ierr);
341 ierr = MatSetValues(_matrixC, 1, &row, colNum, colIdx.data(), rowVals.data(), INSERT_VALUES);
342 CHKERRV(ierr);
343 ++row;
344 }
345 PRECICE_DEBUG("Finished filling Matrix C");
346 eFillC.stop();
347 // -- END FILL LOOP FOR MATRIX C --
348
349 // PETSc requires that all diagonal entries are set, even if set to zero.
350 _matrixC.assemble(MAT_FLUSH_ASSEMBLY);
351 auto zeros = petsc::Vector::allocate(_matrixC);
352 VecZeroEntries(zeros);
353 zeros.assemble();
354 // MatDiagonalSet(_matrixC, zeros, INSERT_VALUES);
355 MatDiagonalSet(_matrixC, zeros, ADD_VALUES);
356
357 // Begin assembly here, all assembly is ended at the end of this function.
358 ierr = MatAssemblyBegin(_matrixC, MAT_FINAL_ASSEMBLY);
359 CHKERRV(ierr);
360 ierr = MatAssemblyBegin(_matrixQ, MAT_FINAL_ASSEMBLY);
361 CHKERRV(ierr);
362
363 vertexData = bgPreallocationMatrixA(inMesh, outMesh);
364
365 // holds the columns indices of the entries
366 colIdx.resize(std::max(_matrixA.getSize().second, _matrixV.getSize().second));
367 // holds the values of the entries
368 rowVals.resize(std::max(_matrixA.getSize().second, _matrixV.getSize().second));
369
370 // -- BEGIN FILL LOOP FOR MATRIX A --
371 PRECICE_DEBUG("Begin filling matrix A.");
372 precice::profiling::Event eFillA("map.pet.fillA.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
373
374 for (PetscInt row = ownerRangeABegin; row < ownerRangeAEnd; ++row) {
375 mesh::Vertex const &oVertex = outMesh->vertex(row - _matrixA.ownerRange().first);
376
377 // -- SET THE POLYNOMIAL PART OF THE MATRIX --
380 PetscInt colNum = 0;
381
382 colIdx[colNum] = colNum;
383 rowVals[colNum++] = 1;
384
385 for (int dim = 0; dim < dimensions; dim++) {
386 if (not this->_deadAxis[dim]) {
387 colIdx[colNum] = colNum;
388 rowVals[colNum++] = oVertex.coord(dim);
389 }
390 }
391 ierr = MatSetValues(*m, 1, &row, colNum, colIdx.data(), rowVals.data(), INSERT_VALUES);
392 CHKERRV(ierr);
393 }
394
395 // -- SETS THE COEFFICIENTS --
396 PetscInt colNum = 0;
397
398 {
399 auto const &rowVertices = vertexData[row - ownerRangeABegin];
400 for (const auto &vertex : rowVertices) {
401 rowVals[colNum] = this->_basisFunction.evaluate(vertex.second);
402 colIdx[colNum++] = vertex.first;
403 }
404 }
405 ierr = AOApplicationToPetsc(_AOmapping, colNum, colIdx.data());
406 CHKERRV(ierr);
407 ierr = MatSetValues(_matrixA, 1, &row, colNum, colIdx.data(), rowVals.data(), INSERT_VALUES);
408 CHKERRV(ierr);
409 }
410 PRECICE_DEBUG("Finished filling Matrix A");
411 eFillA.stop();
412 // -- END FILL LOOP FOR MATRIX A --
413
414 precice::profiling::Event ePostFill("map.pet.postFill.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
415
416 ierr = MatAssemblyBegin(_matrixA, MAT_FINAL_ASSEMBLY);
417 CHKERRV(ierr);
418
419 ierr = MatAssemblyEnd(_matrixC, MAT_FINAL_ASSEMBLY);
420 CHKERRV(ierr);
421 ierr = MatAssemblyEnd(_matrixQ, MAT_FINAL_ASSEMBLY);
422 CHKERRV(ierr);
423 ierr = MatAssemblyEnd(_matrixA, MAT_FINAL_ASSEMBLY);
424 CHKERRV(ierr);
425 _matrixV.assemble();
426
427 ePostFill.stop();
428
429 precice::profiling::Event eSolverInit("map.pet.solverInit.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
430
431 // -- CONFIGURE SOLVER FOR POLYNOMIAL --
433 PC pc;
434 KSPGetPC(_QRsolver, &pc);
435 PCSetType(pc, PCNONE);
436 KSPSetType(_QRsolver, KSPLSQR);
437 KSPSetOperators(_QRsolver, _matrixQ, _matrixQ);
438 }
439
440 // -- CONFIGURE SOLVER FOR SYSTEM MATRIX --
441 KSPSetOperators(_solver, _matrixC, _matrixC);
442 CHKERRV(ierr);
443 // The fourth argument defines the divergence tolerance, i.e. the tolerance, for which the linear system is
444 // considered as diverged and the computation is aborted. The PETSC_DEFAULT value is 1e9. However, the
445 // divergence residual is scaled using the RHS b of the system. In case the RHS is (still nonzero) very small
446 // and we have a (bad) nonzero initial guess x as defined below, the divergence tolerance might be exceeded
447 // in the first iteration, although the system could be solved in the usual way. This behavior is essentially
448 // a glitch in the divergence check. Therefore, we select a very high value (1e30) in order to disable the
449 // divergence check. In practice, the check is very rarely needed with Krylov methods. According to the PETSc
450 // people, the rare use cases aim for a bad preconditioner, which is not even used in our configuration.
451 // Hence, we can disable the divergence check without concerns.
452 KSPSetTolerances(_solver, _solverRtol, PETSC_DEFAULT, 1e30, PETSC_DEFAULT);
453 KSPSetInitialGuessNonzero(_solver, PETSC_TRUE);
454 CHKERRV(ierr); // Reuse the results from the last iteration, held in the out vector.
455 KSPSetOptionsPrefix(_solver, "solverC_"); // s.t. options for only this solver can be set on the command line
456 KSPSetFromOptions(_solver);
457
458 eSolverInit.stop();
459
460 // if (totalNNZ > static_cast<size_t>(20*n)) {
461 // PRECICE_DEBUG("Using Cholesky decomposition as direct solver for dense matrix.");
462 // PC prec;
463 // KSPSetType(_solver, KSPPREONLY);
464 // KSPGetPC(_solver, &prec);
465 // PCSetType(prec, PCCHOLESKY);
466 // PCFactorSetShiftType(prec, MAT_SHIFT_NONZERO);
467 // }
468
469 // -- COMPUTE RESCALING COEFFICIENTS USING THE SYSTEM MATRIX C SOLVER --
471 precice::profiling::Event eRescaling("map.pet.computeRescaling.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
473 auto rescalingCoeffs = petsc::Vector::allocate(_matrixC);
474 VecSet(rhs, 1);
475 rhs.assemble();
476 if (_solver.solve(rhs, rescalingCoeffs) == petsc::KSPSolver::SolverResult::Converged) {
477 PRECICE_INFO("Using rescaling. {}", _solver.summaryFor(rhs));
478 } else {
479 PRECICE_WARN("Deactivating rescaling! {}", _solver.summaryFor(rhs));
480 useRescaling = false;
481 }
482
483 eRescaling.addData("Iterations", _solver.getIterationNumber());
484 ierr = MatCreateVecs(_matrixA, nullptr, &oneInterpolant.vector);
485 CHKERRV(ierr);
486 ierr = MatMult(_matrixA, rescalingCoeffs, oneInterpolant);
487 CHKERRV(ierr); // get the output of g(x) = 1
488 // set values close to zero to exactly 0.0, s.t. PointwiseDevide doesn't do division on these entries
489 ierr = PRECICE_VecFilter(oneInterpolant, 1e-6);
490 CHKERRV(ierr);
491 }
492
493 this->_hasComputedMapping = true;
494
495 PRECICE_DEBUG("Number of mallocs for matrix C = {}", _matrixC.getInfo(MAT_LOCAL).mallocs);
496 PRECICE_DEBUG("Non-zeros allocated / used / unused for matrix C = {} / {} / {}",
497 _matrixC.getInfo(MAT_LOCAL).nz_allocated, _matrixC.getInfo(MAT_LOCAL).nz_used, _matrixC.getInfo(MAT_LOCAL).nz_unneeded);
498 PRECICE_DEBUG("Number of mallocs for matrix A = {}", _matrixA.getInfo(MAT_LOCAL).mallocs);
499 PRECICE_DEBUG("Non-zeros allocated / used / unused for matrix A = {} / {} / {}",
500 _matrixA.getInfo(MAT_LOCAL).nz_allocated, _matrixA.getInfo(MAT_LOCAL).nz_used, _matrixA.getInfo(MAT_LOCAL).nz_unneeded);
501}
502
503template <typename RADIAL_BASIS_FUNCTION_T>
505{
506 _matrixC.reset();
507 _matrixA.reset();
508 _matrixQ.reset();
509 _matrixV.reset();
510
511 _solver.reset();
512 _QRsolver.reset();
513
515
516 this->_hasComputedMapping = false;
517}
518
519template <typename RADIAL_BASIS_FUNCTION_T>
521{
522 return "global-iterative RBF";
523}
524
525template <typename RADIAL_BASIS_FUNCTION_T>
527{
528 // Only skip collectively over all MPI ranks as we use collective Vector ops
529 if (destination.getSize() == 0) {
530 return;
531 }
532 auto sizePerDim = destination.getLocalSize();
533 auto totalSize = sizePerDim * allDimensions;
534
535 if (!this->hasInitialGuess() && (totalSize > 0)) {
536 // We don't need to modify the petsc vector here
537 this->initialGuess() = Eigen::VectorXd::Zero(totalSize);
538 }
539
540 PRECICE_ASSERT(this->initialGuess().size() == totalSize, this->initialGuess().size(), totalSize);
541 auto offset = dimension * sizePerDim;
542 auto begin = std::next(this->initialGuess().data(), offset);
543 destination.copyFrom({begin, static_cast<std::size_t>(sizePerDim)});
544}
545
546template <typename RADIAL_BASIS_FUNCTION_T>
548{
549 // Only skip collectively over all MPI ranks as we use collective Vector ops
550 if (source.getSize() == 0) {
551 return;
552 }
553 auto sizePerDim = source.getLocalSize();
554
555 PRECICE_ASSERT(this->hasInitialGuess() || (sizePerDim == 0), "Call loadInitialGuessForDim first");
556 PRECICE_ASSERT(this->initialGuess().size() == static_cast<Eigen::Index>(sizePerDim) * allDimensions, this->initialGuess().size(), static_cast<Eigen::Index>(sizePerDim) * allDimensions);
557 auto offset = dimension * sizePerDim;
558 auto begin = std::next(this->initialGuess().data(), offset);
559 source.copyTo({begin, static_cast<std::size_t>(sizePerDim)});
560}
561
562template <typename RADIAL_BASIS_FUNCTION_T>
564{
566 precice::profiling::Event e("map.pet.mapData.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
567
568 PetscErrorCode ierr = 0;
569 auto const &inValues = inData.values;
570 auto &outValues = outData;
571
572 int const valueDim = inData.dataDims;
574
575 auto out = petsc::Vector::allocate(_matrixA, "out");
576 auto in = petsc::Vector::allocate(_matrixC, "in");
577 auto a = petsc::Vector::allocate(_matrixQ, "a", petsc::Vector::RIGHT); // holds the solution of the LS polynomial
578
579 PetscScalar const *vecArray;
580
581 // For every data dimension, perform mapping
582 for (int dim = 0; dim < valueDim; dim++) {
583 printMappingInfo(dim);
584
585 // Fill input from input data values
587 inVals.reserve(this->input()->nVertices());
589 inIdx.reserve(this->input()->nVertices());
590 for (size_t i = 0; i < this->input()->nVertices(); ++i) {
591 if (not this->input()->vertex(i).isOwner())
592 continue;
593 inVals.emplace_back(inValues[i * valueDim + dim]);
594 inIdx.emplace_back(inIdx.size() + in.ownerRange().first + localPolyparams);
595 }
596 ierr = VecSetValues(in, inIdx.size(), inIdx.data(), inVals.data(), INSERT_VALUES);
597 CHKERRV(ierr);
598 in.assemble();
599
601 switch (_QRsolver.solve(in, a)) {
603 PRECICE_DEBUG("The polynomial QR system of the RBF mapping from mesh {} to mesh {} converged. {}",
604 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(in));
605 break;
607 PRECICE_WARN("The polynomial QR system of the RBF mapping from mesh {} to mesh {} has not converged. "
608 "This means most probably that the mapping problem is not well-posed or your relative tolerance is too conservative. "
609 "Please check if your coupling meshes are correct. "
610 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
611 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(in));
612 break;
614 KSPView(_QRsolver, PETSC_VIEWER_STDOUT_WORLD);
615 PRECICE_ERROR("The polynomial QR system of the RBF mapping from mesh {} to mesh {} has diverged. "
616 "This means most probably that the mapping problem is not well-posed. "
617 "Please check if your coupling meshes are correct. "
618 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
619 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(in));
620 break;
621 }
622
623 VecScale(a, -1);
624 // in = Q * a + in
625 MatMultAdd(_matrixQ, a, in, in); // Subtract the polynomial from the input values
626 }
627
629 loadInitialGuessForDim(dim, valueDim, p);
630
631 profiling::Event eSolve("map.pet.solveConsistent.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
632 const auto solverResult = _solver.solve(in, p);
633 eSolve.addData("Iterations", _solver.getIterationNumber());
634 eSolve.stop();
635
636 storeInitialGuessForDim(dim, valueDim, p);
637
638 switch (solverResult) {
640 PRECICE_DEBUG("The linear system of the RBF mapping from mesh {} to mesh {} converged. {}",
641 this->input()->getName(), this->output()->getName(), _solver.summaryFor(in));
642 break;
644 PRECICE_WARN("The linear system of the RBF mapping from mesh {} to mesh {} has not converged. "
645 "This means most probably that the mapping problem is not well-posed or your relative tolerance is too conservative. "
646 "Please check if your coupling meshes are correct. "
647 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
648 this->input()->getName(), this->output()->getName(), _solver.summaryFor(in));
649 break;
651 KSPView(_solver, PETSC_VIEWER_STDOUT_WORLD);
652 PRECICE_ERROR("The linear system of the RBF mapping from mesh {} to mesh {} has diverged. "
653 "This means most probably that the mapping problem is not well-posed. "
654 "Please check if your coupling meshes are correct. "
655 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
656 this->input()->getName(), this->output()->getName(), _solver.summaryFor(in));
657 break;
658 }
659
660 ierr = MatMult(_matrixA, p, out);
661 CHKERRV(ierr);
662
664 ierr = VecPointwiseDivide(out, out, oneInterpolant);
665 CHKERRV(ierr);
666 }
667
669 // scale it back to add the polynomial
670 ierr = VecScale(a, -1);
671 // out = V * a + out
672 ierr = MatMultAdd(_matrixV, a, out, out);
673 CHKERRV(ierr);
674 }
675 PRECICE_VecFilter(out, 1e-9);
676
677 // Copy mapped data to output data values
678 ierr = VecGetArrayRead(out, &vecArray);
679 CHKERRV(ierr);
680 int size = out.getLocalSize();
681 for (int i = 0; i < size; i++) {
682 outValues[i * valueDim + dim] = vecArray[i];
683 }
684
685 VecRestoreArrayRead(out, &vecArray);
686 }
687}
688
689template <typename RADIAL_BASIS_FUNCTION_T>
691{
693 precice::profiling::Event e("map.pet.mapData.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
694
695 PetscErrorCode ierr = 0;
696 auto const &inValues = inData.values;
697 auto &outValues = outData;
698
699 int const valueDim = inData.dataDims;
701
703 auto in = petsc::Vector::allocate(_matrixA, "in");
704 int inRangeStart, inRangeEnd;
705 std::tie(inRangeStart, inRangeEnd) = in.ownerRange();
706 for (int dim = 0; dim < valueDim; dim++) {
707 printMappingInfo(dim);
708
709 // Fill input from input data values
710 for (size_t i = 0; i < this->input()->nVertices(); i++) {
711 auto const globalIndex = this->input()->vertex(i).getGlobalIndex(); // globalIndex is target row
712 if (globalIndex >= inRangeStart and globalIndex < inRangeEnd) // only fill local rows
713 ierr = VecSetValue(in, globalIndex, inValues[i * valueDim + dim], INSERT_VALUES);
714 CHKERRV(ierr);
715 }
716 in.assemble();
717
718 // Gets the petsc::vector for the given combination of outputData, inputData and dimension
720
722 auto epsilon = petsc::Vector::allocate(_matrixV, "epsilon", petsc::Vector::RIGHT);
723 // epsilon = V^T * in
724 ierr = MatMultTranspose(_matrixV, in, epsilon);
725 CHKERRV(ierr);
727 ierr = MatMultTranspose(_matrixA, in, eta);
728 CHKERRV(ierr);
730 loadInitialGuessForDim(dim, valueDim, mu);
731 _solver.solve(eta, mu);
732 storeInitialGuessForDim(dim, valueDim, mu);
733 VecScale(epsilon, -1);
735 // tau = Q^T * mu + epsilon
736 ierr = MatMultTransposeAdd(_matrixQ, mu, epsilon, tau);
737 CHKERRV(ierr);
739
740 switch (_QRsolver.solveTranspose(tau, sigma)) {
742 PRECICE_DEBUG("The polynomial linear system of the RBF mapping from mesh {} to mesh {} converged. {}",
743 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(tau));
744 break;
746 PRECICE_WARN("The polynomial linear system of the RBF mapping from mesh {} to mesh {} has not converged. "
747 "This means most probably that the mapping problem is not well-posed or your relative tolerance is too conservative. "
748 "Please check if your coupling meshes are correct. "
749 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
750 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(tau));
751 break;
753 KSPView(_QRsolver, PETSC_VIEWER_STDOUT_WORLD);
754 PRECICE_ERROR("The polynomial linear system of the RBF mapping from mesh {} to mesh {} "
755 "has diverged. This means most probably that the mapping problem is not well-posed. "
756 "Please check if your coupling meshes are correct. "
757 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
758 this->input()->getName(), this->output()->getName(), _QRsolver.summaryFor(tau));
759 break;
760 }
761 // out = alpha * sigma + mu.
762 VecWAXPY(out, -1, sigma, mu);
763 } else {
764 ierr = MatMultTranspose(_matrixA, in, au);
765 CHKERRV(ierr);
766
767 loadInitialGuessForDim(dim, valueDim, out);
768
769 profiling::Event eSolve("map.pet.solveConservative.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
770 const auto solverResult = _solver.solve(au, out);
771 eSolve.addData("Iterations", _solver.getIterationNumber());
772 eSolve.stop();
773
774 storeInitialGuessForDim(dim, valueDim, out);
775
776 switch (solverResult) {
778 PRECICE_DEBUG("The linear system of the RBF mapping from mesh {} to mesh {} converged. {}",
779 this->input()->getName(), this->output()->getName(), _solver.summaryFor(au));
780 break;
782 PRECICE_WARN("The linear system of the RBF mapping from mesh {} to mesh {} has not converged. "
783 "This means most probably that the mapping problem is not well-posed or your relative tolerance is too conservative. "
784 "Please check if your coupling meshes are correct. "
785 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
786 this->input()->getName(), this->output()->getName(), _solver.summaryFor(au));
787 break;
789 KSPView(_solver, PETSC_VIEWER_STDOUT_WORLD);
790 PRECICE_ERROR("The linear system of the RBF mapping from mesh {} to mesh {} "
791 "has diverged. This means most probably that the mapping problem is not well-posed. "
792 "Please check if your coupling meshes are correct. "
793 "Maybe you need to fix axis-aligned mapping setups by marking perpendicular axes as dead? {}",
794 this->input()->getName(), this->output()->getName(), _solver.summaryFor(au));
795 break;
796 }
797 }
798
799 PRECICE_VecFilter(out, 1e-9);
800
801 // Copy mapped data to output data values
802 const PetscScalar *outArray;
803 VecGetArrayRead(out, &outArray);
804
805 int count = 0, ownerCount = 0;
806 for (const mesh::Vertex &vertex : this->output()->vertices()) {
807 if (vertex.isOwner()) {
808 outValues[count * valueDim + dim] = outArray[ownerCount + localPolyparams];
809 ownerCount++;
810 } else {
811 PRECICE_ASSERT(outValues[count * valueDim + dim] == 0.0);
812 }
813 count++;
814 }
815 VecRestoreArrayRead(out, &outArray);
816 }
817}
818
819template <typename RADIAL_BASIS_FUNCTION_T>
821{
822 std::string constraintName;
824 constraintName = "consistent";
826 constraintName = "scaled-consistent-surface";
828 constraintName = "scaled-consistent-volume";
829 } else {
830 constraintName = "conservative";
831 }
832
833 const std::string polynomialName = _polynomial == Polynomial::ON ? "on" : _polynomial == Polynomial::OFF ? "off"
834 : "separate";
835
836 PRECICE_INFO("Mapping {} for dimension {} with polynomial set to {}",
837 constraintName, dim, polynomialName);
838}
839
840template <typename RADIAL_BASIS_FUNCTION_T>
843{
844 PRECICE_INFO("Using tree-based preallocation for matrix C");
845 precice::profiling::Event ePreallocC("map.pet.preallocC.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
846
847 PetscInt n;
848 std::tie(n, std::ignore) = _matrixC.getLocalSize();
849 std::vector<PetscInt> d_nnz(n), o_nnz(n);
850
851 const double supportRadius = this->_basisFunction.getSupportRadius();
852
853 const int dimensions = this->input()->getDimensions();
854 Eigen::VectorXd distance(dimensions);
855
856 PetscInt colOwnerRangeCBegin, colOwnerRangeCEnd;
857 std::tie(colOwnerRangeCBegin, colOwnerRangeCEnd) = _matrixC.ownerRangeColumn();
858
859 VertexData vertexData(n - localPolyparams);
860
861 size_t local_row = 0;
862 // -- PREALLOCATES THE POLYNOMIAL PART OF THE MATRIX --
864 for (local_row = 0; local_row < localPolyparams; local_row++) {
865 d_nnz[local_row] = colOwnerRangeCEnd - colOwnerRangeCBegin;
866 o_nnz[local_row] = _matrixC.getSize().first - d_nnz[local_row];
867 }
868 }
869
870 for (const mesh::Vertex &inVertex : inMesh->vertices()) {
871 if (not inVertex.isOwner())
872 continue;
873
874 PetscInt col = polyparams;
875 PetscInt const global_row = local_row + _matrixC.ownerRange().first;
876 d_nnz[local_row] = 0;
877 o_nnz[local_row] = 0;
878
879 // -- PREALLOCATES THE COEFFICIENTS --
880
881 for (auto const i : inMesh->index().getVerticesInsideBox(inVertex, supportRadius)) {
882 const mesh::Vertex &vj = inMesh->vertex(i);
883
884 PetscInt mappedCol = vj.getGlobalIndex() + polyparams;
885 AOApplicationToPetsc(_AOmapping, 1, &mappedCol); // likely not efficient in the inner loop
886
887 if (global_row > mappedCol) // Skip, since we are below the diagonal
888 continue;
889
890 distance = inVertex.getCoords() - vj.getCoords();
891 for (int d = 0; d < dimensions; d++)
892 if (this->_deadAxis[d])
893 distance[d] = 0;
894
895 double const norm = distance.norm();
896 if (supportRadius > norm or col == global_row) {
897 vertexData[local_row - localPolyparams].emplace_back(vj.getGlobalIndex() + polyparams, norm);
898 if (mappedCol >= colOwnerRangeCBegin and mappedCol < colOwnerRangeCEnd)
899 d_nnz[local_row]++;
900 else
901 o_nnz[local_row]++;
902 }
903 col++;
904 }
905 local_row++;
906 }
907
908 if (_commState->size() == 1) {
909 MatSeqSBAIJSetPreallocation(_matrixC, _matrixC.blockSize(), 0, d_nnz.data());
910 } else {
911 MatMPISBAIJSetPreallocation(_matrixC, _matrixC.blockSize(), 0, d_nnz.data(), 0, o_nnz.data());
912 }
913 MatSetOption(_matrixC, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
914
915 ePreallocC.stop();
916
917 return vertexData;
918}
919
920template <typename RADIAL_BASIS_FUNCTION_T>
923{
924 PRECICE_INFO("Using tree-based preallocation for matrix A");
925 precice::profiling::Event ePreallocA("map.pet.preallocA.From" + this->input()->getName() + "To" + this->output()->getName(), profiling::Synchronize);
926
927 PetscInt ownerRangeABegin, ownerRangeAEnd, colOwnerRangeABegin, colOwnerRangeAEnd;
928 PetscInt const outputSize = _matrixA.getLocalSize().first;
929 double const supportRadius = this->_basisFunction.getSupportRadius();
930
931 std::tie(ownerRangeABegin, ownerRangeAEnd) = _matrixA.ownerRange();
932 std::tie(colOwnerRangeABegin, colOwnerRangeAEnd) = _matrixA.ownerRangeColumn();
933 const int dimensions = this->input()->getDimensions();
934
935 std::vector<PetscInt> d_nnz(outputSize), o_nnz(outputSize);
936 Eigen::VectorXd distance(dimensions);
937
938 // Contains localRow<localCols<colPosition, distance>>>
939 VertexData vertexData(outputSize);
940
941 for (int localRow = 0; localRow < ownerRangeAEnd - ownerRangeABegin; ++localRow) {
942 d_nnz[localRow] = 0;
943 o_nnz[localRow] = 0;
944 PetscInt col = 0;
945 mesh::Vertex const &oVertex = outMesh->vertex(localRow);
946
947 // -- PREALLOCATE THE POLYNOM PART OF THE MATRIX --
948 // col does not need mapping here, because the first polyparams col are always identity mapped
950 if (col >= colOwnerRangeABegin and col < colOwnerRangeAEnd)
951 d_nnz[localRow]++;
952 else
953 o_nnz[localRow]++;
954 col++;
955
956 for (int dim = 0; dim < dimensions; dim++) {
957 if (not this->_deadAxis[dim]) {
958 if (col >= colOwnerRangeABegin and col < colOwnerRangeAEnd)
959 d_nnz[localRow]++;
960 else
961 o_nnz[localRow]++;
962 col++;
963 }
964 }
965 }
966
967 // -- PREALLOCATE THE COEFFICIENTS --
968 for (auto i : inMesh->index().getVerticesInsideBox(oVertex, supportRadius)) {
969 const mesh::Vertex &inVertex = inMesh->vertex(i);
970 distance = oVertex.getCoords() - inVertex.getCoords();
971
972 for (int d = 0; d < dimensions; d++)
973 if (this->_deadAxis[d])
974 distance[d] = 0;
975
976 double const norm = distance.norm();
977 if (supportRadius > norm) {
978 col = inVertex.getGlobalIndex() + polyparams;
979 vertexData[localRow].emplace_back(col, norm);
980
981 AOApplicationToPetsc(_AOmapping, 1, &col);
982 if (col >= colOwnerRangeABegin and col < colOwnerRangeAEnd)
983 d_nnz[localRow]++;
984 else
985 o_nnz[localRow]++;
986 }
987 }
988 }
989 if (_commState->size() == 1) {
990 MatSeqAIJSetPreallocation(_matrixA, 0, d_nnz.data());
991 } else {
992 MatMPIAIJSetPreallocation(_matrixA, 0, d_nnz.data(), 0, o_nnz.data());
993 }
994 MatSetOption(_matrixA, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE);
995
996 ePreallocA.stop();
997 return vertexData;
998}
999
1000} // namespace precice::mapping
1001
1002#endif // PRECICE_NO_PETSC
#define PRECICE_ERROR(...)
Definition LogMacros.hpp:16
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_DEBUG_IF(condition,...)
Definition LogMacros.hpp:63
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_INFO(...)
Definition LogMacros.hpp:14
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
Utility class for managing MPI operations.
Definition Parallel.hpp:24
Abstract base class for mapping of data from one mesh to another.
Definition Mapping.hpp:16
mesh::PtrMesh output() const
Returns pointer to output mesh.
Definition Mapping.cpp:92
Constraint
Specifies additional constraints for a mapping.
Definition Mapping.hpp:30
mesh::PtrMesh input() const
Returns pointer to input mesh.
Definition Mapping.cpp:87
bool _hasComputedMapping
Flag to indicate whether computeMapping() has been called.
Definition Mapping.hpp:307
bool isScaledConsistent() const
Returns true if mapping is a form of scaled consistent mapping.
Definition Mapping.cpp:258
bool hasInitialGuess() const
True if initialGuess().size() == 0.
Definition Mapping.cpp:56
const Eigen::VectorXd & initialGuess() const
Return the provided initial guess of a mapping using an initialGuess.
Definition Mapping.cpp:63
virtual bool hasConstraint(const Constraint &constraint) const
Checks whether the mapping has the given constraint or not.
Definition Mapping.cpp:248
InitialGuessRequirement
Specifies whether the mapping requires an initial guess.
Definition Mapping.hpp:64
void mapConservative(const time::Sample &inData, Eigen::VectorXd &outData) final override
VertexData bgPreallocationMatrixC(const mesh::PtrMesh inMesh)
Preallocate matrix C and saves the coefficients using a boost::geometry spatial tree for neighbor sea...
void printMappingInfo(int dim) const
Prints an INFO about the current mapping.
void mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) final override
PetRadialBasisFctMapping(Mapping::Constraint constraint, int dimensions, const RADIAL_BASIS_FUNCTION_T &function, std::array< bool, 3 > deadAxis, double solverRtol=1e-9, Polynomial polynomial=Polynomial::SEPARATE)
Constructor.
~PetRadialBasisFctMapping() override
Deletes the PETSc objects and the _deadAxis array.
void computeMapping() final override
Computes the mapping coefficients from the in- and output mesh.
std::string getName() const final override
VertexData bgPreallocationMatrixA(const mesh::PtrMesh inMesh, const mesh::PtrMesh outMesh)
void loadInitialGuessForDim(int dimension, int allDimensions, petsc::Vector &destination)
std::vector< std::vector< std::pair< int, double > > > VertexData
void storeInitialGuessForDim(int dimension, int allDimensions, petsc::Vector &source)
RadialBasisFctBaseMapping(Constraint constraint, int dimensions, const RADIAL_BASIS_FUNCTION_T &function, std::array< bool, 3 > deadAxis, InitialGuessRequirement mappingType)
Constructor.
int getPolynomialParameters() const
Computes the number of polynomial degrees of freedom based on the problem dimension and the dead axis...
std::vector< bool > _deadAxis
true if the mapping along some axis should be ignored
RADIAL_BASIS_FUNCTION_T _basisFunction
Radial basis function type used in interpolation.
Vertex of a mesh.
Definition Vertex.hpp:16
double coord(int index) const
Returns a coordinate of a vertex.
Definition Vertex.hpp:126
Eigen::VectorXd getCoords() const
Returns the coordinates of the vertex.
Definition Vertex.hpp:114
int getGlobalIndex() const
Globally unique index.
Definition Vertex.cpp:12
void stop()
Stops a running event.
Definition Event.cpp:51
void addData(std::string_view key, int value)
Adds named integer data, associated to an event.
Definition Event.cpp:62
std::shared_ptr< CommState > CommStatePtr
Definition Parallel.hpp:35
@ Stopped
The solver reached the maximum iterations.
Definition Petsc.hpp:263
Vector & copyTo(precice::span< double > destination)
Definition Petsc.cpp:339
PetscInt getLocalSize() const
Definition Petsc.cpp:273
static Vector allocate(const std::string &name="")
Allocates a new vector on the given MPI communicator.
Definition Petsc.cpp:206
Vector & copyFrom(precice::span< const double > source)
Definition Petsc.cpp:322
PetscInt getSize() const
Definition Petsc.cpp:264
STL class.
T data(T... args)
T emplace_back(T... args)
T max(T... args)
contains the logging framework.
contains data mapping from points to meshes.
Polynomial
How to handle the polynomial?
std::shared_ptr< Mesh > PtrMesh
static constexpr SynchronizeTag Synchronize
Convenience instance of the SynchronizeTag.
Definition Event.hpp:21
contains the time interpolation logic.
Definition Sample.hpp:8
PETSc related utilities.
Definition Petsc.cpp:81
void destroy(ISLocalToGlobalMapping *IS)
Destroys an ISLocalToGlobalMapping, if IS is not null and PetscIsInitialized.
Definition Petsc.cpp:756
contains precice-related utilities.
STL namespace.
T next(T... args)
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)
int dataDims
The dimensionality of the data.
Definition Sample.hpp:60
Eigen::VectorXd values
Definition Sample.hpp:64
T tie(T... args)