TrioCFD 1.9.9_beta
TrioCFD documentation
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 // Number of elements in the domain
41 const int_t nb_elem = les_elems.dimension_tot(0);
42 const int_t local_nb_elem = les_elems.dimension(0);
43 // Number of vertices per element
44 const int nb_sommets_par_element = les_elems.dimension_int(1);
45
46 // Build an array initialised to zero: for each vertex,
47 // number of neighbouring elements
48 ArrOfInt_t nb_elements_voisins(nb_sommets);
49
50 // First pass: compute the number of neighbouring elements for each
51 // vertex to create the data structure
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 polyhedra case
61 if (sommet==-1) break;
62 nb_elements_voisins[sommet]++;
63 }
64 }
65
66 som_elem.set_list_sizes(nb_elements_voisins);
67
68 // Reuse the array to store the number of elements in
69 // each list while filling it
70 nb_elements_voisins = 0;
71
72 // Fill the array of neighbouring elements.
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 polyhedra case
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 // Sort all lists in ascending order
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 // FIRST STEP: compute the number of vertices and edges of the graph:
113 int_t nb_edges = liaisons.size_array(); // 2 links per connection
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 // build item-item connectivity
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(); // some links may be duplicated
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// If use_weights, weight the links between periodic elements
178// to force them onto the same processor. This reduces the number
179// of corrections needed afterwards (see (***))
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 numbering 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 /* do not include virtual elements */);
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 // FIRST STEP: compute the number of vertices and edges of the graph:
243
244 // Total number of boundary faces:
245 const int_t nb_faces_bord = dom.nb_faces_frontiere();
246
247 // Each mesh element is a graph "vertex".
248 // Graph "edges" connect each element to its face-adjacent neighbours.
249 // There are as many edges as faces with two neighbours, times 2.
250 // Classic formula: nb_internal_faces = nnn/2 with:
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 // SECOND STEP: fill the graph
292 // Algorithm: For each element, loop over the faces of the element
293 // and add an "edge" between the element and its neighbours. The
294 // neighbouring element across a face is found using find_adjacent_elements.
295 //
296 // Two working arrays:
297 SmallArrOfTID_t une_face(nb_sommets_par_face); // vertices of the current face
298 SmallArrOfTID_t voisins; // elements neighbouring 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 // Build this face of the element:
317 // (indices of the face vertices in the domain)
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 // Find the neighbouring elements of this face:
332 // (indices of the elements containing the face vertices)
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 // No neighbour: internal error
342 error = 3;
343 break;
344 }
345 case 1:
346 {
347 // Only one neighbour: this is a boundary face
348 const int_t elem = voisins[0];
349 if (elem != i_elem + my_offset)
350 error = 3; // element i_elem is not a neighbour: internal error
351 else
352 elem_voisin = -1;
353 break;
354 }
355 case 2:
356 {
357 // The most common case:
358 const int_t elem0 = voisins[0];
359 const int_t elem1 = voisins[1];
360 if (elem0 == i_elem + my_offset) //neighbours contain global numbering
361 elem_voisin = elem1;
362 else if (elem1 == i_elem + my_offset)
363 elem_voisin = elem0;
364 else
365 error = 3; // element i_elem is not a neighbour: internal error
366 break;
367 }
368 default:
369 {
370 // More than two neighbours
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 can cause discontinuous or highly unbalanced partitions
395
396 }
397 edge_count++;
398 }
399 }
400 // Add supplementary connections for periodic faces
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 // Connections between periodic faces are strong:
414 // we want these elements to be on the same processor.
415 // Warning: if the weight is set much higher than other
416 // edges, the quality of the partition degrades!
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
class Domaine_32_64 A Domain is a mesh composed of a set of geometric elements of the same type.
Definition Domaine.h:62
void construire_elem_virt_pe_num()
Definition Domaine.cpp:703
int_t nb_faces_frontiere() const
Returns the number of boundary faces of the domain (sum of boundaries, connections,...
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
Returns the number of vertices of the domain.
Definition Domaine.h:121
const Noms & bords_perio() const
Definition Domaine.h:278
Class Elem_geom_base This class is the base class for the definition of elements.
virtual int get_tab_faces_sommets_locaux(IntTab &faces_som_local) const
Fills faces_som_local(i,j) giving for 0 <= i < nb_faces() and 0 <= j < nb_som_face(i) the local verte...
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)
An array of character strings (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)
Computes a connectivity graph between elements connected by periodic faces.
Base class for polyedrons and polygons. Connectivity is stored in descending mode:
static int check_int_overflow(trustIdType)
Definition Process.cpp:437
static trustIdType mppartial_sum(trustIdType i)
Computes the partial sum of x over processors 0 to me()-1 (returns 0 on processor 0).
Definition Process.cpp:403
static bool is_parallel()
Definition Process.cpp:108
static int nproc()
Returns the number of processors in the current group. See Comm_Group::nproc() and PE_Groups::current...
Definition Process.cpp:102
static double mp_sum(double)
Computes the sum of x over all processors in the current group.
Definition Process.cpp:145
static void imprimer_ram_totale(int all_process=0)
Definition Process.cpp:655
static int me()
Returns the rank of the local processor in the current communication group. See Comm_Group::rank() an...
Definition Process.cpp:122
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
static bool is_sequential()
Definition Process.cpp:113
This class allows storing lists of integers accessible in constant time.
void set_value(int_t i_liste, int_t i_element, int_t valeur)
Assigns "valeur" to the j-th element of the i-th list with 0 <= i < get_nb_lists() and 0 <= j < get_l...
int_t get_list_size(int_t i_liste) const
Returns the number of elements in list i.
void trier_liste(int_t i)
Sorts the values of the i-th list in ascending order.
void set_list_sizes(const ArrOfInt_t &sizes)
Destroys existing lists and creates new ones.
_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