Arcane  v3.15.0.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
IncrementalComponentModifier.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 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/* IncrementalComponentModifier.cc (C) 2000-2025 */
9/* */
10/* Modification incrémentale des constituants. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/materials/internal/IncrementalComponentModifier.h"
15
16#include "arcane/utils/ITraceMng.h"
17#include "arcane/utils/FunctorUtils.h"
18#include "arcane/utils/ValueConvert.h"
19
20#include "arcane/core/IItemFamily.h"
21#include "arcane/core/materials/IMeshMaterialVariable.h"
22
23#include "arcane/materials/internal/MeshMaterialMng.h"
24#include "arcane/materials/internal/ConstituentConnectivityList.h"
25#include "arcane/materials/internal/AllEnvData.h"
26
27#include "arcane/accelerator/core/ProfileRegion.h"
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Materials
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38IncrementalComponentModifier::
39IncrementalComponentModifier(AllEnvData* all_env_data, const RunQueue& queue)
40: TraceAccessor(all_env_data->traceMng())
41, m_all_env_data(all_env_data)
42, m_material_mng(all_env_data->m_material_mng)
43, m_work_info(queue.allocationOptions(), queue.memoryRessource())
44, m_queue(queue)
45{
46 // 0 si on utilise la copie typée (mode historique) et une commande par variable
47 // 1 si on utilise la copie générique et une commande par variable
48 // 2 si on utilise la copie générique et une commande pour toutes les variables
49 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_USE_GENERIC_COPY_BETWEEN_PURE_AND_PARTIAL", true)) {
50 m_use_generic_copy_between_pure_and_partial = v.value();
51 }
52 else {
53 // Par défaut sur un accélérateur et en multi-threading, on utilise la copie
54 // avec une seule file, car c'est le mécanisme le plus performant.
55 if (queue.executionPolicy() != Accelerator::eExecutionPolicy::Sequential)
56 m_use_generic_copy_between_pure_and_partial = 2;
57 }
58 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_FORCE_MULTIPLE_COMMAND_FOR_MATERIAL_RESIZE", true)) {
59 m_force_multiple_command_for_resize = (v.value());
60 info() << "Force using multiple command for resize = " << m_force_multiple_command_for_resize;
61 }
62}
63
64/*---------------------------------------------------------------------------*/
65/*---------------------------------------------------------------------------*/
66
67void IncrementalComponentModifier::
68initialize(bool is_debug)
69{
70 m_is_debug = is_debug;
71 Int32 max_local_id = m_material_mng->mesh()->cellFamily()->maxLocalId();
72 Int32 nb_mat = m_material_mng->materials().size();
73 Int32 nb_env = m_material_mng->environments().size();
74 m_work_info.initialize(max_local_id, nb_mat, nb_env, m_queue);
75 m_work_info.is_verbose = is_debug || (traceMng()->verbosityLevel() >= 5);
76}
77
78/*---------------------------------------------------------------------------*/
79/*---------------------------------------------------------------------------*/
80
81void IncrementalComponentModifier::
82finalize()
83{
84}
85
86/*---------------------------------------------------------------------------*/
87/*---------------------------------------------------------------------------*/
88/*!
89 * \brief Transforme les entités pour un milieu.
90 *
91 * Parcours le milieux \a env et
92 * convertie les mailles pures en mailles partielles ou
93 * inversement. Après conversion, les valeurs correspondantes aux
94 * mailles modifiées sont mises à jour pour chaque variable.
95 *
96 * Si \a is_add est vrai, alors on transforme de pure en partiel
97 * (ajout de matériau) sinon on transforme de partiel en pure
98 * (suppression d'un matériau)
99 */
100void IncrementalComponentModifier::
101_switchCellsForMaterials(const MeshMaterial* modified_mat,
102 SmallSpan<const Int32> ids)
103{
104 const bool is_add = m_work_info.isAdd();
105 const bool is_device = m_queue.isAcceleratorPolicy();
106 SmallSpan<const bool> is_materials_modified = m_work_info.m_is_materials_modified.view(false);
107
108 for (MeshEnvironment* true_env : m_material_mng->trueEnvironments()) {
109 for (MeshMaterial* mat : true_env->trueMaterials()) {
110 // Ne traite pas le matériau en cours de modification.
111 if (mat == modified_mat)
112 continue;
113
114 if (!is_materials_modified[mat->id()])
115 continue;
116
117 if (!is_device) {
118 m_work_info.pure_local_ids.clearHost();
119 m_work_info.partial_indexes.clearHost();
120 }
121
122 MeshMaterialVariableIndexer* indexer = mat->variableIndexer();
123
124 info(4) << "MatTransformCells is_add?=" << is_add << " indexer=" << indexer->name()
125 << " mat_id=" << mat->id();
126
127 Int32 nb_transformed = _computeCellsToTransformForMaterial(mat, ids);
128 info(4) << "nb_transformed=" << nb_transformed;
129 if (nb_transformed == 0)
130 continue;
131 indexer->transformCells(m_work_info, m_queue, false);
132 _resetTransformedCells(ids);
133
134 auto pure_local_ids = m_work_info.pure_local_ids.view(is_device);
135 auto partial_indexes = m_work_info.partial_indexes.view(is_device);
136
137 Int32 nb_pure = pure_local_ids.size();
138 Int32 nb_partial = partial_indexes.size();
139 info(4) << "NB_MAT_TRANSFORM pure=" << nb_pure
140 << " partial=" << nb_partial << " name=" << mat->name()
141 << " is_device?=" << is_device
142 << " is_modified?=" << is_materials_modified[mat->id()];
143
144 CopyBetweenPartialAndGlobalArgs args(indexer->index(), pure_local_ids,
145 partial_indexes,
146 m_do_copy_between_partial_and_pure,
147 is_add,
148 m_queue);
149 _copyBetweenPartialsAndGlobals(args);
150 }
151 }
152}
153
154/*---------------------------------------------------------------------------*/
155/*---------------------------------------------------------------------------*/
156/*!
157 * \brief Transforme les entités pour les milieux.
158 *
159 * Parcours les milieux, sauf le milieu modifié \a modified_env et
160 * pour chacun convertie les mailles pures en mailles partielles ou
161 * inversement. Après conversion, les valeurs correspondantes aux
162 * mailles modifiées sont mises à jour pour chaque variable.
163 *
164 * Si \a is_add est vrai, alors on transforme de pure en partiel
165 * (dans le cas d'ajout de matériau) sinon on transforme de partiel
166 * en pure (dans le cas de suppression d'un matériau)
167 */
168void IncrementalComponentModifier::
169_switchCellsForEnvironments(const IMeshEnvironment* modified_env,
170 SmallSpan<const Int32> ids)
171{
172 const bool is_add = m_work_info.isAdd();
173 const bool is_device = m_queue.isAcceleratorPolicy();
174 SmallSpan<const bool> is_environments_modified = m_work_info.m_is_environments_modified.view(false);
175
176 // Ne copie pas les valeurs partielles des milieux vers les valeurs globales
177 // en cas de suppression de mailles, car cela sera fait avec la valeur matériau
178 // correspondante. Cela permet d'avoir le même comportement que sans
179 // optimisation. Ce n'est pas actif par défaut pour compatibilité avec l'existant.
180 const bool is_copy = is_add || !(m_material_mng->isUseMaterialValueWhenRemovingPartialValue());
181
182 Int32 nb_transformed = _computeCellsToTransformForEnvironments(ids);
183 info(4) << "Compute Cells for environments nb_transformed=" << nb_transformed;
184 if (nb_transformed == 0)
185 return;
186
187 for (const MeshEnvironment* env : m_material_mng->trueEnvironments()) {
188 // Ne traite pas le milieu en cours de modification.
189 if (env == modified_env)
190 continue;
191 // Si je suis mono matériau, la mise à jour de l'indexeur a été faite par le matériau
192 if (env->isMonoMaterial())
193 continue;
194
195 const Int32 env_id = env->id();
196
197 if (!is_environments_modified[env_id])
198 continue;
199
200 if (!is_device) {
201 m_work_info.pure_local_ids.clearHost();
202 m_work_info.partial_indexes.clearHost();
203 }
204
205 MeshMaterialVariableIndexer* indexer = env->variableIndexer();
206
207 info(4) << "EnvTransformCells is_add?=" << is_add
208 << " env_id=" << env_id
209 << " indexer=" << indexer->name() << " nb_item=" << ids.size();
210
211 indexer->transformCells(m_work_info, m_queue, true);
212
213 SmallSpan<const Int32> pure_local_ids = m_work_info.pure_local_ids.view(is_device);
214 SmallSpan<const Int32> partial_indexes = m_work_info.partial_indexes.view(is_device);
215 const Int32 nb_pure = pure_local_ids.size();
216
217 info(4) << "NB_ENV_TRANSFORM nb_pure=" << nb_pure << " name=" << env->name()
218 << " is_modified=" << is_environments_modified[env_id];
219
220 if (is_copy) {
221 CopyBetweenPartialAndGlobalArgs copy_args(indexer->index(), pure_local_ids,
222 partial_indexes,
223 m_do_copy_between_partial_and_pure, is_add,
224 m_queue);
225 _copyBetweenPartialsAndGlobals(copy_args);
226 }
227 }
228
229 _resetTransformedCells(ids);
230}
231
232/*---------------------------------------------------------------------------*/
233/*---------------------------------------------------------------------------*/
234/*!
235 * \brief Calcule les mailles à transformer pour le matériau \at mat.
236 */
237Int32 IncrementalComponentModifier::
238_computeCellsToTransformForMaterial(const MeshMaterial* mat, SmallSpan<const Int32> ids)
239{
240 const MeshEnvironment* env = mat->trueEnvironment();
241 const Int16 env_id = env->componentId();
242 bool is_add = m_work_info.isAdd();
243
244 ConstituentConnectivityList* connectivity = m_all_env_data->componentConnectivityList();
245 SmallSpan<bool> transformed_cells = m_work_info.transformedCells();
246 return connectivity->fillCellsToTransform(ids, env_id, transformed_cells, is_add, m_queue);
247}
248
249/*---------------------------------------------------------------------------*/
250/*---------------------------------------------------------------------------*/
251/*!
252 * \brief Supprime les mailles d'un matériau du milieu.
253 *
254 * Supprime les mailles données par \a local_ids du matériau \a mat
255 * du milieu. L'indexeur du matériau est mis à jour et si \a update_env_indexer
256 * est vrai, celui du milieu aussi (ce qui signifie que le milieu disparait
257 * des mailles \a local_ids).
258 *
259 * TODO: optimiser cela en ne parcourant pas toutes les mailles
260 * matériaux du milieu (il faut supprimer removed_local_ids_filter).
261 * Si on connait l'indice de chaque maille dans la liste des MatVarIndex
262 * de l'indexeur, on peut directement taper dedans.
263 */
264void IncrementalComponentModifier::
265_removeItemsFromEnvironment(MeshEnvironment* env, MeshMaterial* mat,
266 SmallSpan<const Int32> local_ids, bool update_env_indexer)
267{
268 info(4) << "MeshEnvironment::removeItemsDirect mat=" << mat->name();
269
270 Int32 nb_to_remove = local_ids.size();
271
272 // TODO: à faire dans finalize()
273 env->addToTotalNbCellMat(-nb_to_remove);
274
275 mat->variableIndexer()->endUpdateRemove(m_work_info, nb_to_remove, m_queue);
276
277 if (update_env_indexer) {
278 // Met aussi à jour les entités \a local_ids à l'indexeur du milieu.
279 // Cela n'est possible que si le nombre de matériaux du milieu
280 // est supérieur ou égal à 2 (car sinon le matériau et le milieu
281 // ont le même indexeur)
282 env->variableIndexer()->endUpdateRemove(m_work_info, nb_to_remove, m_queue);
283 }
284}
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288/*!
289 * \brief Ajoute les mailles d'un matériau du milieu.
290 *
291 * Ajoute les mailles données par \a local_ids au matériau \a mat
292 * du milieu. L'indexeur du matériau est mis à jour et si \a update_env_indexer
293 * est vrai, celui du milieu aussi (ce qui signifie que le milieu apparait
294 * dans les mailles \a local_ids).
295 */
296void IncrementalComponentModifier::
297_addItemsToEnvironment(MeshEnvironment* env, MeshMaterial* mat,
298 SmallSpan<const Int32> local_ids, bool update_env_indexer)
299{
300 info(4) << "MeshEnvironment::addItemsDirect"
301 << " mat=" << mat->name();
302
303 MeshMaterialVariableIndexer* var_indexer = mat->variableIndexer();
304 const Int32 nb_to_add = local_ids.size();
305
306 // Met à jour le nombre de matériaux par maille et le nombre total de mailles matériaux.
307 env->addToTotalNbCellMat(nb_to_add);
308
309 const Int16 env_id = env->componentId();
310 m_work_info.m_cells_is_partial.resize(nb_to_add);
311 ConstituentConnectivityList* connectivity = m_all_env_data->componentConnectivityList();
312 connectivity->fillCellsIsPartial(local_ids, env_id, m_work_info.m_cells_is_partial.to1DSmallSpan(), m_queue);
313
314 _addItemsToIndexer(var_indexer, local_ids);
315
316 if (update_env_indexer) {
317 // Met aussi à jour les entités \a local_ids à l'indexeur du milieu.
318 // Cela n'est possible que si le nombre de matériaux du milieu
319 // est supérieur ou égal à 2 (car sinon le matériau et le milieu
320 // ont le même indexeur)
321 _addItemsToIndexer(env->variableIndexer(), local_ids);
322 }
323}
324
325/*---------------------------------------------------------------------------*/
326/*---------------------------------------------------------------------------*/
327
328void IncrementalComponentModifier::
329_addItemsToIndexer(MeshMaterialVariableIndexer* var_indexer,
330 SmallSpan<const Int32> local_ids)
331{
332 // TODO Conserver l'instance au cours de toutes modifications
333 ComponentItemListBuilder& list_builder = m_work_info.list_builder;
334 list_builder.setIndexer(var_indexer);
335
336 const Int32 nb_id = local_ids.size();
337 list_builder.preAllocate(nb_id);
338
339 _computeItemsToAdd(list_builder, local_ids);
340
341 if (traceMng()->verbosityLevel() >= 5)
342 info() << "ADD_MATITEM_TO_INDEXER component=" << var_indexer->name()
343 << " nb_pure=" << list_builder.pureIndexes().size()
344 << " nb_partial=" << list_builder.partialIndexes().size()
345 << "\n pure=(" << list_builder.pureIndexes() << ")"
346 << "\n partial=(" << list_builder.partialIndexes() << ")";
347
348 // TODO: lors de cet appel, on connait le max de \a index_in_partial donc
349 // on peut éviter de faire une réduction pour le recalculer.
350
351 var_indexer->endUpdateAdd(list_builder, m_queue);
352
353 // Redimensionne les variables
354 _resizeVariablesIndexer(var_indexer->index());
355
356 // Maintenant que les nouveaux MatVar sont créés, il faut les
357 // initialiser avec les bonnes valeurs.
358 if (m_do_init_new_items) {
359 IMeshMaterialMng* mm = m_material_mng;
360 bool init_with_zero = mm->isDataInitialisationWithZero();
361
362 Accelerator::ProfileRegion ps(m_queue, "InitializeNewItems", 0xFFFF00);
363
364 SmallSpan<Int32> partial_indexes = list_builder.partialIndexes();
365 if (init_with_zero) {
366 RunQueue::ScopedAsync sc(&m_queue);
367 InitializeWithZeroArgs init_args(var_indexer->index(), partial_indexes, m_queue);
368
369 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
370 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
371 if (do_one_command) {
372 copy_data.clear();
373 copy_data.reserve(m_material_mng->nbVariable());
374 init_args.m_copy_data = &copy_data;
375 }
376
377 auto func_zero = [&](IMeshMaterialVariable* mv) {
378 mv->_internalApi()->initializeNewItemsWithZero(init_args);
379 };
380 functor::apply(mm, &IMeshMaterialMng::visitVariables, func_zero);
381
382 if (do_one_command){
383 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
384 m_work_info.m_variables_copy_data.copy(x, &m_queue);
385 _applyInitializeWithZero(init_args);
386 }
387 m_queue.barrier();
388 }
389 else {
390 SmallSpan<Int32> partial_local_ids = list_builder.partialLocalIds();
391
392 CopyBetweenPartialAndGlobalArgs args(var_indexer->index(), partial_local_ids,
393 partial_indexes, true, true, m_queue);
394 _copyBetweenPartialsAndGlobals(args);
395 }
396 }
397}
398
399/*---------------------------------------------------------------------------*/
400/*---------------------------------------------------------------------------*/
401/*!
402 * \brief Redimensionne l'index \a var_index des variables.
403 */
404void IncrementalComponentModifier::
405_resizeVariablesIndexer(Int32 var_index)
406{
407 Accelerator::ProfileRegion ps(m_queue, "ResizeVariableIndexer", 0xFF00FF);
408 ResizeVariableIndexerArgs resize_args(var_index, m_queue);
409 // Regarde si on n'utilise qu'une seule commande pour les copies des vues.
410 // Pour l'instant (novembre 2024) on ne l'utilise par défaut que si
411 // on est sur accélérateur.
412 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
413
414 if (m_force_multiple_command_for_resize)
415 do_one_command = false;
416
417 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
418 if (do_one_command) {
419 copy_data.clear();
420 copy_data.reserve(m_material_mng->nbVariable());
421 resize_args.m_copy_data = &copy_data;
422 }
423
424 if (m_force_multiple_command_for_resize) {
425 // Le mode de commandes multiples sert à identifier quelles variables
426 // sont encore sur CPU via le déclenchement de PageFault.
427 // C'est pour cela qu'on met le nom de la variable dans la région de profiling
428 // pour avoir les traces avec 'nsys' par exemple. Il faut aussi ajouter
429 // une barrière pour sérialiser les opérations.
430 auto func2 = [&](IMeshMaterialVariable* mv) {
431 Accelerator::ProfileRegion ps2(m_queue, String("Resize_") + mv->name());
432 auto* mvi = mv->_internalApi();
433 mvi->resizeForIndexer(resize_args);
434 m_queue.barrier();
435 };
436 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func2);
437 }
438 else {
439 RunQueue::ScopedAsync sc(&m_queue);
440 auto func1 = [&](IMeshMaterialVariable* mv) {
441 auto* mvi = mv->_internalApi();
442 mvi->resizeForIndexer(resize_args);
443 };
444 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func1);
445 }
446
447 if (do_one_command) {
448 // Copie 'copy_data' dans le tableau correspondant pour le device éventuel.
449 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
450 m_work_info.m_variables_copy_data.copy(x, &m_queue);
451 _applyCopyVariableViews(m_queue);
452 }
453
454 m_queue.barrier();
455}
456
457/*---------------------------------------------------------------------------*/
458/*---------------------------------------------------------------------------*/
459/*!
460 * \brief Copie entre les valeurs partielles et les valeurs globales.
461 *
462 * Si \a pure_to_partial est vrai, alors on copie les valeurs globales
463 * vers les valeurs partielles, sinon on fait l'inverse.
464 * de suppression d'un matériau)
465 */
466void IncrementalComponentModifier::
467_copyBetweenPartialsAndGlobals(const CopyBetweenPartialAndGlobalArgs& args)
468{
469 if (args.m_local_ids.empty())
470 return;
471 const bool do_copy = args.m_do_copy_between_partial_and_pure;
472 const bool is_add_operation = args.m_is_global_to_partial;
473 RunQueue queue(args.m_queue);
474 RunQueue::ScopedAsync sc(&queue);
475 // Comme on a modifié des mailles, il faut mettre à jour les valeurs
476 // correspondantes pour chaque variable.
477 //info(4) << "NB_TRANSFORM=" << nb_transform << " name=" << e->name();
478 //Integer indexer_index = indexer->index();
479
480 Accelerator::RunQueuePool& queue_pool = m_material_mng->_internalApi()->asyncRunQueuePool();
481
482 // Redimensionne les variables si nécessaire
483 if (is_add_operation) {
484 _resizeVariablesIndexer(args.m_var_index);
485 }
486
487 if (do_copy) {
488 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
489 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
490 copy_data.clear();
491 copy_data.reserve(m_material_mng->nbVariable());
492
493 Int32 index = 0;
494 CopyBetweenPartialAndGlobalArgs args2(args);
495 args2.m_use_generic_copy = (m_use_generic_copy_between_pure_and_partial >= 1);
496 if (do_one_command)
497 args2.m_copy_data = &copy_data;
498 auto func2 = [&](IMeshMaterialVariable* mv) {
499 auto* mvi = mv->_internalApi();
500 if (!do_one_command)
501 args2.m_queue = queue_pool[index];
502 mvi->copyBetweenPartialAndGlobal(args2);
503 ++index;
504 };
505 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func2);
506 if (do_one_command) {
507 // Copie 'copy_data' dans le tableau correspondant pour le device éventuel.
508 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
509 m_work_info.m_variables_copy_data.copy(x, &queue);
510 _applyCopyBetweenPartialsAndGlobals(args2, queue);
511 }
512 else
513 queue_pool.barrier();
514 }
515}
516
517/*---------------------------------------------------------------------------*/
518/*---------------------------------------------------------------------------*/
519
520} // End namespace Arcane::Materials
521
522/*---------------------------------------------------------------------------*/
523/*---------------------------------------------------------------------------*/
Active toujours les traces dans les parties Arcane concernant les matériaux.