Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
MshMeshWriter.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/* MshMeshWriter.cc (C) 2000-2025 */
9/* */
10/* Reading/Writing an MSH format file. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/IOException.h"
15#include "arcane/utils/FixedArray.h"
16#include "arcane/utils/Collection.h"
17#include "arcane/utils/ITraceMng.h"
18
19#include "arcane/core/FactoryService.h"
20#include "arcane/core/IMesh.h"
21#include "arcane/core/VariableTypes.h"
22#include "arcane/core/AbstractService.h"
23#include "arcane/core/IMeshWriter.h"
24#include "arcane/core/ItemTypeMng.h"
25#include "arcane/core/SharedVariable.h"
26#include "arcane/core/internal/MshMeshGenerationInfo.h"
27
28#include "arcane/std/internal/IosGmsh.h"
29
30#include <tuple>
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35namespace Arcane
36{
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
40
44class MshMeshWriter
45: public TraceAccessor
46{
47 using MshPeriodicOneInfo = impl::MshMeshGenerationInfo::MshPeriodicOneInfo;
48
49 public:
50
52 class ArcaneToMshTypeInfo
53 {
54 public:
55
56 ArcaneToMshTypeInfo() = default;
57 ArcaneToMshTypeInfo(ItemTypeId iti, Int32 msh_type, ConstArrayView<Int16> reorder_infos)
58 : m_arcane_type(iti)
59 , m_msh_type(msh_type)
60 , m_reorder_infos(reorder_infos)
61 {
62 }
63
64 public:
65
66 ItemTypeId m_arcane_type;
67 Int32 m_msh_type = -1;
68 UniqueArray<Int16> m_reorder_infos;
69 };
70
71 class ItemFamilyWriteInfo
72 : public TraceAccessor
73 {
74 public:
75
76 explicit ItemFamilyWriteInfo(ITraceMng* tm)
77 : TraceAccessor(tm)
78 {
79 }
80 };
81
83 {
84 public:
85
86 Int32 m_dimension = -1;
87 Int32 m_physical_tag = -1;
88 String m_name;
89 };
90 struct EntityInfo
91 {
92 public:
93
94 EntityInfo(Int32 dim, ItemTypeId item_type, Int32 entity_tag)
95 : m_dim(dim)
96 , m_item_type(item_type)
97 , m_entity_tag(entity_tag)
98 {
99 }
100
101 public:
102
103 void setPhysicalTag(Int32 tag, const String& name)
104 {
105 m_physical_tag = tag;
106 m_physical_tag_name = name;
107 }
108
109 public:
110
111 Int32 m_dim = -1;
112 ItemTypeId m_item_type;
113 Int32 m_entity_tag = -1;
114 Int32 m_physical_tag = -1;
115 String m_physical_tag_name;
116 };
117
119 {
120 public:
121
122 void processGroup(ItemGroup group, Int32 base_entity_index);
123
124 public:
125
126 const ItemGroup& group() const { return m_item_group; }
127 ConstArrayView<EntityInfo> entitiesByType() const { return m_entities_by_type; }
128 ConstArrayView<Int32> itemsByType(Int32 item_type) const { return m_items_by_type[item_type]; }
129
130 private:
131
132 ItemGroup m_item_group;
133 UniqueArray<EntityInfo> m_entities_by_type;
134 FixedArray<UniqueArray<Int32>, NB_BASIC_ITEM_TYPE> m_items_by_type;
135 UniqueArray<ItemTypeId> m_existing_items_type;
136 };
137
138 public:
139
140 explicit MshMeshWriter(IMesh* mesh);
141
142 public:
143
144 void writeMesh(const String& file_name);
145
146 private:
147
148 IMesh* m_mesh = nullptr;
149 ItemTypeMng* m_item_type_mng = nullptr;
150
151 // List of physical tags
152 UniqueArray<PhysicalTagInfo> m_physical_tags;
153
154 // Number of entities by dimension
155 FixedArray<Int32, 4> m_nb_entities_by_dim;
156
158 std::vector<std::unique_ptr<ItemGroupWriteInfo>> m_groups_write_info_list;
159
160 impl::MshMeshGenerationInfo* m_mesh_info = nullptr;
161 bool m_has_periodic_info = false;
162
165
166 private:
167
168 bool _writeMeshToFileV4(IMesh* mesh, const String& file_name);
169 std::pair<Int64, Int64> _getFamilyMinMaxUniqueId(IItemFamily* family);
170 void _addGroupsToProcess(IItemFamily* family, Array<ItemGroup>& items_groups);
171 void _writeEntities(std::ostream& ofile);
172 void _writeNodes(std::ostream& ofile);
173 void _writeElements(std::ostream& ofile, Int64 total_nb_cell);
174 void _writePeriodic(std::ostream& ofile);
175 void _initTypes();
176 void _addArcaneTypeInfo(ItemTypeId arcane_type, Int32 msh_type, ConstArrayView<Int16> reorder_infos = {});
177 const ArcaneToMshTypeInfo& arcaneToMshTypeInfo(ItemTypeId arcane_type) const;
178};
179
180/*---------------------------------------------------------------------------*/
181/*---------------------------------------------------------------------------*/
182
183MshMeshWriter::
184MshMeshWriter(IMesh* mesh)
185: TraceAccessor(mesh->traceMng())
186, m_mesh(mesh)
187{
188 _initTypes();
189}
190
191/*---------------------------------------------------------------------------*/
192/*---------------------------------------------------------------------------*/
193
194void MshMeshWriter::ItemGroupWriteInfo::
195processGroup(ItemGroup group, Int32 base_entity_index)
196{
197 m_item_group = group;
198 String group_name = group.name();
199 bool is_all_items = group.isAllItems();
200 IItemFamily* family = group.itemFamily();
201 IMesh* mesh = family->mesh();
202 ItemTypeMng* item_type_mng = mesh->itemTypeMng();
203 ITraceMng* tm = family->traceMng();
204
205 // For GMSH, cells must be sorted by their type (Triangle, Quadrangle, ...)
206 // We create an MSH entity per Arcane entity type.
207
208 ENUMERATE_ (Item, iitem, group) {
209 Item item = *iitem;
210 Int16 item_type = item.type();
211 if (item_type >= NB_BASIC_ITEM_TYPE || item_type <= 0)
212 ARCANE_FATAL("Only pre-defined Item type are supported (current item type is '{0}')",
213 item_type_mng->typeFromId(item_type)->typeName());
214 m_items_by_type[item_type].add(item.localId());
215 }
216
217 // Keep predefined types that have elements
218 Int64 total_nb_item = 0;
219 for (Int16 i = 0; i < NB_BASIC_ITEM_TYPE; ++i) {
220 Int64 nb_type = m_items_by_type[i].size();
221 if (nb_type > 0)
222 m_existing_items_type.add(ItemTypeId(i));
223 total_nb_item += nb_type;
224 }
225
226 Int32 nb_existing_type = m_existing_items_type.size();
227 tm->info() << "NbExistingType=" << nb_existing_type;
228 for (Int32 type_index = 0; type_index < nb_existing_type; ++type_index) {
229 ItemTypeId item_type = m_existing_items_type[type_index];
230 ItemTypeInfo* item_type_info = item_type_mng->typeFromId(item_type);
231 Int32 type_dimension = item_type_info->dimension();
232 EntityInfo entity_info(type_dimension, item_type, base_entity_index + type_index);
233 if (!is_all_items)
234 entity_info.setPhysicalTag(base_entity_index + type_index, group_name);
235 m_entities_by_type.add(entity_info);
236 }
237
238 // If the group is empty, a physical tag is still needed so that the
239 // group is created for reading and thus guarantee in parallel that all
240 // subdomains have the same groups.
241 if (nb_existing_type == 0 && !is_all_items) {
242 Int32 mesh_dim = mesh->dimension();
243 eItemKind ik = family->itemKind();
244 Int32 entity_dim = -1;
245 if (ik == IK_Cell)
246 entity_dim = mesh_dim;
247 else if (ik == IK_Face)
248 entity_dim = mesh_dim - 1;
249 else if (ik == IK_Edge)
250 entity_dim = mesh_dim - 2;
251 else
252 ARCANE_FATAL("Invalid item kind '{0}' for entity dimension", entity_dim);
253 // TODO: take a type that corresponds to the dimension
254 EntityInfo entity_info(entity_dim, ITI_Tetraedron4, base_entity_index);
255 entity_info.setPhysicalTag(base_entity_index, group_name);
256 m_entities_by_type.add(entity_info);
257 }
258}
259
260/*---------------------------------------------------------------------------*/
261/*---------------------------------------------------------------------------*/
262
272{
273 bool has_group = false;
274 // Iterate over all groups in the family
275 for (ItemGroup group : family->groups()) {
276 if (group.isAllItems())
277 continue;
278 if (group.isAutoComputed())
279 continue;
280 info() << "Processing ItemGroup group=" << group.name() << " family=" << group.itemFamily()->name();
281 items_groups.add(group);
282 has_group = true;
283 }
284 // If there are no groups in the family, we take the all items group
285 // if the family is the cell family.
286 if (!has_group && (family->itemKind() == IK_Cell))
287 items_groups.add(family->allItems());
288
289 // TODO: if the processed groups do not form a partition and there are
290 // entities not in these groups, they should still be saved in the form of a $Entity
291 // without an associated physical group.
292}
293
294/*---------------------------------------------------------------------------*/
295/*---------------------------------------------------------------------------*/
305writeMesh(const String& file_name)
306{
307 IMesh* mesh = m_mesh;
308 m_item_type_mng = mesh->itemTypeMng();
309 String mesh_file_name(file_name);
310 if (!file_name.endsWith(".msh"))
311 mesh_file_name = mesh_file_name + ".msh";
312 std::ofstream ofile(mesh_file_name.localstr());
313 ofile.precision(20);
314 if (!ofile)
315 ARCANE_THROW(IOException, "Unable to open file '{0}' for writing", mesh_file_name);
316
317 info() << "writing file '" << mesh_file_name << "'";
318
319 m_mesh_info = impl::MshMeshGenerationInfo::getReference(mesh, false);
320 if (m_mesh_info) {
321 m_has_periodic_info = m_mesh_info->m_periodic_info.hasValues();
322 info() << "Mesh has 'MSH' generation info has_periodic=" << m_has_periodic_info;
323 }
324
325 ofile << "$MeshFormat\n";
326 // 4.1 for the format
327 // 0 for ASCII (1 for binary)
328 // 8 for sizeof(size_t)
329 ofile << "4.1 0 " << sizeof(size_t) << "\n";
330 ofile << "$EndMeshFormat\n";
331
332 IItemFamily* cell_family = mesh->cellFamily();
333 IItemFamily* face_family = mesh->faceFamily();
334 CellGroup all_cells = mesh->allCells();
335
336 UniqueArray<ItemGroup> items_groups;
337 _addGroupsToProcess(cell_family, items_groups);
338 _addGroupsToProcess(face_family, items_groups);
339
340 const Int32 entity_index_increment = 1000;
341 Int32 base_entity_index = entity_index_increment;
342 for (ItemGroup group : items_groups) {
343 auto x(std::make_unique<ItemGroupWriteInfo>());
344 x->processGroup(group, base_entity_index);
345 m_groups_write_info_list.emplace_back(std::move(x));
346 base_entity_index += entity_index_increment;
347 }
348
349 // For GMSH, we must start with 'Entities'.
350 // We need one entity per cell type.
351 // We therefore start by calculating the cell types.
352 // For non-manifold meshes, the cells can be of different dimensions
353
354 // Calculates the total number of elements.
355 // All entities that are not dimension 0 are elements.
356 Int64 total_nb_cell = 0;
357 for (const auto& ginfo : m_groups_write_info_list) {
358 for (const EntityInfo& entity_info : ginfo->entitiesByType()) {
359 Int32 dim = entity_info.m_dim;
360 if (dim >= 0)
361 ++m_nb_entities_by_dim[dim];
362 if (dim > 0) {
363 Int32 item_type = entity_info.m_item_type;
364 Int32 nb_item = ginfo->itemsByType(item_type).size();
365 total_nb_cell += nb_item;
366
367 Int32 physical_tag = entity_info.m_physical_tag;
368 if (physical_tag > 0) {
369 m_physical_tags.add(PhysicalTagInfo{ dim, physical_tag, entity_info.m_physical_tag_name });
370 }
371 }
372 }
373 }
374
375 // $PhysicalNames // same as MSH version 2
376 // numPhysicalNames(ASCII int)
377 // dimension(ASCII int) physicalTag(ASCII int) "name"(127 characters max)
378 // ...
379 // $EndPhysicalNames
380
381 {
382 ofile << "$PhysicalNames\n";
383 Int32 nb_tag = m_physical_tags.size();
384 ofile << nb_tag << "\n";
385 for (const PhysicalTagInfo& tag_info : m_physical_tags) {
386 // TODO: check that the name does not exceed 127 characters.
387 ofile << tag_info.m_dimension << " " << tag_info.m_physical_tag << " " << '"' << tag_info.m_name << '"' << "\n";
388 }
389 ofile << "$EndPhysicalNames\n";
390 }
391
392 _writeEntities(ofile);
393 _writeNodes(ofile);
394 _writeElements(ofile, total_nb_cell);
395 _writePeriodic(ofile);
396}
397
398/*---------------------------------------------------------------------------*/
399/*---------------------------------------------------------------------------*/
400
405_writeEntities(std::ostream& ofile)
406{
407 ItemGroup all_nodes = m_mesh->allNodes();
408
409 // $Entities
410 // numPoints(size_t) numCurves(size_t)
411 // numSurfaces(size_t) numVolumes(size_t)
412 // pointTag(int) X(double) Y(double) Z(double)
413 // numPhysicalTags(size_t) physicalTag(int) ...
414 // ...
415 // curveTag(int) minX(double) minY(double) minZ(double)
416 // maxX(double) maxY(double) maxZ(double)
417 // numPhysicalTags(size_t) physicalTag(int) ...
418 // numBoundingPoints(size_t) pointTag(int; sign encodes orientation) ...
419 // ...
420 // surfaceTag(int) minX(double) minY(double) minZ(double)
421 // maxX(double) maxY(double) maxZ(double)
422 // numPhysicalTags(size_t) physicalTag(int) ...
423 // numBoundingCurves(size_t) curveTag(int; sign encodes orientation) ...
424 // ...
425 // volumeTag(int) minX(double) minY(double) minZ(double)
426 // maxX(double) maxY(double) maxZ(double)
427 // numPhysicalTags(size_t) physicalTag(int) ...
428 // numBoundngSurfaces(size_t) surfaceTag(int; sign encodes orientation) ...
429 // ...
430 // $EndEntities
431
432 // We need the bounding box of each entity.
433 // For simplicity, we take the one for the entire mesh, but eventually
434 // it would be better to calculate the correct value directly.
435 const VariableNodeReal3& nodes_coords = m_mesh->nodesCoordinates();
436 Real3 node_min_bounding_box;
437 Real3 node_max_bounding_box;
438 {
439 Real max_value = FloatInfo<Real>::maxValue();
440 Real min_value = -max_value;
441 Real3 min_box(max_value, max_value, max_value);
442 Real3 max_box(min_value, min_value, min_value);
443 ENUMERATE_ (Node, inode, all_nodes) {
444 Real3 pos = nodes_coords[inode];
445 min_box = math::min(min_box, pos);
446 max_box = math::max(max_box, pos);
447 }
448 node_min_bounding_box = min_box;
449 node_max_bounding_box = max_box;
450 }
451 if (m_has_periodic_info)
452 m_nb_entities_by_dim[0] = 1;
453 {
454 ofile << "$Entities\n";
455 ofile << m_nb_entities_by_dim[0] << " " << m_nb_entities_by_dim[1]
456 << " " << m_nb_entities_by_dim[2] << " " << m_nb_entities_by_dim[3] << "\n";
457
458 // If we have periodicity information,
459 // we create a dimension 0 entity so that the periodicity information
460 // can refer to it. We give it tag 1.
461 if (m_has_periodic_info) {
462 ofile << "1 0.0 0.0 0.0 0\n";
463 }
464
465 for (Int32 idim = 1; idim < 4; ++idim) {
466 for (const auto& ginfo : m_groups_write_info_list) {
467 for (const EntityInfo& entity_info : ginfo->entitiesByType()) {
468 if (entity_info.m_dim != idim)
469 continue;
470 ofile << entity_info.m_entity_tag << " " << node_min_bounding_box.x << " " << node_min_bounding_box.y << " " << node_min_bounding_box.z
471 << " " << node_max_bounding_box.x << " " << node_max_bounding_box.y << " " << node_max_bounding_box.z;
472 // No tag for now
473 Int32 physical_tag = entity_info.m_physical_tag;
474 if (physical_tag > 0) {
475 ofile << " 1 " << physical_tag;
476 }
477 else
478 ofile << " 0";
479 // No boundary for now
480 ofile << " 0";
481 ofile << "\n";
482 }
483 }
484 }
485 ofile << "$EndEntities\n";
486 }
487}
488
489/*---------------------------------------------------------------------------*/
490/*---------------------------------------------------------------------------*/
491
496_writeNodes(std::ostream& ofile)
497{
498 const Int32 mesh_nb_node = m_mesh->nbNode();
499 IItemFamily* node_family = m_mesh->nodeFamily();
500 ItemGroup all_nodes = m_mesh->allNodes();
501
502 // $Nodes
503 // numEntityBlocks(size_t) numNodes(size_t)
504 // minNodeTag(size_t) maxNodeTag(size_t)
505 // entityDim(int) entityTag(int) parametric(int; 0 or 1)
506 // numNodesInBlock(size_t)
507 // nodeTag(size_t)
508 // ...
509 // x(double) y(double) z(double)
510 // < u(double; if parametric and entityDim >= 1) >
511 // < v(double; if parametric and entityDim >= 2) >
512 // < w(double; if parametric and entityDim == 3) >
513 // ...
514 // ...
515 // $EndNodes
516
517 // Block containing the nodes
518 ofile << "$Nodes\n";
519
520 auto [node_min_uid, node_max_uid] = _getFamilyMinMaxUniqueId(node_family);
521
522 ofile << "1 " << mesh_nb_node << " " << node_min_uid << " " << node_max_uid << "\n";
523 // entityDim(int) entityTag(int) parametric(int; 0 or 1) numNodesInBlock(size_t)
524 ofile << "0 " << "100 " << "0 " << mesh_nb_node << "\n";
525
526 // Save the uniqueId() of the nodes
527 ENUMERATE_ (Node, inode, all_nodes) {
528 Int64 uid = inode->uniqueId();
529 ofile << uid << "\n";
530 }
531
532 // Save the coordinates
533 VariableNodeReal3& nodes_coords = m_mesh->nodesCoordinates();
534 ENUMERATE_ (Node, inode, all_nodes) {
535 Real3 coord = nodes_coords[inode];
536 ofile << coord.x << " " << coord.y << " " << coord.z << "\n";
537 }
538
539 ofile << "$EndNodes\n";
540}
541
542/*---------------------------------------------------------------------------*/
543/*---------------------------------------------------------------------------*/
544
549_writeElements(std::ostream& ofile, Int64 total_nb_cell)
550{
551 IItemFamily* cell_family = m_mesh->cellFamily();
552
553 // TODO: check if we need to consider the uniqueId() of the faces.
554 auto [cell_min_uid, cell_max_uid] = _getFamilyMinMaxUniqueId(cell_family);
555
556 // $Elements
557 // numEntityBlocks(size_t) numElements(size_t)
558 // minElementTag(size_t) maxElementTag(size_t)
559 // entityDim(int) entityTag(int) elementType(int; see below)
560 // numElementsInBlock(size_t)
561 // elementTag(size_t) nodeTag(size_t) ...
562 // ...
563 // ...
564 // $EndElements
565
566 // Block containing the cells
567 ofile << "$Elements\n";
568
569 Int32 nb_existing_type = m_nb_entities_by_dim[1] + m_nb_entities_by_dim[2] + m_nb_entities_by_dim[3];
570 ofile << nb_existing_type << " " << total_nb_cell << " " << cell_min_uid << " " << cell_max_uid << "\n";
571 for (const auto& ginfo : m_groups_write_info_list) {
572 ItemGroup item_group = ginfo->group();
573 IItemFamily* item_family = item_group.itemFamily();
574 for (const EntityInfo& entity_info : ginfo->entitiesByType()) {
575 ItemTypeId cell_type = entity_info.m_item_type;
576 ConstArrayView<Int32> items_of_current_type = ginfo->itemsByType(cell_type);
577 ItemTypeInfo* item_type_info = m_item_type_mng->typeFromId(cell_type);
578 Int32 type_dimension = entity_info.m_dim;
579 ofile << "\n";
580 const ArcaneToMshTypeInfo& atm_type_info = arcaneToMshTypeInfo(cell_type);
581 ConstArrayView<Int16> reorder_infos = atm_type_info.m_reorder_infos;
582 ofile << type_dimension << " " << entity_info.m_entity_tag << " " << atm_type_info.m_msh_type
583 << " " << items_of_current_type.size() << "\n";
584 info() << "Writing items family=" << item_family->name() << " type=" << item_type_info->typeName()
585 << " n=" << items_of_current_type.size()
586 << " dimension=" << type_dimension;
587 Int32 nb_node_for_type = item_type_info->nbLocalNode();
588 ENUMERATE_ (ItemWithNodes, iitem, item_family->view(items_of_current_type)) {
589 ItemWithNodes item = *iitem;
590 ofile << item.uniqueId();
591 // Handle the possible permutation between MSH numbering and Arcane
592 if (!reorder_infos.empty()) {
593 for (Int32 i = 0; i < nb_node_for_type; ++i)
594 ofile << " " << item.node(reorder_infos[i]).uniqueId();
595 }
596 else {
597 for (Int32 i = 0; i < nb_node_for_type; ++i)
598 ofile << " " << item.node(i).uniqueId();
599 }
600 ofile << "\n";
601 }
602 }
603 }
604 ofile << "$EndElements\n";
605}
606
607/*---------------------------------------------------------------------------*/
608/*---------------------------------------------------------------------------*/
609
610void MshMeshWriter::
611_writePeriodic(std::ostream& ofile)
612{
613 IItemFamily* node_family = m_mesh->nodeFamily();
614
615 // $Periodic
616 // numPeriodicLinks(size_t)
617 // entityDim(int) entityTag(int) entityTagMaster(int)
618 // numAffine(size_t) value(double) ...
619 // numCorrespondingNodes(size_t)
620 // nodeTag(size_t) nodeTagMaster(size_t)
621 // ...
622 // ...
623 // $EndPeriodic
624
625 // Periodicity information is stored in \a m_mesh_info
626 // which does not exist if we are not coming from an MSH mesh.
627 if (!m_has_periodic_info)
628 return;
629 ARCANE_CHECK_POINTER(m_mesh_info);
630
631 ConstArrayView<MshPeriodicOneInfo> periodic_one_infos = m_mesh_info->m_periodic_info.m_periodic_list;
632 Int32 nb_periodic = periodic_one_infos.size();
633 ofile << "$Periodic\n";
634 ofile << nb_periodic << "\n";
635
636 UniqueArray<Int64> corresponding_nodes;
637 UniqueArray<Int32> node_local_ids;
638 ;
639 // Save each periodicity link.
640 for (const MshPeriodicOneInfo& one_info : periodic_one_infos) {
641 // We do not save the entities associated with the link, so we consider
642 // that the entity is of dimension zero and the tags are also zero.
643 ofile << "\n";
644 ofile << "0 1 1\n";
645
646 // Save the associated affine values
647 ConstArrayView<double> affine_values = one_info.m_affine_values;
648 Int32 nb_affine = affine_values.size();
649 ofile << nb_affine;
650 for (Int32 i = 0; i < nb_affine; ++i)
651 ofile << " " << affine_values[i];
652 ofile << "\n";
653
654 // Save the node pairs (slave/master)
655 // We only save the pairs where at least one of the two nodes
656 // is present in our sub-domain.
657 Int32 nb_orig_node = one_info.m_nb_corresponding_node;
658 ConstArrayView<Int64> orig_corresponding_nodes = one_info.m_corresponding_nodes;
659 node_local_ids.resize(nb_orig_node * 2);
660 node_family->itemsUniqueIdToLocalId(node_local_ids, orig_corresponding_nodes, false);
661 corresponding_nodes.reserve(nb_orig_node * 2);
662 corresponding_nodes.clear();
663 for (Int32 i = 0; i < nb_orig_node; ++i) {
664 Int32 slave_index = (i * 2);
665 Int32 master_index = slave_index + 1;
666 bool has_slave = node_local_ids[slave_index] != NULL_ITEM_LOCAL_ID;
667 bool has_master = node_local_ids[master_index] != NULL_ITEM_LOCAL_ID;
668 if (has_slave || has_master) {
669 corresponding_nodes.add(orig_corresponding_nodes[slave_index]);
670 corresponding_nodes.add(orig_corresponding_nodes[master_index]);
671 }
672 }
673 Int32 nb_new_node = corresponding_nodes.size() / 2;
674 ofile << nb_new_node << "\n";
675 for (Int32 i = 0; i < nb_new_node; ++i)
676 ofile << corresponding_nodes[(i * 2)] << " " << corresponding_nodes[(i * 2) + 1] << "\n";
677 }
678
679 ofile << "$EndPeriodic\n";
680}
681
682/*---------------------------------------------------------------------------*/
683/*---------------------------------------------------------------------------*/
684
685std::pair<Int64, Int64> MshMeshWriter::
686_getFamilyMinMaxUniqueId(IItemFamily* family)
687{
688 Int64 min_uid = INT64_MAX;
689 Int64 max_uid = -1;
690 ENUMERATE_ (Item, iitem, family->allItems()) {
691 Item item = *iitem;
692 Int64 uid = item.uniqueId();
693 if (uid < min_uid)
694 min_uid = uid;
695 if (uid > max_uid)
696 max_uid = uid;
697 }
698 return { min_uid, max_uid };
699}
700
701/*---------------------------------------------------------------------------*/
702/*---------------------------------------------------------------------------*/
703
704void MshMeshWriter::
705_initTypes()
706{
707 m_arcane_to_msh_type_infos.resize(NB_BASIC_ITEM_TYPE);
708 // Initialize the types.
709 // This must be done at the beginning of reading and no more types added afterwards.
710 _addArcaneTypeInfo(ITI_Vertex, MSH_PNT);
711 _addArcaneTypeInfo(ITI_Line2, MSH_LIN_2);
712 _addArcaneTypeInfo(ITI_Line3, MSH_LIN_3);
713 _addArcaneTypeInfo(ITI_Line4, MSH_LIN_4);
714 _addArcaneTypeInfo(ITI_Cell3D_Line2, MSH_LIN_2);
715 _addArcaneTypeInfo(ITI_Triangle3, MSH_TRI_3);
716 _addArcaneTypeInfo(ITI_Cell3D_Triangle3, MSH_TRI_3);
717 _addArcaneTypeInfo(ITI_Quad4, MSH_QUA_4);
718 _addArcaneTypeInfo(ITI_Cell3D_Quad4, MSH_QUA_4);
719 _addArcaneTypeInfo(ITI_Tetraedron4, MSH_TET_4);
720 _addArcaneTypeInfo(ITI_Hexaedron8, MSH_HEX_8);
721 _addArcaneTypeInfo(ITI_Pentaedron6, MSH_PRI_6);
722 _addArcaneTypeInfo(ITI_Pyramid5, MSH_PYR_5);
723 _addArcaneTypeInfo(ITI_Triangle6, MSH_TRI_6);
724 _addArcaneTypeInfo(ITI_Triangle10, MSH_TRI_10);
725 {
726 FixedArray<Int16, 10> x({ 0, 1, 2, 3, 4, 5, 6, 7, 9, 8 });
727 _addArcaneTypeInfo(ITI_Tetraedron10, MSH_TET_10, x.view());
728 }
729 {
730 FixedArray<Int16, 20> x({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 16, 9, 17, 10, 18, 19, 12, 15, 13, 14 });
731 _addArcaneTypeInfo(ITI_Hexaedron20, MSH_HEX_20, x.view());
732 }
733}
734
735/*---------------------------------------------------------------------------*/
736/*---------------------------------------------------------------------------*/
737
738void MshMeshWriter::
739_addArcaneTypeInfo(ItemTypeId arcane_type, Int32 msh_type, ConstArrayView<Int16> reorder_infos)
740{
741 if (arcane_type.isNull())
742 ARCANE_FATAL("Null Arcane Type {0}", arcane_type);
743 if (arcane_type >= m_arcane_to_msh_type_infos.size())
744 ARCANE_FATAL("Invalid Arcane type '{0}'", arcane_type);
745 m_arcane_to_msh_type_infos[arcane_type] = ArcaneToMshTypeInfo(arcane_type, msh_type, reorder_infos);
746}
747
748/*---------------------------------------------------------------------------*/
749/*---------------------------------------------------------------------------*/
750
751const MshMeshWriter::ArcaneToMshTypeInfo& MshMeshWriter::
752arcaneToMshTypeInfo(ItemTypeId arcane_type) const
753{
754 if (arcane_type < m_arcane_to_msh_type_infos.size()) {
755 const ArcaneToMshTypeInfo& tx = m_arcane_to_msh_type_infos[arcane_type];
756 if (tx.m_msh_type > 0)
757 return tx;
758 }
759 ARCANE_THROW(NotSupportedException, "Arcane type '{0}' is not supported in MSH writer", arcane_type);
760}
761
762/*---------------------------------------------------------------------------*/
763/*---------------------------------------------------------------------------*/
764
765/*---------------------------------------------------------------------------*/
766/*---------------------------------------------------------------------------*/
767
771class MshMeshWriterService
772: public AbstractService
773, public IMeshWriter
774{
775 public:
776
777 explicit MshMeshWriterService(const ServiceBuildInfo& sbi);
778
779 public:
780
781 void build() override {}
782 bool writeMeshToFile(IMesh* mesh, const String& file_name) override;
783};
784
785/*---------------------------------------------------------------------------*/
786/*---------------------------------------------------------------------------*/
787
788MshMeshWriterService::
789MshMeshWriterService(const ServiceBuildInfo& sbi)
790: AbstractService(sbi)
791{
792}
793
794/*---------------------------------------------------------------------------*/
795/*---------------------------------------------------------------------------*/
796
798writeMeshToFile(IMesh* mesh, const String& file_name)
799{
800 MshMeshWriter writer(mesh);
801 writer.writeMesh(file_name);
802 return false;
803}
804
805/*---------------------------------------------------------------------------*/
806/*---------------------------------------------------------------------------*/
807
808// Obsolete. Use 'MshMeshReader' instead
810 ServiceProperty("MshNewMeshWriter", ST_SubDomain),
812
814 ServiceProperty("MshMeshWriter", ST_SubDomain),
816
817/*---------------------------------------------------------------------------*/
818/*---------------------------------------------------------------------------*/
819
820} // End namespace Arcane
821
822/*---------------------------------------------------------------------------*/
823/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
Integer size() const
Number of elements in the vector.
AbstractService(const ServiceBuildInfo &)
Constructor from a ServiceBuildInfo.
Base class for 1D data vectors.
void resize(Int64 s)
Changes the number of elements in the array to s.
void clear()
Removes the elements from the array.
void add(ConstReferenceType val)
Adds element val to the end of the array.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
Constant view of an array of type T.
constexpr Integer size() const noexcept
Number of elements in the array.
constexpr bool empty() const noexcept
true if the array is empty (size()==0)
Information about the floating-point type.
Definition Limits.h:49
Interface of an entity family.
Definition IItemFamily.h:83
virtual ItemGroupCollection groups() const =0
Collection of groups in this family.
virtual ItemGroup allItems() const =0
Group of all entities.
virtual String name() const =0
Family name.
virtual eItemKind itemKind() const =0
Entity kind.
virtual ItemVectorView view(Int32ConstArrayView local_ids)=0
View on the entities.
virtual void itemsUniqueIdToLocalId(Int32ArrayView local_ids, Int64ConstArrayView unique_ids, bool do_fatal=true) const =0
Converts an array of unique numbers to local numbers.
virtual IItemFamily * nodeFamily()=0
Returns the node family.
Interface of a mesh writing service.
Definition IMeshWriter.h:38
Exception when an input/output error is detected.
Definition IOException.h:34
Mesh entity group.
Definition ItemGroup.h:51
IItemFamily * itemFamily() const
Entity family to which this group belongs (0 for the null group).
Definition ItemGroup.h:128
Type of an entity (Item).
Definition ItemTypeId.h:33
Info on a mesh entity type.
Integer nbLocalNode() const
Number of nodes of the entity.
String typeName() const
Type name.
Mesh entity type manager.
Definition ItemTypeMng.h:66
Mesh element based on nodes (Edge,Face,Cell).
Definition Item.h:773
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
ItemUniqueId uniqueId() const
Unique identifier across all domains.
Definition Item.h:239
Writing the mesh files in msh format.
void build() override
Build-level construction of the service.
bool writeMeshToFile(IMesh *mesh, const String &file_name) override
Writes a mesh to a file.
Information mapping between MSH type and Arcane type.
Writing mesh files in msh format.
void _writeEntities(std::ostream &ofile)
Writes the block containing the entities ($Entities).
void writeMesh(const String &file_name)
Writes in MSH V4 format.
void _writeNodes(std::ostream &ofile)
Writes the block containing the nodes ($Nodes).
void _addGroupsToProcess(IItemFamily *family, Array< ItemGroup > &items_groups)
Determines the list of groups to process for a family.
std::vector< std::unique_ptr< ItemGroupWriteInfo > > m_groups_write_info_list
List of information to write for each group.
UniqueArray< ArcaneToMshTypeInfo > m_arcane_to_msh_type_infos
Information on conversion between Arcane and MSH types for entities.
void _writeElements(std::ostream &ofile, Int64 total_nb_cell)
Writes the block containing the elements ($Elements).
Node of a mesh.
Definition Item.h:598
Class managing a 3-dimensional real vector.
Definition Real3.h:132
Structure containing the information to create a service.
Service creation properties.
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
bool endsWith(const String &s) const
Indicates if the string ends with the characters of s.
Definition String.cc:1095
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
1D data vector with value semantics (STL style).
Brief information about a mesh derived from the 'msh' format.
__host__ __device__ Real2 min(Real2 a, Real2 b)
Returns the minimum of two Real2.
Definition MathUtils.h:346
T max(const T &a, const T &b, const T &c)
Returns the maximum of three elements.
Definition MathUtils.h:407
ItemGroupT< Cell > CellGroup
Group of cells.
Definition ItemTypes.h:184
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
@ ST_SubDomain
The service is used at the subdomain level.
eItemKind
Mesh entity type.
@ IK_Cell
Cell mesh entity.
@ IK_Face
Face mesh entity.
@ IK_Edge
Edge mesh entity.
std::int16_t Int16
Signed integer type of 16 bits.
double Real
Type representing a real number.
std::int32_t Int32
Signed integer type of 32 bits.
Real y
second component of the triplet
Definition Real3.h:36
Real z
third component of the triplet
Definition Real3.h:37
Real x
first component of the triplet
Definition Real3.h:35