39#ifndef DUMUX_LINEAR_MUMPS_SOLVER_HH
40#define DUMUX_LINEAR_MUMPS_SOLVER_HH
56#include <dune/common/exceptions.hh>
57#include <dune/common/indices.hh>
58#include <dune/common/hybridutilities.hh>
59#include <dune/common/parallel/mpihelper.hh>
60#include <dune/istl/bvector.hh>
61#include <dune/istl/bcrsmatrix.hh>
62#include <dune/istl/solver.hh>
63#include <dune/istl/solvercategory.hh>
84template<
class LSTraits>
85struct MumpsGridTypes {
86 static constexpr bool hasGrid =
false;
88 struct DofMapper { std::size_t size()
const {
return 0; } };
91template<
class LSTraits>
92requires requires {
typename LSTraits::GridView;
typename LSTraits::DofMapper; }
93struct MumpsGridTypes<LSTraits> {
94 static constexpr bool hasGrid =
true;
95 using GridView =
typename LSTraits::GridView;
96 using DofMapper =
typename LSTraits::DofMapper;
103decltype(
auto) mumpsDeref(
const T& t)
105 if constexpr (
requires (
const T& x) { *x; })
119template<
class LSTraits,
class LATraits>
122 using Matrix =
typename LATraits::Matrix;
123 using XVector =
typename LATraits::Vector;
124 using BVector =
typename LATraits::Vector;
129 using GlobalIndex =
long long;
131 using GridTypes_ = Detail::MumpsGridTypes<LSTraits>;
132 using GridView =
typename GridTypes_::GridView;
133 using DofMapper =
typename GridTypes_::DofMapper;
134 static constexpr bool hasGrid = GridTypes_::hasGrid;
137 static constexpr bool isMultiType = isMultiTypeBlockVector<XVector>::value;
139 static constexpr std::size_t
blockSize = []()
constexpr -> std::size_t {
140 if constexpr (isMultiTypeBlockVector<XVector>::value)
return 1;
141 else return XVector::block_type::size();
146 using ScalarBlockVector = Dune::BlockVector<Dune::FieldVector<Scalar, 1>>;
152 std::size_t scalarBS = 1;
153 std::size_t nBlockLocal = 0;
154 std::size_t scalarOffset = 0;
155 bool rowsComplete =
true;
158 std::function<void(ScalarBlockVector&)> rhsPreprocess;
161 std::function<void(ScalarBlockVector&)> ghostBroadcast;
165 struct SolverResult :
public Dune::InverseOperatorResult
167 SolverResult() =
default;
168 SolverResult(
const Dune::InverseOperatorResult& o) : InverseOperatorResult(o) {}
169 operator bool()
const {
return this->converged; }
181 explicit DirectSolverMumps(
const std::string& paramGroup =
"")
182 : LinearSolver(paramGroup)
185 if constexpr (hasGrid)
186 if (Dune::MPIHelper::getCommunication().size() > 1)
187 DUNE_THROW(Dune::InvalidStateException,
"Using sequential constructor for parallel run. Use signature with gridView and dofMapper!");
189 mpiComm_ = MPI_COMM_SELF;
201 DirectSolverMumps(
const GridView& gridView,
202 const DofMapper& dofMapper,
203 const std::string& paramGroup =
"")
205 : LinearSolver(paramGroup)
206 , gridView_(gridView)
207 , mapper_(&dofMapper)
208 , isParallel_(Detail::
solverCategory<LSTraits>(gridView) != Dune::SolverCategory::sequential)
210 if constexpr (LSTraits::canCommunicate)
211 mpiComm_ =
static_cast<MPI_Comm
>(gridView.comm());
213 mpiComm_ = MPI_COMM_SELF;
217 buildGlobalDofIndices_(gridView, dofMapper);
226 template<
class GGTuple>
227 explicit DirectSolverMumps(
const GGTuple& ggTuple,
const std::string& paramGroup =
"")
228 requires (isMultiType && LSTraits::canCommunicate &&
requires { std::tuple_size<GGTuple>::value; })
229 : LinearSolver(paramGroup)
230 , gridView_(Detail::mumpsDeref(std::get<0>(ggTuple)).gridView())
231 , isParallel_(Detail::mumpsDeref(std::get<0>(ggTuple)).gridView().comm().size() > 1)
233 constexpr std::size_t numSubDomains = std::tuple_size_v<GGTuple>;
234 static_assert(numSubDomains == std::tuple_size_v<XVector>,
235 "Number of grid geometries must match the number of MultiTypeBlockVector subdomains");
237 mpiComm_ =
static_cast<MPI_Comm
>(Detail::mumpsDeref(std::get<0>(ggTuple)).gridView().comm());
241 buildMultiTypeMaps_(ggTuple);
254 DirectSolverMumps(
const DirectSolverMumps&) =
delete;
255 DirectSolverMumps& operator=(
const DirectSolverMumps&) =
delete;
262 void updateAfterGridAdaption(
const GridView& gridView,
const DofMapper& dofMapper)
265 gridView_ = gridView;
266 mapper_ = &dofMapper;
267 buildGlobalDofIndices_(gridView, dofMapper);
278 template<
class GGTuple>
279 void updateAfterGridAdaption(
const GGTuple& ggTuple)
280 requires (isMultiType && LSTraits::canCommunicate &&
requires { std::tuple_size<GGTuple>::value; })
282 gridView_ = Detail::mumpsDeref(std::get<0>(ggTuple)).gridView();
283 buildMultiTypeMaps_(ggTuple);
292 SolverResult
solve(
const Matrix& A, XVector& x,
const BVector& b)
303 void setMatrix(
const Matrix& A)
311 void setMatrix(std::shared_ptr<Matrix> A)
317 SolverResult
solve(XVector& x,
const BVector& b)
318 {
return solveColumns_({&x}, {&b}); }
324 SolverResult
solve(std::vector<XVector>& x,
const std::vector<BVector>& b)
326 std::vector<XVector*> xColumns;
327 std::vector<const BVector*> bColumns;
329 xColumns.push_back(&xj);
330 for (
const auto& bj : b)
331 bColumns.push_back(&bj);
332 return solveColumns_(xColumns, bColumns);
335 const Dune::InverseOperatorResult& result()
const
340 if constexpr (isMultiType)
342 if (!isParallel_ || localToGlobal_.empty())
344 auto y = VectorConverter<XVector>::multiTypeToBlockVector(x);
348 auto v = VectorConverter<XVector>::multiTypeToBlockVector(x);
349 for (
const auto& sub : subs_)
350 if (sub.rhsPreprocess)
351 sub.rhsPreprocess(v);
353 double localSumSq = 0.0;
354 for (std::size_t r = 0; r < v.size(); ++r)
356 localSumSq += v[r][0] * v[r][0];
358 double globalSumSq = 0.0;
359 MPI_Allreduce(&localSumSq, &globalSumSq, 1, MPI_DOUBLE, MPI_SUM, mpiComm_);
361 return sqrt(globalSumSq);
365 if (!isParallel_ || localToGlobal_.empty())
369 if constexpr (LSTraits::canCommunicate)
371 if (LSTraits::isNonOverlapping(*gridView_))
374 if constexpr (
requires { LSTraits::dofCodims; })
376 MultiCodimParallelVectorHelper<GridView, DofMapper> vecHelper(*gridView_, *mapper_);
377 vecHelper.makeNonOverlappingConsistent(xcopy, LSTraits::dofCodims);
381 ParallelVectorHelper<GridView, DofMapper, LSTraits::dofCodim> vecHelper(*gridView_, *mapper_);
382 vecHelper.makeNonOverlappingConsistent(xcopy);
384 return ownedGlobalNorm_(xcopy);
387 return ownedGlobalNorm_(x);
391 std::string name()
const {
return "DuMux direct MUMPS solver"; }
395 int& icntl_(
int i) {
return id_.icntl[i - 1]; }
396 const int& infog_(
int i)
const {
return id_.infog[i - 1]; }
407 std::memset(&id_, 0,
sizeof(id_));
408 id_.comm_fortran =
static_cast<int>(MPI_Comm_c2f(mpiComm_));
414 checkError_(
"initialization");
431 void readSolverParams_()
433 const int ordering = getParamFromGroup<int>(this->paramGroup(),
"LinearSolver.MumpsOrdering", -1);
435 icntl_(7) = ordering;
439 const int workspaceIncrease = getParamFromGroup<int>(this->paramGroup(),
"LinearSolver.MumpsWorkspaceIncrease", -1);
440 if (workspaceIncrease >= 0)
441 icntl_(14) = workspaceIncrease;
447 const int parAnalysis = getParamFromGroup<int>(this->paramGroup(),
"LinearSolver.MumpsParallelAnalysis", -1);
448 if (parAnalysis >= 0)
449 icntl_(28) = parAnalysis;
450 const int parOrderingTool = getParamFromGroup<int>(this->paramGroup(),
"LinearSolver.MumpsParallelOrderingTool", -1);
451 if (parOrderingTool >= 0)
452 icntl_(29) = parOrderingTool;
459 void buildGlobalDofIndices_(
const GridView& gridView,
const DofMapper& mapper)
461 const std::size_t nLocal = mapper.size();
462 localToGlobal_.assign(nLocal, std::numeric_limits<GlobalIndex>::max());
463 isOwned_.assign(nLocal,
false);
464 isGhost_.assign(nLocal,
false);
466 if constexpr (LSTraits::canCommunicate)
470 ParallelISTLHelper<LSTraits> pHelper(gridView, mapper);
471 for (std::size_t i = 0; i < nLocal; ++i)
473 isOwned_[i] = pHelper.isOwned(i);
474 isGhost_[i] = pHelper.isGhost(i);
477 GlobalIndex numOwned =
static_cast<GlobalIndex
>(
478 std::count(isOwned_.begin(), isOwned_.end(),
true));
479 const int commSize = gridView.comm().size();
480 const int myRank = gridView.comm().rank();
481 std::vector<GlobalIndex> counts(commSize);
482 gridView.comm().allgather(&numOwned, 1, counts.data());
484 GlobalIndex offset = 0;
485 for (
int p = 0; p < myRank; ++p)
487 numBlocksGlobal_ = 0;
488 for (
auto c : counts) numBlocksGlobal_ +=
static_cast<std::size_t
>(c);
490 for (std::size_t i = 0; i < nLocal; ++i)
492 localToGlobal_[i] = offset++;
495 if constexpr (
requires { LSTraits::dofCodims; }) {
496 MultiCodimVectorCommDataHandleMin<DofMapper, std::vector<GlobalIndex>, GridView::dimension, GlobalIndex>
497 handle(mapper, localToGlobal_, LSTraits::dofCodims);
498 gridView.communicate(handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
500 VectorCommDataHandleMin<DofMapper, std::vector<GlobalIndex>, LSTraits::dofCodim, GlobalIndex>
501 handle(mapper, localToGlobal_);
502 gridView.communicate(handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
509 numBlocksGlobal_ = nLocal;
510 std::fill(isOwned_.begin(), isOwned_.end(),
true);
511 for (std::size_t i = 0; i < nLocal; ++i)
512 localToGlobal_[i] =
static_cast<GlobalIndex
>(i);
518 template<
class SubTraits,
class SubGG>
519 static decltype(
auto) subDofMapper_(
const SubGG& gg)
521 if constexpr (
requires { gg.dofMapper(); })
522 return (gg.dofMapper());
524 return SubTraits::dofMapper(gg);
530 template<
class SubTraits,
class GV>
531 static constexpr bool subCanComm_()
533 using Mapper =
typename SubTraits::DofMapper;
534 using Element =
typename GV::template Codim<0>::Entity;
535 return SubTraits::canCommunicate
536 &&
requires (
const Mapper& m,
const Element& e) { m.indices(e); };
543 template<
class SubLSTraits,
bool CanComm,
class GV,
class Mapper>
544 GlobalIndex buildSubdomainGIDs_(
const GV& gridView,
const Mapper& mapper,
545 GlobalIndex globalOffset,
546 std::vector<GlobalIndex>& gids,
547 std::vector<bool>& owned,
548 std::vector<bool>& ghost)
550 const std::size_t nLocal = mapper.size();
551 gids.assign(nLocal, std::numeric_limits<GlobalIndex>::max());
552 owned.assign(nLocal,
false);
553 ghost.assign(nLocal,
false);
555 if constexpr (CanComm)
559 ParallelISTLHelper<SubLSTraits> pHelper(gridView, mapper);
560 for (std::size_t i = 0; i < nLocal; ++i)
562 owned[i] = pHelper.isOwned(i);
563 ghost[i] = pHelper.isGhost(i);
566 GlobalIndex numOwned =
static_cast<GlobalIndex
>(
567 std::count(owned.begin(), owned.end(),
true));
568 const int commSize = gridView.comm().size();
569 const int myRank = gridView.comm().rank();
570 std::vector<GlobalIndex> counts(commSize);
571 gridView.comm().allgather(&numOwned, 1, counts.data());
573 GlobalIndex localOffset = globalOffset;
574 for (
int p = 0; p < myRank; ++p)
575 localOffset += counts[p];
577 GlobalIndex nGlobal = 0;
578 for (
auto c : counts) nGlobal += c;
580 for (std::size_t i = 0; i < nLocal; ++i)
582 gids[i] = localOffset++;
584 if constexpr (
requires { SubLSTraits::dofCodims; }) {
585 MultiCodimVectorCommDataHandleMin<Mapper, std::vector<GlobalIndex>, GV::dimension, GlobalIndex>
586 handle(mapper, gids, SubLSTraits::dofCodims);
587 gridView.communicate(handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
589 constexpr int codim = SubLSTraits::dofCodim;
590 VectorCommDataHandleMin<Mapper, std::vector<GlobalIndex>, codim, GlobalIndex>
591 handle(mapper, gids);
592 gridView.communicate(handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
599 std::fill(owned.begin(), owned.end(),
true);
600 for (std::size_t i = 0; i < nLocal; ++i)
601 gids[i] = globalOffset +
static_cast<GlobalIndex
>(i);
602 return static_cast<GlobalIndex
>(nLocal);
609 template<
class GGTuple>
610 void buildMultiTypeMaps_(
const GGTuple& ggTuple)
612 constexpr std::size_t numSubDomains = std::tuple_size_v<GGTuple>;
613 localToGlobal_.clear();
616 rowComplete_.clear();
617 subs_.assign(numSubDomains, SubdomainInfo{});
620 GlobalIndex scalarGlobalOffset = 0;
621 std::size_t scalarLocalOffset = 0;
623 using Dune::Hybrid::forEach;
624 forEach(std::make_index_sequence<numSubDomains>{}, [&](
auto I)
626 constexpr std::size_t i =
decltype(I)::value;
627 const auto& gg = Detail::mumpsDeref(std::get<i>(ggTuple));
628 using SubGG = std::decay_t<
decltype(gg)>;
629 using SubTraits = LinearSolverTraits<SubGG>;
630 using SubBlockVec = std::tuple_element_t<i, XVector>;
631 constexpr std::size_t scalarBS = SubBlockVec::block_type::size();
632 using GV =
typename SubGG::GridView;
633 constexpr bool canComm = subCanComm_<SubTraits, GV>();
637 if (isParallel_ && !canComm)
638 DUNE_THROW(Dune::NotImplemented,
639 "DirectSolverMumps: parallel multidomain solve is not supported for subdomain "
640 << i <<
" — its discretization's DOF mapper (e.g. FaceCenteredStaggered) cannot "
641 "drive the generic grid communication. This combination works sequentially; for "
642 "parallel runs use a momentum discretization with a full mapper (CVFE/FCDiamond).");
645 std::vector<GlobalIndex> gids;
646 std::vector<bool> owned, ghost;
647 const GlobalIndex nGlobalBlock = buildSubdomainGIDs_<SubTraits, canComm>(
648 gg.gridView(), subDofMapper_<SubTraits>(gg), 0, gids, owned, ghost);
649 const std::size_t nBlockLocal = gids.size();
654 const bool rowsComplete = !SubTraits::isNonOverlapping(gg.gridView());
658 for (std::size_t b = 0; b < nBlockLocal; ++b)
659 for (std::size_t k = 0; k < scalarBS; ++k)
661 localToGlobal_.push_back(scalarGlobalOffset + scalarBS * gids[b] +
static_cast<GlobalIndex
>(k));
662 isOwned_.push_back(owned[b]);
663 isGhost_.push_back(ghost[b]);
664 rowComplete_.push_back(rowsComplete);
667 subs_[i].scalarBS = scalarBS;
668 subs_[i].nBlockLocal = nBlockLocal;
669 subs_[i].scalarOffset = scalarLocalOffset;
670 subs_[i].rowsComplete = rowsComplete;
672 if constexpr (canComm)
673 setupSubdomainClosures_<i>(gg);
675 scalarLocalOffset += scalarBS * nBlockLocal;
676 scalarGlobalOffset +=
static_cast<GlobalIndex
>(scalarBS) * nGlobalBlock;
679 numBlocksGlobal_ =
static_cast<std::size_t
>(scalarGlobalOffset);
693 template<std::
size_t i,
class SubGG>
694 void setupSubdomainClosures_(
const SubGG& gg)
696 using SubTraits = LinearSolverTraits<SubGG>;
697 using SubBlockVec = std::tuple_element_t<i, XVector>;
698 using GV =
typename SubGG::GridView;
699 using Mapper =
typename SubTraits::DofMapper;
700 constexpr std::size_t scalarBS = SubBlockVec::block_type::size();
702 const GV gv = gg.gridView();
705 auto mapper = std::make_shared<Mapper>(subDofMapper_<SubTraits>(gg));
706 const std::size_t offset = subs_[i].scalarOffset;
707 const std::size_t nBlock = subs_[i].nBlockLocal;
710 auto unpack = [offset, nBlock](
const ScalarBlockVector& v, SubBlockVec& block) {
711 block.resize(nBlock);
712 for (std::size_t b = 0; b < nBlock; ++b)
713 for (std::size_t k = 0; k < scalarBS; ++k)
714 block[b][k] = v[offset + scalarBS * b + k][0];
716 auto pack = [offset, nBlock](
const SubBlockVec& block, ScalarBlockVector& v) {
717 for (std::size_t b = 0; b < nBlock; ++b)
718 for (std::size_t k = 0; k < scalarBS; ++k)
719 v[offset + scalarBS * b + k] = block[b][k];
723 subs_[i].ghostBroadcast = [gv, mapper, unpack, pack](ScalarBlockVector& v) {
726 if constexpr (
requires { SubTraits::dofCodims; }) {
727 MultiCodimVectorCommDataHandleSum<Mapper, SubBlockVec, GV::dimension>
728 handle(*mapper, block, SubTraits::dofCodims);
729 gv.communicate(handle, Dune::All_All_Interface, Dune::ForwardCommunication);
731 VectorCommDataHandleSum<Mapper, SubBlockVec, SubTraits::dofCodim>
732 handle(*mapper, block);
733 gv.communicate(handle, Dune::All_All_Interface, Dune::ForwardCommunication);
740 if (!subs_[i].rowsComplete) {
741 subs_[i].rhsPreprocess = [gv, mapper, unpack, pack](ScalarBlockVector& v) {
744 if constexpr (
requires { SubTraits::dofCodims; })
746 MultiCodimParallelVectorHelper<GV, Mapper> vecHelper(gv, *mapper);
747 vecHelper.makeNonOverlappingConsistent(block, SubTraits::dofCodims);
751 ParallelVectorHelper<GV, Mapper, SubTraits::dofCodim> vecHelper(gv, *mapper);
752 vecHelper.makeNonOverlappingConsistent(block);
760 template<
class MTMatrix>
761 void initSeqDomainOffsets_(
const MTMatrix& A)
763 constexpr std::size_t numSubDomains = MTMatrix::N();
764 if (subs_.size() < numSubDomains)
765 subs_.resize(numSubDomains);
766 std::size_t offset = 0;
767 using namespace Dune::Hybrid;
768 forEach(std::make_index_sequence<numSubDomains>{}, [&](
auto I)
770 constexpr std::size_t i =
decltype(I)::value;
771 using Block =
typename std::decay_t<
decltype(A[Dune::index_constant<i>{}][Dune::index_constant<i>{}])>::block_type;
772 subs_[i].scalarBS = Block::rows;
773 subs_[i].nBlockLocal = A[Dune::index_constant<i>{}][Dune::index_constant<i>{}].N();
774 subs_[i].scalarOffset = offset;
775 offset += subs_[i].scalarBS * subs_[i].nBlockLocal;
779 template<
class MTMatrix>
780 bool subdomainSizesMatch_(
const MTMatrix& A)
const
783 using namespace Dune::Hybrid;
784 forEach(std::make_index_sequence<MTMatrix::N()>{}, [&](
auto I)
786 constexpr std::size_t i =
decltype(I)::value;
787 match = match && A[I][I].N() == subs_[i].nBlockLocal;
793 void initSeqMaps_(std::size_t nDofs)
795 numBlocksGlobal_ = nDofs;
796 localToGlobal_.resize(nDofs);
797 isOwned_.assign(nDofs,
true);
798 isGhost_.assign(nDofs,
false);
799 rowComplete_.assign(nDofs,
true);
800 for (std::size_t i = 0; i < nDofs; ++i)
801 localToGlobal_[i] =
static_cast<GlobalIndex
>(i);
812 void buildIsSrcRow_()
814 const std::size_t n = isOwned_.size();
815 isSrcRow_.assign(n,
false);
816 if constexpr (isMultiType)
818 for (std::size_t r = 0; r < n; ++r)
819 isSrcRow_[r] = rowComplete_[r] ? isOwned_[r] : !isGhost_[r];
822 else if constexpr (LSTraits::canCommunicate)
826 const bool nonOverlapping = LSTraits::isNonOverlapping(*gridView_);
827 for (std::size_t r = 0; r < n; ++r)
828 isSrcRow_[r] = nonOverlapping ? !isGhost_[r] : isOwned_[r];
832 isSrcRow_ = isOwned_;
840 auto tripletWriter_(
bool firstCall, std::size_t& k,
bool& samePattern)
849 return [
this, firstCall, &k, &samePattern](GlobalIndex row, GlobalIndex col,
double value)
853 irnLoc_.push_back(toMumpsInt_(row + 1));
854 jcnLoc_.push_back(toMumpsInt_(col + 1));
855 aLoc_.push_back(value);
857 else if (k >= aLoc_.size() || irnLoc_[k] != row + 1 || jcnLoc_[k] != col + 1)
859 else if (samePattern)
871 template<
class AMatrix>
872 bool assembleTriplets_(
const AMatrix& A,
bool firstCall)
873 requires (isMultiTypeBlockMatrix<AMatrix>::value)
875 using namespace Dune::Hybrid;
876 constexpr std::size_t numSubDomains = AMatrix::N();
878 bool samePattern =
true;
879 auto addTriplet = tripletWriter_(firstCall, k, samePattern);
880 forEach(std::make_index_sequence<numSubDomains>{}, [&](
auto I)
882 forEach(std::make_index_sequence<numSubDomains>{}, [&](
auto J)
884 const auto& block = A[I][J];
885 using BT =
typename std::decay_t<
decltype(block)>::block_type;
886 const std::size_t offI = subs_[
decltype(I)::value].scalarOffset;
887 const std::size_t offJ = subs_[
decltype(J)::value].scalarOffset;
888 for (
auto rowIt = block.begin(); rowIt != block.end(); ++rowIt)
890 for (
int ki = 0; ki < BT::rows; ++ki)
892 const std::size_t r = offI + BT::rows*rowIt.index() + ki;
893 if (!isSrcRow_[r])
continue;
894 const GlobalIndex gRow = localToGlobal_[r];
895 for (
auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt)
896 for (
int kj = 0; kj < BT::cols; ++kj)
897 addTriplet(gRow, localToGlobal_[offJ + BT::cols*colIt.index() + kj], (*colIt)[ki][kj]);
902 return samePattern && k == aLoc_.size();
905 template<
class AMatrix>
906 bool assembleTriplets_(
const AMatrix& A,
bool firstCall)
907 requires (!isMultiTypeBlockMatrix<AMatrix>::value)
909 using BlockType =
typename AMatrix::block_type;
910 constexpr int BS_I = BlockType::rows;
911 constexpr int BS_J = BlockType::cols;
914 bool samePattern =
true;
915 auto addTriplet = tripletWriter_(firstCall, k, samePattern);
916 for (
auto rowIt = A.begin(); rowIt != A.end(); ++rowIt)
918 const std::size_t r = rowIt.index();
919 if (!isSrcRow_[r])
continue;
920 const GlobalIndex gBlockRow = localToGlobal_[r];
922 for (
int ki = 0; ki < BS_I; ++ki)
924 const GlobalIndex gRow = gBlockRow * BS_I + ki;
925 for (
auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt)
927 const GlobalIndex gBlockCol = localToGlobal_[colIt.index()];
928 for (
int kj = 0; kj < BS_J; ++kj)
929 addTriplet(gRow, gBlockCol * BS_J + kj, (*colIt)[ki][kj]);
933 return samePattern && k == aLoc_.size();
942 void setupRhsGather_()
945 const std::size_t nScalar = numBlocksGlobal_ *
blockSize;
946 if (nScalar >
static_cast<std::size_t
>(std::numeric_limits<int>::max()))
947 DUNE_THROW(Dune::NotImplemented,
"DirectSolverMumps: gathering the right-hand side of "
948 << nScalar <<
" unknowns");
951 std::vector<int> myIdx;
952 for (std::size_t i = 0; i < localToGlobal_.size(); ++i)
954 for (std::size_t k = 0; k <
blockSize; ++k)
955 myIdx.push_back(
static_cast<int>(localToGlobal_[i] * blockSize + k));
956 nOwnedScalar_ =
static_cast<int>(myIdx.size());
958 int commSize = 1, myRank = 0;
959 MPI_Comm_size(mpiComm_, &commSize);
960 MPI_Comm_rank(mpiComm_, &myRank);
961 isHost_ = (myRank == 0);
963 recvCounts_.assign(isHost_ ? commSize : 0, 0);
964 MPI_Gather(&nOwnedScalar_, 1, MPI_INT,
965 isHost_ ? recvCounts_.data() :
nullptr, 1, MPI_INT, 0, mpiComm_);
969 displs_.assign(commSize, 0);
970 std::partial_sum(recvCounts_.begin(), recvCounts_.end() - 1, displs_.begin() + 1);
971 allGlobalIdx_.assign(nScalar, 0);
972 rhsHost_.assign(nScalar, 0.0);
973 tmpHost_.assign(nScalar, 0.0);
976 MPI_Gatherv(myIdx.data(), nOwnedScalar_, MPI_INT,
977 isHost_ ? allGlobalIdx_.data() :
nullptr,
978 isHost_ ? recvCounts_.data() :
nullptr,
979 isHost_ ? displs_.data() :
nullptr,
980 MPI_INT, 0, mpiComm_);
981 sendBuf_.resize(
static_cast<std::size_t
>(nOwnedScalar_));
989 template<
class BVec,
class XVec>
990 void solveCentralized_(
const std::vector<const BVec*>& b,
const std::vector<XVec*>& x)
992 const auto start = std::chrono::steady_clock::now();
993 const std::size_t numColumns = b.size();
997 const std::size_t n = tmpHost_.size();
999 rhsHost_.resize(n*numColumns);
1001 for (std::size_t j = 0; j < numColumns; ++j)
1004 const auto&
bj = *b[j];
1006 for (std::size_t i = 0; i <
bj.size(); ++i)
1008 for (std::size_t k = 0; k <
blockSize; ++k)
1009 sendBuf_[s++] = bj[i][k];
1011 MPI_Gatherv(sendBuf_.data(), nOwnedScalar_, MPI_DOUBLE,
1012 isHost_ ? tmpHost_.data() :
nullptr,
1013 isHost_ ? recvCounts_.data() :
nullptr,
1014 isHost_ ? displs_.data() :
nullptr,
1015 MPI_DOUBLE, 0, mpiComm_);
1018 for (std::size_t p = 0; p < n; ++p)
1019 rhsHost_[j*n +
static_cast<std::size_t
>(allGlobalIdx_[p])] = tmpHost_[p];
1024 id_.nrhs =
static_cast<MUMPS_INT
>(numColumns);
1026 id_.rhs = rhsHost_.data();
1030 checkError_(
"solve");
1032 for (std::size_t j = 0; j < numColumns; ++j)
1036 for (std::size_t p = 0; p < n; ++p)
1037 tmpHost_[p] = rhsHost_[j*n +
static_cast<std::size_t
>(allGlobalIdx_[p])];
1039 MPI_Scatterv(isHost_ ? tmpHost_.data() :
nullptr,
1040 isHost_ ? recvCounts_.data() :
nullptr,
1041 isHost_ ? displs_.data() :
nullptr,
1042 MPI_DOUBLE, sendBuf_.data(), nOwnedScalar_, MPI_DOUBLE, 0, mpiComm_);
1046 for (std::size_t i = 0; i < xj.size(); ++i)
1048 for (std::size_t k = 0; k <
blockSize; ++k)
1049 xj[i][k] = sendBuf_[s++];
1052 if (this->verbosity() > 0 && isHost_)
1053 std::cout <<
"[MUMPS] solve+gather "
1054 << std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count()
1055 <<
" ms" << (numColumns > 1 ?
" for " + std::to_string(numColumns) +
" right-hand sides" : std::string{})
1060 void factorize_(
const Matrix& A)
1062 using Clock = std::chrono::steady_clock;
1063 const bool time = this->verbosity() > 0;
1064 auto ms = [](Clock::time_point a, Clock::time_point z) {
1065 return std::chrono::duration<double, std::milli>(z - a).count();
1068 double tAssemble = 0.0, tAnalysis = 0.0;
1069 auto t0 = Clock::now();
1071 const bool analyze = !analyzed_ || !onAllProcesses_(assembleTriplets_(A,
false));
1077 assembleTriplets_(A,
true);
1078 id_.n = toMumpsInt_(
static_cast<GlobalIndex
>(numBlocksGlobal_ * blockSize));
1079 id_.nz_loc = toMumpsInt_(
static_cast<GlobalIndex
>(aLoc_.size()));
1080 id_.irn_loc = irnLoc_.data();
1081 id_.jcn_loc = jcnLoc_.data();
1082 id_.a_loc = aLoc_.data();
1083 auto t1 = Clock::now(); tAssemble = ms(t0, t1);
1087 checkError_(
"symbolic analysis");
1089 tAnalysis = ms(t1, Clock::now());
1092 tAssemble = ms(t0, Clock::now());
1094 auto t2 = Clock::now();
1097 checkError_(
"numeric factorization");
1099 const double tNumeric = ms(t2, Clock::now());
1101 if (time && isHost_)
1102 std::cout <<
"[MUMPS] assemble " << tAssemble <<
" ms"
1103 << (tAnalysis > 0.0 ? (
", analysis " + std::to_string(tAnalysis) +
" ms") : std::string{})
1104 <<
", numeric factorization " << tNumeric <<
" ms" << std::endl;
1108 void prepareMaps_(
const Matrix& A)
1110 if constexpr (isMultiType)
1113 updateMapsToSystemSize_(numScalarRows_(A), subdomainSizesMatch_(A));
1116 updateMapsToSystemSize_(numScalarRows_(A));
1117 initSeqDomainOffsets_(A);
1121 updateMapsToSystemSize_(A.N());
1124 template<
class MTMatrix>
1125 static std::size_t numScalarRows_(
const MTMatrix& A)
1127 std::size_t numRows = 0;
1128 using namespace Dune::Hybrid;
1129 forEach(std::make_index_sequence<MTMatrix::N()>{}, [&](
auto I)
1131 const auto& block = A[I][I];
1132 numRows += block.N()*std::decay_t<
decltype(block)>::block_type::rows;
1137 SolverResult solveColumns_(
const std::vector<XVector*>& x,
const std::vector<const BVector*>& b)
1140 DUNE_THROW(Dune::InvalidStateException,
"Called solve(x, b) but no matrix has been set");
1142 checkColumns_(x, b);
1144 if constexpr (isMultiType)
1145 solveMultiType_(x, b);
1147 solveSingleType_(x, b);
1148 result_.converged =
true;
1154 void checkColumns_(
const std::vector<XVector*>& x,
const std::vector<const BVector*>& b)
const
1156 bool match = x.size() == b.size();
1157 for (std::size_t j = 0; match && j < x.size(); ++j)
1158 match = sizeInDofMaps_(*x[j]) == localToGlobal_.size() && sizeInDofMaps_(*b[j]) == localToGlobal_.size();
1162 long long counts[2] = {
static_cast<long long>(b.size()), -
static_cast<long long>(b.size()) };
1163 MPI_Allreduce(MPI_IN_PLACE, counts, 2, MPI_LONG_LONG, MPI_MIN, mpiComm_);
1164 match = match && counts[0] == -counts[1];
1167 if (!onAllProcesses_(match))
1168 DUNE_THROW(Dune::InvalidStateException,
1169 "DirectSolverMumps: the solutions and right-hand sides do not match each other or the "
1170 "factorized system on at least one process (here " << x.size() <<
" solutions and "
1171 << b.size() <<
" right-hand sides, the system has " << localToGlobal_.size() <<
" rows)");
1176 static std::size_t sizeInDofMaps_(
const V& v)
1178 if constexpr (isMultiTypeBlockVector<V>::value)
1180 std::size_t size = 0;
1181 Dune::Hybrid::forEach(std::make_index_sequence<V::N()>{}, [&](
auto i)
1183 using Block =
typename std::decay_t<
decltype(v[i])>::block_type;
1184 size += v[i].size()*Block::size();
1192 bool onAllProcesses_(
bool value)
const
1196 int local = value ? 1 : 0;
1198 MPI_Allreduce(&local, &global, 1, MPI_INT, MPI_MIN, mpiComm_);
1204 void updateMapsToSystemSize_(std::size_t numRows,
bool subdomainSizesMatch =
true)
1206 const bool sizesMatch = numRows == localToGlobal_.size() && subdomainSizesMatch;
1209 if (!onAllProcesses_(sizesMatch))
1210 DUNE_THROW(Dune::InvalidStateException,
1211 "DirectSolverMumps: the dof distribution of the system differs from the one of "
1212 "the solver on at least one process (here " << numRows <<
" rows, the solver "
1213 "expects " << localToGlobal_.size() <<
"). Call updateAfterGridAdaption after "
1214 "the grid changed.");
1216 else if (!sizesMatch)
1218 initSeqMaps_(numRows);
1223 void solveSingleType_(
const std::vector<XVector*>& x,
const std::vector<const BVector*>& b)
1225 std::vector<BVector> bConsistent;
1226 bConsistent.reserve(b.size());
1227 for (
const auto* bj : b)
1229 bConsistent.push_back(*bj);
1230 makeRhsConsistent_(bConsistent.back());
1233 std::vector<const BVector*> bColumns;
1234 for (
const auto& bj : bConsistent)
1235 bColumns.push_back(&bj);
1236 solveCentralized_(bColumns, x);
1239 distributeSolution_(*xj);
1243 void makeRhsConsistent_(BVector& b)
const
1245 if constexpr (LSTraits::canCommunicate)
1247 if (isParallel_ && LSTraits::isNonOverlapping(*gridView_))
1249 if constexpr (
requires { LSTraits::dofCodims; })
1251 MultiCodimParallelVectorHelper<GridView, DofMapper> vecHelper(*gridView_, *mapper_);
1252 vecHelper.makeNonOverlappingConsistent(b, LSTraits::dofCodims);
1256 ParallelVectorHelper<GridView, DofMapper, LSTraits::dofCodim> vecHelper(*gridView_, *mapper_);
1257 vecHelper.makeNonOverlappingConsistent(b);
1264 void distributeSolution_(XVector& x)
const
1266 if constexpr (LSTraits::canCommunicate)
1270 for (std::size_t i = 0; i < x.size(); ++i)
1273 if constexpr (
requires { LSTraits::dofCodims; }) {
1274 MultiCodimVectorCommDataHandleSum<DofMapper, XVector, GridView::dimension> handle(*mapper_, x, LSTraits::dofCodims);
1275 gridView_->communicate(handle, Dune::All_All_Interface, Dune::ForwardCommunication);
1277 VectorCommDataHandleSum<DofMapper, XVector, LSTraits::dofCodim> handle(*mapper_, x);
1278 gridView_->communicate(handle, Dune::All_All_Interface, Dune::ForwardCommunication);
1284 void solveMultiType_(
const std::vector<XVector*>& x,
const std::vector<const BVector*>& b)
1287 std::vector<ScalarBlockVector> bScalar;
1288 bScalar.reserve(b.size());
1289 for (
const auto* bj : b)
1291 bScalar.push_back(VectorConverter<BVector>::multiTypeToBlockVector(*bj));
1295 for (
const auto& sub : subs_)
1296 if (sub.rhsPreprocess)
1297 sub.rhsPreprocess(bScalar.back());
1300 auto xScalar = bScalar;
1301 std::vector<const ScalarBlockVector*> bColumns;
1302 std::vector<ScalarBlockVector*> xColumns;
1303 for (std::size_t j = 0; j < bScalar.size(); ++j)
1305 bColumns.push_back(&bScalar[j]);
1306 xColumns.push_back(&xScalar[j]);
1308 solveCentralized_(bColumns, xColumns);
1310 for (std::size_t j = 0; j < x.size(); ++j)
1315 for (std::size_t r = 0; r < xScalar[j].size(); ++r)
1316 if (!isOwned_[r]) xScalar[j][r] = 0.0;
1317 for (
const auto& sub : subs_)
1318 if (sub.ghostBroadcast)
1319 sub.ghostBroadcast(xScalar[j]);
1322 VectorConverter<XVector>::retrieveValues(*x[j], xScalar[j]);
1326 Scalar ownedGlobalNorm_(
const XVector& x)
const
1328 double localSumSq = 0.0;
1329 for (std::size_t i = 0; i < x.size(); ++i)
1331 for (std::size_t k = 0; k <
blockSize; ++k)
1332 localSumSq += x[i][k] * x[i][k];
1333 double globalSumSq = 0.0;
1334 MPI_Allreduce(&localSumSq, &globalSumSq, 1, MPI_DOUBLE, MPI_SUM, mpiComm_);
1336 return sqrt(globalSumSq);
1339 static MUMPS_INT toMumpsInt_(GlobalIndex v)
1341 if (v >
static_cast<GlobalIndex
>(std::numeric_limits<MUMPS_INT>::max()))
1342 DUNE_THROW(Dune::RangeError,
"Index " << v <<
" exceeds MUMPS_INT range; "
1343 "rebuild MUMPS with 64-bit integers (-DINTSIZE64)");
1344 return static_cast<MUMPS_INT
>(v);
1347 void checkError_(
const char* phase)
const
1349 const int error = infog_(1);
1353 const bool workspaceTooSmall = error == -8 || error == -9 || error == -14
1354 || error == -15 || error == -17 || error == -20;
1355 DUNE_THROW(Dune::Exception,
"MUMPS " << phase <<
" failed: INFOG(1)=" << error
1356 <<
", INFOG(2)=" << infog_(2)
1357 << (workspaceTooSmall ?
". Increase LinearSolver.MumpsWorkspaceIncrease (ICNTL(14))" :
""));
1361 std::optional<GridView> gridView_;
1362 const DofMapper* mapper_ =
nullptr;
1367 bool initialized_ =
false;
1368 bool analyzed_ =
false;
1369 bool factorized_ =
false;
1370 SolverResult result_;
1373 std::size_t numBlocksGlobal_ = 0;
1374 std::vector<GlobalIndex> localToGlobal_;
1375 std::vector<bool> isOwned_;
1376 std::vector<bool> isGhost_;
1377 std::vector<bool> isSrcRow_;
1378 std::vector<bool> rowComplete_;
1382 std::vector<MUMPS_INT> irnLoc_;
1383 std::vector<MUMPS_INT> jcnLoc_;
1384 std::vector<double> aLoc_;
1388 bool isHost_ =
true;
1389 int nOwnedScalar_ = 0;
1390 std::vector<int> recvCounts_;
1391 std::vector<int> displs_;
1392 std::vector<int> allGlobalIdx_;
1393 std::vector<double> rhsHost_;
1394 std::vector<double> tmpHost_;
1395 std::vector<double> sendBuf_;
1398 std::vector<SubdomainInfo> subs_;
Base class for linear solvers.
Definition solver.hh:27
constexpr BJ bj
Tag for the Beavers-Joseph slip condition.
Definition momentum/slipcondition.hh:42
Define traits for linear solvers.
Type traits to be used with matrix types.
A helper class that converts a Dune::MultiTypeBlockMatrix into a plain Dune::BCRSMatrix.
constexpr std::size_t blockSize()
Definition dunevectors.hh:27
Definition cvfelocalresidual.hh:25
Dune::SolverCategory::Category solverCategory(const GridView &gridView)
Definition solvercategory.hh:20
bool solve(const Matrix &matrix, Vector &x, const Vector &b)
Solve the local linear system .
Definition odesolver.hh:468
auto norm(const Vector &vector)
Compute the Euclidean norm of a scalar or vector-like object.
Definition odesolver.hh:435
typename ScalarType< X >::Type Scalar
Definition odesolver.hh:52
Provides a helper class for nonoverlapping decomposition.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Base class for linear solvers.
Type traits to be used with vector types.
Contains a class to exchange entries of a vector.