PRISMS-PF Manual v3.0-pre
All Classes Functions Variables Enumerations Pages
linear_solver_gmg.h
1// SPDX-FileCopyrightText: © 2025 PRISMS Center at the University of Michigan
2// SPDX-License-Identifier: GNU Lesser General Public Version 2.1
3
4#ifndef linear_solver_gmg_h
5#define linear_solver_gmg_h
6
7#include <deal.II/grid/grid_tools.h>
8#include <deal.II/lac/precondition.h>
9#include <deal.II/lac/solver_cg.h>
10#include <deal.II/matrix_free/matrix_free.h>
11#include <deal.II/matrix_free/operators.h>
12#include <deal.II/multigrid/mg_coarse.h>
13#include <deal.II/multigrid/mg_constrained_dofs.h>
14#include <deal.II/multigrid/mg_matrix.h>
15#include <deal.II/multigrid/mg_smoother.h>
16#include <deal.II/multigrid/mg_tools.h>
17#include <deal.II/multigrid/mg_transfer_global_coarsening.h>
18#include <deal.II/multigrid/mg_transfer_matrix_free.h>
19#include <deal.II/multigrid/multigrid.h>
20
21#include <prismspf/config.h>
22#include <prismspf/core/dof_handler.h>
23#include <prismspf/core/solution_output.h>
24#include <prismspf/core/triangulation_handler.h>
25#include <prismspf/solvers/linear_solver_base.h>
26
27#ifdef PRISMS_PF_WITH_CALIPER
28# include <caliper/cali.h>
29#endif
30
31PRISMS_PF_BEGIN_NAMESPACE
32
36template <int dim, int degree, typename number>
37class customPDE;
38
42template <int dim, int degree>
43class GMGSolver : public linearSolverBase<dim, degree>
44{
45public:
48 using VectorType = dealii::LinearAlgebra::distributed::Vector<double>;
49 using MGVectorType = dealii::LinearAlgebra::distributed::Vector<float>;
50
54 GMGSolver(const userInputParameters<dim> &_user_inputs,
55 const variableAttributes &_variable_attributes,
56 const matrixfreeHandler<dim> &_matrix_free_handler,
57 const constraintHandler<dim> &_constraint_handler,
58 const triangulationHandler<dim> &_triangulation_handler,
59 const dofHandler<dim> &_dof_handler,
60 dealii::MGLevelObject<matrixfreeHandler<dim, float>> &_mg_matrix_free_handler,
61 solutionHandler<dim> &_solution_handler);
62
66 ~GMGSolver() override;
67
71 void
72 init() override;
73
77 void
78 reinit() override;
79
83 void
84 solve(const double step_length = 1.0) override;
85
86private:
90 const triangulationHandler<dim> &triangulation_handler;
91
95 const dofHandler<dim> &dof_handler;
96
100 dealii::MGLevelObject<matrixfreeHandler<dim, float>> &mg_matrix_free_handler;
101
105 unsigned int min_level = 0;
106
110 unsigned int max_level = 0;
111
115 const dealii::MappingQ1<dim> mapping;
116
120 dealii::MGLevelObject<dealii::MGTwoLevelTransfer<dim, MGVectorType>>
121 mg_transfer_operators;
122
126 std::unique_ptr<dealii::MGLevelObject<LevelMatrixType>> mg_operators;
127
131 std::shared_ptr<dealii::mg::Matrix<MGVectorType>> mg_matrix;
132
136 std::shared_ptr<dealii::MGTransferGlobalCoarsening<dim, MGVectorType>> mg_transfer;
137
142 dealii::MGLevelObject<std::vector<MGVectorType *>> mg_newton_update_src;
143};
144
145template <int dim, int degree>
147 const userInputParameters<dim> &_user_inputs,
148 const variableAttributes &_variable_attributes,
149 const matrixfreeHandler<dim> &_matrix_free_handler,
150 const constraintHandler<dim> &_constraint_handler,
151 const triangulationHandler<dim> &_triangulation_handler,
152 const dofHandler<dim> &_dof_handler,
153 dealii::MGLevelObject<matrixfreeHandler<dim, float>> &_mg_matrix_free_handler,
154 solutionHandler<dim> &_solution_handler)
155 : linearSolverBase<dim, degree>(_user_inputs,
156 _variable_attributes,
157 _matrix_free_handler,
158 _constraint_handler,
159 _solution_handler)
160 , triangulation_handler(_triangulation_handler)
161 , dof_handler(_dof_handler)
162 , mg_matrix_free_handler(_mg_matrix_free_handler)
163{}
164
165template <int dim, int degree>
167{
168 for (unsigned int level = min_level; level <= max_level; ++level)
169 {
170 for (const auto *vector : mg_newton_update_src[level])
171 {
172 delete vector;
173 }
174 }
175}
176
177template <int dim, int degree>
178inline void
180{
181 // We solve the system with a global coarsening approach. There are two options when
182 // doing this: geometric coarsening and polynomial coarsening. We only support geometric
183 // as of now.
184
185 // Grab the min and max level
186 min_level = triangulation_handler.get_mg_min_level();
187 max_level = triangulation_handler.get_mg_max_level();
188
189 // Init the multilevel operator objects
190 mg_operators =
191 std::make_unique<dealii::MGLevelObject<LevelMatrixType>>(min_level,
192 max_level,
193 this->user_inputs,
194 this->field_index,
195 this->subset_attributes);
196 mg_transfer_operators.resize(min_level, max_level);
197
198 // Object for constraints on different levels
199 mg_matrix_free_handler.resize(min_level, max_level, this->user_inputs);
200
201 // Setup operator on each level
202 mg_newton_update_src.resize(min_level, max_level);
203 for (unsigned int level = min_level; level <= max_level; ++level)
204 {
205 // TODO (landinjm): Fix so mapping is same as rest of the problem. Do the same for
206 // the finite element I think.
207 mg_matrix_free_handler[level].reinit(
208 mapping,
209 dof_handler.get_mg_dof_handler(this->field_index, level),
210 this->constraint_handler.get_mg_constraint(this->field_index, level),
211 dealii::QGaussLobatto<1>(degree + 1));
212
213 (*mg_operators)[level].initialize(mg_matrix_free_handler[level].get_matrix_free());
214
215 (*mg_operators)[level].add_global_to_local_mapping(
216 this->newton_update_global_to_local_solution);
217
218 // Setup src solutions for each level
219 mg_newton_update_src[level].resize(this->newton_update_src.size());
220 for (const auto &[pair, local_index] : this->newton_update_global_to_local_solution)
221 {
222 mg_newton_update_src[level][local_index] = new MGVectorType();
223 (*mg_operators)[level]
224 .initialize_dof_vector(*mg_newton_update_src[level][local_index], pair.first);
225 }
226
227 // Check that the vector partitioning is right
228 // TODO (landinjm): Check this for all of the vectors. May also be able to remove
229 // this
230 Assert((*mg_operators)[level]
231 .get_matrix_free()
232 ->get_vector_partitioner(this->field_index)
233 ->is_compatible(*(mg_newton_update_src[level][0]->get_partitioner())),
234 dealii::ExcMessage("Incompatabile vector partitioners"));
235
236 (*mg_operators)[level].add_src_solution_subset(mg_newton_update_src[level]);
237 }
238 mg_matrix = std::make_shared<dealii::mg::Matrix<MGVectorType>>(*mg_operators);
239
240 // Setup transfer operators
241 for (unsigned int level = min_level; level < max_level; ++level)
242 {
243 mg_transfer_operators[level + 1].reinit(
244 dof_handler.get_mg_dof_handler(this->field_index, level + 1),
245 dof_handler.get_mg_dof_handler(this->field_index, level),
246 this->constraint_handler.get_mg_constraint(this->field_index, level + 1),
247 this->constraint_handler.get_mg_constraint(this->field_index, level));
248 }
249 mg_transfer = std::make_shared<dealii::MGTransferGlobalCoarsening<dim, MGVectorType>>(
250 mg_transfer_operators);
251
252 this->system_matrix->clear();
253 this->system_matrix->initialize(this->matrix_free_handler.get_matrix_free());
254 this->update_system_matrix->clear();
255 this->update_system_matrix->initialize(this->matrix_free_handler.get_matrix_free());
256
257 this->system_matrix->add_global_to_local_mapping(
258 this->residual_global_to_local_solution);
259 this->system_matrix->add_src_solution_subset(this->residual_src);
260
261 this->update_system_matrix->add_global_to_local_mapping(
262 this->newton_update_global_to_local_solution);
263 this->update_system_matrix->add_src_solution_subset(this->newton_update_src);
264
265 // Apply constraints
266 this->constraint_handler.get_constraint(this->field_index)
267 .distribute(*(this->solution_handler.solution_set.at(
268 std::make_pair(this->field_index, dependencyType::NORMAL))));
269
270 // TODO (landinjm): Should I put this somewhere else?
271#ifdef DEBUG
273 << "\nMultigrid Setup Information for index " << this->field_index << ":\n"
274 << " Min level: " << min_level << "\n"
275 << " Max level: " << max_level << "\n";
276 for (unsigned int level = min_level; level <= max_level; ++level)
277 {
279 << " Level: " << level << "\n"
280 << " Cells: "
281 << triangulation_handler.get_mg_triangulation(level).n_global_active_cells()
282 << "\n"
283 << " DoFs: "
284 << dof_handler.get_mg_dof_handler(this->field_index, level).n_dofs() << "\n"
285 << " Constrained DoFs: "
286 << this->constraint_handler.get_mg_constraint(this->field_index, level)
287 .n_constraints()
288 << "\n";
289 }
291 << " MG vertical communication efficiency: "
292 << dealii::MGTools::vertical_communication_efficiency(
293 triangulation_handler.get_mg_triangulation())
294 << "\n"
295 << " MG workload imbalance: "
296 << dealii::MGTools::workload_imbalance(triangulation_handler.get_mg_triangulation())
297 << "\n\n"
298 << std::flush;
299#endif
300}
301
302template <int dim, int degree>
303inline void
306
307template <int dim, int degree>
308inline void
309GMGSolver<dim, degree>::solve(const double step_length)
310{
311 const auto *current_dof_handler = dof_handler.get_dof_handlers().at(this->field_index);
312 auto *solution = this->solution_handler.solution_set.at(
313 std::make_pair(this->field_index, dependencyType::NORMAL));
314
315 // Compute the residual
316 this->system_matrix->compute_residual(*this->residual, *solution);
318 << " field: " << this->field_index
319 << " Initial residual: " << this->residual->l2_norm() << std::flush;
320
321 // Determine the residual tolerance
322 this->compute_solver_tolerance();
323
324 // Update solver controls
325 this->solver_control.set_tolerance(this->tolerance);
326 dealii::SolverCG<VectorType> cg(this->solver_control);
327
328 // Interpolate the newton update src vector to each multigrid level
329 for (unsigned int local_index = 0; local_index < this->newton_update_src.size();
330 local_index++)
331 {
332 // Create a temporary collection of the the dst pointers
333 dealii::MGLevelObject<MGVectorType> mg_src_subset(min_level, max_level);
334 for (unsigned int level = min_level; level < max_level; ++level)
335 {
336 mg_src_subset[level] = *mg_newton_update_src[level][local_index];
337 }
338
339 // Interpolate
340 mg_transfer->interpolate_to_mg(*current_dof_handler,
341 mg_src_subset,
342 *this->newton_update_src[local_index]);
343 }
344
345 // Create smoother for each level
346 using SmootherType = dealii::PreconditionChebyshev<LevelMatrixType, MGVectorType>;
347 dealii::MGSmootherPrecondition<LevelMatrixType, SmootherType, MGVectorType> mg_smoother;
348 dealii::MGLevelObject<typename SmootherType::AdditionalData> smoother_data(min_level,
349 max_level);
350 for (unsigned int level = min_level; level <= max_level; ++level)
351 {
352 smoother_data[level].smoothing_range =
353 this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index)
354 .smoothing_range;
355 smoother_data[level].degree =
356 this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index)
357 .smoother_degree;
358 smoother_data[level].eig_cg_n_iterations =
359 this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index)
360 .eig_cg_n_iterations;
361 (*mg_operators)[level].compute_diagonal(this->field_index);
362 smoother_data[level].preconditioner =
363 (*mg_operators)[level].get_matrix_diagonal_inverse();
364 smoother_data[level].constraints.copy_from(
365 this->constraint_handler.get_mg_constraint(this->field_index, level));
366 }
367 mg_smoother.initialize(*mg_operators, smoother_data);
368
369 dealii::MGCoarseGridApplySmoother<MGVectorType> mg_coarse;
370 mg_coarse.initialize(mg_smoother);
371
372 // Create multigrid object
373 dealii::Multigrid<MGVectorType> mg(*mg_matrix,
374 mg_coarse,
375 *mg_transfer,
376 mg_smoother,
377 mg_smoother,
378 min_level,
379 max_level,
380 dealii::Multigrid<MGVectorType>::Cycle::v_cycle);
381
382 // Create the preconditioner
383 dealii::PreconditionMG<dim,
384 MGVectorType,
385 dealii::MGTransferGlobalCoarsening<dim, MGVectorType>>
386 preconditioner(*current_dof_handler, mg, *mg_transfer);
387
388 try
389 {
390 *this->newton_update = 0.0;
391 cg.solve(*(this->update_system_matrix),
392 *this->newton_update,
393 *this->residual,
394 preconditioner);
395 }
396 catch (...)
397 {
399 << "Warning: linear solver did not converge as per set tolerances.\n";
400 }
401 this->constraint_handler.get_constraint(this->field_index)
402 .set_zero(*this->newton_update);
403
405 << " Final residual: " << this->solver_control.last_value()
406 << " Steps: " << this->solver_control.last_step() << "\n"
407 << std::flush;
408
409 // Update the solutions
410 (*solution).add(step_length, *this->newton_update);
411 this->solution_handler.update(fieldSolveType::NONEXPLICIT_LINEAR, this->field_index);
412
413 // Apply constraints
414 // This may be redundant with the constraints on the update step.
415 this->constraint_handler.get_constraint(this->field_index).distribute(*solution);
416}
417
418PRISMS_PF_END_NAMESPACE
419
420#endif
Class that handles the assembly and solving of a field with a GMG preconditioner.
Definition linear_solver_gmg.h:44
void init() override
Initialize the system.
Definition linear_solver_gmg.h:179
void reinit() override
Reinitialize the system.
Definition linear_solver_gmg.h:304
GMGSolver(const userInputParameters< dim > &_user_inputs, const variableAttributes &_variable_attributes, const matrixfreeHandler< dim > &_matrix_free_handler, const constraintHandler< dim > &_constraint_handler, const triangulationHandler< dim > &_triangulation_handler, const dofHandler< dim > &_dof_handler, dealii::MGLevelObject< matrixfreeHandler< dim, float > > &_mg_matrix_free_handler, solutionHandler< dim > &_solution_handler)
Constructor.
Definition linear_solver_gmg.h:146
void solve(const double step_length=1.0) override
Solve the system Ax=b.
Definition linear_solver_gmg.h:309
~GMGSolver() override
Destructor.
Definition linear_solver_gmg.h:166
static dealii::ConditionalOStream & pout_base()
Generic parallel output stream. Used for essential information in release and debug mode.
Definition conditional_ostreams.cc:31
static dealii::ConditionalOStream & pout_summary()
Log output stream for writing a summary.log file.
Definition conditional_ostreams.cc:22
The class handles the generation and application of boundary conditions based on the user-inputs.
Definition constraint_handler.h:25
Definition explicit_base.h:27
Class that manages the deal.II DoFHandlers.
Definition dof_handler.h:25
Base class that handles the assembly and linear solving of a field.
Definition linear_solver_base.h:36
virtual void reinit()=0
Reinitialize the system.
This class handlers the management and access of the matrix-free objects.
Definition matrix_free_handler.h:25
Class that manages solution initialization and swapping with old solutions.
Definition solution_handler.h:22
This class handlers the generation and manipulation of triangulations.
Definition triangulation_handler.h:24
Definition user_input_parameters.h:22
Structure to hold the variable attributes of a field. This includes things like the name,...
Definition variable_attributes.h:39