Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
IncrementalComponentModifier.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/* IncrementalComponentModifier.cc (C) 2000-2025 */
9/* */
10/* Incremental modification of constituents. */
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 if using typed copy (historical mode) and one command per variable
47 // 1 if using generic copy and one command per variable
48 // 2 if using generic copy and one command for all 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 // By default on an accelerator and in multi-threading, we use the copy
54 // with a single queue, as it is the most performant mechanism.
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/*!
90 * \brief Transforms entities for an environment.
91 *
92 * Iterates over the environment \a env and
93 * converts pure cells to partial cells or
94 * vice versa. After conversion, the values corresponding to the
95 * modified cells are updated for each variable.
96 *
97 * If \a is_add is true, it transforms from pure to partial
98 * (material addition); otherwise, it transforms from partial to pure
99 * (material removal)
100 */
101void IncrementalComponentModifier::
102_switchCellsForMaterials(const MeshMaterial* modified_mat,
103 SmallSpan<const Int32> ids)
104{
105 const bool is_add = m_work_info.isAdd();
106 const bool is_device = m_queue.isAcceleratorPolicy();
107 SmallSpan<const bool> is_materials_modified = m_work_info.m_is_materials_modified.view(false);
108
109 for (MeshEnvironment* true_env : m_material_mng->trueEnvironments()) {
110 for (MeshMaterial* mat : true_env->trueMaterials()) {
111 // Do not process the material currently being modified.
112 if (mat == modified_mat)
113 continue;
114
115 if (!is_materials_modified[mat->id()])
116 continue;
117
118 if (!is_device) {
119 m_work_info.pure_local_ids.clearHost();
120 m_work_info.partial_indexes.clearHost();
121 }
122
123 MeshMaterialVariableIndexer* indexer = mat->variableIndexer();
124
125 info(4) << "MatTransformCells is_add?=" << is_add << " indexer=" << indexer->name()
126 << " mat_id=" << mat->id();
127
128 Int32 nb_transformed = _computeCellsToTransformForMaterial(mat, ids);
129 info(4) << "nb_transformed=" << nb_transformed;
130 if (nb_transformed == 0)
131 continue;
132 indexer->transformCells(m_work_info, m_queue, false);
133 _resetTransformedCells(ids);
134
135 auto pure_local_ids = m_work_info.pure_local_ids.view(is_device);
136 auto partial_indexes = m_work_info.partial_indexes.view(is_device);
137
138 Int32 nb_pure = pure_local_ids.size();
139 Int32 nb_partial = partial_indexes.size();
140 info(4) << "NB_MAT_TRANSFORM pure=" << nb_pure
141 << " partial=" << nb_partial << " name=" << mat->name()
142 << " is_device?=" << is_device
143 << " is_modified?=" << is_materials_modified[mat->id()];
144
145 CopyBetweenPartialAndGlobalArgs args(indexer->index(), pure_local_ids,
146 partial_indexes,
147 m_do_copy_between_partial_and_pure,
148 is_add,
149 m_queue);
150 _copyBetweenPartialsAndGlobals(args);
151 }
152 }
153}
154
155/*---------------------------------------------------------------------------*/
156/*---------------------------------------------------------------------------*/
157
158/*!
159 * \brief Transforms entities for environments.
160 *
161 * Iterates over environments, except the modified environment \a modified_env, and
162 * for each one converts pure cells to partial cells or
163 * vice versa. After conversion, the values corresponding to the
164 * modified cells are updated for each variable.
165 *
166 * If \a is_add is true, it transforms from pure to partial
167 * (in the case of material addition); otherwise, it transforms from partial to pure
168 * (in the case of material removal)
169 */
170void IncrementalComponentModifier::
171_switchCellsForEnvironments(const IMeshEnvironment* modified_env,
172 SmallSpan<const Int32> ids)
173{
174 const bool is_add = m_work_info.isAdd();
175 const bool is_device = m_queue.isAcceleratorPolicy();
176 SmallSpan<const bool> is_environments_modified = m_work_info.m_is_environments_modified.view(false);
177
178 // Do not copy partial values from environments to global values
179 // in case of cell removal, because this will be done with the material value
180 // corresponding to it. This allows the same behavior as without
181 // optimization. This is not active by default for compatibility with existing code.
182 const bool is_copy = is_add || !(m_material_mng->isUseMaterialValueWhenRemovingPartialValue());
183
184 Int32 nb_transformed = _computeCellsToTransformForEnvironments(ids);
185 info(4) << "Compute Cells for environments nb_transformed=" << nb_transformed;
186 if (nb_transformed == 0)
187 return;
188
189 for (const MeshEnvironment* env : m_material_mng->trueEnvironments()) {
190 // Do not process the environment currently being modified.
191 if (env == modified_env)
192 continue;
193 // If I am mono-material, the indexer update was done by the material
194 if (env->isMonoMaterial())
195 continue;
196
197 const Int32 env_id = env->id();
198
199 if (!is_environments_modified[env_id])
200 continue;
201
202 if (!is_device) {
203 m_work_info.pure_local_ids.clearHost();
204 m_work_info.partial_indexes.clearHost();
205 }
206
207 MeshMaterialVariableIndexer* indexer = env->variableIndexer();
208
209 info(4) << "EnvTransformCells is_add?=" << is_add
210 << " env_id=" << env_id
211 << " indexer=" << indexer->name() << " nb_item=" << ids.size();
212
213 indexer->transformCells(m_work_info, m_queue, true);
214
215 SmallSpan<const Int32> pure_local_ids = m_work_info.pure_local_ids.view(is_device);
216 SmallSpan<const Int32> partial_indexes = m_work_info.partial_indexes.view(is_device);
217 const Int32 nb_pure = pure_local_ids.size();
218
219 info(4) << "NB_ENV_TRANSFORM nb_pure=" << nb_pure << " name=" << env->name()
220 << " is_modified=" << is_environments_modified[env_id];
221
222 if (is_copy) {
223 CopyBetweenPartialAndGlobalArgs copy_args(indexer->index(), pure_local_ids,
224 partial_indexes,
225 m_do_copy_between_partial_and_pure, is_add,
226 m_queue);
227 _copyBetweenPartialsAndGlobals(copy_args);
228 }
229 }
230
231 _resetTransformedCells(ids);
232}
233
234/*---------------------------------------------------------------------------*/
235/*---------------------------------------------------------------------------*/
236
237/*!
238 * \brief Calculates the cells to transform for material \at mat.
239 */
240Int32 IncrementalComponentModifier::
241_computeCellsToTransformForMaterial(const MeshMaterial* mat, SmallSpan<const Int32> ids)
242{
243 const MeshEnvironment* env = mat->trueEnvironment();
244 const Int16 env_id = env->componentId();
245 bool is_add = m_work_info.isAdd();
246
247 ConstituentConnectivityList* connectivity = m_all_env_data->componentConnectivityList();
248 SmallSpan<bool> transformed_cells = m_work_info.transformedCells();
249 return connectivity->fillCellsToTransform(ids, env_id, transformed_cells, is_add, m_queue);
250}
251
252/*---------------------------------------------------------------------------*/
253/*---------------------------------------------------------------------------*/
254
255/*!
256 * \brief Removes cells of a material from the environment.
257 *
258 * Removes the cells provided by \a local_ids from material \a mat
259 * in the environment. The material indexer is updated, and if \a update_env_indexer
260 * is true, the environment indexer is also updated (which means the environment disappears
261 * from the cells \a local_ids).
262 *
263 * TODO: optimize this by not iterating over all cells
264 * materials of the environment (removed_local_ids_filter must be removed).
265 * If we know the index of each cell in the MatVarIndex
266 * from the indexer, we can directly access it.
267 */
268void IncrementalComponentModifier::
269_removeItemsFromEnvironment(MeshEnvironment* env, MeshMaterial* mat,
270 SmallSpan<const Int32> local_ids, bool update_env_indexer)
271{
272 info(4) << "MeshEnvironment::removeItemsDirect mat=" << mat->name();
273
274 Int32 nb_to_remove = local_ids.size();
275
276 // TODO: to be done in finalize()
277 env->addToTotalNbCellMat(-nb_to_remove);
278
279 mat->variableIndexer()->endUpdateRemove(m_work_info, nb_to_remove, m_queue);
280
281 if (update_env_indexer) {
282 // Also updates the entities \a local_ids in the environment's indexer.
283 // This is only possible if the number of environment materials
284 // is greater than or equal to 2 (because otherwise the material and the environment
285 // have the same indexer)
286 env->variableIndexer()->endUpdateRemove(m_work_info, nb_to_remove, m_queue);
287 }
288}
289
290/*---------------------------------------------------------------------------*/
291/*---------------------------------------------------------------------------*/
292
293/*!
294 * \brief Adds the cells of an environment material.
295 *
296 * Adds the cells given by \a local_ids to the environment material \a mat
297 * of the environment. The material indexer is updated, and if \a update_env_indexer
298 * is true, the environment's indexer is also updated (meaning the environment appears
299 * in the cells \a local_ids).
300 */
301void IncrementalComponentModifier::
302_addItemsToEnvironment(MeshEnvironment* env, MeshMaterial* mat,
303 SmallSpan<const Int32> local_ids, bool update_env_indexer)
304{
305 info(4) << "MeshEnvironment::addItemsDirect"
306 << " mat=" << mat->name();
307
308 MeshMaterialVariableIndexer* var_indexer = mat->variableIndexer();
309 const Int32 nb_to_add = local_ids.size();
310
311 // Updates the number of materials per cell and the total number of material cells.
312 env->addToTotalNbCellMat(nb_to_add);
313
314 const Int16 env_id = env->componentId();
315 m_work_info.m_cells_is_partial.resize(nb_to_add);
316 ConstituentConnectivityList* connectivity = m_all_env_data->componentConnectivityList();
317 connectivity->fillCellsIsPartial(local_ids, env_id, m_work_info.m_cells_is_partial.to1DSmallSpan(), m_queue);
318
319 _addItemsToIndexer(var_indexer, local_ids);
320
321 if (update_env_indexer) {
322 // Also updates the entities \a local_ids in the environment's indexer.
323 // This is only possible if the number of environment materials
324 // is greater than or equal to 2 (because otherwise the material and the environment
325 // have the same indexer)
326 _addItemsToIndexer(env->variableIndexer(), local_ids);
327 }
328}
329
330/*---------------------------------------------------------------------------*/
331/*---------------------------------------------------------------------------*/
332
333void IncrementalComponentModifier::
334_addItemsToIndexer(MeshMaterialVariableIndexer* var_indexer,
335 SmallSpan<const Int32> local_ids)
336{
337 // TODO Keep the instance during all modifications
338 ComponentItemListBuilder& list_builder = m_work_info.list_builder;
339 list_builder.setIndexer(var_indexer);
340
341 const Int32 nb_id = local_ids.size();
342 list_builder.preAllocate(nb_id);
343
344 _computeItemsToAdd(list_builder, local_ids);
345
346 if (traceMng()->verbosityLevel() >= 5)
347 info() << "ADD_MATITEM_TO_INDEXER component=" << var_indexer->name()
348 << " nb_pure=" << list_builder.pureIndexes().size()
349 << " nb_partial=" << list_builder.partialIndexes().size()
350 << "\n pure=(" << list_builder.pureIndexes() << ")"
351 << "\n partial=(" << list_builder.partialIndexes() << ")";
352
353 // TODO: during this call, we know the max of \a index_in_partial so
354 // we can avoid performing a reduction to recalculate it.
355
356 var_indexer->endUpdateAdd(list_builder, m_queue);
357
358 // Resizes the variables
359 _resizeVariablesIndexer(var_indexer->index());
360
361 // Now that the new MatVars are created, they must be
362 // initialized with the correct values.
363 if (m_do_init_new_items) {
364 IMeshMaterialMng* mm = m_material_mng;
365 bool init_with_zero = mm->isDataInitialisationWithZero();
366
367 Accelerator::ProfileRegion ps(m_queue, "InitializeNewItems", 0xFFFF00);
368
369 SmallSpan<Int32> partial_indexes = list_builder.partialIndexes();
370 if (init_with_zero) {
371 RunQueue::ScopedAsync sc(&m_queue);
372 InitializeWithZeroArgs init_args(var_indexer->index(), partial_indexes, m_queue);
373
374 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
375 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
376 if (do_one_command) {
377 copy_data.clear();
378 copy_data.reserve(m_material_mng->nbVariable());
379 init_args.m_copy_data = &copy_data;
380 }
381
382 auto func_zero = [&](IMeshMaterialVariable* mv) {
383 mv->_internalApi()->initializeNewItemsWithZero(init_args);
384 };
385 functor::apply(mm, &IMeshMaterialMng::visitVariables, func_zero);
386
387 if (do_one_command) {
388 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
389 m_work_info.m_variables_copy_data.copy(x, &m_queue);
390 _applyInitializeWithZero(init_args);
391 }
392 m_queue.barrier();
393 }
394 else {
395 SmallSpan<Int32> partial_local_ids = list_builder.partialLocalIds();
396
397 CopyBetweenPartialAndGlobalArgs args(var_indexer->index(), partial_local_ids,
398 partial_indexes, true, true, m_queue);
399 _copyBetweenPartialsAndGlobals(args);
400 }
401 }
402}
403
404/*---------------------------------------------------------------------------*/
405/*---------------------------------------------------------------------------*/
406
407/*!
408 * \brief Resizes the variable index \a var_index.
409 */
410void IncrementalComponentModifier::
411_resizeVariablesIndexer(Int32 var_index)
412{
413 Accelerator::ProfileRegion ps(m_queue, "ResizeVariableIndexer", 0xFF00FF);
414 ResizeVariableIndexerArgs resize_args(var_index, m_queue);
415 // Checks if we are only using one command for view copies.
416 // For now (November 2024), we only use it by default if
417 // we are on an accelerator.
418 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
419
420 if (m_force_multiple_command_for_resize)
421 do_one_command = false;
422
423 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
424 if (do_one_command) {
425 copy_data.clear();
426 copy_data.reserve(m_material_mng->nbVariable());
427 resize_args.m_copy_data = &copy_data;
428 }
429
430 if (m_force_multiple_command_for_resize) {
431 // The multiple command mode is used to identify which variables
432 // are still on CPU via PageFault triggering.
433 // That is why we put the variable name in the profiling region
434 // to get traces with 'nsys', for example. We also need to add
435 // a barrier to serialize the operations.
436 auto func2 = [&](IMeshMaterialVariable* mv) {
437 Accelerator::ProfileRegion ps2(m_queue, String("Resize_") + mv->name());
438 auto* mvi = mv->_internalApi();
439 mvi->resizeForIndexer(resize_args);
440 m_queue.barrier();
441 };
442 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func2);
443 }
444 else {
445 RunQueue::ScopedAsync sc(&m_queue);
446 auto func1 = [&](IMeshMaterialVariable* mv) {
447 auto* mvi = mv->_internalApi();
448 mvi->resizeForIndexer(resize_args);
449 };
450 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func1);
451 }
452
453 if (do_one_command) {
454 // Copies 'copy_data' into the corresponding array for the eventual device.
455 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
456 m_work_info.m_variables_copy_data.copy(x, &m_queue);
457 _applyCopyVariableViews(m_queue);
458 }
459
460 m_queue.barrier();
461}
462
463/*---------------------------------------------------------------------------*/
464/*---------------------------------------------------------------------------*/
465
466/*!
467 * \brief Copies between partial and global values.
468 *
469 * If \a pure_to_partial is true, then we copy the global values
470 * to the partial values; otherwise, we do the reverse.
471 * (of material deletion)
472 */
473void IncrementalComponentModifier::
474_copyBetweenPartialsAndGlobals(const CopyBetweenPartialAndGlobalArgs& args)
475{
476 if (args.m_local_ids.empty())
477 return;
478 const bool do_copy = args.m_do_copy_between_partial_and_pure;
479 const bool is_add_operation = args.m_is_global_to_partial;
480 RunQueue queue(args.m_queue);
481 RunQueue::ScopedAsync sc(&queue);
482 // Since we modified cells, we must update the corresponding values
483 // for each variable.
484 //info(4) << "NB_TRANSFORM=" << nb_transform << " name=" << e->name();
485 //Integer indexer_index = indexer->index();
486
487 Accelerator::RunQueuePool& queue_pool = m_material_mng->_internalApi()->asyncRunQueuePool();
488
489 // Resizes the variables if necessary
490 if (is_add_operation) {
491 _resizeVariablesIndexer(args.m_var_index);
492 }
493
494 if (do_copy) {
495 bool do_one_command = (m_use_generic_copy_between_pure_and_partial == 2);
496 UniqueArray<CopyBetweenDataInfo>& copy_data = m_work_info.m_host_variables_copy_data;
497 copy_data.clear();
498 copy_data.reserve(m_material_mng->nbVariable());
499
500 Int32 index = 0;
501 CopyBetweenPartialAndGlobalArgs args2(args);
502 args2.m_use_generic_copy = (m_use_generic_copy_between_pure_and_partial >= 1);
503 if (do_one_command)
504 args2.m_copy_data = &copy_data;
505 auto func2 = [&](IMeshMaterialVariable* mv) {
506 auto* mvi = mv->_internalApi();
507 if (!do_one_command)
508 args2.m_queue = queue_pool[index];
509 mvi->copyBetweenPartialAndGlobal(args2);
510 ++index;
511 };
512 functor::apply(m_material_mng, &MeshMaterialMng::visitVariables, func2);
513 if (do_one_command) {
514 // Copies 'copy_data' into the corresponding array for the eventual device.
515 MDSpan<CopyBetweenDataInfo, MDDim1> x(copy_data.data(), MDIndex<1>(copy_data.size()));
516 m_work_info.m_variables_copy_data.copy(x, &queue);
517 _applyCopyBetweenPartialsAndGlobals(args2, queue);
518 }
519 else
520 queue_pool.barrier();
521 }
522}
523
524/*---------------------------------------------------------------------------*/
525/*---------------------------------------------------------------------------*/
526
527} // End namespace Arcane::Materials
528
529/*---------------------------------------------------------------------------*/
530/*---------------------------------------------------------------------------*/
Always enables tracing in Arcane parts concerning materials.