Arcane  v4.1.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
DistributedCPRPreconditioner.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/* DistributedCPRPreconditioner.h (C) 2000-2026 */
9/* */
10/* Distributed CPR preconditioner. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_DISTRIBUTEDCPRPRECONDITIONER_H
13#define ARCCORE_ALINA_DISTRIBUTEDCPRPRECONDITIONER_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 <cassert>
27#include "arccore/alina/BuiltinBackend.h"
28#include "arccore/alina/AlinaUtils.h"
29#include "arccore/alina/DistributedInnerProduct.h"
30#include "arccore/alina/DistributedMatrix.h"
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35namespace Arcane::Alina
36{
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
43template <class PPrecond, class SPrecond>
44class DistributedCPRPreconditioner
45{
46 static_assert(std::is_same<
47 typename PPrecond::BackendType,
48 typename SPrecond::BackendType>::value,
49 "Backends for pressure and flow preconditioners should coinside!");
50
51 public:
52
53 using BackendType = PPrecond::BackendType;
54
55 typedef BackendType::value_type value_type;
56 typedef math::scalar_of<value_type>::type scalar_type;
57 typedef BackendType::matrix bmatrix;
58 typedef BackendType::vector vector;
59 typedef BackendType::params backend_params;
60
61 typedef DistributedMatrix<BackendType> matrix;
62 typedef typename BuiltinBackend<value_type>::matrix build_matrix;
63
64 struct params
65 {
66 typedef typename PPrecond::params pprecond_params;
67 typedef typename SPrecond::params sprecond_params;
68
69 pprecond_params pprecond;
70 sprecond_params sprecond;
71
72 int block_size = 2;
73
74 params() = default;
75
76 params(const PropertyTree& p)
77 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, pprecond)
78 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, sprecond)
79 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, block_size)
80 {
81 p.check_params({ "pprecond", "sprecond", "block_size", "active_rows" });
82 }
83
84 void get(PropertyTree& p, const std::string& path = "") const
85 {
86 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, pprecond);
87 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, sprecond);
88 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, block_size);
89 }
90 };
91
92 template <class Matrix>
93 DistributedCPRPreconditioner(mpi_communicator comm,
94 const Matrix& K,
95 const params& prm = params(),
96 const backend_params& bprm = backend_params())
97 : prm(prm)
98 , comm(comm)
99 , n(backend::nbRow(K))
100 {
101 init(std::make_shared<matrix>(comm, K, backend::nbRow(K)), bprm);
102 }
103
105 std::shared_ptr<matrix> K,
106 const params& prm = params(),
107 const backend_params& bprm = backend_params())
108 : prm(prm)
109 , comm(comm)
110 , n(K->loc_rows())
111 {
112 init(K, bprm);
113 }
114
115 template <class Vec1, class Vec2>
116 void apply(const Vec1& rhs, Vec2&& x) const
117 {
118 const auto one = math::identity<scalar_type>();
119 const auto zero = math::zero<scalar_type>();
120
121 S->apply(rhs, x);
122 backend::residual(rhs, S->system_matrix(), x, *rs);
123
124 backend::spmv(one, *Fpp, *rs, zero, *rp);
125 P->apply(*rp, *xp);
126
127 backend::spmv(one, *Scatter, *xp, one, x);
128 }
129
130 std::shared_ptr<matrix> system_matrix_ptr() const
131 {
132 return S->system_matrix_ptr();
133 }
134
135 const matrix& system_matrix() const
136 {
137 return S->system_matrix();
138 }
139
140 public:
141
142 params prm;
143
144 private:
145
146 mpi_communicator comm;
147 size_t n, np;
148
149 std::shared_ptr<PPrecond> P;
150 std::shared_ptr<SPrecond> S;
151
152 std::shared_ptr<bmatrix> Fpp, Scatter;
153 std::shared_ptr<vector> rp, xp, rs;
154
155 void init(std::shared_ptr<matrix> K, const backend_params& bprm)
156 {
157 typedef typename backend::row_iterator<build_matrix>::type row_iterator;
158 const int B = prm.block_size;
159
160 auto _K_loc = K->local();
161 auto _K_rem = K->remote();
162
163 auto& K_loc = *_K_loc;
164 auto& K_rem = *_K_rem;
165
166 np = n / B;
167
168 auto fpp = std::make_shared<build_matrix>();
169 fpp->set_size(np, n);
170 fpp->set_nonzeros(n);
171 fpp->ptr[0] = 0;
172
173 auto App_loc = std::make_shared<build_matrix>();
174 auto App_rem = std::make_shared<build_matrix>();
175
176 App_loc->set_size(np, np, true);
177 App_rem->set_size(np, 0, true);
178
179 // Get the pressure matrix nonzero pattern,
180 // extract and invert block diagonals.
181 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
182 std::vector<row_iterator> k;
183 k.reserve(B);
184 multi_array<value_type, 2> v(B, B);
185
186 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
187 ptrdiff_t ik = ip * B;
188 bool done = true;
189 ptrdiff_t cur_col = 0;
190
191 // Local part
192 k.clear();
193 for (int i = 0; i < B; ++i) {
194 k.push_back(backend::row_begin(K_loc, ik + i));
195
196 if (k.back()) {
197 ptrdiff_t col = k.back().col() / B;
198 if (done) {
199 cur_col = col;
200 done = false;
201 }
202 else {
203 cur_col = std::min(cur_col, col);
204 }
205 }
206
207 fpp->col[ik + i] = ik + i;
208 }
209 fpp->ptr[ip + 1] = ik + B;
210
211 while (!done) {
212 ++App_loc->ptr[ip + 1];
213
214 ptrdiff_t end = (cur_col + 1) * B;
215
216 if (cur_col == ip) {
217 // This is diagonal block.
218 // Capture its (transposed) value,
219 // invert it and put the relevant row into fpp.
220 for (int i = 0; i < B; ++i)
221 for (int j = 0; j < B; ++j)
222 v(i, j) = 0;
223
224 for (int i = 0; i < B; ++i)
225 for (; k[i] && k[i].col() < end; ++k[i])
226 v(k[i].col() % B, i) = k[i].value();
227
228 invert(v, &fpp->val[ik]);
229 }
230 else {
231 // This is off-diagonal block.
232 // Just skip it.
233 for (int i = 0; i < B; ++i)
234 while (k[i] && k[i].col() < end)
235 ++k[i];
236 }
237
238 // Get next column number.
239 done = true;
240 for (int i = 0; i < B; ++i) {
241 if (k[i]) {
242 ptrdiff_t col = k[i].col() / B;
243 if (done) {
244 cur_col = col;
245 done = false;
246 }
247 else {
248 cur_col = std::min(cur_col, col);
249 }
250 }
251 }
252 }
253
254 // Remote part
255 k.clear();
256 for (int i = 0; i < B; ++i) {
257 k.push_back(backend::row_begin(K_rem, ik + i));
258
259 if (k.back()) {
260 ptrdiff_t col = k.back().col() / B;
261 if (done) {
262 cur_col = col;
263 done = false;
264 }
265 else {
266 cur_col = std::min(cur_col, col);
267 }
268 }
269 }
270
271 while (!done) {
272 ++App_rem->ptr[ip + 1];
273
274 ptrdiff_t end = (cur_col + 1) * B;
275
276 for (int i = 0; i < B; ++i)
277 while (k[i] && k[i].col() < end)
278 ++k[i];
279
280 // Get next column number.
281 done = true;
282 for (int i = 0; i < B; ++i) {
283 if (k[i]) {
284 ptrdiff_t col = k[i].col() / B;
285 if (done) {
286 cur_col = col;
287 done = false;
288 }
289 else {
290 cur_col = std::min(cur_col, col);
291 }
292 }
293 }
294 }
295 }
296 });
297
298 App_loc->set_nonzeros(App_loc->scan_row_sizes());
299 App_rem->set_nonzeros(App_rem->scan_row_sizes());
300
301 auto scatter = std::make_shared<build_matrix>();
302 scatter->set_size(n, np);
303 scatter->set_nonzeros(np);
304 scatter->ptr[0] = 0;
305
306 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
307 std::vector<row_iterator> k;
308 k.reserve(B);
309
310 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
311 ptrdiff_t ik = ip * B;
312 bool done = true;
313 ptrdiff_t cur_col;
314
315 value_type* d = &fpp->val[ik];
316
317 // Local part
318 ptrdiff_t head = App_loc->ptr[ip];
319 k.clear();
320 for (int i = 0; i < B; ++i) {
321 k.push_back(backend::row_begin(K_loc, ik + i));
322
323 if (k.back()) {
324 ptrdiff_t col = k.back().col() / B;
325 if (done) {
326 cur_col = col;
327 done = false;
328 }
329 else {
330 cur_col = std::min(cur_col, col);
331 }
332 }
333 }
334
335 while (!done) {
336 ptrdiff_t end = (cur_col + 1) * B;
337 value_type app = 0;
338
339 for (int i = 0; i < B; ++i) {
340 for (; k[i] && k[i].col() < end; ++k[i]) {
341 if (k[i].col() % B == 0) {
342 app += d[i] * k[i].value();
343 }
344 }
345 }
346
347 App_loc->col[head] = cur_col;
348 App_loc->val[head] = app;
349 ++head;
350
351 // Get next column number.
352 done = true;
353 for (int i = 0; i < B; ++i) {
354 if (k[i]) {
355 ptrdiff_t col = k[i].col() / B;
356 if (done) {
357 cur_col = col;
358 done = false;
359 }
360 else {
361 cur_col = std::min(cur_col, col);
362 }
363 }
364 }
365 }
366
367 // Remote part
368 head = App_rem->ptr[ip];
369 k.clear();
370 for (int i = 0; i < B; ++i) {
371 k.push_back(backend::row_begin(K_rem, ik + i));
372
373 if (k.back()) {
374 ptrdiff_t col = k.back().col() / B;
375 if (done) {
376 cur_col = col;
377 done = false;
378 }
379 else {
380 cur_col = std::min(cur_col, col);
381 }
382 }
383 }
384
385 while (!done) {
386 ptrdiff_t end = (cur_col + 1) * B;
387 value_type app = 0;
388
389 for (int i = 0; i < B; ++i) {
390 for (; k[i] && k[i].col() < end; ++k[i]) {
391 if (k[i].col() % B == 0) {
392 app += d[i] * k[i].value();
393 }
394 }
395 }
396
397 App_rem->col[head] = cur_col;
398 App_rem->val[head] = app;
399 ++head;
400
401 // Get next column number.
402 done = true;
403 for (int i = 0; i < B; ++i) {
404 if (k[i]) {
405 ptrdiff_t col = k[i].col() / B;
406 if (done) {
407 cur_col = col;
408 done = false;
409 }
410 else {
411 cur_col = std::min(cur_col, col);
412 }
413 }
414 }
415 }
416
417 scatter->col[ip] = ip;
418 scatter->val[ip] = math::identity<value_type>();
419
420 ptrdiff_t nnz = ip;
421 for (int i = 0; i < B; ++i) {
422 if (i == 0)
423 ++nnz;
424 scatter->ptr[ik + i + 1] = nnz;
425 }
426 }
427 });
428
429 auto App = std::make_shared<matrix>(comm, App_loc, App_rem);
430
431 P = std::make_shared<PPrecond>(comm, App, prm.pprecond, bprm);
432 S = std::make_shared<SPrecond>(comm, K, prm.sprecond, bprm);
433
434 Fpp = BackendType::copy_matrix(fpp, bprm);
435 Scatter = BackendType::copy_matrix(scatter, bprm);
436
437 rp = BackendType::create_vector(np, bprm);
438 xp = BackendType::create_vector(np, bprm);
439 rs = BackendType::create_vector(n, bprm);
440 }
441
442 // Inverts dense matrix A;
443 // Returns the first column of the inverted matrix.
444 void invert(multi_array<value_type, 2>& A, value_type* y)
445 {
446 const int B = prm.block_size;
447
448 // Perform LU-factorization of A in-place
449 for (int k = 0; k < B; ++k) {
450 value_type d = A(k, k);
451 assert(!math::is_zero(d));
452 for (int i = k + 1; i < B; ++i) {
453 A(i, k) /= d;
454 for (int j = k + 1; j < B; ++j)
455 A(i, j) -= A(i, k) * A(k, j);
456 }
457 }
458
459 // Invert unit vector in-place.
460 // Lower triangular solve:
461 for (int i = 0; i < B; ++i) {
462 value_type b = static_cast<value_type>(i == 0);
463 for (int j = 0; j < i; ++j)
464 b -= A(i, j) * y[j];
465 y[i] = b;
466 }
467
468 // Upper triangular solve:
469 for (int i = B; i-- > 0;) {
470 for (int j = i + 1; j < B; ++j)
471 y[i] -= A(i, j) * y[j];
472 y[i] /= A(i, i);
473 }
474 }
475
476 template <class P, class S>
477 friend std::ostream& operator<<(std::ostream& os, const DistributedCPRPreconditioner<P, S>& cpr)
478 {
479 os << "CPR (two-stage preconditioner)\n"
480 "### Pressure preconditioner:\n"
481 << *cpr.P << "\n"
482 "### Global preconditioner:\n"
483 << *cpr.S << std::endl;
484 return os;
485 }
486};
487
488/*---------------------------------------------------------------------------*/
489/*---------------------------------------------------------------------------*/
490
491} // namespace Arcane::Alina
492
493/*---------------------------------------------------------------------------*/
494/*---------------------------------------------------------------------------*/
495
496#endif
Distributed Matrix using message passing.
Matrix class, to be used by user.
void arccoreParallelFor(const ComplexForLoopRanges< RankValue > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applique en concurrence la fonction lambda lambda_function sur l'intervalle d'itération donné par loo...
Definition ParallelFor.h:85
std::int32_t Int32
Type entier signé sur 32 bits.
Convenience wrapper around MPI_Comm.