Arcane  4.2.1.0
Developer documentation
Loading...
Searching...
No Matches
DistributedCPR.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// To remove warnings about deprecated Eigen usage.
20#pragma GCC diagnostic ignored "-Wdeprecated-copy"
21#pragma GCC diagnostic ignored "-Wint-in-bool-context"
22
23#include <boost/range/iterator_range.hpp>
24#include <boost/scope_exit.hpp>
25
26#include "arccore/alina/IO.h"
27#include "arccore/alina/Adapters.h"
28#include "arccore/alina/BuiltinBackend.h"
29#include "arccore/alina/DistributedPreconditionedSolver.h"
30#include "arccore/alina/DistributedCPRPreconditioner.h"
31#include "arccore/alina/DistributedAMG.h"
32#include "arccore/alina/DistributedCoarseningRuntime.h"
33#include "arccore/alina/DistributedRelaxationRuntime.h"
34#include "arccore/alina/DistributedSolverRuntime.h"
35#include "arccore/alina/DistributedDirectSolverRuntime.h"
36#include "arccore/alina/MatrixPartitionerRuntime.h"
37#include "arccore/alina/Profiler.h"
38#include "arccore/alina/AlinaUtils.h"
39
40#include "arccore/common/internal/ProgramOptions.h"
41
42using namespace Arcane;
43using namespace Arcane::Alina;
44
45using Alina::precondition;
46
47//---------------------------------------------------------------------------
48ptrdiff_t
49read_matrix_market(Alina::mpi_communicator comm,
50 const std::string& A_file, const std::string& rhs_file, int block_size,
51 std::vector<ptrdiff_t>& ptr,
52 std::vector<ptrdiff_t>& col,
53 std::vector<double>& val,
54 std::vector<double>& rhs)
55{
56 Alina::IO::mm_reader A_mm(A_file);
57 ptrdiff_t n = A_mm.rows();
58
59 ptrdiff_t chunk = (n + comm.size - 1) / comm.size;
60 if (chunk % block_size != 0) {
61 chunk += block_size - chunk % block_size;
62 }
63
64 ptrdiff_t row_beg = std::min(n, chunk * comm.rank);
65 ptrdiff_t row_end = std::min(n, row_beg + chunk);
66
67 chunk = row_end - row_beg;
68
69 A_mm(ptr, col, val, row_beg, row_end);
70
71 if (rhs_file.empty()) {
72 rhs.resize(chunk);
73 std::fill(rhs.begin(), rhs.end(), 1.0);
74 }
75 else {
76 Alina::IO::mm_reader rhs_mm(rhs_file);
77 rhs_mm(rhs, row_beg, row_end);
78 }
79
80 return chunk;
81}
82
83//---------------------------------------------------------------------------
84ptrdiff_t
85read_binary(Alina::mpi_communicator comm,
86 const std::string& A_file, const std::string& rhs_file, int block_size,
87 std::vector<ptrdiff_t>& ptr,
88 std::vector<ptrdiff_t>& col,
89 std::vector<double>& val,
90 std::vector<double>& rhs)
91{
92 ptrdiff_t n = Alina::IO::crs_size<ptrdiff_t>(A_file);
93
94 ptrdiff_t chunk = (n + comm.size - 1) / comm.size;
95 if (chunk % block_size != 0) {
96 chunk += block_size - chunk % block_size;
97 }
98
99 ptrdiff_t row_beg = std::min(n, chunk * comm.rank);
100 ptrdiff_t row_end = std::min(n, row_beg + chunk);
101
102 chunk = row_end - row_beg;
103
104 Alina::IO::read_crs(A_file, n, ptr, col, val, row_beg, row_end);
105
106 if (rhs_file.empty()) {
107 rhs.resize(chunk);
108 std::fill(rhs.begin(), rhs.end(), 1.0);
109 }
110 else {
111 ptrdiff_t rows, cols;
112 Alina::IO::read_dense(rhs_file, rows, cols, rhs, row_beg, row_end);
113 }
114
115 return chunk;
116}
117
118//---------------------------------------------------------------------------
119template <class Backend, class Matrix>
120std::shared_ptr<Alina::DistributedMatrix<Backend>>
121partition(Alina::mpi_communicator comm, const Matrix& Astrip,
122 std::vector<double>& rhs, const typename Backend::params& bprm,
123 Alina::eMatrixPartitionerType ptype, int block_size = 1)
124{
125 auto& prof = Alina::Profiler::globalProfiler();
126 typedef Alina::DistributedMatrix<Backend> DMatrix;
127
128 auto A = std::make_shared<DMatrix>(comm, Astrip);
129
130 if (comm.size == 1 || ptype == Alina::eMatrixPartitionerType::merge)
131 return A;
132
133 prof.tic("partition");
135 prm.put("type", ptype);
137
138 auto I = part(*A, block_size);
139 auto J = transpose(*I);
140 A = product(*J, *product(*A, *I));
141
142 std::vector<double> new_rhs(J->loc_rows());
143
144 J->move_to_backend(bprm);
145
146 Alina::backend::spmv(1, *J, rhs, 0, new_rhs);
147 rhs.swap(new_rhs);
148 prof.toc("partition");
149
150 return A;
151}
152
153//---------------------------------------------------------------------------
154
155int main(int argc, char* argv[])
156{
157 auto& prof = Alina::Profiler::globalProfiler();
158 int provided;
159 MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
160 BOOST_SCOPE_EXIT(void)
161 {
162 MPI_Finalize();
163 }
164 BOOST_SCOPE_EXIT_END
165
166 Alina::mpi_communicator comm(MPI_COMM_WORLD);
167
168 if (comm.rank == 0)
169 std::cout << "World size: " << comm.size << std::endl;
170
171 // Read configuration from command line
172 namespace po = Arcane::ProgramOptions;
173 po::options_description desc("Options");
174
175 desc.add_options()("help,h", "show help")("matrix,A",
176 po::value<std::string>(),
177 "System matrix in the MatrixMarket format. "
178 "When not specified, a Poisson problem in 3D unit cube is assembled. ")(
179 "rhs,f",
180 po::value<std::string>()->default_value(""),
181 "The RHS vector in the MatrixMarket format. "
182 "When omitted, a vector of ones is used by default. "
183 "Should only be provided together with a system matrix. ")(
184 "binary,B",
185 po::bool_switch()->default_value(false),
186 "When specified, treat input files as binary instead of as MatrixMarket. "
187 "It is assumed the files were converted to binary format with mm2bin utility. ")(
188 "block-size,b",
189 po::value<int>()->default_value(1),
190 "The block size of the system matrix. ")(
191 "partitioner,r",
192 po::value<Alina::eMatrixPartitionerType>()->default_value(
193#if defined(ARCCORE_ALINA_HAVE_PARMETIS)
194 Alina::eMatrixPartitionerType::parmetis
195#endif
196 ),
197 "Repartition the system matrix")("prm-file,P",
198 po::value<std::string>(),
199 "Parameter file in json format. ")(
200 "prm,p",
201 po::value<std::vector<std::string>>()->multitoken(),
202 "Parameters specified as name=value pairs. "
203 "May be provided multiple times. Examples:\n"
204 " -p solver.tol=1e-3\n"
205 " -p precond.coarse_enough=300");
206
208 po::store(po::parse_command_line(argc, argv, desc), vm);
209 po::notify(vm);
210
211 if (vm.count("help")) {
212 if (comm.rank == 0)
213 std::cout << desc << std::endl;
214 return 0;
215 }
216
218 if (vm.count("prm-file")) {
219 prm.read_json(vm["prm-file"].as<std::string>());
220 }
221
222 if (vm.count("prm")) {
223 for (const std::string& v : vm["prm"].as<std::vector<std::string>>()) {
224 prm.putKeyValue(v);
225 }
226 }
227
228 ptrdiff_t n;
229 std::vector<ptrdiff_t> ptr;
230 std::vector<ptrdiff_t> col;
231 std::vector<double> val;
232 std::vector<double> rhs;
233
234 int block_size = vm["block-size"].as<int>();
235 prm.put("precond.block_size", block_size);
236
237 prof.tic("read");
238 if (vm["binary"].as<bool>()) {
239 n = read_binary(comm,
240 vm["matrix"].as<std::string>(),
241 vm["rhs"].as<std::string>(),
242 block_size, ptr, col, val, rhs);
243 }
244 else {
245 n = read_matrix_market(comm,
246 vm["matrix"].as<std::string>(),
247 vm["rhs"].as<std::string>(),
248 block_size, ptr, col, val, rhs);
249 }
250 prof.toc("read");
251
252 typedef Alina::BuiltinBackend<double> Backend;
253
254 auto A = partition<Backend>(comm,
255 std::tie(n, ptr, col, val), rhs, Backend::params(),
256 vm["partitioner"].as<Alina::eMatrixPartitionerType>(),
257 block_size);
258
259 prof.tic("setup");
260
261 using AMG = DistributedAMG<Backend,
266
269 AMG,
272 Solver;
273
274 Solver solve(comm, A, prm);
275 prof.toc("setup");
276
277 if (comm.rank == 0)
278 std::cout << solve << std::endl;
279
280 std::vector<double> x(rhs.size(), 0.0);
281
282 prof.tic("solve");
283 Alina::SolverResult r = solve(rhs, x);
284 prof.toc("solve");
285
286 if (comm.rank == 0) {
287 std::cout << "Iterations: " << r.nbIteration() << std::endl
288 << "Error: " << r.residual() << std::endl
289 << prof << std::endl;
290 }
291}
Algebraic multigrid method.
Definition AMG.h:71
Runtime wrapper for distributed direct solvers.
Distributed Matrix using message passing.
Iterative solver wrapper for distributed linear systems.
Matrix market reader.
Definition IO.h:54
Result of a solution.
Definition AlinaUtils.h:53
Matrix class, to be used by user.
Describes a set of command-line options.
Stores parsed option values.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Use a relaxation as a distributed preconditioner.
Distributed memory sparse approximate inverse relaxation scheme.
Runtime-configurable wrapper around matrix partitioner.
Convenience wrapper around MPI_Comm.