Arcane  v4.1.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
CPRPreconditioner.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/* CPRPreconditioner.h (C) 2000-2026 */
9/* */
10/* Two stage preconditioner of the Constrained Pressure Residual type. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_CPRPRECONDITIONER_H
13#define ARCCORE_ALINA_CPRPRECONDITIONER_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 <vector>
27#include <memory>
28#include <cassert>
29
30#include "arccore/alina/BuiltinBackend.h"
31#include "arccore/alina/AlinaUtils.h"
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36namespace Arcane::Alina::preconditioner
37{
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
44template <class PPrecond, class SPrecond>
45class CPRPreconditioner
46{
48 "Pressure backend should have scalar value type!");
49
50 static_assert(backend::backends_compatible<
51 typename PPrecond::backend_type,
52 typename SPrecond::backend_type>::value,
53 "Backends for pressure and flow preconditioners should coincide!");
54
55 public:
56
57 typedef typename SPrecond::backend_type backend_type;
58 typedef typename PPrecond::backend_type backend_type_p;
59
60 typedef typename backend_type::value_type value_type;
61 typedef typename backend_type::matrix matrix;
62 typedef typename backend_type::vector vector;
63 typedef typename backend_type_p::value_type value_type_p;
64 typedef typename backend_type_p::matrix matrix_p;
65 typedef typename backend_type_p::vector vector_p;
66
67 typedef typename backend_type::params backend_params;
68
69 typedef typename BuiltinBackend<value_type>::matrix build_matrix;
70 typedef typename BuiltinBackend<value_type_p>::matrix build_matrix_p;
71
72 typedef typename math::scalar_of<value_type>::type scalar_type;
73
74 struct params
75 {
76 typedef typename PPrecond::params pprecond_params;
77 typedef typename SPrecond::params sprecond_params;
78
79 pprecond_params pprecond;
80 sprecond_params sprecond;
81
83
84 Int32 active_rows = 0;
85
86 params() = default;
87
88 params(const PropertyTree& p)
89 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, pprecond)
90 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, sprecond)
91 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, block_size)
92 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, active_rows)
93 {
94 p.check_params({ "pprecond", "sprecond", "block_size", "active_rows" });
95 }
96
97 void get(PropertyTree& p, const std::string& path = "") const
98 {
99 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, pprecond);
100 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, sprecond);
101 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, block_size);
102 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, active_rows);
103 }
104 };
105
106 template <class Matrix>
107 CPRPreconditioner(const Matrix& K,
108 const params& prm = params(),
109 const backend_params& bprm = backend_params())
110 : prm(prm)
111 , n(backend::nbRow(K))
112 {
113 init(std::make_shared<build_matrix>(K), bprm,
114 std::integral_constant<bool, math::static_rows<value_type>::value == 1>());
115 }
116
117 CPRPreconditioner(std::shared_ptr<build_matrix> K,
118 const params& prm = params(),
119 const backend_params& bprm = backend_params())
120 : prm(prm)
121 , n(backend::nbRow(*K))
122 {
123 init(K, bprm,
124 std::integral_constant<bool, math::static_rows<value_type>::value == 1>());
125 }
126
127 template <class Vec1, class Vec2>
128 void apply(const Vec1& rhs, Vec2&& x) const
129 {
130 const auto one = math::identity<scalar_type>();
131 const auto zero = math::zero<scalar_type>();
132
133 ARCCORE_ALINA_TIC("sprecond");
134 S->apply(rhs, x);
135 ARCCORE_ALINA_TOC("sprecond");
136 backend::residual(rhs, S->system_matrix(), x, *rs);
137
138 backend::spmv(one, *Fpp, *rs, zero, *rp);
139 ARCCORE_ALINA_TIC("pprecond");
140 P->apply(*rp, *xp);
141 ARCCORE_ALINA_TOC("pprecond");
142
143 backend::spmv(one, *Scatter, *xp, one, x);
144 }
145
146 std::shared_ptr<matrix> system_matrix_ptr() const
147 {
148 return S->system_matrix_ptr();
149 }
150
151 const matrix& system_matrix() const
152 {
153 return S->system_matrix();
154 }
155
156 template <class Matrix>
157 void partial_update(const Matrix& K,
158 bool update_transfer_ops = true,
159 const backend_params& bprm = backend_params())
160 {
161 auto K_ptr = std::make_shared<build_matrix>(K);
162 // Update global preconditioner
163 S = std::make_shared<SPrecond>(K_ptr, prm.sprecond, bprm);
164 if (update_transfer_ops) {
165 // Update transfer operator Fpp
166 update_transfer(
167 K_ptr,
168 bprm,
169 std::integral_constant<bool, math::static_rows<value_type>::value == 1>());
170 }
171 }
172
173 params prm;
174
175 private:
176
177 size_t n, np;
178
179 std::shared_ptr<PPrecond> P;
180 std::shared_ptr<SPrecond> S;
181
182 std::shared_ptr<matrix_p> Fpp, Scatter;
183 std::shared_ptr<vector> rs;
184 std::shared_ptr<vector_p> rp, xp;
185
186 // Returns pressure transfer operator fpp and (optionally)
187 // partially constructed pressure system matrix App.
188 std::tuple<std::shared_ptr<build_matrix_p>, std::shared_ptr<build_matrix_p>>
189 first_scalar_pass(std::shared_ptr<build_matrix> K, bool get_app = true)
190 {
191 typedef typename backend::row_iterator<build_matrix>::type row_iterator;
192 const int B = prm.block_size;
193 const ptrdiff_t N = (prm.active_rows ? prm.active_rows : n);
194
195 np = N / B;
196
197 auto fpp = std::make_shared<build_matrix_p>();
198 fpp->set_size(np, N);
199 fpp->set_nonzeros(N);
200 fpp->ptr[0] = 0;
201
202 std::shared_ptr<build_matrix_p> App;
203
204 if (get_app) {
205 App = std::make_shared<build_matrix>();
206 App->set_size(np, np, true);
207 }
208
209 // Get the pressure matrix nonzero pattern,
210 // extract and invert block diagonals.
211 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
212 std::vector<row_iterator> k;
213 k.reserve(B);
214 multi_array<scalar_type, 2> v(B, B);
215
216 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
217 ptrdiff_t ik = ip * B;
218 bool done = true;
219 ptrdiff_t cur_col = 0;
220
221 k.clear();
222 for (int i = 0; i < B; ++i) {
223 k.push_back(backend::row_begin(*K, ik + i));
224
225 if (k.back() && k.back().col() < N) {
226 ptrdiff_t col = k.back().col() / B;
227 if (done) {
228 cur_col = col;
229 done = false;
230 }
231 else {
232 cur_col = std::min(cur_col, col);
233 }
234 }
235
236 fpp->col[ik + i] = ik + i;
237 }
238 fpp->ptr[ip + 1] = ik + B;
239
240 while (!done) {
241 if (get_app)
242 ++App->ptr[ip + 1];
243
244 ptrdiff_t end = (cur_col + 1) * B;
245
246 if (cur_col == ip) {
247 // This is diagonal block.
248 // Capture its (transposed) value,
249 // invert it and put the relevant row into fpp.
250 for (int i = 0; i < B; ++i)
251 for (int j = 0; j < B; ++j)
252 v(i, j) = 0;
253
254 for (int i = 0; i < B; ++i)
255 for (; k[i] && k[i].col() < end; ++k[i])
256 v(k[i].col() % B, i) = k[i].value();
257
258 invert(v.data(), &fpp->val[ik]);
259
260 if (!get_app)
261 break;
262 }
263 else {
264 // This is off-diagonal block.
265 // Just skip it.
266 for (int i = 0; i < B; ++i)
267 while (k[i] && k[i].col() < end)
268 ++k[i];
269 }
270
271 // Get next column number.
272 done = true;
273 for (int i = 0; i < B; ++i) {
274 if (k[i] && k[i].col() < N) {
275 ptrdiff_t col = k[i].col() / B;
276 if (done) {
277 cur_col = col;
278 done = false;
279 }
280 else {
281 cur_col = std::min(cur_col, col);
282 }
283 }
284 }
285 }
286 }
287 });
288
289 if (get_app)
290 App->set_nonzeros(App->scan_row_sizes());
291
292 return std::make_tuple(fpp, App);
293 }
294
295 // The system matrix has scalar values
296 void init(std::shared_ptr<build_matrix> K, const backend_params bprm, std::true_type)
297 {
298 typedef typename backend::row_iterator<build_matrix>::type row_iterator;
299 const int B = prm.block_size;
300 const ptrdiff_t N = (prm.active_rows ? prm.active_rows : n);
301
302 np = N / B;
303
304 std::shared_ptr<build_matrix_p> fpp, App;
305 std::tie(fpp, App) = first_scalar_pass(K);
306
307 auto scatter = std::make_shared<build_matrix_p>();
308 scatter->set_size(n, np);
309 scatter->set_nonzeros(np);
310 scatter->ptr[0] = 0;
311
312 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
313 std::vector<row_iterator> k;
314 k.reserve(B);
315
316 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
317 ptrdiff_t ik = ip * B;
318 ptrdiff_t head = App->ptr[ip];
319 bool done = true;
320 ptrdiff_t cur_col;
321
322 value_type_p* d = &fpp->val[ik];
323
324 k.clear();
325 for (int i = 0; i < B; ++i) {
326 k.push_back(backend::row_begin(*K, ik + i));
327
328 if (k.back() && k.back().col() < N) {
329 ptrdiff_t col = k.back().col() / B;
330 if (done) {
331 cur_col = col;
332 done = false;
333 }
334 else {
335 cur_col = std::min(cur_col, col);
336 }
337 }
338 }
339
340 while (!done) {
341 ptrdiff_t end = (cur_col + 1) * B;
342 value_type_p app = 0;
343
344 for (int i = 0; i < B; ++i) {
345 for (; k[i] && k[i].col() < end; ++k[i]) {
346 if (k[i].col() % B == 0) {
347 app += d[i] * k[i].value();
348 }
349 }
350 }
351
352 App->col[head] = cur_col;
353 App->val[head] = app;
354 ++head;
355
356 // Get next column number.
357 done = true;
358 for (int i = 0; i < B; ++i) {
359 if (k[i] && k[i].col() < N) {
360 ptrdiff_t col = k[i].col() / B;
361 if (done) {
362 cur_col = col;
363 done = false;
364 }
365 else {
366 cur_col = std::min(cur_col, col);
367 }
368 }
369 }
370 }
371
372 scatter->col[ip] = ip;
373 scatter->val[ip] = math::identity<value_type_p>();
374
375 ptrdiff_t nnz = ip;
376 for (int i = 0; i < B; ++i) {
377 if (i == 0)
378 ++nnz;
379 scatter->ptr[ik + i + 1] = nnz;
380 }
381 }
382 });
383
384 for (size_t i = N; i < n; ++i)
385 scatter->ptr[i + 1] = scatter->ptr[i];
386
387 ARCCORE_ALINA_TIC("pprecond");
388 P = std::make_shared<PPrecond>(App, prm.pprecond, bprm);
389 ARCCORE_ALINA_TOC("pprecond");
390 ARCCORE_ALINA_TIC("sprecond");
391 S = std::make_shared<SPrecond>(K, prm.sprecond, bprm);
392 ARCCORE_ALINA_TOC("sprecond");
393
394 Fpp = backend_type_p::copy_matrix(fpp, bprm);
395 Scatter = backend_type_p::copy_matrix(scatter, bprm);
396
397 rp = backend_type_p::create_vector(np, bprm);
398 xp = backend_type_p::create_vector(np, bprm);
399 rs = backend_type::create_vector(n, bprm);
400 }
401
402 void update_transfer(std::shared_ptr<build_matrix> K, const backend_params bprm, std::true_type)
403 {
404 auto fpp = std::get<0>(first_scalar_pass(K, /*get_app*/ false));
405 Fpp = backend_type_p::copy_matrix(fpp, bprm);
406 }
407
408 // The system matrix has block values
409 void init(std::shared_ptr<build_matrix> K, const backend_params bprm, std::false_type)
410 {
411 const int B = math::static_rows<value_type>::value;
412 const ptrdiff_t N = (prm.active_rows ? prm.active_rows : n);
413
414 np = N;
415
416 auto fpp = std::make_shared<build_matrix_p>();
417 fpp->set_size(np, np * B);
418 fpp->set_nonzeros(np * B);
419 fpp->ptr[0] = 0;
420
421 auto scatter = std::make_shared<build_matrix_p>();
422 scatter->set_size(np * B, np);
423 scatter->set_nonzeros(np);
424 scatter->ptr[0] = 0;
425
426 auto App = std::make_shared<build_matrix_p>();
427 App->set_size(np, np, true);
428 App->set_nonzeros(K->nbNonZero());
429 App->ptr[0] = 0;
430
431 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
432 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
433 ptrdiff_t ik = i * B;
434 for (int k = 0; k < B; ++k, ++ik) {
435 fpp->col[ik] = ik;
436 scatter->ptr[ik + 1] = i + 1;
437 }
438
439 fpp->ptr[i + 1] = ik;
440 scatter->col[i] = i;
441 scatter->val[i] = math::identity<value_type_p>();
442
443 ptrdiff_t row_beg = K->ptr[i];
444 ptrdiff_t row_end = K->ptr[i + 1];
445 App->ptr[i + 1] = row_end;
446
447 // Extract and invert block diagonals
448 value_type_p* d = &fpp->val[i * B];
449 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
450 if (K->col[j] == i) {
451 value_type v = math::adjoint(K->val[j]);
452 invert(v.data(), d);
453 break;
454 }
455 }
456
457 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
458 value_type_p app = 0;
459 for (int k = 0; k < B; ++k)
460 app += d[k] * K->val[j](k, 0);
461
462 App->col[j] = K->col[j];
463 App->val[j] = app;
464 }
465 }
466 });
467
468 ARCCORE_ALINA_TIC("pprecond");
469 P = std::make_shared<PPrecond>(App, prm.pprecond, bprm);
470 ARCCORE_ALINA_TOC("pprecond");
471 ARCCORE_ALINA_TIC("sprecond");
472 S = std::make_shared<SPrecond>(K, prm.sprecond, bprm);
473 ARCCORE_ALINA_TOC("sprecond");
474
475 Fpp = backend_type_p::copy_matrix(fpp, bprm);
476 Scatter = backend_type_p::copy_matrix(scatter, bprm);
477
478 rp = backend_type_p::create_vector(np, bprm);
479 xp = backend_type_p::create_vector(np, bprm);
480 rs = backend_type::create_vector(n, bprm);
481 }
482
483 void update_transfer(std::shared_ptr<build_matrix> K, const backend_params bprm, std::false_type)
484 {
485 const int B = math::static_rows<value_type>::value;
486 const ptrdiff_t N = (prm.active_rows ? prm.active_rows : n);
487
488 np = N;
489
490 auto fpp = std::make_shared<build_matrix_p>();
491 fpp->set_size(np, np * B);
492 fpp->set_nonzeros(np * B);
493 fpp->ptr[0] = 0;
494
495 arccoreParallelFor(0, np, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
496 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
497 ptrdiff_t ik = i * B;
498 for (int k = 0; k < B; ++k, ++ik) {
499 fpp->col[ik] = ik;
500 }
501
502 fpp->ptr[i + 1] = ik;
503
504 ptrdiff_t row_beg = K->ptr[i];
505 ptrdiff_t row_end = K->ptr[i + 1];
506
507 // Extract and invert block diagonals
508 value_type_p* d = &fpp->val[i * B];
509 for (ptrdiff_t j = row_beg; j < row_end; ++j) {
510 if (K->col[j] == i) {
511 value_type v = math::adjoint(K->val[j]);
512 invert(v.data(), d);
513 break;
514 }
515 }
516 }
517 });
518 Fpp = backend_type_p::copy_matrix(fpp, bprm);
519 }
520
521 // Inverts dense matrix A;
522 // Returns the first column of the inverted matrix.
523 void invert(scalar_type* A, value_type_p* y)
524 {
525 const int B = math::static_rows<value_type>::value == 1 ? prm.block_size : math::static_rows<value_type>::value;
526
527 // Perform LU-factorization of A in-place
528 for (int k = 0; k < B; ++k) {
529 scalar_type d = A[k * B + k];
530 assert(!math::is_zero(d));
531 for (int i = k + 1; i < B; ++i) {
532 A[i * B + k] /= d;
533 for (int j = k + 1; j < B; ++j)
534 A[i * B + j] -= A[i * B + k] * A[k * B + j];
535 }
536 }
537
538 // Invert unit vector in-place.
539 // Lower triangular solve:
540 for (int i = 0; i < B; ++i) {
541 value_type_p b = static_cast<value_type_p>(i == 0);
542 for (int j = 0; j < i; ++j)
543 b -= A[i * B + j] * y[j];
544 y[i] = b;
545 }
546
547 // Upper triangular solve:
548 for (int i = B; i-- > 0;) {
549 for (int j = i + 1; j < B; ++j)
550 y[i] -= A[i * B + j] * y[j];
551 y[i] /= A[i * B + i];
552 }
553 }
554
555 friend std::ostream& operator<<(std::ostream& os, const CPRPreconditioner& p)
556 {
557 os << "CPR (two-stage preconditioner)\n"
558 "### Pressure preconditioner:\n"
559 << *p.P << "\n"
560 "### Global preconditioner:\n"
561 << *p.S << std::endl;
562 return os;
563 }
564};
565
566/*---------------------------------------------------------------------------*/
567/*---------------------------------------------------------------------------*/
568
569} // namespace Arcane::Alina::preconditioner
570
571/*---------------------------------------------------------------------------*/
572/*---------------------------------------------------------------------------*/
573
574#endif
Two stage preconditioner of the Constrained Pressure Residual type.
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.
Metafunction that checks if two backends are compatible.
Number of rows for statically sized matrix types.