Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
Solver.cc
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/*---------------------------------------------------------------------------*/
9/*
10 * Ce fichier est basé sur le travail effectué sur la bibliothèque AMGCL (version mars 2026)
11 * qui peut être trouvée à https://github.com/ddemidov/amgcl.
12 *
13 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
14 * SPDX-License-Identifier: MIT
15 */
16/*---------------------------------------------------------------------------*/
17/*---------------------------------------------------------------------------*/
18
19#include <iostream>
20#include <string>
21#include <random>
22
23// Pour supprimer les avertissements concernant l'utilisation obsolète d'Eigen.
24#pragma GCC diagnostic ignored "-Wdeprecated-copy"
25#pragma GCC diagnostic ignored "-Wint-in-bool-context"
26
27#include <boost/range/iterator_range.hpp>
28#include <boost/preprocessor/seq/for_each.hpp>
29
30#if defined(SOLVER_BACKEND_CUDA)
31#include "arccore/alina/CudaBackend.h"
32#include "arccore/alina/relaxation_cusparse_ilu0.h"
34#elif defined(SOLVER_BACKEND_EIGEN)
35#include "arccore/alina/EigenBackend.h"
37#else
38#ifndef SOLVER_BACKEND_BUILTIN
39#define SOLVER_BACKEND_BUILTIN
40#endif
41#include "arccore/alina/BuiltinBackend.h"
42#include "arccore/alina/StaticMatrix.h"
43#include "arccore/alina/Adapters.h"
44// Utilise l'indexation 32 bits pour le backend.
46//using Backend = Arcane::Alina::BuiltinBackend<double>;
47#endif
48
49#include <arccore/base/PlatformUtils.h>
50#include <arccore/base/String.h>
51#include <arccore/base/Convert.h>
52
53#include "arccore/alina/RelaxationRuntime.h"
54#include "arccore/alina/CoarseningRuntime.h"
55#include "arccore/alina/SolverRuntime.h"
56#include "arccore/alina/PreconditionerRuntime.h"
57#include "arccore/alina/PreconditionedSolver.h"
58#include "arccore/alina/AMG.h"
59#include "arccore/alina/Adapters.h"
60#include "arccore/alina/IO.h"
61#include "arccore/alina/Profiler.h"
62
63#include "arccore/common/internal/ProgramOptions.h"
64
65#include "SampleProblemCommon.h"
66
67#ifndef ARCCORE_ALINA_BLOCK_SIZES
68#define ARCCORE_ALINA_BLOCK_SIZES (3)(4)
69#endif
70
71using namespace Arcane;
72
73using Alina::precondition;
74
75#ifdef SOLVER_BACKEND_BUILTIN
76
77#include "./HypreComparer.h"
78
79//---------------------------------------------------------------------------
80template <int B> Alina::SolverResult
81block_solve(const Alina::AlinaContext& context,
82 const Alina::PropertyTree& prm,
83 size_t rows,
84 std::vector<ptrdiff_t> const& ptr,
85 std::vector<ptrdiff_t> const& col,
86 std::vector<double> const& val,
87 std::vector<double> const& rhs,
88 std::vector<double>& x,
89 bool reorder)
90{
91 auto& prof = Alina::Profiler::globalProfiler();
92
93 typedef Alina::StaticMatrix<double, B, B> value_type;
94 typedef Alina::StaticMatrix<double, B, 1> rhs_type;
95 typedef Alina::BuiltinBackend<value_type> BBackend;
96
98
99 auto As = std::tie(rows, ptr, col, val);
100 auto Ab = Alina::adapter::block_matrix<value_type>(As);
101
102 std::tuple<size_t, double> info;
103
104 if (reorder) {
105 prof.tic("reorder");
107 prof.toc("reorder");
108
109 prof.tic("setup");
110 Solver solve(context, perm(Ab), prm);
111 prof.toc("setup");
112
113 std::cout << solve << std::endl;
114
115 rhs_type const* fptr = reinterpret_cast<rhs_type const*>(&rhs[0]);
116 rhs_type* xptr = reinterpret_cast<rhs_type*>(&x[0]);
117
119 Alina::numa_vector<rhs_type> X(perm(SmallSpan<rhs_type>(xptr, rows / B)));
120
121 prof.tic("solve");
122 info = solve(F, X);
123 prof.toc("solve");
124
125 perm.inverse(X, xptr);
126 }
127 else {
128 prof.tic("setup");
129 Solver solve(context, Ab, prm);
130 prof.toc("setup");
131
132 std::cout << solve << std::endl;
133
134 rhs_type const* fptr = reinterpret_cast<rhs_type const*>(&rhs[0]);
135 rhs_type* xptr = reinterpret_cast<rhs_type*>(&x[0]);
136
137 Alina::numa_vector<rhs_type> F(fptr, fptr + rows / B);
138 Alina::numa_vector<rhs_type> X(xptr, xptr + rows / B);
139
140 prof.tic("solve");
141 info = solve(F, X);
142 prof.toc("solve");
143
144 std::copy(X.data(), X.data() + X.size(), xptr);
145 }
146
147 return info;
148}
149#endif
150
151//---------------------------------------------------------------------------
153scalar_solve(const Alina::AlinaContext& context,
154 const Alina::PropertyTree& prm,
155 size_t rows,
156 std::vector<ptrdiff_t> const& ptr,
157 std::vector<ptrdiff_t> const& col,
158 std::vector<double> const& val,
159 std::vector<double> const& rhs,
160 std::vector<double>& x,
161 bool reorder)
162{
163 std::cout << "Utilisation de la résolution scalaire ptr_size=" << sizeof(ptrdiff_t)
164 << " taille_type_ptr=" << sizeof(Backend::ptr_type)
165 << " taille_type_col=" << sizeof(Backend::col_type)
166 << " taille_type_valeur=" << sizeof(Backend::value_type)
167 << "\n";
168 auto& prof = Alina::Profiler::globalProfiler();
169 Backend::params bprm;
170
171#if defined(SOLVER_BACKEND_CUDA)
172 cusparseCreate(&bprm.cusparse_handle);
173 {
174 int dev;
175 cudaGetDevice(&dev);
176
177 cudaDeviceProp prop;
178 cudaGetDeviceProperties(&prop, dev);
179 std::cout << prop.name << std::endl
180 << std::endl;
181 }
182#endif
183
185
187
188 if (reorder) {
189 prof.tic("reorder");
190 Alina::adapter::reorder<> perm(std::tie(rows, ptr, col, val));
191 prof.toc("reorder");
192
193 prof.tic("setup");
194 Solver solve(context, perm(std::tie(rows, ptr, col, val)), prm, bprm);
195 prof.toc("setup");
196
197 std::cout << solve << std::endl;
198
199 std::vector<double> tmp(rows);
200
201 perm.forward(rhs, tmp);
202 auto f_b = Backend::copy_vector(tmp, bprm);
203
204 perm.forward(x, tmp);
205 auto x_b = Backend::copy_vector(tmp, bprm);
206
207 prof.tic("solve");
208 info = solve(*f_b, *x_b);
209 prof.toc("solve");
210
211#if defined(SOLVER_BACKEND_CUDA)
212 thrust::copy(x_b->begin(), x_b->end(), tmp.begin());
213#else
214 std::copy(&(*x_b)[0], &(*x_b)[0] + rows, &tmp[0]);
215#endif
216
217 perm.inverse(tmp, x);
218 }
219 else {
220 prof.tic("setup");
221 Solver solve(context, std::tie(rows, ptr, col, val), prm, bprm);
222 prof.toc("setup");
223
224 std::cout << solve << std::endl;
225
226 auto f_b = Backend::copy_vector(rhs, bprm);
227 auto x_b = Backend::copy_vector(x, bprm);
228
229 prof.tic("solve");
230 info = solve(*f_b, *x_b);
231 prof.toc("solve");
232
233#if defined(SOLVER_BACKEND_CUDA)
234 thrust::copy(x_b->begin(), x_b->end(), x.begin());
235#else
236 std::copy(&(*x_b)[0], &(*x_b)[0] + rows, &x[0]);
237#endif
238 }
239
240 return info;
241}
242
243#define ARCCORE_ALINA_CALL_BLOCK_SOLVER(z, data, B) \
244 case B: \
245 return block_solve<B>(context, prm, rows, ptr, col, val, rhs, x, reorder);
246
247//---------------------------------------------------------------------------
249solve(const Alina::AlinaContext& context,
250 const Alina::PropertyTree& prm,
251 size_t rows,
252 std::vector<ptrdiff_t> const& ptr,
253 std::vector<ptrdiff_t> const& col,
254 std::vector<double> const& val,
255 std::vector<double> const& rhs,
256 std::vector<double>& x,
257 int block_size,
258 bool reorder)
259{
260 switch (block_size) {
261 case 1:
262 return scalar_solve(context, prm, rows, ptr, col, val, rhs, x, reorder);
263#if defined(SOLVER_BACKEND_BUILTIN)
264 BOOST_PP_SEQ_FOR_EACH(ARCCORE_ALINA_CALL_BLOCK_SOLVER, ~, ARCCORE_ALINA_BLOCK_SIZES)
265#endif
266 default:
267 precondition(false, "Taille de bloc non prise en charge");
268 return {};
269 }
270}
271
272//---------------------------------------------------------------------------
273
274int main(int argc, char* argv[])
275{
276 auto& prof = Alina::Profiler::globalProfiler();
277 namespace po = Arcane::ProgramOptions;
278 namespace io = Alina::IO;
279 Alina::AlinaContext context;
280
281 using std::string;
282 using std::vector;
283
284 po::options_description desc("Options");
285
286 desc.add_options()("help,h", "Affiche cette aide.")("prm-file,P",
287 po::value<string>(),
288 "Fichier de paramètres au format json. ")(
289 "prm,p",
290 po::value<vector<string>>()->multitoken(),
291 "Paramètres spécifiés sous forme de paires nom=valeur. "
292 "Peut être fourni plusieurs fois. Exemples :\n"
293 " -p solver.tol=1e-3\n"
294 " -p precond.coarse_enough=300")("matrix,A",
295 po::value<string>(),
296 "Matrice du système au format MatrixMarket. "
297 "Lorsqu'elle n'est pas spécifiée, elle résout un problème de Poisson dans un cube unitaire 3D. ")(
298 "rhs,f",
299 po::value<string>(),
300 "Le vecteur du membre de droite (RHS) au format MatrixMarket. "
301 "Lorsqu'il est omis, un vecteur de uns est utilisé par défaut. "
302 "Doit être fourni uniquement avec une matrice de système. ")(
303 "f0",
304 po::bool_switch()->default_value(false),
305 "Utiliser un vecteur RHS nul. Implique --random-initial et solver.ns_search=true")(
306 "f1",
307 po::bool_switch()->default_value(false),
308 "Définir RHS = Ax où x = 1")(
309 "null,N",
310 po::value<string>(),
311 "Les vecteurs proches de l'espace nul au format MatrixMarket. "
312 "Doit être une matrice dense de taille N*M, où N est le nombre de "
313 "variables inconnues et M est le nombre de vecteurs de l'espace nul. "
314 "Doit être fourni uniquement avec une matrice de système. ")(
315 "coords,C",
316 po::value<string>(),
317 "Matrice de coordonnées où le nombre de lignes correspond au nombre de nœuds de grille "
318 "et le nombre de colonnes correspond à la dimensionnalité du problème (2 ou 3). "
319 "Sera utilisée pour construire des vecteurs proches de l'espace nul comme modes de corps rigide. "
320 "Doit être fourni uniquement avec une matrice de système. ")(
321 "binary,B",
322 po::bool_switch()->default_value(false),
323 "Lorsqu'il est spécifié, traiter les fichiers d'entrée comme binaires au lieu de MatrixMarket. "
324 "Il est supposé que les fichiers ont été convertis au format binaire avec l'utilitaire mm2bin. ")(
325 "scale,s",
326 po::bool_switch()->default_value(false),
327 "Mettre à l'échelle la matrice de sorte que la diagonale soit unitaire. ")(
328 "block-size,b",
329 po::value<int>()->default_value(1),
330 "La taille du bloc de la matrice du système. "
331 "Lorsqu'elle est spécifiée, la matrice du système est supposée avoir une structure par blocs. "
332 "C'est généralement le cas pour les problèmes en élasticité, en mécanique des structures, "
333 "pour les systèmes couplés d'EDP (tels que les équations de Navier-Stokes), etc. ")(
334 "size,n",
335 po::value<int>()->default_value(32),
336 "La taille du problème de Poisson à résoudre lorsqu'aucune matrice de système n'est fournie. "
337 "Spécifié comme le nombre de nœuds de grille le long de chaque dimension d'un cube unitaire. "
338 "Le système résultant aura n*n*n inconnues. ")(
339 "anisotropy,a",
340 po::value<double>()->default_value(1.0),
341 "La valeur d'anisotropie pour la valeur de Poisson générée. "
342 "Utilisée pour déterminer la mise à l'échelle du problème le long des axes X, Y et Z : "
343 "hy = hx * a, hz = hy * a.")(
344 "single-level,1",
345 po::bool_switch()->default_value(false),
346 "Lorsqu'il est spécifié, la hiérarchie AMG n'est pas construite. "
347 "Au lieu de cela, le problème est résolu à l'aide d'un lisseur à un niveau comme préconditionneur. ")(
348 "reorder,r",
349 po::bool_switch()->default_value(false),
350 "Lorsqu'il est spécifié, la matrice sera réordonnée pour améliorer la localité du cache")(
351 "initial,x",
352 po::value<double>()->default_value(0),
353 "Valeur à utiliser comme approximation initiale. ")(
354 "random-initial",
355 po::bool_switch()->default_value(false),
356 "Utiliser une approximation initiale aléatoire. ")(
357 "output,o",
358 po::value<string>(),
359 "Fichier de sortie. Sera enregistré au format MatrixMarket. "
360 "Lorsqu'il est omis, la solution n'est pas enregistrée. ");
361
363 p.add("prm", -1);
364
366 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
367 po::notify(vm);
368
369 if (vm.count("help")) {
370 std::cout << desc << std::endl;
371 return 0;
372 }
373
374 for (int i = 0; i < argc; ++i) {
375 if (i)
376 std::cout << " ";
377 std::cout << argv[i];
378 }
379 std::cout << std::endl;
380
382 if (vm.count("prm-file")) {
383 prm.read_json(vm["prm-file"].as<string>());
384 }
385
386 if (vm.count("prm")) {
387 for (const string& v : vm["prm"].as<vector<string>>()) {
388 prm.putKeyValue(v);
389 }
390 }
391
392 size_t rows, nv = 0;
393 vector<ptrdiff_t> ptr, col;
394 vector<double> val, rhs, null, x;
395
396 if (vm.count("matrix")) {
397 auto t = prof.scoped_tic("reading");
398
399 string Afile = vm["matrix"].as<string>();
400 bool binary = vm["binary"].as<bool>();
401
402 if (binary) {
403 io::read_crs(Afile, rows, ptr, col, val);
404 }
405 else {
406 size_t cols;
407 std::tie(rows, cols) = io::mm_reader(Afile)(ptr, col, val);
408 precondition(rows == cols, "Matrice de système non carrée");
409 }
410
411 if (vm.count("rhs")) {
412 string bfile = vm["rhs"].as<string>();
413
414 size_t n, m;
415
416 if (binary) {
417 io::read_dense(bfile, n, m, rhs);
418 }
419 else {
420 std::tie(n, m) = io::mm_reader(bfile)(rhs);
421 }
422
423 precondition(n == rows && m == 1, "Le vecteur RHS a une taille incorrecte");
424 }
425 else if (vm["f1"].as<bool>()) {
426 rhs.resize(rows);
427 for (size_t i = 0; i < rows; ++i) {
428 double s = 0;
429 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j)
430 s += val[j];
431 rhs[i] = s;
432 }
433 }
434 else {
435 rhs.resize(rows, vm["f0"].as<bool>() ? 0.0 : 1.0);
436 }
437
438 if (vm.count("null")) {
439 string nfile = vm["null"].as<string>();
440
441 size_t m;
442
443 if (binary) {
444 io::read_dense(nfile, m, nv, null);
445 }
446 else {
447 std::tie(m, nv) = io::mm_reader(nfile)(null);
448 }
449
450 precondition(m == rows, "Les vecteurs proches de l'espace nul ont une taille incorrecte");
451 }
452 else if (vm.count("coords")) {
453 string cfile = vm["coords"].as<string>();
454 std::vector<double> coo;
455
456 size_t m, ndim;
457
458 if (binary) {
459 io::read_dense(cfile, m, ndim, coo);
460 }
461 else {
462 std::tie(m, ndim) = io::mm_reader(cfile)(coo);
463 }
464
465 precondition(m * ndim == rows && (ndim == 2 || ndim == 3), "Matrice de coordonnées de taille incorrecte");
466
467 nv = Alina::rigid_body_modes(ndim, coo, null);
468 }
469
470 if (nv) {
471 prm.put("precond.coarsening.nullspace.cols", nv);
472 prm.put("precond.coarsening.nullspace.rows", rows);
473 prm.put("precond.coarsening.nullspace.B", &null[0]);
474 }
475 }
476 else {
477 auto t = prof.scoped_tic("assembling");
478 rows = Alina::PoissonProblemGenerator::createSequentialMatrix(vm["size"].as<int>(), val, col, ptr, rhs, vm["anisotropy"].as<double>());
479 }
480
481 if (vm["scale"].as<bool>()) {
482 std::vector<double> dia(rows, 1.0);
483
484 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
485 double d = 1.0;
486 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
487 if (col[j] == i) {
488 d = 1 / sqrt(val[j]);
489 }
490 }
491 if (!std::isnan(d))
492 dia[i] = d;
493 }
494
495 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
496 rhs[i] *= dia[i];
497 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
498 val[j] *= dia[i] * dia[col[j]];
499 }
500 }
501 }
502
503 x.resize(rows, vm["initial"].as<double>());
504 if (vm["random-initial"].as<bool>() || vm["f0"].as<bool>()) {
505 std::mt19937 rng;
506 std::uniform_real_distribution<double> rnd(-1, 1);
507 for (auto& v : x)
508 v = rnd(rng);
509 }
510
511 if (vm["f0"].as<bool>()) {
512 prm.put("solver.ns_search", true);
513 }
514
515 int block_size = vm["block-size"].as<int>();
516 std::cout << "BlockSize= " << block_size << "\n";
517
518 if (vm["single-level"].as<bool>())
519 prm.put("precond.class", "relaxation");
520
521 String do_hypre_str = Platform::getEnvironmentVariable("ALINA_USE_HYPRE");
522 bool do_hypre = false;
523 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ALINA_USE_HYPRE", true))
524 do_hypre = v.value();
525
526 Alina::SolverResult solver_result;
527#ifndef SOLVER_BACKEND_BUILTIN
528 do_hypre = false;
529#endif
530 if (do_hypre) {
531#ifdef SOLVER_BACKEND_BUILTIN
532 HypreComparer hypre_comparer(true);
533 hypre_comparer.solve(rows, ptr, col, val, rhs, x, argc, argv);
534#endif
535 }
536 else {
537 solver_result = solve(context, prm, rows, ptr, col, val, rhs, x, block_size, vm["reorder"].as<bool>());
538
539 if (vm.count("output")) {
540 auto t = prof.scoped_tic("write");
541 Alina::IO::mm_write(vm["output"].as<string>(), &x[0], x.size());
542 }
543 }
544 std::cout << "Itérations: " << solver_result.nbIteration() << std::endl
545 << "Erreur: " << solver_result.residual() << std::endl
546 << prof << std::endl;
547}
Convenience class that bundles together a preconditioner and an iterative solver.
Classe pour stocker les paramètres sous forme d'arbre hiérarchique clé/valeur.
Definition AlinaUtils.h:113
Résultat d'une solution.
Definition AlinaUtils.h:56
NUMA-aware vector container.
Definition NumaVector.h:42
Classe template pour convertir un type.
Constructeur d'analyseur d'arguments en ligne de commande fluide.
Décrit un ensemble d'options en ligne de commande.
Décrit les arguments positionnels (non-options).
Stocke les valeurs d'options analysées.
Vue d'un tableau d'éléments de type T.
Definition Span.h:802
Chaîne de caractères unicode.
String getEnvironmentVariable(const String &name)
Variable d'environnement du nom name.
apfloat sqrt(apfloat v)
Racine carrée de v.
Definition MathApfloat.h:65
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Runtime-configurable wrappers around iterative solvers.