Skip to content

Commit 4895db0

Browse files
[arcane,std] Ajoute partitionnement géométrique simple pour 'MshParallelMeshReader'.
Avant on partitionnait sur les uniqueId() des mailles mais dans GMSH la numérotation est assez quelconque et donc cela pouvait générer beaucoup de mailles fantômes. Pour éviter cela, on partitionne suivant la position des noeuds en prenant en compte uniquement la coordonnée X.
1 parent a14fb14 commit 4895db0

File tree

1 file changed

+146
-24
lines changed

1 file changed

+146
-24
lines changed

arcane/src/arcane/std/MshParallelMeshReader.cc

Lines changed: 146 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,8 @@
4747
* l'option '-save-all' pour sauver un fichier '.msh' à partir d'un '.geo'
4848
*
4949
* TODO:
50-
* - lire les tags des noeuds(uniqueId())
5150
* - supporter les partitions
5251
* - pouvoir utiliser la bibliothèque 'gmsh' directement.
53-
* - améliorer la lecture parallèle en évitant que tous les sous-domaines
54-
* lisent le fichier (même si seul le sous-domaine 0 alloue les entités)
5552
*/
5653

5754
/*---------------------------------------------------------------------------*/
@@ -209,6 +206,10 @@ class MshParallelMeshReader
209206
UniqueArray<Real3> nodes_coordinates;
210207
//! UniqueId() des noeuds de ma partie.
211208
UniqueArray<Int64> nodes_unique_id;
209+
//! Rang auquel le noeud appartiendra
210+
std::unordered_map<Int64, Int32> nodes_rank_map;
211+
Real3 m_node_min_bounding_box;
212+
Real3 m_node_max_bounding_box;
212213
MeshPhysicalNameList physical_name_list;
213214
UniqueArray<MeshV4EntitiesNodes> entities_nodes_list;
214215
UniqueArray<MeshV4EntitiesWithNodes> entities_with_nodes_list[3];
@@ -238,7 +239,7 @@ class MshParallelMeshReader
238239

239240
private:
240241

241-
eReturnType _readNodesFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info);
242+
void _readNodesFromFileAscii(MeshInfo& mesh_info);
242243
Integer _readElementsFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info);
243244
eReturnType _readMeshFromNewMshFile(IosFile* iso_file);
244245
void _setNodesCoordinates(MeshInfo& mesh_info);
@@ -258,6 +259,8 @@ class MshParallelMeshReader
258259
Int32 _getIntegerAndBroadcast();
259260
void _getInt64ArrayAndBroadcast(ArrayView<Int64> values);
260261
void _readOneElementBlock(MeshV4ElementsBlock& block);
262+
void _computeNodesPartition(MeshInfo& mesh_info);
263+
void _computeOwnCells(MeshInfo& mesh_info, MeshV4ElementsBlock& block);
261264
};
262265

263266
/*---------------------------------------------------------------------------*/
@@ -400,6 +403,63 @@ _switchMshType(Int64 mshElemType, Int32& nNodes)
400403
return IT_NullType;
401404
}
402405

406+
/*---------------------------------------------------------------------------*/
407+
/*---------------------------------------------------------------------------*/
408+
409+
void MshParallelMeshReader::
410+
_computeNodesPartition(MeshInfo& mesh_info)
411+
{
412+
// Détermine la bounding box de la maille
413+
Real max_value = FloatInfo<Real>::maxValue();
414+
Real min_value = -max_value;
415+
Real3 min_box(max_value, max_value, max_value);
416+
Real3 max_box(min_value, min_value, min_value);
417+
const Int64 nb_node = mesh_info.nodes_coordinates.largeSize();
418+
for (Real3 pos : mesh_info.nodes_coordinates) {
419+
min_box = math::min(min_box, pos);
420+
max_box = math::max(max_box, pos);
421+
}
422+
mesh_info.m_node_min_bounding_box = min_box;
423+
mesh_info.m_node_max_bounding_box = max_box;
424+
425+
//! Rank auquel appartient les noeuds de ma partie.
426+
UniqueArray<Int32> nodes_part(nb_node, 0);
427+
428+
// Pour la partition, on ne prend en compte que la coordonnée X.
429+
// On est sur qu'elle est valable pour toutes les dimensions du maillage.
430+
// On partitionne avec des intervalles de même longueur.
431+
// NOTE: Cela fonctionne bien si l'ensemble des noeuds est bien réparti.
432+
// Si ce n'est pas le cas on pourrait utiliser une bisection en coupant
433+
// à chaque fois sur la moyenne.
434+
Real min_x = min_box.x;
435+
Real max_x = max_box.x;
436+
IParallelMng* pm = m_parallel_mng;
437+
Real global_min_x = pm->reduce(Parallel::ReduceMin, min_x);
438+
Real global_max_x = pm->reduce(Parallel::ReduceMax, max_x);
439+
info() << "MIN_MAX_X=" << global_min_x << " " << global_max_x;
440+
441+
Real diff_v = (global_max_x - global_min_x) / static_cast<Real>(m_nb_part);
442+
// Ne devrait pas arriver mais c'est nécessaire pour éviter d'éventuelles
443+
// divisions par zéro.
444+
if (!math::isNearlyEqual(global_min_x, global_max_x)) {
445+
for (Int64 i = 0; i < nb_node; ++i) {
446+
Int32 part = static_cast<Int32>((mesh_info.nodes_coordinates[i].x - global_min_x) / diff_v);
447+
part = std::clamp(part, 0, m_nb_part - 1);
448+
nodes_part[i] = part;
449+
}
450+
}
451+
UniqueArray<Int32> nb_node_per_rank(m_nb_part, 0);
452+
// Construit la table de hashage des rangs
453+
for (Int64 i = 0; i < nb_node; ++i) {
454+
Int32 rank = m_parts_rank[nodes_part[i]];
455+
Int64 uid = mesh_info.nodes_unique_id[i];
456+
++nb_node_per_rank[rank];
457+
mesh_info.nodes_rank_map.insert(std::make_pair(uid, rank));
458+
}
459+
pm->reduce(Parallel::ReduceSum, nb_node_per_rank);
460+
info() << "NB_NODE_PER_RANK=" << nb_node_per_rank;
461+
}
462+
403463
/*---------------------------------------------------------------------------*/
404464
/*---------------------------------------------------------------------------*/
405465
/*!
@@ -426,9 +486,10 @@ _switchMshType(Int64 mshElemType, Int32& nNodes)
426486
*$EndNodes
427487
* \endcode
428488
*/
429-
IMeshReader::eReturnType MshParallelMeshReader::
430-
_readNodesFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info)
489+
void MshParallelMeshReader::
490+
_readNodesFromFileAscii(MeshInfo& mesh_info)
431491
{
492+
IosFile* ios_file = m_ios_file.get();
432493
IParallelMng* pm = m_parallel_mng;
433494
const Int32 my_rank = pm->commRank();
434495

@@ -454,6 +515,7 @@ _readNodesFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info)
454515

455516
UniqueArray<Int64> nodes_uids;
456517
UniqueArray<Real3> nodes_coordinates;
518+
457519
for (Integer i_entity = 0; i_entity < nb_entity; ++i_entity) {
458520

459521
FixedArray<Int64, 4> entity_infos;
@@ -551,7 +613,8 @@ _readNodesFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info)
551613
if (ios_file)
552614
ios_file->getNextLine(); // Skip current \n\r
553615
}
554-
return IMeshReader::RTOk;
616+
617+
_computeNodesPartition(mesh_info);
555618
}
556619

557620
/*---------------------------------------------------------------------------*/
@@ -733,28 +796,88 @@ _readElementsFromFileAscii(IosFile* ios_file, MeshInfo& mesh_info)
733796
ARCANE_THROW(NotSupportedException, "mesh dimension '{0}'. Only 2D or 3D meshes are supported", mesh_dimension);
734797
info() << "Computed mesh dimension = " << mesh_dimension;
735798

736-
// On ne conserve que les blocs de notre dimension pour créér les mailles.
737-
// On divise chaque bloc en N partie (avec N le nombre de rangs MPI) et
738-
// chaque rang ne conserve qu'une partie. Ainsi chaque sous-domaine aura une
739-
// partie du maillage afin de garantir un équilibrage sur le nombre de mailles.
740799
for (MeshV4ElementsBlock& block : blocks) {
741-
if (block.dimension != mesh_dimension)
742-
continue;
800+
if (block.dimension == mesh_dimension)
801+
_computeOwnCells(mesh_info, block);
802+
}
743803

744-
Integer item_type = block.item_type;
745-
Integer item_nb_node = block.item_nb_node;
804+
return mesh_dimension;
805+
}
806+
807+
/*---------------------------------------------------------------------------*/
808+
/*---------------------------------------------------------------------------*/
746809

747-
const Int64 block_nb_item = block.uids.size();
748-
for (Int64 i = 0; i < block_nb_item; ++i) {
810+
void MshParallelMeshReader::
811+
_computeOwnCells(MeshInfo& mesh_info, MeshV4ElementsBlock& block)
812+
{
813+
// On ne conserve que les mailles dont le premier noeud appartient à notre rang.
814+
815+
IParallelMng* pm = m_parallel_mng;
816+
const Int32 my_rank = pm->commRank();
817+
818+
const Int32 item_type = block.item_type;
819+
const Int32 item_nb_node = block.item_nb_node;
820+
821+
UniqueArray<Int64> connectivities;
822+
UniqueArray<Int64> uids;
823+
UniqueArray<Int32> nodes_rank;
824+
825+
const Int32 nb_part = m_parts_rank.size();
826+
for (Int32 i_part = 0; i_part < nb_part; ++i_part) {
827+
const Int32 dest_rank = m_parts_rank[i_part];
828+
ArrayView<Int64> connectivities_view;
829+
ArrayView<Int64> uids_view;
830+
831+
// Broadcast la i_part-ème partie des uids et connectivités des mailles
832+
{
833+
FixedArray<Int64, 2> nb_connectivity_and_uid;
834+
if (my_rank == dest_rank) {
835+
nb_connectivity_and_uid[0] = block.connectivities.size();
836+
nb_connectivity_and_uid[1] = block.uids.size();
837+
connectivities_view = block.connectivities;
838+
uids_view = block.uids;
839+
}
840+
pm->broadcast(nb_connectivity_and_uid.view(), dest_rank);
841+
if (my_rank != dest_rank) {
842+
connectivities.resize(nb_connectivity_and_uid[0]);
843+
uids.resize(nb_connectivity_and_uid[1]);
844+
connectivities_view = connectivities;
845+
uids_view = uids;
846+
}
847+
pm->broadcast(connectivities_view, dest_rank);
848+
pm->broadcast(uids_view, dest_rank);
849+
}
850+
Int32 nb_item = uids_view.size();
851+
nodes_rank.resize(nb_item);
852+
nodes_rank.fill(-1);
853+
854+
// Parcours les mailles. Chaque maille appartiendra au rang
855+
// de son premier noeud. Si cette partie correspond à mon rang, alors
856+
// on conserve la maille.
857+
for (Int32 i = 0; i < nb_item; ++i) {
858+
Int64 first_node_uid = connectivities_view[i * item_nb_node];
859+
auto x = mesh_info.nodes_rank_map.find(first_node_uid);
860+
if (x == mesh_info.nodes_rank_map.end())
861+
// Le noeud n'est pas dans ma liste
862+
continue;
863+
Int32 rank = x->second;
864+
nodes_rank[i] = rank;
865+
}
866+
pm->reduce(Parallel::ReduceMax, nodes_rank);
867+
for (Int32 i = 0; i < nb_item; ++i) {
868+
const Int32 rank = nodes_rank[i];
869+
if (rank != my_rank)
870+
// Le noeud n'est pas dans ma partie
871+
continue;
872+
// Le noeud est chez moi, j'ajoute ma maille à la liste des
873+
// mailles que je vais créer.
749874
mesh_info.cells_type.add(item_type);
750875
mesh_info.cells_nb_node.add(item_nb_node);
751-
mesh_info.cells_uid.add(block.uids[i]);
752-
auto v = block.connectivities.subView(i * item_nb_node, item_nb_node);
876+
mesh_info.cells_uid.add(uids_view[i]);
877+
auto v = connectivities_view.subView(i * item_nb_node, item_nb_node);
753878
mesh_info.cells_connectivity.addRange(v);
754879
}
755880
}
756-
757-
return mesh_dimension;
758881
}
759882

760883
/*---------------------------------------------------------------------------*/
@@ -842,7 +965,7 @@ _allocateCells(MeshInfo& mesh_info)
842965
// 1 pour son type,
843966
// 1 pour chaque noeud
844967
UniqueArray<Int64> cells_infos;
845-
Integer connectivity_index = 0;
968+
Int32 connectivity_index = 0;
846969
UniqueArray<Real3> local_coords;
847970
for (Integer i = 0; i < nb_elements; ++i) {
848971
Integer current_cell_nb_node = mesh_info.cells_nb_node[i];
@@ -1305,8 +1428,7 @@ _readMeshFromNewMshFile(IosFile* ios_file)
13051428
ARCANE_THROW(IOException, "Unexpected string '{0}'. Valid values are '$Nodes'", next_line);
13061429

13071430
// Fetch nodes number and the coordinates
1308-
if (_readNodesFromFileAscii(ios_file, mesh_info) != IMeshReader::RTOk)
1309-
ARCANE_THROW(IOException, "Ascii nodes coords error");
1431+
_readNodesFromFileAscii(mesh_info);
13101432

13111433
if (ios_file) {
13121434
// $EndNodes

0 commit comments

Comments
 (0)