TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Domain_Graph.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 <Domaine.h>
16#include <Static_Int_Lists.h>
17#include <Connectivite_som_elem.h>
18#include <Poly_geom_base.h>
19#include <Matrix_tools.h>
20#include <Matrice_Morse.h>
21#include <Array_tools.h>
22#include <communications.h>
23#include <Domain_Graph.h>
24#include <Partitionneur_base.h>
25#include <map>
26
27namespace
28{
29
30template <typename _SIZE_>
31void construire_connectivite_real_som_virtual_elem(const _SIZE_ nb_sommets,
32 const IntTab_T<_SIZE_>& les_elems,
34 const IntTab_T<_SIZE_>& elem_virt_pe_num,
35 const SmallArrOfTID_T<_SIZE_>& offsets)
36{
37 using int_t = _SIZE_;
38 using ArrOfInt_t = ArrOfInt_T<_SIZE_>;
39
40 // Nombre d'elements du domaine
41 const int_t nb_elem = les_elems.dimension_tot(0);
42 const int_t local_nb_elem = les_elems.dimension(0);
43 // Nombre de sommets par element
44 const int nb_sommets_par_element = les_elems.dimension_int(1);
45
46 // Construction d'un tableau initialise a zero : pour chaque sommet,
47 // nombre d'elements voisins de ce sommet
48 ArrOfInt_t nb_elements_voisins(nb_sommets);
49
50 // Premier passage : on calcule le nombre d'elements voisins de chaque
51 // sommet pour creer la structure de donnees
52
53 //real elements
54 for (int_t elem = 0; elem < nb_elem; elem++)
55 {
56 for (int i = 0; i < nb_sommets_par_element; i++)
57 {
58 int_t sommet = les_elems(elem, i);
59 if(sommet >= nb_sommets) continue; //skipping virtual node
60 // GF cas des polyedres
61 if (sommet==-1) break;
62 nb_elements_voisins[sommet]++;
63 }
64 }
65
66 som_elem.set_list_sizes(nb_elements_voisins);
67
68 // On reutilise le tableau pour stocker le nombre d'elements dans
69 // chaque liste pendant qu'on la remplit
70 nb_elements_voisins = 0;
71
72 // Remplissage du tableau des elements voisins.
73 for (int_t elem = 0; elem < nb_elem; elem++)
74 {
75 for (int i = 0; i < nb_sommets_par_element; i++)
76 {
77 int_t sommet = les_elems(elem, i);
78 if(sommet >= nb_sommets) continue;
79 // GF cas des polyedres
80 if (sommet==-1) break;
81 int_t n = (nb_elements_voisins[sommet])++;
82
83 int_t elem_num_global = -1;
84 if(elem>= local_nb_elem)
85 {
86 int proc_of_elem = static_cast<int>(elem_virt_pe_num(elem-local_nb_elem, 0)); // first col of this array is always int
87 int_t elem_number_on_local_proc = elem_virt_pe_num(elem-local_nb_elem, 1);
88 elem_num_global = elem_number_on_local_proc + offsets[proc_of_elem];
89 }
90 else
91 elem_num_global = elem + offsets[Process::me()];
92
93 som_elem.set_value(sommet, n, elem_num_global);
94 }
95 }
96
97 // Tri de toutes les listes dans l'ordre croissant
98 som_elem.trier_liste(-1);
99}
100}
101
102template <typename _SIZE_>
104 bool use_weights)
105{
106 using int_t = _SIZE_;
107 using IntTab_t = IntTab_T<_SIZE_>;
108
109 const IntTab_t& liaisons = dom.les_elems();
110
111 // ****************************************************************
112 // PREMIERE ETAPE: calcul du nombre de vertex et edges du graph:
113 int_t nb_edges = liaisons.size_array(); // 2 liens par liaison
114 int_t nb_elem=liaisons.local_max_vect()+1; // mouif <- [ABN] lol
115
116 assert(nb_elem < std::numeric_limits<_SIZE_>::max());
117
118 nvtxs = nb_elem;
119 xadj.resize_array(nb_elem+1);
120 vwgts.resize_array(0);
121 if (! use_weights)
122 {
123 weightflag = 0;
124 ewgts.resize_array(0);
125 }
126 else
127 {
128 abort();
129 weightflag = 1;
130 ewgts.resize_array(nb_edges);
131 }
132
133 // on construit connectivite item item
134 IntTab_t stencil(0,2);
135
136 int_t size=0;
137 int_t nbl=liaisons.dimension(0);
138 for (int i=0; i<nbl; i++)
139 {
140 stencil.resize(size+2,2);
141 int_t n1=liaisons(i,0);
142 int_t n2=liaisons(i,1);
143 {
144 stencil(size,0)=n1;
145 stencil(size,1)=n2;
146
147 size++;
148 stencil(size,0)=n2;
149 stencil(size,1)=n1;
150
151 size++;
152 }
153 }
154 tableau_trier_retirer_doublons(stencil);
155
156 // Dimensioning tab1, tab2
157 IntTab_t tab1, tab2;
158 tab1.resize(nb_elem+1);
159 tab1 = 1;
160 tab1[nb_elem]=size+1;
161 tab2.resize(size);
162
163 Matrix_tools::fill_csr_arrays(nb_elem,nb_elem,stencil,tab1, tab2);
164
165 nb_edges=tab2.size_array(); // des liens peuvent etre doubles
166 nedges = nb_edges;
167 adjncy.resize_array(nb_edges);
168 //
169 assert(tab1.size_array()==nvtxs+1);
170
171 for (int_t c=0; c<static_cast<_SIZE_>(nvtxs+1); c++) // overflow check done before
172 xadj[c]=tab1[c]-1;
173 for (int_t c=0; c<static_cast<_SIZE_>(nedges); c++)
174 adjncy[c]=tab2[c]-1;
175}
176
177// Si use_weights, on pondere les liens entre les elements periodiques
178// pour les forcer a etre sur le meme processeur. Cela diminue le nombre
179// de corrections a faire ensuite (voir (***))
180template<typename _SIZE_>
182 bool use_weights,
183 Static_Int_Lists_32_64<_SIZE_>& graph_elements_perio)
184{
185 using int_t = _SIZE_;
186 using IntTab_t = IntTab_T<_SIZE_>;
187 using SmallArrOfTID_t = SmallArrOfTID_T<_SIZE_>;
188 using Poly_geom_base_t = Poly_geom_base_32_64<_SIZE_>;
189
190 const Noms& liste_bords_periodiques = dom.bords_perio();
192 const Elem_geom_base_32_64<_SIZE_>& type_elem = dom.type_elem().valeur();
193 IntTab faces_element_reference;
194 const int is_regular = type_elem.get_tab_faces_sommets_locaux(faces_element_reference);
195 // Invoke proper method if Polygon or Polyedron ...:
196 if (! is_regular)
197 ref_cast(Poly_geom_base_t, type_elem).get_tab_faces_sommets_locaux(faces_element_reference,0);
198 int nb_faces_par_element = faces_element_reference.dimension(0);
199 const int nb_sommets_par_face = faces_element_reference.dimension(1);
200
201 const IntTab_t& elem_som = dom.les_elems();
202 const int_t nb_elem = dom.nb_elem();
203
204 SmallArrOfTID_t offsets(Process::nproc());
205 trustIdType totsum = Process::mppartial_sum(nb_elem);
206 if (totsum > std::numeric_limits<_SIZE_>::max())
207 {
208 Cerr << "Are you trying to partition a Domain_64 with a non-64b 'Partitionneur'? Total number of elements is too big!" << finl;
209 Process::exit(-1);
210 }
211 offsets = static_cast<_SIZE_>(totsum);
212 envoyer_all_to_all(offsets, offsets);
213 int_t my_offset = offsets[Process::me()];
214
215 Cerr << " Construction of the som_elem connectivity" << finl;
217 {
218 //what we're doing here is not equivalent to construire_connectivite_som_elem with virtual elements set to 1
219 //in the latter, we also build the connectivity for virtual nodes
220 //here, we add the connectivity of real nodes only with virtual elements
221 // + we want som_elem to contain global numerotation for elements
222 IntTab_t elem_virt_pe_num;
223 dom.construire_elem_virt_pe_num(elem_virt_pe_num);
224 construire_connectivite_real_som_virtual_elem(dom.nb_som(), elem_som, som_elem, elem_virt_pe_num, offsets);
225 }
226 else
227 construire_connectivite_som_elem(dom.nb_som(), elem_som, som_elem,
228 0 /* ne pas inclure les elements virtuels */);
229
231 int_t nb_connexions_perio = 0;
232 if (liste_bords_periodiques.size() > 0)
233 {
234 Cerr << " Construction of graph connectivity for periodic boundaries" << finl;
236 som_elem,
237 my_offset,
238 graph_elements_perio);
239 }
240
241 // ****************************************************************
242 // PREMIERE ETAPE: calcul du nombre de vertex et edges du graph:
243
244 // Nombre total de faces de bord:
245 const int_t nb_faces_bord = dom.nb_faces_frontiere();
246
247 // Chaque element du maillage est un "vertex" du graph.
248 // Les "edges" du graph relient chaque element a ses voisins par une face.
249 // Il y a autant d'edges que de faces ayant deux voisins, fois 2
250 // Formule classique: nb_faces internes = nnn/2 avec :
251 int_t nnn = nb_elem * nb_faces_par_element - nb_faces_bord + nb_connexions_perio;
252 if (sub_type(Poly_geom_base_t, dom.type_elem().valeur()))
253 {
254 const Poly_geom_base_t& poly=ref_cast(Poly_geom_base_t,dom.type_elem().valeur());
255 nnn= poly.get_somme_nb_faces_elem() - nb_faces_bord + nb_connexions_perio;
256 }
257
258 const int_t nb_edges = nnn + nb_faces_bord;
259
260 nvtxs = nb_elem + dom.nb_faces_joint(); //each joint face is linked to a virtual element
261 xadj.resize_array(nb_elem+1);
262 xadj = -1;
263 adjncy.resize_array(nb_edges+nb_faces_bord);
264 adjncy = -1;
265 edgegsttab.resize_array(nb_edges+nb_faces_bord);
266 edgegsttab = -1;
267
268 vwgts.resize_array(0); //nullptr
269 if (! use_weights)
270 {
271 weightflag = 0;
272 ewgts.resize_array(0); //nullptr
273 }
274 else
275 {
276 weightflag = 1;
277 ewgts.resize_array(nb_edges + nb_faces_bord);
278 }
279
280 vtxdist.resize_array(Process::nproc()+1);
281 for(int p = 0; p < Process::nproc(); p++)
282 vtxdist[p] = offsets[p];
283 if(std::is_same<idx_t, int>::value)
285 else
287
289 Cerr << " Construction of the elem_elem connectivity" << finl;
290 // ***************************************************************
291 // DEUXIEME ETAPE: remplissage du graph
292 // Algorithme: Pour chaque element, boucle sur les faces de l'element
293 // et on ajoute un "edge" entre l'element et ses voisins. On cherche
294 // l'element voisin par une face a l'aide de la fonction find_adjacent_elements.
295 //
296 // Deux tableaux de travail:
297 SmallArrOfTID_t une_face(nb_sommets_par_face); // Les sommets de la face en cours
298 SmallArrOfTID_t voisins; // Les elements voisins d'une_face
299
300 int error = 0;
301 int_t edge_count = 0;
302 for (int_t i_elem = 0; i_elem < nb_elem; i_elem++)
303 {
304 xadj[i_elem] = edge_count;
305
306 if (!is_regular)
307 {
308 ref_cast(Poly_geom_base_t,type_elem).get_tab_faces_sommets_locaux(faces_element_reference,i_elem);
309 int nb_faces_elem = faces_element_reference.dimension(0);
310 while ( faces_element_reference(nb_faces_elem-1,0)==-1)
311 nb_faces_elem--;
312 nb_faces_par_element= nb_faces_elem;
313 }
314 for (int i_face = 0; i_face < nb_faces_par_element; i_face++)
315 {
316 // Construction de cette face de l'element:
317 // (indice des sommets de la face dans le domaine)
318 {
319 for (int i = 0; i < nb_sommets_par_face; i++)
320 {
321 const int i_som = faces_element_reference(i_face, i);
322 if (i_som<0)
323 une_face[i] = i_som;
324 else
325 {
326 const int_t sommet = elem_som(i_elem, i_som);
327 une_face[i] = sommet;
328 }
329 }
330 }
331 // Recherche des elements voisins de cette face:
332 // (indices des elements contenant les sommets de la face)
333 find_adjacent_elements(som_elem, une_face, voisins);
334
335 const int nb_voisins = voisins.size_array();
336 int_t elem_voisin = -1;
337 switch (nb_voisins)
338 {
339 case 0:
340 {
341 // Aucun voisin: erreur interne
342 error = 3;
343 break;
344 }
345 case 1:
346 {
347 // Un seul voisin, c'est une face frontiere
348 const int_t elem = voisins[0];
349 if (elem != i_elem + my_offset)
350 error = 3; // l'element i_elem n'est pas voisin: erreur interne
351 else
352 elem_voisin = -1;
353 break;
354 }
355 case 2:
356 {
357 // Le cas le plus courant:
358 const int_t elem0 = voisins[0];
359 const int_t elem1 = voisins[1];
360 if (elem0 == i_elem + my_offset) //neighbours contain global numerotation
361 elem_voisin = elem1;
362 else if (elem1 == i_elem + my_offset)
363 elem_voisin = elem0;
364 else
365 error = 3; // l'element i_elem n'est pas voisin: erreur interne
366 break;
367 }
368 default:
369 {
370 // Plus de deux voisins
371 error = 2;
372 }
373 }
374
375 if (error)
376 break;
377
378 if (elem_voisin >= 0)
379 {
380 if (edge_count >= nb_edges)
381 {
382 error = 1;
383 break;
384 }
385 adjncy[edge_count] = elem_voisin;
386
387 if (use_weights)
388 {
389 //internal connection have more weight:
390 //it's better if the generated partition is not dispatched on several processors
391 if( my_offset <= elem_voisin && elem_voisin < nb_elem+my_offset) //neighbour belongs to me = strong connection
392 ewgts[edge_count] = 4;
393 else
394 ewgts[edge_count] = 1; // -1 peut faire des partitions discontinues ou pas equilibrees du tout
395
396 }
397 edge_count++;
398 }
399 }
400 // Ajout des connexions supplementaires pour les faces periodiques
401 if (nb_connexions_perio > 0)
402 {
403 const int_t n = graph_elements_perio.get_list_size(i_elem);
404 for (int_t i = 0; i < n; i++)
405 {
406 const int_t elem_voisin = graph_elements_perio(i_elem, i);
407 if (edge_count >= nb_edges)
408 {
409 error = 1;
410 break;
411 }
412 adjncy[edge_count] = elem_voisin;
413 // Les connexions entre faces periodiques sont fortes:
414 // on veut que ces elements soient sur le meme processeur
415 // Attention, si on met un poids nettement plus eleve que les autres
416 // edges, on degrade la qualite du decoupage !
417 if (use_weights)
418 ewgts[edge_count] = 4;
419 edge_count++;
420 }
421 }
422 else if (use_weights)
423 {
425 {
426 Cerr << "Warning: You specify use_weights option with Metis but you didn't use Periodique keyword" << finl;
427 Cerr << "to define the boundary where periodicity apply." << finl;
428 Cerr << "Either suppress use_weight or add Periodique keyword." << finl;
430 }
431 }
432 if (error > 0)
433 break;
434 }
435 xadj[nb_elem] = edge_count;
436 nedges = edge_count;
437
438 std::map<int_t,int_t> global_to_local_index;
439 int_t cnt=nb_elem;
440 for(int_t e=0; e<nb_edges + nb_faces_bord; e++)
441 {
442 int_t vertex = static_cast<_SIZE_>(adjncy[e]); // overflow check done aboveS
443 if( my_offset <= vertex && vertex < nb_elem+my_offset) //neighbour belongs to me
444 edgegsttab[e] = vertex - my_offset;
445 else
446 {
447 if(global_to_local_index.find(vertex) != global_to_local_index.end())
448 edgegsttab[e] = global_to_local_index[vertex];
449 else
450 {
451 global_to_local_index[vertex] =cnt++;
452 edgegsttab[e] = global_to_local_index[vertex];
453 }
454 }
455 }
456
457 if (error == 1)
458 {
459 Cerr << "Error in Domaine_Graph::construire_graph_elem_elem\n"
460 << " The number of element-element connections is greater than expected\n"
461 << " The number of boundary faces is wrong.\n"
462 << " You must discretize the domain to check." << finl;
464 }
465 else if (error == 2)
466 {
467 Cerr << "Error in Domain_Graph::construire_graph_elem_elem\n"
468 << " Problem in the mesh: one internal face has more than two neighboring elements\n"
469 << " List of neighboring elements:" << voisins
470 << "\n Nodes of the face:" << une_face;
472 }
473 else if (error)
474 {
475 Cerr << "Internal error in Domain_Graph::construire_graph_elem_elem\n"
476 << " (error " << error << ") Problem in neighborhood algorithms"
477 << finl;
479 }
481}
482
483
484// Explicit instanciations:
485template void Domain_Graph::construire_graph_from_segment(const Domaine_32_64<int>& dom, bool use_weights );
487 bool use_weights, Static_Int_Lists_32_64<int>& graph_elements_perio);
488
489#if INT_is_64_ == 2
490template void Domain_Graph::construire_graph_from_segment(const Domaine_32_64<trustIdType>& dom, bool use_weights );
492 bool use_weights, Static_Int_Lists_32_64<trustIdType>& graph_elements_perio);
493#endif
ArrOfIdx_ vwgts
ArrOfIdx_ edgegsttab
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_ vtxdist
ArrOfIdx_ xadj
classe Domaine_32_64 un Domaine est un maillage compose d'un ensemble d'elements geometriques de meme...
Definition Domaine.h:62
void construire_elem_virt_pe_num()
Definition Domaine.cpp:704
int_t nb_faces_frontiere() const
Renvoie le nombre de faces frontiere du domaine (somme des nombres de bords, de raccords et de bords ...
Definition Domaine.h:488
int_t nb_faces_joint() const
Definition Domaine.h:168
IntTab_t & les_elems()
Definition Domaine.h:129
int_t nb_elem() const
Definition Domaine.h:131
int_t nb_som() const
Renvoie le nombre de sommets du domaine.
Definition Domaine.h:121
const Noms & bords_perio() const
Definition Domaine.h:278
Classe Elem_geom_base Cette classe est la classe de base pour la definition d'elements.
virtual int get_tab_faces_sommets_locaux(IntTab &faces_som_local) const
remplit le tableau faces_som_local(i,j) qui donne pour 0 <= i < nb_faces() et 0 <= j < nb_som_face(i)...
static void fill_csr_arrays(const _SIZE_ nb_lines, const _SIZE_ nb_columns, const TRUSTTab< _SIZE_, _SIZE_ > &stencil, TRUSTVect< _SIZE_, _SIZE_ > &tab1, TRUSTVect< _SIZE_, _SIZE_ > &tab2)
Un tableau de chaine de caracteres (VECT(Nom)).
Definition Noms.h:26
static int_t calculer_graphe_connexions_periodiques(const Domaine_t &domaine, const Static_Int_Lists_t &som_elem, const int_t my_offset, Static_Int_Lists_t &graph)
Calcul d'un graphe de connectivite entre les elements lies par des faces periodiques.
Base class for polyedrons and polygons. Connectivity is stored in descending mode:
static int check_int_overflow(trustIdType)
Definition Process.cpp:428
static trustIdType mppartial_sum(trustIdType i)
Calul de la somme partielle de i sur les processeurs 0 a me()-1 (renvoie 0 sur le processeur 0).
Definition Process.cpp:396
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 double mp_sum(double)
Calcule la somme de x sur tous les processeurs du groupe courant.
Definition Process.cpp:146
static void imprimer_ram_totale(int all_process=0)
Definition Process.cpp:651
static int me()
renvoie mon rang dans le groupe de communication courant.
Definition Process.cpp:125
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
static bool is_sequential()
Definition Process.cpp:115
Cette classe permet de stocker des listes d'entiers accessibles en temps constant.
void set_value(int_t i_liste, int_t i_element, int_t valeur)
affecte la "valeur" au j-ieme element de la i-ieme liste avec 0 <= i < get_nb_lists() et 0 <= j < get...
int_t get_list_size(int_t i_liste) const
renvoie le nombre d'elements de la liste i
void trier_liste(int_t i)
tri par ordre croissant des valeurs de la i-ieme liste.
void set_list_sizes(const ArrOfInt_t &sizes)
detruit les listes existantes et en cree de nouvelles.
_SIZE_ size_array() const
int dimension_int(int d) const
Definition TRUSTTab.tpp:152
_SIZE_ dimension_tot(int) const override
Definition TRUSTTab.tpp:160
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133