TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Connex_components.cpp
1/****************************************************************************
2* Copyright (c) 2025, 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
16#include <Connex_components.h>
17#include <communications.h>
18#include <TRUSTTab.h>
19#include <ArrOfBit.h>
20
21/*! @brief Computes the connected sets by faces of non-"marked" elements (elements are connected to each other via a symmetric graph
22 *
23 * passing through the faces).
24 * A connected domain portion has a unique number 0 <= i < N
25 * and is delimited either by a boundary, or by a neighbor element "marked"
26 * by num_compo[elem] = -1.
27 * This method is sequential (may be called on a single processor)
28 *
29 * @param (elem_faces)
30 * @param (faces_elem)
31 * @param (num_compo)
32 */
33int search_connex_components_local(const IntTab& elem_faces, const IntTab& faces_elem, IntVect& num_compo)
34{
35 const int nbelem = num_compo.size_totale();
36 const int nb_voisins = elem_faces.dimension(1);
37 assert(elem_faces.dimension_tot(0) == nbelem);
38 {
39 int i;
40 for (i = 0; i < nbelem; i++)
41 if (num_compo[i] != -1)
42 num_compo[i] = -2;
43 }
44 int start_element = 0;
45 int num_compo_courant = 0;
46 ArrOfInt liste_elems;
47
48 ArrOfInt tmp_liste;
49
50 do
51 {
52 // Find the next element not yet assigned to a connected component
53 while (start_element < nbelem && num_compo[start_element] >= -1)
54 start_element++;
55 if (start_element == nbelem)
56 break;
57 // Search for the elements of the connected component starting from this element
58 liste_elems.resize_array(1);
59 liste_elems[0] = start_element;
60 num_compo[start_element] = num_compo_courant;
61 while (liste_elems.size_array() > 0)
62 {
63 tmp_liste.resize_array(0);
64 const int liste_elems_size = liste_elems.size_array();
65 for (int i_elem = 0; i_elem < liste_elems_size; i_elem++)
66 {
67 const int elem = liste_elems[i_elem];
68 // Add the unassigned neighbors of this element to the list to
69 // be processed in the next step
70 for (int j = 0; j < nb_voisins; j++)
71 {
72 const int face = elem_faces(elem, j);
73 const int voisin = faces_elem(face, 0) + faces_elem(face, 1) - elem;
74 if (voisin >= 0)
75 {
76 const int num = num_compo[voisin];
77 if (num == -2)
78 {
79 num_compo[voisin] = num_compo_courant;
80 tmp_liste.append_array(voisin);
81 }
82 }
83 }
84 }
85 liste_elems = tmp_liste;
86 }
87 num_compo_courant++;
88 }
89 while (1);
90 // Returns the number of local connected components found
91 return num_compo_courant;
92}
93
94/*! @brief Searches for the connected components of a local (non-distributed across processors) non-symmetric graph.
95 *
96 * @param (graph)
97 * @param (connex_components)
98 */
99int compute_graph_connex_components(const IntTab& graph, ArrOfInt& connex_components)
100{
101 // connex_components must already have the correct size on entry!
102 const int nb_sommets = connex_components.size_array();
103
104 // renum_data defines linked lists of "vertex" numbers belonging to
105 // the same connected component.
106 // renum_data(i,0) = number of the first "vertex" in the list to which i belongs
107 // renum_data(i,1) = number of the next "vertex" in the list
108 IntTab renum_data(nb_sommets, 2);
109 // At the start, each vertex is alone in a list:
110 int i_sommet;
111 for (i_sommet = 0; i_sommet < nb_sommets; i_sommet++)
112 {
113 renum_data(i_sommet, 0) = i_sommet;
114 renum_data(i_sommet, 1) = -1; // end of list
115 }
116 const int nbcouples = graph.dimension(0);
117 for (int i_couple = 0; i_couple < nbcouples; i_couple++)
118 {
119 const int compo1 = graph(i_couple, 0); // the smaller one
120 const int compo2 = graph(i_couple, 1); // the larger one
121 assert(compo1 != compo2);
122 // If the two components are already in the same list,
123 // do nothing.
124 if (renum_data(compo1, 0) == renum_data(compo2, 0))
125 continue;
126 // Merge list1 containing compo1 and list2 containing compo2:
127 // 1) find the end of the first list
128 int fin_liste1 = compo1;
129 for (;;)
130 {
131 const int next = renum_data(fin_liste1, 1);
132 if (next < 0)
133 break;
134 fin_liste1 = next;
135 }
136 // 2) append list2 at the end of list1:
137 const int debut_liste2 = renum_data(compo2, 0);
138 renum_data(fin_liste1, 1) = debut_liste2;
139 // 2) update the beginning of list for list2:
140 i_sommet = debut_liste2;
141 const int debut_liste1 = renum_data(compo1, 0);
142 do
143 {
144 renum_data(i_sommet, 0) = debut_liste1;
145 i_sommet = renum_data(i_sommet, 1);
146 }
147 while (i_sommet >= 0);
148 }
149
150 // Create a contiguous numbering for the components:
151 // Next number to assign
152 int count = 0;
153 connex_components = -1;
154 for (i_sommet = 0; i_sommet < nb_sommets; i_sommet++)
155 {
156 if (connex_components[i_sommet] < 0)
157 {
158 // vertex not yet processed
159 // Assign a new number to all vertices of the connected component
160 // to which i_sommet belongs:
161 for (int i = renum_data(i_sommet, 0); i >= 0; i = renum_data(i, 1))
162 connex_components[i] = count;
163 // New number for the next component
164 count++;
165 }
166 }
167 // Return the number of connected components found
168 return count;
169}
170
171/*! @brief Searches for the connected components of a set of elements distributed across all processors.
172 *
173 * This method is parallel and must be called at the
174 * same time on all processors.
175 *
176 * @param (num_compo)
177 * @param (nb_local_components)
178 */
179int compute_global_connex_components(IntVect& num_compo, int nb_local_components)
180{
181 const int nbelem = num_compo.size();
182 const int nbelem_tot = num_compo.size_totale();
183 //int i;
184
185 // Transform local connected component indices into a global index
186 // (a shift is added to the global indices using mppartial_sum())
187 const int decalage = static_cast<int>(Process::mppartial_sum(nb_local_components)); // compo number are never huge
188 const int nb_total_components = static_cast<int>(Process::mp_sum(nb_local_components));
189 for (int i = 0; i < nbelem_tot; i++)
190 if (num_compo[i] >= 0)
191 num_compo[i] += decalage;
192
193 // To find correspondences between a local component number and a
194 // number of the same component on the neighboring processor, we create a copy of
195 // the num_compo array on which we perform an echange_espace_virtuel(). Thus,
196 // in the virtual entries of the array, num_compo holds the number of the
197 // local component and copie_compo holds the number of that same component on
198 // the processor that owns the element. These two numbers therefore designate
199 // the same connected component.
200 IntVect copie_compo(num_compo);
201 copie_compo.echange_espace_virtuel();
202
203 // Search for equivalences between local component numbers and
204 // neighboring component numbers. We build a graph whose
205 // edges connect equivalent components.
206 // Marker array for equivalences already found.
207 // Dimensions = nb local components * nb total components
208 // (to avoid counting the same component more than once).
209 ArrOfBit markers(nb_local_components * nb_total_components);
210 markers = 0;
211 // Correspondence table between local and remote connected components
212 IntTab graph;
213
214 int graph_size = 0;
215 // Iterate over virtual elements only
216 for (int i = nbelem; i < nbelem_tot; i++)
217 {
218 int compo = num_compo[i];
219 if (compo < 0)
220 continue;
221 int compo2 = copie_compo[i];
222 // Index of the pair compo2/compo in the markers array
223 // The num_compo array must contain only local components:
224 assert(compo >= decalage && compo - decalage < nb_local_components);
225 // compo2 is necessarily a remote component.
226 assert(compo2 < decalage || compo2 - decalage >= nb_local_components);
227 const int index = (compo - decalage) * nb_total_components + compo2;
228 if (!markers.testsetbit(index))
229 {
230 graph.resize(graph_size+1, 2);
231 // Put the smaller component number in column 0:
232 if (compo2 < compo)
233 {
234 int tmp = compo;
235 compo = compo2;
236 compo2 = tmp;
237 }
238 graph(graph_size, 0) = compo;
239 graph(graph_size, 1) = compo2;
240 graph_size++;
241 }
242 }
243
244 ArrOfInt renum;
246 {
247 // Receive graph portions from other processors
248 IntTab tmp;
249 const int nproc = Process::nproc();
250 int pe;
251 for (pe = 1; pe < nproc; pe++)
252 {
253 recevoir(tmp, pe, 54 /* tag */);
254 const int n2 = tmp.dimension(0);
255 graph.resize(graph_size + n2, 2);
256 for (int i = 0; i < n2; i++)
257 {
258 graph(graph_size, 0) = tmp(i, 0);
259 graph(graph_size, 1) = tmp(i, 1);
260 graph_size++;
261 }
262 }
263 // Compute the connected components of the graph
264 renum.resize_array(nb_total_components);
265 const int n = compute_graph_connex_components(graph, renum);
266 Process::Journal() << "compute_global_connex_components: nb_components=" << n << finl;
267 }
268 else
269 {
270 // Send the local graph to processor 0
271 envoyer(graph, 0, 54 /* tag */);
272 }
273
274 // Receive the connected components
275 envoyer_broadcast(renum, 0 /* source processor */);
276
277 // Renumber the components in num_compo
278 for (int i = 0; i < nbelem_tot; i++)
279 {
280 const int x = num_compo[i];
281 if (x >= 0)
282 {
283 const int new_x = renum[x];
284 num_compo[i] = new_x;
285 }
286 }
287 // Verification: if we do a virtual space exchange,
288 // this should not change the connected component numbers!
289
290 int nb_components = 0;
291 // All processors hold the same renum array, so all compute
292 // the same maximum!
293 if (renum.size_array() > 0)
294 nb_components = max_array(renum) + 1;
295 return nb_components;
296}
297
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 Sortie & Journal(int message_level=0)
Returns a static Sortie object used as an event journal.
Definition Process.cpp:592
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 int je_suis_maitre()
Returns 1 if on the master processor of the current group (i.e. me() == 0), 0 otherwise.
Definition Process.cpp:82
void append_array(_TYPE_ valeur)
_SIZE_ size_array() const
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
_SIZE_ dimension_tot(int) const override
Definition TRUSTTab.tpp:160
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
_SIZE_ size() const
Definition TRUSTVect.tpp:45
_SIZE_ size_totale() const
Definition TRUSTVect.tpp:61