Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
DistributedSCHUR.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// For Eigen
20#pragma GCC diagnostic ignored "-Wdeprecated-copy"
21#pragma GCC diagnostic ignored "-Wint-in-bool-context"
22
23#include <iostream>
24#include <iterator>
25#include <iomanip>
26#include <fstream>
27#include <vector>
28#include <numeric>
29#include <cmath>
30
31#include <boost/range/iterator_range.hpp>
32
33#if defined(SOLVER_BACKEND_CUDA)
34# include "arccore/alina/CudaBackend.h"
35# include "arccore/alina/relaxation_cusparse_ilu0.h"
37#else
38# ifndef SOLVER_BACKEND_BUILTIN
39# define SOLVER_BACKEND_BUILTIN
40# endif
41#include "arccore/alina/BuiltinBackend.h"
43#endif
44
45#include "arccore/alina/IO.h"
46#include "arccore/alina/Adapters.h"
47#include "arccore/alina/AMG.h"
48#include "arccore/alina/CoarseningRuntime.h"
49#include "arccore/alina/RelaxationRuntime.h"
50#include "arccore/alina/DistributedPreconditionedSolver.h"
51#include "arccore/alina/DistributedSchurPressureCorrection.h"
52#include "arccore/alina/DistributedPreconditioner.h"
53#include "arccore/alina/DistributedSubDomainDeflation.h"
54#include "arccore/alina/DistributedSolverRuntime.h"
55#include "arccore/alina/DistributedDirectSolverRuntime.h"
56#include "arccore/alina/Profiler.h"
57
58#include "arccore/common/internal/ProgramOptions.h"
59
60using namespace Arcane;
61using namespace Arcane::Alina;
62
63using Alina::precondition;
64
65//---------------------------------------------------------------------------
66std::vector<ptrdiff_t>
67read_problem(const Alina::AlinaCommunicator& world,
68 const std::string& A_file,
69 const std::string& rhs_file,
70 const std::string& part_file,
71 std::vector<ptrdiff_t>& ptr,
72 std::vector<ptrdiff_t>& col,
73 std::vector<double>& val,
74 std::vector<double>& rhs)
75{
76 // Read partition
77 ptrdiff_t n, m;
78 std::vector<ptrdiff_t> domain(world.size + 1, 0);
79 std::vector<int> part;
80
81 std::tie(n, m) = Alina::IO::mm_reader(part_file)(part);
82 for (int p : part) {
83 ++domain[p + 1];
84 precondition(p < world.size, "MPI world does not correspond to partition");
85 }
86 std::partial_sum(domain.begin(), domain.end(), domain.begin());
87
88 ptrdiff_t chunk_beg = domain[world.rank];
89 ptrdiff_t chunk_end = domain[world.rank + 1];
90 ptrdiff_t chunk = chunk_end - chunk_beg;
91
92 // Reorder unknowns
93 std::vector<ptrdiff_t> order(n);
94 for (ptrdiff_t i = 0; i < n; ++i)
95 order[i] = domain[part[i]]++;
96
97 std::rotate(domain.begin(), domain.end() - 1, domain.end());
98 domain[0] = 0;
99
100 // Read matrix chunk
101 {
102 using namespace Arcane::Alina::IO;
103
104 std::ifstream A(A_file.c_str(), std::ios::binary);
105 precondition(A, "Failed to open matrix file (" + A_file + ")");
106
107 std::ifstream b(rhs_file.c_str(), std::ios::binary);
108 precondition(b, "Failed to open rhs file (" + rhs_file + ")");
109
110 ptrdiff_t rows;
111 precondition(read(A, rows), "File I/O error");
112 precondition(rows == n, "Matrix and partition have incompatible sizes");
113
114 ptr.clear();
115 ptr.reserve(chunk + 1);
116 ptr.push_back(0);
117
118 std::vector<ptrdiff_t> gptr(n + 1);
119 precondition(read(A, gptr), "File I/O error");
120
121 size_t col_beg = sizeof(rows) + sizeof(gptr[0]) * (n + 1);
122 size_t val_beg = col_beg + sizeof(col[0]) * gptr.back();
123 size_t rhs_beg = 2 * sizeof(ptrdiff_t);
124
125 // Count local nonzeros
126 for (ptrdiff_t i = 0; i < n; ++i)
127 if (part[i] == world.rank)
128 ptr.push_back(gptr[i + 1] - gptr[i]);
129
130 std::partial_sum(ptr.begin(), ptr.end(), ptr.begin());
131
132 col.clear();
133 col.reserve(ptr.back());
134 val.clear();
135 val.reserve(ptr.back());
136 rhs.clear();
137 rhs.reserve(chunk);
138
139 // Read local matrix and rhs stripes
140 for (ptrdiff_t i = 0; i < n; ++i) {
141 if (part[i] != world.rank)
142 continue;
143
144 ptrdiff_t c;
145 A.seekg(col_beg + gptr[i] * sizeof(c));
146 for (ptrdiff_t j = gptr[i], e = gptr[i + 1]; j < e; ++j) {
147 precondition(read(A, c), "File I/O error (1)");
148 col.push_back(order[c]);
149 }
150 }
151
152 for (ptrdiff_t i = 0; i < n; ++i) {
153 if (part[i] != world.rank)
154 continue;
155
156 double v;
157 A.seekg(val_beg + gptr[i] * sizeof(v));
158 for (ptrdiff_t j = gptr[i], e = gptr[i + 1]; j < e; ++j) {
159 precondition(read(A, v), "File I/O error (2)");
160 val.push_back(v);
161 }
162 }
163
164 for (ptrdiff_t i = 0; i < n; ++i) {
165 if (part[i] != world.rank)
166 continue;
167
168 double f;
169 b.seekg(rhs_beg + i * sizeof(f));
170 precondition(read(b, f), "File I/O error (3)");
171 rhs.push_back(f);
172 }
173 }
174
175 return domain;
176}
177
178//---------------------------------------------------------------------------
179int main(int argc, char* argv[])
180{
181 auto& prof = Alina::Profiler::globalProfiler();
183 auto mpm = StandaloneMpi::createAndInitializeMPI(&argc, &argv, MPI_THREAD_MULTIPLE);
184 Alina::AlinaCommunicator world(mpm);
185 Alina::AlinaContext context;
186 if (world.rank == 0)
187 std::cout << "World size: " << world.size << std::endl;
188
189 // Read configuration from command line
190 namespace po = Arcane::ProgramOptions;
191 using std::string;
192 po::options_description desc("Options");
193
194 desc.add_options()("help,h", "show help")(
195 "matrix,A",
196 po::value<string>()->required(),
197 "The system matrix in binary format")(
198 "rhs,f",
199 po::value<string>(),
200 "The right-hand side in binary format")(
201 "part,s",
202 po::value<string>()->required(),
203 "Partitioning of the problem in MatrixMarket format")(
204 "pmask,m",
205 po::value<string>(),
206 "The pressure mask in binary format. Or, if the parameter has "
207 "the form '%n:m', then each (n+i*m)-th variable is treated as pressure.")(
208 "params,P",
209 po::value<string>(),
210 "parameter file in json format")(
211 "prm,p",
212 po::value<std::vector<string>>()->multitoken(),
213 "Parameters specified as name=value pairs. "
214 "May be provided multiple times. Examples:\n"
215 " -p solver.tol=1e-3\n"
216 " -p precond.coarse_enough=300");
217
219 po::store(po::parse_command_line(argc, argv, desc), vm);
220
221 if (vm.count("help")) {
222 if (world.rank == 0)
223 std::cout << desc << std::endl;
224 return 0;
225 }
226
227 po::notify(vm);
228
230 if (vm.count("params"))
231 prm.read_json(vm["params"].as<string>());
232
233 if (vm.count("prm")) {
234 for (const string& v : vm["prm"].as<std::vector<string>>()) {
235 prm.putKeyValue(v);
236 }
237 }
238
239 prof.tic("read problem");
240 std::vector<ptrdiff_t> ptr;
241 std::vector<ptrdiff_t> col;
242 std::vector<double> val;
243 std::vector<double> rhs;
244
245 std::vector<ptrdiff_t> domain = read_problem(
246 world,
247 vm["matrix"].as<string>(), vm["rhs"].as<string>(), vm["part"].as<string>(),
248 ptr, col, val, rhs);
249
250 ptrdiff_t chunk = domain[world.rank + 1] - domain[world.rank];
251 prof.toc("read problem");
252
253 std::vector<char> pm;
254 if (vm.count("pmask")) {
255 std::string pmask = vm["pmask"].as<string>();
256 prm.put("precond.pmask_size", chunk);
257
258 switch (pmask[0]) {
259 case '%':
260 case '<':
261 case '>':
262 prm.put("precond.pmask_pattern", pmask);
263 break;
264 default:
265 precondition(false, "Pressure mask may only be set with a pattern");
266 }
267 }
268
269 std::function<double(ptrdiff_t, unsigned)> dv = Alina::constant_deflation(1);
270 prm.put("precond.psolver.num_def_vec", 1);
271 prm.put("precond.psolver.def_vec", &dv);
272
273 Backend::params bprm;
274
275#if defined(SOLVER_BACKEND_VEXCL)
276 vex::Context ctx(vex::Filter::Env);
277 std::cout << ctx << std::endl;
278 bprm.q = ctx;
279#elif defined(SOLVER_BACKEND_CUDA)
280 cusparseCreate(&bprm.cusparse_handle);
281#endif
282
283 auto f = Backend::copy_vector(rhs, bprm);
284 auto x = Backend::create_vector(chunk, bprm);
285
286 Alina::backend::clear(*x);
287
288 world.barrier();
289
290 prof.tic("setup");
302 Solver;
303
304 Solver solve(context, world, std::tie(chunk, ptr, col, val), prm, bprm);
305 double tm_setup = prof.toc("setup");
306
307 prof.tic("solve");
308 Alina::SolverResult r = solve(*f, *x);
309 double tm_solve = prof.toc("solve");
310
311 if (world.rank == 0) {
312 std::cout << "Iters: " << r.nbIteration() << std::endl
313 << "Error: " << r.residual() << std::endl
314 << prof << std::endl;
315 }
316}
Algebraic multigrid method.
Definition AMG.h:71
Runtime wrapper for distributed direct solvers.
Iterative solver wrapper for distributed linear systems.
Distributed Schur complement pressure correction preconditioner.
Distributed solver based on subdomain deflation.
Matrix market reader.
Definition IO.h:54
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Allows to use an AMG smoother as standalone preconditioner.
Definition Relaxation.h:144
Result of a solution.
Definition AlinaUtils.h:56
Standalone version of IMessagePassingMng associated to a MPI Communicator.
Describes a set of command-line options.
Stores parsed option values.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Convenience wrapper around MPI_Comm.
Pointwise constant deflation vectors.