Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
PTScotchMeshPartitioner.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2022 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/* PTScotchMeshPartitioner.cc (C) 2000-2020 */
9/* */
10/* Partitioneur de maillage utilisant la bibliothèque 'PTScotch'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/StringBuilder.h"
15#include "arcane/utils/Iostream.h"
16
17#include "arcane/utils/PlatformUtils.h"
18#include "arcane/utils/Convert.h"
19#include "arcane/utils/Array.h"
20#include "arcane/utils/Iostream.h"
21#include "arcane/utils/ScopedPtr.h"
22#include "arcane/utils/NotImplementedException.h"
23#include "arcane/utils/ArgumentException.h"
24
25#include "arcane/ISubDomain.h"
26#include "arcane/IParallelMng.h"
27#include "arcane/ItemEnumerator.h"
28#include "arcane/IMeshSubMeshTransition.h"
29#include "arcane/ItemGroup.h"
30#include "arcane/Service.h"
31#include "arcane/Timer.h"
32#include "arcane/FactoryService.h"
33#include "arcane/ItemPrinter.h"
34#include "arcane/IItemFamily.h"
35#include "arcane/MeshVariable.h"
36#include "arcane/VariableBuildInfo.h"
37#include "arcane/CommonVariables.h"
38
39#include <stdint.h>
40
41// Au cas où on utilise mpich ou openmpi
42#define MPICH_SKIP_MPICXX
43#define OMPI_SKIP_MPICXX
44#include <mpi.h>
45extern "C"
46{
47#include <ptscotch.h>
48}
49
50#include "arcane/std/MeshPartitionerBase.h"
51#include "arcane/std/PTScotchMeshPartitioner_axl.h"
52#include "arcane/std/PartitionConverter.h"
53#include "arcane/std/GraphDistributor.h"
54
55// TODO: supprimer les '#define' et utiliser des tests (if)
56// pour être sur que tout est compilé
57
58#define SCOTCH_SCALING
59// #define SCOTCH_MAPPING
60
61/*---------------------------------------------------------------------------*/
62/*---------------------------------------------------------------------------*/
63
64namespace Arcane
65{
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
74{
75 public:
76
78
79 public:
80
81 void build() override {}
82
83 public:
84
85 void partitionMesh(bool initial_partition) override;
86 void partitionMesh(bool initial_partition,Int32 nb_part) override;
87};
88
89/*---------------------------------------------------------------------------*/
90/*---------------------------------------------------------------------------*/
91
92PTScotchMeshPartitioner::
93PTScotchMeshPartitioner(const ServiceBuildInfo& sbi)
94: ArcanePTScotchMeshPartitionerObject(sbi)
95{
96}
97
98/*---------------------------------------------------------------------------*/
99/*---------------------------------------------------------------------------*/
100
107
108/*---------------------------------------------------------------------------*/
109/*---------------------------------------------------------------------------*/
110
113{
114 ARCANE_UNUSED(initial_partition);
115
116 info() << "Load balancing with PTScotch\n";
117
118 IParallelMng* pm = mesh()->parallelMng();
119 Int32 my_rank = pm->commRank();
120 Int32 nb_rank = pm->commSize();
121
122 bool dumpGraph = false;
123 bool checkGraph = false;
124 if (options()) {
125 dumpGraph = options()->dumpGraph();
126 checkGraph = options()->checkGraph();
127 }
128
129
130 if (nb_part<nb_rank)
131 throw ArgumentException(A_FUNCINFO,"partition with nb_part<nb_rank");
132
133 // initialisations pour la gestion des contraintes (sauf initUidRef)
134 initConstraints(false);
135
136 // Contient les numéros uniques des entités dans la renumérotation
137 // propre à scotch
138 VariableCellInteger cell_scotch_uid(VariableBuildInfo(mesh(),"CellsScotchUid",IVariable::PNoDump|IVariable::PNoRestore));
139
140 IntegerUniqueArray global_nb_own_cell(nb_rank);
141 CellGroup own_cells = mesh()->ownCells();
142 Integer nb_own_cell = nbOwnCellsWithConstraints(); // on tient compte des contraintes
143 pm->allGather(IntegerConstArrayView(1,&nb_own_cell),global_nb_own_cell);
144 Integer total_nb_cell = 0;
145 UniqueArray<SCOTCH_Num> scotch_vtkdist(nb_rank+1);
146 {
147 scotch_vtkdist[0] = 0;
148 for( Integer i=0; i<nb_rank; ++i ){
149 total_nb_cell += global_nb_own_cell[i];
150 scotch_vtkdist[i+1] = static_cast<SCOTCH_Num>(total_nb_cell);
151 // info() << "SCOTCH VTKDIST " << (i+1) << ' ' << scotch_vtkdist[i+1];
152 }
153 }
154 //info() << "Numéro métis de la première entité du domaine: " << scotch_vtkdist[my_rank] << " totalnbcell=" << total_nb_cell;
155 // Nombre max de mailles voisine connectés aux mailles
156 // en supposant les mailles connectées uniquement par les faces
157 // Cette valeur sert à préallouer la mémoire pour la liste des mailles voisines
158 Integer nb_max_face_neighbour_cell = 0;
159 {
160 // Renumérote les mailles pour que chaque sous-domaine
161 // ait des mailles de numéro consécutifs
162 Integer mid = static_cast<Integer>(scotch_vtkdist[my_rank]);
163 ENUMERATE_CELL(i_item,own_cells){
164 const Cell& item = *i_item;
165 if (cellUsedWithConstraints(item)){
166 cell_scotch_uid[item] = mid;
167 ++mid;
168 }
169 nb_max_face_neighbour_cell += item.nbFace();
170 //info() << " GLOBAL_SCOTCH_NUM n=" << mid << " item=" << ItemPrinter(item);
171 }
172 cell_scotch_uid.synchronize();
173 }
174
175 _initUidRef(cell_scotch_uid);
176
177 // libération mémoire
178 cell_scotch_uid.setUsed(false);
179
180 SharedArray<SCOTCH_Num> scotch_xadj;
181 scotch_xadj.reserve(nb_own_cell+1);
182
183 SharedArray<SCOTCH_Num> scotch_adjncy;
184 scotch_adjncy.reserve(nb_max_face_neighbour_cell);
185
186 // Construction de la connectivité entre les cellules et leurs voisines en tenant compte des contraintes
187 // (La connectivité se fait suivant les faces)
188
189 UniqueArray<float> edgeWeights;
190 edgeWeights.resize(0);
191 UniqueArray<float>* edgeWeightsPtr = &edgeWeights;
192// if (initial_partition)
193// edgeWeightsPtr = NULL;
194
195 Int64UniqueArray neighbour_cells;
196 ENUMERATE_CELL(i_item,own_cells){
197 const Cell& item = *i_item;
198
199 if (!cellUsedWithConstraints(item))
200 continue;
201
202 scotch_xadj.add(scotch_adjncy.size());
203
204 getNeighbourCellsUidWithConstraints(item, neighbour_cells, edgeWeightsPtr);
205
206 for( Integer z=0; z<neighbour_cells.size(); ++z )
207 scotch_adjncy.add(static_cast<SCOTCH_Num>(neighbour_cells[z]));
208 }
209 scotch_xadj.add(scotch_adjncy.size());
210
211 int nparts = static_cast<int>(nb_part);
212
213 // Scotch can only deal with one weight per vertex
214 SharedArray<float> cells_weights;
215
216 if (nbCellWeight() == 1) { // One criterion, we balance this criterion
217 cells_weights = cellsWeightsWithConstraints(1, true);
218 }
219 else { // We need multi-criteria partitioning, it's not available yet.
220 // So we try to balance memory !
221 cells_weights = cellsSizeWithConstraints();
222 }
223
224#ifdef SCOTCH_SCALING
225 PartitionConverter<float,SCOTCH_Num> converter(pm, (double)SCOTCH_NUMMAX / 2.0, cells_weights);
226 ArrayConverter<float,SCOTCH_Num,PartitionConverter<float,SCOTCH_Num> > scotch_vwgtConvert(cells_weights, converter);
227#else
228 ArrayConverter<float,SCOTCH_Num> scotch_vwgtConvert(cells_weights);
229#endif
230 SharedArray<SCOTCH_Num> scotch_vwgt(scotch_vwgtConvert.array().constView());
231
232
233
234#ifdef SCOTCH_SCALING
235 converter.reset();
236 if (edgeWeights.size() == 0) // Avoid NULL pointer for Scotch.
237 edgeWeights.add(0);
238 converter.computeContrib(edgeWeights);
239 ArrayConverter<float,SCOTCH_Num,PartitionConverter<float,SCOTCH_Num> > scotch_ewgtConvert(edgeWeights, converter);
240#else
241 ArrayConverter<float,SCOTCH_Num> scotch_ewgtConvert(edgeWeights);
242#endif
243
244 SharedArray<SCOTCH_Num> scotch_ewgt((UniqueArray<SCOTCH_Num>)scotch_ewgtConvert.array());
245
246
247 MPI_Comm scotch_mpicomm = *(MPI_Comm*)getCommunicator();
248#ifdef ARCANE_PART_DUMP
249 {
250 Integer iteration = mesh()->subDomain()->commonVariables().globalIteration();
251 StringBuilder filename("mesh-");
252 filename += iteration;
253 dumpObject(filename.toString());
254 }
255#endif // ARCANE_PART_DUMP
256
257
258 GraphDistributor gd(pm);
259#ifndef SCOTCH_MAPPING
260 gd.initWithOneRankPerNode(true);
261#else // SCOTCH_MAPPING
262 gd.initWithMaxRank(1);
263#endif // SCOTCH_MAPPING
264
265 // TODO: compute correct part number !
266 SharedArray<SCOTCH_Num> scotch_part;
267
268 scotch_xadj = gd.convert<SCOTCH_Num>(scotch_xadj, &scotch_part, true);
269 scotch_vwgt = gd.convert<SCOTCH_Num>(scotch_vwgt);
270 scotch_adjncy = gd.convert<SCOTCH_Num>(scotch_adjncy);
271 scotch_ewgt = gd.convert<SCOTCH_Num>(scotch_ewgt);
272 scotch_mpicomm = gd.getCommunicator();
273
274 if (gd.contribute()) {
275
276 int retval = 0;
277#ifndef SCOTCH_MAPPING
278 SCOTCH_Dgraph graph;
279 retval = SCOTCH_dgraphInit(&graph,scotch_mpicomm);
280 if (retval!=0)
281 error() << "Error in dgraphInit() r=" << retval;
282
283 info() << "Build Scotch graph";
284
285 // TODO: Remove
286 SCOTCH_randomReset(); // For debugging
287
288 retval = SCOTCH_dgraphBuild(&graph,
289 0, /* const SCOTCH_Num baseval */
290 scotch_xadj.size()-1, /* const SCOTCH_Num vertlocnbr */
291 scotch_xadj.size()-1, /* const SCOTCH_Num vertlocmax */
292 scotch_xadj.data(), /* const SCOTCH_Num* vertloctab */
293 0, /* const SCOTCH_Num* vendloctab */
294 scotch_vwgt.data(), /* const SCOTCH_Num* veloloctab */
295 0, /* const SCOTCH_Num* vlblocltab */
296 scotch_adjncy.size(), /* const SCOTCH_Num edgelocnbr */
297 scotch_adjncy.size(), /* const SCOTCH_Num edgelocsiz */
298 scotch_adjncy.data(), /* const SCOTCH_Num* edgeloctab */
299 0, /* const SCOTCH_Num* edgegsttab */
300 scotch_ewgt.data() /* const SCOTCH_Num* edloloctab) */
301 );
302 if (retval!=0)
303 error() << "Error in dgraphBuild() r=" << retval;
304
305 if (dumpGraph) {
306 Integer iteration = mesh()->subDomain()->commonVariables().globalIteration();
307 StringBuilder filename("graph-");
308 filename += iteration;
309 filename += "_";
310 filename += my_rank;
311
312 String name(filename.toString());
313 FILE* ofile = ::fopen(name.localstr(),"w");
314 SCOTCH_dgraphSave(&graph,ofile);
315 ::fclose(ofile);
316 }
317
318
319 if (checkGraph) {
320 // Vérifie que le maillage est correct
321 info() << "Check Scotch graph";
322 retval = SCOTCH_dgraphCheck(&graph);
323 if (retval!=0)
324 error() << "Error in dgraphCheck() r=" << retval;
325 }
326
327 SCOTCH_Strat strategy;
328 retval = SCOTCH_stratInit(&strategy);
329 if (retval!=0)
330 error() << "Error in SCOTCH_stratInit() r=" << retval;
331
332 if (options() && (!(options()->strategy().empty()))) {
333 char* strat = (char*)malloc(options()->strategy().length()+1);
334 ::strncpy(strat, options()->strategy().localstr(), options()->strategy().length()+1);
335 retval = SCOTCH_stratDgraphMap(&strategy, strat);
336 if (retval!=0)
337 error() << "Error in SCOTCH_stratDgraphMap() r=" << retval;
338 }
339
340 // Effectue la partition
341 info() << "Execute 'SCOTCH_dgraphPart'";
342 retval = SCOTCH_dgraphPart(&graph,
343 nparts, /* const SCOTCH_Num partnbr */
344 &strategy, /* const SCOTCH_Strat * straptr */
345 scotch_part.unguardedBasePointer() /* SCOTCH_Num * partloctab */
346 );
347
348 SCOTCH_stratExit(&strategy);
349 SCOTCH_dgraphExit(&graph);
350 if (retval!=0)
351 error() << "Error in dgraphPart() r=" << retval;
352#else // SCOTCH_MAPPING
353 SCOTCH_Graph graph;
354 SCOTCH_Arch architecture;
355 info() << "Build Scotch graph";
356
357 // TODO: Remove
358 SCOTCH_randomReset(); // For debugging
359
360 retval = SCOTCH_graphBuild(&graph,
361 0, /* const SCOTCH_Num baseval */
362 scotch_xadj.size()-1, /* const SCOTCH_Num vertlocnbr */
363 scotch_xadj.unguardedBasePointer(), /* const SCOTCH_Num* vertloctab */
364 0, /* const SCOTCH_Num* vendloctab */
365 scotch_vwgt.begin(), /* const SCOTCH_Num* veloloctab */
366 0, /* const SCOTCH_Num* vlblocltab */
367 scotch_adjncy.size(), /* const SCOTCH_Num edgelocnbr */
368 scotch_adjncy.unguardedBasePointer(), /* const SCOTCH_Num* edgeloctab */
369 scotch_ewgt.begin() /* const SCOTCH_Num* edloloctab) */
370 );
371 if (retval!=0)
372 error() << "Error in graphBuild() r=" << retval;
373
374 // Build hierarchical topology view.
375 // TODO: discover topology automatically.
376 SCOTCH_Num nb_nodes;
377 int level = 3;
378 Array<SCOTCH_Num> sizetab(level);
379 Array<SCOTCH_Num> linktab(level);
380
381 nb_nodes = nb_rank/32;
382 sizetab[0] = nb_nodes;
383 sizetab[1] = 4; // 4 sockets
384 sizetab[2] = 8; // 8 cores
385
386 retval =SCOTCH_archTleaf(&architecture, level, sizetab.unguardedBasePointer(), linktab.unguardedBasePointer());
387 if (retval!=0)
388 error() << "Error in archTleaf() r=" << retval;
389
390 SCOTCH_Strat strategy;
391 retval = SCOTCH_stratInit(&strategy);
392 if (retval!=0)
393 error() << "Error in SCOTCH_stratInit() r=" << retval;
394
395 retval = SCOTCH_graphMap(&graph, &architecture, &strategy, scotch_part.unguardedBasePointer());
396
397
398#endif // SCOTCH_MAPPING
399
400
401 info() << "PART retval=" << retval;
402
403
404#if 0
405 {
406 String filename("s_part");
407 filename += my_rank;
408 FILE* ofile = ::fopen(filename.localstr(),"w");
409 for (Array<SCOTCH_Num>::const_iterator val(scotch_part.begin()) ; val != scotch_part.end() ; val++)
410 ::fprintf(ofile, "%d\n", *val);
411 info() << "GRAPH SAVED in '" << filename << "'";
412 ::fclose(ofile);
413 }
414#endif
415
416 } // if gd.contribute()
417 scotch_part = gd.convertBack<SCOTCH_Num>(scotch_part, nb_own_cell);
418#if 0
419 {
420 String filename("scotch_part");
421 filename += my_rank;
422 FILE* ofile = ::fopen(filename.localstr(),"w");
423 for (Array<SCOTCH_Num>::const_iterator val(scotch_part.begin()) ; val != scotch_part.end() ; val++)
424 ::fprintf(ofile, "%d\n", *val);
425 info() << "GRAPH SAVED in '" << filename << "'";
426 ::fclose(ofile);
427 }
428#endif
429
430 VariableItemInt32& cells_new_owner = mesh()->toPrimaryMesh()->itemsNewOwner(IK_Cell);
431 {
432 Integer index = 0;
433// Integer nb_new_owner = 0;
434 ENUMERATE_CELL(i_item,own_cells){
435 const Cell& item = *i_item;
436 if (!cellUsedWithConstraints(item))
437 continue;
438
439 auto new_owner = static_cast<Int32>(scotch_part[index]);
440 ++index;
441 changeCellOwner(item, cells_new_owner, new_owner);
442 }
443 }
444
445
446 // libération des tableau temporaires
447 freeConstraints();
448
449 cells_new_owner.synchronize();
451}
452
453/*---------------------------------------------------------------------------*/
454/*---------------------------------------------------------------------------*/
455
456ARCANE_REGISTER_SERVICE(PTScotchMeshPartitioner,
457 ServiceProperty("PTScotch",ST_SubDomain),
458 ARCANE_SERVICE_INTERFACE(IMeshPartitioner),
459 ARCANE_SERVICE_INTERFACE(IMeshPartitionerBase));
460
461ARCANE_REGISTER_SERVICE_PTSCOTCHMESHPARTITIONER(PTScotch,PTScotchMeshPartitioner);
462
463#if ARCANE_DEFAULT_PARTITIONER == PTSCOTCH_DEFAULT_PARTITIONER
464ARCANE_REGISTER_SERVICE(PTScotchMeshPartitioner,
465 ServiceProperty("DefaultPartitioner",ST_SubDomain),
466 ARCANE_SERVICE_INTERFACE(IMeshPartitioner),
467 ARCANE_SERVICE_INTERFACE(IMeshPartitionerBase));
468ARCANE_REGISTER_SERVICE_PTSCOTCHMESHPARTITIONER(DefaultPartitioner,PTScotchMeshPartitioner);
469#endif
470
471/*---------------------------------------------------------------------------*/
472/*---------------------------------------------------------------------------*/
473
474} // End namespace Arcane
475
476/*---------------------------------------------------------------------------*/
477/*---------------------------------------------------------------------------*/
#define ENUMERATE_CELL(name, group)
Enumérateur générique d'un groupe de mailles.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Generation de la classe de base du Service.
CaseOptionsPTScotchMeshPartitioner * options() const
Options du jeu de données du service.
virtual CellGroup ownCells()=0
Groupe de toutes les mailles propres au domaine.
virtual IParallelMng * parallelMng()=0
Gestionnaire de parallèlisme.
virtual ISubDomain * subDomain()=0
Sous-domaine associé
virtual IPrimaryMesh * toPrimaryMesh()=0
Retourne l'instance sous la forme d'un IPrimaryMesh.
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual Int32 commRank() const =0
Rang de cette instance dans le communicateur.
virtual Int32 commSize() const =0
Nombre d'instance dans le communicateur.
virtual void allGather(ConstArrayView< char > send_buf, ArrayView< char > recv_buf)=0
Effectue un regroupement sur tous les processeurs. Il s'agit d'une opération collective....
@ PNoRestore
Indique que la variable ne doit pas être restaurée.
Definition IVariable.h:133
@ PNoDump
Indique que la variable ne doit pas être sauvegardée.
Definition IVariable.h:72
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
IMesh * mesh() const
Maillage associé au partitionneur.
virtual void dumpObject(String filename="toto")
Dump les informations de repartitionnement sur le disque.
virtual void changeOwnersFromCells()
Positionne les nouveaux propriétaires des noeuds, arêtes et faces à partir des mailles.
Partitionneur de maillage utilisant la bibliothèque PtScotch.
void build() override
Construction de niveau build du service.
void partitionMesh(bool initial_partition) override
Structure contenant les informations pour créer un service.
TraceMessage error() const
Flot pour un message d'erreur.
TraceMessage info() const
Flot pour un message d'information.
ItemGroupT< Cell > CellGroup
Groupe de mailles.
Definition ItemTypes.h:183
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
MeshVariableScalarRefT< Cell, Integer > VariableCellInteger
Grandeur au centre des mailles de type entier.
ItemVariableScalarRefT< Int32 > VariableItemInt32
Grandeur de type entier 32 bits.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
UniqueArray< Int64 > Int64UniqueArray
Tableau dynamique à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:513
@ ST_SubDomain
Le service s'utilise au niveau du sous-domaine.
@ IK_Cell
Entité de maillage de genre maille.
UniqueArray< Integer > IntegerUniqueArray
Tableau dynamique à une dimension d'entiers.
Definition UtilsTypes.h:519
ConstArrayView< Integer > IntegerConstArrayView
Equivalent C d'un tableau à une dimension d'entiers.
Definition UtilsTypes.h:644
Int32 Integer
Type représentant un entier.