Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
DistributedDirectSolverBase.h
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/* DistributedDirectSolverBase.h (C) 2000-2026 */
9/* */
10/* Base class for distributed direct solver. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_MPI_DISTRIBUTEDDIRECTSOLVERBASE_H
13#define ARCCORE_ALINA_MPI_DISTRIBUTEDDIRECTSOLVERBASE_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26#include "arccore/alina/MessagePassingUtils.h"
27#include "arccore/alina/DistributedMatrix.h"
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Alina
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
40template <typename Backend, class Solver>
41class DistributedDirectSolverBase
42{
43 public:
44
45 typedef typename Backend::value_type value_type;
46 typedef typename math::scalar_of<value_type>::type scalar_type;
47 typedef typename math::rhs_of<value_type>::type rhs_type;
48 //typedef Backend::matrix build_matrix;
49 using col_type = Backend::col_type;
50 using ptr_type = Backend::ptr_type;
52
53 DistributedDirectSolverBase() {}
54
55 void init(AlinaCommunicator comm, const build_matrix& Astrip)
56 {
57 this->comm = comm;
58 n = Astrip.nbRow();
59
60 UniqueArray<int> domain = comm.exclusive_sum(n);
61 std::vector<int> active;
62 active.reserve(comm.size);
63
64 // Find out how many ranks are active (own non-zero matrix rows):
65 int active_rank = 0;
66 for (int i = 0; i < comm.size; ++i) {
67 if (domain[i + 1] - domain[i] > 0) {
68 if (comm.rank == i)
69 active_rank = active.size();
70 active.push_back(i);
71 }
72 }
73
74 // Consolidate the matrix on a fewer processes.
75 int nmasters = std::min<int>(active.size(), solver().comm_size(domain.back()));
76 int slaves_per_master = (active.size() + nmasters - 1) / nmasters;
77 int group_beg = (active_rank / slaves_per_master) * slaves_per_master;
78
79 group_master = active[group_beg];
80
81 // Communicator for masters (used to solve the coarse problem):
82 bool keep = (comm.rank == group_master);
83 m_masters_comm = mpSplitCommunicator(comm.messagePassingMng(), keep);
84 if (!n)
85 return; // I am not active
86
87 // Shift from row pointers to row widths:
88 std::vector<ptr_type> widths(n);
89 for (ptrdiff_t i = 0; i < n; ++i)
90 widths[i] = Astrip.ptr[i + 1] - Astrip.ptr[i];
91
92 if (comm.rank == group_master) {
93 int group_end = std::min<int>(group_beg + slaves_per_master, active.size());
94 group_beg += 1;
95 int group_size = group_end - group_beg;
96
97 UniqueArray<MessagePassing::Request> cnt_req(group_size);
98 UniqueArray<MessagePassing::Request> col_req(group_size);
99 UniqueArray<MessagePassing::Request> val_req(group_size);
100
101 solve_req.resize(group_size);
102 slaves.reserve(group_size);
103 counts.reserve(group_size);
104
105 // Count rows in local chunk of the consolidated matrix,
106 // see who is reporting to us.
107 int nloc = n;
108 for (int j = group_beg; j < group_end; ++j) {
109 int i = active[j];
110
111 int m = domain[i + 1] - domain[i];
112 nloc += m;
113 counts.push_back(m);
114 slaves.push_back(i);
115 }
116
117 // Get matrix chunks from my slaves.
118 build_matrix A;
119 A.set_size(nloc, domain.back(), false);
120 A.ptr[0] = 0;
121
122 cons_f.resize(A.nbRow());
123 cons_x.resize(A.nbRow());
124
125 int shift = n + 1;
126 std::copy(widths.begin(), widths.end(), &A.ptr[1]);
127
128 for (int j = 0; j < group_size; ++j) {
129 int i = slaves[j];
130 cnt_req[j] = comm.doIReceive(&A.ptr[shift], counts[j], i, cnt_tag);
131
132 shift += counts[j];
133 }
134 comm.waitAll(cnt_req);
135
136 A.set_nonzeros(A.scan_row_sizes());
137
138 std::copy(Astrip.col.data(), Astrip.col.data() + Astrip.nbNonZero(), A.col.data());
139 std::copy(Astrip.val.data(), Astrip.val.data() + Astrip.nbNonZero(), A.val.data());
140
141 shift = Astrip.nbNonZero();
142 for (int j = 0, d0 = domain[comm.rank]; j < group_size; ++j) {
143 int i = slaves[j];
144
145 int nnz = A.ptr[domain[i + 1] - d0] - A.ptr[domain[i] - d0];
146
147 col_req[j] = comm.doIReceive(A.col + shift, nnz, i, col_tag);
148 val_req[j] = comm.doIReceive(A.val + shift, nnz, i, val_tag);
149
150 shift += nnz;
151 }
152
153 comm.waitAll(col_req);
154 comm.waitAll(val_req);
155
156 solver().init(AlinaCommunicator(m_masters_comm.get()), A);
157 }
158 else {
159 comm.doSend(widths.data(), n, group_master, cnt_tag);
160 comm.doSend(Astrip.col.data(), Astrip.nbNonZero(), group_master, col_tag);
161 comm.doSend(Astrip.val.data(), Astrip.nbNonZero(), group_master, val_tag);
162 }
163
164 host_v.resize(n);
165 }
166
167 template <class B>
168 void init(AlinaCommunicator comm, const DistributedMatrix<B>& A)
169 {
170 const build_matrix& A_loc = *A.local();
171 const build_matrix& A_rem = *A.remote();
172
173 build_matrix a;
174
175 a.set_size(A.loc_rows(), A.glob_cols(), false);
176 a.set_nonzeros(A_loc.nbNonZero() + A_rem.nbNonZero());
177 a.ptr[0] = 0;
178
179 for (size_t i = 0, head = 0; i < A_loc.nbRow(); ++i) {
180 ptrdiff_t shift = A.loc_col_shift();
181
182 for (ptrdiff_t j = A_loc.ptr[i], e = A_loc.ptr[i + 1]; j < e; ++j) {
183 a.col[head] = A_loc.col[j] + shift;
184 a.val[head] = A_loc.val[j];
185 ++head;
186 }
187
188 for (ptrdiff_t j = A_rem.ptr[i], e = A_rem.ptr[i + 1]; j < e; ++j) {
189 a.col[head] = A_rem.col[j];
190 a.val[head] = A_rem.val[j];
191 ++head;
192 }
193
194 a.ptr[i + 1] = head;
195 }
196
197 init(comm, a);
198 }
199
200 virtual ~DistributedDirectSolverBase()
201 {
202 }
203
204 Solver& solver()
205 {
206 return *static_cast<Solver*>(this);
207 }
208
209 const Solver& solver() const
210 {
211 return *static_cast<const Solver*>(this);
212 }
213
214 template <class VecF, class VecX>
215 void operator()(const VecF& f, VecX& x) const
216 {
217 if (!n)
218 return;
219
220 backend::copy(f, host_v);
221
222 if (comm.rank == group_master) {
223 std::copy(host_v.begin(), host_v.end(), cons_f.begin());
224
225 int shift = n, j = 0;
226 for (int i : slaves) {
227 solve_req[j] = comm.doIReceive(&cons_f[shift], counts[j], i, rhs_tag);
228 shift += counts[j++];
229 }
230
231 comm.waitAll(solve_req);
232
233 solver().solve(cons_f, cons_x);
234
235 std::copy(cons_x.begin(), cons_x.begin() + n, host_v.begin());
236 shift = n;
237 j = 0;
238
239 for (int i : slaves) {
240 solve_req[j] = comm.doISend(&cons_x[shift], counts[j], i, sol_tag);
241 shift += counts[j++];
242 }
243
244 comm.waitAll(solve_req);
245 }
246 else {
247 comm.doSend(host_v.data(), n, group_master, rhs_tag);
248 comm.doReceive(host_v.data(), n, group_master, sol_tag);
249 }
250
251 backend::copy(host_v, x);
252 }
253
254 private:
255
256 static const int cnt_tag = 5001;
257 static const int col_tag = 5002;
258 static const int val_tag = 5003;
259 static const int rhs_tag = 5004;
260 static const int sol_tag = 5005;
261
263 int n;
264 int group_master;
266 std::vector<int> slaves;
267 std::vector<int> counts;
268 mutable std::vector<rhs_type> cons_f, cons_x, host_v;
269 mutable UniqueArray<MessagePassing::Request> solve_req;
270};
271
272/*---------------------------------------------------------------------------*/
273/*---------------------------------------------------------------------------*/
274
275} // namespace Arcane::Alina
276
277/*---------------------------------------------------------------------------*/
278/*---------------------------------------------------------------------------*/
279
280#endif
void resize(size_t new_size)
Set the new size. WARNING: this method do not handle the delete of the current value.
Definition CSRMatrix.h:72
Distributed Matrix using message passing.
T & back()
Last element of the array.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
void push_back(ConstReferenceType val)
Adds the element val to the end of the array.
Reference to an instance.
1D data vector with value semantics (STL style).
Ref< IMessagePassingMng > mpSplitCommunicator(IMessagePassingMng *pm, bool keep)
Creates a sub communicator.
Definition Messages.cc:250
Convenience wrapper around MPI_Comm.
Sparse matrix stored in CSR (Compressed Sparse Row) format.
Definition CSRMatrix.h:98