Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
DistributedAMG.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* DistributedAMG.h (C) 2000-2026 */
9/* */
10/* Distributed memory AMG preconditioner. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_DISTRIBUTEDAMG_H
13#define ARCCORE_ALINA_DISTRIBUTEDAMG_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26#include <iostream>
27#include <iomanip>
28#include <list>
29#include <memory>
30
31#include "arccore/alina/BackendInterface.h"
32#include "arccore/alina/MessagePassingUtils.h"
33#include "arccore/alina/DistributedMatrix.h"
34#include "arccore/alina/DistributedSkylineLUDirectSolver.h"
35#include "arccore/alina/SimpleMatrixPartitioner.h"
36
37/*---------------------------------------------------------------------------*/
38/*---------------------------------------------------------------------------*/
39
40namespace Arcane::Alina
41{
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
45
46template <class Backend,
47 class Coarsening,
48 class Relaxation,
50 class Repartition = SimpleMatrixPartitioner<Backend>>
51class DistributedAMG
52{
53 public:
54
55 using backend_type = Backend;
56 using BackendType = Backend;
57
58 typedef typename Backend::params backend_params;
59 typedef typename Backend::value_type value_type;
60 typedef typename math::scalar_of<value_type>::type scalar_type;
61 typedef DistributedMatrix<Backend> matrix;
62 typedef typename Backend::vector vector;
63
64 struct params
65 {
66 typedef typename Coarsening::params coarsening_params;
67 typedef typename Relaxation::params relax_params;
68 typedef typename DirectSolver::params direct_params;
69 typedef typename Repartition::params repart_params;
70
71 coarsening_params coarsening;
72 relax_params relax;
73 direct_params direct;
74 repart_params repart;
75
83 Int32 coarse_enough = DirectSolver::coarse_enough();
84
91 bool direct_coarse = true;
92
100 Int32 max_levels = std::numeric_limits<Int32>::max();
101
104
107
110
113
115 bool allow_rebuild = false;
116
117 params() = default;
118
119 params(const PropertyTree& p)
120 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, coarsening)
121 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, relax)
122 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, direct)
123 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, repart)
124 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, coarse_enough)
125 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, direct_coarse)
126 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, max_levels)
127 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, npre)
128 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, npost)
129 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ncycle)
130 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, pre_cycles)
131 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, allow_rebuild)
132 {
133 p.check_params({ "coarsening", "relax", "direct", "repart", "coarse_enough", "direct_coarse", "max_levels", "npre", "npost", "ncycle", "pre_cycles", "allow_rebuild" });
134
135 Alina::precondition(max_levels > 0, "max_levels should be positive");
136 }
137
138 void get(Alina::PropertyTree& p, const std::string& path = "") const
139 {
140 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, coarsening);
141 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, relax);
142 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, direct);
143 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, repart);
144 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, coarse_enough);
145 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, direct_coarse);
146 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, max_levels);
147 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, npre);
148 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, npost);
149 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ncycle);
150 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, pre_cycles);
151 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, allow_rebuild);
152 }
153 } prm;
154
155 template <class Matrix>
156 DistributedAMG([[maybe_unused]] const AlinaContext& context,
157 AlinaCommunicator comm,
158 const Matrix& A,
159 const params& prm = params(),
160 const backend_params& bprm = backend_params())
161 : prm(prm)
162 , comm(comm)
163 , repart(prm.repart)
164 {
165 init(std::make_shared<matrix>(comm, A, backend::nbRow(A)), bprm);
166 }
167
168 DistributedAMG([[maybe_unused]] const AlinaContext& context,
169 AlinaCommunicator comm,
170 std::shared_ptr<matrix> A,
171 const params& prm = params(),
172 const backend_params& bprm = backend_params())
173 : prm(prm)
174 , comm(comm)
175 , repart(prm.repart)
176 {
177 init(A, bprm);
178 }
179
186 template <class Matrix>
187 void rebuild(const Matrix& M,
188 const backend_params& bprm = backend_params())
189 {
190 rebuild(std::make_shared<matrix>(comm, M, backend::nbRow(M)), bprm);
191 }
192
193 template <class OtherBackend>
194 typename std::enable_if<!std::is_same<Backend, OtherBackend>::value, void>::type
195 rebuild(std::shared_ptr<DistributedMatrix<OtherBackend>> A,
196 const backend_params& bprm = backend_params())
197 {
198 return rebuild(std::make_shared<matrix>(*A), bprm);
199 }
200
201 void rebuild(std::shared_ptr<matrix> A,
202 const backend_params& bprm = backend_params())
203 {
204 precondition(prm.allow_rebuild, "allow_rebuild is not set!");
205 precondition(A->glob_rows() == system_matrix().glob_rows() &&
206 A->glob_cols() == system_matrix().glob_cols(),
207 "Matrix dimensions differ from the original ones!");
208
209 ARCCORE_ALINA_TIC("rebuild");
210 this->A = A;
211 Coarsening C(prm.coarsening);
212 for (auto& level : levels) {
213 A = level.rebuild(A, C, prm, bprm);
214 }
215 this->A->move_to_backend(bprm);
216 ARCCORE_ALINA_TOC("rebuild");
217 }
218
219 template <class Vec1, class Vec2>
220 void cycle(const Vec1& rhs, Vec2&& x) const
221 {
222 cycle(levels.begin(), rhs, x);
223 }
224
225 template <class Vec1, class Vec2>
226 void apply(const Vec1& rhs, Vec2&& x) const
227 {
228 if (prm.pre_cycles) {
229 backend::clear(x);
230 for (unsigned i = 0; i < prm.pre_cycles; ++i)
231 cycle(levels.begin(), rhs, x);
232 }
233 else {
234 backend::copy(rhs, x);
235 }
236 }
237
239 std::shared_ptr<matrix> system_matrix_ptr() const
240 {
241 return A;
242 }
243
244 const matrix& system_matrix() const
245 {
246 return *system_matrix_ptr();
247 }
248
249 private:
250
251 struct DistributedAMGLevel
252 {
253 ptrdiff_t nrows, nnz;
254 int active_procs;
255
256 std::shared_ptr<matrix> A, P, R;
257 std::shared_ptr<vector> f, u, t;
258 std::shared_ptr<Relaxation> relax;
259 std::shared_ptr<DirectSolver> solve;
260
261 DistributedAMGLevel() = default;
262
263 DistributedAMGLevel(std::shared_ptr<matrix> a,
264 params& prm,
265 const backend_params& bprm,
266 bool direct = false)
267 : nrows(a->glob_rows())
268 , nnz(a->glob_nonzeros())
269 , f(Backend::create_vector(a->loc_rows(), bprm))
270 , u(Backend::create_vector(a->loc_rows(), bprm))
271 {
272 int active = (a->loc_rows() > 0);
273 active_procs = a->comm().reduceSum(active);
274
275 sort_rows(*a);
276
277 if (direct) {
278 ARCCORE_ALINA_TIC("direct solver");
279 solve = std::make_shared<DirectSolver>(a->comm(), *a, prm.direct);
280 ARCCORE_ALINA_TOC("direct solver");
281 }
282 else {
283 A = a;
284 t = Backend::create_vector(a->loc_rows(), bprm);
285
286 ARCCORE_ALINA_TIC("relaxation");
287 relax = std::make_shared<Relaxation>(*a, prm.relax, bprm);
288 ARCCORE_ALINA_TOC("relaxation");
289 }
290 }
291
292 std::shared_ptr<matrix> step_down(Coarsening& C, const Repartition& repart)
293 {
294 ARCCORE_ALINA_TIC("transfer operators");
295 std::tie(P, R) = C.transfer_operators(*A);
296
297 ARCCORE_ALINA_TIC("sort");
298 sort_rows(*P);
299 sort_rows(*R);
300 ARCCORE_ALINA_TOC("sort");
301
302 ARCCORE_ALINA_TOC("transfer operators");
303
304 if (P->glob_cols() == 0) {
305 // Zero-sized coarse level in AMG (diagonal matrix?)
306 return std::shared_ptr<matrix>();
307 }
308
309 ARCCORE_ALINA_TIC("coarse operator");
310 auto Ac = C.coarse_operator(*A, *P, *R);
311 ARCCORE_ALINA_TOC("coarse operator");
312
313 if (repart.is_needed(*Ac)) {
314 ARCCORE_ALINA_TIC("partition");
315 auto I = repart(*Ac, block_size(C));
316 auto J = transpose(*I);
317
318 P = product(*P, *I);
319 R = product(*J, *R);
320 Ac = product(*J, *product(*Ac, *I));
321 ARCCORE_ALINA_TOC("partition");
322 }
323
324 return Ac;
325 }
326
327 std::shared_ptr<matrix> rebuild(std::shared_ptr<matrix> A,
328 const Coarsening& C,
329 const params& prm,
330 const backend_params& bprm)
331 {
332 if (relax) {
333 relax = std::make_shared<Relaxation>(*A, prm.relax, bprm);
334 std::cout << "DistributedAMG: relaxation=" << relax << "\n";
335 }
336
337 if (solve) {
338 solve = std::make_shared<DirectSolver>(A->comm(), *A, prm.direct);
339 }
340
341 if (this->A) {
342 this->A = A;
343 }
344
345 if (P && R) {
346 A = C.coarse_operator(*A, *P, *R);
347 }
348
349 if (this->A) {
350 this->A->move_to_backend(bprm);
351 }
352
353 return A;
354 }
355
356 void move_to_backend(const backend_params& bprm, bool keep_src = false)
357 {
358 ARCCORE_ALINA_TIC("move to backend");
359 if (A)
360 A->move_to_backend(bprm);
361 if (P)
362 P->move_to_backend(bprm, keep_src);
363 if (R)
364 R->move_to_backend(bprm, keep_src);
365 ARCCORE_ALINA_TOC("move to backend");
366 }
367
368 ptrdiff_t rows() const
369 {
370 return nrows;
371 }
372
373 ptrdiff_t nonzeros() const
374 {
375 return nnz;
376 }
377 };
378
379 typedef typename std::list<DistributedAMGLevel>::const_iterator level_iterator;
380
382 std::shared_ptr<matrix> A;
383 Repartition repart;
384 std::list<DistributedAMGLevel> levels;
385
386 void init(std::shared_ptr<matrix> A, const backend_params& bprm)
387 {
388 A->comm().check(A->glob_rows() == A->glob_cols(), "Matrix should be square!");
389
390 this->A = A;
391 Coarsening C(prm.coarsening);
392 //std::cout << "DistributedAMGInit: Coarsening=" << C << "\n";
393 bool need_coarse = true;
394
395 while (A->glob_rows() > prm.coarse_enough) {
396 levels.push_back(DistributedAMGLevel(A, prm, bprm));
397
398 if (levels.size() >= prm.max_levels) {
399 levels.back().move_to_backend(bprm, prm.allow_rebuild);
400 break;
401 }
402
403 A = levels.back().step_down(C, repart);
404 levels.back().move_to_backend(bprm, prm.allow_rebuild);
405
406 if (!A) {
407 // Zero-sized coarse level. Probably the system matrix on
408 // this level is diagonal, should be easily solvable with a
409 // couple of smoother iterations.
410 need_coarse = false;
411 break;
412 }
413 }
414
415 if (!A || A->glob_rows() > prm.coarse_enough) {
416 // The coarse matrix is still too big to be solved directly.
417 need_coarse = false;
418 }
419
420 if (A && need_coarse) {
421 levels.push_back(DistributedAMGLevel(A, prm, bprm, prm.direct_coarse));
422 levels.back().move_to_backend(bprm, prm.allow_rebuild);
423 }
424
425 ARCCORE_ALINA_TIC("move to backend");
426 this->A->move_to_backend(bprm, prm.allow_rebuild);
427 ARCCORE_ALINA_TOC("move to backend");
428 }
429
430 template <class Vec1, class Vec2>
431 void cycle(level_iterator lvl, const Vec1& rhs, Vec2& x) const
432 {
433 level_iterator nxt = lvl, end = levels.end();
434 ++nxt;
435
436 if (nxt == end) {
437 if (lvl->solve) {
438 ARCCORE_ALINA_TIC("direct solver");
439 (*lvl->solve)(rhs, x);
440 ARCCORE_ALINA_TOC("direct solver");
441 }
442 else {
443 ARCCORE_ALINA_TIC("relax");
444 for (size_t i = 0; i < prm.npre; ++i)
445 lvl->relax->apply_pre(*lvl->A, rhs, x, *lvl->t);
446 for (size_t i = 0; i < prm.npost; ++i)
447 lvl->relax->apply_post(*lvl->A, rhs, x, *lvl->t);
448 ARCCORE_ALINA_TOC("relax");
449 }
450 }
451 else {
452 for (size_t j = 0; j < prm.ncycle; ++j) {
453 ARCCORE_ALINA_TIC("relax");
454 for (size_t i = 0; i < prm.npre; ++i)
455 lvl->relax->apply_pre(*lvl->A, rhs, x, *lvl->t);
456 ARCCORE_ALINA_TOC("relax");
457
458 backend::residual(rhs, *lvl->A, x, *lvl->t);
459
460 backend::spmv(math::identity<scalar_type>(), *lvl->R, *lvl->t, math::zero<scalar_type>(), *nxt->f);
461
462 backend::clear(*nxt->u);
463 cycle(nxt, *nxt->f, *nxt->u);
464
465 backend::spmv(math::identity<scalar_type>(), *lvl->P, *nxt->u, math::identity<scalar_type>(), x);
466
467 ARCCORE_ALINA_TIC("relax");
468 for (size_t i = 0; i < prm.npost; ++i)
469 lvl->relax->apply_post(*lvl->A, rhs, x, *lvl->t);
470 ARCCORE_ALINA_TOC("relax");
471 }
472 }
473 }
474
475 template <class B, class C, class R, class D, class I>
476 friend std::ostream& operator<<(std::ostream& os, const DistributedAMG<B, C, R, D, I>& a);
477};
478
479template <class B, class C, class R, class D, class I>
480std::ostream& operator<<(std::ostream& os, const DistributedAMG<B, C, R, D, I>& a)
481{
484
485 size_t sum_dof = 0;
486 size_t sum_nnz = 0;
487
488 for (const level& lvl : a.levels) {
489 sum_dof += lvl.rows();
490 sum_nnz += lvl.nonzeros();
491 }
492
493 os << "Preconditioner: DistributedAMG\n";
494 //os << "Coarsening: " << a.prm.coarsening.type << "\n";
495 //os << "Relaxation: " << a.prm.relax.type << "\n";
496
497 os << "Number of levels: " << a.levels.size()
498 << "\nOperator complexity: " << std::fixed << std::setprecision(2)
499 << 1.0 * sum_nnz / a.levels.front().nonzeros()
500 << "\nGrid complexity: " << std::fixed << std::setprecision(2)
501 << 1.0 * sum_dof / a.levels.front().rows()
502 << "\n\nlevel unknowns nonzeros\n"
503 << "---------------------------------\n";
504
505 size_t depth = 0;
506 for (const level& lvl : a.levels) {
507 os << std::setw(5) << depth++
508 << std::setw(13) << lvl.rows()
509 << std::setw(15) << lvl.nonzeros() << " ("
510 << std::setw(5) << std::fixed << std::setprecision(2)
511 << 100.0 * lvl.nonzeros() / sum_nnz
512 << "%) [" << lvl.active_procs << "]" << std::endl;
513 }
514
515 return os;
516}
517
518/*---------------------------------------------------------------------------*/
519/*---------------------------------------------------------------------------*/
520
521} // namespace Arcane::Alina
522
523/*---------------------------------------------------------------------------*/
524/*---------------------------------------------------------------------------*/
525
526#endif
void rebuild(const Matrix &M, const backend_params &bprm=backend_params())
Rebuild the hierarchy using the new system matrix.
std::shared_ptr< matrix > system_matrix_ptr() const
Returns the system matrix from the finest level.
Distributed Matrix using message passing.
Provides distributed direct solver interface for Skyline LU solver.
Classe pour stocker les paramètres sous forme d'arbre hiérarchique clé/valeur.
Definition AlinaUtils.h:113
Save ostream flags in constructor, restore in destructor.
Matrix class, to be used by user.
std::int32_t Int32
Type entier signé sur 32 bits.
Wrapper de commodité autour de MPI_Comm.
void check(bool cond, const String &message)
Vérification de condition par communicateur.
repart_params repart
Repartition parameters.
Int32 coarse_enough
Specifies when level is coarse enough to be solved directly.
relax_params relax
Relaxation parameters.
bool allow_rebuild
Keep matrices in internal format to allow for quick rebuild of the hierarchy.
Int32 npre
Number of pre-relaxations.
Int32 npost
Number of post-relaxations.
Int32 ncycle
Number of cycles (1 for V-cycle, 2 for W-cycle, etc.).
Int32 max_levels
Maximum number of levels.
coarsening_params coarsening
Coarsening parameters.
direct_params direct
Direct solver parameters.
Int32 pre_cycles
Number of cycles to make as part of preconditioning.
bool direct_coarse
Use direct solver at the coarsest level.
Simple matrix partitioner merging consecutive domains together.