TrioCFD 1.9.8
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 // Calcul du vecteur entre le centre de la face i et le centre de la 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 /* erreurs fatales */);
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 Reordonne le tableau "faces" selon la convention des faces periodiques: D'abord les faces d'une extremite, puis dans le meme ordre, les faces jumelles.
130 *
131 * Attention, l'algorithme est en n carre (lent), et ne fonctionne qu'en sequentiel.
132 *
133 * @param (domaine) le domaine a laquelle appartiennent les faces
134 * @param (direction_perio) le vecteur qui separe le centre d'une face au centre de la face opposee
135 * @param (faces) le tableau des faces (pour chaque face, indices de ses sommets) a reordonner Valeur de retour: 1 si ok, 0 si on n'a pas trouve de face jumelle a une face a precision_geom pres.
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: j'autorise l'operation en parallele car c'est utilise par
144 // l'interprete MaillerParallel...
145 // PL 18/11/2010: Je deplace neanmoins l'interdiction de l'utilisation de l'interprete en // dans le jeu de donnees (voir ::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 // Calcul des coordonnees des centres des faces:
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 // Construction d'un octree contenant les centres des faces:
171 Octree_Double_t octree;
172 octree.build_nodes(centres, 0 /* do not include virtual nodes */);
173
174 // Pour chaque face, on cherche sa face periodique associee (dont le centre
175 // est decale de direction_perio).
176
177 // Pour chaque face, son nouvel indice dans le tableau des faces
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 deja traitee, on passe
188 // Cherche la face opposee dans les deux directions (-1. et +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 // Reordonner les 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 essaie de verifier si les faces du bord num_bord sont ordonnees suivant la convention des faces periodiques.
240 *
241 * On stocke dans vecteur_delta la direction de periodicite presumee (intervalle
242 * mesure entre la premiere face et la face jumelle), et erreur le max de l'erreur par rapport a cette
243 * mesure pour les autres faces.
244 * En parallele, l'erreur est le max sur tous les processeurs
245 * Valeur de retour: 1 si ok, 0 si l'erreur est superieure a 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 // Calculer un vecteur delta (tous les procs n'ont pas forcement des faces de ce bord)
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 // Calculer pour chaque face l'erreur par rapport a ce vecteur delta.
286 ArrOfDouble vect(dim);
287 for (i = 0; i < n; i++)
288 {
289 calculer_vecteur_2faces<_SIZE_>(coord, faces, i, i+n, vect);
290 // Calcul de la difference entre vect et 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 // Calcul du max sur tous les 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 } // attendre qu'on ait ecrit pour continuer (sinon risque de exit() avant d'avoir affiche le message)
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 // Etape 1: pour chaque sommet reel, trouver un sommet associe (si plusieurs directions
339 // de periodicite, un sommet peut etre associe a plusieurs autres).
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 // Direction periodique de ce bord:
345 ArrOfDouble delta;
346 ArrOfDouble erreur;
347 if (!check_faces_periodiques(front, delta, erreur, true /* verbose */))
349 // Tableau pointant vers tous les sommets de toutes les faces
350 // (on cast le IntTab en 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 // Boucle sur les faces d'un cote du domaine (premiere moitie des 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 // Trouver le sommet associe
362 // Comme les frontieres sont ordonnees (voir check_faces_periodiques),
363 // le sommet est forcement un des sommets de la face opposee.
364 // Le vecteur qui va de "sommet" a "sommet_oppose" doit etre egal a "delta"
365 // la direction de periodicite.
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 // On a trouve le sommet oppose
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 // Deuxieme etape: faire pointer tous les sommets periodiques lies entre eux vers le meme sommet
397
398 for (int_t i = 0; i < nb_som; i++)
399 {
400 int_t j = renum[i];
401 // Parcourir les sommet relies pour cette chaine:
402 while (j != renum[j])
403 j = renum[j];
404 renum[i] = j;
405 }
406 // Calcul des valeurs pour les sommets virtuels.
407 // Les sommets opposes aux sommets virtuels doivent etre connus, donc erreurs fatales.
408 if (Process::is_parallel() && calculer_espace_virtuel)
409 ::build_trad_space(domaine, renum);
410
411 // Recopie du resultat dans le tableau renum_som_perio
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
classe Domaine_32_64 un Domaine est un maillage compose d'un ensemble d'elements geometriques de meme...
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
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
const IntTab_t & les_sommets() const
Renvoie le tableau des sommets de toutes les faces.
Definition Faces.h:74
Classe Frontiere.
Definition Frontiere.h:32
const Domaine_t & domaine() const
Renvoie le domaine associe a la frontiere.
const Nom & le_nom() const override
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
Definition Frontiere.h:49
IntTab_t & les_sommets_des_faces()
Renvoie les sommets des faces de la frontiere.
const Faces_t & faces() const
Definition Frontiere.h:54
: Cette classe est un OWN_PTR mais l'objet pointe est partage entre plusieurs
Definition MD_Vector.h:48
class Nom Une chaine de caractere pour nommer les objets de TRUST
Definition Nom.h:31
Un tableau de chaine de caracteres (VECT(Nom)).
Definition Noms.h:26
static int dimension
Definition Objet_U.h:99
static double precision_geom
Definition Objet_U.h:86
: Un octree permettant de chercher dans l'espace des elements ou des points decrits par des coordonne...
static void mp_max_for_each_item(TRUSTArray< _TYPE_ > &x, int n=-1)
Definition Process.cpp:196
static bool is_parallel()
Definition Process.cpp:110
static void barrier()
Synchronise tous les processeurs du groupe courant (attend que tous les processeurs soient arrives a ...
Definition Process.cpp:136
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 int je_suis_maitre()
renvoie 1 si on est sur le processeur maitre du groupe courant (c'est a dire me() == 0),...
Definition Process.cpp:86
Cet interprete permet de reordonner les faces d'un bord periodique selon la convention utilisee dans ...
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)
Reordonne le tableau "faces" selon la convention des faces periodiques: D'abord les faces d'une extre...
static int check_faces_periodiques(const Frontiere_32_64< _SIZE_ > &frontiere, ArrOfDouble &vecteur_delta, ArrOfDouble &erreur, bool verbose=false)
essaie de verifier si les faces du bord num_bord sont ordonnees suivant la convention des faces perio...
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)
Construit la structure items_communs + espaces virtuels d'un tableau contenant des indices d'items ge...
Definition Scatter.cpp:1624
_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