Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
ArcaneCaseMeshService.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/* ArcaneCaseMeshService.cc (C) 2000-2026 */
9/* */
10/* Arcane Service managing a dataset mesh. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/CommandLineArguments.h"
15
17#include "arcane/core/ServiceBuilder.h"
18#include "arcane/core/ICaseMeshService.h"
19#include "arcane/core/ICaseMeshReader.h"
20#include "arcane/core/IMeshBuilder.h"
21#include "arcane/core/IMeshBuilderCustomiser.h"
22#include "arcane/core/IPrimaryMesh.h"
23#include "arcane/core/IItemFamily.h"
24#include "arcane/core/IMeshPartitionerBase.h"
25#include "arcane/core/IVariableMng.h"
26#include "arcane/core/IMeshModifier.h"
27#include "arcane/core/IMeshUtilities.h"
28#include "arcane/core/IParallelMng.h"
29#include "arcane/core/MeshBuildInfo.h"
30#include "arcane/core/IMeshMng.h"
31#include "arcane/core/IMeshFactoryMng.h"
32#include "arcane/core/IGhostLayerMng.h"
33#include "arcane/core/MeshPartInfo.h"
34#include "arcane/core/IMeshSubdivider.h"
35#include "arcane/core/IMeshUniqueIdMng.h"
36#include "arcane/core/ParallelMngUtils.h"
37#include "arcane/core/internal/StringVariableReplace.h"
38
39namespace Arcane
40{
41
42} // namespace Arcane
43
44#include "arcane/impl/ArcaneCaseMeshService_axl.h"
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
48
49namespace Arcane
50{
51
52/*---------------------------------------------------------------------------*/
53/*---------------------------------------------------------------------------*/
57class ArcaneCaseMeshService
59{
60 public:
61
62 explicit ArcaneCaseMeshService(const ServiceBuildInfo& sbi);
63
64 public:
65
66 void createMesh(const String& default_name) override;
67 void allocateMeshItems() override;
68 void partitionMesh() override;
69 void applyAdditionalOperations() override;
70
71 private:
72
73 ISubDomain* m_sub_domain = nullptr;
74 IPrimaryMesh* m_mesh = nullptr;
75 IMeshBuilder* m_mesh_builder = nullptr;
76 Ref<IMeshBuilder> m_mesh_builder_ref;
77 String m_mesh_file_name;
78 String m_partitioner_name;
79 String m_mesh_name;
82 bool m_is_allow_null_mesh = false;
83
84 private:
85
86 void _fillReadInfo(CaseMeshReaderReadInfo& read_info);
87 Ref<IMeshBuilder> _createBuilderFromFile(const CaseMeshReaderReadInfo& read_info);
88 void _initializeVariables();
89 void _doInitialPartition();
90 void _doInitialPartition2(const String& name);
91 void _setGhostLayerInfos();
92 void _checkMeshCreationAndAllocation(bool is_check_allocated);
93 void _setUniqueIdNumberingVersion();
94};
95
96/*---------------------------------------------------------------------------*/
97/*---------------------------------------------------------------------------*/
98
99ArcaneCaseMeshService::
100ArcaneCaseMeshService(const ServiceBuildInfo& sbi)
102, m_sub_domain(sbi.subDomain())
103{
104}
105
106/*---------------------------------------------------------------------------*/
107/*---------------------------------------------------------------------------*/
108
110createMesh(const String& default_name)
111{
113 ARCANE_FATAL("Mesh '{0}' has already been created", default_name);
115 m_mesh_name = default_name;
116 info() << "Creating mesh name='" << m_mesh_name << "' from ArcaneCaseMeshService";
117 // The default mesh 'Mesh0' is not allowed to be null
118 m_is_allow_null_mesh = (default_name != "Mesh0");
119 MeshBuildInfo build_info(default_name);
120 ISubDomain* sd = m_sub_domain;
121 String filename = options()->filename();
122 bool has_filename = options()->filename.isPresent();
123 bool has_generator = options()->generator.isPresent();
124
125 // Either the file name or the generator must be specified, but not both
126 if ((has_filename && has_generator) || (!has_filename && !has_generator))
127 ARCANE_FATAL("In '{0}': one and one only of <{1}> or <{2}> has to be specified",
128 options()->configList()->xpathFullName(),
129 options()->generator.rootTagName(),
130 options()->filename.name());
131 if (has_filename) {
132 m_mesh_file_name = options()->filename();
133 if (m_mesh_file_name.empty())
134 ARCANE_FATAL("Invalid filename '{0}' in option '{1}'",
135 m_mesh_file_name, options()->filename.xpathFullName());
136 CaseMeshReaderReadInfo read_info;
137 _fillReadInfo(read_info);
138 auto& specific_reader = options()->specificReader;
139 if (specific_reader.isPresent()) {
140 m_mesh_builder_ref = specific_reader()->createBuilder(read_info);
141 if (m_mesh_builder_ref.isNull())
142 ARCANE_FATAL("No 'IMeshBuilder' created by specific reader");
143 }
144 else {
145 m_mesh_builder_ref = _createBuilderFromFile(read_info);
146 }
147 m_mesh_builder = m_mesh_builder_ref.get();
148 }
149 else if (has_generator)
150 m_mesh_builder = options()->generator();
151 else
152 ARCANE_FATAL("Invalid operation");
153
154 ARCANE_CHECK_POINTER(m_mesh_builder);
155
156 // Indicates the management of cell dimensions
157 eMeshCellDimensionKind mesh_dim_kind = options()->cellDimensionKind();
158 if (mesh_dim_kind != build_info.meshKind().meshDimensionKind()) {
159 MeshKind mesh_kind = build_info.meshKind();
160 mesh_kind.setMeshDimensionKind(mesh_dim_kind);
161 build_info.addMeshKind(mesh_kind);
162 }
163
164 m_mesh_builder->fillMeshBuildInfo(build_info);
165 // The generator can force the use of partitioning
166 if (build_info.isNeedPartitioning())
167 m_partitioner_name = options()->partitioner();
168
169 // Positions fields not filled with default values.
170 if (build_info.factoryName().empty())
171 build_info.addFactoryName("ArcaneDynamicMeshFactory");
172 if (build_info.parallelMngRef().isNull())
173 build_info.addParallelMng(makeRef(sd->parallelMng()));
174
175 // Check if we want to customise the MeshBuilder
176 IMeshBuilderCustomiser* customiser = options()->meshBuilderCustomiser();
177 if (customiser)
178 customiser->applyCustomisation(build_info);
179
180 IParallelMng* mesh_pm = build_info.parallelMngRef().get();
181 if (mesh_pm) {
182 IPrimaryMesh* pmesh = sd->meshMng()->meshFactoryMng()->createMesh(build_info);
183 // NOTE: The mesh may be null if the parallelMng() is not on all ranks
184 m_mesh = pmesh;
185 }
186 else if (!m_is_allow_null_mesh)
187 ARCANE_FATAL("No mesh created for '{0}'", m_mesh_name);
188}
189
190/*---------------------------------------------------------------------------*/
191/*---------------------------------------------------------------------------*/
192
195{
196 _checkMeshCreationAndAllocation(false);
197 if (!m_mesh)
198 return;
199
200 ARCANE_CHECK_POINTER(m_mesh_builder);
201
202 _setGhostLayerInfos();
203 _setUniqueIdNumberingVersion();
204
205 m_mesh_builder->allocateMeshItems(m_mesh);
206}
207
208/*---------------------------------------------------------------------------*/
209/*---------------------------------------------------------------------------*/
210
213{
214 _checkMeshCreationAndAllocation(true);
215
216 if (m_mesh && (m_mesh->meshPartInfo().nbPart() > 1))
217 if (!m_partitioner_name.empty())
218 _doInitialPartition();
219}
220
221/*---------------------------------------------------------------------------*/
222/*---------------------------------------------------------------------------*/
223
226{
227 _checkMeshCreationAndAllocation(true);
228
229 IMeshSubdivider* subdivider = options()->subdivider();
230 if (subdivider)
231 subdivider->subdivideMesh(m_mesh);
232
233 _initializeVariables();
234}
235
236/*---------------------------------------------------------------------------*/
237/*---------------------------------------------------------------------------*/
238
239void ArcaneCaseMeshService::
240_checkMeshCreationAndAllocation(bool is_check_allocated)
241{
243 ARCANE_FATAL("Mesh is not created. You should call createMesh() before");
244 if (m_mesh)
245 if (is_check_allocated && !m_mesh->isAllocated())
246 ARCANE_FATAL("Mesh is not allocated. You should call initializeMesh() before");
247}
248
249/*---------------------------------------------------------------------------*/
250/*---------------------------------------------------------------------------*/
251
252void ArcaneCaseMeshService::
253_fillReadInfo(CaseMeshReaderReadInfo& read_info)
254{
255 // Searches for the file extension.
256 String file_extension;
257 {
258 std::string_view fview = m_mesh_file_name.toStdStringView();
259 std::size_t extension_pos = fview.find_last_of('.');
260 if (extension_pos != std::string_view::npos) {
261 fview.remove_prefix(extension_pos + 1);
262 file_extension = fview;
263 }
264 read_info.setFormat(file_extension);
265 }
266
267 // Mesh file name to use for reading
268 // Normally it is the mesh name in the dataset
269 // unless an external partitioner is used, in which case
270 // it is 'CPU%05d.$file_extension'.
271 String mesh_file_name = m_mesh_file_name;
272
273 String partitioner_name = options()->partitioner();
274 bool use_internal_partitioner = partitioner_name != "External";
275 if (use_internal_partitioner) {
276 m_partitioner_name = partitioner_name;
277 }
278 else {
279 info() << "Using external partitioner";
280 int mesh_rank = m_sub_domain->parallelMng()->commRank();
281 char buf[128];
282 sprintf(buf, "CPU%05d.%s", mesh_rank, file_extension.localstr());
283 mesh_file_name = String(std::string_view(buf));
284 }
285
286 read_info.setFileName(mesh_file_name);
287
288 info() << "Mesh filename=" << mesh_file_name
289 << " extension=" << read_info.format() << " partitioner=" << partitioner_name;
290
291 read_info.setParallelRead(use_internal_partitioner);
292}
293
294/*---------------------------------------------------------------------------*/
295/*---------------------------------------------------------------------------*/
296
297Ref<IMeshBuilder> ArcaneCaseMeshService::
298_createBuilderFromFile(const CaseMeshReaderReadInfo& read_info)
299{
300 // Constructs potential mesh reading services. These are
301 // those that implement ICaseMeshReader. An instance
302 // of each service is constructed and the createBuilder() method is called.
303 // As soon as one of these methods returns a non-null reference, it is used
304 // to generate the mesh.
305 ServiceBuilder<ICaseMeshReader> builder(m_sub_domain);
306 UniqueArray<Ref<ICaseMeshReader>> mesh_readers(builder.createAllInstances());
307
308 for (auto& mesh_reader_ref : mesh_readers) {
309 ICaseMeshReader* mesh_reader = mesh_reader_ref.get();
310 Ref<IMeshBuilder> builder = mesh_reader->createBuilder(read_info);
311 if (!builder.isNull())
312 return builder;
313 }
314
315 // No service found for this file format.
316 // Displays the list of available services and throws a fatal error.
317 StringUniqueArray valid_names;
318 builder.getServicesNames(valid_names);
319 String available_readers = String::join(", ", valid_names);
320 ARCANE_FATAL("The mesh reader required for format '{0}' is not available."
321 "The following reader services are available: {1}",
322 read_info.format(), available_readers);
323}
324
325/*---------------------------------------------------------------------------*/
326/*---------------------------------------------------------------------------*/
327
328void ArcaneCaseMeshService::
329_doInitialPartition()
330{
331 if (!m_mesh)
332 return;
333 // No longer uses the test partitioning service to ensure
334 // that with ParMetis there are no empty partitions, as this is now
335 // normally supported.
336 const bool use_partitioner_tester = false;
337 String test_service = "MeshPartitionerTester";
338 if (use_partitioner_tester) {
339 Int64 nb_cell = m_mesh->nbCell();
340 Int64 min_nb_cell = m_mesh->parallelMng()->reduce(Parallel::ReduceMin, nb_cell);
341 info() << "Min nb cell=" << min_nb_cell;
342 if (min_nb_cell == 0)
343 _doInitialPartition2(test_service);
344 else
345 info() << "Mesh name=" << m_mesh->name() << " have cells. Do not use " << test_service;
346 }
347 else
348 info() << "No basic partition first needed";
349 _doInitialPartition2(m_partitioner_name);
350}
351
352/*---------------------------------------------------------------------------*/
353/*---------------------------------------------------------------------------*/
354
355void ArcaneCaseMeshService::
356_doInitialPartition2(const String& partitioner_name)
357{
358 if (!m_mesh)
359 return;
360 info() << "Doing initial partitioning service=" << partitioner_name;
361 // NOTE: This service only uses partitioners that implement
362 // IMeshPartitionerBase and not those (historical) that only implement
363 // IMeshPartitioner.
364 ServiceBuilder<IMeshPartitionerBase> sbuilder(m_sub_domain);
365 auto mesh_partitioner = sbuilder.createReference(partitioner_name, m_mesh);
366
367 IMesh* mesh = m_mesh;
368 bool is_dynamic = mesh->isDynamic();
369 mesh->modifier()->setDynamic(true);
370 mesh->utilities()->partitionAndExchangeMeshWithReplication(mesh_partitioner.get(), true);
371 mesh->modifier()->setDynamic(is_dynamic);
372}
373
374/*---------------------------------------------------------------------------*/
375/*---------------------------------------------------------------------------*/
376
377void ArcaneCaseMeshService::
378_initializeVariables()
379{
380 if (!m_mesh)
381 return;
382
383 IVariableMng* vm = m_sub_domain->variableMng();
384 const auto& vars_opt = options()->initialization().variable;
385 UniqueArray<String> errors;
386 for (Integer i = 0, n = vars_opt.size(); i < n; ++i) {
387 const auto& o = vars_opt[i];
388 String var_name = o.name;
389 String group_name = o.group;
390 String value = o.value;
391 info() << "Initialize variable=" << var_name << " group=" << group_name << " value=" << value;
392 IVariable* var = vm->findMeshVariable(m_mesh, var_name);
393 if (!var) {
394 errors.add(String::format("No variable named '{0}' exists", var_name));
395 continue;
396 }
397
398 // Allocate the variable if necessary.
399 if (!var->isUsed())
400 var->setUsed(true);
401 IItemFamily* var_family = var->itemFamily();
402 if (!var_family) {
403 errors.add(String::format("Variable '{0}' has no family", var->fullName()));
404 continue;
405 }
406
407 ItemGroup group = var_family->findGroup(group_name);
408 if (group.null()) {
409 errors.add(String::format("No group named '{0}' exists in family '{1}'",
410 group_name, var_family->name()));
411 continue;
412 }
413
414 bool ret = var->initialize(group, value);
415 if (ret) {
416 errors.add(String::format("Bad value '{0}' for initializing variable '{1}'",
417 value, var->fullName()));
418 continue;
419 }
420 }
421 if (!errors.empty()) {
422 for (String s : errors)
423 pinfo() << "ERROR: " << s;
424 ARCANE_FATAL("Variable initialization failed for option '{0}'",
425 vars_opt.xpathFullName());
426 }
427}
428
429/*---------------------------------------------------------------------------*/
430/*---------------------------------------------------------------------------*/
431
432void ArcaneCaseMeshService::
433_setGhostLayerInfos()
434{
435 IGhostLayerMng* gm = m_mesh->ghostLayerMng();
436 if (!gm)
437 return;
438
439 // Positions the info on the ghost cells.
440 // TODO This is done to remain compatible with the historical mode but
441 // it should be possible to manage this differently (via a service for example)
442 Integer nb_ghost_layer = options()->nbGhostLayer();
443 if (nb_ghost_layer >= 0) {
444 info() << "Set number of ghost layers to '" << nb_ghost_layer << "' from caseoption";
445 gm->setNbGhostLayer(nb_ghost_layer);
446 }
447
448 Integer builder_version = options()->ghostLayerBuilderVersion();
449 if (builder_version >= 0) {
450 info() << "Set ghostlayer builder version to '" << builder_version << "' from caseoption";
451 gm->setBuilderVersion(builder_version);
452 }
453}
454
455/*---------------------------------------------------------------------------*/
456/*---------------------------------------------------------------------------*/
457
458void ArcaneCaseMeshService::
459_setUniqueIdNumberingVersion()
460{
461 // NOTE: currently (12/2024) the 'PolyedralMesh' implementation raises a
462 // exception if meshUniqueIdMng() is called. We only do this if
463 // the option is present.
464 if (options()->faceNumberingVersion.isPresent()) {
465 Int32 v = options()->faceNumberingVersion.value();
466 info() << "Set face uniqueId numbering version to '" << v << "' from caseoption";
467 IMeshUniqueIdMng* mum = m_mesh->meshUniqueIdMng();
468 mum->setFaceBuilderVersion(v);
469 }
470
471 if (options()->edgeNumberingVersion.isPresent()) {
472 Int32 v = options()->edgeNumberingVersion.value();
473 info() << "Set edge uniqueId numbering version to '" << v << "' from caseoption";
474 IMeshUniqueIdMng* mum = m_mesh->meshUniqueIdMng();
475 mum->setEdgeBuilderVersion(v);
476 }
477}
478
479/*---------------------------------------------------------------------------*/
480/*---------------------------------------------------------------------------*/
481
482ARCANE_REGISTER_SERVICE_ARCANECASEMESHSERVICE(ArcaneCaseMeshService,
484
485/*---------------------------------------------------------------------------*/
486/*---------------------------------------------------------------------------*/
487
488} // End namespace Arcane
489
490/*---------------------------------------------------------------------------*/
491/*---------------------------------------------------------------------------*/
#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.
This file contains the various service factories and macros for registering services.
Generation de la classe de base du Service.
CaseOptionsArcaneCaseMeshService * options() const
Options du jeu de données du service.
ArcaneArcaneCaseMeshServiceObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
Arcane Service for reading or generating a mesh from the case option file.
void createMesh(const String &default_name) override
Creates the mesh with the name name.
void allocateMeshItems() override
Allocates the mesh items.
void partitionMesh() override
Partitions the mesh.
bool m_has_created_mesh_called
True if createMesh() has already been called.
void applyAdditionalOperations() override
Applies operations after everything else.
Necessary information for reading a mesh file.
const Type & value() const
Returns the value of the option.
bool isPresent() const
Returns true if the option is present.
Interface of a mesh builder customisation service.
virtual void applyCustomisation(MeshBuildInfo &build_info)=0
Modify build_info to customise mesh creation.
Interface of a mesh creation/reading service.
virtual IPrimaryMesh * createMesh(const MeshBuildInfo &build_info)=0
Creates a mesh or a sub-mesh.
virtual IMeshFactoryMng * meshFactoryMng() const =0
Mesh factory associated with this manager.
virtual void subdivideMesh(IPrimaryMesh *mesh)=0
Subdivides the mesh mesh.
virtual bool isAllocated()=0
True if the mesh is allocated.
Interface of the parallelism manager for a subdomain.
Interface of the subdomain manager.
Definition ISubDomain.h:78
virtual IParallelMng * parallelMng()=0
Returns the parallelism manager.
virtual IMeshMng * meshMng() const =0
Returns the mesh manager.
Parameters necessary for building a mesh.
const MeshKind meshKind() const
Mesh characteristics.
MeshBuildInfo & addParallelMng(Ref< IParallelMng > pm)
Sets the parallelism manager to create the mesh.
bool isNeedPartitioning() const
Indicates if the reader/generator requires partitioning.
Ref< IParallelMng > parallelMngRef() const
Parallelism manager in the case of a new mesh.
const String & factoryName() const
Factory name to create the mesh (via IMeshFactory).
MeshBuildInfo & addMeshKind(const MeshKind &v)
Sets the mesh characteristics.
MeshBuildInfo & addFactoryName(const String &factory_name)
Sets the factory name to create this mesh.
Characteristics of a mesh.
Definition MeshKind.h:113
Reference to an instance.
Structure containing the information to create a service.
bool empty() const
True if the string is empty (null or "").
Definition String.cc:317
std::string_view toStdStringView() const
Returns an STL view of the current string.
Definition String.cc:350
TraceMessage pinfo() const
Flow for a parallel information message.
TraceMessage info() const
Flow for an information message.
@ ReduceMin
Minimum of values.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
eMeshCellDimensionKind
Types of mesh dimension management.
Definition MeshKind.h:73
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
UniqueArray< String > StringUniqueArray
Dynamic 1D array of strings.
Definition UtilsTypes.h:353
std::int32_t Int32
Signed integer type of 32 bits.