Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
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/* Service Arcane gérant un maillage du jeu de données. */
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/*---------------------------------------------------------------------------*/
58class ArcaneCaseMeshService
60{
61 public:
62
63 explicit ArcaneCaseMeshService(const ServiceBuildInfo& sbi);
64
65 public:
66
67 void createMesh(const String& default_name) override;
68 void allocateMeshItems() override;
69 void partitionMesh() override;
70 void applyAdditionalOperations() override;
71
72 private:
73
74 ISubDomain* m_sub_domain = nullptr;
75 IPrimaryMesh* m_mesh = nullptr;
76 IMeshBuilder* m_mesh_builder = nullptr;
77 Ref<IMeshBuilder> m_mesh_builder_ref;
78 String m_mesh_file_name;
79 String m_partitioner_name;
80 String m_mesh_name;
83 bool m_is_allow_null_mesh = false;
84
85 private:
86
87 void _fillReadInfo(CaseMeshReaderReadInfo& read_info);
88 Ref<IMeshBuilder> _createBuilderFromFile(const CaseMeshReaderReadInfo& read_info);
89 void _initializeVariables();
90 void _doInitialPartition();
91 void _doInitialPartition2(const String& name);
92 void _setGhostLayerInfos();
93 void _checkMeshCreationAndAllocation(bool is_check_allocated);
94 void _setUniqueIdNumberingVersion();
95};
96
97/*---------------------------------------------------------------------------*/
98/*---------------------------------------------------------------------------*/
99
100ArcaneCaseMeshService::
101ArcaneCaseMeshService(const ServiceBuildInfo& sbi)
103, m_sub_domain(sbi.subDomain())
104{
105}
106
107/*---------------------------------------------------------------------------*/
108/*---------------------------------------------------------------------------*/
109
111createMesh(const String& default_name)
112{
114 ARCANE_FATAL("Mesh '{0}' has already been created", default_name);
116 m_mesh_name = default_name;
117 info() << "Creating mesh name='" << m_mesh_name << "' from ArcaneCaseMeshService";
118 // Le maillage par défaut 'Mesh0' n'a pas le droit d'être nul
119 m_is_allow_null_mesh = (default_name != "Mesh0");
120 MeshBuildInfo build_info(default_name);
121 ISubDomain* sd = m_sub_domain;
122 String filename = options()->filename();
123 bool has_filename = options()->filename.isPresent();
124 bool has_generator = options()->generator.isPresent();
125
126 // Il faut spécifier soit le nom du fichier, soit le générateur mais pas les deux
127 if ((has_filename && has_generator) || (!has_filename && !has_generator))
128 ARCANE_FATAL("In '{0}': one and one only of <{1}> or <{2}> has to be specified",
129 options()->configList()->xpathFullName(),
130 options()->generator.rootTagName(),
131 options()->filename.name());
132 if (has_filename) {
133 m_mesh_file_name = options()->filename();
134 if (m_mesh_file_name.empty())
135 ARCANE_FATAL("Invalid filename '{0}' in option '{1}'",
136 m_mesh_file_name,options()->filename.xpathFullName());
137 CaseMeshReaderReadInfo read_info;
138 _fillReadInfo(read_info);
139 auto& specific_reader = options()->specificReader;
140 if (specific_reader.isPresent()){
141 m_mesh_builder_ref = specific_reader()->createBuilder(read_info);
142 if (m_mesh_builder_ref.isNull())
143 ARCANE_FATAL("No 'IMeshBuilder' created by specific reader");
144 }
145 else{
146 m_mesh_builder_ref = _createBuilderFromFile(read_info);
147 }
148 m_mesh_builder = m_mesh_builder_ref.get();
149 }
150 else if (has_generator)
151 m_mesh_builder = options()->generator();
152 else
153 ARCANE_FATAL("Invalid operation");
154
155 ARCANE_CHECK_POINTER(m_mesh_builder);
156
157 // Indique la gestion des dimensions des mailles
158 eMeshCellDimensionKind mesh_dim_kind = options()->cellDimensionKind();
159 if (mesh_dim_kind != build_info.meshKind().meshDimensionKind()) {
160 MeshKind mesh_kind = build_info.meshKind();
161 mesh_kind.setMeshDimensionKind(mesh_dim_kind);
162 build_info.addMeshKind(mesh_kind);
163 }
164
165 m_mesh_builder->fillMeshBuildInfo(build_info);
166 // Le générateur peut forcer l'utilisation du partitionnement
167 if (build_info.isNeedPartitioning())
168 m_partitioner_name = options()->partitioner();
169
170 // Positionne avec des valeurs par défaut les champs non remplit.
171 if (build_info.factoryName().empty())
172 build_info.addFactoryName("ArcaneDynamicMeshFactory");
173 if (build_info.parallelMngRef().isNull())
174 build_info.addParallelMng(makeRef(sd->parallelMng()));
175
176 // Regarde si on veut personnaliser le MeshBuilder
177 IMeshBuilderCustomiser* customiser = options()->meshBuilderCustomiser();
178 if (customiser)
179 customiser->applyCustomisation(build_info);
180
181 IParallelMng* mesh_pm = build_info.parallelMngRef().get();
182 if (mesh_pm) {
183 IPrimaryMesh* pmesh = sd->meshMng()->meshFactoryMng()->createMesh(build_info);
184 // NOTE: The mesh may be null if the parallelMng() is not on all ranks
185 m_mesh = pmesh;
186 }
187 else if (!m_is_allow_null_mesh)
188 ARCANE_FATAL("No mesh created for '{0}'", m_mesh_name);
189}
190
191/*---------------------------------------------------------------------------*/
192/*---------------------------------------------------------------------------*/
193
196{
197 _checkMeshCreationAndAllocation(false);
198 if (!m_mesh)
199 return;
200
201 ARCANE_CHECK_POINTER(m_mesh_builder);
202
203 _setGhostLayerInfos();
204 _setUniqueIdNumberingVersion();
205
206 m_mesh_builder->allocateMeshItems(m_mesh);
207}
208
209/*---------------------------------------------------------------------------*/
210/*---------------------------------------------------------------------------*/
211
214{
215 _checkMeshCreationAndAllocation(true);
216
217 if (m_mesh && (m_mesh->meshPartInfo().nbPart() > 1))
218 if (!m_partitioner_name.empty())
219 _doInitialPartition();
220}
221
222/*---------------------------------------------------------------------------*/
223/*---------------------------------------------------------------------------*/
224
227{
228 _checkMeshCreationAndAllocation(true);
229
230 IMeshSubdivider* subdivider = options()->subdivider();
231 if (subdivider)
232 subdivider->subdivideMesh(m_mesh);
233
234 _initializeVariables();
235}
236
237/*---------------------------------------------------------------------------*/
238/*---------------------------------------------------------------------------*/
239
240void ArcaneCaseMeshService::
241_checkMeshCreationAndAllocation(bool is_check_allocated)
242{
244 ARCANE_FATAL("Mesh is not created. You should call createMesh() before");
245 if (m_mesh)
246 if (is_check_allocated && !m_mesh->isAllocated())
247 ARCANE_FATAL("Mesh is not allocated. You should call initializeMesh() before");
248}
249
250/*---------------------------------------------------------------------------*/
251/*---------------------------------------------------------------------------*/
252
253void ArcaneCaseMeshService::
254_fillReadInfo(CaseMeshReaderReadInfo& read_info)
255{
256 // Cherche l'extension du fichier.
257 String file_extension;
258 {
259 std::string_view fview = m_mesh_file_name.toStdStringView();
260 std::size_t extension_pos = fview.find_last_of('.');
261 if (extension_pos!=std::string_view::npos){
262 fview.remove_prefix(extension_pos+1);
263 file_extension = fview;
264 }
265 read_info.setFormat(file_extension);
266 }
267
268 // Nom du maillage à utiliser pour la lecture
269 // Normalement c'est le nom du maillage dans le jeu de données
270 // sauf en cas d'utilisation du partitionneur externe auquel cas
271 // c'est 'CPU%05d.$file_extension'.
272 String mesh_file_name = m_mesh_file_name;
273
274 String partitioner_name = options()->partitioner();
275 bool use_internal_partitioner = partitioner_name != "External";
276 if (use_internal_partitioner){
277 m_partitioner_name = partitioner_name;
278 }
279 else{
280 info() << "Using external partitioner";
281 int mesh_rank = m_sub_domain->parallelMng()->commRank();
282 char buf[128];
283 sprintf(buf,"CPU%05d.%s",mesh_rank,file_extension.localstr());
284 mesh_file_name = String(std::string_view(buf));
285 }
286
287 read_info.setFileName(mesh_file_name);
288
289 info() << "Mesh filename=" << mesh_file_name
290 << " extension=" << read_info.format() << " partitioner=" << partitioner_name;
291
292 read_info.setParallelRead(use_internal_partitioner);
293}
294
295/*---------------------------------------------------------------------------*/
296/*---------------------------------------------------------------------------*/
297
298Ref<IMeshBuilder> ArcaneCaseMeshService::
299_createBuilderFromFile(const CaseMeshReaderReadInfo& read_info)
300{
301 // Construit les services potentiels de lecture de maillage. Ce sont
302 // ceux qui implémentent ICaseMeshReader. Construit une instance
303 // de chaque service et appelle la méthode createBuilder(). Dès que l'une
304 // de ces méthodes renvoie une référence non nulle, on l'utilise
305 // pour générer le maillage.
306 ServiceBuilder<ICaseMeshReader> builder(m_sub_domain);
307 UniqueArray<Ref<ICaseMeshReader>> mesh_readers(builder.createAllInstances());
308
309 for( auto& mesh_reader_ref : mesh_readers ){
310 ICaseMeshReader* mesh_reader = mesh_reader_ref.get();
311 Ref<IMeshBuilder> builder = mesh_reader->createBuilder(read_info);
312 if (!builder.isNull())
313 return builder;
314 }
315
316 // Pas de service trouvé pour ce format de fichier.
317 // Affiche la liste des services disponibles et fait un fatal.
318 StringUniqueArray valid_names;
319 builder.getServicesNames(valid_names);
320 String available_readers = String::join(", ",valid_names);
321 ARCANE_FATAL("The mesh reader required for format '{0}' is not available."
322 "The following reader services are available: {1}",
323 read_info.format(),available_readers);
324}
325
326/*---------------------------------------------------------------------------*/
327/*---------------------------------------------------------------------------*/
328
329void ArcaneCaseMeshService::
330_doInitialPartition()
331{
332 if (!m_mesh)
333 return;
334 // N'utilise plus le service de partitionnement de test pour garantir
335 // avec ParMetis qu'on n'a pas de partitions vides car cela est maintenant
336 // normalement supporté.
337 const bool use_partitioner_tester = false;
338 String test_service = "MeshPartitionerTester";
339 if (use_partitioner_tester) {
340 Int64 nb_cell = m_mesh->nbCell();
341 Int64 min_nb_cell = m_mesh->parallelMng()->reduce(Parallel::ReduceMin,nb_cell);
342 info() << "Min nb cell=" << min_nb_cell;
343 if (min_nb_cell==0)
344 _doInitialPartition2(test_service);
345 else
346 info() << "Mesh name=" << m_mesh->name() << " have cells. Do not use " << test_service;
347 }
348 else
349 info() << "No basic partition first needed";
350 _doInitialPartition2(m_partitioner_name);
351}
352
353/*---------------------------------------------------------------------------*/
354/*---------------------------------------------------------------------------*/
355
356void ArcaneCaseMeshService::
357_doInitialPartition2(const String& partitioner_name)
358{
359 if (!m_mesh)
360 return;
361 info() << "Doing initial partitioning service=" << partitioner_name;
362 // NOTE: Ce service n'utilise que les partionneurs qui implémentent
363 // IMeshPartitionerBase et pas ceux (historiques) qui n'implémentent que
364 // IMeshPartitioner.
365 ServiceBuilder<IMeshPartitionerBase> sbuilder(m_sub_domain);
366 auto mesh_partitioner = sbuilder.createReference(partitioner_name,m_mesh);
367
368 IMesh* mesh = m_mesh;
369 bool is_dynamic = mesh->isDynamic();
370 mesh->modifier()->setDynamic(true);
371 mesh->utilities()->partitionAndExchangeMeshWithReplication(mesh_partitioner.get(),true);
372 mesh->modifier()->setDynamic(is_dynamic);
373}
374
375/*---------------------------------------------------------------------------*/
376/*---------------------------------------------------------------------------*/
377
378void ArcaneCaseMeshService::
379_initializeVariables()
380{
381 if (!m_mesh)
382 return;
383
384 IVariableMng* vm = m_sub_domain->variableMng();
385 const auto& vars_opt = options()->initialization().variable;
386 UniqueArray<String> errors;
387 for( Integer i=0, n=vars_opt.size(); i<n; ++i ){
388 const auto& o = vars_opt[i];
389 String var_name = o.name;
390 String group_name = o.group;
391 String value = o.value;
392 info() << "Initialize variable=" << var_name << " group=" << group_name << " value=" << value;
393 IVariable* var = vm->findMeshVariable(m_mesh,var_name);
394 if (!var){
395 errors.add(String::format("No variable named '{0}' exists",var_name));
396 continue;
397 }
398
399 // Alloue la variable si besoin.
400 if (!var->isUsed())
401 var->setUsed(true);
402 IItemFamily* var_family = var->itemFamily();
403 if (!var_family){
404 errors.add(String::format("Variable '{0}' has no family",var->fullName()));
405 continue;
406 }
407
408 ItemGroup group = var_family->findGroup(group_name);
409 if (group.null()){
410 errors.add(String::format("No group named '{0}' exists in family '{1}'",
411 group_name,var_family->name()));
412 continue;
413 }
414
415 bool ret = var->initialize(group,value);
416 if (ret){
417 errors.add(String::format("Bad value '{0}' for initializing variable '{1}'",
418 value,var->fullName()));
419 continue;
420 }
421 }
422 if (!errors.empty()){
423 for( String s : errors )
424 pinfo() << "ERROR: " << s;
425 ARCANE_FATAL("Variable initialization failed for option '{0}'",
426 vars_opt.xpathFullName());
427 }
428}
429
430/*---------------------------------------------------------------------------*/
431/*---------------------------------------------------------------------------*/
432
433void ArcaneCaseMeshService::
434_setGhostLayerInfos()
435{
436 IGhostLayerMng* gm = m_mesh->ghostLayerMng();
437 if (!gm)
438 return;
439
440 // Positionne les infos sur les mailles fantômes.
441 // TODO Cela est fait pour rester compatible avec le mode historique mais
442 // il faudrait pouvoir gérer cela autrement (via un service par exemple)
443 Integer nb_ghost_layer = options()->nbGhostLayer();
444 if (nb_ghost_layer >= 0) {
445 info() << "Set number of ghost layers to '" << nb_ghost_layer << "' from caseoption";
446 gm->setNbGhostLayer(nb_ghost_layer);
447 }
448
449 Integer builder_version = options()->ghostLayerBuilderVersion();
450 if (builder_version >= 0) {
451 info() << "Set ghostlayer builder version to '" << builder_version << "' from caseoption";
452 gm->setBuilderVersion(builder_version);
453 }
454}
455
456/*---------------------------------------------------------------------------*/
457/*---------------------------------------------------------------------------*/
458
459void ArcaneCaseMeshService::
460_setUniqueIdNumberingVersion()
461{
462 // NOTE: actuellement (12/2024) l'implémentation 'PolyedralMesh' lève une
463 // exception si on appelle meshUniqueIdMng(). On ne le fait que si
464 // l'option est présente.
465 if (options()->faceNumberingVersion.isPresent()) {
466 Int32 v = options()->faceNumberingVersion.value();
467 info() << "Set face uniqueId numbering version to '" << v << "' from caseoption";
468 IMeshUniqueIdMng* mum = m_mesh->meshUniqueIdMng();
469 mum->setFaceBuilderVersion(v);
470 }
471
472 if (options()->edgeNumberingVersion.isPresent()) {
473 Int32 v = options()->edgeNumberingVersion.value();
474 info() << "Set edge uniqueId numbering version to '" << v << "' from caseoption";
475 IMeshUniqueIdMng* mum = m_mesh->meshUniqueIdMng();
476 mum->setEdgeBuilderVersion(v);
477 }
478}
479
480/*---------------------------------------------------------------------------*/
481/*---------------------------------------------------------------------------*/
482
483ARCANE_REGISTER_SERVICE_ARCANECASEMESHSERVICE(ArcaneCaseMeshService,
485
486/*---------------------------------------------------------------------------*/
487/*---------------------------------------------------------------------------*/
488
489} // End namespace Arcane
490
491/*---------------------------------------------------------------------------*/
492/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Ce fichier contient les différentes fabriques de services et macro pour enregistrer les 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.
Service Arcane de lecture ou de génération d'un maillage à partir du fichier d'options du jeu de donn...
void createMesh(const String &default_name) override
Créé le maillage avec le nom name.
void allocateMeshItems() override
Alloue les éléments du maillage.
void partitionMesh() override
Partitionne le maillage.
bool m_has_created_mesh_called
True if createMesh() has already been called.
void applyAdditionalOperations() override
Applique les opérations après tout le reste.
Informations nécessaires pour la lecture d'un fichier de maillage.
const Type & value() const
Retourne la valeur de l'option.
bool isPresent() const
Retourne true si l'option est présente.
Interface d'un service de personnalisation de constructeur de maillage.
virtual void applyCustomisation(MeshBuildInfo &build_info)=0
Modifie build_info pour personnaliser la création du maillage.
Interface d'un service de création/lecture du maillage.
virtual IPrimaryMesh * createMesh(const MeshBuildInfo &build_info)=0
Créé un maillage ou un sous-maillage.
virtual IMeshFactoryMng * meshFactoryMng() const =0
Fabrique de maillages associée à ce gestionnaire.
virtual void subdivideMesh(IPrimaryMesh *mesh)=0
Subdivise le maillage mesh.
virtual bool isAllocated()=0
Vrai si le maillage est allouée.
Interface du gestionnaire de parallélisme pour un sous-domaine.
Interface du gestionnaire d'un sous-domaine.
Definition ISubDomain.h:77
virtual IParallelMng * parallelMng()=0
Retourne le gestionnaire de parallélisme.
virtual IMeshMng * meshMng() const =0
Retourne le gestionnaire de maillage.
Paramètres nécessaires à la construction d'un maillage.
const MeshKind meshKind() const
Caractéristiques du maillage.
MeshBuildInfo & addParallelMng(Ref< IParallelMng > pm)
Positionne le gestionnaire de parallélisme pour créér la maillage.
bool isNeedPartitioning() const
Indique si le lecteur/générateur nécessite un partitionnement.
Ref< IParallelMng > parallelMngRef() const
Gestionnaire de parallélisme dans le cas d'un nouveau maillage.
const String & factoryName() const
Nom de la fabrique pour créer le maillage (via IMeshFactory).
MeshBuildInfo & addMeshKind(const MeshKind &v)
Positionne les caractéristiques du maillage.
MeshBuildInfo & addFactoryName(const String &factory_name)
Positionne le nom de la fabrique pour créer ce maillage.
Caractéristiques d'un maillage.
Definition MeshKind.h:111
Référence à une instance.
Structure contenant les informations pour créer un service.
Chaîne de caractères unicode.
bool empty() const
Vrai si la chaîne est vide (nulle ou "").
Definition String.cc:316
std::string_view toStdStringView() const
Retourne une vue de la STL sur la chaîne actuelle.
Definition String.cc:349
TraceMessage pinfo() const
Flot pour un message d'information en parallèle.
TraceMessage info() const
Flot pour un message d'information.
@ ReduceMin
Minimum des valeurs.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
eMeshCellDimensionKind
Types de gestion de la dimension du maillage.
Definition MeshKind.h:72
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Créé une référence sur un pointeur.
UniqueArray< String > StringUniqueArray
Tableau dynamique à une dimension de chaînes de caractères.
Definition UtilsTypes.h:357
std::int32_t Int32
Type entier signé sur 32 bits.