Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
CartesianMeshCoarsening.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/* CartesianMeshCoarsening.cc (C) 2000-2023 */
9/* */
10/* Coarsening of a Cartesian mesh. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/cartesianmesh/CartesianMeshCoarsening.h"
15
16#include "arcane/utils/FatalErrorException.h"
17#include "arcane/utils/ValueConvert.h"
18
19#include "arcane/core/IMesh.h"
20#include "arcane/core/ItemGroup.h"
21#include "arcane/core/IParallelMng.h"
22#include "arcane/core/CartesianGridDimension.h"
23#include "arcane/core/IMeshModifier.h"
24#include "arcane/core/SimpleSVGMeshExporter.h"
25#include "arcane/core/ItemPrinter.h"
26#include "arcane/core/MeshStats.h"
27
28#include "arcane/cartesianmesh/ICartesianMesh.h"
29#include "arcane/cartesianmesh/CellDirectionMng.h"
30
31#include <unordered_set>
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36namespace Arcane
37{
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
42CartesianMeshCoarsening::
43CartesianMeshCoarsening(ICartesianMesh* m)
44: TraceAccessor(m->traceMng())
45, m_cartesian_mesh(m)
46{
47 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CARTESIANMESH_COARSENING_VERBOSITY_LEVEL", true))
48 m_verbosity_level = v.value();
49}
50
51/*---------------------------------------------------------------------------*/
52/*---------------------------------------------------------------------------*/
53
54//! Returns the max of uniqueId() of entities in a group
55Int64 CartesianMeshCoarsening::
56_getMaxUniqueId(const ItemGroup& group)
57{
58 Int64 max_offset = 0;
59 ENUMERATE_ (Item, iitem, group) {
60 Item item = *iitem;
61 if (max_offset < item.uniqueId())
62 max_offset = item.uniqueId();
63 }
64 return max_offset;
65}
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
69
70void CartesianMeshCoarsening::
71createCoarseCells()
72{
73 if (m_is_create_coarse_called)
74 ARCANE_FATAL("This method has already been called");
75 m_is_create_coarse_called = true;
76
77 const bool is_verbose = m_verbosity_level > 0;
78 IMesh* mesh = m_cartesian_mesh->mesh();
79 Integer nb_patch = m_cartesian_mesh->nbPatch();
80 if (nb_patch != 1)
81 ARCANE_FATAL("This method is only valid for 1 patch (nb_patch={0})", nb_patch);
82
83 if (!mesh->isAmrActivated())
84 ARCANE_FATAL("AMR is not activated for this case");
85
86 // TODO: Delete the ghost cells then reconstruct them
87 // TODO: Update the information in CellDirectionMng
88 // of ownNbCell(), globalNbCell(), ...
89
90 Integer nb_dir = mesh->dimension();
91 if (nb_dir != 2)
92 ARCANE_FATAL("This method is only valid for 2D mesh");
93
94 IParallelMng* pm = mesh->parallelMng();
95 const Int32 my_rank = pm->commRank();
96 info() << "CoarseCartesianMesh nb_direction=" << nb_dir;
97
98 for (Integer idir = 0; idir < nb_dir; ++idir) {
99 CellDirectionMng cdm(m_cartesian_mesh->cellDirection(idir));
100 Int32 nb_own_cell = cdm.ownNbCell();
101 info() << "NB_OWN_CELL dir=" << idir << " n=" << nb_own_cell;
102 if ((nb_own_cell % 2) != 0)
103 ARCANE_FATAL("Invalid number of cells ({0}) for direction {1}. Should be a multiple of 2",
104 nb_own_cell, idir);
105 }
106
107 // Calculates the offset for creating uniqueIds.
108 // We take the max of the uniqueIds of faces and cells as the offset.
109 // Eventually, with Cartesian numbering everywhere, we will be able to determine
110 // this value directly.
111 Int64 max_cell_uid = _getMaxUniqueId(mesh->allCells());
112 Int64 max_face_uid = _getMaxUniqueId(mesh->allFaces());
113 const Int64 coarse_grid_cell_offset = 1 + pm->reduce(Parallel::ReduceMax, math::max(max_cell_uid, max_face_uid));
114 m_first_own_cell_unique_id_offset = coarse_grid_cell_offset;
115
116 CellDirectionMng cdm_x(m_cartesian_mesh->cellDirection(0));
117 CellDirectionMng cdm_y(m_cartesian_mesh->cellDirection(1));
118
119 const Int64 global_nb_cell_x = cdm_x.globalNbCell();
120 const Int64 global_nb_cell_y = cdm_y.globalNbCell();
121 CartesianGridDimension refined_grid_dim(global_nb_cell_x, global_nb_cell_y);
122 CartesianGridDimension coarse_grid_dim(global_nb_cell_x / 2, global_nb_cell_y / 2);
123 CartesianGridDimension::CellUniqueIdComputer2D refined_cell_uid_computer(refined_grid_dim.getCellComputer2D(0));
124 CartesianGridDimension::NodeUniqueIdComputer2D refined_node_uid_computer(refined_grid_dim.getNodeComputer2D(0));
125 CartesianGridDimension::CellUniqueIdComputer2D coarse_cell_uid_computer(coarse_grid_dim.getCellComputer2D(coarse_grid_cell_offset));
126 CartesianGridDimension::FaceUniqueIdComputer2D coarse_face_uid_computer(coarse_grid_dim.getFaceComputer2D(coarse_grid_cell_offset));
127
128 // For the coarse cells and faces, the nodes already exist
129 // Therefore, we cannot use the Cartesian connectivity of the coarse grid
130 // for them (we can do this when patch AMR with duplication is active)
131 // For now, we use the numbering of the refined grid.
132
133 // TODO: Calculate the number of faces and cells and allocate accordingly.
134 UniqueArray<Int64> faces_infos;
135 UniqueArray<Int64> cells_infos;
136 Int32 nb_coarse_face = 0;
137 Int32 nb_coarse_cell = 0;
138 //! List of the first child of each coarse cell
139 UniqueArray<Int64> first_child_cell_unique_ids;
140 ENUMERATE_ (Cell, icell, mesh->ownCells()) {
141 Cell cell = *icell;
142 Int64 cell_uid = cell.uniqueId();
143 Int64x3 cell_xy = refined_cell_uid_computer.compute(cell_uid);
144 const Int64 cell_x = cell_xy.x;
145 const Int64 cell_y = cell_xy.y;
146 // Since we are coarsening by 2, only take cells whose coordinates
147 // are topologically even
148 if ((cell_x % 2) != 0 || (cell_y % 2) != 0)
149 continue;
150 if (is_verbose)
151 info() << "CELLCoarse uid=" << cell_uid << " x=" << cell_x << " y=" << cell_y;
152 const Int64 coarse_cell_x = cell_x / 2;
153 const Int64 coarse_cell_y = cell_y / 2;
154 std::array<Int64, 4> node_uids_container;
155 ArrayView<Int64> node_uids(node_uids_container);
156 node_uids[0] = refined_node_uid_computer.compute(cell_x + 0, cell_y + 0);
157 node_uids[1] = refined_node_uid_computer.compute(cell_x + 2, cell_y + 0);
158 node_uids[2] = refined_node_uid_computer.compute(cell_x + 2, cell_y + 2);
159 node_uids[3] = refined_node_uid_computer.compute(cell_x + 0, cell_y + 2);
160 if (is_verbose)
161 info() << "CELLNodes uid=" << node_uids;
162 std::array<Int64, 4> coarse_face_uids = coarse_face_uid_computer.computeForCell(coarse_cell_x, coarse_cell_y);
163 const ItemTypeInfo* cell_type = cell.typeInfo();
164 // Adds the 4 faces
165 for (Int32 z = 0; z < 4; ++z) {
166 ItemTypeInfo::LocalFace lface = cell_type->localFace(z);
167 faces_infos.add(IT_Line2);
168 faces_infos.add(coarse_face_uids[z]);
169 faces_infos.add(node_uids[lface.node(0)]);
170 faces_infos.add(node_uids[lface.node(1)]);
171 ++nb_coarse_face;
172 }
173 // Adds the cell
174 {
175 cells_infos.add(IT_Quad4);
176 cells_infos.add(coarse_cell_uid_computer.compute(coarse_cell_x, coarse_cell_y));
177 for (Int32 z = 0; z < 4; ++z)
178 cells_infos.add(node_uids[z]);
179 ++nb_coarse_cell;
180 first_child_cell_unique_ids.add(cell.uniqueId());
181 }
182 }
183
184 UniqueArray<Int32> cells_local_ids;
185 UniqueArray<Int32> faces_local_ids;
186 cells_local_ids.resize(nb_coarse_cell);
187 faces_local_ids.resize(nb_coarse_face);
188 mesh->modifier()->addFaces(MeshModifierAddFacesArgs(nb_coarse_face, faces_infos, faces_local_ids));
189 mesh->modifier()->addCells(MeshModifierAddCellsArgs(nb_coarse_cell, cells_infos, cells_local_ids));
190
191 // Now that the coarse cells are created, we must indicate
192 // that they are parents.
193 IItemFamily* cell_family = mesh->cellFamily();
194
195 // Positions the owners of the new cells
196 // and adds a flag (ItemFlags::II_UserMark1) to mark them.
197 // This will be used to destroy the refined cells later.
198 {
199 ENUMERATE_ (Cell, icell, cell_family->view(cells_local_ids)) {
200 Cell cell = *icell;
201 cell.mutableItemBase().setOwner(my_rank, my_rank);
203 }
204 cell_family->notifyItemsOwnerChanged();
205 }
206
207 // We must assign an owner to the faces.
208 // Since the new faces use an already existing node, we take the owner
209 // of the first node of the face
210 {
211 IItemFamily* face_family = mesh->faceFamily();
212 ENUMERATE_ (Face, iface, face_family->view(faces_local_ids)) {
213 Face face = *iface;
214 Int32 owner = face.node(0).owner();
215 face.mutableItemBase().setOwner(owner, my_rank);
216 }
217 face_family->notifyItemsOwnerChanged();
218 }
219
220 // Updates the mesh
221 mesh->modifier()->endUpdate();
222
223 // After calling endUpdate(), the local IDs no longer change.
224 // We can use this to keep the list of refined cells for each coarse cell
225 m_coarse_cells.resize(nb_coarse_cell);
226 m_refined_cells.resize(nb_coarse_cell, 4);
227
228 {
229 CellInfoListView cells(mesh->cellFamily());
230 UniqueArray<Int32> first_child_cell_local_ids(nb_coarse_cell);
231 cell_family->itemsUniqueIdToLocalId(first_child_cell_local_ids, first_child_cell_unique_ids);
232 Int32 coarse_index = 0;
233 std::array<Int32, 4> sub_cell_lids_container;
234 ArrayView<Int32> sub_cell_lids(sub_cell_lids_container);
235
236 ENUMERATE_ (Cell, icell, mesh->ownCells()) {
237 Cell coarse_cell = *icell;
238 if (!(coarse_cell.itemBase().flags() & ItemFlags::II_UserMark1))
239 continue;
240 // Removes the flag
242 m_coarse_cells[coarse_index] = coarse_cell.itemLocalId();
243 Cell first_child_cell = cells[first_child_cell_local_ids[coarse_index]];
244 // Starting from the first sub-cell, we can know the other 3
245 // because they are respectively to the right, top-right, and top.
246 sub_cell_lids[0] = first_child_cell.localId();
247 sub_cell_lids[1] = cdm_x[first_child_cell].next().localId();
248 sub_cell_lids[2] = cdm_y[CellLocalId(sub_cell_lids[1])].next().localId();
249 sub_cell_lids[3] = cdm_y[first_child_cell].next().localId();
250 if (is_verbose)
251 info() << "AddChildForCoarseCell i=" << coarse_index << " coarse=" << ItemPrinter(coarse_cell)
252 << " children_lid=" << sub_cell_lids;
253 for (Int32 z = 0; z < 4; ++z) {
254 CellLocalId sub_local_id = CellLocalId(sub_cell_lids[z]);
255 m_refined_cells[coarse_index][z] = sub_local_id;
256 if (is_verbose)
257 info() << " AddParentCellToCell: z=" << z << " child=" << ItemPrinter(cells[sub_local_id]);
258 }
259 ++coarse_index;
260 }
261 }
262
263 if (is_verbose) {
264 ENUMERATE_ (Cell, icell, mesh->allCells()) {
265 Cell cell = *icell;
266 info() << "Final cell=" << ItemPrinter(cell) << " level=" << cell.level();
267 }
268 }
269
270 const bool dump_coarse_mesh = false;
271 if (dump_coarse_mesh) {
272 String filename = String::format("mesh_coarse_{0}.svg", my_rank);
273 std::ofstream ofile(filename.localstr());
274 SimpleSVGMeshExporter writer(ofile);
275 writer.write(mesh->allCells());
276 }
277}
278
279/*---------------------------------------------------------------------------*/
280/*---------------------------------------------------------------------------*/
281
282void CartesianMeshCoarsening::
283removeRefinedCells()
284{
285 if (!m_is_create_coarse_called)
286 ARCANE_FATAL("You need to call createCoarseCells() before");
287 if (m_is_remove_refined_called)
288 ARCANE_FATAL("This method has already been called");
289 m_is_remove_refined_called = true;
290
291 IMesh* mesh = m_cartesian_mesh->mesh();
292 IMeshModifier* mesh_modifier = mesh->modifier();
293
294 // Deletes all refined cells as well as all ghost cells
295 {
296 std::unordered_set<Int32> coarse_cells_set;
297 for (Int32 cell_lid : m_coarse_cells)
298 coarse_cells_set.insert(cell_lid);
299
300 UniqueArray<Int32> cells_to_remove;
301 ENUMERATE_ (Cell, icell, mesh->ownCells()) {
302 Int32 local_id = icell.itemLocalId();
303 Cell cell = *icell;
304 if (!cell.isOwn() || (coarse_cells_set.find(local_id) != coarse_cells_set.end()))
305 cells_to_remove.add(local_id);
306 }
307 mesh_modifier->removeCells(cells_to_remove);
308 mesh_modifier->endUpdate();
309 }
310
311 // Reconstructs the ghost cells
312 mesh_modifier->setDynamic(true);
313 mesh_modifier->updateGhostLayers();
314
315 // Displays the statistics of the new mesh
316 {
317 MeshStats ms(traceMng(), mesh, mesh->parallelMng());
318 ms.dumpStats();
319 }
320
321 _recomputeMeshGenerationInfo();
322
323 // We must recalculate the new directions
324 m_cartesian_mesh->computeDirections();
325}
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329/*!
330 * \brief Recalculates the information on the number of cells per direction.
331 */
332void CartesianMeshCoarsening::
333_recomputeMeshGenerationInfo()
334{
335 IMesh* mesh = m_cartesian_mesh->mesh();
336 auto* cmgi = ICartesianMeshGenerationInfo::getReference(mesh, false);
337 if (!cmgi)
338 return;
339
340 // Coarsening factor
341 const Int32 cf = 2;
342
343 {
344 ConstArrayView<Int64> v = cmgi->ownCellOffsets();
345 cmgi->setOwnCellOffsets(v[0] / cf, v[1] / cf, v[2] / cf);
346 }
347 {
348 ConstArrayView<Int64> v = cmgi->globalNbCells();
349 cmgi->setGlobalNbCells(v[0] / cf, v[1] / cf, v[2] / cf);
350 }
351 {
352 ConstArrayView<Int32> v = cmgi->ownNbCells();
353 cmgi->setOwnNbCells(v[0] / cf, v[1] / cf, v[2] / cf);
354 }
355 cmgi->setFirstOwnCellUniqueId(m_first_own_cell_unique_id_offset);
356}
357
358/*---------------------------------------------------------------------------*/
359/*---------------------------------------------------------------------------*/
360
361} // End namespace Arcane
362
363/*---------------------------------------------------------------------------*/
364/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
Modifiable view of an array of type T.
void resize(Int64 s)
Changes the number of elements in the array to s.
void add(ConstReferenceType val)
Adds element val to the end of the array.
Information about the dimensions of a Cartesian grid.
Info about the cells in a specific X, Y, or Z direction of a structured mesh.
Int64 globalNbCell() const
Global number of cells in this direction.
Int32 ownNbCell() const
Number of own cells in this direction.
View of cell information.
Cell of a mesh.
Definition Item.h:1300
CellLocalId itemLocalId() const
Local identifier of the entity in the processor subdomain.
Definition Item.h:1394
Int32 level() const
Definition Item.h:1473
Constant view of an array of type T.
Face of a cell.
Definition Item.h:1032
Interface of an entity family.
Definition IItemFamily.h:83
virtual void notifyItemsOwnerChanged()=0
Notifies that the entities specific to the family's subdomain have been modified.
virtual ItemVectorView view(Int32ConstArrayView local_ids)=0
View on the entities.
virtual void itemsUniqueIdToLocalId(Int32ArrayView local_ids, Int64ConstArrayView unique_ids, bool do_fatal=true) const =0
Converts an array of unique numbers to local numbers.
Mesh modification interface.
virtual void updateGhostLayers()=0
Updates the ghost layer.
virtual void setDynamic(bool v)=0
Sets the property indicating whether the mesh can evolve.
virtual void removeCells(Int32ConstArrayView cells_local_id)=0
Removes cells.
virtual void endUpdate()=0
Notifies the instance that mesh modification is finished.
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
Int32 flags() const
Flags of the entity.
@ II_UserMark1
User mark.
Definition ItemFlags.h:94
Utility class for printing information about an entity.
Definition ItemPrinter.h:35
Local information about a cell face.
Integer node(Integer i) const
Local index in the cell of the i-th node of the face.
Info on a mesh entity type.
LocalFace localFace(Integer id) const
Local connectivity of the i-th face of the cell.
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
const ItemTypeInfo * typeInfo() const
Information about the entity type.
Definition Item.h:406
impl::MutableItemBase mutableItemBase() const
Mutable internal part of the entity.
Definition Item.h:394
constexpr Int32 localId() const
Local identifier of the entity in the processor subdomain.
Definition Item.h:233
Int32 owner() const
Owner subdomain number of the entity.
Definition Item.h:252
ItemUniqueId uniqueId() const
Unique identifier across all domains.
Definition Item.h:239
constexpr bool isOwn() const
true if the entity belongs to the subdomain
Definition Item.h:267
impl::ItemBase itemBase() const
Internal part of the entity.
Definition Item.h:383
Arguments for IMeshModifier::addCells().
Arguments for IMeshModifier::addFaces().
void dumpStats() override
Prints mesh information.
Definition MeshStats.cc:63
void setOwner(Integer suid, Int32 current_sub_domain)
Sets the sub-domain number of the entity owner.
void addFlags(Int32 added_flags)
Adds the flags added_flags to those of the entity.
void removeFlags(Int32 removed_flags)
Removes the flags removed_flags from those of the entity.
void write(const CellGroup &cells)
Exports the entities of the cells group.
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
TraceMessage info() const
Flow for an information message.
ITraceMng * traceMng() const
Trace manager.
1D data vector with value semantics (STL style).
T max(const T &a, const T &b, const T &c)
Returns the maximum of three elements.
Definition MathUtils.h:407
@ ReduceMax
Maximum of values.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
std::int32_t Int32
Signed integer type of 32 bits.