Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
MetisWrapper.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 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/* MetisWrapper.cc (C) 2000-2024 */
9/* */
10/* Wrapper autour des appels de Parmetis. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/std/internal/MetisWrapper.h"
15
16#include "arcane/utils/CheckedConvert.h"
17#include "arcane/utils/ITraceMng.h"
18
19#include "arcane/core/IParallelMng.h"
20#include "arcane/core/ParallelMngUtils.h"
21
22#include "arcane/std/internal/MetisGraph.h"
23#include "arcane/std/internal/MetisGraphDigest.h"
24#include "arcane/std/internal/MetisGraphGather.h"
25
26#include <functional>
27
28/*---------------------------------------------------------------------------*/
29/*---------------------------------------------------------------------------*/
30
31namespace Arcane
32{
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37MetisWrapper::
38MetisWrapper(IParallelMng* pm)
39: TraceAccessor(pm->traceMng())
40, m_parallel_mng(pm)
41{
42}
43
44/*---------------------------------------------------------------------------*/
45/*---------------------------------------------------------------------------*/
46
47/*---------------------------------------------------------------------------*/
48/*---------------------------------------------------------------------------*/
52int MetisWrapper::
53_callMetisWith2Processors(const Int32 ncon, const bool need_part,
55 MetisCall& metis_call)
56{
57 Int32 nb_rank = m_parallel_mng->commSize();
58 Int32 my_rank = m_parallel_mng->commRank();
59
61
62 int comm_0_size = nb_rank / 2 + nb_rank % 2;
63 int comm_1_size = nb_rank / 2;
64 int comm_0_io_rank = 0;
66
67 for (int i = 0; i < nb_rank + 1; ++i) {
68 half_vtxdist[i] = vtxdist[i];
69 }
70
71 int color = 1;
72 int key = my_rank;
73
74 if (my_rank >= comm_0_size) {
75 color = 2;
76 for (int i = 0; i < comm_1_size + 1; ++i) {
78 }
79 }
80
82 Ref<IParallelMng> half_pm = ParallelMngUtils::createSubParallelMngRef(m_parallel_mng, color, key);
84
86
87 color = -1;
89 color = 1;
90 }
91
92 Ref<IParallelMng> metis_pm = ParallelMngUtils::createSubParallelMngRef(m_parallel_mng, color, key);
93
95 metis_vtxdist[0] = 0;
97 metis_vtxdist[2] = vtxdist[vtxdist.size() - 1];
98
99 int ierr = 0;
100
101 if (metis_pm.get()) {
104 }
105
106 // S'assure que tout le monde a la même valeur de l'erreur.
107 half_pm->broadcast(ArrayView<int>(1, &ierr), 0);
108
109 metis_gather.scatterPart(half_vtxdist, metis_graph.part, my_graph.part);
110
111 return ierr;
112}
113
114/*---------------------------------------------------------------------------*/
115/*---------------------------------------------------------------------------*/
116
117int MetisWrapper::
118callPartKway(const bool print_digest, const bool gather,
119 idx_t *vtxdist, idx_t *xadj, idx_t *adjncy, idx_t *vwgt,
121 real_t *tpwgts, real_t *ubvec, idx_t *options, idx_t *edgecut, idx_t *part)
122{
123 int ierr = 0;
124 Int32 nb_rank = m_parallel_mng->commSize();
125 Int32 my_rank = m_parallel_mng->commRank();
126
127 MetisCall partkway = [&](IParallelMng* pm, MetisGraphView graph,
129 {
130 MPI_Comm graph_comm = static_cast<MPI_Comm>(pm->communicator());
131 // NOTE GG: il peut arriver que ces deux pointeurs soient nuls s'il n'y a pas
132 // de voisins. Si tout le reste est cohérent cela ne pose pas de problèmes mais ParMetis
133 // n'aime pas les tableaux vides donc si c'est le cas on met un 0.
134 // TODO: il faudrait regarder en amont s'il ne vaudrait pas mieux mettre des valeurs
135 // dans ces deux tableaux au cas où.
136 idx_t null_idx = 0;
137 idx_t* adjncy_data = graph.adjncy.data();
138 idx_t* adjwgt_data = graph.adjwgt.data();
139 if (!adjncy_data)
141 if (!adjwgt_data)
143 return ParMETIS_V3_PartKway(graph_vtxdist.data(), graph.xadj.data(),
144 adjncy_data, graph.vwgt.data(),
146 ubvec, options, edgecut, graph.part.data(), &graph_comm);
147 };
148
149 // Version séquentielle utilisant directement Metis
150 // NOTE: Ce bout de code est le même que dans callAdaptativeRepart.
151 // Il faudrait mutualiser les deux.
154 {
158 options2[METIS_OPTION_UFACTOR] = 30; // TODO: a récupérer depuis les options de parmetis
159 options2[METIS_OPTION_NUMBERING] = 0; // 0 pour indiquer que les tableaux sont en C (indices commencent à 0)
162 options2[METIS_OPTION_SEED] = 25; // TODO: pouvoir changer la graine
163 pwarning() << "MetisWrapper: using user 'imbalance_factor' is not yet implemented. Using defaut value 30";
164
165 // Le nombre de sommets du graph est dans le premier indice de graph_vtxdist
167 return METIS_PartGraphKway(&nvtxs /*graph_vtxdist.data()*/, ncon, graph.xadj.data(),
168 graph.adjncy.data(), graph.vwgt.data(), graph.vsize.data(),
169 graph.adjwgt.data(), nparts, tpwgts,
170 ubvec, options2, edgecut, graph.part.data());
171 };
172
174
175 ArrayView<idx_t> offset(nb_rank + 1, vtxdist);
176 my_graph.nb_vertices = CheckedConvert::toInt32(offset[my_rank + 1] - offset[my_rank]);
177 my_graph.xadj = ArrayView<idx_t>(my_graph.nb_vertices + 1, xadj);
178 const Int32 adjacency_size = CheckedConvert::toInt32(my_graph.xadj[my_graph.nb_vertices]);
179 const Int32 nb_con = CheckedConvert::toInt32(*ncon);
180 my_graph.adjncy = ArrayView<idx_t>(adjacency_size, adjncy);
181 my_graph.vwgt = ArrayView<idx_t>(CheckedConvert::multiply(my_graph.nb_vertices, nb_con), vwgt);
182 my_graph.adjwgt = ArrayView<idx_t>(adjacency_size, adjwgt);
183 my_graph.part = ArrayView<idx_t>(my_graph.nb_vertices, part);
184 my_graph.have_vsize = false;
185 my_graph.have_adjwgt = true;
186
187 if (print_digest){
188 MetisGraphDigest d(m_parallel_mng);
190 ncon, nparts, tpwgts, ubvec, nullptr, options);
191 if (my_rank == 0) {
192 info() << "signature des entrees Metis = " << digest;
193 }
194 }
195
196 if (gather && nb_rank > 2) {
197 // Normalement c'est plus rapide ...
198 info() << "Partitioning metis : re-grouping " << nb_rank << " -> 2 rank";
199 ierr = _callMetisWith2Processors(nb_con, false, offset, my_graph, partkway);
200 }
201 else {
202 info() << "Partitioning metis : nb rank = " << nb_rank;
203 MetisCall& metis_call = (nb_rank == 1) ? partkway_seq : partkway;
204 ierr = metis_call(m_parallel_mng, my_graph, offset);
205 }
206
207 info() << "End Partitioning metis";
208 if (print_digest){
209 MetisGraphDigest d(m_parallel_mng);
211 if (my_rank == 0) {
212 info() << "hash for Metis output = " << digest;
213 }
214 }
215
216 return ierr;
217}
218
219/*---------------------------------------------------------------------------*/
220/*---------------------------------------------------------------------------*/
221
222int MetisWrapper::
223callAdaptiveRepart(const bool print_digest, const bool gather,
224 idx_t *vtxdist, idx_t *xadj, idx_t *adjncy, idx_t *vwgt,
225 idx_t *vsize, idx_t *adjwgt, idx_t *wgtflag, idx_t *numflag, idx_t *ncon,
227 idx_t *options, idx_t *edgecut, idx_t *part)
228{
229 int ierr = 0;
230 Int32 nb_rank = m_parallel_mng->commSize();
231 Int32 my_rank = m_parallel_mng->commRank();
232
233 MetisCall repart_func = [&](IParallelMng* pm, MetisGraphView graph,
235 {
236 MPI_Comm graph_comm = static_cast<MPI_Comm>(pm->communicator());
237 return ParMETIS_V3_AdaptiveRepart(graph_vtxdist.data(), graph.xadj.data(),
238 graph.adjncy.data(), graph.vwgt.data(),
239 graph.vsize.data(), graph.adjwgt.data(),
241 ipc2redist, options, edgecut,
242 graph.part.data(), &graph_comm);
243 };
244
245 // Version séquentielle utilisant directement Metis
246 // NOTE: Ce bout de code est le même que dans callPartKWay
247 // Il faudrait mutualiser les deux.
250 {
254 options2[METIS_OPTION_UFACTOR] = 30; // TODO: a récupérer depuis les options de parmetis
255 options2[METIS_OPTION_NUMBERING] = 0; // 0 pour indiquer que les tableaux sont en C (indices commencent à 0)
258 options2[METIS_OPTION_SEED] = 25; // TODO: pouvoir changer la graine
259 pwarning() << "MetisWrapper: using user 'imbalance_factor' is not yet implemented. Using defaut value 30";
260 // Le nombre de sommets du graph est dans le premier indice de graph_vtxdist
262 return METIS_PartGraphKway(&nvtxs /*graph_vtxdist.data()*/, ncon, graph.xadj.data(),
263 graph.adjncy.data(), graph.vwgt.data(), graph.vsize.data(),
264 graph.adjwgt.data(), nparts, tpwgts,
265 ubvec, options2, edgecut, graph.part.data());
266 };
267
269
270
271 ArrayView<idx_t> offset(nb_rank + 1, vtxdist);
272 my_graph.nb_vertices = CheckedConvert::toInt32(offset[my_rank + 1] - offset[my_rank]);
273 my_graph.xadj = ArrayView<idx_t>(my_graph.nb_vertices + 1, xadj);
274 const Int32 adjacency_size = CheckedConvert::toInt32(my_graph.xadj[my_graph.nb_vertices]);
275 const Int32 nb_con = CheckedConvert::toInt32(*ncon);
276 my_graph.adjncy = ArrayView<idx_t>(adjacency_size, adjncy);
277 my_graph.vwgt = ArrayView<idx_t>(CheckedConvert::multiply(my_graph.nb_vertices, nb_con), vwgt);
278 my_graph.vsize = ArrayView<idx_t>(my_graph.nb_vertices, vsize);
279 my_graph.adjwgt = ArrayView<idx_t>(adjacency_size, adjwgt);
280 my_graph.part = ArrayView<idx_t>(my_graph.nb_vertices, part);
281 my_graph.have_vsize = true;
282 my_graph.have_adjwgt = true;
283
284
285 if (print_digest){
286 MetisGraphDigest d(m_parallel_mng);
288 ncon, nparts, tpwgts, ubvec, nullptr, options);
289 if (my_rank == 0) {
290 info() << "signature des entrees Metis = " << digest;
291 }
292 }
293
294 if (gather && nb_rank > 2) {
295 info() << "Partionnement metis : regroupement " << nb_rank << " -> 2 processeurs";
296 ierr = _callMetisWith2Processors(nb_con, true, offset, my_graph, repart_func);
297 }
298 else {
299 info() << "Partionnement metis : nb processeurs = " << nb_rank;
300 MetisCall& metis_call = (nb_rank == 1) ? repart_seq_func : repart_func;
301 ierr = metis_call(m_parallel_mng, my_graph, offset);
302 }
303
304 if (print_digest) {
305 MetisGraphDigest d(m_parallel_mng);
307 if (my_rank == 0) {
308 info() << "signature des sorties Metis = " << digest;
309 }
310 }
311
312 return ierr;
313}
314
315/*---------------------------------------------------------------------------*/
316/*---------------------------------------------------------------------------*/
317
318} // End namespace Arcane
319
320/*---------------------------------------------------------------------------*/
321/*---------------------------------------------------------------------------*/
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual Parallel::Communicator communicator() const =0
Communicateur MPI associé à ce gestionnaire.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
Calcule une somme de contrôle globale des entrées/sorties Metis.
String computeInputDigest(const bool need_part, const int nb_options, const MetisGraphView &my_graph, const idx_t *vtxdist, const idx_t *wgtflag, const idx_t *numflag, const idx_t *ncon, const idx_t *nparts, const real_t *tpwgts, const real_t *ubvec, const real_t *ipc2redist, const idx_t *options)
String computeOutputDigest(const MetisGraphView &my_graph, const idx_t *edgecut)
Chaîne de caractères unicode.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-