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