TrioCFD 1.9.9_beta
TrioCFD documentation
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 The syntax is { 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 The syntax is { 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 First initialization step of the partitioner: associates a domain.
74 *
75 */
76template <typename _SIZE_>
78{
79 ref_domaine_ = domaine;
80 nb_tranches_.resize(domaine.dimension);
81 nb_tranches_ = -1;
82}
83
84/*! @brief Second initialization step: defines the number of slabs.
85 *
86 * (readOn can be used instead).
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 Fills the directions_perio array from the names of the periodic boundaries.
101 *
102 * For 0 <= i < Objet_U::dimension, directions_perio[i] is 1 if
103 * there exists a periodic boundary whose delta vector is directed in 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 // For each space dimension, is this direction a periodic direction?
179 ArrOfInt directions_perio;
180 this->chercher_direction_perio(dom, directions_perio);
181
182 // Center of gravity of the elements:
183 Cerr << "Calculation of centers of gravity of the elements" << finl;
184 DoubleTab_t coord_g;
185 dom.calculer_centres_gravite(coord_g);
186 assert(coord_g.dimension(0) == nb_elem);
187 assert(coord_g.dimension(1) == dim);
188
189 Cerr << "Moving of centers of gravity" << finl;
190 // Shift coordinates so there is a strict ordering
191 // in each direction (in VDF, avoid identical coordinates)
192 {
193 for (int_t i = 0; i < nb_elem; i++)
194 {
195 double x = coord_g(i,0);
196 double y = coord_g(i,1);
197 double z = (dim == 3) ? coord_g(i,2) : 0.;
198 coord_g(i,0) = x + y*1e-6 + z*1e-12;
199 coord_g(i,1) = y + x*1e-6 + z*1e-12;
200 if (dim == 3)
201 coord_g(i,2) = z + x*1e-6 + y*1e-12;
202 }
203 }
204
205 // Number of elements in each part
206 // (initialization: one part containing nb_elem elements)
207 SmallArrOfTID_T<_SIZE_> nb_elem_part(1);
208 nb_elem_part= nb_elem;
209
210 // In ascending order of parts, list of elements
211 // contained in the part
212 // (example:
213 // from 0..nb_elem_part[0]-1 (elements of part 0)
214 // from nb_elem_part[0]..nb_elem_part[0]+nb_elem_part[1]-1 (part 1)
215 // Initialization: one large part, all elements
216 ArrOfInt_t listes_elem(nb_elem);
217 for (int_t i = 0; i < nb_elem; i++)
218 listes_elem[i] = i;
219
220 SmallArrOfTID_T<_SIZE_> new_nb_elem_part;
221
222 // Index used for sorting elements
223 ArrOfInt_t index;
224
225 // Algorithm: create nb_tranches[0] slabs in the x direction,
226 // each slab having the same number of domain elements.
227 // Then take each slab and split it into nb_tranches[1]
228 // parts in the y direction, also balanced.
229 // Finally, take each resulting part and cut it into nb_tranches[2]
230 // parts in the z direction.
231 // Note: if the domain is not a parallelepiped, the partitioning
232 // is not necessarily Cartesian:
233 // ------------------
234 // | | |
235 // |-------| |
236 // | |--------| <- slab corners do not necessarily coincide
237 // ------------------
238
239 for (int direction = 0; direction < dim; direction++)
240 {
241 // Number of slabs to create in this direction
242 const int nb_tranches = nb_tranches_[direction];
243
244 int_t index_debut_partie = 0;
245 new_nb_elem_part.resize_array(0);
246 const int nb_parts = nb_elem_part.size_array();
247
248 Cerr << "The mesh contains " << nb_parts << " parts. ";
249 Cerr << "Splitting in the direction " << direction << finl;
250
251 // Loop over already created parts. Divide each part
252 // into nb_tranches sub-parts:
253 for (int part = 0; part < nb_parts; part++)
254 {
255 Cerr << " Splitting of subpart " << part << finl;
256 // Extract the element list for this part:
257 // point the index array to a sub-portion of listes_elem
258 const int_t nb_elem_partie = nb_elem_part[part];
259 assert(index_debut_partie >= 0 && index_debut_partie + nb_elem_partie <= nb_elem);
260 index.ref_data(listes_elem.addr()+index_debut_partie, nb_elem_partie);
261 // Sort elements of this part in ascending order
262 // of the coordinate (index points to a portion of liste_elem, so
263 // actually a portion of the liste_elem array is sorted)
264 std::sort(index.begin(), index.end(), [&](int_t a, int_t b)
265 {
266 return ( coord_g(a,direction)<coord_g(b,direction) );
267 });
268
269 // If this direction is periodic, perform a circular shift by
270 // nb_elem_partie/(nb_tranches*2) elements so that elements at one end
271 // end up in the same part as those at the opposite end
272 if (directions_perio[direction])
273 {
274 ArrOfInt_t copie(nb_elem_partie);
275 copie.inject_array(index);
276 int_t j = nb_elem_partie/(nb_tranches*2);
277 for (int i = 0; i < nb_elem_partie; i++)
278 {
279 index[j++] = copie[i];
280 if (j >= nb_elem_partie)
281 j = 0;
282 }
283 }
284 // Build a partition of nb_tranches parts in
285 // ascending coordinate order
286 {
287 int_t n = 0;
288 for (int i = 0; i < nb_tranches; i++)
289 {
290 // All the cast because nb_elem_partie * (i+1) > 2^31 for big meshes
291 const long long new_n0 = (long long)nb_elem_partie * (long long)(i+1) / (long long)nb_tranches;
292 assert(new_n0 < std::numeric_limits<_SIZE_>::max());
293 const int_t new_n = (int_t)new_n0;
294 new_nb_elem_part.append_array(new_n - n);
295 n = new_n;
296 }
297 }
298 index_debut_partie += nb_elem_partie;
299 }
300
301 nb_elem_part = new_nb_elem_part;
302 }
303
304 // Initial partition of the domain: everything in partition 0 at the start
305 elem_part.resize(nb_elem);
306 elem_part = 0;
307 // Build elem_part
308 const int nb_parts = nb_elem_part.size_array();
309 int_t index_fin = 0, i = 0;
310 for (int part = 0; part < nb_parts; part++)
311 {
312 index_fin += nb_elem_part[part];
313 for (; i < index_fin; i++)
314 {
315 const int_t elem = listes_elem[i];
316 elem_part[elem] = part;
317 }
318 }
319
320 if (ref_domaine_->bords_perio().size() > 0)
321 this->corriger_bords_avec_liste(dom, 0, elem_part);
322
323 Cerr << "Correction elem0 on processor 0" << finl;
324 this->corriger_elem0_sur_proc0(elem_part);
325}
326
327
329#if INT_is_64_ == 2
331#endif
332
void calculer_centres_gravite(DoubleTab_t &xp) const
Calculates the centers of gravity of the domain elements.
Definition Domaine.h:503
int_t nb_elem() const
Definition Domaine.h:131
class Nom: a character string for naming TRUST objects.
Definition Nom.h:31
An array of character strings (VECT(Nom)).
Definition Noms.h:26
virtual void set_param(Param &) const
Definition Objet_U.h:130
static int dimension
Definition Objet_U.h:94
static double precision_geom
Definition Objet_U.h:81
virtual const Nom & le_nom() const
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
Definition Objet_U.cpp:317
virtual Sortie & printOn(Sortie &) const
Writes the object to an output stream. Virtual method to override.
Definition Objet_U.cpp:278
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
Domain partitioner that splits the domain into slabs parallel to the coordinate directions.
void validate_params() const override
The syntax is { Tranches nx ny [ nz ] }.
static void chercher_direction_perio(const Domaine_t &domaine, ArrOfInt &directions_perio)
Fills the directions_perio array from the names of the periodic boundaries.
void associer_domaine(const Domaine_t &domaine) override
First initialization step of the partitioner: associates a domain.
Domaine_32_64< _SIZE_ > Domaine_t
void construire_partition(BigIntVect_ &elem_part, int &nb_parts_tot) const override
void initialiser(const ArrOfInt &nb_tranches)
Second initialization step: defines the number of slabs.
TRUSTVect< int, _SIZE_ > BigIntVect_
Base class for domain partitioners (for splitting a mesh before a parallel computation).
static void corriger_bords_avec_liste(const Domaine_t &dom, const int_t my_offset, BigIntVect_ &elem_part)
Computes the periodic element connectivity graphs and calls corriger_periodique_avec_graphe.
static void corriger_elem0_sur_proc0(BigIntVect_ &elem_part)
Corrects the partition so that element 0 of the initial domain is on the first sub-domain of the part...
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
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...
Base class for output streams.
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