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