AMDiS  0.3
The Adaptive Multi-Dimensional Simulation Toolbox
LinearSolver.hpp
1 #pragma once
2 
3 #include <petscpc.h>
4 #include <petscksp.h>
5 
6 #include <amdis/Initfile.hpp>
7 #include <amdis/linearalgebra/LinearSolverInterface.hpp>
8 #include <amdis/linearalgebra/petsc/Interface.hpp>
9 
10 #ifndef KSPDGGMRES
11 #define KSPDGGMRES "dggmres"
12 #endif
13 
14 
15 namespace AMDiS
16 {
18 
42  template <class Matrix, class VectorX, class VectorY = VectorX>
43  class LinearSolver
44  : public LinearSolverInterface<Matrix,VectorX,VectorY>
45  {
46  using Vector = VectorX;
47 
48  public:
50 
58  LinearSolver(std::string const& /*name*/, std::string const& prefix)
59  : prefix_(prefix)
60  {
61  Parameters::get(prefix + "->info", info_);
62  }
63 
64  ~LinearSolver()
65  {
66  finish();
67  }
68 
70  void init(Matrix const& A) override
71  {
72  finish();
73 #if HAVE_MPI
74  KSPCreate(Environment::comm(), &ksp_);
75 #else
76  KSPCreate(PETSC_COMM_SELF, &ksp_);
77 #endif
78 
79  PETSc::KSPSetOperators(ksp_, A.matrix(), A.matrix());
80  initKSP(ksp_, prefix_);
81  initialized_ = true;
82  }
83 
85  void finish() override
86  {
87  if (initialized_)
88  KSPDestroy(&ksp_);
89  initialized_ = false;
90  }
91 
93  void apply(Vector& x, Vector const& b, Dune::InverseOperatorResult& stat) override
94  {
95  KSPSolve(ksp_, b.vector(), x.vector());
96 
97  if (info_ > 0)
98  PETSc::KSPConvergedReasonView(ksp_, PETSC_VIEWER_STDOUT_WORLD);
99 
100  KSPConvergedReason reason;
101  KSPGetConvergedReason(ksp_, &reason);
102 
103  stat.converged = (reason > 0);
104  }
105 
106  KSP ksp() { return ksp_; }
107 
108  protected:
109  // initialize the KSP solver from the initfile
110  virtual void initKSP(KSP ksp, std::string prefix) const
111  {
112  // see https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/KSP/KSPType.html
113  auto kspType = Parameters::get<std::string>(prefix + "->ksp");
114  std::string kspTypeStr = kspType.value_or("default");
115 
116  if (!kspType)
117  Parameters::get(prefix, kspTypeStr);
118 
119  if (kspTypeStr == "direct")
120  initDirectSolver(ksp, prefix);
121  else if (kspTypeStr != "default") {
122  KSPSetType(ksp, kspTypeStr.c_str());
123 
124  // initialize some KSP specific parameters
125  initKSPParameters(ksp, kspTypeStr.c_str(), prefix);
126 
127  // set initial guess to nonzero only for non-preonly ksp type
128  if (kspTypeStr != "preonly")
129  KSPSetInitialGuessNonzero(ksp, PETSC_TRUE);
130  }
131 
132  // set a KSPMonitor if info > 0
133  int info = 0;
134  Parameters::get(prefix + "->info", info);
135  if (info == 1) {
136  KSPMonitorSet(ksp, PETSc::KSPMonitorCyclic, nullptr, nullptr);
137  } else if (info > 1) {
138  KSPMonitorSet(ksp, PETSc::KSPMonitorNoisy, nullptr, nullptr);
139  }
140 
141  // set solver tolerances
142  auto maxIt = Parameters::get<PetscInt>(prefix + "->max it");
143  auto rTol = Parameters::get<PetscReal>(prefix + "->rtol");
144  auto aTol = Parameters::get<PetscReal>(prefix + "->atol");
145  auto dTol = Parameters::get<PetscReal>(prefix + "->divtol");
146  if (maxIt || rTol || aTol || dTol)
147  KSPSetTolerances(ksp,
148  rTol.value_or(PETSC_DEFAULT), aTol.value_or(PETSC_DEFAULT),
149  dTol.value_or(PETSC_DEFAULT), maxIt.value_or(PETSC_DEFAULT));
150 
151  auto kspPrefixParam = Parameters::get<std::string>(prefix + "->prefix");
152  if (kspPrefixParam) {
153  std::string kspPrefix = kspPrefixParam.value() + "_";
154  KSPSetOptionsPrefix(ksp, kspPrefix.c_str());
155  KSPSetFromOptions(ksp);
156  }
157 
158  PC pc;
159  KSPGetPC(ksp, &pc);
160  initPC(pc, prefix + "->pc");
161 
162  // show details of ksp object
163  if (info >= 10) {
164  KSPView(ksp, PETSC_VIEWER_STDOUT_WORLD);
165  }
166  }
167 
168 
169  // initialize a direct solver from the initfile
170  virtual void initDirectSolver(KSP ksp, std::string prefix) const
171  {
172  KSPSetInitialGuessNonzero(ksp, PETSC_TRUE);
173  KSPSetType(ksp, KSPRICHARDSON);
174 
175  PC pc;
176  KSPGetPC(ksp, &pc);
177  PCSetType(pc, PCLU);
178  auto matSolverType = Parameters::get<std::string>(prefix + "->mat solver");
179  if (matSolverType)
180  PETSc::PCFactorSetMatSolverType(pc, matSolverType.value().c_str());
181  else {
182  Mat Amat, Pmat;
183  KSPGetOperators(ksp, &Amat, &Pmat);
184  MatType type;
185  MatGetType(Pmat, &type);
186  if (std::strcmp(type, MATSEQAIJ) == 0)
187  PETSc::PCFactorSetMatSolverType(pc, MATSOLVERUMFPACK);
188  else if (std::strcmp(type, MATAIJ) == 0)
189  PETSc::PCFactorSetMatSolverType(pc, MATSOLVERMUMPS);
190  }
191  }
192 
193 
194  // initialize the preconditioner pc from the initfile
195  virtual void initPC(PC pc, std::string prefix) const
196  {
197  // see https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/PC/PCType.html
198  auto pcType = Parameters::get<std::string>(prefix);
199  std::string pcTypeStr = "default";
200  if (pcType) {
201  pcTypeStr = pcType.value();
202  PCSetType(pc, pcTypeStr.c_str());
203  }
204 
205  if (pcTypeStr == "lu") {
206  // configure matsolverpackage
207  // see https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Mat/MatSolverType.html
208  auto matSolverType = Parameters::get<std::string>(prefix + "->mat solver");
209  if (matSolverType)
210  PETSc::PCFactorSetMatSolverType(pc, matSolverType.value().c_str());
211  } else if (pcTypeStr == "bjacobi") {
212  // configure sub solver
213  PetscInt n_local, first;
214  KSP* ksps;
215  PCSetUp(pc);
216  PCBJacobiGetSubKSP(pc, &n_local, &first, &ksps);
217  for (PetscInt i = 0; i < n_local; ++i)
218  initKSP(ksps[i], prefix + "->sub ksp");
219  } else if (pcTypeStr == "ksp") {
220  // configure sub ksp type
221  KSP ksp;
222  PCSetUp(pc);
223  PCKSPGetKSP(pc, &ksp);
224  initKSP(ksp, prefix + "->ksp");
225  }
226 
227  auto pcPrefixParam = Parameters::get<std::string>(prefix + "->prefix");
228  if (pcPrefixParam) {
229  std::string pcPrefix = pcPrefixParam.value() + "_";
230  PCSetOptionsPrefix(pc, pcPrefix.c_str());
231  PCSetFromOptions(pc);
232  }
233  }
234 
235 
236  // provide initfile parameters for some PETSc KSP parameters
237  virtual void initKSPParameters(KSP ksp, char const* ksptype, std::string prefix) const
238  {
239  // parameters for the Richardson solver
240  if (std::strcmp(ksptype, KSPRICHARDSON) == 0)
241  {
242  auto scale = Parameters::get<PetscReal>(prefix + "->scale");
243  if (scale)
244  KSPRichardsonSetScale(ksp, scale.value());
245  }
246  // parameters for the gmres solver
247  else if (std::strcmp(ksptype, KSPGMRES) == 0 ||
248  std::strcmp(ksptype, KSPFGMRES) == 0 ||
249  std::strcmp(ksptype, KSPLGMRES) == 0 ||
250  std::strcmp(ksptype, KSPDGGMRES) == 0 ||
251  std::strcmp(ksptype, KSPPGMRES) == 0)
252  {
253  auto restart = Parameters::get<PetscInt>(prefix + "->restart");
254  if (restart)
255  KSPGMRESSetRestart(ksp, restart.value());
256 
257  auto gramschmidt = Parameters::get<std::string>(prefix + "->gramschmidt");
258  if (gramschmidt) {
259  if (gramschmidt.value() == "classical") {
260  KSPGMRESSetOrthogonalization(ksp, KSPGMRESClassicalGramSchmidtOrthogonalization);
261  auto refinementType = Parameters::get<std::string>(prefix + "->refinement type");
262  if (refinementType) {
263  if (refinementType.value() == "never")
264  KSPGMRESSetCGSRefinementType(ksp, KSP_GMRES_CGS_REFINE_NEVER);
265  else if (refinementType.value() == "ifneeded")
266  KSPGMRESSetCGSRefinementType(ksp, KSP_GMRES_CGS_REFINE_IFNEEDED);
267  else if (refinementType.value() == "always")
268  KSPGMRESSetCGSRefinementType(ksp, KSP_GMRES_CGS_REFINE_ALWAYS);
269  }
270  } else if (gramschmidt.value() == "modified") {
271  KSPGMRESSetOrthogonalization(ksp, KSPGMRESModifiedGramSchmidtOrthogonalization);
272  }
273  }
274  }
275  // parameters for the BiCGStab_ell solver
276  else if (std::strcmp(ksptype, KSPBCGSL) == 0)
277  {
278  auto ell = Parameters::get<PetscInt>(prefix + "->ell");
279  if (ell)
280  KSPBCGSLSetEll(ksp, ell.value());
281  }
282  // parameters for the GCR solver
283  else if (std::strcmp(ksptype, KSPGCR) == 0)
284  {
285  auto restart = Parameters::get<PetscInt>(prefix + "->restart");
286  if (restart)
287  KSPGCRSetRestart(ksp, restart.value());
288  }
289  }
290 
291  protected:
292  std::string prefix_;
293  int info_ = 0;
294 
295  KSP ksp_;
296  bool initialized_ = false;
297  };
298 
299 } // end namespace AMDiS
void init(Matrix const &A) override
Implements LinearSolverInterface::init()
Definition: LinearSolver.hpp:70
Definition: AdaptBase.hpp:6
void apply(Vector &x, Vector const &b, Dune::InverseOperatorResult &stat) override
Implements LinearSolverInterface::apply()
Definition: LinearSolver.hpp:93
static std::optional< T > get(std::string const &key)
Get parameter-values from parameter-tree.
Definition: Initfile.hpp:25
LinearSolver(std::string const &, std::string const &prefix)
Constructor.
Definition: LinearSolver.hpp:58
Implementation of LinearSolverInterface for EIGEN solvers.
Definition: LinearSolver.hpp:14
void finish() override
Implements LinearSolverInterface::finish()
Definition: LinearSolver.hpp:35
static Dune::MPIHelper::MPICommunicator comm()
Return the MPI_Comm object (or a fake communicator)
Definition: Environment.hpp:86