Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
LegacyMeshBuilder.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/* LegacyMeshBuilder.cc (C) 2000-2025 */
9/* */
10/* Mesh construction via the "historical" method. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/impl/internal/LegacyMeshBuilder.h"
15
16#include "arcane/utils/PlatformUtils.h"
17#include "arcane/utils/StringBuilder.h"
18
19#include "arcane/core/MeshKind.h"
20#include "arcane/core/ISubDomain.h"
21#include "arcane/core/CaseNodeNames.h"
22#include "arcane/core/ICaseDocument.h"
23#include "arcane/core/IParallelMng.h"
24#include "arcane/core/ServiceBuilder.h"
25#include "arcane/core/IMainFactory.h"
26#include "arcane/core/IPrimaryMesh.h"
27#include "arcane/core/IGhostLayerMng.h"
28#include "arcane/core/IMeshReader.h"
29#include "arcane/core/IMeshMng.h"
30#include "arcane/core/IMeshUniqueIdMng.h"
31
32#include "arcane_internal_config.h"
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37namespace Arcane
38{
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43LegacyMeshBuilder::
44LegacyMeshBuilder(ISubDomain* sd, MeshHandle default_mesh_handle)
45: TraceAccessor(sd->traceMng())
46, m_sub_domain(sd)
47, m_default_mesh_handle(default_mesh_handle)
48, m_internal_partitioner_name(ARCANE_DEFAULT_PARTITIONER_STR)
49{
50}
51
52/*---------------------------------------------------------------------------*/
53/*---------------------------------------------------------------------------*/
54
55void LegacyMeshBuilder::
56readCaseMeshes()
57{
58 ISubDomain* sd = m_sub_domain;
59 Integer sub_domain_id = sd->subDomainId();
60 ICaseDocument* case_doc = sd->caseDocument();
61 CaseNodeNames* cnn = case_doc->caseNodeNames();
62 XmlNodeList mesh_elems(case_doc->meshElements());
63 bool has_mesh_file = true;
64 if (mesh_elems.empty()) {
65 info() << "No mesh in the input data";
66 has_mesh_file = false;
67 }
68 Integer nb_mesh = mesh_elems.size();
69 m_meshes_build_info.resize(nb_mesh);
70 for (Integer i = 0; i < nb_mesh; ++i) {
71 MeshBuildInfo& mbi = m_meshes_build_info[i];
72 mbi.m_dir_name = ".";
73 mbi.m_xml_node = mesh_elems[i];
74 XmlNodeList partitioner_elems = mesh_elems[i].children(cnn->mesh_partitioner);
75 m_use_partitioner_tester = false;
76 if (partitioner_elems.empty()) {
77 m_use_partitioner_tester = true;
78 m_internal_partitioner_name = ARCANE_DEFAULT_PARTITIONER_STR;
79 }
80 else {
81 m_internal_partitioner_name = partitioner_elems[0].value();
82 //check if the partitioner is parallel
83 m_use_partitioner_tester = partitioner_elems[0].attr("need-basic-partition-first").valueAsBoolean();
84 }
85
86 XmlNode meshfile_elem = mesh_elems[i].child(cnn->mesh_file);
87 String smesh_file = meshfile_elem.value();
88 StringBuilder mesh_file = smesh_file;
89 mbi.m_orig_file_name = smesh_file;
90 if (smesh_file.null()) {
91 info() << "No mesh in the input data";
92 has_mesh_file = false;
93 }
94
95 String file_format = meshfile_elem.attrValue("format");
96 bool internal_cut = meshfile_elem.attr("internal-partition").valueAsBoolean();
97 String internal_partitioner = meshfile_elem.attr("partitioner").value();
98
99 // If the environment variable is defined, force repartitioning
100 // initially with the service whose name is specified in this variable.
101 String internal_partitioner_env = platform::getEnvironmentVariable("ARCANE_INTERNAL_PARTITIONER");
102 if (!internal_partitioner_env.null()) {
103 info() << "Forcing internal partitioner from environment variable";
104 internal_cut = true;
105 internal_partitioner = internal_partitioner_env;
106 }
107 IParallelMng* pm = sd->parallelMng();
108 // In the case where Arcane is trimmed to a core, we will not look for CPU*.mli2
109 if (pm->isParallel() && (pm->commSize() > 1)) {
110 m_use_internal_mesh_partitioner = internal_cut;
111
112 if (!internal_partitioner.empty())
113 m_internal_partitioner_name = internal_partitioner;
114 Integer nb_sub_domain = pm->commSize();
115 info() << "Subdomain number is " << sub_domain_id << '/' << nb_sub_domain;
116
117 //check if the file mesh reader need a unique or multi file
118 bool use_unique_file = meshfile_elem.attr("unique").valueAsBoolean();
119 if (!use_unique_file) {
120 //Integer nb_sub_domain = m_parallel_mng->commSize();
121 StringBuilder cut_dir_str("cut_");
122 cut_dir_str += nb_sub_domain;
123 String mesh_cut_dir = meshfile_elem.attrValue(cut_dir_str);
124 debug() << "MESH CUT DIR " << mesh_cut_dir << ' ' << cut_dir_str;
125 if (has_mesh_file && !internal_cut) {
126 char buf[128];
127 String file_format_str = "mli2";
128 if (!file_format.null())
129 file_format_str = file_format;
130 sprintf(buf, "CPU%05d.%s", (int)sub_domain_id, file_format_str.localstr());
131 log() << "The original mesh file is " << mesh_file;
132 if (mesh_cut_dir.empty())
133 mesh_file = String(std::string_view(buf));
134 else {
135 mbi.m_dir_name = mesh_cut_dir;
136 mesh_file = mesh_cut_dir;
137 mesh_file += "/";
138 mesh_file += buf;
139 }
140 }
141 }
142 }
143 log() << "The mesh file is " << mesh_file;
144 mbi.m_file_name = mesh_file;
145 // This configuration part must be read before
146 // mesh creation because it may contain options
147 // that the mesh generator needs.
148 //m_case_config->read();
149 }
150
151 // Create MeshHandles for the meshes.
152 // This will allow them to be retrieved in the 'Build' entry points
153 _createMeshesHandle();
154}
155
156/*---------------------------------------------------------------------------*/
157/*---------------------------------------------------------------------------*/
158
159void LegacyMeshBuilder::
160readMeshes()
161{
162 // Builds the mesh reading services. These are
163 // those that implement IMeshReader.
164 ServiceBuilder<IMeshReader> builder(m_sub_domain);
165 UniqueArray<Ref<IMeshReader>> mesh_readers(builder.createAllInstances());
166
167 for (const MeshBuildInfo& mbi : m_meshes_build_info) {
168 _readMesh(mesh_readers, mbi);
169 }
170}
171
172/*---------------------------------------------------------------------------*/
173/*---------------------------------------------------------------------------*/
174
175void LegacyMeshBuilder::
176createDefaultMesh()
177{
178 ISubDomain* sd = m_sub_domain;
179 String mesh_name = m_default_mesh_handle.meshName();
180 ICaseDocument* case_doc = sd->caseDocument();
181 if (!case_doc) {
182 // If no dataset is specified, create a mesh
183 m_default_mesh_handle._setMesh(sd->mainFactory()->createMesh(sd, mesh_name));
184 return;
185 }
186
187 CaseNodeNames* cnn = case_doc->caseNodeNames();
188 XmlNodeList mesh_elems(case_doc->meshElements());
189 if (mesh_elems.empty()) {
190 info() << "No mesh in the input data";
191 }
192 Integer nb_mesh = mesh_elems.size();
193 for (Integer i = 0; i < nb_mesh; ++i) {
194 XmlNode meshfile_elem = mesh_elems[i].child(cnn->mesh_file);
195 String mesh_file = meshfile_elem.value();
196 if (mesh_file.null()) {
197 info() << "No mesh in the input data";
198 }
199 }
200 // default_mesh is the mesh described by mesh_elems[0]
201 // Now that amr flag has to be known at mesh creation, check-it for default mesh
202 bool is_amr = mesh_elems[0].attr("amr").valueAsBoolean();
203 eMeshAMRKind amr_type = static_cast<eMeshAMRKind>(mesh_elems[0].attr("amr-type").valueAsInteger());
204 if (is_amr && amr_type == eMeshAMRKind::None) {
205 amr_type = eMeshAMRKind::Cell;
206 }
207
208 m_default_mesh_handle._setMesh(sd->mainFactory()->createMesh(sd, mesh_name, amr_type));
209}
210
211/*---------------------------------------------------------------------------*/
212/*---------------------------------------------------------------------------*/
213
214void LegacyMeshBuilder::
215_createMeshesHandle()
216{
217 IMeshMng* mesh_mng = m_sub_domain->meshMng();
218
219 // The first mesh is always the default one
220 Integer nb_build_mesh = m_meshes_build_info.size();
221 if (nb_build_mesh > 0) {
222 m_meshes_build_info[0].m_mesh_handle = m_default_mesh_handle;
223 }
224
225 // Create the other meshes specified in the dataset
226 for (Integer z = 1; z < nb_build_mesh; ++z) {
227 String name;
228 if (m_meshes_build_info[z].m_xml_node.attr("dual").valueAsBoolean())
229 name = "DualMesh";
230 else
231 name = "Mesh";
232 name = name + z;
233 MeshHandle handle = mesh_mng->createMeshHandle(name);
234 m_meshes_build_info[z].m_mesh_handle = handle;
235 }
236}
237
238/*---------------------------------------------------------------------------*/
239/*---------------------------------------------------------------------------*/
240
241void LegacyMeshBuilder::
242allocateMeshes()
243{
244 ISubDomain* sd = m_sub_domain;
245
246 // The first mesh is always the default one
247 Integer nb_build_mesh = m_meshes_build_info.size();
248 if (nb_build_mesh > 0) {
249 m_meshes_build_info[0].m_mesh = m_default_mesh_handle.mesh()->toPrimaryMesh();
250 }
251
252 // Create the other meshes specified in the dataset
253 for (Integer z = 1; z < nb_build_mesh; ++z) {
254 MeshHandle handle = m_meshes_build_info[z].m_mesh_handle;
255 if (handle.isNull())
256 ARCANE_FATAL("Invalid null MeshHandle for mesh index={0}", z);
257 // Since 1.8.0 (IFP modification), this method
258 // calls this->addMesh()
259 bool is_amr = m_meshes_build_info[z].m_xml_node.attr("amr").valueAsBoolean();
260 eMeshAMRKind amr_type = static_cast<eMeshAMRKind>(m_meshes_build_info[z].m_xml_node.attr("amr-type").valueAsInteger());
261 if (is_amr && amr_type == eMeshAMRKind::None) {
262 amr_type = eMeshAMRKind::Cell;
263 }
264
265 IPrimaryMesh* mesh = sd->mainFactory()->createMesh(sd, handle.meshName(), amr_type);
266 m_meshes_build_info[z].m_mesh = mesh;
267 }
268}
269
270/*---------------------------------------------------------------------------*/
271/*---------------------------------------------------------------------------*/
272
273void LegacyMeshBuilder::
274initializeMeshVariablesFromCaseFile()
275{
276 info() << "Initialization of the variable from the configuration file";
277 CaseNodeNames* cnn = m_sub_domain->caseDocument()->caseNodeNames();
278 for (const LegacyMeshBuilder::MeshBuildInfo& mbi : m_meshes_build_info) {
279 IMesh* mesh = mbi.m_mesh;
280 XmlNode node = mbi.m_xml_node;
281 XmlNode init_node = node.child(cnn->mesh_initialisation);
282 if (!init_node.null())
283 mesh->initializeVariables(init_node);
284 }
285}
286
287/*---------------------------------------------------------------------------*/
288/*---------------------------------------------------------------------------*/
289
290void LegacyMeshBuilder::
291_readMesh(ConstArrayView<Ref<IMeshReader>> mesh_readers, const MeshBuildInfo& mbi)
292{
293 IPrimaryMesh* mesh = mbi.m_mesh;
295
296 String mesh_file_name = mbi.m_file_name;
297 // If a partitioning service is specified, the file specified
298 // in the case document (JDD) must be used and not the file name possibly transformed in readCaseMeshes()
299 bool use_internal_partitioner = m_use_internal_mesh_partitioner;
300 if (m_initial_partitioner.get()) {
301 mesh_file_name = mbi.m_orig_file_name;
302 use_internal_partitioner = true;
303 }
304 // Allows forcing the dimension in case the format cannot recognize it.
305 Integer wanted_dimension = mbi.m_xml_node.attr("dimension").valueAsInteger();
306 if (wanted_dimension != 0) {
307 info() << "Force mesh dimension to " << wanted_dimension;
308 mesh->setDimension(wanted_dimension);
309 }
310 log() << "Mesh file: " << mesh_file_name;
311
312 Integer nb_ghost_layer = -1;
313 XmlNode nbGhostLayerNode = mbi.m_xml_node.attr("nb-ghostlayer");
314 if (!nbGhostLayerNode.null()) {
315 nb_ghost_layer = nbGhostLayerNode.valueAsInteger();
316 if (nb_ghost_layer >= 0) {
317 info() << "Set number of ghost layers to '" << nb_ghost_layer << "' from caseoption";
318 mesh->ghostLayerMng()->setNbGhostLayer(nb_ghost_layer);
319 }
320 }
321
322 Integer builder_version = mbi.m_xml_node.attr("ghostlayer-builder-version").valueAsInteger();
323 if (nb_ghost_layer >= 0 && builder_version >= 0) {
324 info() << "Set ghostlayer builder version to '" << builder_version << "' from caseoption";
325 mesh->ghostLayerMng()->setBuilderVersion(builder_version);
326 }
327
328 XmlNode face_numbering_version_node = mbi.m_xml_node.attr("face-numbering-version");
329 if (!face_numbering_version_node.null()) {
330 Int32 v = face_numbering_version_node.valueAsInteger();
331 if (v >= 0) {
332 info() << "Set face numbering version to '" << v << "' from caseoption";
333 mesh->meshUniqueIdMng()->setFaceBuilderVersion(v);
334 }
335 }
336 bool is_bad = true;
337 String extension;
338 {
339 // Searches for the file extension and keeps it in \a case_ext
340 std::string_view fview = mesh_file_name.toStdStringView();
341 debug() << " MF=" << fview;
342 std::size_t extension_pos = fview.find_last_of('.');
343 if (extension_pos != std::string_view::npos) {
344 fview.remove_prefix(extension_pos + 1);
345 extension = fview;
346 }
347 }
348
349 for (auto& mesh_reader_ref : mesh_readers) {
350 IMeshReader* mesh_reader = mesh_reader_ref.get();
351 if (!mesh_reader->allowExtension(extension))
352 continue;
353
355 mbi.m_xml_node,
356 mesh_file_name,
357 mbi.m_dir_name,
358 use_internal_partitioner);
359 if (ret == IMeshReader::RTOk) {
360 is_bad = false;
361 break;
362 }
363 if (ret == IMeshReader::RTError) {
364 ARCANE_FATAL("Error while generating the mesh");
365 }
366 }
367
368 if (is_bad) {
369 ARCANE_FATAL("Internal error: no mesh loaded or generated. \n",
370 "The mesh reader or generator required isn't available ",
371 "Recompile with the relevant options");
372 }
373
374 mesh->computeTiedInterfaces(mbi.m_xml_node);
376 //mesh->readAmrActivator(mbi.m_xml_node);
377
378#if 0
379 IMeshWriter* writer = ServiceFinderT<IMeshWriter>::find(serviceMng(),"VtkLegacyMeshWriter");
380 if (writer){
381 writer->writeMeshToFile(mesh,"test.vtk");
382 }
383 if (!parallelMng()->isParallel()){
384 mesh_utils::writeMeshConnectivity(mesh,"mesh-reference.xml");
385 }
386#endif
387}
388
389/*---------------------------------------------------------------------------*/
390/*---------------------------------------------------------------------------*/
391
392} // End namespace Arcane
393
394/*---------------------------------------------------------------------------*/
395/*---------------------------------------------------------------------------*/
#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_FATAL(...)
Macro throwing a FatalErrorException.
void writeMeshConnectivity(IMesh *mesh, const String &file_name)
Writes the connectivity of the mesh mesh to the file file_name.
Definition MeshUtils.cc:829
Constant view of an array of type T.
Interface of the service managing the reading of a mesh.
Definition IMeshReader.h:33
virtual bool allowExtension(const String &str)=0
Checks if the service supports files with the extension str.
virtual eReturnType readMeshFromFile(IPrimaryMesh *mesh, const XmlNode &mesh_element, const String &file_name, const String &dir_name, bool use_internal_partition)=0
Reads a mesh from a file.
eReturnType
Types of return codes for a read or write operation.
Definition IMeshReader.h:38
@ 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 bool writeMeshToFile(IMesh *mesh, const String &file_name)=0
Writes a mesh to a file.
Interface of the subdomain manager.
Definition ISubDomain.h:75
ScopedPtrT< IInitialPartitioner > m_initial_partitioner
Initial partitioner.
bool m_use_internal_mesh_partitioner
true if it partitions the mesh internally
Handle on a mesh.
Definition MeshHandle.h:48
Reference to an instance.
std::string_view toStdStringView() const
Returns an STL view of the current string.
Definition String.cc:350
TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium) const
Flow for a debug message.
TraceMessage log() const
Flow for a log message.
TraceMessage info() const
Flow for an information message.
Node of a DOM tree.
Definition XmlNode.h:51
XmlNode attr(const String &name, bool throw_exception=false) const
Returns the attribute of name name.
Definition XmlNode.cc:257
bool null() const
True if the node is null.
Definition XmlNode.h:303
Integer valueAsInteger(bool throw_exception=false) const
Node value converted to integer.
Definition XmlNode.cc:441
__host__ __device__ double log(double v)
Natural logarithm of v.
Definition Math.h:42
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
eMeshAMRKind
AMR mesh type.
Definition MeshKind.h:49
std::int32_t Int32
Signed integer type of 32 bits.