Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
ArcaneGeometricMeshPartitionerService.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/* ArcaneGeometricMeshPartitionerService.cc (C) 2000-2025 */
9/* */
10/* Mesh geometric partitioning service. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/PlatformUtils.h"
15#include "arcane/utils/NotImplementedException.h"
16#include "arcane/utils/ITraceMng.h"
17#include "arcane/utils/FixedArray.h"
18#include "arcane/utils/SmallArray.h"
19
20#include "arcane/core/IParallelMng.h"
21#include "arcane/core/IPrimaryMesh.h"
22#include "arcane/core/IMeshUtilities.h"
23#include "arcane/core/IMeshPartitioner.h"
24#include "arcane/core/IItemFamily.h"
25#include "arcane/core/ItemVector.h"
27
28#include "arcane/core/IMeshPartitionConstraintMng.h"
29#include "arcane/impl/ArcaneGeometricMeshPartitionerService_axl.h"
30
31#include "arcane_internal_config.h"
32#include <limits>
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37namespace Arcane
38{
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
56{
57 private:
58
61 {
62 Real eigen_value;
63 Real3 eigen_vector;
64 };
65
66 public:
67
71 void computeForMatrix(const Real3x3& orig_matrix)
72 {
73 Real3x3 matrix = orig_matrix;
74
75 constexpr int nb_value = 3;
76 // Iterates to calculate the eigenvalues and eigenvectors
77 // The power method calculates the highest eigenvalue. Since we want to sort the eigenvalues
78 // in ascending order, we range them in reverse order.
79
80 for (int i = (nb_value - 1); i >= 0; --i) {
81 // Calculate the first eigenvector (the largest)
82 PowerResult result = _applyPowerIteration(matrix);
83 m_eigen_values[i] = result.eigen_value;
84 m_eigen_vectors[i] = result.eigen_vector;
85
86 // Apply deflation to eliminate the eigenvector
87 // that was just calculated.
88 // We do not need to do this for the last iteration
89 if (i != 0)
90 _deflateMatrix(matrix, result.eigen_value, result.eigen_vector);
91 }
92 }
93
94 Real3 eigenValues() const { return m_eigen_values; }
97
98 private:
99
100 // Subtracts an eigenvector from the matrix for deflation
101 static void _deflateMatrix(Real3x3& matrix, double eigenvalue, Real3 eigenvector)
102 {
103 for (int i = 0; i < 3; ++i) {
104 for (int j = 0; j < 3; ++j) {
105 matrix[i][j] -= eigenvalue * eigenvector[i] * eigenvector[j];
106 }
107 }
108 }
109
110 // Applies the power method
111 static PowerResult _applyPowerIteration(const Real3x3& matrix)
112 {
113 constexpr Int32 max_iteration = 1000;
114 constexpr Real tolerance = 1e-16;
115 Real eigenvalue = 0.0;
116
117 // Initialization with an initial vector (it could be random)
118 Real3 b{ 1.0, 1.0, 1.0 };
119 Real3 b_next;
120
121 // Power method iterations
122 for (Int32 iter = 0; iter < max_iteration; ++iter) {
123 b_next = math::multiply(matrix, b);
124
125 eigenvalue = b_next.normL2();
126 if (math::isNearlyZero(eigenvalue))
127 break;
128 b_next = b_next / eigenvalue;
129
130 // Check convergence
131 Real diff = math::squareNormL2(b_next - b);
132 if (diff < tolerance) {
133 break; // If the difference is sufficiently small, we have converged
134 }
135
136 b = b_next;
137 }
138
139 return { eigenvalue, b_next };
140 }
141
142 private:
143
146
149};
150
151/*---------------------------------------------------------------------------*/
152/*---------------------------------------------------------------------------*/
153
157class BinaryTree
158: public TraceAccessor
159{
160 public:
161
176 struct TreeNode
177 {
196
197 public:
198
199 friend std::ostream& operator<<(std::ostream& o, const TreeNode& t)
200 {
201 o << "(index=" << t.index << " level=" << t.level
202 << " parent=" << t.parent_index << " sum_left=" << t.nb_left_child
203 << " sum_right=" << t.nb_right_child << " left=" << t.left_child_index << " right="
205 << " left_id=" << t.left_partition_id << " right_id=" << t.right_partition_id
206 << ")";
207 return o;
208 }
209 };
210
211 public:
212
213 explicit BinaryTree(ITraceMng* tm)
214 : TraceAccessor(tm)
215 {}
216
217 public:
218
219 void doPartition(Int32 nb_part)
220 {
221 m_tree_info.resize(nb_part);
222 m_nb_part = nb_part;
223 Int32 sum = 0;
224 Int32 level = 0;
225 Int32 parent_index = -1;
226 Int32 part_id = 0;
227 _doRecursivePart(0, part_id, sum, parent_index, level);
228 for (const TreeNode& t : m_tree_info) {
229 info() << t;
230 }
231 }
232
234 ConstArrayView<TreeNode> tree() const { return m_tree_info; }
235
236 private:
237
238 UniqueArray<TreeNode> m_tree_info;
239 Int32 m_nb_part = 0;
240
241 void _doRecursivePart(Int32 partition_index, Int32& part_id, Int32& nb_child, Int32 parent_index, Int32 level)
242 {
243 Int32 part0_partition_index = partition_index;
244 Int32 part1_partition_index = partition_index + 1;
245
246 Int32 nb_child_left = 0;
247 Int32 nb_child_right = 0;
248
249 Int32 next_left = (2 * partition_index) + 1;
250 Int32 next_right = (2 * partition_index) + 2;
251
252 m_tree_info[part0_partition_index].parent_index = parent_index;
253
254 if ((next_left + 1) < m_nb_part) {
255 m_tree_info[part0_partition_index].left_child_index = next_left;
256 _doRecursivePart(next_left, part_id, nb_child_left,
257 part0_partition_index, level + 1);
258 }
259 else {
260 info() << "DO_PART LEFT parent=" << parent_index << " ID=" << part_id
261 << " nb_child=" << nb_child << " nb_child_left=" << nb_child_left;
262 m_tree_info[part0_partition_index].left_partition_id = part_id;
263 ++part_id;
264 ++nb_child_left;
265 }
266 if ((next_right + 1) < m_nb_part) {
267 m_tree_info[part0_partition_index].right_child_index = next_right;
268 _doRecursivePart(next_right, part_id, nb_child_right,
269 part1_partition_index, level + 1);
270 }
271 else {
272 info() << "DO_PART RIGHT parent=" << parent_index << " ID=" << part_id
273 << " nb_child" << nb_child << " nb_child_right=" << nb_child_right;
274 m_tree_info[part0_partition_index].right_partition_id = part_id;
275 ++part_id;
276 ++nb_child_right;
277 }
278 nb_child += (nb_child_left + nb_child_right);
279 info() << "End for me parent=" << parent_index
280 << " part0_index=" << part0_partition_index
281 << " nb_child_left=" << nb_child_left << " nb_child_right=" << nb_child_right
282 << " level=" << level;
283 m_tree_info[part0_partition_index].nb_left_child = nb_child_left;
284 m_tree_info[part0_partition_index].nb_right_child = nb_child_right;
285 m_tree_info[part0_partition_index].level = level;
286 m_tree_info[part0_partition_index].index = part0_partition_index;
287 }
288};
289
290/*---------------------------------------------------------------------------*/
291/*---------------------------------------------------------------------------*/
332class ArcaneGeometricMeshPartitionerService
334, public IMeshPartitioner
335{
336 public:
337
338 explicit ArcaneGeometricMeshPartitionerService(const ServiceBuildInfo& sbi);
339
340 public:
341
342 IMesh* mesh() const override { return BasicService::mesh(); }
343 void build() override {}
344 void partitionMesh(bool initial_partition) override;
345 void partitionMesh(bool initial_partition, Int32 nb_part) override
346 {
347 _partitionMesh(initial_partition, nb_part);
348 }
349
350 void notifyEndPartition() override {}
351
352 public:
353
354 void setMaximumComputationTime(Real v) override { m_max_computation_time = v; }
355 Real maximumComputationTime() const override { return m_max_computation_time; }
356 void setComputationTimes(RealConstArrayView v) override { m_computation_times.copy(v); }
357 RealConstArrayView computationTimes() const override { return m_computation_times; }
358 void setImbalance(Real v) override { m_imbalance = v; }
359 Real imbalance() const override { return m_imbalance; }
360 void setMaxImbalance(Real v) override { m_max_imbalance = v; }
361 Real maxImbalance() const override { return m_max_imbalance; }
362
363 ArrayView<float> cellsWeight() const override { return m_cells_weight; }
364
365 void setCellsWeight(ArrayView<float> weights, Integer nb_weight) override
366 {
367 m_cells_weight = weights;
368 m_nb_weight = nb_weight;
369 }
370
375
376 ILoadBalanceMng* loadBalanceMng() const override
377 {
379 }
380
381 private:
382
383 Real m_imbalance = 0.0;
384 Real m_max_imbalance = 0.0;
385 Real m_max_computation_time = 0.0;
386 Int32 m_nb_weight = 0;
387 ArrayView<float> m_cells_weight;
388 UniqueArray<Real> m_computation_times;
389 Int32 m_nb_part = 0;
390
391 private:
392
393 Real3 _computeBarycenter(const VariableCellReal3& cells_center, CellVectorView cells);
394 Real3x3 _computeInertiaTensor(Real3 center, const VariableCellReal3& cells_center,
395 CellVectorView cells);
396 Real3 _findPrincipalAxis(Real3x3 tensor);
397 void _partitionMesh2();
398 void _partitionMesh(bool initial_partition, Int32 nb_part);
399 bool _partitionMeshRecursive(const VariableCellReal3& cells_center,
400 CellVectorView cells, Int32 partition_index, Int32& part_id);
401 void _partitionMeshRecursive2(ConstArrayView<BinaryTree::TreeNode> tree_nodes, const VariableCellReal3& cells_center,
402 CellVectorView cells, Int32 partition_index);
403 void _printOwners();
404 Real3 _computeEigenValuesAndVectors(ITraceMng* tm, Real3x3 tensor, Real3x3& eigen_vectors, Real3& eigen_values);
405};
406
407/*---------------------------------------------------------------------------*/
408/*---------------------------------------------------------------------------*/
409
410ArcaneGeometricMeshPartitionerService::
411ArcaneGeometricMeshPartitionerService(const ServiceBuildInfo& sbi)
413{
414}
415
416/*---------------------------------------------------------------------------*/
417/*---------------------------------------------------------------------------*/
418
419// Function to calculate the inertia tensor of the mesh
420Real3 ArcaneGeometricMeshPartitionerService::
421_computeBarycenter(const VariableCellReal3& cells_center, CellVectorView cells)
422{
423 Real3 center;
424 IParallelMng* pm = mesh()->parallelMng();
425 ENUMERATE_ (Cell, icell, cells) {
426 center += cells_center[icell];
427 }
428 Int64 local_nb_cell = cells.size();
429 Int64 total_nb_cell = pm->reduce(Parallel::ReduceSum, local_nb_cell);
430 if (total_nb_cell == 0)
431 return {};
432 Real3 sum_center = pm->reduce(Parallel::ReduceSum, center);
433 Real3 global_center = sum_center / static_cast<Real>(total_nb_cell);
434 return global_center;
435}
436
437/*---------------------------------------------------------------------------*/
438/*---------------------------------------------------------------------------*/
439
440/*
441 * \brief Calculates the inertia tensor of the mesh.
442 */
443Real3x3 ArcaneGeometricMeshPartitionerService::
444_computeInertiaTensor(Real3 center, const VariableCellReal3& cells_center, CellVectorView cells)
445{
446 IParallelMng* pm = mesh()->parallelMng();
447
448 Real3x3 tensor;
449
450 ENUMERATE_ (Cell, icell, cells) {
451 Real3 cell_coord = cells_center[icell];
452 double dx = cell_coord.x - center.x;
453 double dy = cell_coord.y - center.y;
454 double dz = cell_coord.z - center.z;
455
456 tensor[0][0] += dy * dy + dz * dz;
457 tensor[1][1] += dx * dx + dz * dz;
458 tensor[2][2] += dx * dx + dy * dy;
459 tensor[0][1] -= dx * dy;
460 tensor[0][2] -= dx * dz;
461 tensor[1][2] -= dy * dz;
462 }
463
464 Real3x3 sum_tensor = pm->reduce(Parallel::ReduceSum, tensor);
465
466 sum_tensor[1][0] = sum_tensor[0][1];
467 sum_tensor[2][0] = sum_tensor[0][2];
468 sum_tensor[2][1] = sum_tensor[1][2];
469
470 return sum_tensor;
471}
472
473/*---------------------------------------------------------------------------*/
474/*---------------------------------------------------------------------------*/
475
476/*
477 * \brief Finds the principal inertia axis of the mesh.
478 */
479Real3 ArcaneGeometricMeshPartitionerService::
480_findPrincipalAxis(Real3x3 tensor)
481{
482 info() << "Tensor=" << tensor;
483
484 EigenValueAndVectorComputer eigen_computer;
485 eigen_computer.computeForMatrix(tensor);
486 info() << "EigenValues = " << eigen_computer.eigenValues();
487 info() << "EigenVectors = " << eigen_computer.eigenVectors();
488 Real3x3 eigen_vectors = eigen_computer.eigenVectors();
489 Real3 v = eigen_vectors[0];
490 // If the smallest eigenvector is zero, take the next one
491 // (generally this does not happen unless the algorithm in
492 // 'computeForMatrix' did not converge).
493 if (math::isNearlyZero(v.normL2())) {
494 v = eigen_vectors[1];
495 if (math::isNearlyZero(v.normL2()))
496 v = eigen_vectors[2];
497 }
498 info() << "EigenVector=" << v;
499 return v;
500}
501
502/*---------------------------------------------------------------------------*/
503/*---------------------------------------------------------------------------*/
504
505// Function to partition the mesh into two sub-domains
506void ArcaneGeometricMeshPartitionerService::
507_partitionMesh2()
508{
509 // Calculates the center of the cells
510 VariableCellReal3 cells_center(VariableBuildInfo(mesh(), "ArcaneCellCenter"));
511 IItemFamily* cell_family = mesh()->cellFamily();
512 CellVector cells_vector(cell_family, cell_family->allItems().own().view().localIds());
513 CellVectorView cells = cells_vector.view();
514
515 info() << "** ** DO_PARTITION_MESH2 nb_cell=" << cells.size();
516
517 // Calculates the center of each cell
518 {
519 const VariableNodeReal3& nodes_coordinates = mesh()->nodesCoordinates();
520 ENUMERATE_ (Cell, icell, cells) {
521 Real3 c;
522 for (NodeLocalId n : icell->nodeIds())
523 c += nodes_coordinates[n];
524 cells_center[icell] = c / icell->nbNode();
525 }
526 }
527
528 BinaryTree binary_tree(traceMng());
529 binary_tree.doPartition(m_nb_part);
530
531 bool do_new = true;
532 if (platform::getEnvironmentVariable("GEOMETRIC_PARTITIONER_IMPL") == "1")
533 do_new = false;
534 info() << "Using geoemtric partitioner do_new?=" << do_new;
535 if (do_new) {
536 _partitionMeshRecursive2(binary_tree.tree(), cells_center, cells, 0);
537 }
538 else {
539 Int32 partition_index = 0;
540 Int32 part_id = 0;
541 _partitionMeshRecursive(cells_center, cells, partition_index, part_id);
542 }
543}
544
545/*---------------------------------------------------------------------------*/
546/*---------------------------------------------------------------------------*/
547
548bool ArcaneGeometricMeshPartitionerService::
549_partitionMeshRecursive(const VariableCellReal3& cells_center,
550 CellVectorView cells, Int32 partition_index, Int32& part_id)
551{
552 Int32 part0_partition_index = partition_index;
553 Int32 part1_partition_index = partition_index + 1;
554 // For testing. To be removed.
555 Int32 total_nb_cell = mesh()->parallelMng()->reduce(Parallel::ReduceSum, cells.size());
556 info() << "Doing partition partition_index=" << partition_index << " total_nb_cell=" << total_nb_cell;
557 if (part1_partition_index >= m_nb_part)
558 return true;
559
560 info() << "Doing partition really partition_index=" << partition_index;
561 IItemFamily* cell_family = mesh()->cellFamily();
562 VariableItemInt32& cells_new_owner = cell_family->itemsNewOwner();
563
564 // Calculate the center of mass
565 Real3 center = _computeBarycenter(cells_center, cells);
566 info() << "GlobalCenter=" << center;
567
568 // Calculate the inertia tensor
569 Real3x3 tensor = _computeInertiaTensor(center, cells_center, cells);
570
571 // Find the principal inertia axis
572 Real3 eigenvector = _findPrincipalAxis(tensor);
573 info() << "EigenVector=" << eigenvector;
574
575 const Int32 nb_cell = cells.size();
576 UniqueArray<Int32> part0_cells;
577 part0_cells.reserve(nb_cell);
578 UniqueArray<Int32> part1_cells;
579 part1_cells.reserve(nb_cell);
580
581 // Checks which part the cell will belong to
582 // by calculating the dot product between the eigenvector
583 // and the vector from the center of gravity to the cell.
584 // The sign value indicates which part it is in.
585 info() << "Doing partition setting nb_cell=" << nb_cell << " partition=" << part0_partition_index << " " << part1_partition_index;
586 ENUMERATE_ (Cell, icell, cells) {
587 const Real3 cell_coord = cells_center[icell];
588
589 Real projection = 0.0;
590 projection += (cell_coord.x - center.x) * eigenvector.x;
591 projection += (cell_coord.y - center.y) * eigenvector.y;
592 projection += (cell_coord.z - center.z) * eigenvector.z;
593
594 if (projection < 0.0) {
595 part0_cells.add(icell.itemLocalId());
596 }
597 else {
598 part1_cells.add(icell.itemLocalId());
599 }
600 }
601 CellVectorView part0(cell_family, part0_cells);
602 CellVectorView part1(cell_family, part1_cells);
603
604 // If _partitionMeshRecursive() returns true, then there are no more sub-partitions
605 // to perform. In this case, we fill the owners of the cells
606 // for the partition.
607 if (_partitionMeshRecursive(cells_center, part0, (2 * partition_index) + 1, part_id)) {
608 info() << "Filling left part part_index=" << part_id << " nb_cell=" << part0.size();
609 ENUMERATE_ (Cell, icell, part0) {
610 cells_new_owner[icell] = part_id;
611 }
612 ++part_id;
613 }
614 if (_partitionMeshRecursive(cells_center, part1, (2 * partition_index) + 2, part_id)) {
615 info() << "Filling right part part_index=" << part_id << " nb_cell=" << part1.size();
616 ENUMERATE_ (Cell, icell, part1) {
617 cells_new_owner[icell] = part_id;
618 }
619 ++part_id;
620 }
621 return false;
622}
623
624/*---------------------------------------------------------------------------*/
625/*---------------------------------------------------------------------------*/
626
627void ArcaneGeometricMeshPartitionerService::
628_partitionMeshRecursive2(ConstArrayView<BinaryTree::TreeNode> tree_nodes,
629 const VariableCellReal3& cells_center,
630 CellVectorView cells, Int32 partition_index)
631{
632 Int32 part0_partition_index = partition_index;
633 Int32 part1_partition_index = partition_index + 1;
634 IParallelMng* pm = mesh()->parallelMng();
635 // For testing. To be removed later
636 Int32 total_nb_cell = pm->reduce(Parallel::ReduceSum, cells.size());
637 info() << "Doing partition (V2) partition_index=" << partition_index << " total_nb_cell=" << total_nb_cell;
638
639 info() << "Doing partition (V2) really partition_index=" << partition_index;
640 IItemFamily* cell_family = mesh()->cellFamily();
641 VariableItemInt32& cells_new_owner = cell_family->itemsNewOwner();
642
643 // Calculate the center of mass
644 Real3 center = _computeBarycenter(cells_center, cells);
645 info() << "GlobalCenter=" << center;
646
647 // Calculate the inertia tensor
648 Real3x3 tensor = _computeInertiaTensor(center, cells_center, cells);
649
650 // Find the principal inertia axis
651 Real3 eigenvector = _findPrincipalAxis(tensor);
652 info() << "EigenVector=" << eigenvector;
653
654 const Int32 nb_cell = cells.size();
655
656 // Calculates for each element the dot product between
657 // the eigenvector and the vector connecting the center of gravity
658 // to this element and stores it in projections
659 info() << "Doing partition (V2) nb_cell=" << nb_cell << " setting partition=" << part0_partition_index << " " << part1_partition_index;
660 UniqueArray<Real> projections(nb_cell);
661 Real min_projection = std::numeric_limits<Real>::max();
662 Real max_projection = std::numeric_limits<Real>::min();
663 ENUMERATE_ (Cell, icell, cells) {
664 const Real3 cell_coord = cells_center[icell];
665 Real projection = math::dot(cell_coord - center, eigenvector);
666 projections[icell.index()] = projection;
667 min_projection = math::min(min_projection, projection);
668 max_projection = math::max(max_projection, projection);
669 }
670
671 // Globally calculates the min and max of the projection.
672 min_projection = pm->reduce(Parallel::ReduceMin, min_projection);
673 max_projection = pm->reduce(Parallel::ReduceMax, max_projection);
674
675 info() << "min_projection=" << min_projection << " max_projection=" << max_projection;
676
677 // To account for the ratio between the two partitions which is not necessarily 0.5,
678 // we determine several projection values that will be used to partition
679 // These tested values are in the interval [-min_projection, max_projection].
680 // We test nb_to_test values between '-min_projection' and 0, the value 0.0 and nb_to_test
681 // between 0 and max_projection. We will therefore test (2*nb_to_test)+1 values.
682
683 UniqueArray<Real> projections_to_test;
684 // TODO: make nb_to_test parameterizable.
685 // We could also perform a dichotomy on the projection values
686 // to better approximate the balance and not necessarily test too many values
687 int nb_to_test = 10;
688 const int total_nb_to_test = (2 * nb_to_test) + 1;
689 projections_to_test.resize(total_nb_to_test);
690 for (Int32 i = 0; i < nb_to_test; ++i) {
691 Real v1 = min_projection / (i + 1);
692 projections_to_test[i] = v1;
693 Real v2 = max_projection / (nb_to_test - i);
694 projections_to_test[i + 1 + nb_to_test] = v2;
695 }
696 projections_to_test[nb_to_test] = 0.0; //< Central projection
697 info() << "projections_to_test=" << projections_to_test;
698
699 // The binary tree allows knowing how many partitions need to be made
700 // on the left and right side. We use it to calculate a ratio
701 // ideal which is stored in expected_ratio.
702 BinaryTree::TreeNode current_tree_node = tree_nodes[partition_index];
703 Int32 nb_left_child = current_tree_node.nb_left_child;
704 Int32 nb_right_child = current_tree_node.nb_right_child;
705 // Desired ratio of elements between the two parts
706 Real expected_ratio = 1.0;
707 if (nb_left_child != 0) {
708 Real r_nb_left_child = static_cast<Real>(nb_left_child);
709 Real r_nb_right_child = static_cast<Real>(nb_right_child);
710 expected_ratio = r_nb_left_child / (r_nb_left_child + r_nb_right_child);
711 }
712
713 // This array will contain, in the form of a pair, the number of elements
714 // of each partition for each test.
715 // global_nb_parts[i*2+p] is the value of the left partition (p==0)
716 // or right (p==1) for the i-th test.
717 SmallArray<Int64> global_nb_parts(total_nb_to_test * 2);
718
719 // Tests all partitions and calculates the one whose ratio is the
720 // closest to the desired ratio. This is the one we will take for
721 // partitioning
722 Int32 wanted_projection_index = 0;
723 Real best_partition_ratio = std::numeric_limits<Real>::max();
724 for (Int32 z = 0; z < total_nb_to_test; ++z) {
725 Int32 nb_new_part0 = 0;
726 Int32 nb_new_part1 = 0;
727 const Real projection_to_test = projections_to_test[z];
728 // TODO: Move this loop outside
729 ENUMERATE_ (Cell, icell, cells) {
730 Real projection = projections[icell.index()];
731 if (projection < projection_to_test)
732 ++nb_new_part0;
733 else
734 ++nb_new_part1;
735 }
736 global_nb_parts[0 + (z * 2)] = nb_new_part0;
737 global_nb_parts[1 + (z * 2)] = nb_new_part1;
738 }
739
740 // Sums the parts across all sub-domains.
741 pm->reduce(Parallel::ReduceSum, global_nb_parts.view());
742
743 for (Int32 z = 0; z < total_nb_to_test; ++z) {
744 Real ratio_0 = 1.0;
745 Int64 nb_part0 = global_nb_parts[0 + (z * 2)];
746 Int64 nb_part1 = global_nb_parts[1 + (z * 2)];
747 if (nb_part0 != 0) {
748 Real r_nb_part0 = static_cast<Real>(nb_part0);
749 Real r_nb_part1 = static_cast<Real>(nb_part1);
750 ratio_0 = r_nb_part0 / (r_nb_part0 + r_nb_part1);
751 }
752 Real diff_ratio = math::abs(expected_ratio - ratio_0);
753 info(4) << "Partition info nb_part0=" << nb_part0 << " nb_part1=" << nb_part1
754 << " ratio_0=" << ratio_0
755 << " nb_left_child=" << nb_left_child << " nb_right_child=" << nb_right_child
756 << " expected_ratio=" << expected_ratio
757 << " diff_ratio=" << diff_ratio
758 << " best_ratio=" << best_partition_ratio;
759 if (diff_ratio < best_partition_ratio) {
760 wanted_projection_index = z;
761 best_partition_ratio = diff_ratio;
762 }
763 }
764
765 const Real projection_to_use = projections_to_test[wanted_projection_index];
766 info() << "Keep projection index=" << wanted_projection_index << " projection=" << projection_to_use
767 << " best_ratio=" << best_partition_ratio;
768
769 UniqueArray<Int32> part0_cells;
770 part0_cells.reserve(nb_cell);
771 UniqueArray<Int32> part1_cells;
772 part1_cells.reserve(nb_cell);
773
774 // Checks which part the cell will belong to
775 // by calculating the dot product between the eigenvector
776 // and the vector from the center of gravity to the cell.
777 // The sign value indicates which part it is in.
778
779 ENUMERATE_ (Cell, icell, cells) {
780 Real projection = projections[icell.index()];
781 if (projection < projection_to_use) {
782 part0_cells.add(icell.itemLocalId());
783 }
784 else {
785 part1_cells.add(icell.itemLocalId());
786 }
787 }
788
789 CellVectorView part0(cell_family, part0_cells);
790 CellVectorView part1(cell_family, part1_cells);
791
792 Int32 child_left = current_tree_node.left_child_index;
793 if (child_left >= 0) {
794 _partitionMeshRecursive2(tree_nodes, cells_center, part0, child_left);
795 }
796 Int32 left_partition_id = current_tree_node.left_partition_id;
797 if (left_partition_id >= 0) {
798 info() << "Filling left part part_index=" << left_partition_id << " nb_cell=" << part0.size();
799 ENUMERATE_ (Cell, icell, part0) {
800 cells_new_owner[icell] = left_partition_id;
801 }
802 }
803 Int32 child_right = current_tree_node.right_child_index;
804 if (child_right >= 0) {
805 _partitionMeshRecursive2(tree_nodes, cells_center, part1, child_right);
806 }
807
808 Int32 right_partition_id = current_tree_node.right_partition_id;
809 if (right_partition_id >= 0) {
810 info() << "Filling right part part_index=" << right_partition_id << " nb_cell=" << part1.size();
811 ENUMERATE_ (Cell, icell, part1) {
812 cells_new_owner[icell] = right_partition_id;
813 }
814 }
815}
816
817/*---------------------------------------------------------------------------*/
818/*---------------------------------------------------------------------------*/
819
820void ArcaneGeometricMeshPartitionerService::
821_partitionMesh([[maybe_unused]] bool initial_partition, Int32 nb_part)
822{
823 m_nb_part = nb_part;
824
825 IPrimaryMesh* mesh = this->mesh()->toPrimaryMesh();
826
827 info() << "Doing mesh partition with ArcaneGeometricMeshPartitionerService nb_part=" << nb_part;
828 IParallelMng* pm = mesh->parallelMng();
829 Int32 nb_rank = pm->commSize();
830
831 if (nb_rank == 1) {
832 return;
833 }
834
835 _partitionMesh2();
836 _printOwners();
837 VariableItemInt32& cells_new_owner = mesh->itemsNewOwner(IK_Cell);
838 cells_new_owner.synchronize();
839 if (mesh->partitionConstraintMng()) {
840 // Deal with Tied Cells
841 mesh->partitionConstraintMng()->computeAndApplyConstraints();
842 }
843 mesh->utilities()->changeOwnersFromCells();
844}
845
846/*---------------------------------------------------------------------------*/
847/*---------------------------------------------------------------------------*/
848
849void ArcaneGeometricMeshPartitionerService::
850_printOwners()
851{
852 IParallelMng* pm = mesh()->parallelMng();
853 VariableItemInt32& cells_new_owner = mesh()->toPrimaryMesh()->itemsNewOwner(IK_Cell);
854 for (Int32 i = 0; i < m_nb_part; ++i) {
855 Int32 n = 0;
856 ENUMERATE_ (Cell, icell, mesh()->ownCells()) {
857 if (cells_new_owner[icell] == i)
858 ++n;
859 }
860 info() << "NbCell for part=" << i << " total=" << pm->reduce(Parallel::ReduceSum, n);
861 }
862}
863
864/*---------------------------------------------------------------------------*/
865/*---------------------------------------------------------------------------*/
866
868partitionMesh(bool initial_partition)
869{
870 _partitionMesh(initial_partition, mesh()->parallelMng()->commSize());
871}
872
873/*---------------------------------------------------------------------------*/
874/*---------------------------------------------------------------------------*/
875
876#if ARCANE_DEFAULT_PARTITIONER == ARCANEGEOMETRICMESHPARTITIONER_DEFAULT_PARTITIONER
877ARCANE_REGISTER_SERVICE_ARCANEGEOMETRICMESHPARTITIONERSERVICE(DefaultPartitioner,
879#endif
880ARCANE_REGISTER_SERVICE_ARCANEGEOMETRICMESHPARTITIONERSERVICE(ArcaneGeometricMeshPartitioner,
882
883/*---------------------------------------------------------------------------*/
884/*---------------------------------------------------------------------------*/
885
886} // End namespace Arcane
887
888/*---------------------------------------------------------------------------*/
889/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
Various mathematical functions.
ArcaneArcaneGeometricMeshPartitionerServiceObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
void setImbalance(Real v) override
Sets the computation time imbalance.
void setComputationTimes(RealConstArrayView v) override
Computation time of this subdomain. The first element indicates the computation time of the subdomain...
void setMaxImbalance(Real v) override
Sets the maximum allowed imbalance.
void build() override
Build-level construction of the service.
void notifyEndPartition() override
Notification when a re-partitioning finishes (after entity exchange).
IMesh * mesh() const override
Mesh associated with the partitioner.
Real imbalance() const override
Computation time imbalance.
void setILoadBalanceMng(ILoadBalanceMng *) override
Changes the ILoadBalanceMng to use.
Real maxImbalance() const override
Maximum allowed imbalance.
void setCellsWeight(ArrayView< float > weights, Integer nb_weight) override
Allows defining the weights of objects to be partitioned: ILoadBalanceMng must now be used.
void setMaximumComputationTime(Real v) override
Sets the proportion of computation time.
Modifiable view of an array of type T.
ConstArrayView< TreeNode > tree() const
List of tree nodes.
Constant view of an array of type T.
Class to calculate the eigenvalues and eigenvectors of a matrix.
Real3x3 eigenVectors() const
Returns the eigenvectors of the matrix in ascending order.
Real3 eigenValues() const
Returns the eigenvalues of the matrix in ascending order.
void computeForMatrix(const Real3x3 &orig_matrix)
Calculates the eigenvalues and eigenvectors of orig_matrix.
Interface for registering variables for load balancing.
virtual IItemFamily * cellFamily()=0
Returns the cell family.
Interface of a mesh partitioner.
virtual VariableNodeReal3 & nodesCoordinates()=0
Node coordinates.
virtual IParallelMng * parallelMng()=0
Parallelism manager.
virtual IPrimaryMesh * toPrimaryMesh()=0
Returns the instance in the form of an IPrimaryMesh.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
virtual VariableItemInt32 & itemsNewOwner(eItemKind kind)=0
Variable containing the identifier of the owning subdomain.
CellGroup ownCells() const
Returns the group containing all cells specific to this domain.
Class managing a 3-dimensional real vector.
Definition Real3.h:132
Class managing a 3x3 real matrix.
Definition Real3x3.h:67
Structure containing the information to create a service.
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
ITraceMng * traceMng() const
Trace manager.
1D data vector with value semantics (STL style).
__host__ __device__ Real dot(Real2 u, Real2 v)
Dot product of u by v in .
Definition MathUtils.h:94
__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
ItemVectorT< Cell > CellVector
Vector of cells.
Definition ItemTypes.h:602
ItemVectorViewT< Cell > CellVectorView
View over a vector of cells.
Definition ItemTypes.h:305
MeshVariableScalarRefT< Cell, Real3 > VariableCellReal3
Coordinate type quantity at cell center.
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
ItemVariableScalarRefT< Int32 > VariableItemInt32
32-bit integer type quantity
@ ReduceMin
Minimum of values.
@ ReduceMax
Maximum of values.
constexpr __host__ __device__ Real squareNormL2(const Real2 &v)
Returns the squared norm of the pair $ .
Definition Real2.h:476
__host__ __device__ Real3 multiply(const Real3x3 &m, Real3 v)
3x3 matrix multiplied by a vector.
Definition MathUtils.h:848
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- 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.
@ IK_Cell
Cell mesh entity.
double Real
Type representing a real number.
@ Cell
The mesh is AMR by cell.
Definition MeshKind.h:53
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
Int32 nb_right_child
Number of children on the right (if not terminal).
Int32 right_partition_id
Index of the right partition (only for terminal nodes).
Int32 left_partition_id
Index of the left partition (only for terminal nodes).
Int32 parent_index
Index in the tree of the parent (-1 if none).
Int32 left_child_index
Linear index of the left child (-1 if none).
Int32 nb_left_child
Number of children on the left (if not terminal).
Int32 right_child_index
Linear index of the right child (-1 if none).