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