Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
GraphDistributor.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/* GraphDistributor.h (C) 2000-2024 */
9/* */
10/* This file provides declaration and definition of a class used to */
11/* redistribute the graph accross another set of processors. */
12/*---------------------------------------------------------------------------*/
13#ifndef ARCANE_STD_GRAPHDISTRIBUTOR_H
14#define ARCANE_STD_GRAPHDISTRIBUTOR_H
15/*---------------------------------------------------------------------------*/
16/*---------------------------------------------------------------------------*/
17
18#include "arcane/utils/CheckedConvert.h"
19
20#include "arcane/core/IParallelTopology.h"
21#include "arcane/core/ParallelMngUtils.h"
22
23#include <memory>
24
25/*---------------------------------------------------------------------------*/
26/*---------------------------------------------------------------------------*/
27
28namespace Arcane
29{
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
43class GraphDistributor
44{
45 public:
46
47 GraphDistributor(IParallelMng* pm)
48 : m_pm_ini(pm)
49 , m_targetSize(-1)
50 , m_targetRank(-1)
51 , m_skip(true)
52 , m_contribute(false)
53 {
54 }
55
57 void initWithOneRankPerNode(bool allow_only_one_rank)
58 {
59 m_is_init = true;
60 m_targetRank = -1;
61 m_skip = true;
62 m_contribute = false;
63 if (!m_pm_ini)
64 return;
65
66 auto topo{ ParallelMngUtils::createTopologyRef(m_pm_ini) };
67 if (topo->isMasterMachine()) {
68 m_contribute = true;
69 }
70
71 Int32 machineRank = topo->machineRank();
72 Int32ConstArrayView targetRanks = topo->masterMachineRanks();
73
74 m_targetRank = targetRanks[machineRank];
75 m_targetSize = targetRanks.size();
76
77 if ((m_targetSize != m_pm_ini->commSize()) // Only useful if number of processes change.
78 && (allow_only_one_rank || m_targetSize > 1)) { // And if more than 1 node for parmetis
79 m_skip = false;
80 m_pm_sub = m_pm_ini->createSubParallelMngRef(targetRanks);
81 }
82 else { // All ranks have to work.
83 // Still making a new communicator for safety when using with third party library
84 m_contribute = true;
85 m_targetRank = m_pm_ini->commRank();
86 m_targetSize = m_pm_ini->commSize();
87 Int32UniqueArray keptRanks(m_targetSize);
88 for (int i = 0; i < m_targetSize; i++)
89 keptRanks[i] = i;
90 m_pm_sub = m_pm_ini->createSubParallelMngRef(keptRanks);
91 }
92
93 m_pm_ini->traceMng()->info() << "Running on " << m_targetSize << " nodes";
94 }
95
96 // Max is hard-coded to work on integers.
97 void initWithMaxRank(Int32 targetSize)
98 {
99 m_is_init = true;
100 m_targetSize = targetSize;
101 m_targetRank = -1;
102 m_skip = true;
103 m_contribute = false;
104
105 if (!m_pm_ini)
106 return;
107
108 if (m_pm_ini->commRank() < targetSize) { // At this time, no duplication
109 m_contribute = 1;
110 }
111 Int64 my_rank = m_pm_ini->commRank();
112 Int64 x = my_rank * m_targetSize;
113 m_targetRank = CheckedConvert::toInt32(x / m_pm_ini->commSize());
114
115 Int32UniqueArray keepProc(m_targetSize);
116 Int32 step = m_targetSize / m_pm_ini->commSize();
117 step = (step == 0) ? 1 : step;
118 for (int i = 0; i < m_targetSize; ++i) {
119 keepProc[i] = i * step;
120 }
121
122 m_pm_sub = m_pm_ini->createSubParallelMngRef(keepProc);
123
124 if (m_targetSize != m_pm_ini->commSize()) {
125 m_skip = false;
126 }
127 }
128
129 public:
130
131 Int32 size() const { return m_targetSize; }
132
133 bool contribute() const { return m_contribute; }
134
135 //< Do the redistribution pm -> newComm
136 template <typename DataT>
137 SharedArray<DataT> convert(ConstArrayView<DataT> in, Array<DataT>* pattern = nullptr,
138 bool is_indirection = false) const
139 {
140 if (!m_is_init)
141 ARCANE_FATAL("Missing initialisation");
142 if (m_skip) {
143 SharedArray<DataT> out(in);
144 if (pattern != NULL) {
145 Integer size = in.size();
146 if (is_indirection)
147 size -= 1;
148 pattern->resize(size, m_targetRank);
149 }
150 return out;
151 }
152 ConstArrayView<DataT> toSnd;
153
154 Int32 nInfos = 2;
155 if (is_indirection) {
156 toSnd = in.subView(0, in.size() - 1);
157 nInfos += 1; // need to store end of array
158 }
159 else {
160 toSnd = in;
161 }
162
163 Int32 commSize = m_pm_ini->commSize();
164 UniqueArray<Int32> sndCnt(nInfos * commSize, -1);
165 UniqueArray<Parallel::Request> req;
166 UniqueArray<Int32> n_wanted(nInfos);
167 n_wanted[0] = m_targetRank;
168 n_wanted[1] = toSnd.size();
169 if (is_indirection)
170 n_wanted[2] = static_cast<Int32>(in[in.size() - 1]);
171
172 m_pm_ini->allGather(n_wanted, sndCnt);
173
174 UniqueArray<Int32> sndNbr(commSize, 0);
175 UniqueArray<Int32> rcvNbr(commSize, 0);
176 UniqueArray<Int32> sndDsp(commSize, 0);
177 UniqueArray<Int32> rcvDsp(commSize, 0);
178
179 sndNbr[m_targetRank] = toSnd.size();
180
181 if (pattern != NULL) {
182 pattern->resize(0);
183 }
184
185 Int32 myRank = m_pm_ini->commRank();
186 Int32 begin = 0;
187 for (int i = 0; i < commSize; ++i) {
188 if (sndCnt[nInfos * i] == myRank) { // We have to receive this message
189 rcvNbr[i] = sndCnt[nInfos * i + 1];
190 rcvDsp[i] = begin;
191 begin += rcvNbr[i];
192
193 if (pattern != NULL)
194 pattern->addRange(i, rcvNbr[i]);
195 }
196 }
197 if (contribute() && is_indirection)
198 begin += 1; // Trick: add one to mark end of array
199 SharedArray<DataT> out(begin, -1);
200
201 m_pm_ini->allToAllVariable(toSnd, sndNbr, sndDsp, out, rcvNbr, rcvDsp);
202
203 if (contribute() && is_indirection) { // We have to update offsets
204 DataT offset = 0;
205 DataT* my_iter = out.data();
206 for (int i = 0; i < commSize; ++i) {
207 if (sndCnt[nInfos * i] == myRank) { // We have to receive this message
208 Int32 nRecv = sndCnt[nInfos * i + 1];
209 DataT* my_end(my_iter + nRecv);
210 for (; my_iter != my_end; ++my_iter)
211 (*my_iter) += offset;
212 offset += sndCnt[nInfos * i + 2];
213 }
214 }
215 out[out.size() - 1] = offset;
216 }
217
218 return out;
219 }
220
221 //< Do the backward redistribution newComm -> pm
222 template <typename DataT>
223 SharedArray<DataT> convertBack(ConstArrayView<DataT> in, Int32 nRecv) const
224 {
225 if (!m_is_init)
226 ARCANE_FATAL("Missing initialisation");
227 if (m_skip) {
228 SharedArray<DataT> out(in);
229 return out;
230 }
231
232 Int32 nInfos = 2;
233 Int32 commSize = m_pm_ini->commSize();
234 UniqueArray<Int32> sndCnt(nInfos * commSize, -1);
235 UniqueArray<Parallel::Request> req;
236 UniqueArray<Int32> n_wanted(nInfos);
237 n_wanted[0] = m_targetRank;
238 n_wanted[1] = nRecv;
239
240 m_pm_ini->allGather(n_wanted, sndCnt);
241
242 UniqueArray<Int32> sndNbr(commSize, 0);
243 UniqueArray<Int32> rcvNbr(commSize, 0);
244 UniqueArray<Int32> sndDsp(commSize, 0);
245 UniqueArray<Int32> rcvDsp(commSize, 0);
246
247 rcvNbr[m_targetRank] = nRecv;
248
249 Int32 myRank = m_pm_ini->commRank();
250 Int32 begin = 0;
251 for (int i = 0; i < commSize; ++i) {
252 if (sndCnt[nInfos * i] == myRank) { // We have to receive this message
253 sndNbr[i] = sndCnt[nInfos * i + 1];
254 sndDsp[i] = begin;
255 begin += sndNbr[i];
256 }
257 }
258 SharedArray<DataT> out(nRecv, -1);
259
260 m_pm_ini->allToAllVariable(in, sndNbr, sndDsp, out, rcvNbr, rcvDsp);
261
262 return out;
263 }
264
265 IParallelMng* subParallelMng() const
266 {
267 IParallelMng* pm = m_pm_sub.get();
268 if (pm)
269 return pm;
270 return m_pm_ini->sequentialParallelMng();
271 }
272
273 public:
274
275 ARCANE_DEPRECATED_REASON("Y2024: This method is internal to Arcane. Use subParallelMng()->communicator() instead")
276 MPI_Comm getCommunicator() const
277 {
278 if (!m_pm_sub)
279 return MPI_COMM_NULL;
280 Parallel::Communicator comm = m_pm_sub->communicator();
281 return (MPI_Comm)comm;
282 }
283
284 ARCANE_DEPRECATED_REASON("Y2024: This method is internal to Arcane. Use subParallelMng() instead")
285 IParallelMng* parallelManager() const
286 {
287 return m_pm_sub.get();
288 }
289
290 private:
291
292 IParallelMng* m_pm_ini = nullptr;
293 Ref<IParallelMng> m_pm_sub;
294 Int32 m_targetSize = -1; // Sub-communicator size
295 Int32 m_targetRank = -1; // Rank in the sub-communicator
296 bool m_skip = false; // No redistribution
297 bool m_contribute = false;
298 bool m_is_init = false;
299};
300
301/*---------------------------------------------------------------------------*/
302/*---------------------------------------------------------------------------*/
303
304} // End namespace Arcane
305
306/*---------------------------------------------------------------------------*/
307/*---------------------------------------------------------------------------*/
308
309#endif
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
constexpr Integer size() const noexcept
Number of elements in the array.
void initWithOneRankPerNode(bool allow_only_one_rank)
Automatic distribution : one partitioning process per node.
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual IParallelMng * sequentialParallelMng()=0
Returns a sequential parallelism manager.
virtual Int32 commSize() const =0
Number of instances in the communicator.
Ref< IParallelTopology > createTopologyRef(IParallelMng *pm)
Creates an instance containing information about the rank topology of this manager.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:482
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:341
std::int32_t Int32
Signed integer type of 32 bits.