Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
PTScotchMeshPartitioner.cc
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/* PTScotchMeshPartitioner.cc (C) 2000-2025 */
9/* */
10/* Mesh partitioner using the 'PTScotch' library. */
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#include <ptscotch.h>
49}
50
51#include "arcane/std/MeshPartitionerBase.h"
52#include "arcane/std/PTScotchMeshPartitioner_axl.h"
53#include "arcane/std/PartitionConverter.h"
54#include "arcane/std/GraphDistributor.h"
55
56// TODO: supprimer les '#define' et utiliser des tests (if)
57// to ensure everything compiles
58
59#define SCOTCH_SCALING
60// #define SCOTCH_MAPPING
61
62/*---------------------------------------------------------------------------*/
63/*---------------------------------------------------------------------------*/
64
65namespace Arcane
66{
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 if (nb_part < nb_rank)
132 throw ArgumentException(A_FUNCINFO, "partition with nb_part<nb_rank");
133
134 // initialisations pour la gestion des contraintes (sauf initUidRef)
135 initConstraints(false);
136
137 // Contient les numéros uniques des entités dans la renumérotation
138 // propre à scotch
139 VariableCellInteger cell_scotch_uid(VariableBuildInfo(mesh(), "CellsScotchUid", IVariable::PNoDump | IVariable::PNoRestore));
140
141 IntegerUniqueArray global_nb_own_cell(nb_rank);
142 CellGroup own_cells = mesh()->ownCells();
143 Integer nb_own_cell = nbOwnCellsWithConstraints(); // on tient compte des contraintes
144 pm->allGather(IntegerConstArrayView(1, &nb_own_cell), global_nb_own_cell);
145 Integer total_nb_cell = 0;
146 UniqueArray<SCOTCH_Num> scotch_vtkdist(nb_rank + 1);
147 {
148 scotch_vtkdist[0] = 0;
149 for (Integer i = 0; i < nb_rank; ++i) {
150 total_nb_cell += global_nb_own_cell[i];
151 scotch_vtkdist[i + 1] = static_cast<SCOTCH_Num>(total_nb_cell);
152 // info() << "SCOTCH VTKDIST " << (i+1) << ' ' << scotch_vtkdist[i+1];
153 }
154 }
155 //info() << "Numéro métis de la première entité du domaine: " << scotch_vtkdist[my_rank] << " totalnbcell=" << total_nb_cell;
156 // Nombre max de mailles voisine connectés aux mailles
157 // en supposant les mailles connectées uniquement par les faces
158 // Cette valeur sert à préallouer la mémoire pour la liste des mailles voisines
159 Integer nb_max_face_neighbour_cell = 0;
160 {
161 // Renumérote les mailles pour que chaque sous-domaine
162 // ait des mailles de numéro consécutifs
163 Integer mid = static_cast<Integer>(scotch_vtkdist[my_rank]);
164 ENUMERATE_CELL (i_item, own_cells) {
165 const Cell& item = *i_item;
166 if (cellUsedWithConstraints(item)) {
167 cell_scotch_uid[item] = mid;
168 ++mid;
169 }
170 nb_max_face_neighbour_cell += item.nbFace();
171 //info() << " GLOBAL_SCOTCH_NUM n=" << mid << " item=" << ItemPrinter(item);
172 }
173 cell_scotch_uid.synchronize();
174 }
175
176 _initUidRef(cell_scotch_uid);
177
178 // libération mémoire
179 cell_scotch_uid.setUsed(false);
180
181 SharedArray<SCOTCH_Num> scotch_xadj;
182 scotch_xadj.reserve(nb_own_cell + 1);
183
184 SharedArray<SCOTCH_Num> scotch_adjncy;
185 scotch_adjncy.reserve(nb_max_face_neighbour_cell);
186
187 // Construction de la connectivité entre les cellules et leurs voisines en tenant compte des contraintes
188 // (La connectivité se fait suivant les faces)
189
190 UniqueArray<float> edgeWeights;
191 edgeWeights.resize(0);
192 UniqueArray<float>* edgeWeightsPtr = &edgeWeights;
193 // if (initial_partition)
194 // edgeWeightsPtr = NULL;
195
196 Int64UniqueArray neighbour_cells;
197 ENUMERATE_CELL (i_item, own_cells) {
198 const Cell& item = *i_item;
199
200 if (!cellUsedWithConstraints(item))
201 continue;
202
203 scotch_xadj.add(scotch_adjncy.size());
204
205 getNeighbourCellsUidWithConstraints(item, neighbour_cells, edgeWeightsPtr);
206
207 for (Integer z = 0; z < neighbour_cells.size(); ++z)
208 scotch_adjncy.add(static_cast<SCOTCH_Num>(neighbour_cells[z]));
209 }
210 scotch_xadj.add(scotch_adjncy.size());
211
212 int nparts = static_cast<int>(nb_part);
213
214 // Scotch can only deal with one weight per vertex
215 SharedArray<float> cells_weights;
216
217 if (nbCellWeight() == 1) { // One criterion, we balance this criterion
218 cells_weights = cellsWeightsWithConstraints(1, true);
219 }
220 else { // We need multi-criteria partitioning, it's not available yet.
221 // So we try to balance memory !
222 cells_weights = cellsSizeWithConstraints();
223 }
224
225#ifdef SCOTCH_SCALING
226 PartitionConverter<float, SCOTCH_Num> converter(pm, (double)SCOTCH_NUMMAX / 2.0, cells_weights);
227 ArrayConverter<float, SCOTCH_Num, PartitionConverter<float, SCOTCH_Num>> scotch_vwgtConvert(cells_weights, converter);
228#else
229 ArrayConverter<float, SCOTCH_Num> scotch_vwgtConvert(cells_weights);
230#endif
231 SharedArray<SCOTCH_Num> scotch_vwgt(scotch_vwgtConvert.array().constView());
232
233#ifdef SCOTCH_SCALING
234 converter.reset();
235 if (edgeWeights.size() == 0) // Avoid NULL pointer for Scotch.
236 edgeWeights.add(0);
237 converter.computeContrib(edgeWeights);
238 ArrayConverter<float, SCOTCH_Num, PartitionConverter<float, SCOTCH_Num>> scotch_ewgtConvert(edgeWeights, converter);
239#else
240 ArrayConverter<float, SCOTCH_Num> scotch_ewgtConvert(edgeWeights);
241#endif
242
243 SharedArray<SCOTCH_Num> scotch_ewgt((UniqueArray<SCOTCH_Num>)scotch_ewgtConvert.array());
244
245 MPI_Comm scotch_mpicomm = *(MPI_Comm*)getCommunicator();
246#ifdef ARCANE_PART_DUMP
247 {
248 Integer iteration = mesh()->subDomain()->commonVariables().globalIteration();
249 StringBuilder filename("mesh-");
250 filename += iteration;
251 dumpObject(filename.toString());
252 }
253#endif // ARCANE_PART_DUMP
254
255 GraphDistributor gd(pm);
256#ifndef SCOTCH_MAPPING
257 gd.initWithOneRankPerNode(true);
258#else // SCOTCH_MAPPING
259 gd.initWithMaxRank(1);
260#endif // SCOTCH_MAPPING
261
262 // TODO: compute correct part number !
263 SharedArray<SCOTCH_Num> scotch_part;
264
265 scotch_xadj = gd.convert<SCOTCH_Num>(scotch_xadj, &scotch_part, true);
266 scotch_vwgt = gd.convert<SCOTCH_Num>(scotch_vwgt);
267 scotch_adjncy = gd.convert<SCOTCH_Num>(scotch_adjncy);
268 scotch_ewgt = gd.convert<SCOTCH_Num>(scotch_ewgt);
269 scotch_mpicomm = gd.getCommunicator();
270
271 if (gd.contribute()) {
272
273 int retval = 0;
274#ifndef SCOTCH_MAPPING
275 SCOTCH_Dgraph graph;
276 retval = SCOTCH_dgraphInit(&graph, scotch_mpicomm);
277 if (retval != 0)
278 error() << "Error in dgraphInit() r=" << retval;
279
280 info() << "Build Scotch graph";
281
282 // TODO: Remove
283 SCOTCH_randomReset(); // For debugging
284
285 retval = SCOTCH_dgraphBuild(&graph,
286 0, /* const SCOTCH_Num baseval */
287 scotch_xadj.size() - 1, /* const SCOTCH_Num vertlocnbr */
288 scotch_xadj.size() - 1, /* const SCOTCH_Num vertlocmax */
289 scotch_xadj.data(), /* const SCOTCH_Num* vertloctab */
290 0, /* const SCOTCH_Num* vendloctab */
291 scotch_vwgt.data(), /* const SCOTCH_Num* veloloctab */
292 0, /* const SCOTCH_Num* vlblocltab */
293 scotch_adjncy.size(), /* const SCOTCH_Num edgelocnbr */
294 scotch_adjncy.size(), /* const SCOTCH_Num edgelocsiz */
295 scotch_adjncy.data(), /* const SCOTCH_Num* edgeloctab */
296 0, /* const SCOTCH_Num* edgegsttab */
297 scotch_ewgt.data() /* const SCOTCH_Num* edloloctab) */
298 );
299 if (retval != 0)
300 error() << "Error in dgraphBuild() r=" << retval;
301
302 if (dumpGraph) {
303 Integer iteration = mesh()->subDomain()->commonVariables().globalIteration();
304 StringBuilder filename("graph-");
305 filename += iteration;
306 filename += "_";
307 filename += my_rank;
308
309 String name(filename.toString());
310 FILE* ofile = ::fopen(name.localstr(), "w");
311 SCOTCH_dgraphSave(&graph, ofile);
312 ::fclose(ofile);
313 }
314
315 if (checkGraph) {
316 // Vérifie que le maillage est correct
317 info() << "Check Scotch graph";
318 retval = SCOTCH_dgraphCheck(&graph);
319 if (retval != 0)
320 error() << "Error in dgraphCheck() r=" << retval;
321 }
322
323 SCOTCH_Strat strategy;
324 retval = SCOTCH_stratInit(&strategy);
325 if (retval != 0)
326 error() << "Error in SCOTCH_stratInit() r=" << retval;
327
328 if (options() && (!(options()->strategy().empty()))) {
329 char* strat = (char*)malloc(options()->strategy().length() + 1);
330 ::strncpy(strat, options()->strategy().localstr(), options()->strategy().length() + 1);
331 retval = SCOTCH_stratDgraphMap(&strategy, strat);
332 if (retval != 0)
333 error() << "Error in SCOTCH_stratDgraphMap() r=" << retval;
334 }
335
336 // Effectue la partition
337 info() << "Execute 'SCOTCH_dgraphPart'";
338 retval = SCOTCH_dgraphPart(&graph,
339 nparts, /* const SCOTCH_Num partnbr */
340 &strategy, /* const SCOTCH_Strat * straptr */
341 scotch_part.unguardedBasePointer() /* SCOTCH_Num * partloctab */
342 );
343
344 SCOTCH_stratExit(&strategy);
345 SCOTCH_dgraphExit(&graph);
346 if (retval != 0)
347 error() << "Error in dgraphPart() r=" << retval;
348#else // SCOTCH_MAPPING
349 SCOTCH_Graph graph;
350 SCOTCH_Arch architecture;
351 info() << "Build Scotch graph";
352
353 // TODO: Remove
354 SCOTCH_randomReset(); // For debugging
355
356 retval = SCOTCH_graphBuild(&graph,
357 0, /* const SCOTCH_Num baseval */
358 scotch_xadj.size() - 1, /* const SCOTCH_Num vertlocnbr */
359 scotch_xadj.unguardedBasePointer(), /* const SCOTCH_Num* vertloctab */
360 0, /* const SCOTCH_Num* vendloctab */
361 scotch_vwgt.begin(), /* const SCOTCH_Num* veloloctab */
362 0, /* const SCOTCH_Num* vlblocltab */
363 scotch_adjncy.size(), /* const SCOTCH_Num edgelocnbr */
364 scotch_adjncy.unguardedBasePointer(), /* const SCOTCH_Num* edgeloctab */
365 scotch_ewgt.begin() /* const SCOTCH_Num* edloloctab) */
366 );
367 if (retval != 0)
368 error() << "Error in graphBuild() r=" << retval;
369
370 // Build hierarchical topology view.
371 // TODO: discover topology automatically.
372 SCOTCH_Num nb_nodes;
373 int level = 3;
374 Array<SCOTCH_Num> sizetab(level);
375 Array<SCOTCH_Num> linktab(level);
376
377 nb_nodes = nb_rank / 32;
378 sizetab[0] = nb_nodes;
379 sizetab[1] = 4; // 4 sockets
380 sizetab[2] = 8; // 8 cores
381
382 retval = SCOTCH_archTleaf(&architecture, level, sizetab.unguardedBasePointer(), linktab.unguardedBasePointer());
383 if (retval != 0)
384 error() << "Error in archTleaf() r=" << retval;
385
386 SCOTCH_Strat strategy;
387 retval = SCOTCH_stratInit(&strategy);
388 if (retval != 0)
389 error() << "Error in SCOTCH_stratInit() r=" << retval;
390
391 retval = SCOTCH_graphMap(&graph, &architecture, &strategy, scotch_part.unguardedBasePointer());
392
393#endif // SCOTCH_MAPPING
394
395 info() << "PART retval=" << retval;
396
397#if 0
398 {
399 String filename("s_part");
400 filename += my_rank;
401 FILE* ofile = ::fopen(filename.localstr(),"w");
402 for (Array<SCOTCH_Num>::const_iterator val(scotch_part.begin()) ; val != scotch_part.end() ; val++)
403 ::fprintf(ofile, "%d\n", *val);
404 info() << "GRAPH SAVED in '" << filename << "'";
405 ::fclose(ofile);
406 }
407#endif
408
409 } // if gd.contribute()
410 scotch_part = gd.convertBack<SCOTCH_Num>(scotch_part, nb_own_cell);
411#if 0
412 {
413 String filename("scotch_part");
414 filename += my_rank;
415 FILE* ofile = ::fopen(filename.localstr(),"w");
416 for (Array<SCOTCH_Num>::const_iterator val(scotch_part.begin()) ; val != scotch_part.end() ; val++)
417 ::fprintf(ofile, "%d\n", *val);
418 info() << "GRAPH SAVED in '" << filename << "'";
419 ::fclose(ofile);
420 }
421#endif
422
423 VariableItemInt32& cells_new_owner = mesh()->toPrimaryMesh()->itemsNewOwner(IK_Cell);
424 {
425 Integer index = 0;
426 // Integer nb_new_owner = 0;
427 ENUMERATE_CELL (i_item, own_cells) {
428 const Cell& item = *i_item;
429 if (!cellUsedWithConstraints(item))
430 continue;
431
432 auto new_owner = static_cast<Int32>(scotch_part[index]);
433 ++index;
434 changeCellOwner(item, cells_new_owner, new_owner);
435 }
436 }
437
438 // libération des tableau temporaires
439 freeConstraints();
440
441 cells_new_owner.synchronize();
443}
444
445/*---------------------------------------------------------------------------*/
446/*---------------------------------------------------------------------------*/
447
449 ServiceProperty("PTScotch", ST_SubDomain),
452
453ARCANE_REGISTER_SERVICE_PTSCOTCHMESHPARTITIONER(PTScotch, PTScotchMeshPartitioner);
454
455#if ARCANE_DEFAULT_PARTITIONER == PTSCOTCH_DEFAULT_PARTITIONER
457 ServiceProperty("DefaultPartitioner", ST_SubDomain),
460ARCANE_REGISTER_SERVICE_PTSCOTCHMESHPARTITIONER(DefaultPartitioner, PTScotchMeshPartitioner);
461#endif
462
463/*---------------------------------------------------------------------------*/
464/*---------------------------------------------------------------------------*/
465
466} // End namespace Arcane
467
468/*---------------------------------------------------------------------------*/
469/*---------------------------------------------------------------------------*/
Types and macros for iterating over mesh entities.
#define ENUMERATE_CELL(name, group)
Generic enumerator for a cell group.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
ArcanePTScotchMeshPartitionerObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
CaseOptionsPTScotchMeshPartitioner * options() const
Options du jeu de données du service.
void add(ConstReferenceType val)
Adds element val to the end of the array.
ArrayIterator< const_pointer > const_iterator
Type of the constant iterator over an array element.
virtual CellGroup ownCells()=0
Group of all domain-specific cells.
Interface of a mesh partitioner.
Interface of a mesh partitioner.
virtual IMesh * mesh() const =0
Mesh associated with the partitioner.
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual Int32 commSize() const =0
Number of instances in the communicator.
virtual void allGather(ConstArrayView< char > send_buf, ArrayView< char > recv_buf)=0
Performs an all-gather operation across all processors. This is a collective operation....
@ PNoRestore
Indicates that the variable should not be restored.
Definition IVariable.h:120
@ PNoDump
Indicates that the variable should not be saved.
Definition IVariable.h:62
virtual void dumpObject(String filename="toto")
Dumps the partitioning information to disk.
virtual void changeOwnersFromCells()
Positions the new owners of nodes, edges and faces based on the cells.
Mesh partitioner using the PtScotch library.
void build() override
Build-level construction of the service.
void partitionMesh(bool initial_partition) override
Structure containing the information to create a service.
Service creation properties.
TraceMessage info() const
Flow for an information message.
TraceMessage error() const
Flow for an error message.
ItemGroupT< Cell > CellGroup
Group of cells.
Definition ItemTypes.h:184
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
MeshVariableScalarRefT< Cell, Integer > VariableCellInteger
Quantity at the cell center of integer type.
ItemVariableScalarRefT< Int32 > VariableItemInt32
32-bit integer type quantity
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
Int32 Integer
Type representing an integer.
@ ST_SubDomain
The service is used at the subdomain level.
@ IK_Cell
Cell mesh entity.
UniqueArray< Integer > IntegerUniqueArray
Dynamic 1D array of integers.
Definition UtilsTypes.h:347
ConstArrayView< Integer > IntegerConstArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:486
@ Cell
The mesh is AMR by cell.
Definition MeshKind.h:53
std::int32_t Int32
Signed integer type of 32 bits.