Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
Lima.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/* Lima.cc (C) 2000-2026 */
9/* */
10/* Reading/Writing a file in Lima format. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/Iostream.h"
15#include "arcane/utils/StdHeader.h"
16#include "arcane/utils/HashTableMap.h"
17#include "arcane/utils/ValueConvert.h"
18#include "arcane/utils/ScopedPtr.h"
19#include "arcane/utils/ArcanePrecomp.h"
20#include "arcane/utils/ITraceMng.h"
21#include "arcane/utils/Collection.h"
22#include "arcane/utils/Enumerator.h"
23#include "arcane/utils/PlatformUtils.h"
24#include "arcane/utils/Real3.h"
25#include "arcane/utils/OStringStream.h"
26#include "arcane/utils/CheckedConvert.h"
27#include "arcane/utils/StringBuilder.h"
28
29#include "arcane/core/IMeshReader.h"
30#include "arcane/core/IGhostLayerMng.h"
31#include "arcane/core/ISubDomain.h"
32#include "arcane/core/IIOMng.h"
33#include "arcane/core/IParallelMng.h"
34#include "arcane/core/IPrimaryMesh.h"
35#include "arcane/core/Item.h"
36#include "arcane/core/ItemTypeMng.h"
37#include "arcane/core/ItemGroup.h"
38#include "arcane/core/ArcaneException.h"
39#include "arcane/core/Service.h"
40#include "arcane/core/Timer.h"
42#include "arcane/core/ServiceInfo.h"
43#include "arcane/core/CaseOptionsMain.h"
46#include "arcane/core/VariableTypes.h"
47#include "arcane/core/ServiceBuildInfo.h"
48#include "arcane/core/XmlNodeList.h"
49#include "arcane/core/IXmlDocumentHolder.h"
50#include "arcane/core/IItemFamily.h"
51#include "arcane/core/FactoryService.h"
52#include "arcane/core/IMeshWriter.h"
53#include "arcane/core/AbstractService.h"
54#include "arcane/core/ICaseDocument.h"
55#include "arcane/core/ICaseMeshReader.h"
56#include "arcane/core/IMeshBuilder.h"
57
58#include "arcane/lima/LimaCutInfosReader.h"
59#include "arcane/lima/internal/LimaUtils.h"
60
61#include <Lima/lima++.h>
62
63#include <memory>
64#include <algorithm>
65#include <mutex>
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
69
70namespace Arcane
71{
72
73/*---------------------------------------------------------------------------*/
74/*---------------------------------------------------------------------------*/
75
76namespace
77{
78
79 // Mutex to protect calls to Lima in case MLI2 is used
80 // because this format uses HDF5 which is not thread-safe in most cases
81 // (this depends on the HDF5 compilation options but the thread-safe
82 // version is incompatible with the MPI version and generally we need this
83 // latter)
84 std::mutex global_lima_mutex;
85 class GlobalLimaMutex
86 {
87 public:
88
89 explicit GlobalLimaMutex(bool is_active)
90 : m_is_active(is_active)
91 {
92 if (m_is_active)
93 global_lima_mutex.lock();
94 }
95 ~GlobalLimaMutex()
96 {
97 if (m_is_active)
98 global_lima_mutex.unlock();
99 }
100
101 private:
102
103 bool m_is_active = false;
104 };
105
106} // namespace
107
108/*---------------------------------------------------------------------------*/
109/*---------------------------------------------------------------------------*/
113class LimaMeshBase
114: public TraceAccessor
115{
116 public:
117
118 LimaMeshBase(ISubDomain* sub_domain)
119 : TraceAccessor(sub_domain->traceMng())
120 , m_sub_domain(sub_domain)
121 {}
122 virtual ~LimaMeshBase() {}
123
124 public:
125
126 virtual bool readMesh(Lima::Maillage& lima, IPrimaryMesh* mesh, const String& filename,
127 const String& dir_name, bool use_internal_partition, Real length_multiplier) = 0;
128
129 ISubDomain* subDomain() const { return m_sub_domain; }
130
131 protected:
132 private:
133
134 ISubDomain* m_sub_domain;
135
136 private:
137};
138
139/*---------------------------------------------------------------------------*/
140/*---------------------------------------------------------------------------*/
147template <typename ReaderWrapper>
148class LimaWrapper
149: public LimaMeshBase
150{
151 public:
152
153 LimaWrapper(ISubDomain* sub_domain)
154 : LimaMeshBase(sub_domain)
155 , m_cut_infos_reader(new LimaCutInfosReader(sub_domain->parallelMng()))
156 {}
157
158 ~LimaWrapper()
159 {
160 delete m_cut_infos_reader;
161 }
162
163 public:
164
165 virtual bool readMesh(Lima::Maillage& lima, IPrimaryMesh* mesh, const String& filename,
166 const String& dir_name, bool use_internal_partition, Real length_multiplier);
167
168 private:
169
170 LimaCutInfosReader* m_cut_infos_reader;
171 ReaderWrapper m_wrapper;
172
173 bool _readMesh(Lima::Maillage& lima, IPrimaryMesh* mesh, const String& filename,
174 const String& dir_name, bool use_internal_partition, Real length_multiplier);
175
176 void _getProcList(UniqueArray<Integer>& proc_list, const String& dir_name);
177};
178
179/*---------------------------------------------------------------------------*/
180/*---------------------------------------------------------------------------*/
181
183{
184 public:
185
186 void setLima(const Lima::Maillage& lima_mesh)
187 {
188 m_lima_mesh = lima_mesh;
189 }
190
191 protected:
192
193 Lima::Maillage m_lima_mesh;
194};
195
196/*---------------------------------------------------------------------------*/
197/*---------------------------------------------------------------------------*/
198
201{
202 public:
203
204 typedef Lima::Surface LimaCellGroup;
205 typedef Lima::Polygone LimaCell;
206 typedef Lima::Ligne LimaFaceGroup;
207 typedef Lima::Bras LimaFace;
208
209 public:
210
211 Integer nbCellGroup()
212 {
213 return CheckedConvert::toInteger(m_lima_mesh.nb_surfaces());
214 }
215 Integer nbFaceGroup()
216 {
217 return CheckedConvert::toInteger(m_lima_mesh.nb_lignes());
218 }
219 LimaCellGroup cellGroup(Integer i)
220 {
221 return m_lima_mesh.surface(i);
222 }
223 LimaFaceGroup faceGroup(Integer i)
224 {
225 return m_lima_mesh.ligne(i);
226 }
227 Integer faceGroupNbFace(const LimaFaceGroup& group)
228 {
229 return CheckedConvert::toInteger(group.nb_bras());
230 }
231 LimaFace faceFaceGroup(const LimaFaceGroup& group, Integer i)
232 {
233 return group.bras(i);
234 }
235 Integer cellGroupNbCell(const LimaCellGroup& group)
236 {
237 return CheckedConvert::toInteger(group.nb_polygones());
238 }
239 LimaCell cellCellGroup(const LimaCellGroup& group, Integer i)
240 {
241 return group.polygone(i);
242 }
243 LimaCell cell(Integer i)
244 {
245 return m_lima_mesh.polygone(i);
246 }
247 LimaFace face(Integer i)
248 {
249 return m_lima_mesh.bras(i);
250 }
251 Integer nbCell()
252 {
253 return CheckedConvert::toInteger(m_lima_mesh.nb_polygones());
254 }
255 Integer nbFace()
256 {
257 return CheckedConvert::toInteger(m_lima_mesh.nb_bras());
258 }
259 int limaDimension()
260 {
261 return Lima::D2;
262 }
263 const char* strDimension()
264 {
265 return "2D";
266 }
267 static Integer cellToType(Integer nb_node)
268 {
269 switch (nb_node) {
270 case 3:
271 return IT_Triangle3;
272 case 4:
273 return IT_Quad4;
274 case 5:
275 return IT_Pentagon5;
276 case 6:
277 return IT_Hexagon6;
278 default:
279 break;
280 }
281 return IT_NullType;
282 }
283};
284
285/*---------------------------------------------------------------------------*/
286/*---------------------------------------------------------------------------*/
287
290{
291 public:
292
293 typedef Lima::Volume LimaCellGroup;
294 typedef Lima::Polyedre LimaCell;
295 typedef Lima::Surface LimaFaceGroup;
296 typedef Lima::Polygone LimaFace;
297
298 Integer nbCellGroup()
299 {
300 return CheckedConvert::toInteger(m_lima_mesh.nb_volumes());
301 }
302 Integer nbFaceGroup()
303 {
304 return CheckedConvert::toInteger(m_lima_mesh.nb_surfaces());
305 }
306 LimaCellGroup cellGroup(Integer i)
307 {
308 return m_lima_mesh.volume(i);
309 }
310 LimaFaceGroup faceGroup(Integer i)
311 {
312 return m_lima_mesh.surface(i);
313 }
314 Integer faceGroupNbFace(const LimaFaceGroup& group)
315 {
316 return CheckedConvert::toInteger(group.nb_polygones());
317 }
318 LimaFace faceFaceGroup(const LimaFaceGroup& group, Integer i)
319 {
320 return group.polygone(i);
321 }
322 Integer cellGroupNbCell(const LimaCellGroup& group)
323 {
324 return CheckedConvert::toInteger(group.nb_polyedres());
325 }
326 LimaCell cellCellGroup(const LimaCellGroup& group, Integer i)
327 {
328 return group.polyedre(i);
329 }
330 LimaCell cell(Integer i)
331 {
332 return m_lima_mesh.polyedre(i);
333 }
334 LimaFace face(Integer i)
335 {
336 return m_lima_mesh.polygone(i);
337 }
338 Integer nbCell()
339 {
340 return CheckedConvert::toInteger(m_lima_mesh.nb_polyedres());
341 }
342 Integer nbFace()
343 {
344 return CheckedConvert::toInteger(m_lima_mesh.nb_polygones());
345 }
346 int limaDimension()
347 {
348 return Lima::D3;
349 }
350 const char* strDimension()
351 {
352 return "3D";
353 }
354 static Integer cellToType(Integer nb_node)
355 {
356 switch (nb_node) {
357 case 4:
358 return IT_Tetraedron4;
359 case 5:
360 return IT_Pyramid5;
361 case 6:
362 return IT_Pentaedron6;
363 case 8:
364 return IT_Hexaedron8;
365 case 10:
366 return IT_Heptaedron10;
367 case 12:
368 return IT_Octaedron12;
369 default:
370 break;
371 }
372 return IT_NullType;
373 }
374};
375
376/*---------------------------------------------------------------------------*/
377/*---------------------------------------------------------------------------*/
381class LimaMeshReader
382: public TraceAccessor
383{
384 public:
385
386 LimaMeshReader(ISubDomain* sd)
387 : TraceAccessor(sd->traceMng())
388 , m_sub_domain(sd)
389 {}
390
391 public:
392
393 auto readMesh(IPrimaryMesh* mesh, const String& file_name,
394 const String& dir_name, bool use_internal_partition,
395 bool use_length_unit) -> IMeshReader::eReturnType;
396
397 private:
398
399 ISubDomain* m_sub_domain;
400};
401
402/*---------------------------------------------------------------------------*/
403/*---------------------------------------------------------------------------*/
407class LimaMeshReaderService
408: public AbstractService
409, public IMeshReader
410{
411 public:
412
413 explicit LimaMeshReaderService(const ServiceBuildInfo& sbi);
414
415 public:
416
417 void build() {}
418
419 public:
420
421 bool allowExtension(const String& str) override
422 {
423 return str == "unf" || str == "mli" || str == "mli2" || str == "ice" || str == "uns" || str == "unv";
424 }
425
427 const String& file_name,
428 const String& dir_name, bool use_internal_partition) override;
429 ISubDomain* subDomain() { return m_sub_domain; }
430
431 private:
432
433 ISubDomain* m_sub_domain;
434
435 private:
436};
437
438/*---------------------------------------------------------------------------*/
439/*---------------------------------------------------------------------------*/
440
444
445/*---------------------------------------------------------------------------*/
446/*---------------------------------------------------------------------------*/
447
448LimaMeshReaderService::
449LimaMeshReaderService(const ServiceBuildInfo& sbi)
450: AbstractService(sbi)
451, m_sub_domain(sbi.subDomain())
452{
453}
454
455/*---------------------------------------------------------------------------*/
456/*---------------------------------------------------------------------------*/
457
459readMeshFromFile(IPrimaryMesh* mesh, const XmlNode& mesh_node,
460 const String& filename, const String& dir_name,
461 bool use_internal_partition)
462{
463 ISubDomain* sd = subDomain();
464
465 String case_doc_lang = "en";
466 ICaseDocument* case_doc = sd->caseDocument();
467 if (case_doc)
468 case_doc_lang = case_doc->language();
469
470 // Checks if we want to use the length unit in the mesh file.
471 String use_unit_attr_name = "utilise-unite";
472 String use_unit_str = mesh_node.attrValue(use_unit_attr_name);
473 if (case_doc_lang == "en") {
474 // For compatibility reasons, check if 'utilise-unite' is present
475 // if so, use it. Otherwise, use the English term.
476 if (!use_unit_str.empty()) {
477 warning() << "'utilise-unite' should only be used for French datasets."
478 << "Use 'use-unit' instead";
479 }
480 use_unit_attr_name = "use-unit";
481 }
482 else {
483 // If it is not English and 'utilise-unite' is not found,
484 // try using 'use-unit'. This is necessary to take into account
485 // MeshReaderMng::isUseMeshUnit().
486 if (use_unit_str.null()) {
487 info() << "Attribute '" << use_unit_attr_name << "' is not found. Trying with 'use-unit'";
488 use_unit_attr_name = "use-unit";
489 }
490 }
491
492 // Since Arcane 2.8.0, we default to reading length units if
493 // the environment variable ARCANE_LIMA_DEFAULT_NO_UNIT is defined.
494 bool use_length_unit = true;
495 if (!platform::getEnvironmentVariable("ARCANE_LIMA_DEFAULT_NO_UNIT").null())
496 use_length_unit = false;
497 info() << "Default value for unit usage: " << use_length_unit;
498
499 if (use_unit_str.empty())
500 use_unit_str = mesh_node.attrValue(use_unit_attr_name);
501 info() << "Checking for attribute '" << use_unit_attr_name << "' value='" << use_unit_str << "'";
502 if (!use_unit_str.empty()) {
503 if (use_unit_str == "1" || use_unit_str == "true")
504 use_length_unit = true;
505 else if (use_unit_str == "0" || use_unit_str == "false")
506 use_length_unit = false;
507 else
508 ARCANE_FATAL("Invalid value boolean value '{0}' for '{1}' attribute."
509 " Valid values are '0', '1' 'true' or 'false'",
510 use_unit_str, use_unit_attr_name);
511 }
512
513 info() << "Uses Lima's length unit: " << use_length_unit << " (lang=" << case_doc_lang << ")";
514
515 LimaMeshReader reader(sd);
516 return reader.readMesh(mesh, filename, dir_name, use_internal_partition, use_length_unit);
517}
518
519/*---------------------------------------------------------------------------*/
520/*---------------------------------------------------------------------------*/
521
522IMeshReader::eReturnType LimaMeshReader::
523readMesh(IPrimaryMesh* mesh, const String& filename, const String& dir_name,
524 bool use_internal_partition, bool use_length_unit)
525{
526 if (filename.null() || filename.empty())
528
529 ISubDomain* sd = m_sub_domain;
530 ITimerMng* timer_mng = sd->timerMng();
531 LimaMeshBase* lm = 0;
532 ICaseDocument* case_doc = sd->caseDocument();
533
534 info() << "Lima: use_length_unit=" << use_length_unit
535 << " use_internal_partition=" << use_internal_partition;
536 Real length_multiplier = 0.0;
537 if (use_length_unit) {
538 String code_system;
539 if (case_doc)
540 code_system = case_doc->codeUnitSystem();
541 if (code_system.null() || code_system.empty()) {
542 info() << "No unit system configured. Use MKS unit system.";
543 length_multiplier = 1.0;
544 }
545 else if (code_system == "CGS") {
546 length_multiplier = 100.0;
547 }
548 else if (code_system == "MKS") {
549 length_multiplier = 1.0;
550 }
551 else {
552 ARCANE_FATAL("Unknown unit system '{0}' (valid values are: 'CGS' or 'MKS'", code_system);
553 }
554 }
555
556 std::string loc_file_name = filename.localstr();
557 size_t rpos = loc_file_name.rfind(".mli");
558 size_t rpos2 = loc_file_name.rfind(".mli2");
559 const bool need_mutex = rpos2 != std::string::npos;
560 info() << " FILE_NAME=" << loc_file_name;
561 info() << " RPOS MLI=" << rpos << " s=" << loc_file_name.length();
562 info() << " RPOS MLI2=" << rpos2 << " s=" << loc_file_name.length();
563
564 // If each PE reads the mesh and we use shared memory,
565 // the reading of Lima files will happen concurrently.
566 // Since Lima's 'Malipp' API does not support multi-threading,
567 // we switch to the classic API in this case.
568 // Even with the classic API, there are crashes with certain
569 // versions of Lima (at least 7.11.2) when using the MLI2
570 // format (because it uses HDF5). To avoid any problems, we put a lock
571 // around the read/write in this case.
572 bool has_thread = !use_internal_partition && mesh->parallelMng()->isThreadImplementation();
573 // We cannot use the mali pp API with threads
574 if (!has_thread && use_internal_partition && ((rpos + 4) == loc_file_name.length())) {
575 info() << "Use direct partitioning with mli";
576#ifdef ARCANE_LIMA_HAS_MLI
577 return LimaUtils::_directLimaPartitionMalipp(timer_mng, mesh, filename, length_multiplier);
578#else
579 ARCANE_FATAL("Can not use 'mli' files because Lima is not compiled with 'mli' support");
580#endif
581 }
582 else if (!has_thread && use_internal_partition && ((rpos2 + 5) == loc_file_name.length())) {
583 info() << "Use direct partitioning with mli2";
584#ifdef ARCANE_LIMA_HAS_MLI2
585 return LimaUtils::_directLimaPartitionMalipp2(timer_mng, mesh, filename, length_multiplier);
586#else
587 ARCANE_FATAL("Can not use 'mli2' files because Lima is not compiled with 'mli2' support");
588#endif
589 }
590 else {
591 info() << "Loading Lima file '" << filename << "'";
592
593 const char* version = Lima::lima_version();
594 info() << "Using version " << version << " of Lima";
595
596 Timer time_to_read(sd, "ReadLima", Timer::TimerReal);
597
598 // No specific preparation needed
599 LM_TYPEMASQUE preparation = LM_ORIENTATION | LM_COMPACTE;
600
601 log() << "Starting file read " << filename;
602
603 Lima::Maillage lima(filename.localstr());
604
605 try {
606 {
607 Timer::Sentry sentry(&time_to_read);
608 Timer::Phase t_action(sd, TP_InputOutput);
609 GlobalLimaMutex sc(need_mutex);
610 lima.lire(filename.localstr(), Lima::SUFFIXE, true);
611 //warning() << "Lima preparation removed";
612 lima.preparation_parametrable(preparation);
613 }
614 }
615 catch (const Lima::erreur& ex) {
616 ARCANE_FATAL("Can not read lima file '{0}' error is '{1}'", filename, ex.what());
617 }
618 catch (...) {
619 ARCANE_FATAL("Can not read lima file '{0}'", filename);
620 }
621
622 info() << "Mesh read and preparation time (unit: seconds): "
623 << time_to_read.lastActivationTime();
624 // If the dimension has not yet been set, use the one
625 // provided by Lima.
626 if (mesh->dimension() <= 0) {
627 if (lima.dimension() == Lima::D3) {
628 mesh->setDimension(3);
629 info() << "3D Mesh";
630 }
631 else if (lima.dimension() == Lima::D2) {
632 mesh->setDimension(2);
633 info() << "2D Mesh";
634 }
635 }
636
637 if (mesh->dimension() == 3) {
638 lm = new LimaWrapper<Lima3DReaderWrapper>(sd);
639 }
640 else if (mesh->dimension() == 2) {
641 lm = new LimaWrapper<Lima2DReaderWrapper>(sd);
642 }
643 if (!lm) {
644 log() << "Mesh dimension not recognized by lima";
646 }
647
648 bool ret = lm->readMesh(lima, mesh, filename, dir_name, use_internal_partition, length_multiplier);
649 if (ret)
651
652 // To be done if there are multiple ghost cell layers
653 {
654 Integer nb_ghost_layer = mesh->ghostLayerMng()->nbGhostLayer();
655 if (nb_ghost_layer > 1)
657 }
658
659 delete lm;
660 }
661
662 return IMeshReader::RTOk;
663}
664
665/*---------------------------------------------------------------------------*/
666/*---------------------------------------------------------------------------*/
667
668template <typename ReaderWrapper> bool LimaWrapper<ReaderWrapper>::
669readMesh(Lima::Maillage& lima, IPrimaryMesh* mesh, const String& filename,
670 const String& dir_name, bool use_internal_partition, Real length_multiplier)
671{
672 return _readMesh(lima, mesh, filename, dir_name, use_internal_partition, length_multiplier);
673}
674
675/*---------------------------------------------------------------------------*/
676/*---------------------------------------------------------------------------*/
677
678template <typename ReaderWrapper> bool LimaWrapper<ReaderWrapper>::
679_readMesh(Lima::Maillage& lima, IPrimaryMesh* mesh, const String& file_name,
680 const String& dir_name, bool use_internal_partition, Real length_multiplier)
681{
682 ARCANE_UNUSED(file_name);
683
684 // We must use the mesh's parallelMng, which may be different
685 // from that of the sub-domain, for example if the mesh is sequential.
686 IParallelMng* pm = mesh->parallelMng();
687
688 bool is_parallel = pm->isParallel();
689 Integer sid = pm->commRank();
690
691 Integer mesh_nb_node = 0;
692 Integer nb_edge = 0;
693 Integer lima_nb_face = 0;
694 Integer mesh_nb_cell = 0;
695
696 m_wrapper.setLima(lima);
697
698 mesh_nb_node = CheckedConvert::toInteger(lima.nb_noeuds());
699 mesh_nb_cell = m_wrapper.nbCell(); //lima.nb_polyedres();
700 lima_nb_face = m_wrapper.nbFace(); //lima.nb_polygones();
701 nb_edge = 0; //lima.nb_bras();
702
703 info() << "-- Mesh Information (Internal):";
704 info() << "Number of nodes " << mesh_nb_node;
705 info() << "Number of edges " << nb_edge;
706 info() << "Number of faces " << lima_nb_face;
707 info() << "Number of cells " << mesh_nb_cell;
708 info() << "-- Mesh Information (Lima):";
709 info() << "Number of nodes " << lima.nb_noeuds();
710 info() << "Number of edges " << lima.nb_bras();
711 info() << "Number of polygons " << lima.nb_polygones();
712 info() << "Number of polyhedra " << lima.nb_polyedres();
713 info() << "Number of surfaces " << lima.nb_surfaces();
714 info() << "Number of volumes " << lima.nb_volumes();
715
716 info() << "File length unit: " << lima.unite_longueur();
717 // If 0.0, it indicates that we do not want to use the file unit.
718 // In this case, this corresponds to a multiplier of 1.0
719 if (length_multiplier == 0.0)
720 length_multiplier = 1.0;
721 else
722 length_multiplier *= lima.unite_longueur();
723
724 if (mesh_nb_node == 0) {
725 ARCANE_FATAL("No node in mesh");
726 }
727
728 // ------------------------------------------------------------
729 // -------------------------------- Cell creation
730 // ------------------------------------------------------------
731
732 // Reads the unique IDs of the entities (in parallel)
733 UniqueArray<Int64> nodes_unique_id(mesh_nb_node);
734 UniqueArray<Int64> cells_unique_id(mesh_nb_cell);
735 if (is_parallel && !use_internal_partition && pm->commSize() > 1) {
736 m_cut_infos_reader->readItemsUniqueId(nodes_unique_id, cells_unique_id, dir_name);
737 }
738 else {
739 for (Integer i = 0; i < mesh_nb_node; ++i)
740 nodes_unique_id[i] = i;
741 for (Integer i = 0; i < mesh_nb_cell; ++i)
742 cells_unique_id[i] = i;
743 }
744
745 // For now, leave it as false.
746 // If true, the UIDs are incremented to start at one.
747 bool first_uid_is_one = false;
748 if (!platform::getEnvironmentVariable("ARCANE_LIMA_UNIQUE_ID").null()) {
749 first_uid_is_one = true;
750 info() << "WARNING: UniqueId begin at 1";
751 }
752 if (first_uid_is_one) {
753 for (Integer i = 0; i < mesh_nb_node; ++i)
754 ++nodes_unique_id[i];
755 for (Integer i = 0; i < mesh_nb_cell; ++i)
756 ++cells_unique_id[i];
757 }
758
759 HashTableMapT<Int64, Real3> nodes_coords(mesh_nb_node, true);
760 if (!math::isEqual(length_multiplier, 1.0)) {
761 info() << "Using length multiplier v=" << length_multiplier;
762 for (Integer i = 0; i < mesh_nb_node; ++i) {
763 const Lima::Noeud& node = lima.noeud(i);
764 Real3 coord(node.x(), node.y(), node.z());
765 coord *= length_multiplier;
766 nodes_coords.nocheckAdd(nodes_unique_id[i], coord);
767 }
768 }
769 else {
770 for (Integer i = 0; i < mesh_nb_node; ++i) {
771 const Lima::Noeud& node = lima.noeud(i);
772 Real3 coord(node.x(), node.y(), node.z());
773 nodes_coords.nocheckAdd(nodes_unique_id[i], coord);
774 }
775 }
776
777 Integer cells_infos_index = 0;
778
779 UniqueArray<Integer> cells_filter;
780
781 bool use_own_mesh = false;
782
783 if (is_parallel && pm->commSize() > 1) {
784 use_own_mesh = true;
785 if (use_internal_partition)
786 use_own_mesh = false;
787 }
788
789 typedef typename ReaderWrapper::LimaCellGroup LimaCellGroup;
790 typedef typename ReaderWrapper::LimaCell LimaCell;
791 typedef typename ReaderWrapper::LimaFaceGroup LimaFaceGroup;
792 typedef typename ReaderWrapper::LimaFace LimaFace;
793
794 if (use_own_mesh) {
795 Integer nb = m_wrapper.nbCellGroup();
796 for (Integer i = 0; i < nb; ++i) {
797 const LimaCellGroup& lima_group = m_wrapper.cellGroup(i);
798 std::string group_name = lima_group.nom();
799 if (group_name == "LOCAL" || group_name == "local") {
800 Integer nb_own_cell = m_wrapper.cellGroupNbCell(lima_group);
801 cells_filter.resize(nb_own_cell);
802 for (Integer z = 0; z < nb_own_cell; ++z) {
803 cells_filter[z] = CheckedConvert::toInteger(m_wrapper.cellCellGroup(lima_group, z).id() - 1);
804 }
805 }
806 }
807 }
808 else {
809 if (use_internal_partition && sid != 0) {
810 mesh_nb_cell = 0;
811 mesh_nb_node = 0;
812 lima_nb_face = 0;
813 }
814 cells_filter.resize(mesh_nb_cell);
815 for (Integer i = 0; i < mesh_nb_cell; ++i)
816 cells_filter[i] = i;
817 }
818
819 // Calculate the number of cells/nodes
820 Integer mesh_nb_cell_node = 0;
821 for (Integer j = 0, js = cells_filter.size(); j < js; ++j) {
822 mesh_nb_cell_node += CheckedConvert::toInteger(m_wrapper.cell(cells_filter[j]).nb_noeuds());
823 }
824
825 // Array containing cell info (see IMesh::allocateMesh())
826 UniqueArray<Int64> cells_infos(mesh_nb_cell_node + cells_filter.size() * 2);
827
828 // Fills the array containing mesh info
829 for (Integer i_cell = 0, s_cell = cells_filter.size(); i_cell < s_cell; ++i_cell) {
830
831 Integer cell_indirect_id = cells_filter[i_cell];
832 LimaCell lima_cell = m_wrapper.cell(cell_indirect_id);
833 Integer n = CheckedConvert::toInteger(lima_cell.nb_noeuds());
834
835 Integer ct = ReaderWrapper::cellToType(n);
836 if (ct == IT_NullType)
837 throw UnknownItemTypeException("Lima::readFile: Cell", n, cell_indirect_id);
838 // Stores the cell type
839 cells_infos[cells_infos_index] = ct;
840 ++cells_infos_index;
841 // Stores the unique ID of the cell
842 cells_infos[cells_infos_index] = cells_unique_id[cell_indirect_id];
843 ++cells_infos_index;
844
845 // Fills the list of node numbers for the cell
846 for (Integer z = 0, sz = n; z < sz; ++z) {
847 Int64 node_uid = nodes_unique_id[CheckedConvert::toInteger(lima_cell.noeud(z).id() - 1)];
848 cells_infos[cells_infos_index + z] = node_uid;
849 }
850
851#if 0
852 cout << "CELL LIMA1 " << cells_unique_id[cell_indirect_id] << " ";
853 for( Integer z=0; z<n; ++z )
854 cout << " " << cells_infos[cells_infos_index+z];
855 cout << '\n';
856#endif
857
858 cells_infos_index += n;
859 }
860
861 logdate() << "Début allocation du maillage nb_cell=" << cells_filter.size();
862 mesh->allocateCells(cells_filter.size(), cells_infos, false);
863 logdate() << "Fin allocation du maillage";
864
865 // Positions the owners of the nodes based on Lima node groups
866 if (use_internal_partition) {
867 ItemInternalList nodes(mesh->itemsInternal(IK_Node));
868 for (Integer i = 0, is = nodes.size(); i < is; ++i)
869 nodes[i]->setOwner(sid, sid);
870 ItemInternalList cells(mesh->itemsInternal(IK_Cell));
871 for (Integer i = 0, is = cells.size(); i < is; ++i)
872 cells[i]->setOwner(sid, sid);
873 }
874 else {
875 Int64UniqueArray unique_ids;
876 Int32UniqueArray local_ids;
877 Integer sub_domain_id = subDomain()->subDomainId();
878 Integer nb = CheckedConvert::toInteger(lima.nb_nuages());
879 for (Integer i = 0; i < nb; ++i) {
880 const Lima::Nuage& lima_group = lima.nuage(i);
881 Integer nb_item_in_group = CheckedConvert::toInteger(lima_group.nb_noeuds());
882 std::string group_name = lima_group.nom();
883 unique_ids.resize(nb_item_in_group);
884 local_ids.resize(nb_item_in_group);
885 for (Integer z = 0; z < nb_item_in_group; ++z) {
886 unique_ids[z] = nodes_unique_id[CheckedConvert::toInteger(lima_group.noeud(z).id() - 1)];
887 }
888 mesh->nodeFamily()->itemsUniqueIdToLocalId(local_ids, unique_ids, false);
889 bool remove_group = false;
890 if (group_name == "LOCALN" || group_name == "localn") {
891 info() << "Utilisation du groupe 'LOCALN' pour indiquer que les "
892 << "noeuds appartiennent au sous-domaine";
893 ItemInternalList nodes(mesh->itemsInternal(IK_Node));
894 for (Integer z = 0, sz = nb_item_in_group; z < sz; ++z) {
895 Integer local_id = local_ids[z];
896 if (local_id != NULL_ITEM_ID)
897 nodes[local_id]->setOwner(sub_domain_id, sub_domain_id);
898 }
899 remove_group = true;
900 }
901 debug() << "Vérification du groupe '" << group_name << "'";
902 if (group_name.length() > 3 && !remove_group) {
903 String grp = group_name; //.c_str();
904 if (grp.startsWith("NF_")) {
905 grp = grp.substring(3);
906 Int32 ghost_sub_domain_id = 0;
907 bool is_bad = builtInGetValue(ghost_sub_domain_id, grp);
908 debug() << "Vérification du groupe '" << group_name << "' (3) " << is_bad;
909 if (!is_bad) {
910 info() << "Utilisation du groupe " << group_name << " pour indiquer que le "
911 << "sous-domaine " << ghost_sub_domain_id << " est propriétaire de ses noeuds";
912 ItemInternalList nodes(mesh->itemsInternal(IK_Node));
913 for (Integer z = 0, sz = nb_item_in_group; z < sz; ++z) {
914 Integer local_id = local_ids[z];
915 if (local_id != NULL_ITEM_ID)
916 nodes[local_ids[z]]->setOwner(ghost_sub_domain_id, sub_domain_id);
917 }
918 remove_group = true;
919 }
920 }
921 }
922 }
923 }
924
925 mesh->endAllocate();
926
927 // Since the mesh creates its own faces without taking into account those that
928 // possibly exist in Lima, we must now determine the local number
929 // in our mesh for each Lima face.
930 UniqueArray<Integer> faces_id(lima_nb_face); // Lima face number in the mesh \a mesh
931 {
932 // Number of faces/nodes
933 Integer face_nb_node = 0;
934 for (Integer i_face = 0; i_face < lima_nb_face; ++i_face) {
935 const LimaFace& lima_face = m_wrapper.face(i_face);
936 face_nb_node += CheckedConvert::toInteger(lima_face.nb_noeuds());
937 }
938
939 UniqueArray<Int64> faces_first_node_unique_id(lima_nb_face);
940 UniqueArray<Int32> faces_first_node_local_id(lima_nb_face);
941 UniqueArray<Int64> faces_nodes_unique_id(face_nb_node);
942 Integer faces_nodes_unique_id_index = 0;
943
944 UniqueArray<Int64> orig_nodes_id;
945 orig_nodes_id.reserve(100);
946
947 UniqueArray<Integer> face_nodes_index;
948 face_nodes_index.reserve(100);
949
950 ItemInternalList mesh_nodes(mesh->itemsInternal(IK_Node));
951
952 for (Integer i_face = 0; i_face < lima_nb_face; ++i_face) {
953 const LimaFace& lima_face = m_wrapper.face(i_face);
954 Integer n = CheckedConvert::toInteger(lima_face.nb_noeuds());
955 orig_nodes_id.resize(n);
956 face_nodes_index.resize(n);
957 for (Integer z = 0; z < n; ++z)
958 orig_nodes_id[z] = nodes_unique_id[CheckedConvert::toInteger(lima_face.noeud(z).id() - 1)];
959 //face_orig_nodes_id[z] = lima_face.noeud(z).id() - 1;
960
961#if 0
962 cout << "FACE LIMA1 " << lima_face.id()-1 << " ";
963 for( Integer z=0; z<n; ++z )
964 cout << " " << orig_nodes_id[z];
965 cout << '\n';
966#endif
967
968 mesh_utils::reorderNodesOfFace2(orig_nodes_id, face_nodes_index);
969 for (Integer z = 0; z < n; ++z)
970 faces_nodes_unique_id[faces_nodes_unique_id_index + z] = orig_nodes_id[face_nodes_index[z]];
971 faces_first_node_unique_id[i_face] = orig_nodes_id[face_nodes_index[0]];
972 faces_nodes_unique_id_index += n;
973 }
974
975 mesh->nodeFamily()->itemsUniqueIdToLocalId(faces_first_node_local_id, faces_first_node_unique_id);
976
977 faces_nodes_unique_id_index = 0;
978 for (Integer i_face = 0; i_face < lima_nb_face; ++i_face) {
979 const LimaFace& lima_face = m_wrapper.face(i_face);
980 Integer n = CheckedConvert::toInteger(lima_face.nb_noeuds());
981 Int64ConstArrayView face_nodes_id(n, &faces_nodes_unique_id[faces_nodes_unique_id_index]);
982 Node current_node(mesh_nodes[faces_first_node_local_id[i_face]]);
983 Face face = mesh_utils::getFaceFromNodesUnique(current_node, face_nodes_id);
984
985 if (face.null()) {
986 OStringStream ostr;
987 ostr() << "(Nodes:";
988 for (Integer z = 0; z < n; ++z)
989 ostr() << ' ' << face_nodes_id[z];
990 ostr() << " - " << current_node.localId() << ")";
991 ARCANE_FATAL("INTERNAL: Lima face index={0} with nodes '{1}' is not in node/face connectivity",
992 i_face, ostr.str());
993 }
994 faces_id[i_face] = face.localId();
995
996 faces_nodes_unique_id_index += n;
997 }
998 }
999
1000 IItemFamily* node_family = mesh->nodeFamily();
1001 IItemFamily* face_family = mesh->faceFamily();
1002 IItemFamily* cell_family = mesh->cellFamily();
1003
1004 // Creation of groups
1005 if (use_internal_partition && sid != 0) {
1006 {
1007 Integer nb = CheckedConvert::toInteger(lima.nb_nuages());
1008 for (Integer i = 0; i < nb; ++i) {
1009 const Lima::Nuage& lima_group = lima.nuage(i);
1010 std::string group_name = lima_group.nom();
1011 LimaUtils::createGroup(node_family, group_name, Int32ArrayView());
1012 }
1013 }
1014 {
1015 Integer nb = m_wrapper.nbFaceGroup();
1016 for (Integer i = 0; i < nb; ++i) {
1017 const LimaFaceGroup& lima_group = m_wrapper.faceGroup(i);
1018 std::string group_name = lima_group.nom();
1019 LimaUtils::createGroup(face_family, group_name, Int32ArrayView());
1020 }
1021 }
1022 {
1023 Integer nb = m_wrapper.nbCellGroup();
1024 for (Integer i = 0; i < nb; ++i) {
1025 const LimaCellGroup& lima_group = m_wrapper.cellGroup(i);
1026 std::string group_name = lima_group.nom();
1027 LimaUtils::createGroup(cell_family, group_name, Int32ArrayView());
1028 }
1029 }
1030 }
1031 else {
1032 UniqueArray<Int64> unique_ids;
1033 UniqueArray<Int32> local_ids;
1034 Integer sub_domain_id = subDomain()->subDomainId();
1035 // Creation of node groups
1036 {
1037 Integer nb = CheckedConvert::toInteger(lima.nb_nuages());
1038 for (Integer i = 0; i < nb; ++i) {
1039 const Lima::Nuage& lima_group = lima.nuage(i);
1040 Integer nb_item_in_group = CheckedConvert::toInteger(lima_group.nb_noeuds());
1041 std::string group_name = lima_group.nom();
1042 unique_ids.resize(nb_item_in_group);
1043 local_ids.resize(nb_item_in_group);
1044 for (Integer z = 0; z < nb_item_in_group; ++z) {
1045 Integer lima_node_id = CheckedConvert::toInteger(lima_group.noeud(z).id());
1046 unique_ids[z] = nodes_unique_id[lima_node_id - 1];
1047 }
1048 mesh->nodeFamily()->itemsUniqueIdToLocalId(local_ids, unique_ids);
1049 bool remove_group = false;
1050 if (group_name == "LOCALN" || group_name == "localn") {
1051 remove_group = true;
1052 }
1053 debug() << "Vérification du groupe '" << group_name << "'";
1054 if (group_name.length() > 3 && !remove_group) {
1055 String grp = group_name.c_str();
1056 //grp.left(3);
1057 debug() << "Vérification du groupe '" << group_name << "' (2) '" << grp << "'";
1058 if (grp.startsWith("NF_")) {
1059 //grp = group_name.c_str();
1060 //grp.right(group_name.length()-3);
1061 grp = grp.substring(3);
1062 Integer ghost_sub_domain_id = 0;
1063 bool is_bad = builtInGetValue(ghost_sub_domain_id, grp);
1064 debug() << "Vérification du groupe '" << group_name << "' (3) " << is_bad;
1065 if (!is_bad) {
1066 remove_group = true;
1067 }
1068 }
1069 }
1070 if (!remove_group) {
1071 log() << "NodeGroup Name <" << group_name << "> (" << nb_item_in_group << " elements)";
1072 LimaUtils::createGroup(node_family, group_name, local_ids);
1073 }
1074 }
1075 }
1076 // Creation of face groups
1077 {
1078 Integer nb = m_wrapper.nbFaceGroup();
1079 for (Integer i = 0; i < nb; ++i) {
1080 const LimaFaceGroup& lima_group = m_wrapper.faceGroup(i);
1081 Integer nb_item_in_group = m_wrapper.faceGroupNbFace(lima_group);
1082 local_ids.resize(nb_item_in_group);
1083 // Since the face numbers provided by Lima do not correspond
1084 // to those created in Arcane, we use the mapping array
1085 for (Integer z = 0; z < nb_item_in_group; ++z) {
1086 local_ids[z] = faces_id[CheckedConvert::toInteger(m_wrapper.faceFaceGroup(lima_group, z).id() - 1)];
1087 }
1088 std::string group_name = lima_group.nom();
1089 log() << "FaceGroup Name <" << group_name << "> (" << nb_item_in_group << " elements)";
1090 LimaUtils::createGroup(face_family, group_name, local_ids);
1091 }
1092 }
1093 // Creation of cell groups
1094 {
1095 Integer nb = m_wrapper.nbCellGroup();
1096 for (Integer i = 0; i < nb; ++i) {
1097 const LimaCellGroup& lima_group = m_wrapper.cellGroup(i);
1098 Integer nb_item_in_group = m_wrapper.cellGroupNbCell(lima_group);
1099 std::string group_name = lima_group.nom();
1100 unique_ids.resize(nb_item_in_group);
1101 local_ids.resize(nb_item_in_group);
1102 for (Integer z = 0; z < nb_item_in_group; ++z) {
1103 unique_ids[z] = cells_unique_id[CheckedConvert::toInteger(m_wrapper.cellCellGroup(lima_group, z).id() - 1)];
1104 }
1105 mesh->cellFamily()->itemsUniqueIdToLocalId(local_ids, unique_ids);
1106 bool remove_group = false;
1107 if (group_name == "LOCAL" || group_name == "local") {
1108 ItemInternalList cells(mesh->itemsInternal(IK_Cell));
1109 for (Integer z = 0, sz = nb_item_in_group; z < sz; ++z)
1110 cells[local_ids[z]]->setOwner(sub_domain_id, sub_domain_id);
1111 remove_group = true;
1112 }
1113 if (!remove_group) {
1114 String grp(group_name.c_str());
1115 if (grp.startsWith("MF_")) {
1116 info() << "Le groupe de mailles " << group_name << " n'est pas utilisé";
1117 remove_group = true;
1118 }
1119 }
1120 if (!remove_group) {
1121 log() << "CellGroup Name <" << group_name << "> (" << nb_item_in_group << " elements)";
1122 LimaUtils::createGroup(cell_family, group_name, local_ids);
1123 }
1124 }
1125 }
1126 }
1127
1128 {
1129 // Fills the variable containing the node coordinates
1130 // In parallel with the mesh already cut by Decoupe3D, the
1131 // mesh already contains a layer of ghost cells. To
1132 // have the node coordinates if we only need one layer
1133 // of ghost cells, it is enough to iterate over all
1134 // nodes. If we need multiple layers of ghost cells,
1135 // synchronization must be performed.
1136 VariableNodeReal3& nodes_coord_var(mesh->nodesCoordinates());
1137 NodeGroup nodes = mesh->allNodes();
1138 Integer nb_ghost_layer = mesh->ghostLayerMng()->nbGhostLayer();
1139 if (nb_ghost_layer > 1)
1140 nodes = mesh->ownNodes();
1141 ENUMERATE_NODE (i, nodes) {
1142 const Node& node = *i;
1143 nodes_coord_var[node] = nodes_coords.lookupValue(node.uniqueId());
1144 //info() << "Coord: " << node.uniqueId() << " v=" << nodes_coord_var[node];
1145 }
1146 if (nb_ghost_layer > 1)
1147 nodes_coord_var.synchronize();
1148 }
1149
1150 // -------------------------------- Reading groups
1151 info() << "Nombre de nuages " << lima.nb_nuages();
1152 info() << "Nombre de lignes " << lima.nb_lignes();
1153 info() << "Nombre de surfaces " << lima.nb_surfaces();
1154 info() << "Nombre de volumes " << lima.nb_volumes();
1155
1156 // -------------------------------- Creation of internal structures.
1157
1158 //_buildInternalMesh();
1159
1160 logdate() << "Fin de lecture du fichier";
1161 return false;
1162}
1163
1164/*---------------------------------------------------------------------------*/
1165/*---------------------------------------------------------------------------*/
1166
1167template <typename ReaderWrapper> void LimaWrapper<ReaderWrapper>::
1168_getProcList(UniqueArray<Integer>& proc_list, const String& dir_name)
1169{
1170 ISubDomain* sd = subDomain();
1171 StringBuilder comm_file_nameb;
1172 if (!dir_name.empty()) {
1173 comm_file_nameb += dir_name;
1174 comm_file_nameb += "/";
1175 }
1176 comm_file_nameb += "Communications";
1177 String comm_file_name = comm_file_nameb.toString();
1178
1179 // Reading the communication file
1180 ScopedPtrT<IXmlDocumentHolder> doc_holder(sd->ioMng()->parseXmlFile(comm_file_name));
1181 if (!doc_holder.get())
1182 ARCANE_FATAL("Invalid file '{0}'", comm_file_name);
1183
1184 XmlNode root_elem = doc_holder->documentNode().documentElement();
1185 XmlNode cpu_elem;
1186 XmlNodeList cpu_list = root_elem.child(String("cpus")).children(String("cpu-from"));
1187
1188 String ustr_buf = String::fromNumber(sd->subDomainId());
1189 String ustr_id(String("id"));
1190 for (Integer i = 0, s = cpu_list.size(); i < s; ++i) {
1191 String id_str = cpu_list[i].attrValue(ustr_id);
1192 if (id_str == ustr_buf) {
1193 cpu_elem = cpu_list[i];
1194 break;
1195 }
1196 }
1197 if (cpu_elem.null())
1198 ARCANE_FATAL("No element <cpus/cpu-from[@id=\"{0}\"]>", sd->subDomainId());
1199 {
1200 cpu_list = cpu_elem.children(String("cpu-to"));
1201 debug() << "Nb procs " << cpu_list.size();
1202 for (Integer i = 0; i < cpu_list.size(); ++i) {
1203 Integer v = cpu_list[i].valueAsInteger();
1204 proc_list.add(v);
1205 debug() << "Read proc " << v;
1206 }
1207 }
1208}
1209
1210/*---------------------------------------------------------------------------*/
1211/*---------------------------------------------------------------------------*/
1212
1213class LimaCaseMeshReader
1214: public AbstractService
1215, public ICaseMeshReader
1216{
1217 public:
1218
1219 class Builder
1220 : public IMeshBuilder
1221 {
1222 public:
1223
1224 explicit Builder(ISubDomain* sd, const CaseMeshReaderReadInfo& read_info)
1225 : m_sub_domain(sd)
1226 , m_trace_mng(sd->traceMng())
1227 , m_read_info(read_info)
1228 {}
1229
1230 public:
1231
1232 void fillMeshBuildInfo(MeshBuildInfo& build_info) override
1233 {
1234 ARCANE_UNUSED(build_info);
1235 }
1237 {
1238 LimaMeshReader reader(m_sub_domain);
1239 String fname = m_read_info.fileName();
1240 m_trace_mng->info() << "Lima Reader (ICaseMeshReader) file_name=" << fname;
1241 bool use_length_unit = true; // With ICaseMeshReader, we always use the unit system.
1242 String directory_name = m_read_info.directoryName();
1243 IMeshReader::eReturnType ret = reader.readMesh(pm, fname, directory_name, m_read_info.isParallelRead(), use_length_unit);
1244 if (ret != IMeshReader::RTOk)
1245 ARCANE_FATAL("Can not read MSH File");
1246 }
1247
1248 private:
1249
1250 ISubDomain* m_sub_domain;
1251 ITraceMng* m_trace_mng;
1252 CaseMeshReaderReadInfo m_read_info;
1253 };
1254
1255 public:
1256
1257 explicit LimaCaseMeshReader(const ServiceBuildInfo& sbi)
1258 : AbstractService(sbi)
1259 , m_sub_domain(sbi.subDomain())
1260 {}
1261
1262 public:
1263
1265 {
1266 IMeshBuilder* builder = nullptr;
1267 String str = read_info.format();
1268 if (str == "unf" || str == "mli" || str == "mli2" || str == "ice" || str == "uns" || str == "unv")
1269 builder = new Builder(m_sub_domain, read_info);
1270 return makeRef(builder);
1271 }
1272
1273 private:
1274
1275 ISubDomain* m_sub_domain = nullptr;
1276};
1277
1278/*---------------------------------------------------------------------------*/
1279/*---------------------------------------------------------------------------*/
1280
1281ARCANE_REGISTER_SERVICE(LimaCaseMeshReader,
1282 ServiceProperty("LimaCaseMeshReader", ST_SubDomain),
1283 ARCANE_SERVICE_INTERFACE(ICaseMeshReader));
1284
1285/*---------------------------------------------------------------------------*/
1286/*---------------------------------------------------------------------------*/
1287
1288/*---------------------------------------------------------------------------*/
1289/*---------------------------------------------------------------------------*/
1290
1291class LimaMeshWriter
1292: public AbstractService
1293, public IMeshWriter
1294{
1295 public:
1296
1297 LimaMeshWriter(const ServiceBuildInfo& sbi)
1298 : AbstractService(sbi)
1299 , m_sub_domain(sbi.subDomain())
1300 {}
1301
1302 public:
1303
1304 virtual void build() {}
1305 virtual bool writeMeshToFile(IMesh* mesh, const String& file_name);
1306
1307 private:
1308
1309 ISubDomain* m_sub_domain;
1310 void _writeItem(Lima::Maillage& lima_mesh, ConstArrayView<Lima::Noeud> nodes,
1311 ItemWithNodes item);
1312};
1313
1314/*---------------------------------------------------------------------------*/
1315/*---------------------------------------------------------------------------*/
1316
1317ARCANE_REGISTER_SERVICE(LimaMeshWriter,
1318 ServiceProperty("Lima", ST_SubDomain),
1319 ARCANE_SERVICE_INTERFACE(IMeshWriter));
1320
1321/*---------------------------------------------------------------------------*/
1322/*---------------------------------------------------------------------------*/
1323
1324void LimaMeshWriter::
1325_writeItem(Lima::Maillage& m, ConstArrayView<Lima::Noeud> nodes, ItemWithNodes c)
1326{
1327 switch (c.type()) {
1328 case IT_Octaedron12:
1329 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1330 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1331 nodes[c.node(4).localId()], nodes[c.node(5).localId()],
1332 nodes[c.node(6).localId()], nodes[c.node(7).localId()],
1333 nodes[c.node(8).localId()], nodes[c.node(9).localId()],
1334 nodes[c.node(10).localId()], nodes[c.node(11).localId()]));
1335 break;
1336 case IT_Heptaedron10:
1337 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1338 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1339 nodes[c.node(4).localId()], nodes[c.node(5).localId()],
1340 nodes[c.node(6).localId()], nodes[c.node(7).localId()],
1341 nodes[c.node(8).localId()], nodes[c.node(9).localId()]));
1342 break;
1343 case IT_Hexaedron8:
1344 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1345 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1346 nodes[c.node(4).localId()], nodes[c.node(5).localId()],
1347 nodes[c.node(6).localId()], nodes[c.node(7).localId()]));
1348 break;
1349 case IT_Pentaedron6:
1350 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1351 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1352 nodes[c.node(4).localId()], nodes[c.node(5).localId()]));
1353 break;
1354 case IT_Pyramid5:
1355 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1356 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1357 nodes[c.node(4).localId()]));
1358 break;
1359 case IT_Tetraedron4:
1360 m.ajouter(Lima::Polyedre(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1361 nodes[c.node(2).localId()], nodes[c.node(3).localId()]));
1362 break;
1363 case IT_Hexagon6:
1364 m.ajouter(Lima::Polygone(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1365 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1366 nodes[c.node(4).localId()], nodes[c.node(5).localId()]));
1367 break;
1368 case IT_Pentagon5:
1369 m.ajouter(Lima::Polygone(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1370 nodes[c.node(2).localId()], nodes[c.node(3).localId()],
1371 nodes[c.node(4).localId()]));
1372 break;
1373 case IT_Quad4:
1374 m.ajouter(Lima::Polygone(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1375 nodes[c.node(2).localId()], nodes[c.node(3).localId()]));
1376 break;
1377 case IT_Triangle3:
1378 m.ajouter(Lima::Polygone(nodes[c.node(0).localId()], nodes[c.node(1).localId()],
1379 nodes[c.node(2).localId()]));
1380 break;
1381 case IT_Line2:
1382 m.ajouter(Lima::Bras(nodes[c.node(0).localId()], nodes[c.node(1).localId()]));
1383 break;
1384 }
1385}
1386
1387/*---------------------------------------------------------------------------*/
1388/*---------------------------------------------------------------------------*/
1395writeMeshToFile(IMesh* mesh, const String& file_name)
1396{
1397 ITraceMng* trace = mesh->traceMng();
1398 int dimension = mesh->dimension();
1399
1400 std::string std_file_name = file_name.localstr();
1401 //TODO: ADD EXTENSION if not present
1402 // Check if the file has the extension '.unf', '.mli', or '.mli2'.
1403 // Otherwise, add '.mli2'
1404 const size_t rpos2 = std_file_name.rfind(".mli2");
1405 std::string::size_type std_end = std::string::npos;
1406 bool need_mutex = rpos2 != std_end;
1407 if (rpos2 == std_end && std_file_name.rfind(".mli") == std_end && std_file_name.rfind(".unf") == std_end) {
1408 std_file_name += ".mli2";
1409 need_mutex = true;
1410 }
1411 info() << "FINAL_FILE_NAME=" << std_file_name;
1412 Lima::Maillage lima(std_file_name);
1413
1414 if (dimension == 3)
1415 lima.dimension(Lima::D3);
1416 else if (dimension == 2)
1417 lima.dimension(Lima::D2);
1418
1419 IItemFamily* node_family = mesh->nodeFamily();
1420 IItemFamily* edge_family = mesh->edgeFamily();
1421 IItemFamily* face_family = mesh->faceFamily();
1422 IItemFamily* cell_family = mesh->cellFamily();
1423
1424 Integer mesh_nb_node = node_family->nbItem();
1425 Integer mesh_nb_edge = edge_family->nbItem();
1426 Integer mesh_nb_face = face_family->nbItem();
1427 Integer mesh_nb_cell = cell_family->nbItem();
1428
1429 NodeInfoListView nodes(node_family);
1430 EdgeInfoListView edges(edge_family);
1431 FaceInfoListView faces(face_family);
1432 CellInfoListView cells(cell_family);
1433
1434 UniqueArray<Lima::Noeud> lm_nodes(mesh_nb_node);
1435
1436 VariableItemReal3& nodes_coords = mesh->nodesCoordinates();
1437
1438 // Save nodes
1439 for (Integer i = 0; i < mesh_nb_node; ++i) {
1440 Node node = nodes[i];
1441 Real3 coord = nodes_coords[node];
1442 lm_nodes[i].set_x(Convert::toDouble(coord.x));
1443 lm_nodes[i].set_y(Convert::toDouble(coord.y));
1444 lm_nodes[i].set_z(Convert::toDouble(coord.z));
1445 lima.ajouter(lm_nodes[i]);
1446 }
1447
1448 // Save edges
1449 for (Integer i = 0; i < mesh_nb_edge; ++i) {
1450 _writeItem(lima, lm_nodes, edges[i]);
1451 }
1452
1453 // Save faces
1454 for (Integer i = 0; i < mesh_nb_face; ++i) {
1455 _writeItem(lima, lm_nodes, faces[i]);
1456 }
1457
1458 // Save cells
1459 for (Integer i = 0; i < mesh_nb_cell; ++i) {
1460 _writeItem(lima, lm_nodes, cells[i]);
1461 }
1462
1463 try {
1464
1465 // Save node groups
1466 for (ItemGroupCollection::Enumerator i(node_family->groups()); ++i;) {
1467 ItemGroup group = *i;
1468 if (group.isAllItems())
1469 continue;
1470 Lima::Nuage lm_group(group.name().localstr());
1471 lima.ajouter(lm_group);
1472 ENUMERATE_ITEM (iitem, group) {
1473 lm_group.ajouter(lima.noeud(iitem.localId()));
1474 }
1475 }
1476
1477 // Save edge groups
1478 for (ItemGroupCollection::Enumerator i(edge_family->groups()); ++i;) {
1479 ItemGroup group = *i;
1480 if (group.isAllItems())
1481 continue;
1482 Lima::Ligne lm_group(group.name().localstr());
1483 lima.ajouter(lm_group);
1484 ENUMERATE_ITEM (iitem, group) {
1485 lm_group.ajouter(lima.bras(iitem.localId()));
1486 }
1487 }
1488
1489 // Save face groups
1490 for (ItemGroupCollection::Enumerator i(face_family->groups()); ++i;) {
1491 ItemGroup group = *i;
1492 if (group.isAllItems())
1493 continue;
1494 if (dimension == 3) {
1495 Lima::Surface lm_group(group.name().localstr());
1496 lima.ajouter(lm_group);
1497 ENUMERATE_ITEM (iitem, group) {
1498 lm_group.ajouter(lima.polygone(iitem.localId()));
1499 }
1500 }
1501 else if (dimension == 2) {
1502 Lima::Ligne lm_group(group.name().localstr());
1503 lima.ajouter(lm_group);
1504 ENUMERATE_ITEM (iitem, group) {
1505 lm_group.ajouter(lima.bras(iitem.localId()));
1506 }
1507 }
1508 }
1509
1510 // Save cell groups
1511 for (ItemGroupCollection::Enumerator i(cell_family->groups()); ++i;) {
1512 ItemGroup group = *i;
1513 if (group.isAllItems())
1514 continue;
1515 if (dimension == 3) {
1516 Lima::Volume lm_group(group.name().localstr());
1517 lima.ajouter(lm_group);
1518 ENUMERATE_ITEM (iitem, group) {
1519 lm_group.ajouter(lima.polyedre(iitem.localId()));
1520 }
1521 }
1522 else if (dimension == 2) {
1523 Lima::Surface lm_group(group.name().localstr());
1524 lima.ajouter(lm_group);
1525 ENUMERATE_ITEM (iitem, group) {
1526 lm_group.ajouter(lima.polygone(iitem.localId()));
1527 }
1528 }
1529 }
1530 info(4) << "Writing file '" << std_file_name << "'";
1531
1532 {
1533 GlobalLimaMutex sc(need_mutex);
1534 lima.ecrire(std_file_name);
1535 }
1536 }
1537 catch (const std::exception& ex) {
1538 trace->warning() << "Exception (std::exception) in LIMA: Can not write file <" << std_file_name << ">"
1539 << " Exception: " << ex.what() << '\n';
1540 return true;
1541 }
1542 catch (...) {
1543 trace->warning() << "Exception (unknown) in LIMA: Can not write file <" << std_file_name << ">";
1544 return true;
1545 }
1546
1547 return false;
1548}
1549
1550/*---------------------------------------------------------------------------*/
1551/*---------------------------------------------------------------------------*/
1552
1553} // End namespace Arcane
1554
1555/*---------------------------------------------------------------------------*/
1556/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Types and macros for iterating over mesh entities.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
#define ENUMERATE_NODE(name, group)
Generic enumerator for a node group.
Utility functions for the mesh.
bool reorderNodesOfFace2(Int64ConstArrayView nodes_unique_id, Int32ArrayView new_index)
Reorders the nodes of a face.
This file contains the various service factories and macros for registering services.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
Base class of a service.
AbstractService(const ServiceBuildInfo &)
Constructor from a ServiceBuildInfo.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
Necessary information for reading a mesh file.
View of cell information.
Constant view of an array of type T.
View of edge information.
View of face information.
Face of a cell.
Definition Item.h:1032
Hash table for associative arrays.
virtual ITraceMng * traceMng() const =0
Trace manager.
virtual String language() const =0
Language used in the dataset.
Interface of a class managing an XML document of the dataset.
virtual String codeUnitSystem() const =0
Name of the document's unit system.
Interface for the mesh reading service from the dataset.
virtual Integer nbGhostLayer() const =0
Number of ghost layers.
Interface of an entity family.
Definition IItemFamily.h:83
virtual ItemGroupCollection groups() const =0
Collection of groups in this family.
virtual Integer nbItem() const =0
Number of entities.
virtual Integer dimension()=0
Mesh dimension (1D, 2D, or 3D).
Interface of a mesh creation/reading service.
Interface of the service managing the reading of a mesh.
Definition IMeshReader.h:33
eReturnType
Types of return codes for a read or write operation.
Definition IMeshReader.h:38
@ RTIrrelevant
Not relevant to the operation. This means that the file format does not match this reader or that the...
Definition IMeshReader.h:46
@ RTError
Error during the operation.
Definition IMeshReader.h:40
@ RTOk
Operation successfully performed.
Definition IMeshReader.h:39
Interface of a mesh writing service.
Definition IMeshWriter.h:38
virtual IParallelMng * parallelMng()=0
Parallelism manager.
virtual void synchronizeGroupsAndVariables()=0
Synchronizes all mesh groups and variables.
virtual IGhostLayerMng * ghostLayerMng() const =0
Associated ghost layer manager.
Interface of the parallelism manager for a subdomain.
virtual bool isThreadImplementation() const =0
Indicates if the implementation uses threads.
virtual void setDimension(Integer dim)=0
Positions the mesh dimension (1D, 2D, or 3D).
Interface of the subdomain manager.
Definition ISubDomain.h:75
virtual ICaseDocument * caseDocument()=0
Case XML document.
virtual IParallelMng * parallelMng()=0
Returns the parallelism manager.
virtual ITimerMng * timerMng() const =0
Returns the timer manager.
Interface of a timer manager.
Definition ITimerMng.h:50
virtual TraceMessage warning()=0
Stream for a warning message.
Mesh entity group.
Definition ItemGroup.h:51
const String & name() const
Group name.
Definition ItemGroup.h:81
bool isAllItems() const
Indicates if the group is that of all entities.
Definition ItemGroup.cc:607
Mesh element based on nodes (Edge,Face,Cell).
Definition Item.h:773
void fillMeshBuildInfo(MeshBuildInfo &build_info) override
Fills build_info with the necessary information to create the mesh.
Definition Lima.cc:1232
void allocateMeshItems(IPrimaryMesh *pm) override
Allocates the mesh entities managed by this service.
Definition Lima.cc:1236
Ref< IMeshBuilder > createBuilder(const CaseMeshReaderReadInfo &read_info) const override
Returns a builder to create and read the mesh whose information is specified in read_info.
Definition Lima.cc:1264
Mesh file reader via the LIMA library.
Definition Lima.cc:115
Mesh file reader via the LIMA library.
Definition Lima.cc:410
eReturnType readMeshFromFile(IPrimaryMesh *mesh, const XmlNode &mesh_node, const String &file_name, const String &dir_name, bool use_internal_partition) override
Reads a mesh from a file.
Definition Lima.cc:459
void build()
Build-level construction of the service.
Definition Lima.cc:417
bool allowExtension(const String &str) override
Checks if the service supports files with the extension str.
Definition Lima.cc:421
Mesh file reader via the LIMA library.
Definition Lima.cc:383
virtual bool writeMeshToFile(IMesh *mesh, const String &file_name)
Writing the mesh in Lima format.
Definition Lima.cc:1395
virtual void build()
Build-level construction of the service.
Definition Lima.cc:1304
static void createGroup(IItemFamily *family, const String &name, Int32ArrayView local_ids)
Creates a group of entities.
Definition LimaUtils.cc:43
Mesh file reader via the LIMA library.
Definition Lima.cc:150
Parameters necessary for building a mesh.
View of node information.
Node of a mesh.
Definition Item.h:598
Output stream linked to a String.
Class managing a 3-dimensional real vector.
Definition Real3.h:132
Reference to an instance.
Encapsulation of an automatically destructing pointer.
Definition ScopedPtr.h:44
ISubDomain * subDomain() const
Access to the associated ISubDomain.
Structure containing the information to create a service.
Service creation properties.
Unicode character string constructor.
bool null() const
Returns true if the string is null.
Definition String.cc:306
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
bool empty() const
True if the string is empty (null or "").
Definition String.cc:317
@ TimerReal
Timer using real time.
Definition Timer.h:77
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage log() const
Flow for a log message.
TraceMessage info() const
Flow for an information message.
TraceMessage warning() const
Flow for a warning message.
1D data vector with value semantics (STL style).
Exception when a mesh entity is not of a known type.
List of nodes of a DOM tree.
Definition XmlNodeList.h:36
Node of a DOM tree.
Definition XmlNode.h:51
XmlNode documentElement() const
Returns the document element.
Definition XmlNode.cc:565
String attrValue(const String &name, bool throw_exception=false) const
Value of attribute name.
Definition XmlNode.cc:234
ItemGroupT< Node > NodeGroup
Group of nodes.
Definition ItemTypes.h:168
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
ItemVariableScalarRefT< Real3 > VariableItemReal3
3D coordinate type quantity
double toDouble(Real r)
Converts a Real to double.
__host__ __device__ double log(double v)
Natural logarithm of v.
Definition Math.h:42
constexpr __host__ __device__ bool isEqual(const _Type &a, const _Type &b)
Tests the bit-by-bit equality between two values.
Definition Numeric.h:260
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
ConstArrayView< ItemInternal * > ItemInternalList
Type of the internal list of entities.
Definition ItemTypes.h:466
@ ST_SubDomain
The service is used at the subdomain level.
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:341
ArrayView< Int32 > Int32ArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:453
@ IK_Node
Node mesh entity.
@ IK_Cell
Cell mesh entity.
double Real
Type representing a real number.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
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