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