Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
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 * This file is based on the work on AMGCL library (version march 2026)
11 * which can be found at 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// To remove warnings about deprecated Eigen usage.
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// Use 32 bit indexing for 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 << "Using scalar solve ptr_size=" << sizeof(ptrdiff_t)
164 << " ptr_type_size=" << sizeof(Backend::ptr_type)
165 << " col_type_size=" << sizeof(Backend::col_type)
166 << " value_type_size=" << 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, "Unsupported block size");
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", "Show this help.")("prm-file,P",
287 po::value<string>(),
288 "Parameter file in json format. ")(
289 "prm,p",
290 po::value<vector<string>>()->multitoken(),
291 "Parameters specified as name=value pairs. "
292 "May be provided multiple times. Examples:\n"
293 " -p solver.tol=1e-3\n"
294 " -p precond.coarse_enough=300")("matrix,A",
295 po::value<string>(),
296 "System matrix in the MatrixMarket format. "
297 "When not specified, solves a Poisson problem in 3D unit cube. ")(
298 "rhs,f",
299 po::value<string>(),
300 "The RHS vector in the MatrixMarket format. "
301 "When omitted, a vector of ones is used by default. "
302 "Should only be provided together with a system matrix. ")(
303 "f0",
304 po::bool_switch()->default_value(false),
305 "Use zero RHS vector. Implies --random-initial and solver.ns_search=true")(
306 "f1",
307 po::bool_switch()->default_value(false),
308 "Set RHS = Ax where x = 1")(
309 "null,N",
310 po::value<string>(),
311 "The near null-space vectors in the MatrixMarket format. "
312 "Should be a dense matrix of size N*M, where N is the number of "
313 "unknowns, and M is the number of null-space vectors. "
314 "Should only be provided together with a system matrix. ")(
315 "coords,C",
316 po::value<string>(),
317 "Coordinate matrix where number of rows corresponds to the number of grid nodes "
318 "and the number of columns corresponds to the problem dimensionality (2 or 3). "
319 "Will be used to construct near null-space vectors as rigid body modes. "
320 "Should only be provided together with a system matrix. ")(
321 "binary,B",
322 po::bool_switch()->default_value(false),
323 "When specified, treat input files as binary instead of as MatrixMarket. "
324 "It is assumed the files were converted to binary format with mm2bin utility. ")(
325 "scale,s",
326 po::bool_switch()->default_value(false),
327 "Scale the matrix so that the diagonal is unit. ")(
328 "block-size,b",
329 po::value<int>()->default_value(1),
330 "The block size of the system matrix. "
331 "When specified, the system matrix is assumed to have block-wise structure. "
332 "This usually is the case for problems in elasticity, structural mechanics, "
333 "for coupled systems of PDE (such as Navier-Stokes equations), etc. ")(
334 "size,n",
335 po::value<int>()->default_value(32),
336 "The size of the Poisson problem to solve when no system matrix is given. "
337 "Specified as number of grid nodes along each dimension of a unit cube. "
338 "The resulting system will have n*n*n unknowns. ")(
339 "anisotropy,a",
340 po::value<double>()->default_value(1.0),
341 "The anisotropy value for the generated Poisson value. "
342 "Used to determine problem scaling along X, Y, and Z axes: "
343 "hy = hx * a, hz = hy * a.")(
344 "single-level,1",
345 po::bool_switch()->default_value(false),
346 "When specified, the AMG hierarchy is not constructed. "
347 "Instead, the problem is solved using a single-level smoother as preconditioner. ")(
348 "reorder,r",
349 po::bool_switch()->default_value(false),
350 "When specified, the matrix will be reordered to improve cache-locality")(
351 "initial,x",
352 po::value<double>()->default_value(0),
353 "Value to use as initial approximation. ")(
354 "random-initial",
355 po::bool_switch()->default_value(false),
356 "Use random initial approximation. ")(
357 "output,o",
358 po::value<string>(),
359 "Output file. Will be saved in the MatrixMarket format. "
360 "When omitted, the solution is not saved. ");
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, "Non-square system matrix");
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, "The RHS vector has wrong size");
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, "Near null-space vectors have wrong size");
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), "Coordinate matrix has wrong size");
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 << "Iterations: " << solver_result.nbIteration() << std::endl
545 << "Error: " << solver_result.residual() << std::endl
546 << prof << std::endl;
547}
Convenience class that bundles together a preconditioner and an iterative solver.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Result of a solution.
Definition AlinaUtils.h:56
NUMA-aware vector container.
Definition NumaVector.h:42
Template class for converting a type.
Fluent command-line parser builder.
Describes a set of command-line options.
Describes positional (non-option) arguments.
Stores parsed option values.
View of an array of elements of type T.
Definition Span.h:803
String getEnvironmentVariable(const String &name)
Environment variable named name.
apfloat sqrt(apfloat v)
Square root of v.
Definition MathApfloat.h:69
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Runtime-configurable wrappers around iterative solvers.