Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
SimpleGridMeshPartitioner.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/* SimpleGridMeshPartitioner.cc (C) 2000-2022 */
9/* */
10/* Partitionneur de maillage sur une grille. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/NotSupportedException.h"
15#include "arcane/utils/PlatformUtils.h"
16#include "arcane/utils/ScopedPtr.h"
17
18#include "arcane/IGridMeshPartitioner.h"
19#include "arcane/BasicService.h"
20#include "arcane/IPrimaryMesh.h"
21#include "arcane/ServiceFactory.h"
22#include "arcane/IParallelMng.h"
23#include "arcane/ItemPrinter.h"
24#include "arcane/IExtraGhostCellsBuilder.h"
25
26#include "arcane/IMeshPartitionConstraintMng.h"
27#include "arcane/IMeshUtilities.h"
28#include "arcane/IMeshModifier.h"
29#include "arcane/IItemFamily.h"
30
31#include <array>
32#include <map>
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37namespace Arcane
38{
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
46: public BasicService
48{
49 public:
50
63 {
64 public:
65
66 explicit GhostCellsBuilder(IMesh* mesh)
67 : m_mesh(mesh)
68 {}
69
70 public:
71
73 {
74 for (const auto& v : m_ghost_cell_uids) {
75 Int32 rank = v.first;
76 Int32 nb_ghost = v.second.size();
77 UniqueArray<Int32>& local_ids = m_ghost_cell_local_ids[rank];
78 local_ids.resize(nb_ghost);
79 m_mesh->cellFamily()->itemsUniqueIdToLocalId(local_ids, v.second);
80 }
81 }
82
83 Int32ConstArrayView extraCellsToSend(Int32 rank) const override
84 {
85 auto x = m_ghost_cell_local_ids.find(rank);
86 if (x == m_ghost_cell_local_ids.end())
87 return {};
88 return x->second;
89 }
90
91 std::map<Int32, UniqueArray<ItemUniqueId>> m_ghost_cell_uids;
92 std::map<Int32, UniqueArray<Int32>> m_ghost_cell_local_ids;
93 IMesh* m_mesh;
94 };
95
97 {
98 public:
99
100 UniqueArray<UniqueArray<Real>> m_grid_coord;
101 Int32 m_nb_direction = 0;
102 Int32 m_offset_y = 0;
103 Int32 m_offset_z = 0;
104 };
105
106 public:
107
109
110 public:
111
112 void build() override {}
113 IPrimaryMesh* primaryMesh() override { return mesh()->toPrimaryMesh(); }
114 void partitionMesh(bool initial_partition) override;
115 void notifyEndPartition() override {}
116
117 public:
118
120 {
121 m_min_box = min_val;
122 m_max_box = max_val;
123 m_is_bounding_box_set = true;
124 }
125
126 void setPartIndex(Int32 i, Int32 j, Int32 k) override
127 {
128 m_ijk_part[0] = i;
129 m_ijk_part[1] = j;
130 m_ijk_part[2] = k;
131 m_is_ijk_set = true;
132 }
133
134 void applyMeshPartitioning(IMesh* mesh) override;
135
136 private:
137
138 Real3 m_min_box;
139 Real3 m_max_box;
140 std::array<Int32, 3> m_ijk_part;
141 bool m_is_bounding_box_set = false;
142 bool m_is_ijk_set = false;
143 bool m_is_verbose = false;
144 GhostCellsBuilder* m_ghost_cells_builder = nullptr;
145 ScopedPtrT<GridInfo> m_grid_info;
146
147 private:
148
149 Int32 _findPart(RealConstArrayView coords, Real center);
150 void _addGhostCell(Int32 rank, Cell cell);
151 void _buildGridInfo();
152 void _computeSpecificGhostLayer();
153 void _addCellToIntersectedParts(Cell cell, std::array<Int32, 3> min_part, std::array<Int32, 3> nb_part);
154};
155
156/*---------------------------------------------------------------------------*/
157/*---------------------------------------------------------------------------*/
158
159SimpleGridMeshPartitioner::
160SimpleGridMeshPartitioner(const ServiceBuildInfo& sbi)
161: BasicService(sbi)
162{
163 if (platform::getEnvironmentVariable("ARCANE_DEBUG_SIMPLE_GRID_MESH_PARTITIONER") == "1")
164 m_is_verbose = true;
165}
166
167/*---------------------------------------------------------------------------*/
168/*---------------------------------------------------------------------------*/
177_findPart(RealConstArrayView coords, Real position)
178{
179 const Int32 nb_value = coords.size();
180 if (position < coords[0])
181 return 0;
182 if (math::isNearlyEqual(position,coords[0]))
183 return 0;
184 Int32 part_id = -1;
185 for (Int32 z = 0; z < nb_value; ++z) {
186 if (m_is_verbose)
187 info() << " z=" << z << " coord=" << coords[z] << " part=" << part_id;
188 if (position > coords[z])
189 part_id = z;
190 else
191 break;
192 }
193
194 if (part_id == (-1))
195 part_id = (nb_value - 1);
196
197 return part_id;
198}
199
200/*---------------------------------------------------------------------------*/
201/*---------------------------------------------------------------------------*/
202
203void SimpleGridMeshPartitioner::
204_addGhostCell(Int32 rank, Cell cell)
205{
206 m_ghost_cells_builder->m_ghost_cell_uids[rank].add(cell.uniqueId());
207}
208
209/*---------------------------------------------------------------------------*/
210/*---------------------------------------------------------------------------*/
211
212void SimpleGridMeshPartitioner::
213_buildGridInfo()
214{
215 m_grid_info = new GridInfo();
216
217 if (!m_is_bounding_box_set)
218 ARCANE_FATAL("Bounding box is not set. call method setBoundingBox() before");
219 if (!m_is_ijk_set)
220 ARCANE_FATAL("Part index is not set. call method setPartIndex() before");
221
222 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
223 const Int32 dimension = mesh->dimension();
224
225 IParallelMng* pm = mesh->parallelMng();
226 //Int32 nb_rank = pm->commSize();
227
228 // Calcule le nombre de parties par direction
229 std::array<Int32, 3> nb_part_by_direction_buf = { 0, 0, 0 };
230 ArrayView<Int32> nb_part_by_direction(3, nb_part_by_direction_buf.data());
231 for (Integer i = 0; i < 3; ++i)
232 if (m_ijk_part[i] >= 0)
233 nb_part_by_direction[i] = m_ijk_part[i] + 1;
234
235 auto& nb_direction = m_grid_info->m_nb_direction;
236
237 nb_direction = 0;
238 if (nb_part_by_direction[2] > 0)
239 nb_direction = 3;
240 else if (nb_part_by_direction[1] > 0)
241 nb_direction = 2;
242 else
243 ARCANE_THROW(NotImplementedException, "SimpleGridMeshPartitioner for 1D mesh");
244
245 if (nb_direction != dimension)
246 ARCANE_FATAL("Invalid number of direction: mesh_dimension={0} nb_direction={1}", dimension, nb_direction);
247
248 pm->reduce(Parallel::ReduceMax, nb_part_by_direction);
249 info() << "NB_DIRECTION=" << nb_direction << " NB_PART=" << nb_part_by_direction;
250
251 // Calcul les coordonnées de la grille par direction
252 const Real min_value = -FloatInfo<Real>::maxValue();
253 auto& grid_coord = m_grid_info->m_grid_coord;
254
255 grid_coord.resize(nb_direction);
256 grid_coord.resize(nb_direction);
257 for (Integer i = 0; i < nb_direction; ++i)
258 grid_coord[i].resize(nb_part_by_direction[i], min_value);
259
260 for (Integer i = 0; i < nb_direction; ++i) {
261 Int32 index = m_ijk_part[i];
262 if (index >= 0)
263 grid_coord[i][index] = m_min_box[i];
264 }
265 for (Integer i = 0; i < nb_direction; ++i) {
266 pm->reduce(Parallel::ReduceMax, grid_coord[i]);
267 info() << "GRID_COORD dir=" << i << " V=" << grid_coord[i];
268 }
269
270 // Vérifie que chaque direction de la grille est croissante
271 for (Integer i = 0; i < nb_direction; ++i) {
272 ConstArrayView<Real> coords(grid_coord[0].view());
273 Int32 nb_value = coords.size();
274 if (nb_value == 0)
275 continue;
276 for (Int32 z = 0; z < (nb_value - 1); ++z)
277 if (coords[z] > coords[z + 1])
278 ARCANE_FATAL("Grid coord '{0}' is not sorted: {1} > {2}", i, coords[z], coords[z + 1]);
279 }
280
281 m_grid_info->m_offset_y = (nb_direction >= 2) ? nb_part_by_direction[0] : 0;
282 m_grid_info->m_offset_z = (nb_direction == 3) ? (nb_part_by_direction[0] * nb_part_by_direction[1]) : 0;
283}
284
285/*---------------------------------------------------------------------------*/
286/*---------------------------------------------------------------------------*/
287
290{
291 if (m_grid_info.get())
292 ARCANE_FATAL("partitionMesh() has already been called. Only one call par SimpleGridMeshPartitioner instance is allowed");
293
294 _buildGridInfo();
295
296 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
299 IParallelMng* pm = mesh->parallelMng();
300 Int32 nb_rank = pm->commSize();
301
302 const Int32 offset_y = m_grid_info->m_offset_y;
303 const Int32 offset_z = m_grid_info->m_offset_z;
304 const Int32 nb_direction = m_grid_info->m_nb_direction;
305
306 // Parcours pour chaque maille chaque direction et regarder dans indice dans
307 // la grille elle se trouve. En déduit le nouveau rang.
308 ENUMERATE_ (Cell, icell, mesh->allCells().own()) {
309 Cell cell = *icell;
310 std::array<Int32, 3> cell_part = { -1, -1, -1 };
312 Int32 nb_node = cell.nbNode();
313 for (Integer inode = 0; inode < nb_node; ++inode)
315 cell_center /= static_cast<Real>(nb_node);
316
317 for (Integer idir = 0; idir < nb_direction; ++idir) {
318 ConstArrayView<Real> coords(m_grid_info->m_grid_coord[idir].view());
319 Real cc = cell_center[idir];
320
321 if (m_is_verbose)
322 info() << " Cell uid=" << cell.uniqueId() << " idir=" << idir << " cc=" << cc;
323
324 cell_part[idir] = _findPart(coords, cc);
325 }
326
327 const Int32 new_owner = cell_part[0] + cell_part[1] * offset_y + cell_part[2] * offset_z;
328 if (m_is_verbose)
329 info() << "CELL=" << ItemPrinter(cell) << " coord=" << cell_center << " new_owner=" << new_owner
330 << " dir=" << cell_part[0] << " " << cell_part[1] << " " << cell_part[2];
331 if (new_owner < 0 || new_owner >= nb_rank)
332 ARCANE_FATAL("Bad value for new owner cell={0} new_owner={1} (max={2})", ItemPrinter(cell), new_owner, nb_rank);
334 }
335
336 cells_new_owner.synchronize();
337 if (mesh->partitionConstraintMng()) {
338 // Deal with Tied Cells
339 mesh->partitionConstraintMng()->computeAndApplyConstraints();
340 }
341 mesh->utilities()->changeOwnersFromCells();
342}
343
344/*---------------------------------------------------------------------------*/
345/*---------------------------------------------------------------------------*/
351_addCellToIntersectedParts(Cell cell, std::array<Int32, 3> min_part, std::array<Int32, 3> nb_part)
352{
353 const Int32 offset_y = m_grid_info->m_offset_y;
354 const Int32 offset_z = m_grid_info->m_offset_z;
355 const Int32 cell_owner = cell.owner();
356
357 for (Integer k0 = 0, maxk0 = nb_part[0]; k0 < maxk0; ++k0)
358 for (Integer k1 = 0, maxk1 = nb_part[1]; k1 < maxk1; ++k1)
359 for (Integer k2 = 0, maxk2 = nb_part[2]; k2 < maxk2; ++k2) {
360 Int32 p0 = min_part[0] + k0;
361 Int32 p1 = min_part[1] + k1;
362 Int32 p2 = min_part[2] + k2;
363 Int32 owner = p0 + (p1 * offset_y) + (p2 * offset_z);
364 // On ne s'envoie pas à soi-même.
365 if (owner != cell_owner)
366 _addGhostCell(owner, cell);
367 }
368}
369
370/*---------------------------------------------------------------------------*/
371/*---------------------------------------------------------------------------*/
372
373void SimpleGridMeshPartitioner::
374_computeSpecificGhostLayer()
375{
376 if (!m_grid_info.get())
377 ARCANE_FATAL("partitionMesh() has to be called before this method.");
378
379 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
381
382 const Int32 nb_direction = m_grid_info->m_nb_direction;
383
384 ENUMERATE_ (Cell, icell, mesh->allCells().own()) {
385 Cell cell = *icell;
386
387 std::array<Int32, 3> min_part = { -1, -1, -1 };
388 std::array<Int32, 3> max_part = { -1, -1, -1 };
389 std::array<Int32, 3> nb_node_part = { 1, 1, 1 };
390
391 // Détermine la bounding box de la maille
392 Real max_value = FloatInfo<Real>::maxValue();
393 Real min_value = -max_value;
394 Real3 min_box(max_value,max_value,max_value);
395 Real3 max_box(min_value,min_value,min_value);
396 for (Node node : cell.nodes()) {
397 Real3 pos = nodes_coord[node];
398 min_box = math::min(min_box,pos);
399 max_box = math::max(max_box,pos);
400 }
401
402 for (Integer idir = 0; idir < nb_direction; ++idir) {
403 ConstArrayView<Real> coords(m_grid_info->m_grid_coord[idir].view());
404 Integer nb_coord = coords.size();
405
406 const Real min_pos = min_box[idir];
407 const Real max_pos = max_box[idir];
408 Int32 min_part_id = _findPart(coords,min_pos);
409 Int32 max_part_id = _findPart(coords,max_pos);
410
411 if (m_is_verbose)
412 info() << " Cell uid=" << cell.uniqueId() << " idir=" << idir
413 << " min_pos=" << min_pos << " max_pos=" << max_pos
414 << " min_part=" << min_part_id << " max_part_id=" << max_part_id;
415
416 // Si on est au bord d'une partie, prend aussi la partie d'avant ou d'après.
417 // Cela permet d'éviter de rater une appartenance si les coordonnées de la grille
418 // et du maillage à partitionner sont les mêmes
419 if (min_part_id>0 && math::isNearlyEqual(min_pos,coords[min_part_id]))
420 --min_part_id;
421 if (max_part_id<(nb_coord-1) && math::isNearlyEqual(max_pos,coords[max_part_id]))
422 ++max_part_id;
423 min_part[idir] = min_part_id;
424 max_part[idir] = max_part_id;
425 }
426
427 Int32 total_nb_part = 1;
428 for (Integer idir = 0; idir < nb_direction; ++idir) {
429 Int32 nb_part = 1 + (max_part[idir] - min_part[idir]);
430 nb_node_part[idir] = nb_part;
431 total_nb_part *= nb_part;
432 }
433
434 if (m_is_verbose)
435 info() << " Cell uid=" << cell.uniqueId() << " min_part=" << ArrayView<Int32>(min_part)
436 << " max_part=" << ArrayView<Int32>(max_part)
437 << " nb_part=" << ArrayView<Int32>(nb_node_part)
438 << " total=" << total_nb_part;
439
440 _addCellToIntersectedParts(cell, min_part, nb_node_part);
441 }
442
443 if (m_is_verbose) {
444 info() << "GHOST_CELLS_TO_SEND";
445 for (const auto& v : m_ghost_cells_builder->m_ghost_cell_uids) {
446 info() << "RANK=" << v.first << " ids=" << v.second;
447 }
448 }
449}
450
451/*---------------------------------------------------------------------------*/
452/*---------------------------------------------------------------------------*/
453
456{
457 //TODO A terme supprimer l'utilisation du maillage issu de l'instance.
458 if (mesh != this->mesh())
459 ARCANE_FATAL("mesh argument should be the same mesh that the one used to create this instance");
460
461 if (m_ghost_cells_builder)
462 ARCANE_FATAL("Only one call to this method is allower per instance.");
463
464 mesh->modifier()->setDynamic(true);
465 mesh->utilities()->partitionAndExchangeMeshWithReplication(this, true);
466
468 m_ghost_cells_builder = scoped_builder.get();
469 mesh->modifier()->addExtraGhostCellsBuilder(m_ghost_cells_builder);
470
471 // Recalcule spécifiquement les mailles fantômes pour recouvrir les partitions
472 _computeSpecificGhostLayer();
473 mesh->modifier()->updateGhostLayers();
474
475 mesh->modifier()->removeExtraGhostCellsBuilder(m_ghost_cells_builder);
476
477 m_ghost_cells_builder = nullptr;
478}
479
480/*---------------------------------------------------------------------------*/
481/*---------------------------------------------------------------------------*/
482
484 ServiceProperty("SimpleGridMeshPartitioner", ST_SubDomain),
486
487/*---------------------------------------------------------------------------*/
488/*---------------------------------------------------------------------------*/
489
490} // End namespace Arcane
491
492/*---------------------------------------------------------------------------*/
493/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ENUMERATE_(type, name, group)
Enumérateur générique d'un groupe d'entité
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Classe de base de service lié à un sous-domaine.
Maille d'un maillage.
Definition Item.h:1178
Interface d'un constructeur de mailles fantômes "extraordinaires"
Interface d'un partitionneur de maillage sur une grille.
virtual CellGroup allCells()=0
Groupe de toutes les mailles.
virtual IItemFamily * cellFamily()=0
Retourne la famille des mailles.
virtual IParallelMng * parallelMng()=0
Gestionnaire de parallèlisme.
virtual IMeshModifier * modifier()=0
Interface de modification associée.
virtual IMeshUtilities * utilities()=0
Interface des fonctions utilitaires associée.
virtual IMeshPartitionConstraintMng * partitionConstraintMng()=0
Gestionnaire des contraintes de partitionnement associées à ce maillage.
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 commSize() const =0
Nombre d'instance dans le communicateur.
virtual char reduce(eReduceType rt, char v)=0
Effectue la réduction de type rt sur le réel v et retourne la valeur.
virtual VariableNodeReal3 & nodesCoordinates()=0
Coordonnées des noeuds.
virtual VariableItemInt32 & itemsNewOwner(eItemKind kind)=0
Variable contenant l'identifiant du sous-domaine propriétaire.
Classe utilitaire pour imprimer les infos sur une entité.
Definition ItemPrinter.h:35
Node node(Int32 i) const
i-ème noeud de l'entité
Definition Item.h:768
Int32 nbNode() const
Nombre de noeuds de l'entité
Definition Item.h:765
Int32 owner() const
Numéro du sous-domaine propriétaire de l'entité
Definition Item.h:229
ItemUniqueId uniqueId() const
Identifiant unique sur tous les domaines.
Definition Item.h:216
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
Classe gérant un vecteur de réel de dimension 3.
Definition Real3.h:132
Structure contenant les informations pour créer un service.
Propriétés de création d'un service.
Informations sur les mailles fantômes supplémentaires.
Int32ConstArrayView extraCellsToSend(Int32 rank) const override
Indices locaux des mailles "extraordinaires" pour envoi.
void computeExtraCellsToSend() override
Calcul des mailles "extraordinaires" à envoyer.
Partitionneur de maillage sur une grille.
void setPartIndex(Int32 i, Int32 j, Int32 k) override
Indice (i,j,k) de la partie.
void build() override
Construction de niveau build du service.
void notifyEndPartition() override
Notification lors de la fin d'un repartionnement (après échange des entités)
void _addCellToIntersectedParts(Cell cell, std::array< Int32, 3 > min_part, std::array< Int32, 3 > nb_part)
IPrimaryMesh * primaryMesh() override
Maillage associé
Int32 _findPart(RealConstArrayView coords, Real center)
Retourne l'indice dans coords de la valeur immédiatement inférieure à position.
void setBoundingBox(Real3 min_val, Real3 max_val) override
Positionne la bounding box de notre sous-domaine.
void applyMeshPartitioning(IMesh *mesh) override
Applique le repartitionnement sur le maillage mesh.
void partitionMesh(bool initial_partition) override
Vue constante d'un tableau de type T.
constexpr Integer size() const noexcept
Nombre d'éléments du tableau.
TraceMessage info() const
Flot pour un message d'information.
T max(const T &a, const T &b, const T &c)
Retourne le maximum de trois éléments.
Definition MathUtils.h:392
ARCCORE_HOST_DEVICE Real2 min(Real2 a, Real2 b)
Retourne le minimum de deux Real2.
Definition MathUtils.h:336
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
constexpr ARCCORE_HOST_DEVICE bool isNearlyEqual(const _Type &a, const _Type &b)
Teste si deux valeurs sont à un peu près égales. Pour les types entiers, cette fonction est équivalen...
Definition Numeric.h:212
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
@ ST_SubDomain
Le service s'utilise au niveau du sous-domaine.
@ IK_Cell
Entité de maillage de genre maille.
Int32 Integer
Type représentant un entier.