Arcane  4.2.2.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::PropertyTree& prm,
82 size_t rows,
83 std::vector<ptrdiff_t> const& ptr,
84 std::vector<ptrdiff_t> const& col,
85 std::vector<double> const& val,
86 std::vector<double> const& rhs,
87 std::vector<double>& x,
88 bool reorder)
89{
90 auto& prof = Alina::Profiler::globalProfiler();
91
92 typedef Alina::StaticMatrix<double, B, B> value_type;
93 typedef Alina::StaticMatrix<double, B, 1> rhs_type;
94 typedef Alina::BuiltinBackend<value_type> BBackend;
95
97
98 auto As = std::tie(rows, ptr, col, val);
99 auto Ab = Alina::adapter::block_matrix<value_type>(As);
100
101 std::tuple<size_t, double> info;
102
103 if (reorder) {
104 prof.tic("reorder");
106 prof.toc("reorder");
107
108 prof.tic("setup");
109 Solver solve(perm(Ab), prm);
110 prof.toc("setup");
111
112 std::cout << solve << std::endl;
113
114 rhs_type const* fptr = reinterpret_cast<rhs_type const*>(&rhs[0]);
115 rhs_type* xptr = reinterpret_cast<rhs_type*>(&x[0]);
116
118 Alina::numa_vector<rhs_type> X(perm(SmallSpan<rhs_type>(xptr, rows / B)));
119
120 prof.tic("solve");
121 info = solve(F, X);
122 prof.toc("solve");
123
124 perm.inverse(X, xptr);
125 }
126 else {
127 prof.tic("setup");
128 Solver solve(Ab, prm);
129 prof.toc("setup");
130
131 std::cout << solve << std::endl;
132
133 rhs_type const* fptr = reinterpret_cast<rhs_type const*>(&rhs[0]);
134 rhs_type* xptr = reinterpret_cast<rhs_type*>(&x[0]);
135
136 Alina::numa_vector<rhs_type> F(fptr, fptr + rows / B);
137 Alina::numa_vector<rhs_type> X(xptr, xptr + rows / B);
138
139 prof.tic("solve");
140 info = solve(F, X);
141 prof.toc("solve");
142
143 std::copy(X.data(), X.data() + X.size(), xptr);
144 }
145
146 return info;
147}
148#endif
149
150//---------------------------------------------------------------------------
152scalar_solve(const Alina::PropertyTree& prm,
153 size_t rows,
154 std::vector<ptrdiff_t> const& ptr,
155 std::vector<ptrdiff_t> const& col,
156 std::vector<double> const& val,
157 std::vector<double> const& rhs,
158 std::vector<double>& x,
159 bool reorder)
160{
161 std::cout << "Using scalar solve ptr_size=" << sizeof(ptrdiff_t)
162 << " ptr_type_size=" << sizeof(Backend::ptr_type)
163 << " col_type_size=" << sizeof(Backend::col_type)
164 << " value_type_size=" << sizeof(Backend::value_type)
165 << "\n";
166 auto& prof = Alina::Profiler::globalProfiler();
167 Backend::params bprm;
168
169#if defined(SOLVER_BACKEND_CUDA)
170 cusparseCreate(&bprm.cusparse_handle);
171 {
172 int dev;
173 cudaGetDevice(&dev);
174
175 cudaDeviceProp prop;
176 cudaGetDeviceProperties(&prop, dev);
177 std::cout << prop.name << std::endl
178 << std::endl;
179 }
180#endif
181
183
185
186 if (reorder) {
187 prof.tic("reorder");
188 Alina::adapter::reorder<> perm(std::tie(rows, ptr, col, val));
189 prof.toc("reorder");
190
191 prof.tic("setup");
192 Solver solve(perm(std::tie(rows, ptr, col, val)), prm, bprm);
193 prof.toc("setup");
194
195 std::cout << solve << std::endl;
196
197 std::vector<double> tmp(rows);
198
199 perm.forward(rhs, tmp);
200 auto f_b = Backend::copy_vector(tmp, bprm);
201
202 perm.forward(x, tmp);
203 auto x_b = Backend::copy_vector(tmp, bprm);
204
205 prof.tic("solve");
206 info = solve(*f_b, *x_b);
207 prof.toc("solve");
208
209#if defined(SOLVER_BACKEND_CUDA)
210 thrust::copy(x_b->begin(), x_b->end(), tmp.begin());
211#else
212 std::copy(&(*x_b)[0], &(*x_b)[0] + rows, &tmp[0]);
213#endif
214
215 perm.inverse(tmp, x);
216 }
217 else {
218 prof.tic("setup");
219 Solver solve(std::tie(rows, ptr, col, val), prm, bprm);
220 prof.toc("setup");
221
222 std::cout << solve << std::endl;
223
224 auto f_b = Backend::copy_vector(rhs, bprm);
225 auto x_b = Backend::copy_vector(x, bprm);
226
227 prof.tic("solve");
228 info = solve(*f_b, *x_b);
229 prof.toc("solve");
230
231#if defined(SOLVER_BACKEND_CUDA)
232 thrust::copy(x_b->begin(), x_b->end(), x.begin());
233#else
234 std::copy(&(*x_b)[0], &(*x_b)[0] + rows, &x[0]);
235#endif
236 }
237
238 return info;
239}
240
241#define ARCCORE_ALINA_CALL_BLOCK_SOLVER(z, data, B) \
242 case B: \
243 return block_solve<B>(prm, rows, ptr, col, val, rhs, x, reorder);
244
245//---------------------------------------------------------------------------
247solve(const Alina::PropertyTree& prm,
248 size_t rows,
249 std::vector<ptrdiff_t> const& ptr,
250 std::vector<ptrdiff_t> const& col,
251 std::vector<double> const& val,
252 std::vector<double> const& rhs,
253 std::vector<double>& x,
254 int block_size,
255 bool reorder)
256{
257 switch (block_size) {
258 case 1:
259 return scalar_solve(prm, rows, ptr, col, val, rhs, x, reorder);
260#if defined(SOLVER_BACKEND_BUILTIN)
261 BOOST_PP_SEQ_FOR_EACH(ARCCORE_ALINA_CALL_BLOCK_SOLVER, ~, ARCCORE_ALINA_BLOCK_SIZES)
262#endif
263 default:
264 precondition(false, "Unsupported block size");
265 return {};
266 }
267}
268
269//---------------------------------------------------------------------------
270int main(int argc, char* argv[])
271{
272 auto& prof = Alina::Profiler::globalProfiler();
273 namespace po = Arcane::ProgramOptions;
274 namespace io = Alina::IO;
275
276 using std::string;
277 using std::vector;
278
279 po::options_description desc("Options");
280
281 desc.add_options()("help,h", "Show this help.")("prm-file,P",
282 po::value<string>(),
283 "Parameter file in json format. ")(
284 "prm,p",
285 po::value<vector<string>>()->multitoken(),
286 "Parameters specified as name=value pairs. "
287 "May be provided multiple times. Examples:\n"
288 " -p solver.tol=1e-3\n"
289 " -p precond.coarse_enough=300")("matrix,A",
290 po::value<string>(),
291 "System matrix in the MatrixMarket format. "
292 "When not specified, solves a Poisson problem in 3D unit cube. ")(
293 "rhs,f",
294 po::value<string>(),
295 "The RHS vector in the MatrixMarket format. "
296 "When omitted, a vector of ones is used by default. "
297 "Should only be provided together with a system matrix. ")(
298 "f0",
299 po::bool_switch()->default_value(false),
300 "Use zero RHS vector. Implies --random-initial and solver.ns_search=true")(
301 "f1",
302 po::bool_switch()->default_value(false),
303 "Set RHS = Ax where x = 1")(
304 "null,N",
305 po::value<string>(),
306 "The near null-space vectors in the MatrixMarket format. "
307 "Should be a dense matrix of size N*M, where N is the number of "
308 "unknowns, and M is the number of null-space vectors. "
309 "Should only be provided together with a system matrix. ")(
310 "coords,C",
311 po::value<string>(),
312 "Coordinate matrix where number of rows corresponds to the number of grid nodes "
313 "and the number of columns corresponds to the problem dimensionality (2 or 3). "
314 "Will be used to construct near null-space vectors as rigid body modes. "
315 "Should only be provided together with a system matrix. ")(
316 "binary,B",
317 po::bool_switch()->default_value(false),
318 "When specified, treat input files as binary instead of as MatrixMarket. "
319 "It is assumed the files were converted to binary format with mm2bin utility. ")(
320 "scale,s",
321 po::bool_switch()->default_value(false),
322 "Scale the matrix so that the diagonal is unit. ")(
323 "block-size,b",
324 po::value<int>()->default_value(1),
325 "The block size of the system matrix. "
326 "When specified, the system matrix is assumed to have block-wise structure. "
327 "This usually is the case for problems in elasticity, structural mechanics, "
328 "for coupled systems of PDE (such as Navier-Stokes equations), etc. ")(
329 "size,n",
330 po::value<int>()->default_value(32),
331 "The size of the Poisson problem to solve when no system matrix is given. "
332 "Specified as number of grid nodes along each dimension of a unit cube. "
333 "The resulting system will have n*n*n unknowns. ")(
334 "anisotropy,a",
335 po::value<double>()->default_value(1.0),
336 "The anisotropy value for the generated Poisson value. "
337 "Used to determine problem scaling along X, Y, and Z axes: "
338 "hy = hx * a, hz = hy * a.")(
339 "single-level,1",
340 po::bool_switch()->default_value(false),
341 "When specified, the AMG hierarchy is not constructed. "
342 "Instead, the problem is solved using a single-level smoother as preconditioner. ")(
343 "reorder,r",
344 po::bool_switch()->default_value(false),
345 "When specified, the matrix will be reordered to improve cache-locality")(
346 "initial,x",
347 po::value<double>()->default_value(0),
348 "Value to use as initial approximation. ")(
349 "random-initial",
350 po::bool_switch()->default_value(false),
351 "Use random initial approximation. ")(
352 "output,o",
353 po::value<string>(),
354 "Output file. Will be saved in the MatrixMarket format. "
355 "When omitted, the solution is not saved. ");
356
358 p.add("prm", -1);
359
361 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
362 po::notify(vm);
363
364 if (vm.count("help")) {
365 std::cout << desc << std::endl;
366 return 0;
367 }
368
369 for (int i = 0; i < argc; ++i) {
370 if (i)
371 std::cout << " ";
372 std::cout << argv[i];
373 }
374 std::cout << std::endl;
375
377 if (vm.count("prm-file")) {
378 prm.read_json(vm["prm-file"].as<string>());
379 }
380
381 if (vm.count("prm")) {
382 for (const string& v : vm["prm"].as<vector<string>>()) {
383 prm.putKeyValue(v);
384 }
385 }
386
387 size_t rows, nv = 0;
388 vector<ptrdiff_t> ptr, col;
389 vector<double> val, rhs, null, x;
390
391 if (vm.count("matrix")) {
392 auto t = prof.scoped_tic("reading");
393
394 string Afile = vm["matrix"].as<string>();
395 bool binary = vm["binary"].as<bool>();
396
397 if (binary) {
398 io::read_crs(Afile, rows, ptr, col, val);
399 }
400 else {
401 size_t cols;
402 std::tie(rows, cols) = io::mm_reader(Afile)(ptr, col, val);
403 precondition(rows == cols, "Non-square system matrix");
404 }
405
406 if (vm.count("rhs")) {
407 string bfile = vm["rhs"].as<string>();
408
409 size_t n, m;
410
411 if (binary) {
412 io::read_dense(bfile, n, m, rhs);
413 }
414 else {
415 std::tie(n, m) = io::mm_reader(bfile)(rhs);
416 }
417
418 precondition(n == rows && m == 1, "The RHS vector has wrong size");
419 }
420 else if (vm["f1"].as<bool>()) {
421 rhs.resize(rows);
422 for (size_t i = 0; i < rows; ++i) {
423 double s = 0;
424 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j)
425 s += val[j];
426 rhs[i] = s;
427 }
428 }
429 else {
430 rhs.resize(rows, vm["f0"].as<bool>() ? 0.0 : 1.0);
431 }
432
433 if (vm.count("null")) {
434 string nfile = vm["null"].as<string>();
435
436 size_t m;
437
438 if (binary) {
439 io::read_dense(nfile, m, nv, null);
440 }
441 else {
442 std::tie(m, nv) = io::mm_reader(nfile)(null);
443 }
444
445 precondition(m == rows, "Near null-space vectors have wrong size");
446 }
447 else if (vm.count("coords")) {
448 string cfile = vm["coords"].as<string>();
449 std::vector<double> coo;
450
451 size_t m, ndim;
452
453 if (binary) {
454 io::read_dense(cfile, m, ndim, coo);
455 }
456 else {
457 std::tie(m, ndim) = io::mm_reader(cfile)(coo);
458 }
459
460 precondition(m * ndim == rows && (ndim == 2 || ndim == 3), "Coordinate matrix has wrong size");
461
462 nv = Alina::rigid_body_modes(ndim, coo, null);
463 }
464
465 if (nv) {
466 prm.put("precond.coarsening.nullspace.cols", nv);
467 prm.put("precond.coarsening.nullspace.rows", rows);
468 prm.put("precond.coarsening.nullspace.B", &null[0]);
469 }
470 }
471 else {
472 auto t = prof.scoped_tic("assembling");
473 rows = sample_problem(vm["size"].as<int>(), val, col, ptr, rhs, vm["anisotropy"].as<double>());
474 }
475
476 if (vm["scale"].as<bool>()) {
477 std::vector<double> dia(rows, 1.0);
478
479 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
480 double d = 1.0;
481 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
482 if (col[j] == i) {
483 d = 1 / sqrt(val[j]);
484 }
485 }
486 if (!std::isnan(d))
487 dia[i] = d;
488 }
489
490 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
491 rhs[i] *= dia[i];
492 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
493 val[j] *= dia[i] * dia[col[j]];
494 }
495 }
496 }
497
498 x.resize(rows, vm["initial"].as<double>());
499 if (vm["random-initial"].as<bool>() || vm["f0"].as<bool>()) {
500 std::mt19937 rng;
501 std::uniform_real_distribution<double> rnd(-1, 1);
502 for (auto& v : x)
503 v = rnd(rng);
504 }
505
506 if (vm["f0"].as<bool>()) {
507 prm.put("solver.ns_search", true);
508 }
509
510 int block_size = vm["block-size"].as<int>();
511 std::cout << "BlockSize= " << block_size << "\n";
512
513 if (vm["single-level"].as<bool>())
514 prm.put("precond.class", "relaxation");
515
516 String do_hypre_str = Platform::getEnvironmentVariable("ALINA_USE_HYPRE");
517 bool do_hypre = false;
518 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ALINA_USE_HYPRE", true))
519 do_hypre = v.value();
520
521 Alina::SolverResult solver_result;
522#ifndef SOLVER_BACKEND_BUILTIN
523 do_hypre = false;
524#endif
525 if (do_hypre) {
526#ifdef SOLVER_BACKEND_BUILTIN
527 HypreComparer hypre_comparer(true);
528 hypre_comparer.solve(rows, ptr, col, val, rhs, x, argc, argv);
529#endif
530 }
531 else {
532 solver_result = solve(prm, rows, ptr, col, val, rhs, x, block_size, vm["reorder"].as<bool>());
533
534 if (vm.count("output")) {
535 auto t = prof.scoped_tic("write");
536 Alina::IO::mm_write(vm["output"].as<string>(), &x[0], x.size());
537 }
538 }
539 std::cout << "Iterations: " << solver_result.nbIteration() << std::endl
540 << "Error: " << solver_result.residual() << std::endl
541 << prof << std::endl;
542}
Convenience class that bundles together a preconditioner and an iterative solver.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:112
Result of a solution.
Definition AlinaUtils.h:53
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.