Arcane  v4.1.10.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
MEDMeshReaderService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* MEDMeshReaderService.cc (C) 2000-2026 */
9/* */
10/* Lecture d'un maillage au format MED. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/ITraceMng.h"
15#include "arcane/utils/SmallArray.h"
16#include "arcane/utils/FixedArray.h"
17
18#include "arcane/core/IMeshReader.h"
19#include "arcane/core/BasicService.h"
21#include "arcane/core/IPrimaryMesh.h"
22#include "arcane/core/IItemFamily.h"
23#include "arcane/core/ICaseMeshReader.h"
24#include "arcane/core/IMeshBuilder.h"
25#include "arcane/core/IParallelMng.h"
26#include "arcane/core/MeshPartInfo.h"
27#include "arcane/core/NodesOfItemReorderer.h"
29#include "arcane/core/ItemPrinter.h"
30
31#include <med.h>
32#define MESGERR 1
33#include <med_utils.h>
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38namespace Arcane
39{
40
41/*---------------------------------------------------------------------------*/
42/*---------------------------------------------------------------------------*/
43/*!
44 * \brief Lecteur de maillages au format MED.
45 *
46 * Première version d'un lecteur MED gérant uniquement les maillages 2D, 3D et
47 * non structurés.
48 */
49class MEDMeshReader
50: public TraceAccessor
51{
52 public:
53
54 /*!
55 * \brief Informations pour passer des types MED aux types Arcane pour les entités.
56 *
57 * \a indirection() est non nul si la connectivité MED est différente de la
58 * connectivité Arcane, ce qui est le cas pour les entités 2D et 3D.
59 */
60 class MEDToArcaneItemInfo
61 {
62 public:
63
64 MEDToArcaneItemInfo(int dimension, int nb_node, med_int med_type,
65 ItemTypeId arcane_type, const Int32* indirection)
66 : m_dimension(dimension)
67 , m_nb_node(nb_node)
68 , m_med_type(med_type)
69 , m_arcane_type(arcane_type)
70 , m_indirection(indirection)
71 {}
72
73 public:
74
75 int dimension() const { return m_dimension; }
76 int nbNode() const { return m_nb_node; }
77 med_int medType() const { return m_med_type; }
78 Int16 arcaneType() const { return m_arcane_type; }
79 const Int32* indirection() const { return m_indirection; }
80
81 private:
82
83 int m_dimension = -1;
84 int m_nb_node = -1;
85 med_int m_med_type = {};
86 ItemTypeId m_arcane_type = ITI_NullType;
87 const Int32* m_indirection = nullptr;
88 };
89
90 //! Information sur une famille d'entité MED
91 class MEDFamilyInfo
92 {
93 public:
94
95 explicit MEDFamilyInfo(Int32 family_id)
96 : m_family_id(family_id)
97 {}
98
99 public:
100
101 //! Id de la famille pour MED
103 //! Index dans la liste des groupes Arcane.
105 };
106
107 /*!
108 * \brief Liste des groupes et des entités leur appartenant.
109 *
110 * Pour chaque groupe, on peut soit donner la liste des uniqueId()
111 * des entités qui vont être dedans, soit la liste des localId().
112 * Le premier cas est utilisé par les mailles et le second
113 * par les faces et les noeuds
114 */
115 class MEDGroupInfo
116 {
117 public:
118
119 explicit MEDGroupInfo(Int32 index)
120 : m_index(index)
121 {}
122
123 public:
124
125 //! Index du groupe dans la liste des groupes
127 //! Nom des groupes associés
129 //! Liste des uniqueId() des entités du groupe.
131 //! Liste des localId() des entités du groupe.
133 };
134
135 public:
136
137 explicit MEDMeshReader(ITraceMng* tm)
138 : TraceAccessor(tm)
139 {
140 _initMEDToArcaneTypes();
141 }
142
143 public:
144
145 [[nodiscard]] IMeshReader::eReturnType
146 readMesh(IPrimaryMesh* mesh, const String& file_name);
147
148 private:
149
150 IMeshReader::eReturnType _readMesh(IPrimaryMesh* mesh, const String& filename);
151
152 private:
153
154 // Structure pour fermer automatiquement les fichiers MED ouverts
155 struct AutoCloseMED
156 {
157 explicit AutoCloseMED(med_idt id)
158 : fid(id)
159 {}
160 ~AutoCloseMED()
161 {
162 if (fid >= 0)
163 ::MEDfileClose(fid);
164 }
165
166 med_idt fid;
167 };
168
169 //! Maillage en cours de lecture
170 IPrimaryMesh* m_mesh = nullptr;
171 //! Tableau de conversion entre les types MED et Arcane
172 UniqueArray<MEDToArcaneItemInfo> m_med_to_arcane_types;
173 //! Table des index dans \a m_med_to_arcane_type de chaque geotype
174 std::unordered_map<med_int, Int32> m_med_geotype_to_arcane_type_index;
175 //! Liste des familles
176 std::unordered_map<Int32, MEDFamilyInfo> m_med_families_map;
177 //! Liste des informations sur les groupes
178 UniqueArray<MEDGroupInfo> m_med_groups;
179 //! Liste des 'geotype' présents dans le maillage
180 UniqueArray<med_int> m_med_geotypes_in_mesh;
181
182 private:
183
184 Int32 _readItems(med_idt fid, const char* meshnane, const MEDToArcaneItemInfo& iinfo,
185 Array<med_int>& connectivity, Array<med_int>& family_values);
186 void _initMEDToArcaneTypes();
187 void _addTypeInfo(int dimension, int nb_node, med_int med_type, ItemTypeId arcane_type)
188 {
189 _addTypeInfo(dimension, nb_node, med_type, arcane_type, nullptr);
190 }
191 void _addTypeInfo(int dimension, int nb_node, med_int med_type, ItemTypeId arcane_type,
192 const Int32* indirection)
193 {
194 MEDToArcaneItemInfo t(dimension, nb_node, med_type, arcane_type, indirection);
195 Int32 index = m_med_to_arcane_types.size();
196 m_med_to_arcane_types.add(t);
197 m_med_geotype_to_arcane_type_index.insert(std::make_pair(med_type, index));
198 }
199 void _readAndCreateCells(IPrimaryMesh* mesh, Int32 mesh_dimension, med_idt fid, const char* meshname);
200 void _readFaces(IPrimaryMesh* mesh, Int32 mesh_dimension, med_idt fid, const char* meshname);
201
202 [[nodiscard]] IMeshReader::eReturnType
203 _readNodesCoordinates(IPrimaryMesh* mesh, Int64 nb_node, Int32 spacedim,
204 med_idt fid, const char* meshname);
205 void _readFamilies(med_idt fid, const char* meshname);
206 void _readAvailableTypes(med_idt fid, const char* meshname);
207 void _clearItemsInGroups()
208 {
209 for (MEDGroupInfo& g : m_med_groups) {
210 g.m_unique_ids.clear();
211 g.m_local_ids.clear();
212 }
213 }
214 void _broadcastGroups(ConstArrayView<String> names, IItemFamily* family);
215};
216
217/*---------------------------------------------------------------------------*/
218/*---------------------------------------------------------------------------*/
219
220namespace
221{
222 // Les conventions de numérotations de MED sont différentes de celles
223 // utilisées dans Arcane. Ces tableaux permettent d'effectuer la renumérotation.
224 const Int32 Hexaedron8_indirection[] = { 1, 0, 3, 2, 5, 4, 7, 6 };
225 const Int32 Hexaedron20_indirection[] = { 1, 8, 10, 3, 9, 2, 0, 11, 5, 14, 18, 7, 6, 4, 16, 15, 13, 12, 17, 19 };
226 const Int32 Pyramid5_indirection[] = { 1, 0, 3, 2, 4 };
227 const Int32 Quad4_indirection[] = { 1, 0, 3, 2 };
228 const Int32 Quad8_indirection[] = { 1, 0, 3, 2, 5, 4, 7, 6 };
229 const Int32 Triangle3_indirection[] = { 1, 0, 2 };
230 // PAS utilisé pour l'instant. À tester.
231 const Int32 Tetraedron4_indirection[] = { 1, 0, 2, 3 };
232} // namespace
233
234/*---------------------------------------------------------------------------*/
235/*---------------------------------------------------------------------------*/
236
237void MEDMeshReader::
238_initMEDToArcaneTypes()
239{
240 m_med_to_arcane_types.clear();
241
242 // TODO: regarder la correspondance de connectivité entre
243 // Arcane et MED pour les éléments quadratiques
244 // Types 1D
245 _addTypeInfo(1, 2, MED_SEG2, ITI_Line2);
246 _addTypeInfo(1, 3, MED_SEG3, ITI_Line3); // Non supporté
247 _addTypeInfo(1, 4, MED_SEG4, ITI_NullType); // Non supporté
248
249 // Types 2D.
250 _addTypeInfo(2, 3, MED_TRIA3, ITI_Triangle3, Triangle3_indirection);
251 _addTypeInfo(2, 4, MED_QUAD4, ITI_Quad4, Quad4_indirection);
252 _addTypeInfo(2, 6, MED_TRIA6, ITI_NullType); // Non supporté
253 _addTypeInfo(2, 7, MED_TRIA7, ITI_NullType); // Non supporté
254 _addTypeInfo(2, 8, MED_QUAD8, ITI_Quad8, Quad8_indirection);
255 _addTypeInfo(2, 9, MED_QUAD9, ITI_NullType); // Non supporté
256
257 // Types 3D
258 _addTypeInfo(3, 4, MED_TETRA4, ITI_Tetraedron4);
259 _addTypeInfo(3, 5, MED_PYRA5, ITI_Pyramid5, Pyramid5_indirection);
260 _addTypeInfo(3, 6, MED_PENTA6, ITI_Pentaedron6);
261 _addTypeInfo(3, 8, MED_HEXA8, ITI_Hexaedron8, Hexaedron8_indirection);
262 _addTypeInfo(3, 10, MED_TETRA10, ITI_Tetraedron10);
263 _addTypeInfo(3, 12, MED_OCTA12, ITI_Octaedron12);
264 _addTypeInfo(3, 13, MED_PYRA13, ITI_NullType); // Non supporté
265 _addTypeInfo(3, 15, MED_PENTA15, ITI_NullType); // Non supporté
266 _addTypeInfo(3, 18, MED_PENTA18, ITI_NullType); // Non supporté
267 _addTypeInfo(3, 20, MED_HEXA20, ITI_Hexaedron20, Hexaedron20_indirection);
268 _addTypeInfo(3, 27, MED_HEXA27, ITI_NullType); // Non supporté
269
270 // Mailles dont la géométrie à une connectivité variable.
271 // Pour l'instant, on ne supporte aucun de ces types dans Arcane.
272 // On traite quand même ces éléments pour afficher une erreur s'ils sont
273 // présents dans le maillage. En mettant la valeur (0) pour le nombre
274 // de noeuds, on signale à _readItems() qu'on ne sait pas traiter ces éléments.
275 ///
276 _addTypeInfo(2, 0, MED_POLYGON, ITI_NullType);
277 _addTypeInfo(2, 0, MED_POLYGON2, ITI_NullType);
278 _addTypeInfo(3, 0, MED_POLYHEDRON, ITI_NullType);
279
280 // Mailles dont la géométrie est dynamique (découverte du modèle dans le fichier)
281 // TODO: regarder comment les traiter
282 //#define MED_STRUCT_GEO_INTERNAL 600
283 //#define MED_STRUCT_GEO_SUP_INTERNAL 700
284}
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288
289IMeshReader::eReturnType MEDMeshReader::
290readMesh(IPrimaryMesh* mesh, const String& file_name)
291{
292 info() << "Trying to read MED File name=" << file_name;
293 m_mesh = mesh;
294 return _readMesh(mesh, file_name);
295}
296
297/*---------------------------------------------------------------------------*/
298/*---------------------------------------------------------------------------*/
299
300IMeshReader::eReturnType MEDMeshReader::
301_readMesh(IPrimaryMesh* mesh, const String& filename)
302{
303 const med_idt fid = MEDfileOpen(filename.localstr(), MED_ACC_RDONLY);
304 if (fid < 0) {
305 MESSAGE("ERROR: can not open MED file ");
306 error() << "ERROR: can not open MED file '" << filename << "'";
308 }
309 // Pour garantir la fermeture du fichier.
310 AutoCloseMED auto_close_med(fid);
311
312 int nb_mesh = MEDnMesh(fid);
313 if (nb_mesh < 0) {
314 error() << "Error reading number of meshes";
316 }
317 info() << "MED: nb_mesh=" << nb_mesh;
318 if (nb_mesh == 0) {
319 error() << "No mesh is present";
321 }
322
323 // Le maillage qu'on lit est toujours le premier
324 int mesh_index = 1;
325
326 // Récupère la dimension d'espace. Cela est nécessaire pour dimensionner axisname eet unitname
327 int nb_axis = MEDmeshnAxis(fid, mesh_index);
328 if (nb_axis < 0) {
329 error() << "Can not read number of axis (MEDmeshnAxis)";
331 }
332 info() << "MED: nb_axis=" << nb_axis;
333
334 UniqueArray<char> axisname(MED_SNAME_SIZE * nb_axis + 1, '\0');
335 UniqueArray<char> unitname(MED_SNAME_SIZE * nb_axis + 1, '\0');
336
337 char meshname[MED_NAME_SIZE + 1];
338 meshname[0] = '\0';
339 char meshdescription[MED_COMMENT_SIZE + 1];
340 meshdescription[0] = '\0';
341 char dtunit[MED_SNAME_SIZE + 1];
342 dtunit[0] = '\0';
343 med_int spacedim = 0;
344 med_int meshdim = 0;
345 med_mesh_type meshtype = MED_UNDEF_MESH_TYPE;
346 med_sorting_type sortingtype = MED_SORT_UNDEF;
347 med_int nstep = 0;
348 med_axis_type axistype = MED_UNDEF_AXIS_TYPE;
349 int err = 0;
350 err = MEDmeshInfo(fid, mesh_index, meshname, &spacedim, &meshdim, &meshtype, meshdescription,
351 dtunit, &sortingtype, &nstep, &axistype, axisname.data(), unitname.data());
352 if (err < 0) {
353 error() << "Can not read mesh info (MEDmeshInfo) r=" << err;
355 }
356 if (meshtype != MED_UNSTRUCTURED_MESH) {
357 error() << "Arcane handle only MED unstructured mesh (MED_UNSTRUCTURED_MESH) type=" << meshtype;
359 }
360 Integer mesh_dimension = meshdim;
361 if (mesh_dimension != 2 && mesh_dimension != 3)
362 ARCANE_FATAL("MED reader handles only 2D or 3D meshes");
363
364 info() << "MED: name=" << meshname;
365 info() << "MED: description=" << meshdescription;
366 info() << "MED: spacedim=" << spacedim;
367 info() << "MED: meshdim=" << meshdim;
368 info() << "MED: dtunit=" << dtunit;
369 info() << "MED: meshtype=" << meshtype;
370 info() << "MED: sortingtype=" << sortingtype;
371 info() << "MED: axistype=" << axistype;
372 info() << "MED: nstep=" << nstep;
373
374 Int64 nb_node = 0;
375 // Lecture du nombre de noeuds.
376 {
377 med_bool coordinatechangement;
378 med_bool geotransformation;
379 // TODO: traiter les informations telles que coordinatechangement
380 // et geotransformation si besoin
381 med_int med_nb_node = MEDmeshnEntity(fid, meshname, MED_NO_DT, MED_NO_IT, MED_NODE, MED_NO_GEOTYPE,
382 MED_COORDINATE, MED_NO_CMODE, &coordinatechangement,
383 &geotransformation);
384 if (med_nb_node < 0) {
385 error() << "Can not read number of nodes (MEDmeshnEntity) err=" << med_nb_node;
387 }
388 nb_node = med_nb_node;
389 }
390 info() << "MED: nb_node=" << nb_node;
391
392 mesh->setDimension(mesh_dimension);
393
394 IParallelMng* pm = mesh->parallelMng();
395 bool is_parallel = pm->isParallel();
396 Int32 rank = mesh->meshPartInfo().partRank();
397 // En parallèle, seul le rang 0 lit le maillage
398 bool is_read_items = !(is_parallel && rank != 0);
399 if (is_read_items) {
400 _readAvailableTypes(fid, meshname);
401 _readFamilies(fid, meshname);
402 _readAndCreateCells(mesh, mesh_dimension, fid, meshname);
403 }
404 // La méthode IPrimaryMesh::endAllocate() est collective donc tout
405 // le monde doit l'appeler même si les rangs autres que le rang 0
406 // qui n'ont pas de mailles.
407 mesh->endAllocate();
408
409 // Liste des noms des groupes de mailles créées
410 // Elle servira à transférer la liste des groupes à tous les rangs.
411 UniqueArray<String> cell_group_names;
412 IItemFamily* cell_family = mesh->cellFamily();
413 if (is_read_items) {
414 // Maintenant qu'on a créé toutes les mailles, on crée les groupes correspondants
415 // Pour cela, on parcourt toutes les instances de 'm_med_groups' et si une a des entités
416 // alors ce sont des mailles à ajouter à un groupe.
417 // ATTENTION ATTENTION:
418 // NOTE: Les groupes doivent être communs à tous les rangs. Il faut les broadcaster
419 UniqueArray<Int32> cell_local_ids;
420 for (const MEDGroupInfo& g : m_med_groups) {
421 Int32 nb_cell_in_group = g.m_unique_ids.size();
422 cell_local_ids.resize(nb_cell_in_group);
423 cell_family->itemsUniqueIdToLocalId(cell_local_ids, g.m_unique_ids);
424 for (const String& name : g.m_names) {
425 info() << "Group=" << name << " index=" << g.m_index << " nb_item=" << nb_cell_in_group;
426 CellGroup cell_group = cell_family->findGroup(name, true);
427 cell_group.addItems(cell_local_ids);
428 cell_group_names.add(name);
429 }
430 }
431 }
432 _broadcastGroups(cell_group_names, cell_family);
433
434 // Lit les faces
435 if (is_read_items) {
436 // Comme la numérotation des faces n'est pas forcément correcte pour tous
437 // les types d'entités (notamment pour l'ordre 2), on ajoute une option pour
438 // ne pas lire les faces.
439 bool is_face_group_disabled = false;
440 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_MED_DISABLE_FACEGROUP", true))
441 is_face_group_disabled = (v.value());
442 if (!is_face_group_disabled)
443 _readFaces(mesh, mesh_dimension, fid, meshname);
444 }
445
446 UniqueArray<String> face_group_names;
447 IItemFamily* face_family = mesh->faceFamily();
448 // Maintenant ajoute les faces aux groupes.
449 if (is_read_items) {
450 for (const MEDGroupInfo& g : m_med_groups) {
451 Int32 nb_face_in_group = g.m_local_ids.size();
452 info() << "Check Group index=" << g.m_index << " nb_item=" << nb_face_in_group;
453 if (nb_face_in_group == 0)
454 continue;
455 for (const String& name : g.m_names) {
456 info() << "FaceGroup=" << name << " index=" << g.m_index << " nb_item=" << nb_face_in_group;
457 FaceGroup face_group = face_family->findGroup(name, true);
458 face_group.addItems(g.m_local_ids);
459 face_group_names.add(name);
460 }
461 }
462 }
463 _broadcastGroups(face_group_names, face_family);
464
465 if (is_read_items) {
466 // Lit les coordonnées
467 return _readNodesCoordinates(mesh, nb_node, spacedim, fid, meshname);
468 }
469 return IMeshReader::RTOk;
470}
471
472/*---------------------------------------------------------------------------*/
473/*---------------------------------------------------------------------------*/
474/*!
475 * \brief Récupère la liste des types géométriques présents dans le maillage.
476 */
477void MEDMeshReader::
478_readAvailableTypes(med_idt fid, const char* meshname)
479{
480 // Récupère le nombre de types géométriques
481 med_bool coordinatechangement;
482 med_bool geotransformation;
483 med_int nb_geo = MEDmeshnEntity(fid, meshname, MED_NO_DT, MED_NO_IT, MED_CELL, MED_GEO_ALL,
484 MED_CONNECTIVITY, MED_NODAL, &coordinatechangement,
485 &geotransformation);
486 if (nb_geo < 0)
487 ARCANE_FATAL("Can not read number of geometric entities nb_geo={0}", nb_geo);
488 info() << "MED: nb_geotype = " << nb_geo;
489
490 // Boucle sur les types présents
491 for (med_int it = 1; it <= nb_geo; it++) {
492
493 med_geometry_type geotype = MED_GEO_ALL;
494 FixedArray<char, MED_NAME_SIZE + 1> geotype_name;
495
496 /* get geometry type */
497 med_int type_ret = MEDmeshEntityInfo(fid, meshname, MED_NO_DT, MED_NO_IT, MED_CELL, it,
498 geotype_name.data(), &geotype);
499 if (type_ret < 0)
500 ARCANE_FATAL("Can not read informations for geotype index={0} ret={1}", it, type_ret);
501 /* how many cells of type geotype ? */
502 med_int nb_item = MEDmeshnEntity(fid, meshname, MED_NO_DT, MED_NO_IT, MED_CELL, geotype,
503 MED_CONNECTIVITY, MED_NODAL, &coordinatechangement,
504 &geotransformation);
505 if (nb_item < 0)
506 ARCANE_FATAL("Can not read number of items for geotype={0} name={1} ret={2}",
507 geotype, geotype_name.data(), nb_item);
508 info() << "MED: type=" << geotype << " '" << geotype_name.data() << "' nb_item=" << nb_item;
509 m_med_geotypes_in_mesh.add(geotype);
510 }
511}
512
513/*---------------------------------------------------------------------------*/
514/*---------------------------------------------------------------------------*/
515
516void MEDMeshReader::
517_readAndCreateCells(IPrimaryMesh* mesh, Int32 mesh_dimension, med_idt fid, const char* meshname)
518{
519 _clearItemsInGroups();
520
521 // A priori il n'y a pas de uniqueId() pour les entités dans MED (TODO: à vérifier)
522 // Donc on numérote les mailles en commencant par zéro et on incrémente à chaque
523 // maille créée.
524 Int64 cell_unique_id = 0;
525
526 // Alloue les mailles types par type.
527 // Parcours les types disponibles et traite ceux qui correspondent à la dimension
528 // du maillage.
529 for (med_int geotype : m_med_geotypes_in_mesh) {
530 Int32 index_in_list = m_med_geotype_to_arcane_type_index[geotype];
531 const MEDToArcaneItemInfo& iinfo = m_med_to_arcane_types[index_in_list];
532
533 Int32 item_dimension = iinfo.dimension();
534 // On ne traite que les entités de la dimension du maillage.
535 if (item_dimension != mesh_dimension)
536 continue;
537 UniqueArray<med_int> med_connectivity;
538 UniqueArray<med_int> med_family_values;
539 Int32 nb_item = _readItems(fid, meshname, iinfo, med_connectivity, med_family_values);
540 if (nb_item == 0)
541 continue;
542 Int16 arcane_type = iinfo.arcaneType();
543 Int32 nb_item_node = iinfo.nbNode();
544 Int32 nb_family_values = med_family_values.size();
545 if (arcane_type == IT_NullType) {
546 // Indique un type supporté par MED mais pas par Arcane
547 ARCANE_FATAL("MED type '{0}' is not supported by Arcane", iinfo.medType());
548 }
549 Int64 cells_infos_index = 0;
550 Int64 med_connectivity_index = 0;
551 UniqueArray<Int64> cells_infos((2 + nb_item_node) * nb_item);
552 info() << "CELL_INFOS size=" << cells_infos.size() << " nb_item=" << nb_item
553 << " type=" << arcane_type;
554 const Int32* indirection = iinfo.indirection();
555 for (Int32 i = 0; i < nb_item; ++i) {
556 Int64 current_cell_unique_id = cell_unique_id;
557 ++cell_unique_id;
558 cells_infos[cells_infos_index] = arcane_type;
559 ++cells_infos_index;
560 cells_infos[cells_infos_index] = current_cell_unique_id;
561 ++cells_infos_index;
562 // La connectivité dans MED commence à 1 et Arcane à 0.
563 // Il faut donc retrancher 1 de la connectivité donnée par MED.
564 Span<Int64> cinfo_span(cells_infos.span().subspan(cells_infos_index, nb_item_node));
565 Span<med_int> med_cinfo_span(med_connectivity.span().subspan(med_connectivity_index, nb_item_node));
566 if (indirection) {
567 for (Integer k = 0; k < nb_item_node; ++k) {
568 cinfo_span[k] = med_cinfo_span[indirection[k]] - 1;
569 }
570 }
571 else {
572 for (Integer k = 0; k < nb_item_node; ++k)
573 cinfo_span[k] = med_cinfo_span[k] - 1;
574 }
575 if (i < nb_family_values) {
576 // Il y a une famille associée à l'entité
577 med_int f = med_family_values[i];
578 auto x = m_med_families_map.find(f);
579 if (x == m_med_families_map.end()) {
580 ARCANE_FATAL("Can not find family id '{0}' for cell '{1}' of geotype '{2}'",
581 f, i, iinfo.medType());
582 }
583 m_med_groups[x->second.m_index].m_unique_ids.add(current_cell_unique_id);
584 }
585
586 med_connectivity_index += nb_item_node;
587 cells_infos_index += nb_item_node;
588 }
589 mesh->allocateCells(nb_item, cells_infos, false);
590 }
591}
592
593/*---------------------------------------------------------------------------*/
594/*---------------------------------------------------------------------------*/
595/*!
596 * \brief Lit les faces.
597 *
598 * Il n'y a pas besoin de créer explicitement les faces car cela est fait
599 * automatiquement dans Arcane. On se sert donc des faces de MED uniquement
600 * pour ajouter les faces dans les groupes correspondants dans le fichier
601 * de maillage.
602 */
603void MEDMeshReader::
604_readFaces(IPrimaryMesh* mesh, Int32 mesh_dimension, med_idt fid, const char* meshname)
605{
606 _clearItemsInGroups();
607 ItemTypeMng* itm = mesh->itemTypeMng();
608 NodesOfItemReorderer nodes_reorderer(itm);
609
610 IItemFamily* node_family = mesh->nodeFamily();
611 NodeInfoListView mesh_nodes(node_family);
612
613 // Parcours les types disponibles et traite ceux qui correspondent à la dimension
614 // du maillage moins 1.
615 for (med_int geotype : m_med_geotypes_in_mesh) {
616 Int32 index_in_list = m_med_geotype_to_arcane_type_index[geotype];
617 const MEDToArcaneItemInfo& iinfo = m_med_to_arcane_types[index_in_list];
618
619 Int32 item_dimension = iinfo.dimension();
620 // On ne traite que les entités de la dimension du maillage.
621 if (item_dimension != (mesh_dimension - 1))
622 continue;
623 ItemTypeInfo* iti = itm->typeFromId(iinfo.arcaneType());
624 info() << "Reading faces geotype=" << geotype << " arcane_type=" << iinfo.arcaneType()
625 << " " << iti->typeName();
626
627 UniqueArray<med_int> med_connectivity;
628 UniqueArray<med_int> med_family_values;
629 Int32 nb_item = _readItems(fid, meshname, iinfo, med_connectivity, med_family_values);
630 if (nb_item == 0)
631 continue;
632 ItemTypeId arcane_type(iinfo.arcaneType());
633 Int32 nb_item_node = iinfo.nbNode();
634 Int32 nb_family_values = med_family_values.size();
635 if (arcane_type == IT_NullType) {
636 // Indique un type supporté par MED mais pas par Arcane
637 ARCANE_FATAL("MED type '{0}' is not supported by Arcane", iinfo.medType());
638 }
639
640 SmallArray<Int64> orig_nodes_id(nb_item_node);
641 info() << "FACES_INFOS nb_item=" << nb_item << " type=" << arcane_type
642 << " nb_family_values=" << nb_family_values;
643
644 const Int32* indirection = iinfo.indirection();
645 Int64 med_connectivity_index = 0;
646
647 for (Int32 i = 0; i < nb_item; ++i) {
648 // La connectivité dans MED commence à 1 et Arcane à 0.
649 // Il faut donc retrancher 1 de la connectivité donnée par MED.
650 ArrayView<Int64> cinfo_span(orig_nodes_id);
651 Span<med_int> med_cinfo_span(med_connectivity.span().subspan(med_connectivity_index, nb_item_node));
652 if (indirection) {
653 for (Integer k = 0; k < nb_item_node; ++k) {
654 cinfo_span[k] = med_cinfo_span[indirection[k]] - 1;
655 }
656 }
657 else {
658 for (Integer k = 0; k < nb_item_node; ++k)
659 cinfo_span[k] = med_cinfo_span[k] - 1;
660 }
661 med_connectivity_index += nb_item_node;
662 // Recherche la face dans le maillage à partir des uniqueId() triés de ses noeuds
663 nodes_reorderer.reorder(arcane_type, cinfo_span);
664 ConstArrayView<Int64> ordered_nodes = nodes_reorderer.sortedNodes();
665 //info() << "OrigMedNodes=" << med_cinfo_span;
666 //info() << "OrigNodes=" << orig_nodes_id;
667 //info() << "Nodes=" << ordered_nodes;
668 Node first_node(MeshUtils::findOneItem(node_family, ordered_nodes[0]));
669 if (first_node.null())
670 ARCANE_FATAL("Can not find node uid={0} for face index '{1}'", ordered_nodes[0], i);
671 Face face = MeshUtils::getFaceFromNodesUniqueId(first_node, ordered_nodes);
672 if (face.null()) {
673 info() << "ERROR: Can not find face in mesh i=" << i << " nodes=" << ordered_nodes;
674 info() << "List of faces for node=" << ItemPrinter(first_node);
675 for (Face subface : first_node.faces()) {
676 info() << "Face=" << ItemPrinter(subface);
677 for (Node subnode : subface.nodes()) {
678 info() << " Node=" << ItemPrinter(subnode);
679 }
680 }
681 ARCANE_FATAL("Can not find face with nodes=", ordered_nodes);
682 }
683 //info() << "Face=" << ItemPrinter(face);
684
685 // Ajoute la face dans les groupes correspondants
686 if (i < nb_family_values) {
687 // Il y a une famille associée à l'entité
688 med_int f = med_family_values[i];
689 auto x = m_med_families_map.find(f);
690 if (x == m_med_families_map.end()) {
691 ARCANE_FATAL("Can not find family id '{0}' for face '{1}' of geotype '{2}'",
692 f, i, iinfo.medType());
693 }
694 //info() << "Add face to group_index=" << x->second.m_index;
695 m_med_groups[x->second.m_index].m_local_ids.add(face.localId());
696 }
697 }
698 info() << "END_READING_ITEMS";
699 }
700}
701
702/*---------------------------------------------------------------------------*/
703/*---------------------------------------------------------------------------*/
704
705IMeshReader::eReturnType MEDMeshReader::
706_readNodesCoordinates(IPrimaryMesh* mesh, Int64 nb_node, Int32 spacedim,
707 med_idt fid, const char* meshname)
708{
709 const bool do_verbose = false;
710 // Lit les coordonnées des noeuds et positionne les coordonnées dans Arcane
711 UniqueArray<Real3> nodes_coordinates(nb_node);
712 {
713 UniqueArray<med_float> coordinates(nb_node * spacedim);
714 int err = MEDmeshNodeCoordinateRd(fid, meshname, MED_NO_DT, MED_NO_IT, MED_FULL_INTERLACE,
715 coordinates.data());
716 if (err < 0) {
717 error() << "Can not read nodes coordinates err=" << err;
719 }
720
721 if (spacedim == 3) {
722 for (Int64 i = 0; i < nb_node; ++i) {
723 Real3 xyz(coordinates[i * 3], coordinates[(i * 3) + 1], coordinates[(i * 3) + 2]);
724 if (do_verbose)
725 info() << "I=" << i << " XYZ=" << xyz;
726 nodes_coordinates[i] = xyz;
727 }
728 }
729 else if (spacedim == 2) {
730 for (Int64 i = 0; i < nb_node; ++i) {
731 Real3 xyz(coordinates[i * 2], coordinates[(i * 2) + 1], 0.0);
732 if (do_verbose)
733 info() << "I=" << i << " XYZ=" << xyz;
734 nodes_coordinates[i] = xyz;
735 }
736 }
737 else
738 ARCANE_THROW(NotImplementedException, "spacedim!=2 && spacedim!=3");
739 }
740
741 // Positionne les coordonnées
742 {
743 VariableNodeReal3& nodes_coord_var(mesh->nodesCoordinates());
744 ENUMERATE_NODE (inode, mesh->allNodes()) {
745 Node node = *inode;
746 nodes_coord_var[inode] = nodes_coordinates[node.uniqueId()];
747 }
748 }
749 return IMeshReader::RTOk;
750}
751
752/*---------------------------------------------------------------------------*/
753/*---------------------------------------------------------------------------*/
754/*!
755 * \brief Lit les informations des entités d'un type donné.
756 *
757 * Lit les informations des entités dont le type est donné par \a iinfo. Les entités sont
758 * des mailles au sens MED, donc des Edge, Face ou Cell.
759 * En retour, indique le nombre d'entités lues.
760 * \a connectivity contiendra les connectivités pour les entités lues et
761 * \a family_values le tableau pour chaque entité de la famille à laquelle elle
762 * appartient. À noter qu'il est possible que \a family_values soit vide s'il n'y
763 * a pas de famille associée aux entités.
764 */
765Int32 MEDMeshReader::
766_readItems(med_idt fid, const char* meshname, const MEDToArcaneItemInfo& iinfo,
767 Array<med_int>& connectivity, Array<med_int>& family_values)
768{
769 constexpr bool is_verbose = false;
770
771 connectivity.clear();
772 family_values.clear();
773
774 int med_item_type = iinfo.medType();
775 med_bool coordinatechangement;
776 med_bool geotransformation;
777 med_int nb_med_item = ::MEDmeshnEntity(fid, meshname, MED_NO_DT, MED_NO_IT, MED_CELL, med_item_type,
778 MED_CONNECTIVITY, MED_NODAL, &coordinatechangement,
779 &geotransformation);
780 if (nb_med_item < 0)
781 ARCANE_FATAL("Can not read MED med_item_type '{0}' error={1}", med_item_type, nb_med_item);
782
783 info() << "MED: Reading items";
784 info() << "MED: type=" << med_item_type << " nb_item=" << nb_med_item;
785 if (nb_med_item == 0)
786 return 0;
787
788 Int64 nb_node = iinfo.nbNode();
789 if (nb_node == 0)
790 // Indique un élément qu'on ne sait pas traiter.
791 ARCANE_THROW(NotImplementedException, "Reading items with MED type '{0}'", med_item_type);
792
793 connectivity.resize(nb_node * nb_med_item);
794 int err = MEDmeshElementConnectivityRd(fid, meshname, MED_NO_DT, MED_NO_IT, MED_CELL,
795 med_item_type, MED_NODAL, MED_FULL_INTERLACE,
796 connectivity.data());
797 if (err < 0)
798 ARCANE_FATAL("Can not read connectivity MED med_item_type '{0}' error={1}",
799 med_item_type, err);
800
801 if (is_verbose)
802 info() << "CON: " << connectivity;
803 {
804 med_int nb_med_family = MEDmeshnEntity(fid, meshname, MED_NO_DT, MED_NO_IT,
805 MED_CELL, med_item_type, MED_FAMILY_NUMBER, MED_NODAL,
806 &coordinatechangement, &geotransformation);
807 info() << "nb_family=" << nb_med_family;
808 if (nb_med_family < 0)
809 ARCANE_FATAL("Can not read family size for type med_item_type={0} error={1}", med_item_type, nb_med_family);
810 if (nb_med_family > 0) {
811 family_values.resize(nb_med_family);
812 int r = MEDmeshEntityFamilyNumberRd(fid, meshname, MED_NO_DT, MED_NO_IT,
813 MED_CELL, med_item_type, family_values.data());
814 if (r < 0)
815 ARCANE_FATAL("Can not read family values for type med_item_type={0} error={1}", med_item_type, nb_med_family);
816 if (is_verbose)
817 info() << "FAM: " << family_values;
818 }
819 }
820 return nb_med_item;
821}
822
823/*---------------------------------------------------------------------------*/
824/*---------------------------------------------------------------------------*/
825
826void MEDMeshReader::
827_readFamilies(med_idt fid, const char* meshname)
828{
829 FixedArray<char, MED_NAME_SIZE + 1> familyname;
830
831 info() << "Read families";
832
833 // Récupère le nombre de familles
834 med_int nb_family = MEDnFamily(fid, meshname);
835 if (nb_family < 0)
836 ARCANE_FATAL("Can not read number of families (error={0})", nb_family);
837
838 info() << "MED: nb_family= " << nb_family;
839 for (med_int i = 0; i < nb_family; i++) {
840 info() << "MED: Read family i=" << i;
841
842 med_int nb_group = MEDnFamilyGroup(fid, meshname, i + 1);
843 if (nb_group < 0)
844 ARCANE_FATAL("Can not read number of groups for family index={0}", i);
845 info() << "MED: family index=" << i << " nb_group=" << nb_group;
846
847 // Lit les groupes de la famille
848 // Même s'il n'y a pas de groupes associés à la famille on continue
849 // le traitement car les entités peuvent référencer des familles sans groupe.
850
851 // Dans MED, les groupes ont une taille fixe maximale MED_LNAME_SIZE
852 UniqueArray<char> all_group_names(MED_LNAME_SIZE * nb_group + 1);
853 med_int family_number = 0;
854 if (MEDfamilyInfo(fid, meshname, i + 1, familyname.data(), &family_number, all_group_names.data()) < 0)
855 ARCANE_FATAL("Can not read group names from family index={0}", i);
856
857 MEDFamilyInfo med_family(family_number);
858 Int32 group_index = m_med_groups.size();
859 med_family.m_index = group_index;
860 MEDGroupInfo med_group(group_index);
861
862 // Récupère les noms des groupes de la famille
863 for (Int32 z = 0; z < nb_group; ++z) {
864 //info() << " groupname=" << group_names << " number=" << familynumber;
865 SmallSpan<char> med_group_name = all_group_names.smallSpan().subSpan(MED_LNAME_SIZE * z, MED_LNAME_SIZE);
866 // Les groupes dans MED peuvent contenir des caractères non supportés par Arcane.
867 // On les enlève.
868 SmallArray<Byte, MED_LNAME_SIZE + 1> valid_name;
869 Int32 pos = 0;
870 for (; pos < MED_LNAME_SIZE; ++pos) {
871 char c = med_group_name[pos];
872 if (c == '\0')
873 break;
874 if (c == ' ' || c == '_')
875 continue;
876 valid_name.add(static_cast<Byte>(c));
877 }
878 String name(valid_name.view());
879 med_group.m_names.add(name);
880 info() << "Family id=" << family_number << " group='" << name << "'";
881 }
882
883 m_med_families_map.insert(std::make_pair(family_number, med_family));
884 m_med_groups.add(med_group);
885 }
886}
887
888/*---------------------------------------------------------------------------*/
889/*---------------------------------------------------------------------------*/
890/*!
891 * \brief Broadcast les groupes de \a group_names pour la famille \a family.
892 *
893 * La liste des groupes \a group_names est seulement utilisée pour le rang 0.
894 */
895void MEDMeshReader::
896_broadcastGroups(ConstArrayView<String> group_names, IItemFamily* family)
897{
898 IParallelMng* pm = m_mesh->parallelMng();
899
900 Int32 rank = pm->commRank();
901 // S'assure que tous les rangs connaissent les groupes
902 if (rank == 0) {
903 Int32 nb_group = group_names.size();
904 pm->broadcast(ArrayView<Int32>(1, &nb_group), 0);
905 for (String name : group_names)
906 pm->broadcastString(name, 0);
907 }
908 else {
909 Int32 nb_group = 0;
910 pm->broadcast(ArrayView<Int32>(1, &nb_group), 0);
911 String current_group_name;
912 for (Int32 i = 0; i < nb_group; ++i) {
913 pm->broadcastString(current_group_name, 0);
914 CellGroup cell_group = family->findGroup(current_group_name, true);
915 }
916 }
917}
918
919/*---------------------------------------------------------------------------*/
920/*---------------------------------------------------------------------------*/
921
922/*---------------------------------------------------------------------------*/
923/*---------------------------------------------------------------------------*/
924/*!
925 * \brief Service de lecture d'un maillage au format MED.
926 */
927class MEDMeshReaderService
928: public BasicService
929, public IMeshReader
930{
931 public:
932
933 explicit MEDMeshReaderService(const ServiceBuildInfo& sbi)
934 : BasicService(sbi)
935 {}
936
937 public:
938
939 void build() override {}
940 bool allowExtension(const String& str) override
941 {
942 return str == "med";
943 }
945 [[maybe_unused]] const XmlNode& mesh_element,
946 const String& file_name,
947 const String& dir_name,
948 [[maybe_unused]] bool use_internal_partition) override
949 {
950 ARCANE_UNUSED(dir_name);
951 MEDMeshReader reader(traceMng());
952 return reader.readMesh(mesh, file_name);
953 }
954};
955
956/*---------------------------------------------------------------------------*/
957/*---------------------------------------------------------------------------*/
958
959ARCANE_REGISTER_SERVICE(MEDMeshReaderService,
960 ServiceProperty("MEDMeshReader", ST_SubDomain),
961 ARCANE_SERVICE_INTERFACE(IMeshReader));
962
963/*---------------------------------------------------------------------------*/
964/*---------------------------------------------------------------------------*/
965
966/*---------------------------------------------------------------------------*/
967/*---------------------------------------------------------------------------*/
968/*!
969 * \brief Service de lecture d'un maillage au format MED depuis le jeu de données.
970 */
971class MEDCaseMeshReader
972: public AbstractService
973, public ICaseMeshReader
974{
975 public:
976
977 class Builder
978 : public IMeshBuilder
979 {
980 public:
981
982 explicit Builder(ITraceMng* tm, const CaseMeshReaderReadInfo& read_info)
983 : m_trace_mng(tm)
984 , m_read_info(read_info)
985 {}
986
987 public:
988
989 void fillMeshBuildInfo(MeshBuildInfo& build_info) override
990 {
991 ARCANE_UNUSED(build_info);
992 }
994 {
995 MEDMeshReader reader(m_trace_mng);
996 String fname = m_read_info.fileName();
997 m_trace_mng->info() << "MED Reader (ICaseMeshReader) file_name=" << fname;
998 IMeshReader::eReturnType ret = reader.readMesh(pm, fname);
999 if (ret != IMeshReader::RTOk)
1000 ARCANE_FATAL("Can not read MED File");
1001 }
1002
1003 private:
1004
1005 ITraceMng* m_trace_mng;
1006 CaseMeshReaderReadInfo m_read_info;
1007 };
1008
1009 public:
1010
1011 explicit MEDCaseMeshReader(const ServiceBuildInfo& sbi)
1012 : AbstractService(sbi)
1013 {}
1014
1015 public:
1016
1018 {
1019 IMeshBuilder* builder = nullptr;
1020 if (read_info.format() == "med")
1021 builder = new Builder(traceMng(), read_info);
1022 return makeRef(builder);
1023 }
1024};
1025
1026/*---------------------------------------------------------------------------*/
1027/*---------------------------------------------------------------------------*/
1028
1029ARCANE_REGISTER_SERVICE(MEDCaseMeshReader,
1030 ServiceProperty("MEDCaseMeshReader", ST_SubDomain),
1031 ARCANE_SERVICE_INTERFACE(ICaseMeshReader));
1032
1033/*---------------------------------------------------------------------------*/
1034/*---------------------------------------------------------------------------*/
1035
1036} // namespace Arcane
1037
1038/*---------------------------------------------------------------------------*/
1039/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ENUMERATE_NODE(name, group)
Enumérateur générique d'un groupe de noeuds.
Fonctions utilitaires sur le maillage.
Ce fichier contient les différentes fabriques de services et macro pour enregistrer les services.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Classe de base d'un service.
AbstractService(const ServiceBuildInfo &)
Constructeur à partir d'un ServiceBuildInfo.
Classe de base des vecteurs 1D de données.
Informations nécessaires pour la lecture d'un fichier de maillage.
Vue constante d'un tableau de type T.
static std::optional< Int32 > tryParseFromEnvironment(StringView s, bool throw_if_invalid)
Interface du service de lecture du maillage à partir du jeu de données.
Interface d'une famille d'entités.
Definition IItemFamily.h:84
Interface d'un service de création/lecture du maillage.
Interface du service gérant la lecture d'un maillage.
Definition IMeshReader.h:32
eReturnType
Types des codes de retour d'une lecture ou écriture.
Definition IMeshReader.h:37
@ RTError
Erreur lors de l'opération.
Definition IMeshReader.h:39
@ RTOk
Opération effectuée avec succès.
Definition IMeshReader.h:38
Interface du gestionnaire de traces.
void addItems(Int32ConstArrayView items_local_id, bool check_if_present=true)
Ajoute des entités.
Definition ItemGroup.cc:446
Type d'une entité (Item).
Definition ItemTypeId.h:32
void allocateMeshItems(IPrimaryMesh *pm) override
Alloue les entités du maillage géré par ce service.
void fillMeshBuildInfo(MeshBuildInfo &build_info) override
Remplit build_info avec les informations nécessaires pour créer le maillage.
Service de lecture d'un maillage au format MED depuis le jeu de données.
Ref< IMeshBuilder > createBuilder(const CaseMeshReaderReadInfo &read_info) const override
Retourne un builder pour créer et lire le maillage dont les informations sont spécifiées dans read_in...
bool allowExtension(const String &str) override
Vérifie si le service supporte les fichiers avec l'extension str.
void build() override
Construction de niveau build du service.
eReturnType readMeshFromFile(IPrimaryMesh *mesh, const XmlNode &mesh_element, const String &file_name, const String &dir_name, bool use_internal_partition) override
Lit un maillage à partir d'un fichier.
Information sur une famille d'entité MED.
Int32 m_family_id
Id de la famille pour MED.
Int32 m_index
Index dans la liste des groupes Arcane.
Liste des groupes et des entités leur appartenant.
Int32 m_index
Index du groupe dans la liste des groupes.
UniqueArray< Int64 > m_unique_ids
Liste des uniqueId() des entités du groupe.
UniqueArray< String > m_names
Nom des groupes associés.
UniqueArray< Int32 > m_local_ids
Liste des localId() des entités du groupe.
Informations pour passer des types MED aux types Arcane pour les entités.
Lecteur de maillages au format MED.
Paramètres nécessaires à la construction d'un maillage.
Référence à une instance.
Structure contenant les informations pour créer un service.
Chaîne de caractères unicode.
TraceAccessor(ITraceMng *m)
Construit un accesseur via le gestionnaire de trace m.
TraceMessage info() const
Flot pour un message d'information.
TraceMessage error() const
Flot pour un message d'erreur.
ITraceMng * traceMng() const
Gestionnaire de trace.
Vecteur 1D de données avec sémantique par valeur (style STL).
Noeud d'un arbre DOM.
Definition XmlNode.h:51
ItemGroupT< Cell > CellGroup
Groupe de mailles.
Definition ItemTypes.h:183
ItemGroupT< Face > FaceGroup
Groupe de faces.
Definition ItemTypes.h:178
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Grandeur au noeud de type coordonnées.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
@ ST_SubDomain
Le service s'utilise au niveau du sous-domaine.
std::int16_t Int16
Type entier signé sur 16 bits.
unsigned char Byte
Type d'un octet.
Definition BaseTypes.h:43
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Créé une référence sur un pointeur.
std::int32_t Int32
Type entier signé sur 32 bits.