Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
Coarsening.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/* Coarsening.h (C) 2000-2026 */
9/* */
10/* Stratégies de dé-raffinement pour la construction de la hiérarchie AMG. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_COARSENING_H
13#define ARCCORE_ALINA_COARSENING_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * Ce fichier est basé sur le travail effectué sur la librairie AMGCL (version mars 2026),
18 * disponible à l'adresse 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 <tuple>
27#include <memory>
28#include <vector>
29#include <numeric>
30#include <limits>
31#include <algorithm>
32#include <cmath>
33
34#include "arccore/alina/BuiltinBackend.h"
35#include "arccore/alina/AlinaUtils.h"
36#include "arccore/alina/QRFactorizationImpl.h"
37#include "arccore/alina/Adapters.h"
38#include "arccore/alina/ValueTypeInterface.h"
39
40#include <mutex>
41
42/*---------------------------------------------------------------------------*/
43/*---------------------------------------------------------------------------*/
44
45namespace Arcane::Alina::detail
46{
47
48/*---------------------------------------------------------------------------*/
49/*---------------------------------------------------------------------------*/
50
54template <class Matrix>
55std::shared_ptr<Matrix> galerkin(const Matrix& A, const Matrix& P, const Matrix& R)
56{
57 return product(R, *product(A, P));
58}
59
60/*---------------------------------------------------------------------------*/
61/*---------------------------------------------------------------------------*/
65template <class Matrix>
66std::shared_ptr<Matrix> scaled_galerkin(const Matrix& A, const Matrix& P, const Matrix& R, float s)
67{
68 auto a = galerkin(A, P, R);
69 scale(*a, s);
70 return a;
71}
72
73/*---------------------------------------------------------------------------*/
74/*---------------------------------------------------------------------------*/
75
76} // namespace Arcane::Alina::detail
77
78/*---------------------------------------------------------------------------*/
79/*---------------------------------------------------------------------------*/
80
81namespace Arcane::Alina
82{
83
84/*---------------------------------------------------------------------------*/
85/*---------------------------------------------------------------------------*/
86
87struct nullspace_params
88{
90 int cols;
91
93
98
99 nullspace_params()
100 : cols(0)
101 {}
102
104 : cols(p.get("cols", nullspace_params().cols))
105 {
106 double* b = 0;
107 b = p.get("B", b);
108
109 if (b) {
110 Int32 rows = 0;
111 rows = p.get("rows", rows);
112
113 precondition(cols > 0, "Error in nullspace parameters: "
114 "B is set, but cols is not");
115
116 precondition(rows > 0, "Error in nullspace parameters: "
117 "B is set, but rows is not");
118
119 B.assign(b, b + rows * cols);
120 }
121 else {
122 precondition(cols == 0, "Error in nullspace parameters: "
123 "cols > 0, but B is empty");
124 }
125
126 p.check_params( { "cols", "rows", "B" });
127 }
128
129 void get(PropertyTree&, const std::string&) const {}
130};
131
132/*---------------------------------------------------------------------------*/
133/*---------------------------------------------------------------------------*/
148{
150 struct params
151 {
153
160
161 params()
162 : eps_strong(0.08f)
163 {}
164
165 params(const PropertyTree& p)
166 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, eps_strong)
167 {
168 p.check_params( { "eps_strong", "block_size" });
169 }
170
171 void get(PropertyTree& p, const std::string& path) const
172 {
173 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_strong);
174 }
175 };
176
177 static const ptrdiff_t undefined = -1;
178 static const ptrdiff_t removed = -2;
179
181 size_t count;
182
190
198
205 template <class Matrix>
206 plain_aggregates(const Matrix& A, const params& prm)
207 : count(0)
208 , strong_connection(backend::nonzeros(A))
209 , id(backend::nbRow(A))
210 {
211 typedef typename backend::value_type<Matrix>::type value_type;
212 typedef typename math::scalar_of<value_type>::type scalar_type;
213
214 scalar_type eps_squared = prm.eps_strong * prm.eps_strong;
215
216 const size_t n = backend::nbRow(A);
217
218 /* 1. Obtenir les connexions fortes */
219 auto dia = diagonal(A);
220 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
221 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
222 value_type eps_dia_i = eps_squared * (*dia)[i];
223
224 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
225 ptrdiff_t c = A.col[j];
226 value_type v = A.val[j];
227
228 strong_connection[j] = (c != i) && (eps_dia_i * (*dia)[c] < v * v);
229 }
230 }
231 });
232
233 /* 2. Obtenir les IDs des agrégats */
234
235 // Supprime les nœuds isolés.
236 size_t max_neib = 0;
237 for (size_t i = 0; i < n; ++i) {
238 ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1];
239 max_neib = std::max<size_t>(max_neib, e - j);
240
241 ptrdiff_t state = removed;
242 for (; j < e; ++j)
243 if (strong_connection[j]) {
244 state = undefined;
245 break;
246 }
247
248 id[i] = state;
249 }
250
252 neib.reserve(max_neib);
253
254 // Effectue l'agrégation simple
255 for (size_t i = 0; i < n; ++i) {
256 if (id[i] != undefined)
257 continue;
258
259 // Le point n'est pas adjacent à un cœur de tout agrégat précédent :
260 // c'est donc une graine d'un nouvel agrégat.
261 ptrdiff_t cur_id = static_cast<ptrdiff_t>(count++);
262 id[i] = cur_id;
263
264 // (*) Inclure également ses voisins.
265 neib.clear();
266 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
267 ptrdiff_t c = A.col[j];
268 if (strong_connection[j] && id[c] != removed) {
269 id[c] = cur_id;
270 neib.push_back(c);
271 }
272 }
273
274 // Marque temporairement les points indéfinis adjacents au nouvel agrégat
275 // comme membres de l'agrégat.
276 // Si personne ne les revendique plus tard, ils resteront ici.
277 for (ptrdiff_t c : neib) {
278 for (ptrdiff_t j = A.ptr[c], e = A.ptr[c + 1]; j < e; ++j) {
279 ptrdiff_t cc = A.col[j];
280 if (strong_connection[j] && id[cc] == undefined)
281 id[cc] = cur_id;
282 }
283 }
284 }
285
286 if (!count)
287 throw error::empty_level();
288
289 // Certains agrégats pourraient potentiellement disparaître pendant l'étape d'expansion
290 // (*) ci-dessus. Nous devons les exclure et renuméroter le reste.
292 for (ptrdiff_t i : id)
293 if (i >= 0)
294 cnt[i] = 1;
295 std::partial_sum(cnt.begin(), cnt.end(), cnt.begin());
296
297 if (static_cast<ptrdiff_t>(count) > cnt.back()) {
298 count = cnt.back();
299
300 for (size_t i = 0; i < n; ++i)
301 if (id[i] >= 0)
302 id[i] = cnt[id[i]] - 1;
303 }
304 }
305};
306
307/*---------------------------------------------------------------------------*/
308/*---------------------------------------------------------------------------*/
309
310namespace detail
311{
312 struct skip_negative
313 {
314 const UniqueArray<ptrdiff_t>& key;
315 int block_size;
316
317 skip_negative(const UniqueArray<ptrdiff_t>& key, int block_size)
318 : key(key)
319 , block_size(block_size)
320 {}
321
322 bool operator()(ptrdiff_t i, ptrdiff_t j) const
323 {
324 // Cast to unsigned type to keep negative values at the end
325 return static_cast<size_t>(key[i]) / block_size <
326 static_cast<size_t>(key[j]) / block_size;
327 }
328 };
329} // namespace detail
330
331/*---------------------------------------------------------------------------*/
332/*---------------------------------------------------------------------------*/
339template <class Matrix>
340std::shared_ptr<Matrix>
341tentative_prolongation(size_t n,
342 size_t naggr,
343 const UniqueArray<ptrdiff_t> aggr,
344 nullspace_params& nullspace,
345 int block_size)
346{
347 typedef typename backend::value_type<Matrix>::type value_type;
348 typedef typename backend::col_type<Matrix>::type col_type;
349
350 auto P = std::make_shared<Matrix>();
351
352 ARCCORE_ALINA_TIC("tentative");
353 if (nullspace.cols > 0) {
354 ptrdiff_t nba = naggr / block_size;
355
356 // Trie les points fins par numéro d'agrégat.
357 // Place les points n'appartenant à aucun agrégat à la fin de la liste.
358 UniqueArray<ptrdiff_t> order(n);
359 for (size_t i = 0; i < n; ++i)
360 order[i] = i;
361 std::stable_sort(order.begin(), order.end(), detail::skip_negative(aggr, block_size));
362 UniqueArray<ptrdiff_t> aggr_ptr(nba + 1, 0);
363 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(n); ++i) {
364 ptrdiff_t a = aggr[order[i]];
365 if (a < 0)
366 break;
367 ++aggr_ptr[a / block_size + 1];
368 }
369 std::partial_sum(aggr_ptr.begin(), aggr_ptr.end(), aggr_ptr.begin());
370
371 // Précalcule la forme de l'opérateur de prolongation.
372 // Chaque ligne contient exactement nullspace.cols entrées non nulles.
373 // Les lignes qui n'appartiennent à aucun agrégat sont vides.
374 P->set_size(n, nullspace.cols * nba);
375 P->ptr[0] = 0;
376
377 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
378 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
379 P->ptr[i + 1] = aggr[i] < 0 ? 0 : nullspace.cols;
380 }
381 });
382
383 P->scan_row_sizes();
384 P->set_nonzeros();
385
386 // Calcule l'opérateur de prolongation tentative et les vecteurs d'espace nul
387 // pour le niveau plus grossier.
388 UniqueArray<double> Bnew;
389 Bnew.resize(nba * nullspace.cols * nullspace.cols);
390
391 arccoreParallelFor(0, nba, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
392 Alina::detail::QRFactorization<double> qr;
393 UniqueArray<double> Bpart;
394
395 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
396 auto aggr_beg = aggr_ptr[i];
397 auto aggr_end = aggr_ptr[i + 1];
398 auto d = aggr_end - aggr_beg;
399
400 Bpart.resize(d * nullspace.cols);
401
402 for (ptrdiff_t j = aggr_beg, jj = 0; j < aggr_end; ++j, ++jj) {
403 ptrdiff_t ib = nullspace.cols * order[j];
404 for (int k = 0; k < nullspace.cols; ++k)
405 Bpart[jj + d * k] = nullspace.B[ib + k];
406 }
407
408 qr.factorize(d, nullspace.cols, &Bpart[0], Alina::detail::col_major);
409
410 for (int ii = 0, kk = 0; ii < nullspace.cols; ++ii)
411 for (int jj = 0; jj < nullspace.cols; ++jj, ++kk)
412 Bnew[i * nullspace.cols * nullspace.cols + kk] = qr.R(ii, jj);
413
414 for (ptrdiff_t j = aggr_beg, ii = 0; j < aggr_end; ++j, ++ii) {
415 col_type* c = &P->col[P->ptr[order[j]]];
416 value_type* v = &P->val[P->ptr[order[j]]];
417
418 for (int jj = 0; jj < nullspace.cols; ++jj) {
419 c[jj] = i * nullspace.cols + jj;
420 // TODO: this is just a workaround to make non-scalar value
421 // types compile. Most probably this won't actually work.
422 v[jj] = qr.Q(ii, jj) * math::identity<value_type>();
423 }
424 }
425 }
426 });
427
428 std::swap(nullspace.B, Bnew);
429 }
430 else {
431 P->set_size(n, naggr);
432 P->ptr[0] = 0;
433 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
434 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
435 P->ptr[i + 1] = (aggr[i] >= 0);
436 }
437 });
438
439 P->set_nonzeros(P->scan_row_sizes());
440
441 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
442 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
443 if (aggr[i] >= 0) {
444 P->col[P->ptr[i]] = aggr[i];
445 P->val[P->ptr[i]] = math::identity<value_type>();
446 }
447 }
448 });
449 }
450 ARCCORE_ALINA_TOC("tentative");
451
452 return P;
453}
454
455/*---------------------------------------------------------------------------*/
456/*---------------------------------------------------------------------------*/
466{
467 public:
468
471 {
478
479 params() = default;
480
481 params(const PropertyTree& p)
482 : plain_aggregates::params(p)
483 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, block_size)
484 {
485 p.check_params( { "eps_strong", "block_size" });
486 }
487
488 void get(Alina::PropertyTree& p, const std::string& path) const
489 {
490 plain_aggregates::params::get(p, path);
491 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, block_size);
492 }
493 };
494
495 static const ptrdiff_t undefined = -1;
496 static const ptrdiff_t removed = -2;
497
499 size_t count;
500
503
506
508 template <class Matrix>
509 pointwise_aggregates(const Matrix& A, const params& prm, unsigned min_aggregate)
510 : count(0)
511 {
512 if (prm.block_size == 1) {
513 plain_aggregates aggr(A, prm);
514
515 remove_small_aggregates(A.nbRow(), 1, min_aggregate, aggr);
516
517 count = aggr.count;
518 strong_connection.swap(aggr.strong_connection);
519 id.swap(aggr.id);
520 }
521 else {
522 strong_connection.resize(backend::nonzeros(A));
523 id.resize(backend::nbRow(A));
524
525 auto ap = pointwise_matrix(A, prm.block_size);
526 auto& Ap = *ap;
527
528 plain_aggregates pw_aggr(Ap, prm);
529
530 remove_small_aggregates(Ap.nbRow(), prm.block_size, min_aggregate, pw_aggr);
531
532 count = pw_aggr.count * prm.block_size;
533
534 arccoreParallelFor(0, Ap.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
535 UniqueArray<ptrdiff_t> j(prm.block_size);
536 UniqueArray<ptrdiff_t> e(prm.block_size);
537
538 for (ptrdiff_t ip = begin; ip < (begin + size); ++ip) {
539 ptrdiff_t ia = ip * prm.block_size;
540
541 for (unsigned k = 0; k < prm.block_size; ++k, ++ia) {
542 id[ia] = prm.block_size * pw_aggr.id[ip] + k;
543
544 j[k] = A.ptr[ia];
545 e[k] = A.ptr[ia + 1];
546 }
547
548 for (ptrdiff_t jp = Ap.ptr[ip], ep = Ap.ptr[ip + 1]; jp < ep; ++jp) {
549 ptrdiff_t cp = Ap.col[jp];
550 bool sp = (cp == ip) || pw_aggr.strong_connection[jp];
551
552 ptrdiff_t col_end = (cp + 1) * prm.block_size;
553
554 for (unsigned k = 0; k < prm.block_size; ++k) {
555 ptrdiff_t beg = j[k];
556 ptrdiff_t end = e[k];
557
558 while (beg < end && A.col[beg] < col_end) {
559 strong_connection[beg] = sp && A.col[beg] != (ia + k);
560 ++beg;
561 }
562
563 j[k] = beg;
564 }
565 }
566 }
567 });
568 }
569 }
570
571 static void remove_small_aggregates(size_t n, unsigned block_size, unsigned min_aggregate,
572 plain_aggregates& aggr)
573 {
574 if (min_aggregate <= 1)
575 return; // rien à faire
576
577 // Compte les entrées dans chaque agrégat
578 UniqueArray<ptrdiff_t> count(aggr.count, 0);
579
580 for (size_t i = 0; i < n; ++i) {
581 ptrdiff_t id = aggr.id[i];
582 if (id != removed)
583 ++count[id];
584 }
585
586 // Si un agrégat a moins d'entrées que requis, le supprimer.
587 // Renumérote le reste des agrégats pour ne laisser aucun trou.
588 size_t m = 0;
589 for (size_t i = 0; i < aggr.count; ++i) {
590 if (block_size * count[i] < min_aggregate) {
591 count[i] = removed;
592 }
593 else {
594 count[i] = m++;
595 }
596 }
597
598 // Met à jour le nombre d'agrégats et les IDs des agrégats.
599 aggr.count = m;
600
601 for (size_t i = 0; i < n; ++i) {
602 ptrdiff_t id = aggr.id[i];
603 if (id != removed)
604 aggr.id[i] = count[id];
605 }
606 }
607};
608
609/*---------------------------------------------------------------------------*/
610/*---------------------------------------------------------------------------*/
629template <class Backend>
630struct AggregationCoarsening
631{
632 typedef pointwise_aggregates Aggregates;
633
635 struct params
636 {
639
642
658
659 params()
660 : over_interp(math::static_rows<typename Backend::value_type>::value == 1 ? 1.5f : 2.0f)
661 {}
662
663 params(const PropertyTree& p)
664 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
665 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
666 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, over_interp)
667 {
668 p.check_params( { "aggr", "nullspace", "over_interp" });
669 }
670
671 void get(PropertyTree& p, const std::string& path) const
672 {
673 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
674 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
675 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, over_interp);
676 }
677 } prm;
678
679 explicit AggregationCoarsening(const params& prm = params())
680 : prm(prm)
681 {}
682
690 template <class Matrix>
691 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
693 {
694 const size_t n = backend::nbRow(A);
695
696 ARCCORE_ALINA_TIC("aggregates");
697 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
698 ARCCORE_ALINA_TOC("aggregates");
699
700 ARCCORE_ALINA_TIC("interpolation");
701 auto P = tentative_prolongation<Matrix>(
702 n, aggr.count, aggr.id, prm.nullspace, prm.aggr.block_size);
703 ARCCORE_ALINA_TOC("interpolation");
704
705 return std::make_tuple(P, transpose(*P));
706 }
707
716 template <class Matrix>
717 std::shared_ptr<Matrix>
718 coarse_operator(const Matrix& A, const Matrix& P, const Matrix& R) const
719 {
720 return detail::scaled_galerkin(A, P, R, 1 / prm.over_interp);
721 }
722};
723
724/*---------------------------------------------------------------------------*/
725/*---------------------------------------------------------------------------*/
732template <template <class> class Coarsening>
734{
735 template <class Backend>
736 struct type
737 {
738 typedef math::element_of<typename Backend::value_type>::type Scalar;
740 typedef Coarsening<BaseBackend> Base;
741
742 typedef typename Base::params params;
743 Base base;
744
745 type(const params& prm = params())
746 : base(prm) {};
747
748 template <class Matrix>
749 typename std::enable_if<backend::coarsening_is_supported<BaseBackend, Coarsening>::value &&
751 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
752 transfer_operators(const Matrix& B)
753 {
754 typedef typename backend::value_type<Matrix>::type Block;
755 auto T = base.transfer_operators(*adapter::unblock_matrix(B));
756
757 auto& P = *std::get<0>(T);
758 auto& R = *std::get<1>(T);
759
760 sort_rows(P);
761 sort_rows(R);
762
763 return std::make_tuple(
764 std::make_shared<Matrix>(adapter::block_matrix<Block>(P)),
765 std::make_shared<Matrix>(adapter::block_matrix<Block>(R)));
766 }
767
768 template <class Matrix>
769 typename std::enable_if<backend::coarsening_is_supported<BaseBackend, Coarsening>::value &&
771 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
772 transfer_operators(const Matrix& A)
773 {
774 return base.transfer_operators(A);
775 }
776
777 template <class Matrix>
778 typename std::enable_if<!backend::coarsening_is_supported<BaseBackend, Coarsening>::value,
779 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>>::type
780 transfer_operators(const Matrix&)
781 {
782 throw std::logic_error("The coarsening is not supported by the backend");
783 }
784
785 template <class Matrix>
786 std::shared_ptr<Matrix>
787 coarse_operator(const Matrix& A, const Matrix& P, const Matrix& R) const
788 {
789 return base.coarse_operator(A, P, R);
790 }
791 };
792};
793
794/*---------------------------------------------------------------------------*/
795/*---------------------------------------------------------------------------*/
796
797// Crée les modes de corps rigide à partir du vecteur de coordonnées.
798// À utiliser comme vecteurs de quasi-nullespace avec dé-raffinement par agrégation
799// pour les problèmes d'élasticité 2D ou 3D.
800// La matrice de sortie B peut être transposée sur demande
801// (pour être utilisée comme un ensemble de vecteurs de déflation).
802template <typename Vector1, typename Vector2>
803int rigid_body_modes(int ndim, const Vector1& coo, Vector2& B, bool transpose = false)
804{
805 precondition(ndim == 2 || ndim == 3, "Only 2D or 3D problems are supported");
806 precondition(coo.size() % ndim == 0, "Coordinate vector size should be divisible by ndim");
807
808 size_t n = coo.size();
809 int nmodes = (ndim == 2 ? 3 : 6);
810 B.resize(n * nmodes, 0.0);
811
812 const int stride1 = transpose ? 1 : nmodes;
813 const int stride2 = transpose ? n : 1;
814
815 double sn = 1 / sqrt(n);
816
817 if (ndim == 2) {
818 for (size_t i = 0; i < n; ++i) {
819 size_t nod = i / ndim;
820 size_t dim = i % ndim;
821
822 double x = coo[nod * 2 + 0];
823 double y = coo[nod * 2 + 1];
824
825 // Translation
826 B[i * stride1 + dim * stride2] = sn;
827
828 // Rotation
829 switch (dim) {
830 case 0:
831 B[i * stride1 + 2 * stride2] = -y;
832 break;
833 case 1:
834 B[i * stride1 + 2 * stride2] = x;
835 break;
836 }
837 }
838 }
839 else if (ndim == 3) {
840 for (size_t i = 0; i < n; ++i) {
841 size_t nod = i / ndim;
842 size_t dim = i % ndim;
843
844 double x = coo[nod * 3 + 0];
845 double y = coo[nod * 3 + 1];
846 double z = coo[nod * 3 + 2];
847
848 // Translation
849 B[i * stride1 + dim * stride2] = sn;
850
851 // Rotation
852 switch (dim) {
853 case 0:
854 B[i * stride1 + 3 * stride2] = y;
855 B[i * stride1 + 5 * stride2] = z;
856 break;
857 case 1:
858 B[i * stride1 + 3 * stride2] = -x;
859 B[i * stride1 + 4 * stride2] = -z;
860 break;
861 case 2:
862 B[i * stride1 + 4 * stride2] = y;
863 B[i * stride1 + 5 * stride2] = -x;
864 break;
865 }
866 }
867 }
868
869 // Orthonormalization
870 std::array<double, 6> dot;
871 for (int i = ndim; i < nmodes; ++i) {
872 std::fill(dot.begin(), dot.end(), 0.0);
873 for (size_t j = 0; j < n; ++j) {
874 for (int k = 0; k < i; ++k)
875 dot[k] += B[j * stride1 + k * stride2] * B[j * stride1 + i * stride2];
876 }
877 double s = 0.0;
878 for (size_t j = 0; j < n; ++j) {
879 for (int k = 0; k < i; ++k)
880 B[j * stride1 + i * stride2] -= dot[k] * B[j * stride1 + k * stride2];
881 s += B[j * stride1 + i * stride2] * B[j * stride1 + i * stride2];
882 }
883 s = sqrt(s);
884 for (size_t j = 0; j < n; ++j)
885 B[j * stride1 + i * stride2] /= s;
886 }
887
888 return nmodes;
889}
890
891/*---------------------------------------------------------------------------*/
892/*---------------------------------------------------------------------------*/
899template <class Backend>
900struct RugeStubenCoarsening
901{
903 struct params
904 {
914 float eps_strong = 0.25f;
915
927 bool do_trunc = true;
928
930 float eps_trunc = 0.2f;
931
932 params() = default;
933
934 params(const PropertyTree& p)
935 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, eps_strong)
936 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, do_trunc)
937 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, eps_trunc)
938 {
939 p.check_params( { "eps_strong", "do_trunc", "eps_trunc" });
940 }
941
942 void get(PropertyTree& p, const std::string& path) const
943 {
944 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_strong);
945 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, do_trunc);
946 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, eps_trunc);
947 }
948 } prm;
949
950 explicit RugeStubenCoarsening(const params& prm = params())
951 : prm(prm)
952 {}
953
954 template <class Matrix>
955 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
956 transfer_operators(const Matrix& A) const
957 {
958 typedef typename backend::value_type<Matrix>::type Val;
959 typedef typename backend::col_type<Matrix>::type Col;
960 typedef typename backend::ptr_type<Matrix>::type Ptr;
961 typedef typename math::scalar_of<Val>::type Scalar;
962
963 const size_t n = backend::nbRow(A);
964
965 static const Scalar eps = Alina::detail::eps<Scalar>(1);
966
967 static const Val zero = math::zero<Val>();
968
969 UniqueArray<char> cf(n, 'U');
970 CSRMatrix<char, Col, Ptr> S;
971
972 ARCCORE_ALINA_TIC("C/F split");
973 connect(A, prm.eps_strong, S, cf);
974 cfsplit(A, S, cf);
975 ARCCORE_ALINA_TOC("C/F split");
976
977 ARCCORE_ALINA_TIC("interpolation");
978 size_t nc = 0;
979 UniqueArray<ptrdiff_t> cidx(n);
980 for (size_t i = 0; i < n; ++i)
981 if (cf[i] == 'C')
982 cidx[i] = static_cast<ptrdiff_t>(nc++);
983
984 if (!nc)
985 throw error::empty_level();
986
987 auto P = std::make_shared<Matrix>();
988 P->set_size(n, nc, true);
989
990 UniqueArray<Val> Amin, Amax;
991
992 if (prm.do_trunc) {
993 Amin.resize(n);
994 Amax.resize(n);
995 }
996
997 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
998 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
999 if (cf[i] == 'C') {
1000 ++P->ptr[i + 1];
1001 continue;
1002 }
1003
1004 if (prm.do_trunc) {
1005 Val amin = zero, amax = zero;
1006
1007 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1008 if (!S.val[j] || cf[A.col[j]] != 'C')
1009 continue;
1010
1011 amin = std::min(amin, A.val[j]);
1012 amax = std::max(amax, A.val[j]);
1013 }
1014
1015 Amin[i] = (amin *= prm.eps_trunc);
1016 Amax[i] = (amax *= prm.eps_trunc);
1017
1018 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1019 if (!S.val[j] || cf[A.col[j]] != 'C')
1020 continue;
1021
1022 if (A.val[j] < amin || amax < A.val[j])
1023 ++P->ptr[i + 1];
1024 }
1025 }
1026 else {
1027 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1028 if (S.val[j] && cf[A.col[j]] == 'C')
1029 ++P->ptr[i + 1];
1030 }
1031 }
1032 });
1033
1034 P->set_nonzeros(P->scan_row_sizes());
1035
1036 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1037 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1038 ptrdiff_t row_head = P->ptr[i];
1039
1040 if (cf[i] == 'C') {
1041 P->col[row_head] = cidx[i];
1042 P->val[row_head] = math::identity<Val>();
1043 continue;
1044 }
1045
1046 Val dia = zero;
1047 Val a_num = zero, a_den = zero;
1048 Val b_num = zero, b_den = zero;
1049 Val d_neg = zero, d_pos = zero;
1050
1051 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1052 ptrdiff_t c = A.col[j];
1053 Val v = A.val[j];
1054
1055 if (c == i) {
1056 dia = v;
1057 continue;
1058 }
1059
1060 if (v < zero) {
1061 a_num += v;
1062 if (S.val[j] && cf[c] == 'C') {
1063 a_den += v;
1064 if (prm.do_trunc && Amin[i] < v)
1065 d_neg += v;
1066 }
1067 }
1068 else {
1069 b_num += v;
1070 if (S.val[j] && cf[c] == 'C') {
1071 b_den += v;
1072 if (prm.do_trunc && v < Amax[i])
1073 d_pos += v;
1074 }
1075 }
1076 }
1077
1078 Scalar cf_neg = 1;
1079 Scalar cf_pos = 1;
1080
1081 if (prm.do_trunc) {
1082 if (math::norm(static_cast<Val>(a_den - d_neg)) > eps)
1083 cf_neg = math::norm(a_den) / math::norm(static_cast<Val>(a_den - d_neg));
1084
1085 if (math::norm(static_cast<Val>(b_den - d_pos)) > eps)
1086 cf_pos = math::norm(b_den) / math::norm(static_cast<Val>(b_den - d_pos));
1087 }
1088
1089 if (zero < b_num && math::norm(b_den) < eps)
1090 dia += b_num;
1091
1092 Scalar alpha = math::norm(a_den) > eps ? -cf_neg * math::norm(a_num) / (math::norm(dia) * math::norm(a_den)) : 0;
1093 Scalar beta = math::norm(b_den) > eps ? -cf_pos * math::norm(b_num) / (math::norm(dia) * math::norm(b_den)) : 0;
1094
1095 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1096 ptrdiff_t c = A.col[j];
1097 Val v = A.val[j];
1098
1099 if (!S.val[j] || cf[c] != 'C')
1100 continue;
1101 if (prm.do_trunc && Amin[i] <= v && v <= Amax[i])
1102 continue;
1103
1104 P->col[row_head] = cidx[c];
1105 P->val[row_head] = (v < zero ? alpha : beta) * v;
1106 ++row_head;
1107 }
1108 }
1109 });
1110 ARCCORE_ALINA_TOC("interpolation");
1111
1112 return std::make_tuple(P, transpose(*P));
1113 }
1114
1115 template <class Matrix>
1116 std::shared_ptr<Matrix>
1117 coarse_operator(const Matrix& A, const Matrix& P, const Matrix& R) const
1118 {
1119 return detail::galerkin(A, P, R);
1120 }
1121
1122 private:
1123
1124 //-------------------------------------------------------------------
1125 // Au retour, S contiendra à la fois la matrice de connexion forte (dans S.val, qui
1126 // est piggybacking A.ptr et A.col), et sa transposition (dans S.ptr
1127 // et S.val).
1128 //
1129 // Marquez les variables qui n'ont aucune connexion positive comme F(ine).
1130 //-------------------------------------------------------------------
1131 template <typename Val, typename Col, typename Ptr>
1132 static void connect(CSRMatrix<Val, Col, Ptr> const& A, float eps_strong,
1133 CSRMatrix<char, Col, Ptr>& S,
1134 UniqueArray<char>& cf)
1135 {
1136 typedef typename math::scalar_of<Val>::type Scalar;
1137
1138 const size_t n = backend::nbRow(A);
1139 const size_t nnz = backend::nonzeros(A);
1140 const Scalar eps = Alina::detail::eps<Scalar>(1);
1141
1142 S.setNbRow(n);
1143 S.ncols = n;
1144 S.ptr.resize(n + 1);
1145 S.val.resize(nnz);
1146 S.ptr[0] = 0;
1147
1148 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1149 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1150
1151 S.ptr[i + 1] = 0;
1152
1153 Val a_min = math::zero<Val>();
1154
1155 for (auto a = backend::row_begin(A, i); a; ++a)
1156 if (a.col() != i)
1157 a_min = std::min(a_min, a.value());
1158
1159 if (math::norm(a_min) < eps) {
1160 cf[i] = 'F';
1161 continue;
1162 }
1163
1164 a_min *= eps_strong;
1165
1166 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1167 S.val[j] = (A.col[j] != i && A.val[j] < a_min);
1168 }
1169 });
1170
1171 // Transposition de S:
1172 for (size_t i = 0; i < nnz; ++i)
1173 if (S.val[i])
1174 ++(S.ptr[A.col[i] + 1]);
1175
1176 S.scan_row_sizes();
1177 S.col.resize(S.ptr[n]);
1178
1179 for (size_t i = 0; i < n; ++i)
1180 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j)
1181 if (S.val[j])
1182 S.col[S.ptr[A.col[j]]++] = i;
1183
1184 std::rotate(S.ptr.data(), S.ptr.data() + n, S.ptr.data() + n + 1);
1185 S.ptr[0] = 0;
1186 }
1187
1188 // Divise les variables en ensembles C(oarse) et F(ine).
1189 template <typename Val, typename Col, typename Ptr>
1190 static void cfsplit(CSRMatrix<Val, Col, Ptr> const& A,
1191 CSRMatrix<char, Col, Ptr> const& S,
1192 UniqueArray<char>& cf)
1193 {
1194 const size_t n = A.nbRow();
1195
1196 UniqueArray<Col> lambda(n);
1197
1198 // Initialise les lambdas :
1199 for (size_t i = 0; i < n; ++i) {
1200 Col temp = 0;
1201 for (Ptr j = S.ptr[i], e = S.ptr[i + 1]; j < e; ++j)
1202 temp += (cf[S.col[j]] == 'U' ? 1 : 2);
1203 lambda[i] = temp;
1204 }
1205
1206 // Garde une trace des groupes de variables avec des valeurs lambda égales.
1207 // ptr - début d'un groupe;
1208 // cnt - taille d'un groupe;
1209 // i2n - numéro de variable;
1210 // n2i - position de la variable dans un groupe.
1211 UniqueArray<Ptr> ptr(n + 1, 0);
1212 UniqueArray<Ptr> cnt(n, 0);
1213 UniqueArray<Ptr> i2n(n);
1214 UniqueArray<Ptr> n2i(n);
1215
1216 for (size_t i = 0; i < n; ++i)
1217 ++ptr[lambda[i] + 1];
1218
1219 std::partial_sum(ptr.begin(), ptr.end(), ptr.begin());
1220
1221 for (size_t i = 0; i < n; ++i) {
1222 Col lam = lambda[i];
1223 Ptr idx = ptr[lam] + cnt[lam]++;
1224 i2n[idx] = i;
1225 n2i[i] = idx;
1226 }
1227
1228 // Traite les variables par ordre décroissant de la valeur lambda.
1229 // 1. La variable avec la valeur maximale de lambda devient la prochaine variable C.
1230 // 2. Ses voisins de S' deviennent des variables F.
1231 // 3. Maintenir les valeurs lambda synchronisées.
1232 for (size_t top = n; top-- > 0;) {
1233 Ptr i = i2n[top];
1234 Col lam = lambda[i];
1235
1236 if (lam == 0) {
1237 std::replace(cf.begin(), cf.end(), 'U', 'C');
1238 break;
1239 }
1240
1241 // Retire la variable de son groupe.
1242 --cnt[lam];
1243
1244 if (cf[i] == 'F')
1245 continue;
1246
1247 // Marque la variable comme 'C'.
1248 cf[i] = 'C';
1249
1250 // Ses voisins de S' deviennent des variables F.
1251 for (Ptr j = S.ptr[i], e = S.ptr[i + 1]; j < e; ++j) {
1252 Col c = S.col[j];
1253
1254 if (cf[c] != 'U')
1255 continue;
1256
1257 cf[c] = 'F';
1258
1259 // Augmente les lambdas des voisins des F nouvellement créés.
1260 for (Ptr aj = A.ptr[c], ae = A.ptr[c + 1]; aj < ae; ++aj) {
1261 if (!S.val[aj])
1262 continue;
1263
1264 Col ac = A.col[aj];
1265 Col lam_a = lambda[ac];
1266
1267 if (cf[ac] != 'U' || static_cast<size_t>(lam_a) + 1 >= n)
1268 continue;
1269
1270 Ptr old_pos = n2i[ac];
1271 Ptr new_pos = ptr[lam_a] + cnt[lam_a] - 1;
1272
1273 n2i[i2n[old_pos]] = new_pos;
1274 n2i[i2n[new_pos]] = old_pos;
1275
1276 std::swap(i2n[old_pos], i2n[new_pos]);
1277
1278 --cnt[lam_a];
1279 ++cnt[lam_a + 1];
1280 ptr[lam_a + 1] = ptr[lam_a] + cnt[lam_a];
1281
1282 lambda[ac] = lam_a + 1;
1283 }
1284 }
1285
1286 // Diminue les lambdas des voisins des C nouvellement créés.
1287 for (Ptr j = A.ptr[i], e = A.ptr[i + 1]; j < e; j++) {
1288 if (!S.val[j])
1289 continue;
1290
1291 Col c = A.col[j];
1292 Col lam = lambda[c];
1293
1294 if (cf[c] != 'U' || lam == 0)
1295 continue;
1296
1297 Ptr old_pos = n2i[c];
1298 Ptr new_pos = ptr[lam];
1299
1300 n2i[i2n[old_pos]] = new_pos;
1301 n2i[i2n[new_pos]] = old_pos;
1302
1303 std::swap(i2n[old_pos], i2n[new_pos]);
1304
1305 --cnt[lam];
1306 ++cnt[lam - 1];
1307 ++ptr[lam];
1308 lambda[c] = lam - 1;
1309 }
1310 }
1311 }
1312};
1313
1314/*---------------------------------------------------------------------------*/
1315/*---------------------------------------------------------------------------*/
1322template <class Backend>
1323struct SmoothedAggregationCoarserning
1324{
1325 typedef pointwise_aggregates Aggregates;
1326
1328 struct params
1329 {
1332
1335
1361 float relax = 1.0f;
1362
1363 // Estime le rayon spectral de la matrice.
1364 // Cela améliore généralement le taux de convergence et donne des résolutions plus rapides,
1365 // mais cela coûte du temps pendant la configuration.
1366 bool estimate_spectral_radius = false;
1367
1368 // Nombre d'itérations de puissance à appliquer pour l'estimation du rayon spectral. Utilise le théorème du disque de Gershgorin lorsque power_iters = 0.
1369 int power_iters = 0;
1370
1371 params() = default;
1372
1373 params(const PropertyTree& p)
1374 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
1375 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
1376 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, relax)
1377 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, estimate_spectral_radius)
1378 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, power_iters)
1379 {
1380 p.check_params({ "aggr", "nullspace", "relax", "estimate_spectral_radius", "power_iters" });
1381 }
1382
1383 void get(PropertyTree& p, const std::string& path) const
1384 {
1385 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1386 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
1387 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, relax);
1388 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, estimate_spectral_radius);
1389 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, power_iters);
1390 }
1391 };
1392
1393 SmoothedAggregationCoarserning(const params& prm = params())
1394 : prm(prm)
1395 {}
1396
1397 template <class Matrix>
1398 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
1399 transfer_operators(const Matrix& A)
1400 {
1401 typedef typename backend::value_type<Matrix>::type value_type;
1402 typedef typename math::scalar_of<value_type>::type scalar_type;
1403
1404 const size_t n = backend::nbRow(A);
1405
1406 ARCCORE_ALINA_TIC("aggregates");
1407 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
1408 prm.aggr.eps_strong *= 0.5;
1409 ARCCORE_ALINA_TOC("aggregates");
1410
1411 auto P_tent = tentative_prolongation<Matrix>(n, aggr.count, aggr.id, prm.nullspace, prm.aggr.block_size);
1412
1413 auto P = std::make_shared<Matrix>();
1414 P->set_size(backend::nbRow(*P_tent), backend::nbColumn(*P_tent), true);
1415
1416 scalar_type omega = prm.relax;
1417 if (prm.estimate_spectral_radius) {
1418 omega *= static_cast<scalar_type>(4.0 / 3) / spectral_radius<true>(A, prm.power_iters);
1419 }
1420 else {
1421 omega *= static_cast<scalar_type>(2.0 / 3);
1422 }
1423
1424 ARCCORE_ALINA_TIC("smoothing");
1425 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1426 UniqueArray<ptrdiff_t> marker(P->ncols, -1);
1427
1428 // Compte le nombre d'entrées dans P.
1429 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1430 for (ptrdiff_t ja = A.ptr[i], ea = A.ptr[i + 1]; ja < ea; ++ja) {
1431 ptrdiff_t ca = A.col[ja];
1432
1433 // Ignore les connexions hors diagonale faibles.
1434 if (ca != i && !aggr.strong_connection[ja])
1435 continue;
1436
1437 for (ptrdiff_t jp = P_tent->ptr[ca], ep = P_tent->ptr[ca + 1]; jp < ep; ++jp) {
1438 ptrdiff_t cp = P_tent->col[jp];
1439
1440 if (marker[cp] != i) {
1441 marker[cp] = i;
1442 ++(P->ptr[i + 1]);
1443 }
1444 }
1445 }
1446 }
1447 });
1448
1449 P->scan_row_sizes();
1450 P->set_nonzeros();
1451
1452 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1453 UniqueArray<ptrdiff_t> marker(P->ncols, -1);
1454
1455 // Remplit la matrice d'interpolation.
1456 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1457
1458 // La diagonale de la matrice filtrée est la diagonale de la matrice originale
1459 // moins ses connexions faibles.
1460 value_type dia = math::zero<value_type>();
1461 for (ptrdiff_t j = A.ptr[i], e = A.ptr[i + 1]; j < e; ++j) {
1462 if (A.col[j] == i || !aggr.strong_connection[j])
1463 dia += A.val[j];
1464 }
1465 if (!math::is_zero(dia))
1466 dia = -omega * math::inverse(dia);
1467
1468 ptrdiff_t row_beg = P->ptr[i];
1469 ptrdiff_t row_end = row_beg;
1470 for (ptrdiff_t ja = A.ptr[i], ea = A.ptr[i + 1]; ja < ea; ++ja) {
1471 ptrdiff_t ca = A.col[ja];
1472
1473 // Ignore les connexions hors diagonale faibles.
1474 if (ca != i && !aggr.strong_connection[ja])
1475 continue;
1476
1477 value_type va = (ca == i)
1478 ? static_cast<value_type>(static_cast<scalar_type>(1 - omega) * math::identity<value_type>())
1479 : dia * A.val[ja];
1480
1481 for (ptrdiff_t jp = P_tent->ptr[ca], ep = P_tent->ptr[ca + 1]; jp < ep; ++jp) {
1482 ptrdiff_t cp = P_tent->col[jp];
1483 value_type vp = P_tent->val[jp];
1484
1485 if (marker[cp] < row_beg) {
1486 marker[cp] = row_end;
1487 P->col[row_end] = cp;
1488 P->val[row_end] = va * vp;
1489 ++row_end;
1490 }
1491 else {
1492 P->val[marker[cp]] += va * vp;
1493 }
1494 }
1495 }
1496 }
1497 });
1498 ARCCORE_ALINA_TOC("smoothing");
1499
1500 return std::make_tuple(P, transpose(*P));
1501 }
1502
1503 template <class Matrix>
1504 std::shared_ptr<Matrix>
1505 coarse_operator(const Matrix& A, const Matrix& P, const Matrix& R) const
1506 {
1507 return detail::galerkin(A, P, R);
1508 }
1509
1510 params prm;
1511};
1512
1513/*---------------------------------------------------------------------------*/
1514/*---------------------------------------------------------------------------*/
1521template <class Backend>
1522struct SmoothedAggregationEnergyMinCoarsening
1523{
1524 typedef pointwise_aggregates Aggregates;
1525
1527 struct params
1528 {
1531
1534
1535 params() {}
1536
1537 params(const PropertyTree& p)
1538 : ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, aggr)
1539 , ARCCORE_ALINA_PARAMS_IMPORT_CHILD(p, nullspace)
1540 {
1541 p.check_params( { "aggr", "nullspace" });
1542 }
1543
1544 void get(PropertyTree& p, const std::string& path) const
1545 {
1546 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, aggr);
1547 ARCCORE_ALINA_PARAMS_EXPORT_CHILD(p, path, nullspace);
1548 }
1549 } prm;
1550
1551 SmoothedAggregationEnergyMinCoarsening(const params& prm = params())
1552 : prm(prm)
1553 {}
1554
1555 template <class Matrix>
1556 std::tuple<std::shared_ptr<Matrix>, std::shared_ptr<Matrix>>
1557 transfer_operators(const Matrix& A)
1558 {
1559 typedef typename backend::value_type<Matrix>::type Val;
1560 typedef typename backend::col_type<Matrix>::type Col;
1561 typedef typename backend::ptr_type<Matrix>::type Ptr;
1562 typedef ptrdiff_t Idx;
1563
1564 ARCCORE_ALINA_TIC("aggregates");
1565 Aggregates aggr(A, prm.aggr, prm.nullspace.cols);
1566 prm.aggr.eps_strong *= 0.5;
1567 ARCCORE_ALINA_TOC("aggregates");
1568
1569 ARCCORE_ALINA_TIC("interpolation");
1570 auto P_tent = tentative_prolongation<Matrix>(backend::nbRow(A), aggr.count, aggr.id, prm.nullspace, prm.aggr.block_size);
1571
1572 // Filtre la matrice du système
1573 CSRMatrix<Val, Col, Ptr> Af;
1574 Af.set_size(backend::nbRow(A), backend::nbColumn(A));
1575 Af.ptr[0] = 0;
1576
1577 UniqueArray<Val> dia(Af.nbRow());
1578
1579 arccoreParallelFor(0, Af.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1580 for (Idx i = begin; i < (begin + size); ++i) {
1581 Idx row_begin = A.ptr[i];
1582 Idx row_end = A.ptr[i + 1];
1583 Idx row_width = row_end - row_begin;
1584
1585 Val D = math::zero<Val>();
1586 for (Idx j = row_begin; j < row_end; ++j) {
1587 Idx c = A.col[j];
1588 Val v = A.val[j];
1589
1590 if (c == i)
1591 D += v;
1592 else if (!aggr.strong_connection[j]) {
1593 D += v;
1594 --row_width;
1595 }
1596 }
1597
1598 dia[i] = D;
1599 Af.ptr[i + 1] = row_width;
1600 }
1601 });
1602
1603 Af.set_nonzeros(Af.scan_row_sizes());
1604
1605 arccoreParallelFor(0, Af.nbRow(), ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1606 for (Idx i = begin; i < (begin + size); ++i) {
1607
1608 Idx row_begin = A.ptr[i];
1609 Idx row_end = A.ptr[i + 1];
1610 Idx row_head = Af.ptr[i];
1611
1612 for (Idx j = row_begin; j < row_end; ++j) {
1613 Idx c = A.col[j];
1614
1615 if (c == i) {
1616 Af.col[row_head] = i;
1617 Af.val[row_head] = dia[i];
1618 ++row_head;
1619 }
1620 else if (aggr.strong_connection[j]) {
1621 Af.col[row_head] = c;
1622 Af.val[row_head] = A.val[j];
1623 ++row_head;
1624 }
1625 }
1626 }
1627 });
1628
1629 UniqueArray<Val> omega;
1630
1631 auto P = interpolation(Af, dia, *P_tent, omega);
1632 auto R = restriction(Af, dia, *P_tent, omega);
1633 ARCCORE_ALINA_TOC("interpolation");
1634
1635 return std::make_tuple(P, R);
1636 }
1637
1638 template <class Matrix>
1639 std::shared_ptr<Matrix>
1640 coarse_operator(const Matrix& A, const Matrix& P, const Matrix& R) const
1641 {
1642 return detail::galerkin(A, P, R);
1643 }
1644
1645 private:
1646
1647 template <class AMatrix, typename Val, typename Col, typename Ptr>
1648 static std::shared_ptr<CSRMatrix<Val, Col, Ptr>>
1649 interpolation(const AMatrix& A, const UniqueArray<Val>& Adia,
1650 const CSRMatrix<Val, Col, Ptr>& P_tent,
1651 UniqueArray<Val>& omega)
1652 {
1653 const size_t n = backend::nbRow(P_tent);
1654 const size_t nc = backend::nbColumn(P_tent);
1655
1656 auto AP = product(A, P_tent, /*sort rows: */ true);
1657
1658 omega.resize(nc, math::zero<Val>());
1659 UniqueArray<Val> denum(nc, math::zero<Val>());
1660
1661 // TODO CONCURRENCE : supprimer ces mutex
1662 std::mutex mutex1;
1663 std::mutex mutex2;
1664
1665 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1666 UniqueArray<ptrdiff_t> marker(nc, -1);
1667
1668 // Calcule A * Dinv * AP ligne par ligne et calcule les
1669 // produits scalaires par colonne nécessaires au calcul de omega. Les
1670 // résultats réels du produit matrice-matrice ne sont pas stockés.
1671 UniqueArray<Col> adap_col(128);
1672 UniqueArray<Val> adap_val(128);
1673
1674 for (ptrdiff_t ia = begin; ia < (begin + size); ++ia) {
1675 adap_col.clear();
1676 adap_val.clear();
1677
1678 // Forme la ligne actuelle de la matrice ADAP.
1679 for (auto a = A.row_begin(ia); a; ++a) {
1680 Col ca = a.col();
1681 Val va = math::inverse(Adia[ca]) * a.value();
1682
1683 for (auto p = AP->row_begin(ca); p; ++p) {
1684 Col c = p.col();
1685 Val v = va * p.value();
1686
1687 if (marker[c] < 0) {
1688 marker[c] = adap_col.size();
1689 adap_col.push_back(c);
1690 adap_val.push_back(v);
1691 }
1692 else {
1693 adap_val[marker[c]] += v;
1694 }
1695 }
1696 }
1697
1698 if (adap_col.size() != 0)
1699 detail::sort_row(&adap_col[0], &adap_val[0], adap_col.size());
1700
1701 // Met à jour les produits scalaires par colonne (AP,ADAP) et (ADAP,ADAP).
1702 // 1. (AP, ADAP)
1703 for (
1704 Ptr ja = AP->ptr[ia], ea = AP->ptr[ia + 1],
1705 jb = 0, eb = adap_col.size();
1706 ja < ea && jb < eb;) {
1707 Col ca = AP->col[ja];
1708 Col cb = adap_col[jb];
1709
1710 if (ca < cb)
1711 ++ja;
1712 else if (cb < ca)
1713 ++jb;
1714 else /*ca == cb*/ {
1715 Val v = AP->val[ja] * adap_val[jb];
1716 {
1717 std::scoped_lock lock(mutex1);
1718 omega[ca] += v;
1719 ++ja;
1720 ++jb;
1721 }
1722 }
1723 }
1724
1725 // 2. (ADAP, ADAP) (et effacer le marqueur)
1726 for (size_t j = 0, e = adap_col.size(); j < e; ++j) {
1727 Col c = adap_col[j];
1728 Val v = adap_val[j];
1729 {
1730 std::scoped_lock lock(mutex2);
1731 denum[c] += v * v;
1732 marker[c] = -1;
1733 }
1734 }
1735 }
1736 });
1737
1738 for (size_t i = 0, m = omega.size(); i < m; ++i)
1739 omega[i] = math::inverse(denum[i]) * omega[i];
1740
1741 // Met à jour AP pour obtenir P: P = (P_tent - D^-1 A P Omega)
1742 /*
1743 * Ici, nous utilisons le fait que si P(i,j) != 0,
1744 * alors nécessairement AP(i,j) != 0:
1745 *
1746 * AP(i,j) = sum_k(A_ik P_kj), et A_ii != 0.
1747 */
1748 arccoreParallelFor(0, n, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1749 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1750 Val dia = math::inverse(Adia[i]);
1751
1752 for (Ptr ja = AP->ptr[i], ea = AP->ptr[i + 1],
1753 jp = P_tent.ptr[i], ep = P_tent.ptr[i + 1];
1754 ja < ea; ++ja) {
1755 Col ca = AP->col[ja];
1756 Val va = -dia * AP->val[ja] * omega[ca];
1757
1758 for (; jp < ep; ++jp) {
1759 Col cp = P_tent.col[jp];
1760 if (cp > ca)
1761 break;
1762
1763 if (cp == ca) {
1764 va += P_tent.val[jp];
1765 break;
1766 }
1767 }
1768
1769 AP->val[ja] = va;
1770 }
1771 }
1772 });
1773
1774 return AP;
1775 }
1776
1777 template <typename AMatrix, typename Val, typename Col, typename Ptr>
1778 static std::shared_ptr<CSRMatrix<Val, Col, Ptr>>
1779 restriction(const AMatrix& A, const UniqueArray<Val>& Adia,
1780 const CSRMatrix<Val, Col, Ptr>& P_tent,
1781 const UniqueArray<Val>& omega)
1782 {
1783 const size_t nc = backend::nbColumn(P_tent);
1784
1785 auto R_tent = transpose(P_tent);
1786 sort_rows(*R_tent);
1787
1788 auto RA = product(*R_tent, A, /*sort rows: */ true);
1789
1790 // Calcule R = R_tent - Omega R_tent A D^-1.
1791 /*
1792 * Ici, nous utilisons le fait que si R(i,j) != 0,
1793 * alors nécessairement RA(i,j) != 0:
1794 *
1795 * RA(i,j) = sum_k(R_ik A_kj), et A_jj != 0.
1796 */
1797 arccoreParallelFor(0, nc, ForLoopRunInfo{}, [&](Int32 begin, Int32 size) {
1798 for (ptrdiff_t i = begin; i < (begin + size); ++i) {
1799 Val w = omega[i];
1800
1801 for (Ptr ja = RA->ptr[i], ea = RA->ptr[i + 1],
1802 jr = R_tent->ptr[i], er = R_tent->ptr[i + 1];
1803 ja < ea; ++ja) {
1804 Col ca = RA->col[ja];
1805 Val va = -w * math::inverse(Adia[ca]) * RA->val[ja];
1806
1807 for (; jr < er; ++jr) {
1808 Col cr = R_tent->col[jr];
1809 if (cr > ca)
1810 break;
1811
1812 if (cr == ca) {
1813 va += R_tent->val[jr];
1814 break;
1815 }
1816 }
1817
1818 RA->val[ja] = va;
1819 }
1820 }
1821 });
1822
1823 return RA;
1824 }
1825};
1826
1827/*---------------------------------------------------------------------------*/
1828/*---------------------------------------------------------------------------*/
1829
1830} // namespace Arcane::Alina
1831
1832/*---------------------------------------------------------------------------*/
1833/*---------------------------------------------------------------------------*/
1834
1835namespace Arcane::Alina::backend
1836{
1837
1838template <class Backend>
1840 typename std::enable_if<!std::is_arithmetic<typename backend::value_type<Backend>::type>::value>::type> : std::false_type
1841{};
1842
1843/*---------------------------------------------------------------------------*/
1844/*---------------------------------------------------------------------------*/
1845
1846} // namespace Arcane::Alina::backend
1847
1848/*---------------------------------------------------------------------------*/
1849/*---------------------------------------------------------------------------*/
1850
1851#endif
Classe pour stocker les paramètres sous forme d'arbre hiérarchique clé/valeur.
Definition AlinaUtils.h:113
Agrégation ponctuelle.
Definition Coarsening.h:466
UniqueArray< char > strong_connection
Matrice de connectivité forte.
Definition Coarsening.h:502
size_t count
Nombre d'agrégats.
Definition Coarsening.h:499
UniqueArray< ptrdiff_t > id
ID de l'agrégat auquel appartient chaque variable de niveau fin.
Definition Coarsening.h:505
pointwise_aggregates(const Matrix &A, const params &prm, unsigned min_aggregate)
Construit les agrégats pour une matrice donnée.
Definition Coarsening.h:509
void clear()
Supprime les éléments du tableau.
iterator end()
Itérateur sur le premier élément après la fin du tableau.
void reserve(Int64 new_capacity)
Réserve le mémoire pour new_capacity éléments.
void push_back(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
iterator begin()
Itérateur sur le premier élément du tableau.
T & back()
Dernier élément du tableau.
Informations d'exécution d'une boucle.
Matrix class, to be used by user.
Vecteur 1D de données avec sémantique par valeur (style STL).
Classe gérant un vecteur de dimension 2 de type T.
Definition Vector2.h:38
__host__ __device__ Real dot(Real2 u, Real2 v)
Produit scalaire de u par v dans .
Definition MathUtils.h:94
apfloat sqrt(apfloat v)
Racine carrée de v.
Definition MathApfloat.h:65
__host__ __device__ Real3x3 transpose(const Real3x3 &t)
Transpose la matrice.
Definition MathUtils.h:265
void arccoreParallelFor(const ComplexForLoopRanges< RankValue, IndexType_ > &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:86
std::int32_t Int32
Type entier signé sur 32 bits.
Paramètres de dé-raffinement.
Definition Coarsening.h:636
float over_interp
Facteur de sur-interpolation .
Definition Coarsening.h:657
nullspace_params nullspace
Paramètres de quasi-nullspace.
Definition Coarsening.h:641
Aggregates::params aggr
Paramètres d'agrégation.
Definition Coarsening.h:638
std::tuple< std::shared_ptr< Matrix >, std::shared_ptr< Matrix > > transfer_operators(const Matrix &A)
Crée les opérateurs de transfert pour la matrice système donnée.
Definition Coarsening.h:692
std::shared_ptr< Matrix > coarse_operator(const Matrix &A, const Matrix &P, const Matrix &R) const
Crée la matrice système pour le niveau plus grossier.
Definition Coarsening.h:718
Applique un dé-raffinement scalaire sur une matrice de blocs.
Definition Coarsening.h:734
bool do_trunc
Tronquer l'opérateur de prolongation ?
Definition Coarsening.h:927
float eps_trunc
Paramètre de troncature .
Definition Coarsening.h:930
float eps_strong
Paramètre définissant les couplages forts.
Definition Coarsening.h:914
Coarsening Ruge-Stuben classique avec interpolation directe.
Definition Coarsening.h:901
Aggregates::params aggr
Paramètres d'agrégation.
nullspace_params nullspace
Paramètres de l'espace nul proche.
Aggregates::params aggr
Paramètres d'agrégation.
nullspace_params nullspace
Paramètres de l'espace nul proche.
Is the coarsening supported by the backend?
Number of rows for statically sized matrix types.
UniqueArray< double > B
Vecteurs de l'espace nul proche.
Definition Coarsening.h:97
int cols
Nombre de vecteurs dans l'espace nul proche.
Definition Coarsening.h:90
Paramètres d'agrégation.
Definition Coarsening.h:151
float eps_strong
Paramètre définissant les couplages forts.
Definition Coarsening.h:159
UniqueArray< ptrdiff_t > id
ID de l'agrégat auquel appartient chaque variable de niveau fin.
Definition Coarsening.h:197
plain_aggregates(const Matrix &A, const params &prm)
Construit les agrégats pour une matrice donnée.
Definition Coarsening.h:206
size_t count
Nombre d'agrégats.
Definition Coarsening.h:181
UniqueArray< char > strong_connection
Matrice de connectivité forte.
Definition Coarsening.h:189
Int32 block_size
Taille du bloc pour la matrice système.
Definition Coarsening.h:477