Skip to content

Commit a3dccae

Browse files
Merge pull request #1138 from arcaneframework/dev/gg-add-basic-coordinate-partition-for-msh-parallel-reader
Add basic geometric partitioning for MSH parallel reader
2 parents a14fb14 + 23fd7b9 commit a3dccae

File tree

1 file changed

+155
-25
lines changed

1 file changed

+155
-25
lines changed

arcane/src/arcane/std/MshParallelMeshReader.cc

+155-25
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
@@ -1354,7 +1476,15 @@ readMeshFromMshFile(IMesh* mesh, const String& filename)
13541476
const Int32 nb_rank = pm->commSize();
13551477

13561478
// Détermine les rangs qui vont conserver les données
1357-
m_nb_part = math::min(m_nb_part, nb_rank);
1479+
m_nb_part = nb_rank;
1480+
if (nb_rank > 64)
1481+
m_nb_part = nb_rank / 2;
1482+
if (nb_rank > 128)
1483+
m_nb_part = nb_rank / 4;
1484+
if (nb_rank > 512)
1485+
m_nb_part = nb_rank / 8;
1486+
if (nb_rank > 2048)
1487+
m_nb_part = nb_rank / 16;
13581488
m_parts_rank.resize(m_nb_part);
13591489
for (Int32 i = 0; i < m_nb_part; ++i) {
13601490
m_parts_rank[i] = i % nb_rank;

0 commit comments

Comments
 (0)