Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
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(const AlinaContext& context,
95 const Matrix& K,
96 const params& prm = params(),
97 const backend_params& bprm = backend_params())
98 : prm(prm)
99 , comm(comm)
100 , n(backend::nbRow(K))
101 {
102 init(context, std::make_shared<matrix>(comm, K, backend::nbRow(K)), bprm);
103 }
104
107 std::shared_ptr<matrix> K,
108 const params& prm = params(),
109 const backend_params& bprm = backend_params())
110 : prm(prm)
111 , comm(comm)
112 , n(K->loc_rows())
113 {
114 init(context, K, bprm);
115 }
116
117 template <class Vec1, class Vec2>
118 void apply(const Vec1& rhs, Vec2&& x) const
119 {
120 const auto one = math::identity<scalar_type>();
121 const auto zero = math::zero<scalar_type>();
122
123 S->apply(rhs, x);
124 backend::residual(rhs, S->system_matrix(), x, *rs);
125
126 backend::spmv(one, *Fpp, *rs, zero, *rp);
127 P->apply(*rp, *xp);
128
129 backend::spmv(one, *Scatter, *xp, one, x);
130 }
131
132 std::shared_ptr<matrix> system_matrix_ptr() const
133 {
134 return S->system_matrix_ptr();
135 }
136
137 const matrix& system_matrix() const
138 {
139 return S->system_matrix();
140 }
141
142 public:
143
144 params prm;
145
146 private:
147
148 AlinaCommunicator comm;
149 size_t n, np;
150
151 std::shared_ptr<PPrecond> P;
152 std::shared_ptr<SPrecond> S;
153
154 std::shared_ptr<bmatrix> Fpp, Scatter;
155 std::shared_ptr<vector> rp, xp, rs;
156
157 void init(const AlinaContext& context, std::shared_ptr<matrix> K, const backend_params& bprm)
158 {
159 typedef typename backend::row_iterator<build_matrix>::type row_iterator;
160 const int B = prm.block_size;
161
162 auto _K_loc = K->local();
163 auto _K_rem = K->remote();
164
165 auto& K_loc = *_K_loc;
166 auto& K_rem = *_K_rem;
167
168 np = n / B;
169
170 auto fpp = std::make_shared<build_matrix>();
171 fpp->set_size(np, n);
172 fpp->set_nonzeros(n);
173 fpp->ptr[0] = 0;
174
175 auto App_loc = std::make_shared<build_matrix>();
176 auto App_rem = std::make_shared<build_matrix>();
177
178 App_loc->set_size(np, np, true);
179 App_rem->set_size(np, 0, true);
180
181 // Get the pressure matrix nonzero pattern,
182 // extract and invert block diagonals.
183 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
184 std::vector<row_iterator> k;
185 k.reserve(B);
186 multi_array<value_type, 2> v(B, B);
187
188 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
189 ptrdiff_t ik = ip * B;
190 bool done = true;
191 ptrdiff_t cur_col = 0;
192
193 // Local part
194 k.clear();
195 for (int i = 0; i < B; ++i) {
196 k.push_back(backend::row_begin(K_loc, ik + i));
197
198 if (k.back()) {
199 ptrdiff_t col = k.back().col() / B;
200 if (done) {
201 cur_col = col;
202 done = false;
203 }
204 else {
205 cur_col = std::min(cur_col, col);
206 }
207 }
208
209 fpp->col[ik + i] = ik + i;
210 }
211 fpp->ptr[ip + 1] = ik + B;
212
213 while (!done) {
214 ++App_loc->ptr[ip + 1];
215
216 ptrdiff_t end = (cur_col + 1) * B;
217
218 if (cur_col == ip) {
219 // This is diagonal block.
220 // Capture its (transposed) value,
221 // invert it and put the relevant row into fpp.
222 for (int i = 0; i < B; ++i)
223 for (int j = 0; j < B; ++j)
224 v(i, j) = 0;
225
226 for (int i = 0; i < B; ++i)
227 for (; k[i] && k[i].col() < end; ++k[i])
228 v(k[i].col() % B, i) = k[i].value();
229
230 invert(v, &fpp->val[ik]);
231 }
232 else {
233 // This is off-diagonal block.
234 // Just skip it.
235 for (int i = 0; i < B; ++i)
236 while (k[i] && k[i].col() < end)
237 ++k[i];
238 }
239
240 // Get next column number.
241 done = true;
242 for (int i = 0; i < B; ++i) {
243 if (k[i]) {
244 ptrdiff_t col = k[i].col() / B;
245 if (done) {
246 cur_col = col;
247 done = false;
248 }
249 else {
250 cur_col = std::min(cur_col, col);
251 }
252 }
253 }
254 }
255
256 // Remote part
257 k.clear();
258 for (int i = 0; i < B; ++i) {
259 k.push_back(backend::row_begin(K_rem, ik + i));
260
261 if (k.back()) {
262 ptrdiff_t col = k.back().col() / B;
263 if (done) {
264 cur_col = col;
265 done = false;
266 }
267 else {
268 cur_col = std::min(cur_col, col);
269 }
270 }
271 }
272
273 while (!done) {
274 ++App_rem->ptr[ip + 1];
275
276 ptrdiff_t end = (cur_col + 1) * B;
277
278 for (int i = 0; i < B; ++i)
279 while (k[i] && k[i].col() < end)
280 ++k[i];
281
282 // Get next column number.
283 done = true;
284 for (int i = 0; i < B; ++i) {
285 if (k[i]) {
286 ptrdiff_t col = k[i].col() / B;
287 if (done) {
288 cur_col = col;
289 done = false;
290 }
291 else {
292 cur_col = std::min(cur_col, col);
293 }
294 }
295 }
296 }
297 }
298 });
299
300 App_loc->set_nonzeros(App_loc->scan_row_sizes());
301 App_rem->set_nonzeros(App_rem->scan_row_sizes());
302
303 auto scatter = std::make_shared<build_matrix>();
304 scatter->set_size(n, np);
305 scatter->set_nonzeros(np);
306 scatter->ptr[0] = 0;
307
308 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
309 std::vector<row_iterator> k;
310 k.reserve(B);
311
312 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
313 ptrdiff_t ik = ip * B;
314 bool done = true;
315 ptrdiff_t cur_col;
316
317 value_type* d = &fpp->val[ik];
318
319 // Local part
320 ptrdiff_t head = App_loc->ptr[ip];
321 k.clear();
322 for (int i = 0; i < B; ++i) {
323 k.push_back(backend::row_begin(K_loc, ik + i));
324
325 if (k.back()) {
326 ptrdiff_t col = k.back().col() / B;
327 if (done) {
328 cur_col = col;
329 done = false;
330 }
331 else {
332 cur_col = std::min(cur_col, col);
333 }
334 }
335 }
336
337 while (!done) {
338 ptrdiff_t end = (cur_col + 1) * B;
339 value_type app = 0;
340
341 for (int i = 0; i < B; ++i) {
342 for (; k[i] && k[i].col() < end; ++k[i]) {
343 if (k[i].col() % B == 0) {
344 app += d[i] * k[i].value();
345 }
346 }
347 }
348
349 App_loc->col[head] = cur_col;
350 App_loc->val[head] = app;
351 ++head;
352
353 // Get next column number.
354 done = true;
355 for (int i = 0; i < B; ++i) {
356 if (k[i]) {
357 ptrdiff_t col = k[i].col() / B;
358 if (done) {
359 cur_col = col;
360 done = false;
361 }
362 else {
363 cur_col = std::min(cur_col, col);
364 }
365 }
366 }
367 }
368
369 // Remote part
370 head = App_rem->ptr[ip];
371 k.clear();
372 for (int i = 0; i < B; ++i) {
373 k.push_back(backend::row_begin(K_rem, ik + i));
374
375 if (k.back()) {
376 ptrdiff_t col = k.back().col() / B;
377 if (done) {
378 cur_col = col;
379 done = false;
380 }
381 else {
382 cur_col = std::min(cur_col, col);
383 }
384 }
385 }
386
387 while (!done) {
388 ptrdiff_t end = (cur_col + 1) * B;
389 value_type app = 0;
390
391 for (int i = 0; i < B; ++i) {
392 for (; k[i] && k[i].col() < end; ++k[i]) {
393 if (k[i].col() % B == 0) {
394 app += d[i] * k[i].value();
395 }
396 }
397 }
398
399 App_rem->col[head] = cur_col;
400 App_rem->val[head] = app;
401 ++head;
402
403 // Get next column number.
404 done = true;
405 for (int i = 0; i < B; ++i) {
406 if (k[i]) {
407 ptrdiff_t col = k[i].col() / B;
408 if (done) {
409 cur_col = col;
410 done = false;
411 }
412 else {
413 cur_col = std::min(cur_col, col);
414 }
415 }
416 }
417 }
418
419 scatter->col[ip] = ip;
420 scatter->val[ip] = math::identity<value_type>();
421
422 ptrdiff_t nnz = ip;
423 for (int i = 0; i < B; ++i) {
424 if (i == 0)
425 ++nnz;
426 scatter->ptr[ik + i + 1] = nnz;
427 }
428 }
429 });
430
431 auto App = std::make_shared<matrix>(comm, App_loc, App_rem);
432
433 P = std::make_shared<PPrecond>(context, comm, App, prm.pprecond, bprm);
434 S = std::make_shared<SPrecond>(context, comm, K, prm.sprecond, bprm);
435
436 Fpp = BackendType::copy_matrix(fpp, bprm);
437 Scatter = BackendType::copy_matrix(scatter, bprm);
438
439 rp = BackendType::create_vector(np, bprm);
440 xp = BackendType::create_vector(np, bprm);
441 rs = BackendType::create_vector(n, bprm);
442 }
443
444 // Inverts dense matrix A;
445 // Returns the first column of the inverted matrix.
446 void invert(multi_array<value_type, 2>& A, value_type* y)
447 {
448 const int B = prm.block_size;
449
450 // Perform LU-factorization of A in-place
451 for (int k = 0; k < B; ++k) {
452 value_type d = A(k, k);
453 assert(!math::is_zero(d));
454 for (int i = k + 1; i < B; ++i) {
455 A(i, k) /= d;
456 for (int j = k + 1; j < B; ++j)
457 A(i, j) -= A(i, k) * A(k, j);
458 }
459 }
460
461 // Invert unit vector in-place.
462 // Lower triangular solve:
463 for (int i = 0; i < B; ++i) {
464 value_type b = static_cast<value_type>(i == 0);
465 for (int j = 0; j < i; ++j)
466 b -= A(i, j) * y[j];
467 y[i] = b;
468 }
469
470 // Upper triangular solve:
471 for (int i = B; i-- > 0;) {
472 for (int j = i + 1; j < B; ++j)
473 y[i] -= A(i, j) * y[j];
474 y[i] /= A(i, i);
475 }
476 }
477
478 template <class P, class S>
479 friend std::ostream& operator<<(std::ostream& os, const DistributedCPRPreconditioner<P, S>& cpr)
480 {
481 os << "CPR (two-stage preconditioner)\n"
482 "### Pressure preconditioner:\n"
483 << *cpr.P << "\n"
484 "### Global preconditioner:\n"
485 << *cpr.S << std::endl;
486 return os;
487 }
488};
489
490/*---------------------------------------------------------------------------*/
491/*---------------------------------------------------------------------------*/
492
493} // namespace Arcane::Alina
494
495/*---------------------------------------------------------------------------*/
496/*---------------------------------------------------------------------------*/
497
498#endif
Distributed Matrix using message passing.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Matrix class, to be used by user.
void arccoreParallelFor(const ComplexForLoopRanges< RankValue, IndexType_ > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applies the lambda function lambda_function concurrently over the iteration interval given by loop_ra...
Definition ParallelFor.h:86
std::int32_t Int32
Signed integer type of 32 bits.
Convenience wrapper around MPI_Comm.