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