Arcane  v3.15.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
MshParallelMeshReader.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/* MshParallelMeshReader.cc (C) 2000-2025 */
9/* */
10/* Lecture parallèle d'un fichier au format MSH. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/TraceAccessor.h"
15#include "arcane/utils/Array.h"
16#include "arcane/utils/IOException.h"
17#include "arcane/utils/FatalErrorException.h"
18#include "arcane/utils/Ref.h"
19#include "arcane/utils/NotSupportedException.h"
20#include "arcane/utils/Real3.h"
21#include "arcane/utils/OStringStream.h"
22#include "arcane/utils/FixedArray.h"
23#include "arcane/utils/PlatformUtils.h"
24#include "arcane/utils/CheckedConvert.h"
25
26#include "arcane/core/IMeshReader.h"
27#include "arcane/core/IPrimaryMesh.h"
28#include "arcane/core/IItemFamily.h"
29#include "arcane/core/ItemGroup.h"
30#include "arcane/core/VariableTypes.h"
31#include "arcane/core/IParallelMng.h"
33#include "arcane/core/IMeshModifier.h"
34#include "arcane/core/MeshKind.h"
35
36// Element types in .msh file format, found in gmsh-2.0.4/Common/GmshDefines.h
37#include "arcane/std/internal/IosFile.h"
38#include "arcane/std/internal/IosGmsh.h"
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43/*
44 * NOTES:
45 * - La bibliothèque `gmsh` fournit un script 'open.py' dans le répertoire
46 * 'demos/api' qui permet de générer un fichier '.msh' à partir d'un '.geo'.
47 * - Il est aussi possible d'utiliser directement l'exécutable 'gmsh' avec
48 * l'option '-save-all' pour sauver un fichier '.msh' à partir d'un '.geo'
49 *
50 * TODO:
51 * - supporter les partitions
52 * - pouvoir utiliser la bibliothèque 'gmsh' directement.
53 */
54
55/*---------------------------------------------------------------------------*/
56/*---------------------------------------------------------------------------*/
57
58namespace Arcane
59{
60
61/*---------------------------------------------------------------------------*/
62/*---------------------------------------------------------------------------*/
78: public TraceAccessor
79, public IMshMeshReader
80{
81 public:
82
83 using eReturnType = typename IMeshReader::eReturnType;
84
95 {
96 Int32 index = 0;
97 Int64 nb_entity = 0;
99 Int32 dimension = -1;
100 Int32 item_nb_node = 0;
101 Int64 entity_tag = -1;
102 bool is_built_as_cells = false;
103 UniqueArray<Int64> uids;
105 };
106
111 {
112 MeshPhysicalName(Int32 _dimension, Int64 _tag, const String& _name)
113 : dimension(_dimension)
114 , tag(_tag)
115 , name(_name)
116 {}
117 MeshPhysicalName() = default;
118 bool isNull() const { return dimension == (-1); }
119 Int32 dimension = -1;
120 Int64 tag = -1;
121 String name;
122 };
123
128 {
129 void add(Int32 dimension, Int64 tag, String name)
130 {
131 m_physical_names[dimension].add(MeshPhysicalName{ dimension, tag, name });
132 }
133 MeshPhysicalName find(Int32 dimension, Int64 tag) const
134 {
135 for (auto& x : m_physical_names[dimension])
136 if (x.tag == tag)
137 return x;
138 return {};
139 }
140
141 private:
142
143 FixedArray<UniqueArray<MeshPhysicalName>, 4> m_physical_names;
144 };
145
148 {
149 MeshV4EntitiesNodes(Int64 _tag, Int64 _physical_tag)
150 : tag(_tag)
151 , physical_tag(_physical_tag)
152 {}
153 Int64 tag;
154 Int64 physical_tag;
155 };
156
159 {
160 MeshV4EntitiesWithNodes(Int32 _dim, Int64 _tag, Int64 _physical_tag)
161 : dimension(_dim)
162 , tag(_tag)
163 , physical_tag(_physical_tag)
164 {}
165 Int32 dimension;
166 Int64 tag;
167 Int64 physical_tag;
168 };
169
171 {
172 public:
173
174 Int32 m_entity_dim = -1;
175 Int32 m_entity_tag = -1;
176 Int32 m_entity_tag_master = -1;
177 UniqueArray<double> m_affine_values;
178 Int32 m_nb_corresponding_node = 0;
179 UniqueArray<Int64> m_corresponding_nodes;
180 };
181
184 {
185 public:
186
187 UniqueArray<PeriodicOneInfo> m_periodic_list;
188 };
189
194 {
195 // TODO: Allouer la connectivité par bloc pour éviter de trop grosses allocations
196
197 public:
198
199 void addItem(Int16 type_id, Int64 unique_id, SmallSpan<const Int64> nodes_uid)
200 {
201 ++nb_item;
202 items_infos.add(type_id);
203 items_infos.add(unique_id);
204 items_infos.addRange(nodes_uid);
205 }
206
207 public:
208
209 Int32 nb_item = 0;
210 UniqueArray<Int64> items_infos;
211 };
212
215 {
216 public:
217
218 void findEntities(Int32 dimension, Int64 tag, Array<MeshV4EntitiesWithNodes>& entities)
219 {
220 entities.clear();
221 for (auto& x : entities_with_nodes_list[dimension - 1])
222 if (x.tag == tag)
223 entities.add(x);
224 }
225
226 MeshV4EntitiesNodes* findNodeEntities(Int64 tag)
227 {
228 for (auto& x : entities_nodes_list)
229 if (x.tag == tag)
230 return &x;
231 return nullptr;
232 }
233
234 public:
235
236 ItemKindInfo faces_infos;
237 ItemKindInfo cells_infos;
238
244 std::unordered_map<Int64, Int32> nodes_rank_map;
245 Real3 m_node_min_bounding_box;
246 Real3 m_node_max_bounding_box;
247 MeshPhysicalNameList physical_name_list;
248 UniqueArray<MeshV4EntitiesNodes> entities_nodes_list;
249 FixedArray<UniqueArray<MeshV4EntitiesWithNodes>, 3> entities_with_nodes_list;
251 PeriodicInfo m_periodic_info;
252 };
253
254 public:
255
258 {}
259
260 eReturnType readMeshFromMshFile(IMesh* mesh, const String& file_name) override;
261
262 private:
263
264 IMesh* m_mesh = nullptr;
265 IParallelMng* m_parallel_mng = nullptr;
266 Int32 m_master_io_rank = A_NULL_RANK;
267 bool m_is_parallel = false;
268 Ref<IosFile> m_ios_file; // nullptr sauf pour le rang maitre.
269 MeshInfo m_mesh_info;
271 Int32 m_nb_part = 4;
275 bool m_is_binary = false;
276
277 private:
278
279 void _readNodesFromFile();
280 void _readNodesOneEntity(Int32 entity_index);
281 Integer _readElementsFromFile();
282 void _readMeshFromFile();
284 void _addFaces();
285 void _allocateCells();
286 void _allocateGroups();
288 void _addFaceGroupOnePart(ConstArrayView<Int64> connectivities, Int32 item_nb_node,
289 const String& group_name, Int32 block_index);
291 void _addCellOrNodeGroupOnePart(ConstArrayView<Int64> uids, const String& group_name,
292 Int32 block_index, IItemFamily* family);
293 Int16 _switchMshType(Int64, Int32&) const;
294 void _readPhysicalNames();
295 void _readEntities();
296 void _readPeriodic();
297 void _readOneEntity(Int32 entity_dim);
300 Int32 _getIntegerAndBroadcast();
301 Int64 _getInt64AndBroadcast();
302 void _getInt64ArrayAndBroadcast(ArrayView<Int64> values);
303 void _getInt32ArrayAndBroadcast(ArrayView<Int32> values);
304 void _getDoubleArrayAndBroadcast(ArrayView<double> values);
307 void _computeOwnItems(MeshV4ElementsBlock& block, ItemKindInfo& item_kind_info, bool is_generate_uid);
308 Real3 _getReal3();
309 Int32 _getInt32();
310 Int64 _getInt64();
311 void _goToNextLine();
313};
314
315/*---------------------------------------------------------------------------*/
316/*---------------------------------------------------------------------------*/
317
318namespace
319{
326 inline std::pair<Int64, Int64>
327 _interval(Int32 index, Int32 nb_interval, Int64 size)
328 {
329 Int64 isize = size / nb_interval;
330 Int64 ibegin = index * isize;
331 // Pour le dernier interval, prend les elements restants
332 if ((index + 1) == nb_interval)
333 isize = size - ibegin;
334 return { ibegin, isize };
335 }
336} // namespace
337
338/*---------------------------------------------------------------------------*/
339/*---------------------------------------------------------------------------*/
345{
346 IosFile* f = m_ios_file.get();
347 String s;
348 if (f) {
349 s = f->getNextLine();
350 }
351 if (m_is_parallel) {
352 if (f)
353 info() << "BroadcastNextLine: " << s;
354 m_parallel_mng->broadcastString(s, m_master_io_rank);
355 }
356 info() << "GetNextLine: " << s;
357 return s;
358}
359
360/*---------------------------------------------------------------------------*/
361/*---------------------------------------------------------------------------*/
367{
368 IosFile* f = m_ios_file.get();
369 Int32 is_end_int = 0;
370 if (f) {
371 is_end_int = f->isEnd() ? 1 : 0;
372 info() << "IsEndOfFile_Master: " << is_end_int;
373 }
374 if (m_is_parallel) {
375 if (f)
376 info() << "IsEndOfFile: " << is_end_int;
377 m_parallel_mng->broadcast(ArrayView<Int32>(1, &is_end_int), m_master_io_rank);
378 }
379 bool is_end = (is_end_int != 0);
380 info() << "IsEnd: " << is_end;
381 return is_end;
382}
383
384/*---------------------------------------------------------------------------*/
385/*---------------------------------------------------------------------------*/
386
388//_getASCIIIntegerAndBroadcast()
389_getIntegerAndBroadcast()
390{
391 IosFile* f = m_ios_file.get();
393 if (f)
394 v[0] = f->getInteger();
395 if (m_is_parallel) {
396 m_parallel_mng->broadcast(v.view(), m_master_io_rank);
397 }
398 return v[0];
399}
400
401/*---------------------------------------------------------------------------*/
402/*---------------------------------------------------------------------------*/
403
404Int64 MshParallelMeshReader::
405_getInt64AndBroadcast()
406{
407 IosFile* f = m_ios_file.get();
408 FixedArray<Int64, 1> v;
409 if (f)
410 v[0] = _getInt64();
411 if (m_is_parallel) {
412 m_parallel_mng->broadcast(v.view(), m_master_io_rank);
413 }
414 return v[0];
415}
416
417/*---------------------------------------------------------------------------*/
418/*---------------------------------------------------------------------------*/
419
420void MshParallelMeshReader::
421_getInt64ArrayAndBroadcast(ArrayView<Int64> values)
422{
423 IosFile* f = m_ios_file.get();
424 if (f) {
425 if (m_is_binary) {
426 f->binaryRead(values);
427 }
428 else {
429 for (Int64& v : values)
430 v = f->getInt64();
431 }
432 }
433 if (m_is_parallel)
434 m_parallel_mng->broadcast(values, m_master_io_rank);
435}
436
437/*---------------------------------------------------------------------------*/
438/*---------------------------------------------------------------------------*/
439
440void MshParallelMeshReader::
441_getInt32ArrayAndBroadcast(ArrayView<Int32> values)
442{
443 IosFile* f = m_ios_file.get();
444 if (f) {
445 if (m_is_binary) {
446 f->binaryRead(values);
447 }
448 else {
449 for (Int32& v : values)
450 v = f->getInteger();
451 }
452 }
453 if (m_is_parallel)
454 m_parallel_mng->broadcast(values, m_master_io_rank);
455}
456
457/*---------------------------------------------------------------------------*/
458/*---------------------------------------------------------------------------*/
459
460void MshParallelMeshReader::
461_getDoubleArrayAndBroadcast(ArrayView<double> values)
462{
463 IosFile* f = m_ios_file.get();
464 if (f) {
465 if (m_is_binary) {
466 f->binaryRead(values);
467 }
468 else {
469 for (double& v : values)
470 v = f->getReal();
471 }
472 }
473 if (m_is_parallel)
474 m_parallel_mng->broadcast(values, m_master_io_rank);
475}
476
477/*---------------------------------------------------------------------------*/
478/*---------------------------------------------------------------------------*/
479
480Real3 MshParallelMeshReader::
481_getReal3()
482{
483 IosFile* f = m_ios_file.get();
485 Real3 v;
486 if (m_is_binary) {
487 f->binaryRead(SmallSpan<Real3>(&v, 1));
488 }
489 else {
490 Real x = f->getReal();
491 Real y = f->getReal();
492 Real z = f->getReal();
493 v = Real3(x, y, z);
494 }
495 return v;
496}
497
498/*---------------------------------------------------------------------------*/
499/*---------------------------------------------------------------------------*/
500
501Int32 MshParallelMeshReader::
502_getInt32()
503{
504 IosFile* f = m_ios_file.get();
506 Int32 v = 0;
507 if (m_is_binary)
508 f->binaryRead(SmallSpan<Int32>(&v, 1));
509 else
510 v = f->getInteger();
511 return v;
512}
513
514/*---------------------------------------------------------------------------*/
515/*---------------------------------------------------------------------------*/
516
517Int64 MshParallelMeshReader::
518_getInt64()
519{
520 IosFile* f = m_ios_file.get();
522 Int64 v = 0;
523 if (m_is_binary)
524 f->binaryRead(SmallSpan<Int64>(&v, 1));
525 else
526 v = f->getInt64();
527 return v;
528}
529
530/*---------------------------------------------------------------------------*/
531/*---------------------------------------------------------------------------*/
532
533void MshParallelMeshReader::
534_goToNextLine()
535{
536 if (m_ios_file.get())
537 m_ios_file->getNextLine();
538}
539
540/*---------------------------------------------------------------------------*/
541/*---------------------------------------------------------------------------*/
542
543Int16 MshParallelMeshReader::
544_switchMshType(Int64 mshElemType, Int32& nNodes) const
545{
546 switch (mshElemType) {
547 case IT_NullType: // used to decode IT_NullType: IT_HemiHexa7|IT_Line9
548 switch (nNodes) {
549 case 7:
550 return IT_HemiHexa7;
551 default:
552 info() << "Could not decode IT_NullType with nNodes=" << nNodes;
553 throw IOException("_switchMshType", "Could not decode IT_NullType with nNodes");
554 }
555 break;
556 case MSH_PNT:
557 nNodes = 1;
558 return IT_Vertex;
559 case MSH_LIN_2:
560 nNodes = 2;
561 return IT_Line2;
562 case MSH_TRI_3:
563 nNodes = 3;
564 return IT_Triangle3;
565 case MSH_QUA_4:
566 nNodes = 4;
567 return IT_Quad4;
568 case MSH_TET_4:
569 nNodes = 4;
570 return IT_Tetraedron4;
571 case MSH_HEX_8:
572 nNodes = 8;
573 return IT_Hexaedron8;
574 case MSH_PRI_6:
575 nNodes = 6;
576 return IT_Pentaedron6;
577 case MSH_PYR_5:
578 nNodes = 5;
579 return IT_Pyramid5;
580 case MSH_TRI_10:
581 nNodes = 10;
582 return IT_Heptaedron10;
583 case MSH_TRI_12:
584 nNodes = 12;
585 return IT_Octaedron12;
586 case MSH_TRI_6:
587 nNodes = 6;
588 return IT_Triangle6;
589 case MSH_TET_10:
590 nNodes = 10;
591 return IT_Tetraedron10;
592 case MSH_QUA_9:
593 case MSH_HEX_27:
594 case MSH_PRI_18:
595 case MSH_PYR_14:
596 case MSH_QUA_8:
597 case MSH_HEX_20:
598 case MSH_PRI_15:
599 case MSH_PYR_13:
600 case MSH_TRI_9:
601 case MSH_TRI_15:
602 case MSH_TRI_15I:
603 case MSH_TRI_21:
604 default:
605 ARCANE_THROW(NotSupportedException, "Unknown GMSH element type '{0}'", mshElemType);
606 }
607 return IT_NullType;
608}
609
610/*---------------------------------------------------------------------------*/
611/*---------------------------------------------------------------------------*/
612
615{
616 // Détermine la bounding box de la maille
617 Real max_value = FloatInfo<Real>::maxValue();
618 Real min_value = -max_value;
619 Real3 min_box(max_value, max_value, max_value);
620 Real3 max_box(min_value, min_value, min_value);
621 const Int64 nb_node = m_mesh_info.nodes_coordinates.largeSize();
622 for (Real3 pos : m_mesh_info.nodes_coordinates) {
625 }
626 m_mesh_info.m_node_min_bounding_box = min_box;
627 m_mesh_info.m_node_max_bounding_box = max_box;
628
631
632 // Pour la partition, on ne prend en compte que la coordonnée X.
633 // On est sur qu'elle est valable pour toutes les dimensions du maillage.
634 // On partitionne avec des intervalles de même longueur.
635 // NOTE: Cela fonctionne bien si l'ensemble des noeuds est bien réparti.
636 // Si ce n'est pas le cas on pourrait utiliser une bisection en coupant
637 // à chaque fois sur la moyenne.
638 Real min_x = min_box.x;
639 Real max_x = max_box.x;
640 IParallelMng* pm = m_parallel_mng;
641 Real global_min_x = pm->reduce(Parallel::ReduceMin, min_x);
642 Real global_max_x = pm->reduce(Parallel::ReduceMax, max_x);
643 info() << "MIN_MAX_X=" << global_min_x << " " << global_max_x;
644
645 Real diff_v = (global_max_x - global_min_x) / static_cast<Real>(m_nb_part);
646 // Ne devrait pas arriver mais c'est nécessaire pour éviter d'éventuelles
647 // divisions par zéro.
649 for (Int64 i = 0; i < nb_node; ++i) {
650 Int32 part = static_cast<Int32>((m_mesh_info.nodes_coordinates[i].x - global_min_x) / diff_v);
651 part = std::clamp(part, 0, m_nb_part - 1);
652 nodes_part[i] = part;
653 }
654 }
656 // Construit la table de hashage des rangs
657 for (Int64 i = 0; i < nb_node; ++i) {
658 Int32 rank = m_parts_rank[nodes_part[i]];
659 Int64 uid = m_mesh_info.nodes_unique_id[i];
660 ++nb_node_per_rank[rank];
661 m_mesh_info.nodes_rank_map.insert(std::make_pair(uid, rank));
662 }
663 pm->reduce(Parallel::ReduceSum, nb_node_per_rank);
664 info() << "NB_NODE_PER_RANK=" << nb_node_per_rank;
665}
666
667/*---------------------------------------------------------------------------*/
668/*---------------------------------------------------------------------------*/
695{
697 _getInt64ArrayAndBroadcast(nodes_info.view());
698
699 Int64 nb_entity = nodes_info[0];
700 Int64 total_nb_node = nodes_info[1];
701 Int64 min_node_tag = nodes_info[2];
702 Int64 max_node_tag = nodes_info[3];
703
704 if (!m_is_binary)
705 _goToNextLine();
706
707 if (total_nb_node < 0)
708 ARCANE_THROW(IOException, "Invalid number of nodes : '{0}'", total_nb_node);
709
710 info() << "[Nodes] nb_entity=" << nb_entity
711 << " total_nb_node=" << total_nb_node
712 << " min_tag=" << min_node_tag
713 << " max_tag=" << max_node_tag
714 << " read_nb_part=" << m_nb_part
715 << " nb_rank=" << m_parallel_mng->commSize();
716
717 for (Integer i_entity = 0; i_entity < nb_entity; ++i_entity) {
718 _readNodesOneEntity(i_entity);
719 }
720
722
723 if (m_is_binary)
724 _goToNextLine();
725}
726
727/*---------------------------------------------------------------------------*/
728/*---------------------------------------------------------------------------*/
729
730void MshParallelMeshReader::
731_readNodesOneEntity(Int32 entity_index)
732{
733 IosFile* ios_file = m_ios_file.get();
734 IParallelMng* pm = m_parallel_mng;
735 const Int32 my_rank = pm->commRank();
736
738 UniqueArray<Real3> nodes_coordinates;
739
741 _getInt32ArrayAndBroadcast(entity_infos.view());
742 Int64 nb_node2 = _getInt64AndBroadcast();
743
744 if (!m_is_binary)
745 _goToNextLine();
746
747 // Dimension de l'entité (pas utile)
748 [[maybe_unused]] Int32 entity_dim = entity_infos[0];
749 // Tag de l'entité (pas utile)
750 [[maybe_unused]] Int32 entity_tag = entity_infos[1];
752 //Int64 nb_node2 = entity_infos[3];
753
754 info() << "[Nodes] index=" << entity_index << " entity_dim=" << entity_dim << " entity_tag=" << entity_tag
755 << " parametric=" << parametric_coordinates
756 << " nb_node2=" << nb_node2;
757
758 if (parametric_coordinates != 0)
759 ARCANE_THROW(NotSupportedException, "Only 'parametric coordinates' value of '0' is supported (current={0})", parametric_coordinates);
760
761 // Il est possible que le nombre de noeuds soit 0.
762 // Dans ce cas, il faut directement passer à la ligne suivante
763 if (nb_node2 == 0)
764 return;
765
766 // Partitionne la lecture en \a m_nb_part
767 // Pour chaque i_entity , on a d'abord la liste des identifiants puis la liste des coordonnées
768
769 for (Int32 i_part = 0; i_part < m_nb_part; ++i_part) {
770 Int64 nb_to_read = _interval(i_part, m_nb_part, nb_node2).second;
772 info() << "Reading UIDS part i=" << i_part << " dest_rank=" << dest_rank << " nb_to_read=" << nb_to_read;
773 if (my_rank == dest_rank || my_rank == m_master_io_rank) {
774 nodes_uids.resize(nb_to_read);
775 }
776
777 // Le rang maitre lit les informations des noeuds pour la partie concernée
778 // et les transfère au rang destination
779 if (ios_file) {
780 if (m_is_binary) {
781 ios_file->binaryRead(nodes_uids.view());
782 }
783 else {
784 for (Integer i = 0; i < nb_to_read; ++i) {
785 // Conserve le uniqueId() du noeuds.
786 nodes_uids[i] = ios_file->getInt64();
787 //info() << "I=" << i << " ID=" << nodes_uids[i];
788 }
789 }
790 if (dest_rank != m_master_io_rank) {
791 pm->send(nodes_uids, dest_rank);
792 }
793 }
794 else if (my_rank == dest_rank) {
795 pm->recv(nodes_uids, m_master_io_rank);
796 }
797
798 // Conserve les informations de ma partie
799 if (my_rank == dest_rank) {
800 m_mesh_info.nodes_unique_id.addRange(nodes_uids);
801 }
802 }
803
804 // Lecture par partie des coordonnées
805 for (Int32 i_part = 0; i_part < m_nb_part; ++i_part) {
806 Int64 nb_to_read = _interval(i_part, m_nb_part, nb_node2).second;
807 Int32 dest_rank = m_parts_rank[i_part];
808 info() << "Reading COORDS part i=" << i_part << " dest_rank=" << dest_rank << " nb_to_read=" << nb_to_read;
809 if (my_rank == dest_rank || my_rank == m_master_io_rank) {
810 nodes_coordinates.resize(nb_to_read);
811 }
812
813 // Le rang maitre lit les informations des noeuds pour la partie concernée
814 // et les transfère au rang destination
815 if (ios_file) {
816 if (m_is_binary) {
817 ios_file->binaryRead(nodes_coordinates.view());
818 }
819 else {
820 for (Integer i = 0; i < nb_to_read; ++i) {
821 nodes_coordinates[i] = _getReal3();
822 //info() << "I=" << i << " ID=" << nodes_uids[i] << " COORD=" << Real3(nx, ny, nz);
823 }
824 }
825 if (dest_rank != m_master_io_rank) {
826 pm->send(nodes_coordinates, dest_rank);
827 }
828 }
829 else if (my_rank == dest_rank) {
830 pm->recv(nodes_coordinates, m_master_io_rank);
831 }
832
833 // Conserve les informations de ma partie
834 if (my_rank == dest_rank) {
835 m_mesh_info.nodes_coordinates.addRange(nodes_coordinates);
836 }
837 }
838
839 if (!m_is_binary)
840 _goToNextLine();
841}
842
843/*---------------------------------------------------------------------------*/
844/*---------------------------------------------------------------------------*/
850{
851 IosFile* ios_file = m_ios_file.get();
852 IParallelMng* pm = m_parallel_mng;
853 const Int32 my_rank = pm->commRank();
854 const Int64 nb_entity_in_block = block.nb_entity;
855 const Int32 item_nb_node = block.item_nb_node;
856
857 info() << "Reading block nb_entity=" << nb_entity_in_block << " item_nb_node=" << item_nb_node;
858
860 UniqueArray<Int64> connectivities;
861
863
864 for (Int32 i_part = 0; i_part < m_nb_part; ++i_part) {
866 const Int32 dest_rank = m_parts_rank[i_part];
867
868 info() << "Reading block part i_part=" << i_part
869 << " nb_to_read=" << nb_to_read << " dest_rank=" << dest_rank;
870
871 const Int64 nb_uid = nb_to_read;
872 const Int64 nb_connectivity = nb_uid * item_nb_node;
873 if (my_rank == dest_rank || my_rank == m_master_io_rank) {
874 uids.resize(nb_uid);
875 connectivities.resize(nb_connectivity);
876 }
877 if (ios_file) {
878 if (m_is_binary) {
879 Int64 nb_to_read = nb_uid * (item_nb_node + 1);
881 ios_file->binaryRead(tmp_uid_and_connectivities.view());
882 Int64 index = 0;
883 for (Int64 i = 0; i < nb_uid; ++i) {
885 ++index;
886 uids[i] = item_unique_id;
887 for (Int32 j = 0; j < item_nb_node; ++j) {
888 connectivities[(i * item_nb_node) + j] = tmp_uid_and_connectivities[index];
889 ++index;
890 }
891 }
892 }
893 else {
894 // Utilise des Int64 pour garantir qu'on ne déborde pas.
895 for (Int64 i = 0; i < nb_uid; ++i) {
896 Int64 item_unique_id = ios_file->getInt64();
897 uids[i] = item_unique_id;
898 for (Int32 j = 0; j < item_nb_node; ++j)
899 connectivities[(i * item_nb_node) + j] = ios_file->getInt64();
900 }
901 }
902 if (dest_rank != m_master_io_rank) {
903 pm->send(uids, dest_rank);
904 pm->send(connectivities, dest_rank);
905 }
906 }
907 else if (my_rank == dest_rank) {
908 pm->recv(uids, m_master_io_rank);
909 pm->recv(connectivities, m_master_io_rank);
910 }
911 if (my_rank == dest_rank) {
912 block.uids.addRange(uids);
913 block.connectivities.addRange(connectivities);
914 }
915 }
916}
917
918/*---------------------------------------------------------------------------*/
919/*---------------------------------------------------------------------------*/
950{
951 IosFile* ios_file = m_ios_file.get();
952 IParallelMng* pm = m_parallel_mng;
953
955 _getInt64ArrayAndBroadcast(elements_info.view());
956
957 Int64 nb_block = elements_info[0];
961
962 if (!m_is_binary)
963 _goToNextLine();
964
965 info() << "[Elements] nb_block=" << nb_block
966 << " nb_elements=" << number_of_elements
967 << " min_element_tag=" << min_element_tag
968 << " max_element_tag=" << max_element_tag;
969
970 if (number_of_elements < 0)
971 ARCANE_THROW(IOException, "Invalid number of elements: {0}", number_of_elements);
972
973 UniqueArray<MeshV4ElementsBlock>& blocks = m_mesh_info.blocks;
974 blocks.resize(nb_block);
975
976 {
977 // Numérote les blocs (pour le débug)
978 Integer index = 0;
979 for (MeshV4ElementsBlock& block : blocks) {
980 block.index = index;
981 ++index;
982 }
983 }
984
985 for (MeshV4ElementsBlock& block : blocks) {
986
988 _getInt32ArrayAndBroadcast(block_info.view());
989
990 Int32 entity_dim = block_info[0];
991 Int32 entity_tag = block_info[1];
992 Int32 entity_type = block_info[2];
993 Int64 nb_entity_in_block = _getInt64AndBroadcast();
994
995 Integer item_nb_node = 0;
996 ItemTypeId item_type(_switchMshType(entity_type, item_nb_node));
997
998 info() << "[Elements] index=" << block.index << " entity_dim=" << entity_dim
999 << " entity_tag=" << entity_tag
1000 << " entity_type=" << entity_type << " nb_in_block=" << nb_entity_in_block
1001 << " item_type=" << item_type << " item_nb_node=" << item_nb_node;
1002
1003 block.nb_entity = nb_entity_in_block;
1004 block.item_type = item_type;
1005 block.item_nb_node = item_nb_node;
1006 block.dimension = entity_dim;
1007 block.entity_tag = entity_tag;
1008
1009 if (entity_type == MSH_PNT) {
1010 // Si le type est un point, le traitement semble un peu particulier.
1011 // Il y a dans ce cas deux entiers dans la ligne suivante:
1012 // - un entier qui ne semble pas être utilisé
1013 // - le numéro unique du noeud qui nous intéresse
1014 Int64 item_unique_id = NULL_ITEM_UNIQUE_ID;
1015 if (ios_file) {
1016 [[maybe_unused]] Int64 unused_id = _getInt64();
1017 item_unique_id = _getInt64();
1018 info() << "Adding unique node uid=" << item_unique_id;
1019 }
1020 if (m_is_parallel)
1021 pm->broadcast(ArrayView<Int64>(1, &item_unique_id), m_master_io_rank);
1022 block.uids.add(item_unique_id);
1023 }
1024 else {
1025 _readOneElementBlock(block);
1026 }
1027 if (!m_is_binary)
1028 _goToNextLine();
1029 }
1030
1031 if (m_is_binary)
1032 _goToNextLine();
1033
1034 // Maintenant qu'on a tous les blocs, la dimension du maillage est
1035 // la plus grande dimension des blocs
1036 Integer mesh_dimension = -1;
1037 for (const MeshV4ElementsBlock& block : blocks)
1038 mesh_dimension = math::max(mesh_dimension, block.dimension);
1039 if (mesh_dimension < 0)
1040 ARCANE_FATAL("Invalid computed mesh dimension '{0}'", mesh_dimension);
1041 if (mesh_dimension != 2 && mesh_dimension != 3)
1042 ARCANE_THROW(NotSupportedException, "mesh dimension '{0}'. Only 2D or 3D meshes are supported", mesh_dimension);
1043 info() << "Computed mesh dimension = " << mesh_dimension;
1044
1045 bool allow_multi_dim_cell = m_mesh->meshKind().isNonManifold();
1046 bool use_experimental_type_for_cell = false;
1048 // Par défaut utilise les nouveaux types.
1050 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_USE_EXPERIMENTAL_CELL_TYPE", true))
1051 use_experimental_type_for_cell = (v.value() != 0);
1052 }
1053 info() << "Use experiemental cell type?=" << use_experimental_type_for_cell;
1054 for (MeshV4ElementsBlock& block : blocks) {
1055 const Int32 block_dim = block.dimension;
1057 _computeOwnItems(block, m_mesh_info.cells_infos, false);
1058 else if (allow_multi_dim_cell) {
1059 if (block_dim == (mesh_dimension - 1)) {
1061 // Ici on va créer des mailles 2D dans un maillage 3D.
1062 // On converti le type de base en un type équivalent pour les mailles.
1063 if (block.item_type == IT_Triangle3)
1064 block.item_type = ItemTypeId(IT_Cell3D_Triangle3);
1065 else if (block.item_type == IT_Quad4)
1066 block.item_type = ItemTypeId(IT_Cell3D_Quad4);
1067 else
1068 ARCANE_FATAL("Not supported sub dimension cell type={0}", block.item_type);
1069 }
1070 block.is_built_as_cells = true;
1071 _computeOwnItems(block, m_mesh_info.cells_infos, false);
1072 }
1073 }
1074 }
1075
1076 return mesh_dimension;
1077}
1078
1079/*---------------------------------------------------------------------------*/
1080/*---------------------------------------------------------------------------*/
1081
1082namespace
1083{
1084 template <typename DataType> inline ArrayView<DataType>
1086 UniqueArray<DataType>& work_values, Int32 dest_rank, Int64 size)
1087 {
1088 const Int32 my_rank = pm->commRank();
1089 ArrayView<DataType> view = values.view();
1090 if (my_rank != dest_rank) {
1091 work_values.resize(size);
1092 view = work_values.view();
1093 }
1094 pm->broadcast(view, dest_rank);
1095 return view;
1096 }
1104 template <typename DataType> inline ArrayView<DataType>
1105 _broadcastArray(IParallelMng* pm, UniqueArray<DataType>& values,
1106 UniqueArray<DataType>& work_values, Int32 dest_rank)
1107 {
1108 const Int32 my_rank = pm->commRank();
1109 Int64 size = 0;
1110 // Envoie la taille
1111 if (dest_rank == my_rank)
1112 size = values.size();
1113 pm->broadcast(ArrayView<Int64>(1, &size), dest_rank);
1114 return _broadcastArrayWithSize(pm, values, work_values, dest_rank, size);
1115 }
1116
1117} // namespace
1118
1119/*---------------------------------------------------------------------------*/
1120/*---------------------------------------------------------------------------*/
1121
1122void MshParallelMeshReader::
1123_computeOwnItems(MeshV4ElementsBlock& block, ItemKindInfo& item_kind_info, bool is_generate_uid)
1124{
1125 // On ne conserve que les entités dont le premier noeud appartient à notre rang.
1126
1127 IParallelMng* pm = m_parallel_mng;
1128 const Int32 my_rank = pm->commRank();
1129
1130 const ItemTypeId item_type = block.item_type;
1131 const Int32 item_nb_node = block.item_nb_node;
1132
1133 UniqueArray<Int64> connectivities;
1134 UniqueArray<Int64> uids;
1135 UniqueArray<Int32> nodes_rank;
1136
1137 const Int32 nb_part = m_parts_rank.size();
1138 info() << "Compute own items block_index=" << block.index << " nb_part=" << nb_part;
1139 for (Int32 i_part = 0; i_part < nb_part; ++i_part) {
1140 const Int32 dest_rank = m_parts_rank[i_part];
1141 // Broadcast la i_part-ème partie des uids et connectivités des mailles
1142 ArrayView<Int64> connectivities_view = _broadcastArray(pm, block.connectivities, connectivities, dest_rank);
1143 ArrayView<Int64> uids_view = _broadcastArray(pm, block.uids, uids, dest_rank);
1144
1145 Int32 nb_item = uids_view.size();
1146 nodes_rank.resize(nb_item);
1147 nodes_rank.fill(-1);
1148
1149 // Parcours les entités. Chaque entité appartiendra au rang
1150 // de son premier noeud. Si cette partie correspond à mon rang, alors
1151 // on conserve la maille.
1152 for (Int32 i = 0; i < nb_item; ++i) {
1153 Int64 first_node_uid = connectivities_view[i * item_nb_node];
1154 auto x = m_mesh_info.nodes_rank_map.find(first_node_uid);
1155 if (x == m_mesh_info.nodes_rank_map.end())
1156 // Le noeud n'est pas dans ma liste
1157 continue;
1158 Int32 rank = x->second;
1159 nodes_rank[i] = rank;
1160 }
1161 pm->reduce(Parallel::ReduceMax, nodes_rank);
1162 for (Int32 i = 0; i < nb_item; ++i) {
1163 const Int32 rank = nodes_rank[i];
1164 if (rank != my_rank)
1165 // Le noeud n'est pas dans ma partie
1166 continue;
1167 // Le noeud est chez moi, j'ajoute l'entité à la liste des
1168 // entités que je vais créer.
1169 ConstArrayView<Int64> v = connectivities_view.subView(i * item_nb_node, item_nb_node);
1170 Int64 uid = uids_view[i];
1171 if (is_generate_uid)
1172 // Le uniqueId() sera généré automatiquement
1173 uid = NULL_ITEM_UNIQUE_ID;
1174 item_kind_info.addItem(item_type, uid, v);
1175 }
1176 }
1177}
1178
1179/*---------------------------------------------------------------------------*/
1180/*---------------------------------------------------------------------------*/
1197{
1201
1202 IParallelMng* pm = m_parallel_mng;
1203
1204 const IItemFamily* node_family = m_mesh->nodeFamily();
1206
1207 for (Int32 dest_rank : m_parts_rank) {
1210
1211 Int32 nb_item = uids.size();
1212 local_ids.resize(nb_item);
1213
1214 // Converti les uniqueId() en localId(). S'ils sont non nuls
1215 // c'est que l'entité est dans mon sous-domaine et donc on peut
1216 // positionner sa coordonnée
1217 node_family->itemsUniqueIdToLocalId(local_ids, uids, false);
1218 for (Int32 i = 0; i < nb_item; ++i) {
1220 if (!nid.isNull())
1221 nodes_coord_var[nid] = coords[i];
1222 }
1223 }
1224}
1225
1226/*---------------------------------------------------------------------------*/
1227/*---------------------------------------------------------------------------*/
1228
1229void MshParallelMeshReader::
1230_addFaces()
1231{
1232 IMesh* mesh = m_mesh;
1233 Integer nb_item = m_mesh_info.faces_infos.nb_item;
1234 info() << "Adding faces direct nb_face=" << nb_item;
1235 if (nb_item != 0)
1236 mesh->modifier()->addFaces(nb_item, m_mesh_info.faces_infos.items_infos);
1237}
1238
1239/*---------------------------------------------------------------------------*/
1240/*---------------------------------------------------------------------------*/
1241
1242void MshParallelMeshReader::
1243_allocateCells()
1244{
1245 IMesh* mesh = m_mesh;
1246 Integer nb_elements = m_mesh_info.cells_infos.nb_item;
1247 info() << "nb_of_elements=cells_type.size()=" << nb_elements;
1248 Integer nb_cell_node = m_mesh_info.cells_infos.items_infos.size();
1249 info() << "nb_cell_node=cells_connectivity.size()=" << nb_cell_node;
1250
1251 // Création des mailles
1252 info() << "Building cells, nb_cell=" << nb_elements << " nb_cell_node=" << nb_cell_node;
1253 IPrimaryMesh* pmesh = mesh->toPrimaryMesh();
1254 info() << "## Allocating ##";
1255 pmesh->allocateCells(nb_elements, m_mesh_info.cells_infos.items_infos, false);
1256 info() << "## Ending ##";
1257 // Ajoute ensuite faces si elles peuvent ne pas être connectées au maillage.
1258 if (m_mesh->meshKind().isNonManifold())
1259 _addFaces();
1260 pmesh->endAllocate();
1261 info() << "## Done ##";
1262}
1263
1264/*---------------------------------------------------------------------------*/
1265/*---------------------------------------------------------------------------*/
1266
1267void MshParallelMeshReader::
1268_allocateGroups()
1269{
1270 IMesh* mesh = m_mesh;
1271 Int32 mesh_dim = mesh->dimension();
1272 Int32 face_dim = mesh_dim - 1;
1273 UniqueArray<MeshV4EntitiesWithNodes> entity_list;
1274 UniqueArray<MeshPhysicalName> physical_name_list;
1275 for (MeshV4ElementsBlock& block : m_mesh_info.blocks) {
1276 entity_list.clear();
1277 physical_name_list.clear();
1278 Int32 block_index = block.index;
1279 Int32 block_dim = block.dimension;
1280 // On alloue un groupe s'il a un nom physique associé.
1281 // Pour cela, il faut déjà qu'il soit associé à une entité.
1282 Int64 block_entity_tag = block.entity_tag;
1283 if (block_entity_tag < 0) {
1284 info(5) << "[Groups] Skipping block index=" << block_index << " because it has no entity";
1285 continue;
1286 }
1287 if (block_dim == 0) {
1288 const MeshV4EntitiesNodes* entity = m_mesh_info.findNodeEntities(block_entity_tag);
1289 if (!entity) {
1290 info(5) << "[Groups] Skipping block index=" << block_index
1291 << " because entity tag is invalid";
1292 continue;
1293 }
1294 Int64 entity_physical_tag = entity->physical_tag;
1295 MeshPhysicalName physical_name = m_mesh_info.physical_name_list.find(block_dim, entity_physical_tag);
1296 physical_name_list.add(physical_name);
1297 }
1298 else {
1299 m_mesh_info.findEntities(block_dim, block_entity_tag, entity_list);
1300 for (const MeshV4EntitiesWithNodes& x : entity_list) {
1301 Int64 entity_physical_tag = x.physical_tag;
1302 MeshPhysicalName physical_name = m_mesh_info.physical_name_list.find(block_dim, entity_physical_tag);
1303 physical_name_list.add(physical_name);
1304 }
1305 }
1306 for (const MeshPhysicalName& physical_name : physical_name_list) {
1307 if (physical_name.isNull()) {
1308 info(5) << "[Groups] Skipping block index=" << block_index
1309 << " because entity physical tag is invalid";
1310 continue;
1311 }
1312 info(4) << "[Groups] Block index=" << block_index << " dim=" << block_dim
1313 << " name='" << physical_name.name << "' built_as_cells=" << block.is_built_as_cells;
1314 if (block_dim == mesh_dim || block.is_built_as_cells) {
1315 _addCellOrNodeGroup(block, physical_name.name, mesh->cellFamily());
1316 }
1317 else if (block_dim == face_dim) {
1318 _addFaceGroup(block, physical_name.name);
1319 }
1320 else {
1321 _addCellOrNodeGroup(block, physical_name.name, mesh->nodeFamily());
1322 }
1323 }
1324 }
1325}
1326
1327/*---------------------------------------------------------------------------*/
1328/*---------------------------------------------------------------------------*/
1334{
1335 IParallelMng* pm = m_parallel_mng;
1336 const Int32 item_nb_node = block.item_nb_node;
1337
1338 UniqueArray<Int64> connectivities;
1339 for (Int32 dest_rank : m_parts_rank) {
1341 _addFaceGroupOnePart(connectivities_view, item_nb_node, group_name, block.index);
1342 }
1343}
1344
1345/*---------------------------------------------------------------------------*/
1346/*---------------------------------------------------------------------------*/
1347
1348void MshParallelMeshReader::
1349_addFaceGroupOnePart(ConstArrayView<Int64> connectivities, Int32 item_nb_node,
1350 const String& group_name, Int32 block_index)
1351{
1352 IMesh* mesh = m_mesh;
1353 const Int32 nb_entity = connectivities.size() / item_nb_node;
1354
1355 // Il peut y avoir plusieurs blocs pour le même groupe.
1356 // On récupère le groupe s'il existe déjà.
1357 FaceGroup face_group = mesh->faceFamily()->findGroup(group_name, true);
1358
1359 UniqueArray<Int32> faces_id; // Numéro de la face dans le maillage \a mesh
1360 faces_id.reserve(nb_entity);
1361
1362 const Int32 face_nb_node = nb_entity * item_nb_node;
1363
1367 Integer faces_nodes_unique_id_index = 0;
1368
1369 UniqueArray<Int64> orig_nodes_id(item_nb_node);
1371
1374
1375 // Réordonne les identifiants des faces retrouver la face dans le maillage.
1376 // Pour cela, on récupère le premier noeud de la face et on regarde s'il
1377 // se trouve dans notre sous-domaine. Si oui, la face sera ajoutée à notre
1378 // partie du maillage
1379 for (Integer i_face = 0; i_face < nb_entity; ++i_face) {
1380 for (Integer z = 0; z < item_nb_node; ++z)
1381 orig_nodes_id[z] = connectivities[faces_nodes_unique_id_index + z];
1382
1383 MeshUtils::reorderNodesOfFace2(orig_nodes_id, face_nodes_index);
1384 for (Integer z = 0; z < item_nb_node; ++z)
1387 faces_nodes_unique_id_index += item_nb_node;
1388 }
1389
1390 node_family->itemsUniqueIdToLocalId(faces_first_node_local_id, faces_first_node_unique_id, false);
1391
1392 faces_nodes_unique_id_index = 0;
1393 for (Integer i_face = 0; i_face < nb_entity; ++i_face) {
1394 const Integer n = item_nb_node;
1395 Int32 face_first_node_lid = faces_first_node_local_id[i_face];
1396 if (face_first_node_lid != NULL_ITEM_LOCAL_ID) {
1397 Int64ConstArrayView face_nodes_id(item_nb_node, &faces_nodes_unique_id[faces_nodes_unique_id_index]);
1398 Node current_node(mesh_nodes[faces_first_node_local_id[i_face]]);
1399 Face face = MeshUtils::getFaceFromNodesUniqueId(current_node, face_nodes_id);
1400
1401 // En parallèle, il est possible que la face ne soit pas dans notre sous-domaine
1402 // même si un de ses noeuds l'est
1403 if (face.null()) {
1404 if (!m_is_parallel) {
1405 OStringStream ostr;
1406 ostr() << "(Nodes:";
1407 for (Integer z = 0; z < n; ++z)
1408 ostr() << ' ' << face_nodes_id[z];
1409 ostr() << " - " << current_node.localId() << ")";
1410 ARCANE_FATAL("INTERNAL: MeshMeshReader face index={0} with nodes '{1}' is not in node/face connectivity",
1411 i_face, ostr.str());
1412 }
1413 }
1414 else
1415 faces_id.add(face.localId());
1416 }
1417
1418 faces_nodes_unique_id_index += n;
1419 }
1420 info(4) << "Adding " << faces_id.size() << " faces from block index=" << block_index
1421 << " to group '" << face_group.name() << "'";
1422 face_group.addItems(faces_id);
1423}
1424
1425/*---------------------------------------------------------------------------*/
1426/*---------------------------------------------------------------------------*/
1432{
1433 IParallelMng* pm = m_parallel_mng;
1434
1435 UniqueArray<Int64> uids;
1436 for (Int32 dest_rank : m_parts_rank) {
1437 ArrayView<Int64> uids_view = _broadcastArray(pm, block.uids, uids, dest_rank);
1438 _addCellOrNodeGroupOnePart(uids_view, group_name, block.index, family);
1439 }
1440}
1441
1442/*---------------------------------------------------------------------------*/
1443/*---------------------------------------------------------------------------*/
1444
1445void MshParallelMeshReader::
1446_addCellOrNodeGroupOnePart(ConstArrayView<Int64> uids, const String& group_name,
1447 Int32 block_index, IItemFamily* family)
1448{
1449 const Int32 nb_entity = uids.size();
1450
1451 // Il peut y avoir plusieurs blocs pour le même groupe.
1452 // On récupère le groupe s'il existe déjà.
1453 ItemGroup group = family->findGroup(group_name, true);
1454
1455 UniqueArray<Int32> items_lid(nb_entity);
1456
1457 family->itemsUniqueIdToLocalId(items_lid, uids, false);
1458
1459 // En parallèle, il est possible que certaines entités du groupe ne soient
1460 // pas dans notre sous-domaine. Il faut les filtrer.
1461 if (m_is_parallel) {
1462 auto items_begin = items_lid.begin();
1463 Int64 new_size = std::remove(items_begin, items_lid.end(), NULL_ITEM_LOCAL_ID) - items_begin;
1464 items_lid.resize(new_size);
1465 }
1466
1467 info() << "Adding " << items_lid.size() << " items from block index=" << block_index
1468 << " to group '" << group_name << "' for family=" << family->name();
1469
1470 group.addItems(items_lid);
1471}
1472
1473/*---------------------------------------------------------------------------*/
1474/*---------------------------------------------------------------------------*/
1475/*
1476 * $PhysicalNames // same as MSH version 2
1477 * numPhysicalNames(ASCII int)
1478 * dimension(ASCII int) physicalTag(ASCII int) "name"(127 characters max)
1479 * ...
1480 * $EndPhysicalNames
1481 */
1482void MshParallelMeshReader::
1483_readPhysicalNames()
1484{
1485 // NOTE: même en binaire, la partie $PhysicalNames est écrite en ASCII.
1486 String quote_mark = "\"";
1487 Int32 nb_name = _getIntegerAndBroadcast();
1488 info() << "nb_physical_name=" << nb_name;
1489
1490 _goToNextLine();
1491
1492 for (Int32 i = 0; i < nb_name; ++i) {
1493 Int32 dim = _getIntegerAndBroadcast();
1494 Int32 tag = _getIntegerAndBroadcast();
1495 String s = _getNextLineAndBroadcast();
1496 if (dim < 0 || dim > 3)
1497 ARCANE_FATAL("Invalid value for physical name dimension dim={0}", dim);
1498 // Les noms des groupes peuvent commencer par des espaces et contiennent
1499 // des guillemets qu'il faut supprimer.
1501 if (s.startsWith(quote_mark))
1502 s = s.substring(1);
1503 if (s.endsWith(quote_mark))
1504 s = s.substring(0, s.length() - 1);
1505 m_mesh_info.physical_name_list.add(dim, tag, s);
1506 info(4) << "[PhysicalName] index=" << i << " dim=" << dim << " tag=" << tag << " name='" << s << "'";
1507 }
1508
1509 String s = _getNextLineAndBroadcast();
1510 if (s != "$EndPhysicalNames")
1511 ARCANE_FATAL("found '{0}' and expected '$EndPhysicalNames'", s);
1512}
1513
1514/*---------------------------------------------------------------------------*/
1515/*---------------------------------------------------------------------------*/
1548{
1549 IosFile* ios_file = m_ios_file.get();
1550
1552 _getInt64ArrayAndBroadcast(nb_dim_item.view());
1553
1554 info(4) << "[Entities] nb_0d=" << nb_dim_item[0] << " nb_1d=" << nb_dim_item[1]
1555 << " nb_2d=" << nb_dim_item[2] << " nb_3d=" << nb_dim_item[3];
1556 // Après le format, on peut avoir les entités mais cela est optionnel
1557 // Si elles sont présentes, on lit le fichier jusqu'à la fin de cette section.
1558 if (!m_is_binary)
1559 _goToNextLine();
1560
1561 for (Int64 i = 0; i < nb_dim_item[0]; ++i) {
1563 info() << "Reading entity index=" << i;
1564 if (ios_file) {
1565 Int32 tag = _getInt32();
1566 Real3 xyz = _getReal3();
1567 Int64 num_physical_tag = _getInt64();
1568 if (num_physical_tag > 1)
1569 ARCANE_FATAL("NotImplemented numPhysicalTag>1 (n={0}, index={1} xyz={2})",
1570 num_physical_tag, i, xyz);
1571
1572 Int32 physical_tag = -1;
1573 if (num_physical_tag == 1)
1574 physical_tag = _getInt32();
1575 info(4) << "[Entities] point tag=" << tag << " pos=" << xyz << " phys_tag=" << physical_tag;
1576
1577 tag_info[0] = tag;
1578 tag_info[1] = physical_tag;
1579 }
1580 m_parallel_mng->broadcast(tag_info.view(), m_master_io_rank);
1581 m_mesh_info.entities_nodes_list.add(MeshV4EntitiesNodes(tag_info[0], tag_info[1]));
1582 if (!m_is_binary)
1583 _goToNextLine();
1584 }
1585
1586 for (Int32 i_dim = 1; i_dim <= 3; ++i_dim)
1587 for (Int32 i = 0; i < nb_dim_item[i_dim]; ++i)
1588 _readOneEntity(i_dim);
1589
1590 // En binaire, il faut aller au début de la ligne suivante.
1591 if (m_is_binary)
1592 _goToNextLine();
1593 _readAndCheck("$EndEntities");
1594}
1595
1596/*---------------------------------------------------------------------------*/
1597/*---------------------------------------------------------------------------*/
1598
1599void MshParallelMeshReader::
1600_readOneEntity(Int32 entity_dim)
1601{
1602 IosFile* ios_file = m_ios_file.get();
1603
1604 // Infos pour les tags
1605 // [0] entity_dim
1606 // [1] tag
1607 // [2] nb_physical_tag
1608 // [3] physical_tag1
1609 // [4] physical_tag2
1610 // [5] ...
1613 if (ios_file) {
1614 Int32 tag = _getInt32();
1615 dim_and_tag_info[1] = tag;
1616 Real3 min_pos = _getReal3();
1617 Real3 max_pos = _getReal3();
1618 Int64 nb_physical_tag = _getInt64();
1619 if (nb_physical_tag >= 124)
1620 ARCANE_FATAL("NotImplemented numPhysicalTag>=124 (n={0})", nb_physical_tag);
1622 for (Int32 z = 0; z < nb_physical_tag; ++z) {
1623 Int32 physical_tag = _getInt32();
1624 dim_and_tag_info[3 + z] = physical_tag;
1625 info(4) << "[Entities] z=" << z << " physical_tag=" << physical_tag;
1626 }
1627 // TODO: Lire les informations numBounding...
1628 Int64 num_bounding_group = _getInt64();
1629 for (Int64 k = 0; k < num_bounding_group; ++k) {
1630 [[maybe_unused]] Int32 group_tag = _getInt32();
1631 }
1632 info(4) << "[Entities] dim=" << entity_dim << " tag=" << tag
1633 << " min_pos=" << min_pos << " max_pos=" << max_pos
1634 << " nb_phys_tag=" << nb_physical_tag;
1635 }
1636
1637 m_parallel_mng->broadcast(dim_and_tag_info.view(), m_master_io_rank);
1638
1639 {
1640 Int32 dim = CheckedConvert::toInt32(dim_and_tag_info[0]);
1641 Int64 tag = dim_and_tag_info[1];
1642 Int64 nb_physical_tag = dim_and_tag_info[2];
1643 for (Int32 z = 0; z < nb_physical_tag; ++z) {
1644 Int64 physical_tag = dim_and_tag_info[3 + z];
1645 info(4) << "[Entities] adding info dim=" << entity_dim << " tag=" << tag
1646 << " physical_tag=" << physical_tag;
1647 m_mesh_info.entities_with_nodes_list[dim - 1].add(MeshV4EntitiesWithNodes(dim, tag, physical_tag));
1648 }
1649 }
1650
1651 if (!m_is_binary)
1652 _goToNextLine();
1653}
1654
1655/*---------------------------------------------------------------------------*/
1656/*---------------------------------------------------------------------------*/
1676{
1677 Int64 nb_link = _getInt64AndBroadcast();
1678 info() << "[Periodic] nb_link=" << nb_link;
1679
1680 // TODO: pour l'instant, tous les rangs conservent les
1681 // données car on suppose qu'il n'y en a pas beaucoup.
1682 // A terme, il faudra aussi distribuer ces informations.
1683 PeriodicInfo& periodic_info = m_mesh_info.m_periodic_info;
1684 periodic_info.m_periodic_list.resize(nb_link);
1685 for (Int64 ilink = 0; ilink < nb_link; ++ilink) {
1686 PeriodicOneInfo& one_info = periodic_info.m_periodic_list[ilink];
1688 _getInt32ArrayAndBroadcast(entity_info.view());
1689
1690 info() << "[Periodic] dim=" << entity_info[0] << " tag=" << entity_info[1]
1691 << " tag_master=" << entity_info[2];
1692 one_info.m_entity_dim = entity_info[0];
1693 one_info.m_entity_tag = entity_info[1];
1694 one_info.m_entity_tag_master = entity_info[2];
1695
1696 Int64 num_affine = _getInt64AndBroadcast();
1697 info() << "[Periodic] num_affine=" << num_affine;
1698 one_info.m_affine_values.resize(num_affine);
1699 _getDoubleArrayAndBroadcast(one_info.m_affine_values);
1700 one_info.m_nb_corresponding_node = CheckedConvert::toInt32(_getInt64AndBroadcast());
1701 info() << "[Periodic] nb_corresponding_node=" << one_info.m_nb_corresponding_node;
1702 one_info.m_corresponding_nodes.resize(one_info.m_nb_corresponding_node * 2);
1703 _getInt64ArrayAndBroadcast(one_info.m_corresponding_nodes);
1704 //info() << "[Periodic] corresponding_nodes=" << corresponding_nodes;
1705 }
1706
1707 _goToNextLine();
1708
1710 if (s != "$EndPeriodic")
1711 ARCANE_FATAL("found '{0}' and expected '$EndPeriodic'", s);
1712}
1713
1714/*---------------------------------------------------------------------------*/
1715/*---------------------------------------------------------------------------*/
1721{
1722 String s;
1723 if (m_is_binary) {
1724 constexpr Int32 MAX_SIZE = 128;
1728 SmallSpan<Byte> bytes_to_read = buf_bytes.span().subSpan(0, read_size);
1729 if (read_size >= MAX_SIZE)
1730 ARCANE_FATAL("local buffer is too small (size={0} max={1})", read_size, MAX_SIZE);
1731 IosFile* f = m_ios_file.get();
1732 if (f) {
1733 f->binaryRead(bytes_to_read);
1734 s = String(bytes_to_read);
1735 info() << "S=" << s;
1736 if (m_is_parallel) {
1737 m_parallel_mng->broadcastString(s, m_master_io_rank);
1738 }
1739 }
1740 _goToNextLine();
1741 }
1742 else {
1744 }
1745 if (s != expected_value)
1746 ARCANE_FATAL("found '{0}' and expected '{1}'", s, expected_value);
1747}
1748
1749/*---------------------------------------------------------------------------*/
1750/*---------------------------------------------------------------------------*/
1753void MshParallelMeshReader::
1754_readMeshFromFile()
1755{
1756 IosFile* ios_file = m_ios_file.get();
1757 IMesh* mesh = m_mesh;
1758 info() << "Reading 'msh' file in parallel";
1759
1760 const int MSH_BINARY_TYPE = 1;
1761
1762 if (ios_file) {
1763 Real version = ios_file->getReal();
1764 if (version != 4.1)
1765 ARCANE_THROW(IOException, "Wrong msh file version '{0}'. Only version '4.1' is supported in parallel", version);
1766 Integer file_type = ios_file->getInteger(); // is an integer equal to 0 in the ASCII file format, equal to 1 for the binary format
1767 if (file_type == MSH_BINARY_TYPE)
1768 m_is_binary = true;
1769 info() << "IsBinary?=" << m_is_binary;
1770 if (m_is_binary)
1771 pwarning() << "MSH reader for binary format is experimental";
1772 Int32 data_size = ios_file->getInteger(); // is an integer equal to the size of the floating point numbers used in the file
1773 ARCANE_UNUSED(data_size);
1774 if (data_size != 8)
1775 ARCANE_FATAL("Only 'size_t' of size '8' is allowed (current size is '{0}')", data_size);
1776 // En binaire, il a un entier à lire qui vaut 1 et qui permet de savoir si on n'est en big/little endian.
1777 if (m_is_binary) {
1778 // Il faut lire jusqu'à la fin de la ligne pour le binaire.
1779 _goToNextLine();
1780 Int32 int_value_one = 0;
1781 ios_file->binaryRead(SmallSpan<Int32>(&int_value_one, 1));
1782 if (int_value_one != 1)
1783 ARCANE_FATAL("Bad endianess for file. Read int as value '{0}' (expected=1)", int_value_one);
1784 }
1785
1786 ios_file->getNextLine(); // Skip current \n\r
1787
1788 // $EndMeshFormat
1789 if (!ios_file->lookForString("$EndMeshFormat"))
1790 ARCANE_THROW(IOException, "$EndMeshFormat not found");
1791 }
1792
1793 // TODO: Les différentes sections ($Nodes, $Entitites, ...) peuvent
1794 // être dans n'importe quel ordre (à part $Nodes qui doit être avant $Elements)
1795 // Faire un méthode qui gère cela.
1796
1797 String next_line = _getNextLineAndBroadcast();
1798 // Noms des groupes
1799 if (next_line == "$PhysicalNames") {
1800 _readPhysicalNames();
1801 next_line = _getNextLineAndBroadcast();
1802 }
1803
1804 // Après le format, on peut avoir les entités mais cela est optionnel
1805 // Si elles sont présentes, on lit le fichier jusqu'à la fin de cette section.
1806 if (next_line == "$Entities") {
1807 _readEntities();
1808 next_line = _getNextLineAndBroadcast();
1809 }
1810 // $Nodes
1811 if (next_line != "$Nodes")
1812 ARCANE_THROW(IOException, "Unexpected string '{0}'. Valid values are '$Nodes'", next_line);
1813
1814 // Fetch nodes number and the coordinates
1816
1817 // $EndNodes
1818 if (ios_file && !ios_file->lookForString("$EndNodes"))
1819 ARCANE_THROW(IOException, "$EndNodes not found");
1820
1821 // $Elements
1822 if (ios_file && !ios_file->lookForString("$Elements"))
1823 ARCANE_THROW(IOException, "$Elements not found");
1824
1825 Int32 mesh_dimension = _readElementsFromFile();
1826
1827 // $EndElements
1828 if (ios_file && !ios_file->lookForString("$EndElements"))
1829 ARCANE_THROW(IOException, "$EndElements not found");
1830
1831 info() << "Computed mesh dimension = " << mesh_dimension;
1832
1833 IPrimaryMesh* pmesh = mesh->toPrimaryMesh();
1834 pmesh->setDimension(mesh_dimension);
1835
1836 info() << "NextLine=" << next_line;
1837
1838 bool is_end = _getIsEndOfFileAndBroadcast();
1839 if (!is_end) {
1840 next_line = _getNextLineAndBroadcast();
1841 // $Periodic
1842 if (next_line == "$Periodic") {
1843 _readPeriodic();
1844 }
1845 }
1846
1847 _allocateCells();
1849 _allocateGroups();
1850}
1851
1852/*---------------------------------------------------------------------------*/
1853/*---------------------------------------------------------------------------*/
1858readMeshFromMshFile(IMesh* mesh, const String& filename)
1859{
1860 info() << "Trying to read in parallel 'msh' file '" << filename;
1861 m_mesh = mesh;
1862 IParallelMng* pm = mesh->parallelMng();
1863 m_parallel_mng = pm;
1864 const Int32 nb_rank = pm->commSize();
1865
1866 // Détermine les rangs qui vont conserver les données
1867 // Il n'est pas obligatoire que tous les rangs participent
1868 // à la conservation des données. L'idéal avec un
1869 // grand nombre de rangs serait qu'un rang sur 2 ou 4 participent.
1870 // Cependant, cela génère alors des partitions vides (pour
1871 // les rangs qui ne participent pas) et cela peut
1872 // faire planter certains partitionneurs (comme ParMetis)
1873 // lorsqu'il y a des partitions vides. Il faudrait d'abord
1874 // corriger ce problème.
1875 m_nb_part = nb_rank;
1877 for (Int32 i = 0; i < m_nb_part; ++i) {
1878 m_parts_rank[i] = i % nb_rank;
1879 }
1880
1881 bool is_master_io = pm->isMasterIO();
1882 Int32 master_io_rank = pm->masterIORank();
1883 m_is_parallel = pm->isParallel();
1884 m_master_io_rank = master_io_rank;
1886 // Seul le rang maître va lire le fichier.
1887 // On vérifie d'abord qu'il est lisible
1888 if (is_master_io) {
1889 bool is_readable = platform::isFileReadable(filename);
1890 info() << "Is file readable ?=" << is_readable;
1891 file_readable[0] = is_readable ? 1 : 0;
1892 if (!is_readable)
1893 error() << "Unable to read file '" << filename << "'";
1894 }
1895 pm->broadcast(file_readable.view(), master_io_rank);
1896 if (file_readable[0] == 0) {
1897 return IMeshReader::RTError;
1898 }
1899
1900 std::ifstream ifile;
1902 if (is_master_io) {
1903 ifile.open(filename.localstr());
1905 }
1906 m_ios_file = ios_file;
1908 if (IosFile::isEqualString(mesh_format_str, "$MeshFormat")) {
1909 _readMeshFromFile();
1910 return IMeshReader::RTOk;
1911 }
1912
1913 info() << "The file does not begin with '$MeshFormat' returning RTError";
1914 return IMeshReader::RTError;
1915}
1916
1917/*---------------------------------------------------------------------------*/
1918/*---------------------------------------------------------------------------*/
1919
1920extern "C++" Ref<IMshMeshReader>
1921createMshParallelMeshReader(ITraceMng* tm)
1922{
1924}
1925
1926/*---------------------------------------------------------------------------*/
1927/*---------------------------------------------------------------------------*/
1928
1929} // End namespace Arcane
1930
1931/*---------------------------------------------------------------------------*/
1932/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Fonctions utilitaires sur le maillage.
Informations sur le type flottant.
Definition Limits.h:48
Interface d'une famille d'entités.
virtual ItemGroup findGroup(const String &name) const =0
Recherche un groupe.
virtual String name() const =0
Nom de la famille.
virtual void itemsUniqueIdToLocalId(Int32ArrayView local_ids, Int64ConstArrayView unique_ids, bool do_fatal=true) const =0
Converti un tableau de numéros uniques en numéros locaux.
virtual IItemFamily * nodeFamily()=0
Retourne la famille des noeuds.
virtual IItemFamily * faceFamily()=0
Retourne la famille des faces.
eReturnType
Types des codes de retour d'une lecture ou écriture.
Definition IMeshReader.h:42
@ RTError
Erreur lors de l'opération.
Definition IMeshReader.h:44
@ RTOk
Opération effectuée avec succès.
Definition IMeshReader.h:43
virtual VariableNodeReal3 & nodesCoordinates()=0
Coordonnées des noeuds.
virtual IParallelMng * parallelMng()=0
Gestionnaire de parallèlisme.
virtual IMeshModifier * modifier()=0
Interface de modification associée.
virtual const MeshKind meshKind() const =0
Caractéristiques du maillage.
virtual IPrimaryMesh * toPrimaryMesh()=0
Retourne l'instance sous la forme d'un IPrimaryMesh.
Exception lorsqu'une erreur d'entrée/sortie est détectée.
Definition IOException.h:32
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual Int32 commRank() const =0
Rang de cette instance dans le communicateur.
virtual void recv(ArrayView< char > values, Int32 rank)=0
virtual bool isMasterIO() const =0
true si l'instance est un gestionnaire maître des entrées/sorties.
virtual Int32 commSize() const =0
Nombre d'instance dans le communicateur.
virtual Integer masterIORank() const =0
Rang de l'instance gérant les entrées/sorties (pour laquelle isMasterIO() est vrai)
virtual bool isParallel() const =0
Retourne true si l'exécution est parallèle.
virtual char reduce(eReduceType rt, char v)=0
Effectue la réduction de type rt sur le réel v et retourne la valeur.
Routines des Lecture/Ecriture d'un fichier.
Definition IosFile.h:33
Groupe d'entités de maillage.
Definition ItemGroup.h:49
const String & name() const
Nom du groupe.
Definition ItemGroup.h:76
void addItems(Int32ConstArrayView items_local_id, bool check_if_present=true)
Ajoute des entités.
Definition ItemGroup.cc:445
Type d'une entité (Item).
Definition ItemTypeId.h:32
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:149
Informations pour créer les entités d'un genre.
Informations sur le maillage créé
std::unordered_map< Int64, Int32 > nodes_rank_map
Tableau associatif (uniqueId(),rang) auquel le noeud appartiendra.
UniqueArray< Int64 > nodes_unique_id
UniqueId() des noeuds de ma partie.
UniqueArray< Real3 > nodes_coordinates
Coordonnées des noeuds de ma partie.
Lecteur de fichiers de maillage au format msh.
bool _getIsEndOfFileAndBroadcast()
Retourne true si on est à la fin du fichier.
void _readPeriodic()
Lecture des informations périodiques.
void _addFaceGroup(MeshV4ElementsBlock &block, const String &group_name)
Ajoute des faces au groupe group_name.
Integer _readElementsFromFile()
Lecture des éléments (mailles,faces,...)
void _readAndCheck(const String &expected_value)
Tente de lire la valeur value.
UniqueArray< Int32 > m_parts_rank
Liste des rangs qui participent à la conservation des données.
void _readEntities()
Lecture des entités.
Int32 m_nb_part
Nombre de partitions pour la lecture des noeuds et blocs.
void _readOneElementBlock(MeshV4ElementsBlock &block)
Lit un bloc d'entité de type 'Element'.
void _readNodesFromFile()
Lecture des noeuds du maillage.
void _setNodesCoordinates()
Positionne les coordonnées des noeuds.
eReturnType readMeshFromMshFile(IMesh *mesh, const String &file_name) override
Lit le maillage contenu dans le fichier filename et le construit dans mesh.
bool m_is_binary
Vrai si le format est binaire.
void _addCellOrNodeGroup(MeshV4ElementsBlock &block, const String &group_name, IItemFamily *family)
Ajoute des faces au groupe group_name.
String _getNextLineAndBroadcast()
Lis la valeur de la prochaine ligne et la broadcast aux autres rangs.
Vue sur les informations des noeuds.
Classe gérant un vecteur de réel de dimension 3.
Definition Real3.h:132
Integer size() const
Nombre d'éléments du vecteur.
Int64 largeSize() const
Nombre d'éléments du vecteur (en 64 bits)
void resize(Int64 s)
Change le nombre d'éléments du tableau à s.
void addRange(ConstReferenceType val, Int64 n)
Ajoute n élément de valeur val à la fin du tableau.
void add(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
Interface du gestionnaire de traces.
Exception lorsqu'une opération n'est pas supportée.
Chaîne de caractères unicode.
static String collapseWhiteSpace(const String &rhs)
Effectue une normalisation des caractères espaces.
Definition String.cc:452
const char * localstr() const
Retourne la conversion de l'instance dans l'encodage UTF-8.
Definition String.cc:227
TraceMessage pwarning() const
TraceMessage error() const
Flot pour un message d'erreur.
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
Int32 toInt32(Int64 v)
Converti un Int64 en un Int32.
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 -*-
ConstArrayView< Int64 > Int64ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:570
Infos d'un bloc pour $Elements pour la version 4.
bool is_built_as_cells
Indique si les entités du bloc sont des mailles.
UniqueArray< Int64 > connectivities
< Liste des uniqueId() du bloc