Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
SimpleGridMeshPartitioner.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/* SimpleGridMeshPartitioner.cc (C) 2000-2022 */
9/* */
10/* Grid mesh partitioner. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/NotSupportedException.h"
15#include "arcane/utils/PlatformUtils.h"
16#include "arcane/utils/ScopedPtr.h"
17
18#include "arcane/core/IGridMeshPartitioner.h"
19#include "arcane/core/BasicService.h"
20#include "arcane/core/IPrimaryMesh.h"
22#include "arcane/core/IParallelMng.h"
23#include "arcane/core/ItemPrinter.h"
24#include "arcane/core/IExtraGhostCellsBuilder.h"
25#include "arcane/core/IMeshPartitionConstraintMng.h"
26#include "arcane/core/IMeshUtilities.h"
27#include "arcane/core/IMeshModifier.h"
28#include "arcane/core/IItemFamily.h"
29
30#include <array>
31#include <map>
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36namespace Arcane
37{
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
45class SimpleGridMeshPartitioner
46: public BasicService
48{
49 public:
50
61 class GhostCellsBuilder
63 {
64 public:
65
66 explicit GhostCellsBuilder(IMesh* mesh)
67 : m_mesh(mesh)
68 {}
69
70 public:
71
73 {
74 for (const auto& v : m_ghost_cell_uids) {
75 Int32 rank = v.first;
76 Int32 nb_ghost = v.second.size();
77 UniqueArray<Int32>& local_ids = m_ghost_cell_local_ids[rank];
78 local_ids.resize(nb_ghost);
79 m_mesh->cellFamily()->itemsUniqueIdToLocalId(local_ids, v.second);
80 }
81 }
82
84 {
85 auto x = m_ghost_cell_local_ids.find(rank);
86 if (x == m_ghost_cell_local_ids.end())
87 return {};
88 return x->second;
89 }
90
91 std::map<Int32, UniqueArray<ItemUniqueId>> m_ghost_cell_uids;
92 std::map<Int32, UniqueArray<Int32>> m_ghost_cell_local_ids;
93 IMesh* m_mesh;
94 };
95
97 {
98 public:
99
100 UniqueArray<UniqueArray<Real>> m_grid_coord;
101 Int32 m_nb_direction = 0;
102 Int32 m_offset_y = 0;
103 Int32 m_offset_z = 0;
104 };
105
106 public:
107
108 explicit SimpleGridMeshPartitioner(const ServiceBuildInfo& sbi);
109
110 public:
111
112 void build() override {}
113 IPrimaryMesh* primaryMesh() override { return mesh()->toPrimaryMesh(); }
114 void partitionMesh(bool initial_partition) override;
115 void notifyEndPartition() override {}
116
117 public:
118
119 void setBoundingBox(Real3 min_val, Real3 max_val) override
120 {
121 m_min_box = min_val;
122 m_max_box = max_val;
123 m_is_bounding_box_set = true;
124 }
125
126 void setPartIndex(Int32 i, Int32 j, Int32 k) override
127 {
128 m_ijk_part[0] = i;
129 m_ijk_part[1] = j;
130 m_ijk_part[2] = k;
131 m_is_ijk_set = true;
132 }
133
134 void applyMeshPartitioning(IMesh* mesh) override;
135
136 private:
137
138 Real3 m_min_box;
139 Real3 m_max_box;
140 std::array<Int32, 3> m_ijk_part;
141 bool m_is_bounding_box_set = false;
142 bool m_is_ijk_set = false;
143 bool m_is_verbose = false;
144 GhostCellsBuilder* m_ghost_cells_builder = nullptr;
145 ScopedPtrT<GridInfo> m_grid_info;
146
147 private:
148
149 Int32 _findPart(RealConstArrayView coords, Real center);
150 void _addGhostCell(Int32 rank, Cell cell);
151 void _buildGridInfo();
152 void _computeSpecificGhostLayer();
153 void _addCellToIntersectedParts(Cell cell, std::array<Int32, 3> min_part, std::array<Int32, 3> nb_part);
154};
155
156/*---------------------------------------------------------------------------*/
157/*---------------------------------------------------------------------------*/
158
159SimpleGridMeshPartitioner::
160SimpleGridMeshPartitioner(const ServiceBuildInfo& sbi)
161: BasicService(sbi)
162{
163 if (platform::getEnvironmentVariable("ARCANE_DEBUG_SIMPLE_GRID_MESH_PARTITIONER") == "1")
164 m_is_verbose = true;
165}
166
167/*---------------------------------------------------------------------------*/
168/*---------------------------------------------------------------------------*/
169
178_findPart(RealConstArrayView coords, Real position)
179{
180 const Int32 nb_value = coords.size();
181 if (position < coords[0])
182 return 0;
183 if (math::isNearlyEqual(position, coords[0]))
184 return 0;
185 Int32 part_id = -1;
186 for (Int32 z = 0; z < nb_value; ++z) {
187 if (m_is_verbose)
188 info() << " z=" << z << " coord=" << coords[z] << " part=" << part_id;
189 if (position > coords[z])
190 part_id = z;
191 else
192 break;
193 }
194
195 if (part_id == (-1))
196 part_id = (nb_value - 1);
197
198 return part_id;
199}
200
201/*---------------------------------------------------------------------------*/
202/*---------------------------------------------------------------------------*/
203
204void SimpleGridMeshPartitioner::
205_addGhostCell(Int32 rank, Cell cell)
206{
207 m_ghost_cells_builder->m_ghost_cell_uids[rank].add(cell.uniqueId());
208}
209
210/*---------------------------------------------------------------------------*/
211/*---------------------------------------------------------------------------*/
212
213void SimpleGridMeshPartitioner::
214_buildGridInfo()
215{
216 m_grid_info = new GridInfo();
217
218 if (!m_is_bounding_box_set)
219 ARCANE_FATAL("Bounding box is not set. call method setBoundingBox() before");
220 if (!m_is_ijk_set)
221 ARCANE_FATAL("Part index is not set. call method setPartIndex() before");
222
223 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
224 const Int32 dimension = mesh->dimension();
225
226 IParallelMng* pm = mesh->parallelMng();
227 //Int32 nb_rank = pm->commSize();
228
229 // Calculate the number of parts per direction
230 std::array<Int32, 3> nb_part_by_direction_buf = { 0, 0, 0 };
231 ArrayView<Int32> nb_part_by_direction(3, nb_part_by_direction_buf.data());
232 for (Integer i = 0; i < 3; ++i)
233 if (m_ijk_part[i] >= 0)
234 nb_part_by_direction[i] = m_ijk_part[i] + 1;
235
236 auto& nb_direction = m_grid_info->m_nb_direction;
237
238 nb_direction = 0;
239 if (nb_part_by_direction[2] > 0)
240 nb_direction = 3;
241 else if (nb_part_by_direction[1] > 0)
242 nb_direction = 2;
243 else
244 ARCANE_THROW(NotImplementedException, "SimpleGridMeshPartitioner for 1D mesh");
245
246 if (nb_direction != dimension)
247 ARCANE_FATAL("Invalid number of direction: mesh_dimension={0} nb_direction={1}", dimension, nb_direction);
248
249 pm->reduce(Parallel::ReduceMax, nb_part_by_direction);
250 info() << "NB_DIRECTION=" << nb_direction << " NB_PART=" << nb_part_by_direction;
251
252 // Calculate the grid coordinates per direction
253 const Real min_value = -FloatInfo<Real>::maxValue();
254 auto& grid_coord = m_grid_info->m_grid_coord;
255
256 grid_coord.resize(nb_direction);
257 grid_coord.resize(nb_direction);
258 for (Integer i = 0; i < nb_direction; ++i)
259 grid_coord[i].resize(nb_part_by_direction[i], min_value);
260
261 for (Integer i = 0; i < nb_direction; ++i) {
262 Int32 index = m_ijk_part[i];
263 if (index >= 0)
264 grid_coord[i][index] = m_min_box[i];
265 }
266 for (Integer i = 0; i < nb_direction; ++i) {
267 pm->reduce(Parallel::ReduceMax, grid_coord[i]);
268 info() << "GRID_COORD dir=" << i << " V=" << grid_coord[i];
269 }
270
271 // Check that each grid direction is increasing
272 for (Integer i = 0; i < nb_direction; ++i) {
273 ConstArrayView<Real> coords(grid_coord[0].view());
274 Int32 nb_value = coords.size();
275 if (nb_value == 0)
276 continue;
277 for (Int32 z = 0; z < (nb_value - 1); ++z)
278 if (coords[z] > coords[z + 1])
279 ARCANE_FATAL("Grid coord '{0}' is not sorted: {1} > {2}", i, coords[z], coords[z + 1]);
280 }
281
282 m_grid_info->m_offset_y = (nb_direction >= 2) ? nb_part_by_direction[0] : 0;
283 m_grid_info->m_offset_z = (nb_direction == 3) ? (nb_part_by_direction[0] * nb_part_by_direction[1]) : 0;
284}
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288
290partitionMesh([[maybe_unused]] bool initial_partition)
291{
292 if (m_grid_info.get())
293 ARCANE_FATAL("partitionMesh() has already been called. Only one call par SimpleGridMeshPartitioner instance is allowed");
294
295 _buildGridInfo();
296
297 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
298 VariableNodeReal3& nodes_coord = mesh->nodesCoordinates();
299 VariableItemInt32& cells_new_owner = mesh->itemsNewOwner(IK_Cell);
300 IParallelMng* pm = mesh->parallelMng();
301 Int32 nb_rank = pm->commSize();
302
303 const Int32 offset_y = m_grid_info->m_offset_y;
304 const Int32 offset_z = m_grid_info->m_offset_z;
305 const Int32 nb_direction = m_grid_info->m_nb_direction;
306
307 // Iterate over each cell in each direction and check which grid index
308 // it belongs to. Deduce the new rank.
309 ENUMERATE_ (Cell, icell, mesh->allCells().own()) {
310 Cell cell = *icell;
311 std::array<Int32, 3> cell_part = { -1, -1, -1 };
312 Real3 cell_center;
313 Int32 nb_node = cell.nbNode();
314 for (Integer inode = 0; inode < nb_node; ++inode)
315 cell_center += nodes_coord[cell.node(inode)];
316 cell_center /= static_cast<Real>(nb_node);
317
318 for (Integer idir = 0; idir < nb_direction; ++idir) {
319 ConstArrayView<Real> coords(m_grid_info->m_grid_coord[idir].view());
320 Real cc = cell_center[idir];
321
322 if (m_is_verbose)
323 info() << " Cell uid=" << cell.uniqueId() << " idir=" << idir << " cc=" << cc;
324
325 cell_part[idir] = _findPart(coords, cc);
326 }
327
328 const Int32 new_owner = cell_part[0] + cell_part[1] * offset_y + cell_part[2] * offset_z;
329 if (m_is_verbose)
330 info() << "CELL=" << ItemPrinter(cell) << " coord=" << cell_center << " new_owner=" << new_owner
331 << " dir=" << cell_part[0] << " " << cell_part[1] << " " << cell_part[2];
332 if (new_owner < 0 || new_owner >= nb_rank)
333 ARCANE_FATAL("Bad value for new owner cell={0} new_owner={1} (max={2})", ItemPrinter(cell), new_owner, nb_rank);
334 cells_new_owner[icell] = new_owner;
335 }
336
337 cells_new_owner.synchronize();
338 if (mesh->partitionConstraintMng()) {
339 // Deal with Tied Cells
340 mesh->partitionConstraintMng()->computeAndApplyConstraints();
341 }
342 mesh->utilities()->changeOwnersFromCells();
343}
344
345/*---------------------------------------------------------------------------*/
346/*---------------------------------------------------------------------------*/
347
353_addCellToIntersectedParts(Cell cell, std::array<Int32, 3> min_part, std::array<Int32, 3> nb_part)
354{
355 const Int32 offset_y = m_grid_info->m_offset_y;
356 const Int32 offset_z = m_grid_info->m_offset_z;
357 const Int32 cell_owner = cell.owner();
358
359 for (Integer k0 = 0, maxk0 = nb_part[0]; k0 < maxk0; ++k0)
360 for (Integer k1 = 0, maxk1 = nb_part[1]; k1 < maxk1; ++k1)
361 for (Integer k2 = 0, maxk2 = nb_part[2]; k2 < maxk2; ++k2) {
362 Int32 p0 = min_part[0] + k0;
363 Int32 p1 = min_part[1] + k1;
364 Int32 p2 = min_part[2] + k2;
365 Int32 owner = p0 + (p1 * offset_y) + (p2 * offset_z);
366 // Do not send to self.
367 if (owner != cell_owner)
368 _addGhostCell(owner, cell);
369 }
370}
371
372/*---------------------------------------------------------------------------*/
373/*---------------------------------------------------------------------------*/
374
375void SimpleGridMeshPartitioner::
376_computeSpecificGhostLayer()
377{
378 if (!m_grid_info.get())
379 ARCANE_FATAL("partitionMesh() has to be called before this method.");
380
381 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
382 VariableNodeReal3& nodes_coord = mesh->nodesCoordinates();
383
384 const Int32 nb_direction = m_grid_info->m_nb_direction;
385
386 ENUMERATE_ (Cell, icell, mesh->allCells().own()) {
387 Cell cell = *icell;
388
389 std::array<Int32, 3> min_part = { -1, -1, -1 };
390 std::array<Int32, 3> max_part = { -1, -1, -1 };
391 std::array<Int32, 3> nb_node_part = { 1, 1, 1 };
392
393 // Determine the bounding box of the cell
394 Real max_value = FloatInfo<Real>::maxValue();
395 Real min_value = -max_value;
396 Real3 min_box(max_value, max_value, max_value);
397 Real3 max_box(min_value, min_value, min_value);
398 for (Node node : cell.nodes()) {
399 Real3 pos = nodes_coord[node];
400 min_box = math::min(min_box, pos);
401 max_box = math::max(max_box, pos);
402 }
403
404 for (Integer idir = 0; idir < nb_direction; ++idir) {
405 ConstArrayView<Real> coords(m_grid_info->m_grid_coord[idir].view());
406 Integer nb_coord = coords.size();
407
408 const Real min_pos = min_box[idir];
409 const Real max_pos = max_box[idir];
410 Int32 min_part_id = _findPart(coords, min_pos);
411 Int32 max_part_id = _findPart(coords, max_pos);
412
413 if (m_is_verbose)
414 info() << " Cell uid=" << cell.uniqueId() << " idir=" << idir
415 << " min_pos=" << min_pos << " max_pos=" << max_pos
416 << " min_part=" << min_part_id << " max_part_id=" << max_part_id;
417
418 // If we are on the edge of a part, also take the previous or next part.
419 // This prevents missing an ownership if the grid coordinates
420 // and the mesh to be partitioned are the same.
421 if (min_part_id > 0 && math::isNearlyEqual(min_pos, coords[min_part_id]))
422 --min_part_id;
423 if (max_part_id < (nb_coord - 1) && math::isNearlyEqual(max_pos, coords[max_part_id]))
424 ++max_part_id;
425 min_part[idir] = min_part_id;
426 max_part[idir] = max_part_id;
427 }
428
429 Int32 total_nb_part = 1;
430 for (Integer idir = 0; idir < nb_direction; ++idir) {
431 Int32 nb_part = 1 + (max_part[idir] - min_part[idir]);
432 nb_node_part[idir] = nb_part;
433 total_nb_part *= nb_part;
434 }
435
436 if (m_is_verbose)
437 info() << " Cell uid=" << cell.uniqueId() << " min_part=" << ArrayView<Int32>(min_part)
438 << " max_part=" << ArrayView<Int32>(max_part)
439 << " nb_part=" << ArrayView<Int32>(nb_node_part)
440 << " total=" << total_nb_part;
441
442 _addCellToIntersectedParts(cell, min_part, nb_node_part);
443 }
444
445 if (m_is_verbose) {
446 info() << "GHOST_CELLS_TO_SEND";
447 for (const auto& v : m_ghost_cells_builder->m_ghost_cell_uids) {
448 info() << "RANK=" << v.first << " ids=" << v.second;
449 }
450 }
451}
452
453/*---------------------------------------------------------------------------*/
454/*---------------------------------------------------------------------------*/
455
458{
459 //TODO Eventually remove the use of the mesh from the instance.
460 if (mesh != this->mesh())
461 ARCANE_FATAL("mesh argument should be the same mesh that the one used to create this instance");
462
463 if (m_ghost_cells_builder)
464 ARCANE_FATAL("Only one call to this method is allower per instance.");
465
466 mesh->modifier()->setDynamic(true);
467 mesh->utilities()->partitionAndExchangeMeshWithReplication(this, true);
468
470 m_ghost_cells_builder = scoped_builder.get();
471 mesh->modifier()->addExtraGhostCellsBuilder(m_ghost_cells_builder);
472
473 // Specifically recalculate the ghost cells to cover the partitions
474 _computeSpecificGhostLayer();
475 mesh->modifier()->updateGhostLayers();
476
477 mesh->modifier()->removeExtraGhostCellsBuilder(m_ghost_cells_builder);
478
479 m_ghost_cells_builder = nullptr;
480}
481
482/*---------------------------------------------------------------------------*/
483/*---------------------------------------------------------------------------*/
484
486 ServiceProperty("SimpleGridMeshPartitioner", ST_SubDomain),
488
489/*---------------------------------------------------------------------------*/
490/*---------------------------------------------------------------------------*/
491
492} // End namespace Arcane
493
494/*---------------------------------------------------------------------------*/
495/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
This file contains the various service factories and macros for registering services.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
void resize(Int64 s)
Changes the number of elements in the array to s.
Cell of a mesh.
Definition Item.h:1300
Constant view of an array of type T.
constexpr Integer size() const noexcept
Number of elements in the array.
Interface of a builder for "extraordinary" ghost cells.
Interface of a mesh partitioner on a grid.
virtual Integer dimension()=0
Mesh dimension (1D, 2D, or 3D).
virtual IPrimaryMesh * toPrimaryMesh()=0
Returns the instance in the form of an IPrimaryMesh.
Interface of the parallelism manager for a subdomain.
virtual Int32 commSize() const =0
Number of instances in the communicator.
Utility class for printing information about an entity.
Definition ItemPrinter.h:35
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
NodeConnectedListViewType nodes() const
List of nodes of the entity.
Definition Item.h:843
Int32 nbNode() const
Number of nodes of the entity.
Definition Item.h:837
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
T * get() const
Returns the object referenced by the instance.
Definition Ptr.h:122
Class managing a 3-dimensional real vector.
Definition Real3.h:132
Encapsulation of an automatically destructing pointer.
Definition ScopedPtr.h:44
Structure containing the information to create a service.
Service creation properties.
Int32ConstArrayView extraCellsToSend(Int32 rank) const override
Local indices of "extraordinary" cells for sending.
void computeExtraCellsToSend() override
Calculates the "extraordinary" cells to send.
void setPartIndex(Int32 i, Int32 j, Int32 k) override
Index (i,j,k) of the part.
void build() override
Build-level construction of the service.
void notifyEndPartition() override
Notification when a re-partitioning finishes (after entity exchange).
void _addCellToIntersectedParts(Cell cell, std::array< Int32, 3 > min_part, std::array< Int32, 3 > nb_part)
IPrimaryMesh * primaryMesh() override
Associated mesh.
Int32 _findPart(RealConstArrayView coords, Real center)
Returns the index in coords of the value immediately lower than position.
void setBoundingBox(Real3 min_val, Real3 max_val) override
Positions the bounding box of our subdomain.
void applyMeshPartitioning(IMesh *mesh) override
Applies the repartitioning to the mesh mesh.
void partitionMesh(bool initial_partition) override
TraceMessage info() const
Flow for an information message.
1D data vector with value semantics (STL style).
__host__ __device__ Real2 min(Real2 a, Real2 b)
Returns the minimum of two Real2.
Definition MathUtils.h:346
T max(const T &a, const T &b, const T &c)
Returns the maximum of three elements.
Definition MathUtils.h:407
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
ItemVariableScalarRefT< Int32 > VariableItemInt32
32-bit integer type quantity
@ ReduceMax
Maximum of values.
constexpr __host__ __device__ bool isNearlyEqual(const _Type &a, const _Type &b)
Tests if two values are approximately equal. For integer types, this function is equivalent to IsEqua...
Definition Numeric.h:219
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:482
@ ST_SubDomain
The service is used at the subdomain level.
@ IK_Cell
Cell mesh entity.
double Real
Type representing a real number.
std::int32_t Int32
Signed integer type of 32 bits.
ConstArrayView< Real > RealConstArrayView
C equivalent of a 1D array of reals.
Definition UtilsTypes.h:488