TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Partitionneur_Metis.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15#include <Partitionneur_Metis.h>
16#include <Domaine.h>
17#include <Static_Int_Lists.h>
18
19#include <Param.h>
20
21#include <communications.h>
22#include <Domain_Graph.h>
23#include <metis.h>
24
25inline void not_implemented(const Nom& chaine)
26{
27 Cerr << chaine << " is not implemented yet to the METIS API." << finl;
29}
30
31Implemente_instanciable_32_64(Partitionneur_Metis_32_64,"Partitionneur_Metis",Partitionneur_base_32_64<_T_>);
32// XD partitionneur_metis_64 partitionneur_deriv metis_64 INHERITS_BRACE Metis is an external partitionning library. It
33// XD_CONT is a general algorithm that will generate a partition of big (64b) domain.
34// XD partitionneur_metis partitionneur_deriv metis INHERITS_BRACE Metis is an external partitionning library. It is a
35// XD_CONT general algorithm that will generate a partition of the domain.
36
37namespace
38{
39
40/*! Handy method to convert a METIS idx_t to a '_SIZE_' value.
41 * In the case where METIS is compiled in 64b, and the template is instanciated in 32b, this is actually a down-cast, so check.
42 */
43template<typename _SIZE_>
44_SIZE_ from_idx_t_to_SIZE(idx_t val)
45{
46 assert(val < std::numeric_limits<_SIZE_>::max());
47 return (_SIZE_) val;
48}
49}
50
51
52template <typename _SIZE_>
54{
55 Cerr << "Partitionneur_Metis_32_64<_SIZE_>::printOn invalid\n" << finl;
57 return os;
58}
59
60
61template <typename _SIZE_>
63{
64 param.ajouter("nb_parts",&nb_parties_,Param::REQUIRED);
65 param.ajouter("nb_essais",&nb_essais_);
66 param.ajouter_condition("(value_of_nb_parts_ge_1)_and_(value_of_nb_parts_le_100000)","The following condition must be satisfied : 1 <= nb_parties <= 100000");
67 param.ajouter_non_std("pmetis",(this));
68 param.ajouter_non_std("kmetis",(this)); // XD attr kmetis rien kmetis OPT The default values are pmetis, default
69 // XD_CONT parameters are automatically chosen by Metis. \'kmetis\' is faster than pmetis option but the last option
70 // XD_CONT produces better partitioning quality. In both cases, the partitioning quality may be slightly improved by
71 // XD_CONT increasing the nb_essais option (by default N=1). It will compute N partitions and will keep the best one
72 // XD_CONT (smallest edge cut number). But this option is CPU expensive, taking N=10 will multiply the CPU cost of
73 // XD_CONT partitioning by 10. NL2 Experiments show that only marginal improvements can be obtained with non default
74 // XD_CONT parameters.
75 param.ajouter_non_std("match_type",(this));
76 param.ajouter_non_std("initial_partition_type",(this));
77 param.ajouter_non_std("refinement_type",(this));
78 param.ajouter_flag("use_weights",&use_weights_); // XD attr use_weights rien use_weights OPT If use_weights is
79 // XD_CONT specified, weighting of the element-element links in the graph is used to force metis to keep opposite
80 // XD_CONT periodic elements on the same processor. This option can slightly improve the partitionning quality but it
81 // XD_CONT consumes more memory and takes more time. It is not mandatory since a correction algorithm is always
82 // XD_CONT applied afterwards to ensure a correct partitionning for periodic boundaries.
83 param.ajouter_flag("use_segment_to_build_connectivite_elem_elem",&use_segment_to_build_connectivite_elem_elem_); // option pour construire le grpah a partir des liens (segment) pour reseau electrique, sides ....
84}
85
86template <typename _SIZE_>
88{
90 {
91 Cerr << "WARNING! You're using a sequential algorithm on " << this->nproc() << "processors " << finl;
92 Cerr << "Use PARMETIS for parallel domain cutting" << finl;
94 }
95 if (mot=="pmetis")
96 {
97 Cerr << " Algorithm PMETIS" << finl;
98 algo_ = PMETIS;
99 return 1;
100 }
101 else if (mot=="kmetis")
102 {
103 Cerr << " Algorithm KMETIS" << finl;
104 algo_ = KMETIS;
105 return 1;
106 }
107 else if (mot=="match_type")
108 {
109 not_implemented(mot);
110 return 1;
111 }
112 else if (mot=="initial_partition_type")
113 {
114 not_implemented(mot);
115 return 1;
116 }
117 else if (mot=="refinement_type")
118 {
119 not_implemented(mot);
120 return 1;
121 }
122 else
124}
125
126/*! @brief Calcule le graphe de connectivite pour Metis, appelle le partitionneur et remplit elem_part (pour chaque element, numero de la partie qui lui
127 *
128 * est attribuee).
129 * Les parties sont equilibrees de facon a minimiser le nombre de faces de joint
130 * et a equilibrer le nombre d'elements par partie.
131 *
132 */
133template <typename _SIZE_>
135{
136#ifdef NO_METIS
137 Cerr << "METIS is not compiled with this version. Use another partition tool like Tranche." << finl;
139#else
140 if (!ref_domaine_)
141 {
142 Cerr << "Error in Partitionneur_Metis_32_64<_SIZE_>::construire_partition\n";
143 Cerr << " The domain has not been associated" << finl;
145 }
146 if (nb_parties_ <= 0)
147 {
148 Cerr << "Error in Partitionneur_Metis_32_64<_SIZE_>::construire_partition\n";
149 Cerr << " The parts number has not been initialized" << finl;
151 }
152
153 // Cas particulier: si nb_parts == 1, METIS ne veut rien faire...
154 if (nb_parties_ == 1)
155 {
156 int_t nb_elem = ref_domaine_->nb_elem_tot();
157 if (use_segment_to_build_connectivite_elem_elem_)
158 nb_elem = ref_domaine_->nb_som();
159 elem_part.resize(nb_elem);
160 elem_part = 0;
161 return;
162 }
163
164 if (ref_domaine_->nb_elem() == 0)
165 return;
166
167 Cerr << "Partitionneur_Metis_32_64<_SIZE_>::construire_partition" << finl;
168 Cerr << " Construction of graph connectivity..." << finl;
169 Static_Int_Lists_32_64<_SIZE_> graph_elements_perio;
170 //const Domaine& dom = ref_domaine_.valeur();
171 Domain_Graph graph;
172 if (!use_segment_to_build_connectivite_elem_elem_)
173 graph.construire_graph_elem_elem<_SIZE_>(ref_domaine_.valeur(),
174 use_weights_,
175 graph_elements_perio);
176 else
177 graph.construire_graph_from_segment<_SIZE_>(ref_domaine_.valeur(), use_weights_);
178
179 std::vector<idx_t> partition(graph.nvtxs);
180 idx_t int_parts = nb_parties_;
181 idx_t edgecut = 0; // valeur renvoyee par metis (nombre total de faces de joint)
182
183 switch(algo_)
184 {
185 case PMETIS:
186 {
187 Cerr << "===============" << finl;
188 Cerr << "Call for PMETIS" << finl;
189 Cerr << "===============" << finl;
190 // Voir le manual.pdf de METIS 5.0
191 idx_t options[METIS_NOPTIONS];
192 METIS_SetDefaultOptions(options);
193 //options[METIS_OPTION_PTYPE]=METIS_PTYPE_RB|METIS_PTYPE_KWAY; // Methode de partitionnement
194 //options[METIS_OPTION_OBJTYPE]=METIS_OBJTYPE_CUT|METIS_OBJTYPE_VOL; // Objective type
195 //options[METIS_OPTION_CTYPE]=METIS_CTYPE_SHEM|METIS_CTYPE_RM; // Matching scheme during coarsening
196 //options[METIS_OPTION_IPTYPE]=METIS_IPTYPE_GROW; // Algorithm during initial partitioning
197 //options[METIS_OPTION_RTYPE]=METIS_RTYPE_FM; // Algorithm for refinement
198 options[METIS_OPTION_NCUTS]=nb_essais_; // Nombre de partitionnements testes pour en prendre le meilleur
199 options[METIS_OPTION_NUMBERING]=0; // Numerotation C qui demarre a 0
200 options[METIS_OPTION_DBGLVL]=111111111; // Mode verbose maximal
201 //options[METIS_OPTION_NO2HOP]=1; // 5.1.0: not perform any 2-hop matchings (as 5.0.3)
202 idx_t ncon=1;
203
204 // Implementation reduite (plusieurs valeurs par defaut->nullptr) pour METIS 5.0
205 int status = METIS_PartGraphRecursive(&graph.nvtxs, &ncon, graph.xadj.addr(),
206 graph.adjncy.addr(), graph.vwgts.addr(), nullptr, graph.ewgts.addr(),
207 &int_parts, nullptr, nullptr, options,
208 &edgecut, partition.data());
209 if (status != METIS_OK)
210 {
211 Cerr << "Call to METIS failed." << finl;
212 if (status == METIS_ERROR_INPUT) Cerr << "It seems there is an input error." << finl;
213 if (status == METIS_ERROR_MEMORY) Cerr << "It seems it couldn't allocate enough memory." << finl;
214 if (status == METIS_ERROR) Cerr << "It seems there is a METIS internal error." << finl;
215 Cerr << "Contact TRUST support." << finl;
217 }
218 Cerr << "===============" << finl;
219 break;
220 }
221 case KMETIS:
222 {
223 Cerr << " Call for KMETIS" << finl;
224 idx_t options[METIS_NOPTIONS];
225 METIS_SetDefaultOptions(options);
226 //options[METIS_OPTION_PTYPE]=METIS_PTYPE_RB|METIS_PTYPE_KWAY; // Methode de partitionnement
227 //options[METIS_OPTION_OBJTYPE]=METIS_OBJTYPE_CUT|METIS_OBJTYPE_VOL; // Objective type
228 //options[METIS_OPTION_CTYPE]=METIS_CTYPE_SHEM; // Matching scheme during coarsening
229 //options[METIS_OPTION_IPTYPE]=METIS_IPTYPE_GROW; // Algorithm during initial partitioning
230 //options[METIS_OPTION_RTYPE]=METIS_RTYPE_FM; // Algorithm for refinement
231 options[METIS_OPTION_NCUTS]=nb_essais_; // Nombre de partitionnements testes pour en prendre le meilleur
232 options[METIS_OPTION_NUMBERING]=0; // Numerotation C qui demarre a 0
233 options[METIS_OPTION_DBGLVL]=111111111; // Mode verbose maximal
234 idx_t ncon=1;
235 // Conseil de la doc Metis 4.0 : METIS_PartGraphKway si int_parts>8, METIS_PartGraphRecursive sinon...
236 // En effet semble plus rapide, mais edgecut en sortie est moins bon...
237 int status = METIS_PartGraphKway(&graph.nvtxs, &ncon, graph.xadj.addr(),
238 graph.adjncy.addr(), graph.vwgts.addr(), nullptr, graph.ewgts.addr(),
239 &int_parts, nullptr, nullptr, options ,
240 &edgecut, partition.data());
241 if (status != METIS_OK)
242 {
243 Cerr << "Call to METIS failed." << finl;
244 if (status == METIS_ERROR_INPUT) Cerr << "It seems there is an input error." << finl;
245 if (status == METIS_ERROR_MEMORY) Cerr << "It seems it couldn't allocate enough memory." << finl;
246 if (status == METIS_ERROR) Cerr << "It seems there is a METIS internal error." << finl;
247 Cerr << "Contact TRUST support." << finl;
249 }
250 break;
251 }
252 default:
253 {
254 Cerr << "Internal error Partitionneur_Metis_32_64: not coded" << finl;
256 }
257 }
258 Cerr << "Partitioning quality : edgecut = " << edgecut << finl;
259 Cerr << "-> It is roughly the total number of edges (faces) which will be shared by the processors." << finl;
260 Cerr << "-> The lesser this number is, the lesser the total volume of communication between processors." << finl;
261 Cerr << "-> You can increase nb_essais option (default 1) to try to reduce (but at a higher CPU cost) this number." << finl;
262 Cerr << "===============" << finl;
263
264 const int_t n = from_idx_t_to_SIZE<_SIZE_>(graph.nvtxs);
265 elem_part.resize(n);
266 for (int_t i = 0; i < n; i++)
267 elem_part[i] = static_cast<int>(partition[i]); // here cast is OK, a partition index (i.e. a proc number) should always be under 32b
268
269 // Correction de la partition pour la periodicite. (***)
270 if (graph_elements_perio.get_nb_lists() > 0)
271 {
272 Cerr << "Correction of the partition for the periodicity" << finl;
273 this->corriger_bords_avec_liste(ref_domaine_.valeur(), 0, elem_part);
274 Cerr << " If this number is high, you can improve the splitting with the option use_weights\n"
275 << " but it takes more memory)" << finl;
276 }
277
278 if (!use_segment_to_build_connectivite_elem_elem_)
279 {
280 Cerr << "Correction elem0 on processor 0" << finl;
281 this->corriger_elem0_sur_proc0(elem_part);
282 }
283#endif
284}
285
286
287
288template class Partitionneur_Metis_32_64<int>;
289#if INT_is_64_ == 2
291#endif
292
293
Build the graph of the domain that the METIS/PARMETIS/PTSCOTCH libraries need.
ArrOfIdx_ vwgts
void construire_graph_elem_elem(const Domaine_32_64< _SIZE_ > &dom, bool use_weights, Static_Int_Lists_32_64< _SIZE_ > &graph_elements_perio)
void construire_graph_from_segment(const Domaine_32_64< _SIZE_ > &dom, bool use_weights)
ArrOfIdx_ ewgts
ArrOfIdx_ adjncy
ArrOfIdx_ xadj
Une chaine de caractere (Nom) en majuscules.
Definition Motcle.h:26
class Nom Une chaine de caractere pour nommer les objets de TRUST
Definition Nom.h:31
virtual void set_param(Param &) const
Definition Objet_U.h:135
friend class Entree
Definition Objet_U.h:76
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
Helper class to factorize the readOn method of Objet_U classes.
Definition Param.h:112
void ajouter_flag(const char *keyword, const bool *value)
Register a boolean flag whose mere presence switches it to true.
Definition Param.cpp:474
void ajouter_condition(const char *condition, const char *message, const char *name=0)
Declare a post-read logical condition that must hold on the parameter values.
Definition Param.cpp:496
void ajouter(const char *keyword, const int *value, Param::Nature nat=Param::OPTIONAL)
Register an integer parameter.
Definition Param.cpp:364
@ REQUIRED
Definition Param.h:115
void ajouter_non_std(const char *keyword, const Objet_U *value, Param::Nature nat=Param::OPTIONAL)
Register a keyword handled by Objet_U::lire_motcle_non_standard.
Definition Param.cpp:489
Partition d'un domaine en nb_parties parties equilibrees en utilisant la librairie METIS.
int lire_motcle_non_standard(const Motcle &, Entree &) override
Lecture des parametres de type non simple d'un objet_U a partir d'un flot d'entree.
TRUSTVect< int, _SIZE_ > BigIntVect_
void construire_partition(BigIntVect_ &elem_part, int &nb_parts_tot) const override
Calcule le graphe de connectivite pour Metis, appelle le partitionneur et remplit elem_part (pour cha...
Classe de base des partitionneurs de domaine (pour decouper un maillage avant un calcul parallele).
int lire_motcle_non_standard(const Motcle &, Entree &) override
Lecture des parametres de type non simple d'un objet_U a partir d'un flot d'entree.
static void corriger_bords_avec_liste(const Domaine_t &dom, const int_t my_offset, BigIntVect_ &elem_part)
Calcul des graphes de connectivite elements periodiques et appel a corriger_periodique_avec_graphe.
static void corriger_elem0_sur_proc0(BigIntVect_ &elem_part)
corrige la partition pour que l'element 0 du domaine initial se trouve sur le premier sous-domaine de...
static bool is_parallel()
Definition Process.cpp:110
static int nproc()
renvoie le nombre de processeurs dans le groupe courant Voir Comm_Group::nproc() et PE_Groups::curren...
Definition Process.cpp:104
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
Classe de base des flux de sortie.
Definition Sortie.h:52
Cette classe permet de stocker des listes d'entiers accessibles en temps constant.
int_t get_nb_lists() const
renvoie le nombre de listes stockees
_TYPE_ * addr()
void resize(_SIZE_, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTVect.tpp:91