ExaDG
Loading...
Searching...
No Matches
preconditioner_amg.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef EXADG_SOLVERS_AND_PRECONDITIONERS_PRECONDITIONERS_PRECONDITIONER_AMG_H_
23#define EXADG_SOLVERS_AND_PRECONDITIONERS_PRECONDITIONERS_PRECONDITIONER_AMG_H_
24
25// deal.II
26#include <deal.II/lac/la_parallel_vector.h>
27#include <deal.II/lac/petsc_precondition.h>
28#include <deal.II/lac/petsc_solver.h>
29#include <deal.II/lac/petsc_sparse_matrix.h>
30#include <deal.II/lac/trilinos_precondition.h>
31#include <deal.II/lac/trilinos_sparse_matrix.h>
32
33// Trilinos
34#ifdef DEAL_II_WITH_TRILINOS
35# include <ml_MultiLevelPreconditioner.h>
36#endif
37
38// ExaDG
39#include <exadg/solvers_and_preconditioners/multigrid/multigrid_parameters.h>
40#include <exadg/solvers_and_preconditioners/preconditioners/preconditioner_base.h>
41#include <exadg/solvers_and_preconditioners/solvers/iterative_solvers_dealii_wrapper.h>
42#include <exadg/solvers_and_preconditioners/utilities/linear_algebra_utilities.h>
43#include <exadg/utilities/print_functions.h>
44
45namespace ExaDG
46{
47template<int dim, int spacedim>
48std::unique_ptr<MPI_Comm, void (*)(MPI_Comm *)>
49create_subcommunicator(dealii::DoFHandler<dim, spacedim> const & dof_handler)
50{
51 unsigned int n_locally_owned_cells = 0;
52 for(auto const & cell : dof_handler.active_cell_iterators())
53 if(cell->is_locally_owned())
54 ++n_locally_owned_cells;
55
56 MPI_Comm const mpi_comm = dof_handler.get_mpi_communicator();
57
58 // In case some of the MPI ranks do not have cells, we create a
59 // sub-communicator to exclude all those processes from the MPI
60 // communication in the matrix-based operations and hence speed up those
61 // operations. Note that we have to free the communicator again, which is
62 // done by a custom deleter of the unique pointer that is run when it goes
63 // out of scope.
64 if(dealii::Utilities::MPI::min(n_locally_owned_cells, mpi_comm) == 0)
65 {
66 std::unique_ptr<MPI_Comm, void (*)(MPI_Comm *)> subcommunicator(new MPI_Comm,
67 [](MPI_Comm * comm) {
68 MPI_Comm_free(comm);
69 delete comm;
70 });
71 MPI_Comm_split(mpi_comm,
72 n_locally_owned_cells > 0,
73 dealii::Utilities::MPI::this_mpi_process(mpi_comm),
74 subcommunicator.get());
75
76 return subcommunicator;
77 }
78 else
79 {
80 std::unique_ptr<MPI_Comm, void (*)(MPI_Comm *)> communicator(new MPI_Comm, [](MPI_Comm * comm) {
81 delete comm;
82 });
83 *communicator = mpi_comm;
84
85 return communicator;
86 }
87}
88
89#ifdef DEAL_II_WITH_TRILINOS
90inline Teuchos::ParameterList
91get_ML_parameter_list(dealii::TrilinosWrappers::PreconditionAMG::AdditionalData const & ml_data,
92 int const dimension)
93{
94 Teuchos::ParameterList parameter_list;
95
96 // Slightly modified from deal::PreconditionAMG::AdditionalData::set_parameters().
97 if(ml_data.elliptic == true)
98 {
99 ML_Epetra::SetDefaults("SA", parameter_list);
100 }
101 else
102 {
103 ML_Epetra::SetDefaults("NSSA", parameter_list);
104 parameter_list.set("aggregation: block scaling", true);
105 }
106 parameter_list.set("aggregation: type", "Uncoupled");
107 parameter_list.set("smoother: type", ml_data.smoother_type);
108 parameter_list.set("coarse: type", ml_data.coarse_type);
109
110// Force re-initialization of the random seed to make ML deterministic
111// (only supported in trilinos >12.2):
112# if DEAL_II_TRILINOS_VERSION_GTE(12, 4, 0)
113 parameter_list.set("initialize random seed", true);
114# endif
115
116 parameter_list.set("smoother: sweeps", static_cast<int>(ml_data.smoother_sweeps));
117 parameter_list.set("cycle applications", static_cast<int>(ml_data.n_cycles));
118 if(ml_data.w_cycle == true)
119 {
120 parameter_list.set("prec type", "MGW");
121 }
122 else
123 {
124 parameter_list.set("prec type", "MGV");
125 }
126
127 parameter_list.set("smoother: Chebyshev alpha", 10.);
128 parameter_list.set("smoother: ifpack overlap", static_cast<int>(ml_data.smoother_overlap));
129 parameter_list.set("aggregation: threshold", ml_data.aggregation_threshold);
130
131 // Minimum size of the coarse problem, i.e. no coarser problems
132 // smaller than `coarse: max size` are constructed.
133 parameter_list.set("coarse: max size", 2000);
134
135 // This extends the settings in deal::PreconditionAMG::AdditionalData::set_parameters().
136 parameter_list.set("repartition: enable", 1);
137 parameter_list.set("repartition: max min ratio", 1.3);
138 parameter_list.set("repartition: min per proc", 300);
139 parameter_list.set("repartition: partitioner", "Zoltan");
140 parameter_list.set("repartition: Zoltan dimensions", dimension);
141
142 if(ml_data.output_details)
143 {
144 parameter_list.set("ML output", 10);
145 }
146 else
147 {
148 parameter_list.set("ML output", 0);
149 }
150
151 return parameter_list;
152}
153
154template<typename Operator>
155class PreconditionerML : public PreconditionerBase<double>
156{
157private:
158 typedef dealii::LinearAlgebra::distributed::Vector<double> VectorType;
159
160 typedef dealii::TrilinosWrappers::PreconditionAMG::AdditionalData MLData;
161
162public:
163 // distributed sparse system matrix
164 dealii::TrilinosWrappers::SparseMatrix system_matrix;
165
166private:
167 dealii::TrilinosWrappers::PreconditionAMG amg;
168
169public:
170 PreconditionerML(Operator const & op, bool const initialize, MLData ml_data = MLData())
171 : pde_operator(op), ml_data(ml_data)
172 {
173 // initialize system matrix
174 pde_operator.init_system_matrix(system_matrix,
175 op.get_matrix_free().get_dof_handler().get_mpi_communicator());
176
177 if(initialize)
178 {
179 this->update();
180 }
181 }
182
183 void
184 vmult(VectorType & dst, VectorType const & src) const override
185 {
186 amg.vmult(dst, src);
187 }
188
189 void
190 apply_krylov_solver_with_amg_preconditioner(VectorType & dst,
191 VectorType const & src,
192 MultigridCoarseGridSolver const & solver_type,
193 SolverData const & solver_data) const
194 {
195 dealii::ReductionControl solver_control(solver_data.max_iter,
196 solver_data.abs_tol,
197 solver_data.rel_tol);
198
199 if(solver_type == MultigridCoarseGridSolver::CG)
200 {
201 dealii::SolverCG<VectorType> solver(solver_control);
202 solver.solve(system_matrix, dst, src, *this);
203 }
204 else if(solver_type == MultigridCoarseGridSolver::GMRES)
205 {
206 typename dealii::SolverGMRES<VectorType>::AdditionalData gmres_data;
207 gmres_data.max_n_tmp_vectors = solver_data.max_krylov_size;
208 gmres_data.right_preconditioning = true;
209
210 dealii::SolverGMRES<VectorType> solver(solver_control, gmres_data);
211 solver.solve(system_matrix, dst, src, *this);
212 }
213 else
214 {
215 AssertThrow(false, dealii::ExcMessage("Not implemented."));
216 }
217 }
218
219 void
220 update() override
221 {
222 // Clear content of matrix since calculate_system_matrix() adds the result.
223 system_matrix *= 0.0;
224
225 // Re-calculate the system matrix.
226 pde_operator.calculate_system_matrix(system_matrix);
227
228 // Construct AMG preconditioner based on `Teuchos::ParameterList`.
229 unsigned int const dimension = pde_operator.get_matrix_free().dimension;
230 Teuchos::ParameterList parameter_list = get_ML_parameter_list(ml_data, dimension);
231
232 // Add near null space basis vectors to `Teuchos::ParameterList`.
233 // If the `std::vector<std::vector<double>> constant_modes_values`
234 // were filled, use these, otherwise use `std::vector<std::vector<bool>> constant_modes`.
235 std::vector<std::vector<bool>> constant_modes;
236 std::vector<std::vector<double>> constant_modes_values;
237 pde_operator.get_constant_modes(constant_modes, constant_modes_values);
238 MPI_Comm const & mpi_comm = system_matrix.get_mpi_communicator();
239 bool const some_constant_mode_set =
240 dealii::Utilities::MPI::logical_or(constant_modes.size() > 0, mpi_comm);
241 if(not some_constant_mode_set)
242 {
243 bool const some_constant_mode_values_set =
244 dealii::Utilities::MPI::logical_or(constant_modes_values.size() > 0, mpi_comm);
245 AssertThrow(some_constant_mode_values_set > 0,
246 dealii::ExcMessage(
247 "Neither `constant_modes` nor `constant_modes_values` were provided. "
248 "AMG setup requires near null space basis vectors."));
249
250 // Attach constant modes only if they have contributions on this processor subdomain.
251 if(constant_modes_values.size() > 0 and constant_modes_values[0].size() > 0)
252 {
253 ml_data.constant_modes_values = constant_modes_values;
254 }
255 }
256 else
257 {
258 // Attach constant modes only if they have contributions on this processor subdomain.
259 if(constant_modes.size() > 0 and constant_modes[0].size() > 0)
260 {
261 ml_data.constant_modes = constant_modes;
262 }
263 }
264
265 // Add near null space basis vectors to `Teuchos::ParameterList`.
266 // `ptr_distributed_modes` must stay alive for amg.initialize()
267 std::unique_ptr<Epetra_MultiVector> ptr_operator_modes;
268 ml_data.set_operator_null_space(parameter_list,
269 ptr_operator_modes,
270 system_matrix.trilinos_matrix());
271
272 // Initialize with the `Teuchos::ParameterList`.
273 amg.initialize(system_matrix, parameter_list);
274
275 this->update_needed = false;
276 }
277
278private:
279 // reference to matrix-free operator
280 Operator const & pde_operator;
281
282 MLData ml_data;
283};
284#endif
285
286#ifdef DEAL_II_WITH_PETSC
287/*
288 * Wrapper class for BoomerAMG from Hypre
289 */
290template<typename Operator, typename Number>
291class PreconditionerBoomerAMG : public PreconditionerBase<Number>
292{
293private:
294 typedef dealii::LinearAlgebra::distributed::Vector<Number> VectorType;
295
296 typedef dealii::PETScWrappers::PreconditionBoomerAMG::AdditionalData BoomerData;
297
298 // subcommunicator; declared before the matrix to ensure that it gets
299 // deleted after the matrix and preconditioner depending on it
300 std::unique_ptr<MPI_Comm, void (*)(MPI_Comm *)> subcommunicator;
301
302public:
303 // distributed sparse system matrix
304 dealii::PETScWrappers::MPI::SparseMatrix system_matrix;
305
306 // amg preconditioner for access by PETSc solver
307 dealii::PETScWrappers::PreconditionBoomerAMG amg;
308
309 PreconditionerBoomerAMG(Operator const & op,
310 bool const initialize,
311 BoomerData boomer_data = BoomerData())
312 : subcommunicator(
313 create_subcommunicator(op.get_matrix_free().get_dof_handler(op.get_dof_index()))),
314 pde_operator(op),
315 boomer_data(boomer_data)
316 {
317 // initialize system matrix
318 pde_operator.init_system_matrix(system_matrix, *subcommunicator);
319
320 if(initialize)
321 {
322 this->update();
323 }
324 }
325
326 ~PreconditionerBoomerAMG()
327 {
328 if(system_matrix.m() > 0)
329 {
330 PetscErrorCode ierr = VecDestroy(&petsc_vector_dst);
331 AssertThrow(ierr == 0, dealii::ExcPETScError(ierr));
332 ierr = VecDestroy(&petsc_vector_src);
333 AssertThrow(ierr == 0, dealii::ExcPETScError(ierr));
334 }
335 }
336
337 void
338 vmult(VectorType & dst, VectorType const & src) const override
339 {
340 if(system_matrix.m() > 0)
341 apply_petsc_operation(dst,
342 src,
343 petsc_vector_dst,
344 petsc_vector_src,
345 [&](dealii::PETScWrappers::VectorBase & petsc_dst,
346 dealii::PETScWrappers::VectorBase const & petsc_src) {
347 amg.vmult(petsc_dst, petsc_src);
348 });
349 }
350
351 void
352 apply_krylov_solver_with_amg_preconditioner(VectorType & dst,
353 VectorType const & src,
354 MultigridCoarseGridSolver const & solver_type,
355 SolverData const & solver_data) const
356 {
357 apply_petsc_operation(dst,
358 src,
359 system_matrix.get_mpi_communicator(),
360 [&](dealii::PETScWrappers::VectorBase & petsc_dst,
361 dealii::PETScWrappers::VectorBase const & petsc_src) {
362 dealii::ReductionControl solver_control(solver_data.max_iter,
363 solver_data.abs_tol,
364 solver_data.rel_tol);
365
366 if(solver_type == MultigridCoarseGridSolver::CG)
367 {
368 dealii::PETScWrappers::SolverCG solver(solver_control);
369 solver.solve(system_matrix, petsc_dst, petsc_src, amg);
370 }
371 else if(solver_type == MultigridCoarseGridSolver::GMRES)
372 {
373 dealii::PETScWrappers::SolverGMRES solver(solver_control);
374 solver.solve(system_matrix, petsc_dst, petsc_src, amg);
375 }
376 else
377 {
378 AssertThrow(false, dealii::ExcMessage("Not implemented."));
379 }
380 });
381 }
382
383 void
384 update() override
385 {
386 // clear content of matrix since the next calculate_system_matrix calls
387 // add their result; since we might run this on a sub-communicator, we
388 // skip the processes that do not participate in the matrix and have size
389 // zero
390 if(system_matrix.m() > 0)
391 system_matrix = 0.0;
392
393 calculate_preconditioner();
394
395 this->update_needed = false;
396 }
397
398private:
399 void
400 calculate_preconditioner()
401 {
402 // calculate_matrix in case the current MPI rank participates in the PETSc communicator
403 if(system_matrix.m() > 0)
404 {
405 pde_operator.calculate_system_matrix(system_matrix);
406
407 amg.initialize(system_matrix, boomer_data);
408
409 // get vector partitioner
410 dealii::LinearAlgebra::distributed::Vector<typename Operator::value_type> vector;
411 pde_operator.initialize_dof_vector(vector);
412 VecCreateMPI(system_matrix.get_mpi_communicator(),
413 vector.get_partitioner()->locally_owned_size(),
414 PETSC_DETERMINE,
415 &petsc_vector_dst);
416 VecCreateMPI(system_matrix.get_mpi_communicator(),
417 vector.get_partitioner()->locally_owned_size(),
418 PETSC_DETERMINE,
419 &petsc_vector_src);
420 }
421 }
422
423 // reference to MultigridOperator
424 Operator const & pde_operator;
425
426 BoomerData boomer_data;
427
428 // PETSc vector objects to avoid re-allocation in every vmult() operation
429 mutable Vec petsc_vector_src;
430 mutable Vec petsc_vector_dst;
431};
432#endif
433
437template<typename Operator, typename Number>
438class PreconditionerAMG : public PreconditionerBase<Number>
439{
440private:
441 typedef typename PreconditionerBase<Number>::VectorType VectorType;
442
443public:
444 PreconditionerAMG(Operator const & pde_operator, bool const initialize, AMGData const & data)
445 {
446 (void)pde_operator;
447 (void)initialize;
448 this->data = data;
449
450 if(data.amg_type == AMGType::BoomerAMG)
451 {
452#ifdef DEAL_II_WITH_PETSC
453 preconditioner_boomer =
454 std::make_shared<PreconditionerBoomerAMG<Operator, Number>>(pde_operator,
455 initialize,
456 data.boomer_data);
457#else
458 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with PETSc!"));
459#endif
460 }
461 else if(data.amg_type == AMGType::ML)
462 {
463#ifdef DEAL_II_WITH_TRILINOS
464 preconditioner_ml =
465 std::make_shared<PreconditionerML<Operator>>(pde_operator, initialize, data.ml_data);
466#else
467 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with Trilinos!"));
468#endif
469 }
470 else
471 {
472 AssertThrow(false, dealii::ExcNotImplemented());
473 }
474 }
475
476 void
477 vmult(VectorType & dst, VectorType const & src) const final
478 {
479 if(data.amg_type == AMGType::BoomerAMG)
480 {
481#ifdef DEAL_II_WITH_PETSC
482 preconditioner_boomer->vmult(dst, src);
483#else
484 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with PETSc!"));
485#endif
486 }
487 else if(data.amg_type == AMGType::ML)
488 {
489#ifdef DEAL_II_WITH_TRILINOS
491 dst,
492 src,
493 [&](dealii::LinearAlgebra::distributed::Vector<double> & dst_double,
494 dealii::LinearAlgebra::distributed::Vector<double> const & src_double) {
495 preconditioner_ml->vmult(dst_double, src_double);
496 });
497#else
498 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with Trilinos!"));
499#endif
500 }
501 else
502 {
503 AssertThrow(false, dealii::ExcNotImplemented());
504 }
505 }
506
507 void
508 apply_krylov_solver_with_amg_preconditioner(VectorType & dst,
509 VectorType const & src,
510 MultigridCoarseGridSolver const & solver_type,
511 SolverData const & solver_data) const
512 {
513 if(data.amg_type == AMGType::BoomerAMG)
514 {
515#ifdef DEAL_II_WITH_PETSC
516 std::shared_ptr<PreconditionerBoomerAMG<Operator, Number>> preconditioner =
517 std::dynamic_pointer_cast<PreconditionerBoomerAMG<Operator, Number>>(preconditioner_boomer);
518
519 preconditioner->apply_krylov_solver_with_amg_preconditioner(dst,
520 src,
521 solver_type,
522 solver_data);
523#else
524 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with PETSc!"));
525#endif
526 }
527 else if(data.amg_type == AMGType::ML)
528 {
529#ifdef DEAL_II_WITH_TRILINOS
530 std::shared_ptr<PreconditionerML<Operator>> preconditioner =
531 std::dynamic_pointer_cast<PreconditionerML<Operator>>(preconditioner_ml);
532
534 dst,
535 src,
536 [&](dealii::LinearAlgebra::distributed::Vector<double> & dst_double,
537 dealii::LinearAlgebra::distributed::Vector<double> const & src_double) {
538 preconditioner->apply_krylov_solver_with_amg_preconditioner(dst_double,
539 src_double,
540 solver_type,
541 solver_data);
542 });
543#else
544 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with Trilinos!"));
545#endif
546 }
547 else
548 {
549 AssertThrow(false, dealii::ExcNotImplemented());
550 }
551 }
552
553 void
554 update() final
555 {
556 if(data.amg_type == AMGType::BoomerAMG)
557 {
558#ifdef DEAL_II_WITH_PETSC
559 preconditioner_boomer->update();
560#else
561 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with PETSc!"));
562#endif
563 }
564 else if(data.amg_type == AMGType::ML)
565 {
566#ifdef DEAL_II_WITH_TRILINOS
567 preconditioner_ml->update();
568#else
569 AssertThrow(false, dealii::ExcMessage("deal.II is not compiled with Trilinos!"));
570#endif
571 }
572 else
573 {
574 AssertThrow(false, dealii::ExcNotImplemented());
575 }
576
577 this->update_needed = false;
578 }
579
580private:
581 AMGData data;
582
583 std::shared_ptr<PreconditionerBase<Number>> preconditioner_boomer;
584
585 std::shared_ptr<PreconditionerBase<double>> preconditioner_ml;
586};
587
588} // namespace ExaDG
589
590#endif /* EXADG_SOLVERS_AND_PRECONDITIONERS_PRECONDITIONERS_PRECONDITIONER_AMG_H_ */
Definition preconditioner_base.h:35
Definition driver.cpp:33
void apply_function_in_double_precision(dealii::LinearAlgebra::distributed::Vector< Number > &dst, dealii::LinearAlgebra::distributed::Vector< Number > const &src, std::function< void(dealii::LinearAlgebra::distributed::Vector< double > &, dealii::LinearAlgebra::distributed::Vector< double > const &)> operation)
Definition linear_algebra_utilities.h:141
Definition multigrid_parameters.h:98
Definition solver_data.h:34