TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Partitionneur_Tranche.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
16#include <Reordonner_faces_periodiques.h>
17#include <Partitionneur_Tranche.h>
18#include <TRUSTArray.h>
19#include <Domaine.h>
20#include <Param.h>
21
22Implemente_instanciable_32_64(Partitionneur_Tranche_32_64, "Partitionneur_Tranche", Partitionneur_base_32_64<_T_>);
23// XD partitionneur_tranche partitionneur_deriv tranche INHERITS_BRACE This algorithm will create a geometrical
24// XD_CONT partitionning by slicing the mesh in the two or three axis directions, based on the geometric center of each
25// XD_CONT mesh element. nz must be given if dimension=3. Each slice contains the same number of elements (slices don\'t
26// XD_CONT have the same geometrical width, and for VDF meshes, slice boundaries are generally not flat except if the
27// XD_CONT number of mesh elements in each direction is an exact multiple of the number of slices). First, nx slices in
28// XD_CONT the X direction are created, then each slice is split in ny slices in the Y direction, and finally, each part
29// XD_CONT is split in nz slices in the Z direction. The resulting number of parts is nx*ny*nz. If one particular
30// XD_CONT direction has been declared periodic, the default slicing (0, 1, 2, ..., n-1)is replaced by (0, 1, 2, ...
31// XD_CONT n-1, 0), each of the two \'0\' slices having twice less elements than the other slices.
32// XD attr tranches listentierf tranches OPT Partitioned by nx in the X direction, ny in the Y direction, nz in the Z
33// XD_CONT direction. Works only for structured meshes. No warranty for unstructured meshes.
34
35
36template <typename _SIZE_>
38{
39 Cerr << "Partitionneur_Tranche_32_64<_SIZE_>::printOn invalid\n" << finl;
41 return os;
42}
43
44/*! @brief La syntaxe est { Tranches nx ny [ nz ] }
45 */
46template <typename _SIZE_>
48{
49 if (!ref_domaine_)
50 {
51 Cerr << " Error: the domain has not been associated" << finl;
53 }
54 param.ajouter_arr_size_predefinie("tranches",&nb_tranches_,Param::REQUIRED);
55
56}
57
58/*! @brief La syntaxe est { Tranches nx ny [ nz ] }
59 */
60template <typename _SIZE_>
62{
63
64 // used to be done in readOn
65 if (min_array(nb_tranches_)<1)
66 {
67 Cerr << "Error for the cutting domain tool (Tranche) specifications : " <<finl;
68 Cerr<< " the number of slice must be greater than 0 for each direction. " << finl;
70 }
71}
72
73/*! @brief Premiere etape d'initialisation du partitionneur: on associe un domaine.
74 *
75 */
76template <typename _SIZE_>
78{
79 ref_domaine_ = domaine;
80 nb_tranches_.resize(domaine.dimension);
81 nb_tranches_ = -1;
82}
83
84/*! @brief Deuxieme etape d'initialisation: on definit le nombre de tranches.
85 *
86 * (on peut utiliser readOn a la place).
87 *
88 */
89template <typename _SIZE_>
90void Partitionneur_Tranche_32_64<_SIZE_>::initialiser(const ArrOfInt& nb_tranches)
91{
92
93 assert(ref_domaine_);
94 assert(nb_tranches.size_array() == nb_tranches_.size_array());
95 assert(min_array(nb_tranches) > 0);
96
97 nb_tranches_ = nb_tranches;
98}
99
100/*! @brief Remplissage du tableau directions perio a partir des noms des bords periodiques.
101 *
102 * Pour 0 <= i < Objet_U::dimension directions_perio[i] vaut 1 s'il
103 * existe un bord periodique pour lequel le vecteur delta est dirige dans la direction i.
104 *
105 */
106template <typename _SIZE_>
108 ArrOfInt& directions_perio)
109{
110 Cerr << "Search of periodic directions of domain " << domaine.le_nom() << finl;
111 const int dim = Objet_U::dimension;
112 const Noms& liste_bords_perio = domaine.bords_perio();
113
114 directions_perio.resize_array(dim);
115 directions_perio = 0;
116 for (auto& itr : liste_bords_perio)
117 {
118 int num_bord = 0;
119 const Nom& nom_bord = itr;
120 const int nb_bords = domaine.nb_bords();
121 while (num_bord < nb_bords)
122 {
123 if (domaine.bord(num_bord).le_nom() == nom_bord)
124 break;
125 num_bord++;
126 }
127 if (num_bord == nb_bords)
128 {
129 Cerr << "Error in Partitionneur_Tranche_32_64<_SIZE_>::chercher_direction_perio\n"
130 << " boundary not found : " << nom_bord << finl;
132 }
133 ArrOfDouble delta;
134 ArrOfDouble erreur;
135 const double epsilon = Objet_U::precision_geom;
136 Reordonner_faces_periodiques_32_64<_SIZE_>::check_faces_periodiques(domaine.bord(num_bord), delta, erreur);
137 int count = 0;
138 for (int j = 0; j < dim; j++)
139 {
140 if (std::abs(delta[j]) > epsilon)
141 {
142 directions_perio[j]++;
143 Cerr << " Boundary : " << nom_bord << " periodic direction : " << j << finl;
144 count++;
145 }
146 }
147 if (count != 1)
148 {
149 Cerr << "Error in Partitionneur_Tranche_32_64<_SIZE_>::chercher_direction_perio" << finl;
150 Cerr << " periodic direction not found for the boundary " << nom_bord << finl;
151 Cerr << " Vector delta found between faces twin : " << delta << finl;
152 Cerr << " with a maximum error : " << erreur << finl;
153 Cerr << "TIP: Try to add 'declare_only' flag into declare_bord_perio block or switch to metis tool" << finl;
155 }
156 }
157 if (max_array(directions_perio) > 1)
158 {
159 Cerr << "Error in Partitionneur_Tranche_32_64<_SIZE_>::chercher_direction_perio" << finl;
160 Cerr << " several boundaries have the same periodic direction" << finl;
162 }
163}
164
165template <typename _SIZE_>
167{
168 using DoubleTab_t = DoubleTab_T<_SIZE_>;
169
170 assert(ref_domaine_);
171 assert(nb_tranches_[0] > 0);
172
173 const Domaine_t& dom = ref_domaine_.valeur();
174 const int_t nb_elem = dom.nb_elem();
175
176 const int dim = Objet_U::dimension;
177
178 // Pour chaque dimension d'espace, cette direction est-elle
179 // une direction de periodicite
180 ArrOfInt directions_perio;
181 this->chercher_direction_perio(dom, directions_perio);
182
183 // Centre de gravite des elements:
184 Cerr << "Calculation of centers of gravity of the elements" << finl;
185 DoubleTab_t coord_g;
186 dom.calculer_centres_gravite(coord_g);
187 assert(coord_g.dimension(0) == nb_elem);
188 assert(coord_g.dimension(1) == dim);
189
190 Cerr << "Moving of centers of gravity" << finl;
191 // Deplacement des coordonnees pour qu'il y ait une vraie relation d'ordre
192 // dans chaque direction (en VDF, eviter les coordonnees identiques)
193 {
194 for (int_t i = 0; i < nb_elem; i++)
195 {
196 double x = coord_g(i,0);
197 double y = coord_g(i,1);
198 double z = (dim == 3) ? coord_g(i,2) : 0.;
199 coord_g(i,0) = x + y*1e-6 + z*1e-12;
200 coord_g(i,1) = y + x*1e-6 + z*1e-12;
201 if (dim == 3)
202 coord_g(i,2) = z + x*1e-6 + y*1e-12;
203 }
204 }
205
206 // Nombre d'elements dans chaque partie
207 // (initialisation: une partie contenant nb_elem elements)
208 SmallArrOfTID_T<_SIZE_> nb_elem_part(1);
209 nb_elem_part= nb_elem;
210
211 // Dans l'ordre croissant des parties, liste des elements
212 // contenus dans la partie
213 // (exemple:
214 // de 0..nb_elem_part[0]-1 (elements de la partie 0)
215 // de nb_elem_part[0]..nb_elem_part[0]+nb_elem_part[1]-1 (partie 1)
216 // Initialisation: une seule grosse partie, tous les elements
217 ArrOfInt_t listes_elem(nb_elem);
218 for (int_t i = 0; i < nb_elem; i++)
219 listes_elem[i] = i;
220
221 SmallArrOfTID_T<_SIZE_> new_nb_elem_part;
222
223 // Index utilise pour trier les elements
224 ArrOfInt_t index;
225
226 // Algorithme: on cree nb_tranches[0] tranches dans la direction x,
227 // chaque tranche compte le meme nombre d'elements du domaine.
228 // Puis on prend chacune de ces tranches et on la decoupe en nb_tranches[1]
229 // parties dans la direction y, elles aussi equilibrees.
230 // Enfin, on prend chacune des parties obtenues et on la coupe en nb_tranches[2]
231 // parties dans la direction z.
232 // Attention: si le domaine n'est pas un parallelipipede, le decoupage
233 // n'est pas forcement cartesien:
234 // ------------------
235 // | | |
236 // |-------| |
237 // | |--------| <- les coins des tranches ne coincident pas forcement
238 // ------------------
239
240 for (int direction = 0; direction < dim; direction++)
241 {
242 // Nombre de tranches a creer dans cette direction
243 const int nb_tranches = nb_tranches_[direction];
244
245 int_t index_debut_partie = 0;
246 new_nb_elem_part.resize_array(0);
247 const int nb_parts = nb_elem_part.size_array();
248
249 Cerr << "The mesh contains " << nb_parts << " parts. ";
250 Cerr << "Splitting in the direction " << direction << finl;
251
252 // Boucle sur les parties deja creees. On divise chaque partie
253 // en nb_tranches sous-parties:
254 for (int part = 0; part < nb_parts; part++)
255 {
256 Cerr << " Splitting of subpart " << part << finl;
257 // Extraction de la liste des elements de cette partie:
258 // on fait pointer le tableau index vers une sous-partie de listes_elem
259 const int_t nb_elem_partie = nb_elem_part[part];
260 assert(index_debut_partie >= 0 && index_debut_partie + nb_elem_partie <= nb_elem);
261 index.ref_data(listes_elem.addr()+index_debut_partie, nb_elem_partie);
262 // Tri des elements de cette partie dans l'ordre croissant
263 // de la coordonnee (index pointe sur une partie de liste_elem, on trie
264 // donc en realite une partie du tableau liste_elem)
265 std::sort(index.begin(), index.end(), [&](int_t a, int_t b)
266 {
267 return ( coord_g(a,direction)<coord_g(b,direction) );
268 });
269
270 // Si cette direction est periodique, permutation circulaire de
271 // nb_elem_partie/(nb_tranches*2) elements pour que les elements de l'extremite
272 // se retrouvent sur la meme partie que ceux de l'extremite opposee
273 if (directions_perio[direction])
274 {
275 ArrOfInt_t copie(nb_elem_partie);
276 copie.inject_array(index);
277 int_t j = nb_elem_partie/(nb_tranches*2);
278 for (int i = 0; i < nb_elem_partie; i++)
279 {
280 index[j++] = copie[i];
281 if (j >= nb_elem_partie)
282 j = 0;
283 }
284 }
285 // Construction d'une partition en nb_tranches parties, par
286 // ordre croissant des coordonnees
287 {
288 int_t n = 0;
289 for (int i = 0; i < nb_tranches; i++)
290 {
291 // All the cast because nb_elem_partie * (i+1) > 2^31 for big meshes
292 const long long new_n0 = (long long)nb_elem_partie * (long long)(i+1) / (long long)nb_tranches;
293 assert(new_n0 < std::numeric_limits<_SIZE_>::max());
294 const int_t new_n = (int_t)new_n0;
295 new_nb_elem_part.append_array(new_n - n);
296 n = new_n;
297 }
298 }
299 index_debut_partie += nb_elem_partie;
300 }
301
302 nb_elem_part = new_nb_elem_part;
303 }
304
305 // Partition initiale du domaine: au depart tout dans 0
306 elem_part.resize(nb_elem);
307 elem_part = 0;
308 // Construction de elem_part
309 const int nb_parts = nb_elem_part.size_array();
310 int_t index_fin = 0, i = 0;
311 for (int part = 0; part < nb_parts; part++)
312 {
313 index_fin += nb_elem_part[part];
314 for (; i < index_fin; i++)
315 {
316 const int_t elem = listes_elem[i];
317 elem_part[elem] = part;
318 }
319 }
320
321 if (ref_domaine_->bords_perio().size() > 0)
322 this->corriger_bords_avec_liste(dom, 0, elem_part);
323
324 Cerr << "Correction elem0 on processor 0" << finl;
325 this->corriger_elem0_sur_proc0(elem_part);
326}
327
328
330#if INT_is_64_ == 2
332#endif
333
void calculer_centres_gravite(DoubleTab_t &xp) const
Calcule les centres de gravites des elements du domaine.
Definition Domaine.h:503
int_t nb_elem() const
Definition Domaine.h:131
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
virtual void set_param(Param &) const
Definition Objet_U.h:135
static int dimension
Definition Objet_U.h:99
static double precision_geom
Definition Objet_U.h:86
virtual const Nom & le_nom() const
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
Definition Objet_U.cpp:319
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
Helper class to factorize the readOn method of Objet_U classes.
Definition Param.h:112
void ajouter_arr_size_predefinie(const char *keyword, const ArrOfInt *value, Param::Nature nat=Param::OPTIONAL)
Register an ArrOfInt whose size has already been fixed.
Definition Param.cpp:411
@ REQUIRED
Definition Param.h:115
Partitionneur de domaine en tranches paralleles aux directions de l'espace.
void validate_params() const override
La syntaxe est { Tranches nx ny [ nz ] }.
static void chercher_direction_perio(const Domaine_t &domaine, ArrOfInt &directions_perio)
Remplissage du tableau directions perio a partir des noms des bords periodiques.
void associer_domaine(const Domaine_t &domaine) override
Premiere etape d'initialisation du partitionneur: on associe un domaine.
Domaine_32_64< _SIZE_ > Domaine_t
void construire_partition(BigIntVect_ &elem_part, int &nb_parts_tot) const override
void initialiser(const ArrOfInt &nb_tranches)
Deuxieme etape d'initialisation: on definit le nombre de tranches.
TRUSTVect< int, _SIZE_ > BigIntVect_
Classe de base des partitionneurs de domaine (pour decouper un maillage avant un calcul parallele).
static void corriger_bords_avec_liste(const Domaine_t &dom, const int_t my_offset, BigIntVect_ &elem_part)
Calcul des graphes de connectivite elements periodiques et appel a corriger_periodique_avec_graphe.
static void corriger_elem0_sur_proc0(BigIntVect_ &elem_part)
corrige la partition pour que l'element 0 du domaine initial se trouve sur le premier sous-domaine de...
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
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...
Classe de base des flux de sortie.
Definition Sortie.h:52
void append_array(_TYPE_ valeur)
Iterator_ end()
Definition TRUSTArray.h:112
_SIZE_ size_array() const
_TYPE_ * addr()
TRUSTArray & inject_array(const TRUSTArray &source, _SIZE_ nb_elements=-1, _SIZE_ first_element_dest=0, _SIZE_ first_element_source=0)
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Iterator_ begin()
Definition TRUSTArray.h:111
virtual void ref_data(_TYPE_ *ptr, _SIZE_ size)
void resize(_SIZE_, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTVect.tpp:91