TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Reordonner_faces_periodiques.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 <Reordonner_faces_periodiques.h>
16#include <Domaine.h>
17#include <Scatter.h>
18#include <Octree_Double.h>
19#include <Param.h>
20
21namespace
22{
23inline void message()
24{
25 Cerr << "You need to use the Declarer_bord_perio keyword on the periodic boundaries." << finl;
26 Cerr << "See the reference manual to use this keyword on your data file." << finl;
27}
28
29template <typename _SIZE_>
30inline void calculer_vecteur_2faces(const DoubleTab_T<_SIZE_>& coord,
31 const IntTab_T<_SIZE_>& faces,
32 const _SIZE_ i_face1,
33 const _SIZE_ i_face2,
34 ArrOfDouble& vect)
35{
36 const int nb_som_faces = faces.dimension_int(1);
37 const int dim = coord.dimension_int(1);
38 assert(vect.size_array() == dim);
39 vect = 0.;
40 // Compute the vector between the centre of face i and the centre of face i+n
41 for (int j = 0; j < nb_som_faces; j++)
42 {
43 const _SIZE_ sommet1 = faces(i_face1, j),
44 sommet2 = faces(i_face2, j);
45 for (int compo = 0; compo < dim; compo++)
46 vect[compo] += coord(sommet2, compo) - coord(sommet1, compo);
47 }
48 vect /= nb_som_faces;
49}
50
51
52template <typename _SIZE_>
53double local_norme_vect(const DoubleVect_T<_SIZE_>& dv)
54{
55 using int_t = _SIZE_;
56 double x=0.0;
57 for(int_t i=0; i< dv.size_reelle(); i++)
58 x += dv(i)*dv(i);
59 x = sqrt(x);
60 return x;
61}
62
63template <typename _SIZE_>
64void build_trad_space(const Domaine_32_64<_SIZE_>& domaine, IntTab_T<_SIZE_>& renum)
65{
66 // If we reach this point, we are already in parallel, and hence we should be in 32b only,
67 // never in 64b
68 assert(Process::is_parallel());
69
70 const MD_Vector& md_sommets = domaine.les_sommets().get_md_vector();
71 Scatter::construire_espace_virtuel_traduction(md_sommets, md_sommets, renum, 1 /* fatal errors */);
72}
73
74#if INT_is_64_ == 2
75template <>
76void build_trad_space(const Domaine_32_64<trustIdType>& domaine, IntTab_T<trustIdType>& renum)
77{
78 Cerr << "Reordonner_faces_periodiques::renum_som_perio() was invoked from a parallel environment with" << finl;
79 Cerr << "a 64b object. Did you use 'Domaine_64' keyword instead of 'Domaine' after a Scatter keyword??" << finl;
80 Process::exit(-1);
81}
82#endif
83
84}
85
86template<typename _SIZE_>
88{
89 using DoubleTab_t = DoubleTab_T<_SIZE_>;
90 using IntTab_t = IntTab_T<_SIZE_>;
91 using ArrOfDouble_t = ArrOfDouble_T<_SIZE_>;
92 using Frontiere_t = Frontiere_32_64<_SIZE_>;
93
94 const DoubleTab_t& sommets = dom.coord_sommets();
95 const int dim = static_cast<int>(sommets.dimension(1));
96 direction_perio.resize_array(dim);
97 direction_perio = 0.;
98 const Frontiere_t& front = dom.frontiere(bord);
99 const int_t nb_faces = front.nb_faces();
100 if (nb_faces == 0)
101 return;
102 const IntTab_t& faces = front.faces().les_sommets();
103 const int nb_som_face = static_cast<int>(faces.dimension(1));
104 DoubleTab_t normale(1, dim);
105 IntTab_t une_face(1, nb_som_face);
106 for (int i = 0; i < nb_som_face; i++)
107 une_face(0, i) = faces(0, i);
108 dom.type_elem()->calculer_normales(une_face, normale);
109 normale /= local_norme_vect<_SIZE_>(normale);
110
111 ArrOfDouble_t delta(nb_faces);
112 ArrOfDouble vect(dim);
113 for (int_t i = 1; i < nb_faces; i++)
114 {
115 calculer_vecteur_2faces<_SIZE_>(sommets, faces, 0, i, vect);
116 double x = 0.;
117 for (int j = 0; j < dim; j++)
118 x += vect[j] * normale(0,j);
119 delta[i] = x;
120 }
121 const double min = min_array(delta);
122 const double max = max_array(delta);
123 double facteur = (std::fabs(min) > std::fabs(max)) ? min : max;
124 for (int i = 0; i < dim; i++)
125 direction_perio[i] = normale(0, i) * facteur;
126 Cerr << "Periodicity direction for " << dom.le_nom() << "/" << bord << " " << direction_perio;
127}
128
129/*! @brief Reorders the "faces" array according to the periodic face convention: First the faces of one end, then in the same order, the twin faces.
130 *
131 * Warning, the algorithm is O(n^2) (slow), and only works in sequential mode.
132 *
133 * @param (domaine) the domain to which the faces belong
134 * @param (direction_perio) the vector from the centre of a face to the centre of the opposite face
135 * @param (faces) the faces array (for each face, indices of its vertices) to reorder. Return value: 1 if ok, 0 if no twin face was found within precision_geom.
136 */
137template<typename _SIZE_>
139 IntTab_T<_SIZE_>& faces,
140 const ArrOfDouble& direction_perio,
141 const double epsilon)
142{
143 // Modif B.M. 04/06/2010: allowing parallel operation since it is used by
144 // the MaillerParallel interpreter...
145 // PL 18/11/2010: Nevertheless moving the prohibition of using the interpreter in // parallel in the data set (see ::interpreter_)
146 using IntTab_t = IntTab_T<_SIZE_>;
147 using DoubleTab_t = DoubleTab_T<_SIZE_>;
148 using ArrOfInt_t = ArrOfInt_T<_SIZE_>;
149 using Octree_Double_t = Octree_Double_32_64<_SIZE_>;
150
151 const int_t nb_faces = faces.dimension(0);
152 const int nb_som_faces = static_cast<int>(faces.dimension(1));
153 const int dim = static_cast<int>(domaine.les_sommets().dimension(1));
154 // Compute the coordinates of the face centres:
155 DoubleTab_t centres(nb_faces, 3);
156 {
157 const DoubleTab_t& coord = domaine.les_sommets();
158 const double inv_nb_som = 1. / (double) nb_som_faces;
159 for (int_t i = 0; i < nb_faces; i++)
160 {
161 for (int j = 0; j < nb_som_faces; j++)
162 {
163 const int_t sommet = faces(i, j);
164 for (int k = 0; k < dim; k++)
165 centres(i, k) += coord(sommet, k) * inv_nb_som;
166 }
167 }
168 }
169
170 // Build an octree containing the face centres:
171 Octree_Double_t octree;
172 octree.build_nodes(centres, 0 /* do not include virtual nodes */);
173
174 // For each face, find its associated periodic face (whose centre
175 // is offset by direction_perio).
176
177 // For each face, its new index in the faces array
178 ArrOfInt_t renum_faces(nb_faces);
179 renum_faces= -1;
180 ArrOfInt_t nodes_list;
181
182 ArrOfDouble coord(dim);
183 int count = 0;
184 for (int_t i_face = 0; i_face < nb_faces; i_face++)
185 {
186 if (renum_faces[i_face] >= 0)
187 continue; // Face already processed, skip
188 // Search for the opposite face in both directions (-1. and +1.)
189 double facteur;
190 int_t i_face2 = -1;
191 for (facteur = -1.; facteur < 1.5; facteur += 2.)
192 {
193 for (int i = 0; i < dim; i++)
194 coord[i] = centres(i_face, i) + facteur * direction_perio[i];
195 octree.search_elements_box(coord, epsilon, nodes_list);
196 i_face2 = octree.search_nodes_close_to(coord, centres, nodes_list, epsilon);
197 if (i_face2 >= 0)
198 break;
199 }
200 if (i_face2 >= 0)
201 {
202 if (renum_faces[i_face2] >= 0)
203 {
204 Cerr << "====================================================" << finl;
205 Cerr << "Error in reordonner_faces_periodiques: the face " << i_face
206 << " of " << centres(i_face,0) << " " << centres(i_face,1) << " "
207 << ((dim==3)?centres(i_face,2):0.)
208 << " center already has a face twin."
209 << finl;
210 Cerr << "Possible problem: the boundary is not periodic. Check your mesh." << finl;
211 return 0;
212 }
213 int_t f0 = (facteur > 0.) ? i_face : i_face2;
214 int_t f1 = (facteur > 0.) ? i_face2: i_face;
215 renum_faces[f0] = count;
216 renum_faces[f1] = count + nb_faces / 2;
217 count++;
218 }
219 else
220 {
221 Cerr << "====================================================" << finl;
222 Cerr << "Error in reordonner_faces_periodiques: the face " << i_face << " of " << centres(i_face,0) << " " << centres(i_face,1) << " "
223 << ((dim==3)?centres(i_face,2):0.) << finl;
224 Cerr << "center has no face twin into the specified direction in the list of faces." << finl;
225 return 0;
226 }
227 }
228 // Reorder the faces:
229 const IntTab_t oldfaces(faces);
230 for (int_t i = 0; i < nb_faces; i++)
231 {
232 const int_t new_i = renum_faces[i];
233 for (int j = 0; j < nb_som_faces; j++)
234 faces(new_i, j) = oldfaces(i, j);
235 }
236 return 1;
237}
238
239/*! @brief Tries to verify whether the faces on boundary num_bord are ordered according to the periodic face convention.
240 *
241 * Stores in vecteur_delta the presumed periodicity direction (interval
242 * measured between the first face and its twin), and in erreur the max error relative to this
243 * measurement for the other faces.
244 * In parallel, the error is the max over all processors.
245 * Return value: 1 if ok, 0 if the error exceeds precision_geom.
246 *
247 */
248template <typename _SIZE_>
250 ArrOfDouble& vecteur_delta, ArrOfDouble& erreur,
251 bool verbose)
252{
253 using IntTab_t = IntTab_T<_SIZE_>;
254 using DoubleTab_t = DoubleTab_T<_SIZE_>;
255
256 const int dim = Objet_U::dimension;
257 vecteur_delta.resize_array(dim);
258 vecteur_delta = 0.;
259 erreur.resize_array(dim);
260 erreur = 0.;
261
262 if (verbose && Process::je_suis_maitre())
263 Cerr << "Check periodic faces to the boundary : " << frontiere.le_nom() << finl;
264
265 const IntTab_t& faces = frontiere.faces().les_sommets();
266 const int_t nb_faces = faces.dimension(0);
267 if (nb_faces % 2 != 0)
268 {
269 Cerr << "Error in Check_faces_periodiques to the boundary " << frontiere.le_nom()
270 << "\n The number of faces is odd : " << nb_faces << finl;
271 Cerr << "You probably forgot to define periodicity on some boundaries during partition:" << finl;
272 Cerr << "Partition domain { ... periodique 1 " << frontiere.le_nom() << " }" << finl;
273 Process::Process::exit();
274 }
275 const int_t n = nb_faces / 2;
276 const DoubleTab_t coord = frontiere.domaine().les_sommets();
277
278 int i;
279 // Compute a delta vector (not all procs necessarily have faces on this boundary)
280 vecteur_delta = -1.e37;
281 if (n > 0)
282 calculer_vecteur_2faces<_SIZE_>(coord, faces, 0, n, vecteur_delta);
283 Process::mp_max_for_each_item(vecteur_delta);
284
285 // Compute for each face the error relative to this delta vector.
286 ArrOfDouble vect(dim);
287 for (i = 0; i < n; i++)
288 {
289 calculer_vecteur_2faces<_SIZE_>(coord, faces, i, i+n, vect);
290 // Compute the difference between vect and vecteur_delta:
291 for (int compo = 0; compo < dim; compo++)
292 erreur[compo] = std::max(erreur[compo], std::fabs(vecteur_delta[compo] - vect[compo]));
293 }
294 // Compute the max over all procs:
296 double maxerr = 0.;
297 for (i = 0; i < dim; i++)
298 maxerr = std::max(maxerr, erreur[i]);
299
300 if (verbose && Process::je_suis_maitre())
301 {
302 Cerr << " Delta vector = [ ";
303 for (i = 0; i < dim; i++) Cerr << vecteur_delta[i] << " ";
304 Cerr << "] error = [ ";
305 for (i = 0; i < dim; i++) Cerr << erreur[i] << " ";
306 Cerr << "]" << finl;
307 }
308 if (!(maxerr < Objet_U::precision_geom))
309 {
311 {
312 Cerr << " This boundary is not detected as periodic (geometric error > precision_geom)" << finl;
313 message();
314 if (Process::is_parallel()) Cerr << "Or you forgot to define the periodic boundary in the Decouper keyword." << finl;
315 } // wait until output is done before continuing (otherwise risk of exit() before message is displayed)
317 return 0;
318 }
319 return 1;
320}
321
322template<typename _SIZE_>
324 ArrOfInt_T<_SIZE_>& renum_som_perio, bool calculer_espace_virtuel)
325{
326 using IntTab_t = IntTab_T<_SIZE_>;
327 using DoubleTab_t = DoubleTab_T<_SIZE_>;
328
329 const Noms& liste_bords_periodiques = domaine.bords_perio();
330 const int_t nb_som = domaine.nb_som();
331 IntTab_t renum(nb_som);
332 for (int_t i = 0; i < nb_som; i++)
333 renum[i] = renum_som_perio[i];
334
335 const DoubleTab_t& coord = domaine.coord_sommets();
336 const int dim = coord.dimension_int(1);
337
338 // Step 1: for each real vertex, find an associated vertex (if multiple periodicity directions,
339 // a vertex may be associated with several others).
340 for (auto& itr : liste_bords_periodiques)
341 {
342 const Nom& nom_bord = itr;
343 const Frontiere_32_64<_SIZE_>& front = domaine.bord(nom_bord);
344 // Periodic direction for this boundary:
345 ArrOfDouble delta;
346 ArrOfDouble erreur;
347 if (!check_faces_periodiques(front, delta, erreur, true /* verbose */))
349 // Array pointing to all vertices of all faces
350 // (cast IntTab to ArrOfInt)
351 const IntTab_t& faces_sommets = front.les_sommets_des_faces();
352 const int nb_som_face = faces_sommets.dimension_int(1);
353 const int_t nb_faces = faces_sommets.dimension(0) / 2;
354 // Loop over faces on one side of the domain (first half of faces)
355 for (int_t i_face = 0; i_face < nb_faces; i_face++)
356 {
357 for (int i_som = 0; i_som < nb_som_face; i_som++)
358 {
359 const int_t sommet = faces_sommets(i_face, i_som);
360
361 // Find the associated vertex.
362 // Since the boundaries are ordered (see check_faces_periodiques),
363 // the vertex must be one of the vertices of the opposite face.
364 // The vector from "sommet" to "sommet_oppose" must equal "delta",
365 // the periodicity direction.
366 const int_t i_face_opposee = i_face + nb_faces;
367 int_t sommet_opp = -1;
368 int i_som_opp = 0;
369 for (; i_som_opp < nb_som_face; i_som_opp++)
370 {
371 sommet_opp = faces_sommets(i_face_opposee, i_som_opp);
372 int i = 0;
373 for (; i < dim; i++)
374 {
375 double epsilon = std::fabs((coord(sommet_opp, i) - coord(sommet, i)) - delta[i]);
376 if (epsilon > Objet_U::precision_geom)
377 break;
378 }
379 // Found the opposite vertex
380 if (i == dim) break;
381 }
382 if (i_som_opp >= nb_som_face)
383 {
384 Cerr << "[PE" << Process::me() << "] Error in Reordonner_faces_periodiques::renum_som_perio\n"
385 << " An opposite node has not been found\n"
386 << " Boundary " << nom_bord << "\n Face 1: " << i_face << "\n Node: " << sommet << finl;
387 Cerr << " May be you should define the periodic boundary " << nom_bord << finl;
388 message();
390 }
391 renum[sommet_opp] = sommet;
392 }
393 }
394 }
395
396 // Second step: make all mutually linked periodic vertices point to the same vertex
397
398 for (int_t i = 0; i < nb_som; i++)
399 {
400 int_t j = renum[i];
401 // Traverse the chain of linked vertices:
402 while (j != renum[j])
403 j = renum[j];
404 renum[i] = j;
405 }
406 // Compute values for virtual vertices.
407 // The vertices opposite to virtual vertices must be known, hence fatal errors.
408 if (Process::is_parallel() && calculer_espace_virtuel)
409 ::build_trad_space(domaine, renum);
410
411 // Copy the result into the renum_som_perio array
412 assert(renum.dimension_tot(0) == domaine.nb_som_tot());
413 renum_som_perio = renum;
414}
415
416// Explicit instanciations
418#if INT_is_64_ == 2
420#endif
421
class Domaine_32_64 A Domain is a mesh composed of a set of geometric elements of the same type.
Definition Domaine.h:62
DoubleTab_t & les_sommets()
Definition Domaine.h:113
const Frontiere_t & frontiere(int i) const
Definition Domaine.h:539
const DoubleTab_t & coord_sommets() const
Definition Domaine.h:112
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
const IntTab_t & les_sommets() const
Returns the array of vertices of all faces.
Definition Faces.h:74
Class Frontiere.
Definition Frontiere.h:32
const Domaine_t & domaine() const
Returns the domain associated with the boundary (const version).
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
Definition Frontiere.h:49
IntTab_t & les_sommets_des_faces()
Returns the vertices of the boundary faces.
const Faces_t & faces() const
Definition Frontiere.h:54
: This class is an OWN_PTR but the pointed object is shared among multiple
Definition MD_Vector.h:48
class Nom: a character string for naming TRUST objects.
Definition Nom.h:31
An array of character strings (VECT(Nom)).
Definition Noms.h:26
static int dimension
Definition Objet_U.h:94
static double precision_geom
Definition Objet_U.h:81
: An octree allowing to search in space for elements or points described by real-valued coordinates.
static void mp_max_for_each_item(TRUSTArray< _TYPE_ > &x, int n=-1)
Definition Process.cpp:197
static bool is_parallel()
Definition Process.cpp:108
static void barrier()
Synchronizes all processors in the current group (waits until all processors have reached the barrier...
Definition Process.cpp:133
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 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
This interpreter reorders the faces of a periodic boundary according to the convention used in the pa...
static void chercher_direction_perio(ArrOfDouble &direction_perio, const Domaine_32_64< _SIZE_ > &dom, const Nom &bord)
static int reordonner_faces_periodiques(const Domaine_32_64< _SIZE_ > &domaine, IntTab_T< _SIZE_ > &faces, const ArrOfDouble &direction_perio, const double epsilon)
Reorders the "faces" array according to the periodic face convention: First the faces of one end,...
static int check_faces_periodiques(const Frontiere_32_64< _SIZE_ > &frontiere, ArrOfDouble &vecteur_delta, ArrOfDouble &erreur, bool verbose=false)
Tries to verify whether the faces on boundary num_bord are ordered according to the periodic face con...
static void renum_som_perio(const Domaine_32_64< _SIZE_ > &dom, ArrOfInt_T< _SIZE_ > &renum_som_perio, bool calculer_espace_virtuel)
static void construire_espace_virtuel_traduction(const MD_Vector &md_indice, const MD_Vector &md_valeur, IntTab &tableau, const int error_is_fatal=1)
Builds the items_communs + virtual space structure of an array containing indices of geometric items,...
Definition Scatter.cpp:1621
_SIZE_ size_array() const
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
int dimension_int(int d) const
Definition TRUSTTab.tpp:152
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
_SIZE_ size_reelle() const
Definition TRUSTVect.tpp:27