TrioCFD 1.9.8
TrioCFD documentation
Loading...
Searching...
No Matches
Domaine_IJK.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 <EFichier.h>
16#include <Domaine_IJK.h>
17#include <Hexaedre.h>
18#include <IJK_tools.h>
19
20#define print_vect(x) (Nom("[") + Nom(x[0]) + Nom(" ") + Nom(x[1]) + Nom(" ") + Nom(x[2]) + Nom("]"))
21
22
23// XD domaine_IJK domaine_base domaine_ijk INHERITS_BRACE domain for IJK simulation (used in TrioCFD)
24Implemente_instanciable_sans_constructeur(Domaine_IJK, "Domaine_IJK", Domaine_base);
25
26// XD attr nbelem listentierf nbelem REQ Number of elements in each direction (integers, 2 or 3 values depending on
27// XD_CONT dimension)
28// XD attr size_dom listf size_dom REQ Domain size in each direction (floats, 2 or 3 values depending on dimension)
29// XD attr perio listentierf perio REQ Is the direction periodic ? (0 or 1, 2 or 3 values depending on dimension)
30// XD attr nproc listentierf nproc REQ Number of procs in each direction (integers, 2 or 3 values depending on
31// XD_CONT dimension)
32// XD attr origin listf origin OPT Domain origin in each direction (floats, 2 or 3 values depending on dimension)
33// XD attr ijk_splitting_ft_extension entier ijk_splitting_ft_extension OPT not_set
34// XD attr file_coords troismots file_coords OPT not_set
35
37{
38 volume_elem_status_ = DEFAULT;
39 delta_xyz_.dimensionner(3);
40 node_coordinates_xyz_.dimensionner(3);
41 offsets_all_slices_.dimensionner(3);
42 sizes_all_slices_.dimensionner(3);
43 volume_elem_ = 0.;
44 mapping_ = 0;
45 for(int i = 0; i < 3; ++i)
46 {
47 nb_elem_local_[i] = 0;
48 nb_nodes_local_[i] = 0;
49 offset_[i] = 0;
50 nproc_per_direction_[i] = 0;
51 for(int j = 0; j < 3; ++j)
52 {
53 nb_faces_local_[i][j] = 0;
54 nb_edges_local_[i][j] = 0;
55 }
56 for(int j = 0; j < 2; ++j)
57 neighbour_processors_[j][i] = -1;
58 }
59}
60
62{
63 Cerr << "Domain loaded for this subclass" << finl;
64 return os;
65}
66
68{
69 static const int dim = Objet_U::dimension;
70
71 Cerr << "Reading Domaine_IJK (" << le_nom() << ")" << finl;
72
73 if(dim < 1)
74 Process::exit("Dimension must be set before declaring the domain.");
75
76 ArrOfDouble origin(dim); // Defaults to 0.
77 ArrOfInt perio_flags(dim); // Defaults to not periodic.
78 ArrOfInt ndir(dim); // Number of elements per direction.
79 ndir = -1; // Default means "unspecified".
80 ArrOfDouble size_dom(dim); // Constant element size in each direction.
81 Noms file_coords(dim); // File(s) with the coord of each node in a direction.
82 size_dom = -1.; // Default means "unspecified".
83 IntTab groups(dim);
84 groups = 1;
85
86 // Number of procs per direction
87 IntTab nprocs(dim);
88 nprocs = -1;
89
90 //Param param(que_suis_je());
91 perio_flags = 0;
92
93 Motcle motlu;
94 int rang;
95 is >> motlu;
96
97 if(motlu != "{")
98 Process::exit(" The symbol { was expected after 'read|lire'");
99
100 Motcles les_mots(50);
101 {
102 les_mots[0] = "nbelem";
103 les_mots[1] = "size_dom";
104 les_mots[2] = "file_coords";
105 les_mots[3] = "origin";
106 les_mots[4] = "perio";
107 les_mots[5] = "nproc";
108 les_mots[6] = "process_grouping";
109 les_mots[7] = "ijk_splitting_ft_extension";
110 }
111
112 is >> motlu;
113 while(motlu != "}")
114 {
115 rang = les_mots.search(motlu);
116 switch(rang)
117 {
118 case 0:
119 for(int i = 0; i < dim; ++i)
120 is >> ndir[i];
121 break;
122 case 1:
123 for(int i = 0; i < dim; ++i)
124 is >> size_dom[i];
125 break;
126 case 2:
127 for(int i = 0; i < dim; ++i)
128 is >> file_coords[i];
129 break;
130 case 3:
131 for(int i = 0; i < dim; ++i)
132 is >> origin[i];
133 break;
134 case 4:
135 for(int i = 0; i < dim; ++i)
136 is >> perio_flags[i];
137 break;
138 case 5:
139 for(int i = 0; i < dim; ++i)
140 is >> nprocs[i];
141 break;
142 case 6:
143 for(int i = 0; i < dim; ++i)
144 is >> groups[i];
145 break;
146 case 7:
147 is >> ft_extension_;
148 break;
149 default:
150 Cerr << "Keyword : " << motlu <<" not understood by Domaine_IJK::readOn. Either update if needed or change the keyword." << finl;
152 break;
153 }
154 is >> motlu;
155 }
156
157 // Initializing the sizes of eulerian elements
158 VECT(ArrOfDouble) delta_dir(3);
159 for (int i = 0; i < 3; ++i)
160 {
161 if (file_coords[i] != "??")
162 {
164 {
165 Cerr << "Reading coordinates for direction " << i << " in file " << file_coords[i] << finl;
166 EFichier EFcoord(file_coords[i]);
167 // Skipping the origin. If the origin was written in the file, it may lead to errors.
168 double previous_x = 0.;
169 int n = 0;
170 while(1)
171 {
172 double next_x;
173 EFcoord >> next_x;
174 if (EFcoord.eof())
175 break;
176 if (n == 0)
177 origin[i] = next_x;
178 else
179 delta_dir[i].append_array(next_x - previous_x);
180 n++;
181 previous_x = next_x;
182 }
183 if (delta_dir[i].size_array() < 1)
184 Cerr << "Error in Domaine_IJK::readOn: input file contains less than 2 values" << finl;
185 }
186 envoyer_broadcast(delta_dir[i], 0);
187 }
188 else if (ndir[i] != -1) // Then it's uniform
189 {
190 if (ndir[i] < 1 || size_dom[i] <= 0.)
191 {
192 Cerr << "Error in Domaine_IJK::readOn: wrong value of nbelem or uniform_domain_size for direction " << i
193 << "\n : nbelem = " << ndir[i] << " domain_size = " << size_dom[i] << finl;
195 }
196 delta_dir[i].resize_array(ndir[i]);
197 delta_dir[i] = size_dom[i] / ndir[i];
198 }
199 else // The dataset wasn't filled properly. nbelem takes 3 parameters.
200 {
201 Cerr << "Error in Domaine_IJK::readOn: you must provide (nbelem x y z)\n"
202 << " or file_coords for each direction i, j and k" << finl;
204 }
205 double x = 0;
206 for (int j = 0; j < delta_dir[i].size_array(); j++)
207 {
208 if (delta_dir[i][j] <= 0.)
209 {
210 Cerr << "Error in Domaine_IJK::readOn: size of element " << j << " in direction " << i << " is not positive" << finl;
212 }
213 x += delta_dir[i][j];
214 }
215 Cerr << "Direction " << i << " has " << delta_dir[i].size_array() << " elements. Total domain size = " << x << finl;
216 }
217
218 int tot_proc = 1;
219 for (int j = 0; j < dim; j++)
220 {
221 if(nprocs[j] < 1)
222 Process::exit("Proc number in every direction must be strictly positive! Did you forget 'nprocs'?");
223 tot_proc *= nprocs[j];
224 }
225 if (tot_proc != Process::nproc())
226 {
227 Cerr << "!! ERROR: Domaine_IJK is built with a total number of " << tot_proc << " procs, but TRUST/Trio was launched with " << Process::nproc() << " procs!!" << finl;
229 }
230
231 Cerr << "nproc in i, j, k directions = " << nprocs[0] << " " << nprocs[1] << " " << nprocs[2] << finl;
232 Cerr << "grouping processes in i, j, k directions (node topology) = "
233 << groups[0] << " " << groups[1] << " " << groups[2] << finl;
234
235 initialize_origin_deltas(origin[0], origin[1], origin[2], delta_dir[0], delta_dir[1],
236 delta_dir[2], perio_flags[0], perio_flags[1], perio_flags[2]);
237
238 initialize_splitting(*this, nprocs[0], nprocs[1], nprocs[2], groups[0], groups[1], groups[2]);
240 return is;
241}
242
243static void retirer_doublons(ArrOfDouble& tab, double epsilon)
244{
245 const int n = tab.size_array();
246 if (n == 0)
247 return;
248 double last_value = tab[0];
249 int dest = 1;
250 for (int i = 1; i < n; i++)
251 {
252 double x = tab[i];
253 if (x > last_value + epsilon)
254 {
255 tab[dest++] = x;
256 last_value = x;
257 }
258 }
259 tab.resize_array(dest);
260}
261
262static void find_unique_coord(const DoubleTab& src, int column, ArrOfDouble& result)
263{
264 const int n = src.dimension(0);
265 ArrOfDouble tmp(n);
266 int i;
267 for (i = 0; i < n; i++)
268 tmp[i] = src(i, column);
269 tmp.ordonne_array();
270 retirer_doublons(tmp, Objet_U::precision_geom);
271
272
273 if (Process::me() != 0)
274 {
275 envoyer(tmp, 0, 53);
276 }
277 else
278 {
279 result = tmp;
280 int np = Process::nproc();
281 for (i = 1; i < np; i++)
282 {
283 recevoir(tmp, i, 53 /* canal */);
284 int m1Loc = tmp.size_array();
285 int m2Loc = result.size_array();
286 result.resize_array(m2Loc+m1Loc);
287 result.inject_array(tmp, m1Loc /* nbelem */, m2Loc /* dest index */, 0 /* src index */);
288 }
289 result.ordonne_array();
290 retirer_doublons(result, Objet_U::precision_geom);
291 }
292
293 envoyer_broadcast(result, 0);
294}
295
296
297
298// Extracts the mesh origin, dimensions and cell sizes from a distributed VDF mesh.
299//
301 int direction_for_x,
302 int direction_for_y,
303 int direction_for_z,
304 bool perio_x, bool perio_y, bool perio_z)
305{
306 if (!sub_type(Hexaedre, domaine.type_elem().valeur()))
307 {
308 Cerr << "Error in Domaine_IJK::initialize_from_unstructured:\n"
309 << " the provided domaine does not have Hexaedre element type" << finl;
310 exit();
311 }
312 periodic_[0] = perio_x;
313 periodic_[1] = perio_y;
314 periodic_[2] = perio_z;
315 // Find all coordinates in the unstructured mesh
316 // swap directions
317 const DoubleTab& coord_som = domaine.les_sommets();
318 Cout << "Domaine_IJK::initialize_from_unstructured maps x->" << direction_for_x
319 << " y->" << direction_for_y << " z->" << direction_for_z << finl;
320
321 find_unique_coord(coord_som, 0 /* coordonnees y */, node_coordinates_xyz_[direction_for_x]);
322 find_unique_coord(coord_som, 1 /* coordonnees y */, node_coordinates_xyz_[direction_for_y]);
323 find_unique_coord(coord_som, 2 /* coordonnees z (swap y et z) */, node_coordinates_xyz_[direction_for_z]);
324 const double eps = Objet_U::precision_geom;
325
326 for (int dir = 0; dir < 3; dir++)
327 {
328 const ArrOfDouble& coord = node_coordinates_xyz_[dir];
329 ArrOfDouble& delta = delta_xyz_[dir];
330 const int nelem = coord.size_array() - 1;
331 Cout << " Direction " << dir << " has " << nelem << " elements. coord[0]="
332 << coord[0] << " coord[" << nelem << "]=" << coord[nelem];
333 delta.resize_array(nelem);
334 double mindelta = 1e30;
335 double maxdelta = 0.;
336 for (int i = 0; i < nelem; i++)
337 {
338 const double d = coord[i+1] - coord[i];
339 delta[i] = d;
340 mindelta = std::min(d, mindelta);
341 maxdelta = std::max(d, maxdelta);
342 }
343 if (maxdelta - mindelta < eps)
344 {
345 // The mesh is uniform: recompute a constant delta with a better accuracy:
346 double d = (coord[nelem] - coord[0]) / (double) nelem;
347 delta = d; // affect the exact same value to all cells
348 Cout << " Uniform, delta=" << d << finl;
349 uniform_[dir] = true;
350 }
351 else
352 {
353 Cout << "Domaine_IJK::initialize_from_unstructured direction " << dir
354 << " Not uniform, min delta=" << mindelta
355 << " max delta=" << maxdelta << finl;
356 uniform_[dir] = false;
357 }
358 }
359}
360
361/*! @brief Buils a splitting of the given deometry on the requested number of processors
362 * in each direction.
363 *
364 *
365 * Process_grouping allows to rearrange process ranks by packets of ni*nj*nk processes
366 * to matc the topology of the cluster/node. ex: 8 cores node/machine => use groups
367 * of size 2x2x2 to minimize extra-node messages.
368 *
369 * @param geom another domaine
370 * @param nproc_i Number of processors in i direction.
371 * @param nproc_j Number of processors in j direction.
372 * @param nproc_k Number of processors in k direction.
373 * @param process_grouping_i 1 by default. Number of processors per subdomain in i direction.
374 * @param process_grouping_j 1 by default. Number of processors per subdomain in j direction.
375 * @param process_grouping_k 1 by default. Number of processors per subdomain in k direction.
376 */
378 int nproc_i, int nproc_j, int nproc_k,
379 int process_grouping_i,
380 int process_grouping_j,
381 int process_grouping_k)
382{
383 assert(nproc_i % process_grouping_i == 0 && "While this will still work, may not bring expected results. Try having a process grouping number that divides the number of proc allocated to this direction.");
384 assert(nproc_j % process_grouping_j == 0 && "While this will still work, may not bring expected results. Try having a process grouping number that divides the number of proc allocated to this direction.");
385 assert(nproc_k % process_grouping_k == 0 && "While this will still work, may not bring expected results. Try having a process grouping number that divides the number of proc allocated to this direction.");
386
387 const int available_nproc = Process::nproc();
388 if (nproc_i * nproc_j * nproc_k > available_nproc)
389 {
390 Cerr << "Error in Domaine_IJK::initialize_splitting(nproc_i =" << nproc_i
391 << ", nproc_j =" << nproc_j
392 << ", nproc_k =" << nproc_k
393 << "): requested splitting larger than available processor number (" << available_nproc << ")" << finl;
395 }
396
397 if (nproc_i > geom.get_nb_elem_tot(0)
398 || nproc_j > geom.get_nb_elem_tot(1)
399 || nproc_k > geom.get_nb_elem_tot(2))
400 {
401 Cerr << "Error in Domaine_IJK::initialize_splitting(nproc_i = " << nproc_i
402 << ", nproc_j = " << nproc_j
403 << ", nproc_k = " << nproc_k
404 << "): requested splitting larger than total number of cells in one direction";
405 Cerr << "\n (number of cells: "
406 << geom.get_nb_elem_tot(0) << " "
407 << geom.get_nb_elem_tot(1) << " "
408 << geom.get_nb_elem_tot(2) << ")" << finl;
410 }
411
412 IntTab mapping;
413 VECT(ArrOfInt) sizes_all_slices(3);
414 // Compute processor mapping
415 mapping.resize(nproc_i, nproc_j, nproc_k);
416
417 // master processor computes splitting:
419 {
420 // Try to group subdomains by n in each direction on consecutive mpi processes:
421 FixedVector<int, 3> process_grouping;
422 process_grouping[0] = process_grouping_i;
423 process_grouping[1] = process_grouping_j;
424 process_grouping[2] = process_grouping_k;
425 FixedVector<int, 3> nprocdir;
426 nprocdir[0] = nproc_i;
427 nprocdir[1] = nproc_j;
428 nprocdir[2] = nproc_k;
429 FixedVector<int, 3> ngroups;
430 for (int i = 0; i < 3; i++)
431 {
432 while (nprocdir[i] % process_grouping[i] != 0)
433 process_grouping[i] /= 2;
434 ngroups[i] = nprocdir[i] / process_grouping[i];
435 }
436
437 // Build processor mapping
438 // Loop on groups of processes (idea is that one group maps to one computer node)
439 for (int k = 0; k < ngroups[2]; k++)
440 {
441 for (int j = 0; j < ngroups[1]; j++)
442 {
443 for (int i = 0; i < ngroups[0]; i++)
444 {
445 // Loop on processes in the group
446 for (int k2 = 0; k2 < process_grouping[2]; k2++)
447 {
448 for (int j2 = 0; j2 < process_grouping[1]; j2++)
449 {
450 for (int i2 = 0; i2 < process_grouping[0]; i2++)
451 {
452 int p = k;
453 p = (p * ngroups[1]) + j;
454 p = (p * ngroups[0]) + i;
455 p = (p * process_grouping[2]) + k2;
456 p = (p * process_grouping[1]) + j2;
457 p = (p * process_grouping[0]) + i2;
458 int iposition = i * process_grouping[0] + i2;
459 int jposition = j * process_grouping[1] + j2;
460 int kposition = k * process_grouping[2] + k2;
461 mapping(iposition, jposition, kposition) = p;
462 }
463 }
464 }
465 }
466 }
467 }
468
469 // Compute mesh slicing
470 for (int i = 0; i < 3; i++)
471 {
472 const int n = mapping.dimension(i);
473 const int ne = geom.get_nb_elem_tot(i);
474 sizes_all_slices[i].resize_array(n);
475 for (int j = 0; j < n; j++)
476 {
477 sizes_all_slices[i][j] = (ne * (j+1) / n) - (ne * j / n);
478 }
479 }
480 }
481 // broadcast to all mpi processes:
482 envoyer_broadcast(mapping, 0);
483 envoyer_broadcast(sizes_all_slices, 0);
484
485 // Initialize object with these data:
486 initialize_mapping(geom, sizes_all_slices[0], sizes_all_slices[1], sizes_all_slices[2], mapping);
487}
488
489/*! @brief Creates a splitting of the domain by specifying the slice
490 * sizes and the processor mapping.
491 *
492 *
493 * The total cell number in directions i, j, and k must match the
494 * total number of cells in the whole geometry for each direction.
495 * The number of slices in each direction must match each corresponding
496 * dimensions of the mapping array.
497 * All processors do not have to be used!
498 *
499 * @param slice_size_i Contains for each slice in the i direction, the number of cells this slice.
500 * @param slice_size_j Contains for each slice in the j direction, the number of cells this slice.
501 * @param slice_size_k Contains for each slice in the k direction, the number of cells this slice.
502 * @param processor_mapping Provides the rank of the mpi process that will own this subdomain.
503 */
504void Domaine_IJK::initialize_mapping(Domaine_IJK& geom, const ArrOfInt& slice_size_i,
505 const ArrOfInt& slice_size_j,
506 const ArrOfInt& slice_size_k,
507 const IntTab& processor_mapping)
508{
509 assert(slice_size_i.size_array() == processor_mapping.dimension(0));
510 assert(slice_size_j.size_array() == processor_mapping.dimension(1));
511 assert(slice_size_k.size_array() == processor_mapping.dimension(2));
512
513 // Copy all geometrical information:
514 *this = geom;
515
516 mapping_ = processor_mapping;
517 sizes_all_slices_[0] = slice_size_i;
518 sizes_all_slices_[1] = slice_size_j;
519 sizes_all_slices_[2] = slice_size_k;
520 for (int i = 0; i < 3; i++)
521 {
522 const int n = sizes_all_slices_[i].size_array();
523 nproc_per_direction_[i] = n;
524 offsets_all_slices_[i].resize_array(n);
525 offsets_all_slices_[i][0] = 0;
526 for (int j = 1; j < n; ++j)
527 offsets_all_slices_[i][j] = offsets_all_slices_[i][j - 1] + sizes_all_slices_[i][j - 1];
528 assert(offsets_all_slices_[i][n - 1] + sizes_all_slices_[i][n - 1] == geom.get_nb_elem_tot(i));
529 }
530 // Find my rank in the processor mapping, fill processor_position_
531 bool ok = false;
532 processor_position_[0] = processor_position_[1] = processor_position_[2] = -1; // default, if I'm not in the mapping.
533 {
534 int pos_i = -1, pos_j = -1, pos_k = -1;
535 const int myrank = Process::me();
536 for (pos_k = 0; pos_k < nproc_per_direction_[2]; pos_k++)
537 {
538 for (pos_j = 0; pos_j < nproc_per_direction_[1]; pos_j++)
539 {
540 for (pos_i = 0; pos_i < nproc_per_direction_[0]; pos_i++)
541 {
542 int numproc = mapping_(pos_i, pos_j, pos_k);
543 assert(numproc >= 0 && numproc < Process::nproc());
544 if (numproc == myrank)
545 {
546 assert(!ok); // check that processor has not yet been visited
547 ok = true;
548 processor_position_[0] = pos_i;
549 processor_position_[1] = pos_j;
550 processor_position_[2] = pos_k;
551 // don't break, finish browsing the array to search to check for inconsistencies
552 }
553 }
554 }
555 }
556 }
557 if (!ok)
558 {
559 // this processor has no part of the domain
560 for (int i = 0; i < 3; ++i)
561 {
562 nb_elem_local_[i] = 0;
563 nb_nodes_local_[i] = 0;
564 for (int j = 0; j < 3; ++j)
565 {
566 nb_faces_local_[i][j] = 0;
567 nb_edges_local_[i][j] = 0;
568 }
569 offset_[i] = 0;
570 neighbour_processors_[0][i] = -1;
571 neighbour_processors_[1][i] = -1;
572 }
573 }
574 else
575 {
576 for (int i = 0; i < 3; ++i)
577 {
578 nb_elem_local_[i] = sizes_all_slices_[i][processor_position_[i]];
579 nb_nodes_local_[i] = nb_elem_local_[i];
580 if (!geom.get_periodic_flag(i) && processor_position_[i] == nproc_per_direction_[i] -1)
581 {
582 // We have one more node on this processor only if we are the last processor in this direction
583 // and if the domain is not periodic (otherwise, last node is owned by the next processor)
584 nb_nodes_local_[i]++;
585 }
586 for (int j = 0; j < 3; ++j)
587 {
588 // Compute local number of faces on this processor in each direction.
589 // In general nb_faces will be equal to nb_elements except if at the end of
590 // the domain and in the direction of the face normal and if not periodic:
591 if (i == j)
592 nb_faces_local_[j][i] = nb_nodes_local_[i];
593 else
594 nb_faces_local_[j][i] = nb_elem_local_[i];
595 if (i == j)
596 nb_edges_local_[j][i] = nb_elem_local_[i];
597 else
598 nb_edges_local_[j][i] = nb_nodes_local_[i];
599 }
600 offset_[i] = offsets_all_slices_[i][processor_position_[i]];
601 }
602 // Compute neighbour processors, taking into account periodicity:
603 for (int i = 0; i < 3; ++i)
604 {
605 for (int previous_or_next = 0; previous_or_next < 2; previous_or_next++)
606 {
607 FixedVector<int, 3> other_pos(processor_position_); // ijk index of neighbour processor
608 if (previous_or_next == 0)
609 other_pos[i]--;
610 else
611 other_pos[i]++;
612 if (other_pos[i] < 0 || other_pos[i] >= nproc_per_direction_[i])
613 {
614 if (geom.get_periodic_flag(i))
615 {
616 // wrap to processor at other end
617 other_pos[i] = nproc_per_direction_[i] - 1 - processor_position_[i];
618 }
619 else
620 other_pos[i] = -1;
621 }
622 if (other_pos[i] >= 0)
623 {
624 neighbour_processors_[previous_or_next][i] = mapping_(other_pos[0], other_pos[1], other_pos[2]);
625 }
626 else
627 {
628 // at boundary of domain:
629 neighbour_processors_[previous_or_next][i] = -1;
630 }
631 }
632 }
633 }
634 Journal() << "Domaine_IJK::initialize_mapping:\n"
635 << " processor_position =" << print_vect(processor_position_) << "\n"
636 << " offset =" << print_vect(offset_) << "\n"
637 << " nb_elem_local =" << print_vect(nb_elem_local_) << "\n";
638 Journal() << " neighbour_procs = left:" << print_vect(neighbour_processors_[0])
639 << " right:" << print_vect(neighbour_processors_[1]) << finl;
640}
641
642/*! @brief Initializes class elements given dataset's parameters.
643 *
644 *
645 * @param x0 Origin of the whole domain on the x axis.
646 * @param y0 Origin of the whole domain on the y axis.
647 * @param z0 Origin of the whole domain on the z axis.
648 * @param delta_x Array with the sizes of the elements on the x axis.
649 * @param delta_y Array with the sizes of the elements on the y axis.
650 * @param delta_z Array with the sizes of the elements on the z axis.
651 * @param perio_x Periodic flag along x axis.
652 * @param perio_y Periodic flag along y axis.
653 * @param perio_z Periodic flag along z axis.
654 */
655void Domaine_IJK::initialize_origin_deltas(double x0, double y0, double z0,
656 const ArrOfDouble& delta_x,
657 const ArrOfDouble& delta_y,
658 const ArrOfDouble& delta_z,
659 bool perio_x, bool perio_y, bool perio_z)
660{
661 periodic_[0] = perio_x;
662 periodic_[1] = perio_y;
663 periodic_[2] = perio_z;
664
665 static const double eps = Objet_U::precision_geom;
666
667 delta_xyz_[0] = delta_x;
668 delta_xyz_[1] = delta_y;
669 delta_xyz_[2] = delta_z;
670 for (int dir = 0; dir < 3; ++dir)
671 {
672 int n = delta_xyz_[dir].size_array();
673 node_coordinates_xyz_[dir].resize_array(n+1);
674 node_coordinates_xyz_[dir][0] = (dir == 0) ? x0 : ((dir == 1) ? y0 : z0);
675 double mindelta = 1e30;
676 double maxdelta = 0.;
677 for (int i = 0; i < n; i++)
678 {
679 const double d = delta_xyz_[dir][i];
680 node_coordinates_xyz_[dir][i+1] = node_coordinates_xyz_[dir][i] + d;
681 mindelta = std::min(d, mindelta);
682 maxdelta = std::max(d, maxdelta);
683 }
684 if (maxdelta - mindelta < eps)
685 {
686 uniform_[dir] = true;
687 }
688 else
689 {
690 uniform_[dir] = false;
691 }
692 }
693}
694
695/*! @brief Builds the geometry, parallel splitting and DOF correspondance
696 * between a "father" region and a "son" region which is a subpart
697 * of the father region.
698 *
699 *
700 * Only conformal subregion is supported for now, with ELEMENT types.
701 * Missing features: be able to build a subregion which is the boundary
702 * of another, eg: father is "3D elements", son is "2D faces".
703 *
704 * @param ni Number of elements in direction(0)
705 * @param nj Number of elements in direction(1)
706 * @param nk Number of elements in direction(2)
707 * @param offset_i Offset along x axis for the "son" subregion
708 * @param offset_j Offset along x axis for the "son" subregion
709 * @param offset_k Offset along x axis for the "son" subregion
710 * @param subregion_name Name of the "son" subregion
711 * @param perio_x Whether if domain is periodic along x axis
712 * @param perio_y Whether if domain is periodic along y axis
713 * @param perio_z Whether if domain is periodic along z axis
714 */
715void Domaine_IJK::init_subregion(const Domaine_IJK& src, int ni, int nj, int nk,
716 int offset_i, int offset_j, int offset_k,
717 const Nom& subregion_name,
718 bool perio_x, bool perio_y, bool perio_z )
719{
720 /* methode difficile a ecrire pour etre generale
721 traiter proprement les differentes localisation, par exemple les faces de bord est un casse-tete
722 Pour l'instant, en dur on suppose que la sous region ne peut etre partiel que dans la direction K
723 (on garde les plans entiers en i et j)
724 et que le decoupage en processeurs ne peut etre fait qu'en I et J, et que la localisation est toujours
725 ELEM
726 */
727 assert(src.nproc_per_direction_[0] == 1 || (ni == src.get_nb_elem_tot(0) && offset_i == 0));
728 assert(src.nproc_per_direction_[1] == 1 || (nj == src.get_nb_elem_tot(1) && offset_j == 0));
729 assert(src.nproc_per_direction_[2] == 1 || (nk == src.get_nb_elem_tot(2) && offset_k == 0));
730
731 // La construction de la geometrie ne necessite pas les prerequis: c'est general
732 double x0 = src.get_node_coordinates(0)[offset_i];
733 double y0 = src.get_node_coordinates(1)[offset_j];
734 double z0 = src.get_node_coordinates(2)[offset_k];
735 ArrOfDouble dx0 = src.get_delta(0);
736 ArrOfDouble dy0 = src.get_delta(1);
737 ArrOfDouble dz0 = src.get_delta(2);
738 ArrOfDouble dx, dy, dz;
739 dx.ref_array(dx0, offset_i, ni);
740 dy.ref_array(dy0, offset_j, nj);
741 dz.ref_array(dz0, offset_k, nk);
742 initialize_origin_deltas(x0, y0, z0, dx, dy, dz, perio_x, perio_y, perio_z);
743
744 nommer(subregion_name);
745 // Pour le decoupage, on recopie:
746
747 int nproc_i = src.nproc_per_direction_[0];
748 int nproc_j = src.nproc_per_direction_[1];
749 int nproc_k = src.nproc_per_direction_[2];
750 initialize_splitting(*this, nproc_i, nproc_j, nproc_k);
751}
752
753/*! @brief Creates a splitting of the domain by specifying the mapping.
754 *
755 *
756 * The total cell number in directions i,j,k must match the total
757 * number of cells in each direction.
758 * The number of slices in direction i, j , k must match dimensions
759 * 0,1 and 2 of the processor_mapping() array.
760 *
761 * @param slice_size_i Contains, for each slice in the x direction, the number of cells in this slice.
762 * @param slice_size_j Contains, for each slice in the y direction, the number of cells in this slice.
763 * @param slice_size_k Contains, for each slice in the z direction, the number of cells in this slice.
764 * @param processor_mapping Provides the rank of the mpi process that will have this subdomain.
765 */
766void Domaine_IJK::initialize_with_mapping(const ArrOfInt& slice_size_i,
767 const ArrOfInt& slice_size_j,
768 const ArrOfInt& slice_size_k,
769 const IntTab& processor_mapping)
770{
771 assert(slice_size_i.size_array() == processor_mapping.dimension(0));
772 assert(slice_size_j.size_array() == processor_mapping.dimension(1));
773 assert(slice_size_k.size_array() == processor_mapping.dimension(2));
774 // assert_parallel(slice_size_i); // check that same value on all processors
775 // assert_parallel(slice_size_j);
776 // assert_parallel(slice_size_k);
777 // assert_parallel(processor_mapping);
778
779 mapping_ = processor_mapping;
780 sizes_all_slices_[0] = slice_size_i;
781 sizes_all_slices_[1] = slice_size_j;
782 sizes_all_slices_[2] = slice_size_k;
783 for (int i = 0; i < 3; i++)
784 {
785 const int n = sizes_all_slices_[i].size_array();
786 nproc_per_direction_[i] = n;
787 offsets_all_slices_[i].resize_array(n);
788 offsets_all_slices_[i][0] = 0;
789 for (int j = 1; j < n; j++)
790 {
791 offsets_all_slices_[i][j] = offsets_all_slices_[i][j-1] + sizes_all_slices_[i][j-1];
792 }
793 assert(offsets_all_slices_[i][n-1] + sizes_all_slices_[i][n-1] == get_nb_elem_tot(i));
794 }
795 // Find my rank in the processor mapping, fill processor_position_
796 bool ok = false;
797 processor_position_[0] = processor_position_[1] = processor_position_[2] = -1; // default, if I'm not in the mapping.
798 {
799 int pos_i = -1, pos_j = -1, pos_k = -1;
800 const int myrank = Process::me();
801 for (pos_k = 0; pos_k < nproc_per_direction_[2]; pos_k++)
802 {
803 for (pos_j = 0; pos_j < nproc_per_direction_[1]; pos_j++)
804 {
805 for (pos_i = 0; pos_i < nproc_per_direction_[0]; pos_i++)
806 {
807 int numproc = mapping_(pos_i,pos_j,pos_k);
808 assert(numproc >= 0 && numproc < Process::nproc());
809 if (numproc == myrank)
810 {
811 assert(!ok); // check that processor has not yet been visited
812 ok = true;
813 processor_position_[0] = pos_i;
814 processor_position_[1] = pos_j;
815 processor_position_[2] = pos_k;
816 // don't break, finish browsing the array to search to check for inconsistencies
817 }
818 }
819 }
820 }
821 }
822 if (!ok)
823 {
824 // this processor has no part of the domain
825 for (int i = 0; i < 3; i++)
826 {
827 nb_elem_local_[i] = 0;
828 nb_nodes_local_[i] = 0;
829 for (int j = 0; j < 3; j++)
830 nb_faces_local_[i][j] = 0;
831 offset_[i] = 0;
832 neighbour_processors_[0][i] = -1;
833 neighbour_processors_[1][i] = -1;
834 }
835 }
836 else
837 {
838 for (int i = 0; i < 3; i++)
839 {
840 nb_elem_local_[i] = sizes_all_slices_[i][processor_position_[i]];
841 nb_nodes_local_[i] = nb_elem_local_[i];
842 if (!get_periodic_flag(i) && processor_position_[i] == nproc_per_direction_[i]-1)
843 {
844 // We have one more node on this processor only if we are the last processor in this direction
845 // and if the domain is not periodic (otherwise, last node is owned by the next processor)
846 nb_nodes_local_[i]++;
847 }
848 for (int j = 0; j < 3; j++)
849 {
850 // Compute local number of faces on this processor in each direction.
851 // In general nb_faces will be equal to nb_elements except if at the end of
852 // the domain and in the direction of the face normal and if not periodic:
853 if (i == j)
854 nb_faces_local_[j][i] = nb_nodes_local_[i];
855 else
856 nb_faces_local_[j][i] = nb_elem_local_[i];
857 }
858 offset_[i] = offsets_all_slices_[i][processor_position_[i]];
859 }
860 // Compute neighbour processors, taking into account periodicity:
861 for (int i = 0; i < 3; i++)
862 {
863 for (int previous_or_next = 0; previous_or_next < 2; previous_or_next++)
864 {
865 Int3 other_pos(processor_position_); // ijk index of neighbour processor
866 if (previous_or_next == 0)
867 other_pos[i]--;
868 else
869 other_pos[i]++;
870 if (other_pos[i] < 0 || other_pos[i] >= nproc_per_direction_[i])
871 {
872 if (get_periodic_flag(i))
873 {
874 // wrap to processor at other end
875 other_pos[i] = nproc_per_direction_[i] - 1 - processor_position_[i];
876 }
877 else
878 {
879 other_pos[i] = -1;
880 }
881 }
882 if (other_pos[i] >= 0)
883 {
884 neighbour_processors_[previous_or_next][i] = mapping_(other_pos[0], other_pos[1], other_pos[2]);
885 }
886 else
887 {
888 // at boundary of domain:
889 neighbour_processors_[previous_or_next][i] = -1;
890 }
891 }
892 }
893 }
894 Journal() << "Domaine_IJK::initialize_with_mapping:\n"
895 << " processor_position=" << print_vect(processor_position_) << "\n"
896 << " offset =" << print_vect(offset_) << "\n"
897 << " nb_elem_local =" << print_vect(nb_elem_local_) << "\n";
898 Journal() << " neighbour_procs = left:" << print_vect(neighbour_processors_[0])
899 << " right:" << print_vect(neighbour_processors_[1]) << finl;
900}
901
902/*! @brief renvoie new(Faces) ! elle est surchargee par Domaine_VDF par ex.
903 */
905{
906 Faces* les_faces = new(Faces);
907 return les_faces;
908}
909
910/*! @brief Returns the number of local items (on this processor) for the given localisation in the requested direction
911 *
912 *
913 * @param loc In IJK, ELEM, NODES, FACES_I, FACES_J, FACES_K, ELEM_I, ELEM_J or ELEM_K
914 * @param direction In IJK, x(0), y(1) or z(2)
915 * @return Number of requested items
916 */
918{
919 assert(loc == ELEM || loc == NODES ||
920 loc == FACES_I || loc == FACES_J || loc == FACES_K ||
921 loc == EDGES_I || loc == EDGES_J || loc == EDGES_K);
922 assert(direction >= 0 && direction < 3);
923 switch(loc)
924 {
925 case ELEM:
926 return get_nb_elem_local(direction);
927 case NODES:
928 return get_nb_nodes_local(direction);
929 case EDGES_I:
930 return get_nb_edges_local(0, direction);
931 case EDGES_J:
932 return get_nb_edges_local(1, direction);
933 case EDGES_K:
934 return get_nb_edges_local(2, direction);
935 case FACES_I:
936 return get_nb_faces_local(0, direction);
937 case FACES_J:
938 return get_nb_faces_local(1, direction);
939 case FACES_K:
940 return get_nb_faces_local(2, direction);
941 case FACES:
942 case EDGES:
943 break;
944 }
945 return -1;
946}
947
948/*! @brief Returns the length of the entire domain in requested direction
949 * @param direction In IJK, x(0), y(1) or z(2).
950 * @return Global length of the domain
951 */
952double Domaine_IJK::get_domain_length(int direction) const
953{
954 assert(direction >= 0 && direction < 3);
955 const int n = get_nb_elem_tot(direction);
956 const double x0 = get_origin(direction);
957 const double x1 = get_node_coordinates(direction)[n];
958 return x1 - x0;
959}
960
961/*! @brief Fills the "delta" array with the size of the cells owned by the processor in the requested direction.
962 *
963 *
964 * "delta" is redimensionned with the specified number of ghost cells and filled.
965 * If domain is periodic, takes periodic mesh size in ghost cells on first and last subdomain,
966 * if domain is not periodic, copy the size of the first and last cell into ghost cells in the walls.
967 *
968 * @param direction In IJK, x(0), y(1) or z(2).
969 * @param ghost_cells Size of the ghost cells
970 * @param delta Size of cells in each direction
971 */
972void Domaine_IJK::get_local_mesh_delta(int direction, int ghost_cells,
973 ArrOfDouble_with_ghost& delta) const
974{
975 const int ntot = get_nb_elem_tot(direction);
976 const int offset = get_offset_local(direction);
977 const ArrOfDouble& global_delta = get_delta(direction);
978 const int nlocal = get_nb_elem_local(direction);
979 delta.resize(nlocal, ghost_cells);
980
981 if (ghost_cells > ntot)
982 {
983 Cerr << "Error in Domaine_IJK::get_local_mesh_delta(dir = "<<direction<<",ghost_cells = "<<ghost_cells<<")\n"
984 << "Number of ghost cells larger than number of nodes (" << ntot << ") in the domain !" << finl;
986 }
987
988 for (int ilocal = -ghost_cells; ilocal < nlocal + ghost_cells; ilocal++)
989 {
990 const int iglobal = ilocal + offset;
991 double d;
992 if (iglobal < 0)
993 {
994 if (get_periodic_flag(direction))
995 d = global_delta[iglobal + ntot];
996 else
997 d = global_delta[0];
998 }
999 else if (iglobal >= ntot)
1000 {
1001 if (get_periodic_flag(direction))
1002 d = global_delta[iglobal - ntot];
1003 else
1004 d = global_delta[ntot - 1];
1005 }
1006 else
1007 d = global_delta[iglobal];
1008
1009 delta[ilocal] = d;
1010 }
1011}
1012
1013/*! @brief Returns the number of local items (on this processor) for the given localisation in the requested direction
1014 *
1015 * If periodic along requested direction, need to add the last item for nodes and faces.
1016 *
1017 * @param loc In IJK, ELEM, NODES, FACES_I, FACES_J, FACES_K, EDGES_I, EDGES_J or EDGES_K
1018 * @param direction In IJK, x(0), y(1) or z(2)
1019 * @return Number of requested items
1020 */
1022{
1023 assert(loc == ELEM || loc == NODES ||
1024 loc == FACES_I || loc == FACES_J || loc == FACES_K ||
1025 loc == EDGES_I || loc == EDGES_J || loc == EDGES_K);
1026 assert(direction >= 0 && direction < 3);
1027 int n = get_nb_elem_tot(direction);
1028 int no_perio = 0;
1029 if (!periodic_[direction])
1030 no_perio = 1;
1031
1032 switch(loc)
1033 {
1034 case ELEM:
1035 case EDGES:
1036 case FACES:
1037 break;
1038 case NODES:
1039 n += no_perio;
1040 break;
1041 case EDGES_I:
1042 if (direction != 0)
1043 n += no_perio;
1044 break;
1045 case EDGES_J:
1046 if (direction != 1)
1047 n += no_perio;
1048 break;
1049 case EDGES_K:
1050 if (direction != 2)
1051 n += no_perio;
1052 break;
1053 case FACES_I:
1054 if (direction == 0)
1055 n += no_perio;
1056 break;
1057 case FACES_J:
1058 if (direction == 1)
1059 n += no_perio;
1060 break;
1061 case FACES_K:
1062 if (direction == 2)
1063 n += no_perio;
1064 break;
1065 }
1066 return n;
1067}
1068
1069/*! @brief Returns the number of items of given location (elements, nodes, faces...)
1070 * for all slices in the requested direction.
1071 *
1072 * @param direction In IJK, x(0), y(1) or z(2).
1073 * @param loc In IJK, ELEM, NODES, EDGES_I, EDGES_J, EDGES_K, FACES_I, FACES_J or FACES_K
1074 * @param tab Array in which we'll store the number of slices in given direction
1075 */
1076void Domaine_IJK::get_slice_size(int direction, Localisation loc, ArrOfInt& tab) const
1077{
1078 assert(loc == ELEM || loc == NODES || loc == FACES_I ||
1079 loc == FACES_J || loc == FACES_K);
1080 assert(direction >= 0 && direction < 3);
1081 tab = sizes_all_slices_[direction];
1082 const int n = tab.size_array() - 1;
1083 if (!periodic_[direction])
1084 {
1085 switch(loc)
1086 {
1087 case ELEM:
1088 break;
1089 case NODES:
1090 tab[n]++;
1091 break;
1092 case EDGES_I:
1093 if (direction != 0) tab[n]++;
1094 break;
1095 case EDGES_J:
1096 if (direction != 1) tab[n]++;
1097 break;
1098 case EDGES_K:
1099 if (direction != 2) tab[n]++;
1100 break;
1101 case FACES_I:
1102 if (direction == 0) tab[n]++;
1103 break;
1104 case FACES_J:
1105 if (direction == 1) tab[n]++;
1106 break;
1107 case FACES_K:
1108 if (direction == 2) tab[n]++;
1109 break;
1110 default:
1111 break;
1112 }
1113 }
1114}
1115
1116/*! @brief Determines the dof of an element along a localisation
1117 *
1118 * TODO: Not sure about the brief?
1119 *
1120 * @param i Local index of an element along x axis.
1121 * @param j Local index of an element along y axis.
1122 * @param k Local index of an element along z axis.
1123 * @param In IJK, ELEM, NODES, EDGES_I, EDGES_J, EDGES_K, FACES_I, FACES_J or FACES_K.
1124 *
1125 * @return A vector with the coordinates of dof
1126 */
1128{
1129 assert(loc == ELEM || loc == NODES || loc == FACES_I ||
1130 loc == FACES_J || loc == FACES_K);
1131 int gi = i + offset_[0];
1132 int gj = j + offset_[1];
1133 int gk = k + offset_[2];
1134 Vecteur3 xyz;
1135 xyz[0] = get_node_coordinates(0)[gi];
1136 xyz[1] = get_node_coordinates(1)[gj];
1137 xyz[2] = get_node_coordinates(2)[gk];
1138 if (loc == ELEM || loc == EDGES_I || loc == FACES_J || loc == FACES_K)
1139 xyz[0] += get_delta(0)[gi] * 0.5;
1140 if (loc == ELEM || loc == EDGES_J || loc == FACES_I || loc == FACES_K)
1141 xyz[1] += get_delta(1)[gj] * 0.5;
1142 if (loc == ELEM || loc == EDGES_K || loc == FACES_I || loc == FACES_J)
1143 xyz[2] += get_delta(2)[gk] * 0.5;
1144 return xyz;
1145}
1146
1147/*! @brief With three indices, find the local index of an element
1148 *
1149 * @param i Local index of an element along x axis.
1150 * @param j Local index of an element along y axis.
1151 * @param k Local index of an element along z axis.
1152 *
1153 * @return The LOCAL index of an element.
1154 */
1155int Domaine_IJK::convert_ijk_cell_to_packed(int i, int j, int k) const
1156{
1157 const int nbmailles_euler_i = get_nb_elem_local(0);
1158 const int nbmailles_euler_j = get_nb_elem_local(1);
1159
1160 if (i < 0)
1161 return -1;
1162 else
1163 return (k * nbmailles_euler_j + j) * nbmailles_euler_i + i;
1164}
1165
1166/*! @brief Convert the local index of an element to a vector with IJK indices.
1167 *
1168 * Adapted from Maillage_FT_IJK.h
1169 *
1170 * @param index Local index of the element.
1171 * @return Vector with IJK coordinates.
1172 */
1174{
1175 const int nbmailles_euler_i = get_nb_elem_local(0);
1176 const int nbmailles_euler_j = get_nb_elem_local(1);
1177
1179 if (index < 0)
1180 ijk[0] = ijk[1] = ijk[2] = -1;
1181 else
1182 {
1183 ijk[0] = index % nbmailles_euler_i;
1184 index /= nbmailles_euler_i;
1185 ijk[1] = index % nbmailles_euler_j;
1186 index /= nbmailles_euler_j;
1187 ijk[2] = index;
1188 }
1189 return ijk;
1190}
1191
1192/*! @brief Find the element which contains the item's coodirnates.
1193 *
1194 *
1195 * The element's coordinates can be outside of this processor's subdomain.
1196 *
1197 *
1198 * @param x First coordinate of an item in the mesh.
1199 * @param y Second coordinate of an item in the mesh.
1200 * @param z Third coordinate of an item in the mesh.
1201 * @param ijk_global The global coordinates of the cell.
1202 * @param ijk_local The local coordinates of the cell.
1203 * @param ijk_me A sort of 3D flag. Will be [1,1,1] if the element belongs to me.
1204 */
1205void Domaine_IJK::search_elem(const double& x, const double& y, const double& z,
1206 FixedVector<int, 3>& ijk_global,
1207 FixedVector<int, 3>& ijk_local,
1208 FixedVector<int, 3>& ijk_me) const
1209{
1210 Vecteur3 coords(x, y, z);
1211 ijk_me[0] = 0;
1212 ijk_me[1] = 0;
1213 ijk_me[2] = 0;
1214 for (int dir = 0; dir < 3; ++dir)
1215 {
1216 const double d = get_constant_delta(dir);
1217 const double o = get_origin(dir);
1218 const double val = (coords[dir] - o) / d;
1219 const int ival = (int)std::lrint(std::floor(val));
1220 ijk_global[dir] = ival;
1221 assert((ival >= 0) && (ival < get_nb_elem_tot(dir)));
1222 const int ioff = get_offset_local(dir);
1223 const int n_loc = get_nb_elem_local(dir);
1224 if ((ival >= ioff) && (ival < ioff + n_loc))
1225 // It's within my subdomain along this direction
1226 ijk_me[dir] = 1;
1227 ijk_local[dir] = ival - ioff;
1228 }
1229}
1230
1231/*! @brief Updates volume_elem_
1232 * / ! \ If the grid changes, this needs to be called again!
1233 */
1235{
1236 // volume_elem is already up to date
1237 if(volume_elem_status_ == DONE)
1238 return;
1239
1240 // This method only makes sense for 3D
1241 // resize of volume_elem_
1242 const int nx = get_nb_elem_local(0);
1243 assert(nx > 0);
1244 const int ny = get_nb_elem_local(1);
1245 assert(ny > 0);
1246 const int nz = get_nb_elem_local(2);
1247 assert(nz > 0);
1248 const int nsize = nx * ny * nz;
1249 volume_elem_.resize(nsize);
1250
1251 // Fill with the volume of each elem
1252 double size_elem = 0.;
1253 // Nb of axis with uniform size
1254 int nb_uniform_axis = 0;
1255 int not_uniform = -1;
1256 for(int dir = 0; dir < 3; ++dir)
1257 if(is_uniform(dir))
1258 nb_uniform_axis++;
1259
1260 const double size_x = is_uniform(0) ? get_constant_delta(0) : throw;
1261 const double size_y = is_uniform(1) ? get_constant_delta(1): throw;
1262 const double size_z = is_uniform(2) ? get_constant_delta(2) : -123.;
1263 const ArrOfDouble& sizes_x = get_delta(0);
1264 const ArrOfDouble& sizes_y = get_delta(1);
1265 const ArrOfDouble& sizes_z = get_delta(2);
1266 switch(nb_uniform_axis)
1267 {
1268 case 3: // Constant on all axis
1269 size_elem = size_x * size_y * size_z;
1270 volume_elem_ = size_elem;
1271 volume_elem_status_ = DONE;
1272 return;
1273 case 2: // Not constant on 1 axis
1274 for(int dir = 0; dir < 3; ++dir)
1275 if(!is_uniform(dir))
1276 not_uniform = dir;
1277 if(not_uniform == 0) // Not constant along x axis
1278 {
1279 assert(nx == sizes_x.size_array());
1280 for(int idz = 0; idz < nz; ++idz)
1281 for(int idy = 0; idy < ny; ++idy)
1282 for(int idx = 0; idx < nx; ++idx)
1283 {
1284 size_elem = sizes_x[idx] * size_y * size_z;
1285 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1286 }
1287 }
1288 else if(not_uniform == 1) // Not constant along y axis
1289 {
1290 assert(ny == sizes_y.size_array());
1291 for(int idz = 0; idz < nz; ++idz)
1292 for(int idy = 0; idy < ny; ++idy)
1293 for(int idx = 0; idx < nx; ++idx)
1294 {
1295 size_elem = size_x * sizes_y[idy] * size_z;
1296 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1297 }
1298 }
1299 else if(not_uniform == 2) // Not constant along z axis
1300 {
1301 assert(nz == sizes_z.size_array());
1302 for(int idz = 0; idz < nz; ++idz)
1303 for(int idy = 0; idy < ny; ++idy)
1304 for(int idx = 0; idx < nx; ++idx)
1305 {
1306 size_elem = size_x * size_y * sizes_z[idz];
1307 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1308 }
1309 }
1310 else
1311 {
1312 Cerr << "Error in Maillage_Ft_IJK::update_volume_elem !!! No direction selected?!" <<finl;
1313 assert(0);
1314 Process::exit();
1315 }
1316 volume_elem_status_ = DONE;
1317 return;
1318 case 1: // constant along one axis
1319 if(is_uniform(0))
1320 not_uniform = 23;
1321 else if(is_uniform(1))
1322 not_uniform = 13;
1323 else if(is_uniform(2))
1324 not_uniform = 12;
1325 if(not_uniform == 23) // constant along x axis
1326 {
1327 assert(ny == sizes_y.size_array());
1328 assert(nz == sizes_z.size_array());
1329 for(int idz = 0; idz < nz; ++idz)
1330 for(int idy = 0; idy < ny; ++idy)
1331 for(int idx = 0; idx < nx; ++idx)
1332 {
1333 size_elem = size_x * sizes_y[idy] * sizes_z[idz];
1334 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1335 }
1336 }
1337 else if(not_uniform == 13) // constant along y axis
1338 {
1339 assert(nx == sizes_x.size_array());
1340 assert(nz == sizes_z.size_array());
1341 for(int idz = 0; idz < nz; ++idz)
1342 for(int idy = 0; idy < ny; ++idy)
1343 for(int idx = 0; idx < nx; ++idx)
1344 {
1345 size_elem = sizes_x[idx] * size_y * sizes_z[idz];
1346 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1347 }
1348 }
1349 else if(not_uniform == 12) // constant along z axis
1350 {
1351 assert(ny == sizes_y.size_array());
1352 assert(nx == sizes_x.size_array());
1353 for(int idz = 0; idz < nz; ++idz)
1354 for(int idy = 0; idy < ny; ++idy)
1355 for(int idx = 0; idx < nx; ++idx)
1356 {
1357 size_elem = sizes_x[idx] * sizes_y[idy] * size_z;
1358 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1359 }
1360 }
1361 else
1362 {
1363 Cerr << "Error in Maillage_Ft_IJK::update_volume_elem !!! No direction selected?!" <<finl;
1364 assert(0);
1365 Process::exit();
1366 }
1367 volume_elem_status_ = DONE;
1368 return;
1369 case 0: // Varies on all axis
1370 // Checking if everything makes sense
1371 assert(nx == sizes_x.size_array());
1372 assert(ny == sizes_y.size_array());
1373 assert(nz == sizes_z.size_array());
1374 for(int idz = 0; idz < nz; ++idz)
1375 for(int idy = 0; idy < ny; ++idy)
1376 for(int idx = 0; idx < nx; ++idx)
1377 {
1378 size_elem = sizes_x[idx] * sizes_y[idy] * sizes_z[idz];
1379 volume_elem_[(idz * ny * nx) +(idy * nx) + idx] = size_elem;
1380 }
1381 volume_elem_status_ = DONE;
1382 return;
1383 default:
1384 Cerr << "Error Maillage_FT_IJK::update_volume_elem" << finl;
1385 assert(0);
1386 Process::exit();
1387 return;
1388 }
1389}
1390
1391void Domaine_IJK::set_extension_from_bulle_param(double vol_bulle, double diam_bulle)
1392{
1393 int ijk_splitting_ft_extension_from_diameter = 0;
1394 for (int c=0; c<3; c++)
1395 {
1396 const double delta = get_constant_delta(c);
1397 ijk_splitting_ft_extension_from_diameter = std::max(ijk_splitting_ft_extension_from_diameter, (int) ceil(diam_bulle/delta));
1398 }
1399 ft_extension_ = (ijk_splitting_ft_extension_from_diameter > ft_extension_) ? ijk_splitting_ft_extension_from_diameter : ft_extension_;
1400}
1401
1402
1403
1404int Domaine_IJK::periodic_get_processor_by_ijk(int slice_i, int slice_j, int slice_k) const
1405{
1406 int periodic_slice_i = slice_i;
1407 int periodic_slice_j = slice_j;
1408 int periodic_slice_k = slice_k;
1409
1410 // Correction du processeur a chercher dans le cas periodique,
1411 // si le slice est plus petit que zero ou plus grand que le nombre maximal.
1412 if (get_periodic_flag(0) && (slice_i < 0))
1413 periodic_slice_i += get_nprocessor_per_direction(0);
1414 else if (get_periodic_flag(0) && (slice_i >= get_nprocessor_per_direction(0)))
1415 periodic_slice_i -= get_nprocessor_per_direction(0);
1416
1417 if (get_periodic_flag(1) && (slice_j < 0))
1418 periodic_slice_j += get_nprocessor_per_direction(1);
1419 else if (get_periodic_flag(1) && (slice_j >= get_nprocessor_per_direction(1)))
1420 periodic_slice_j -= get_nprocessor_per_direction(1);
1421
1422 if (get_periodic_flag(2) && (slice_k < 0))
1423 periodic_slice_k += get_nprocessor_per_direction(2);
1424 else if (get_periodic_flag(2) && (slice_k >= get_nprocessor_per_direction(2)))
1425 periodic_slice_k -= get_nprocessor_per_direction(2);
1426
1427 // Si on est pas periodique dans une direction, on retourne -1 pour indiquer l'absence de processeur
1428 if ((!get_periodic_flag(0)) && (slice_i < 0))
1429 return -1;
1430 else if ((!get_periodic_flag(0)) && (slice_i >= get_nprocessor_per_direction(0)))
1431 return -1;
1432
1433 if ((!get_periodic_flag(1)) && (slice_j < 0))
1434 return -1;
1435 else if ((!get_periodic_flag(1)) && (slice_j >= get_nprocessor_per_direction(1)))
1436 return -1;
1437
1438 if ((!get_periodic_flag(2)) && (slice_k < 0))
1439 return -1;
1440 else if ((!get_periodic_flag(2)) && (slice_k >= get_nprocessor_per_direction(2)))
1441 return -1;
1442 else
1443 // Sinon, on retourne le numero du processeur
1444 return get_processor_by_ijk(periodic_slice_i, periodic_slice_j, periodic_slice_k);
1445}
1446
1447
1448/*! independent_index adds a ghost_size to the packed index.
1449 * It is similar to the linear_index defined in IJK_Field_local_template, but with
1450 * a universal, predefined ghost_size of 256 instead of a field-dependent ghost_size.
1451 * Since the ghost_size_ value is larger than any ghost_size expected to be used in
1452 * practice, any virtual cell can be represented by the independent index.
1453 */
1454int Domaine_IJK::get_independent_index(int i, int j, int k) const
1455{
1456 int ghost_size = 256;
1457 const int ni = get_nb_elem_local(0);
1458 const int nj = get_nb_elem_local(1);
1459
1460 int offset = ghost_size + (ni + 2*ghost_size)*ghost_size + (ni + 2*ghost_size)*(nj + 2*ghost_size)*ghost_size;
1461 int independent_index = offset + i + (ni + 2*ghost_size)*j + (ni + 2*ghost_size)*(nj + 2*ghost_size)*k;
1462
1463 return independent_index;
1464}
1465
1466Int3 Domaine_IJK::get_ijk_from_independent_index(int independent_index) const
1467{
1468 int ghost_size = 256;
1469 const int ni = get_nb_elem_local(0);
1470 const int nj = get_nb_elem_local(1);
1471
1472 // Computation of the indices disregarding the offset (i goes from 0 to ni + 2*ghost_size)
1473 int k = (independent_index)/((ni + 2*ghost_size)*(nj + 2*ghost_size));
1474 int j = ((independent_index) - (ni + 2*ghost_size)*(nj + 2*ghost_size)*k)/(ni + 2*ghost_size);
1475 int i = (independent_index) - (ni + 2*ghost_size)*(nj + 2*ghost_size)*k - (ni + 2*ghost_size)*j;
1476
1477 // Computation of the indices with the offset (i goes from -ghost_size_ to ni + ghost_size)
1478 k -= ghost_size;
1479 j -= ghost_size;
1480 i -= ghost_size;
1481
1482 Int3 ijk = {i, j, k};
1483 return ijk;
1484}
1485
1486/*! signed_independent_index: encodes in the sign the phase of the cell in a
1487 * two-phase flow: positive sign for phase 0, and negative sign for phase 1.
1488 * With a cut-cell method, this can be used to disambiguate the sub-cell.
1489 */
1490int Domaine_IJK::get_signed_independent_index(int phase, int i, int j, int k) const
1491{
1492 int signed_independent_index = (phase == 1) ? -1 - get_independent_index(i,j,k) : get_independent_index(i,j,k);
1493 return signed_independent_index;
1494}
1495
1497{
1498 int independent_index = (signed_independent_index < 0) ? -1 - signed_independent_index : signed_independent_index;
1499 return independent_index;
1500}
1501
1502int Domaine_IJK::get_phase_from_signed_independent_index(int signed_independent_index) const
1503{
1504 int phase = (signed_independent_index < 0) ? 1 : 0;
1505 return phase;
1506}
1507
1508/*! Check whether the cell (i,j,k) is contained within the specified ghost along any direction.
1509 */
1510bool Domaine_IJK::within_ghost(int i, int j, int k, int negative_ghost_size, int positive_ghost_size) const
1511{
1512 bool i_within_ghost = ((i >= -negative_ghost_size) && (i < get_nb_elem_local(0) + positive_ghost_size));
1513 bool j_within_ghost = ((j >= -negative_ghost_size) && (j < get_nb_elem_local(1) + positive_ghost_size));
1514 bool k_within_ghost = ((k >= -negative_ghost_size) && (k < get_nb_elem_local(2) + positive_ghost_size));
1515
1516 return (i_within_ghost && j_within_ghost && k_within_ghost);
1517}
1518
1519/*! Check whether the cell (i,j,k) is contained within the specified ghost along a specific direction.
1520 */
1521bool Domaine_IJK::within_ghost_along_dir(int dir, int i, int j, int k, int negative_ghost_size, int positive_ghost_size) const
1522{
1523 assert(dir >= 0 && dir < 3);
1524
1525 bool i_local = ((i >= 0) && (i < get_nb_elem_local(0)));
1526 bool j_local = ((j >= 0) && (j < get_nb_elem_local(1)));
1527 bool k_local = ((k >= 0) && (k < get_nb_elem_local(2)));
1528
1529 bool i_within_ghost = ((i >= -negative_ghost_size) && (i < get_nb_elem_local(0) + positive_ghost_size));
1530 bool j_within_ghost = ((j >= -negative_ghost_size) && (j < get_nb_elem_local(1) + positive_ghost_size));
1531 bool k_within_ghost = ((k >= -negative_ghost_size) && (k < get_nb_elem_local(2) + positive_ghost_size));
1532
1533 bool case_i_outside = (i_within_ghost && j_local && k_local);
1534 bool case_j_outside = (i_local && j_within_ghost && k_local);
1535 bool case_k_outside = (i_local && j_local && k_within_ghost);
1536
1537 return select_dir(dir, case_i_outside, case_j_outside, case_k_outside);
1538}
1539
1540int Domaine_IJK::correct_perio_i_local(int direction, int i) const
1541{
1542 int n = get_nb_elem_local(direction);
1543 int ng = get_nb_elem_tot(direction);
1544
1545 int index = -1;
1546 if (is_uniform(direction))
1547 {
1548 index = i;
1549
1550 if (get_periodic_flag(direction))
1551 {
1552 if ((index < 0) && (index + ng - n + 1 < -index))
1553 {
1554 index += ng;
1555 }
1556 else if ((index >= n) && (-(index - ng) < index - n + 1))
1557 {
1558 index -= ng;
1559 }
1560 }
1561 }
1562 else
1563 {
1564 Cerr << "Error: In correct_perio_i_local(), the case of a non-uniform mesh along direction " << direction << " is not implemented." << finl;
1565 Process::exit();
1566 }
1567
1568 return index;
1569}
1570
1571int Domaine_IJK::get_i_along_dir_no_perio(int direction, double coord_dir, Domaine_IJK::Localisation loc) const
1572{
1573 bool loc_equal_dir = (((loc == Domaine_IJK::FACES_I) && (direction == 0)) || ((loc == Domaine_IJK::FACES_J) && (direction == 1)) || ((loc == Domaine_IJK::FACES_K) && (direction == 2)));
1574
1575 const double d = get_constant_delta(direction);
1576
1577 const int offset_dir = get_offset_local(direction);
1578 double origin_dir = get_origin(direction) - .5*d*loc_equal_dir;
1579
1580 int index = -1;
1581 if (is_uniform(direction))
1582 {
1583 index = (int)(std::floor((coord_dir - origin_dir)/d)) - offset_dir;
1584 }
1585 else
1586 {
1587 Cerr << "Error: get_i_along_dir_no_perio(), the case of a non-uniform mesh along direction " << direction << " is not implemented." << finl;
1588 Process::exit();
1589 }
1590
1591 return index;
1592}
1593
1594int Domaine_IJK::get_i_along_dir_perio(int direction, double coord_dir, Domaine_IJK::Localisation loc) const
1595{
1596 int index_no_perio = get_i_along_dir_no_perio(direction, coord_dir, loc);
1597 int index_perio = correct_perio_i_local(direction, index_no_perio);
1598 return index_perio;
1599}
1600
1601Int3 Domaine_IJK::get_ijk_from_coord(double coord_x, double coord_y, double coord_z, Domaine_IJK::Localisation loc) const
1602{
1603 int i = get_i_along_dir_perio(0, coord_x, loc);
1604 int j = get_i_along_dir_perio(1, coord_y, loc);
1605 int k = get_i_along_dir_perio(2, coord_z, loc);
1606
1607 Int3 ijk = {i, j, k};
1608 return ijk;
1609}
1610
1611
This class encapsulates all the information related to the eulerian mesh for TrioIJK.
Definition Domaine_IJK.h:47
void set_extension_from_bulle_param(double vol_bulle, double diam_bulle)
int get_offset_local(int direction) const
Returns the local offset in requested direction.
int get_nb_elem_local() const
Returns the number of element owned by this processor.
int get_processor_by_ijk(const FixedVector< int, 3 > &slice) const
Return the global index of the processor according to its position.
int get_independent_index(int i, int j, int k) const
void update_volume_elem()
Updates volume_elem_ / ! \ If the grid changes, this needs to be called again!
bool within_ghost(int i, int j, int k, int negative_ghost_size, int positive_ghost_size) const
int periodic_get_processor_by_ijk(int slice_i, int slice_j, int slice_k) const
void init_subregion(const Domaine_IJK &src, int ni, int nj, int nk, int offset_i, int offset_j, int offset_k, const Nom &subregion, bool perio_x=false, bool perio_y=false, bool perio_z=false)
Builds the geometry, parallel splitting and DOF correspondance between a "father" region and a "son" ...
bool within_ghost_along_dir(int dir, int i, int j, int k, int negative_ghost_size, int positive_ghost_size) const
bool is_uniform(int direction) const
Method returns true if uniform in this direction.
int get_nb_faces_local(int compo, int direction) const
Returns the number, in requested direction, of faces that are oriented in direction of "compo".
Faces * creer_faces()
renvoie new(Faces) ! elle est surchargee par Domaine_VDF par ex.
int get_nb_elem_tot() const
Returns the total (global) number of mesh cells.
int get_nb_edges_local(int compo, int direction) const
Returns the number, in requested direction, of edges of faces in direction of "compo".
bool get_periodic_flag(int direction) const
Method returns true if periodic in this direction.
double get_domain_length(int direction) const
Returns the length of the entire domain in requested direction.
int get_nprocessor_per_direction(int direction) const
Returns the number of slices in the given direction.
int get_i_along_dir_no_perio(int direction, double coord_dir, Localisation loc) const
Int3 get_ijk_from_independent_index(int independent_index) const
void initialize_from_unstructured(const Domaine &, int direction_for_x, int direction_for_y, int direction_for_z, bool perio_x, bool perio_y, bool perio_z)
int get_nb_items_local(Localisation loc, int direction) const
Returns the number of local items (on this processor) for the given localisation in the requested dir...
void get_local_mesh_delta(int direction, int ghost_cells, ArrOfDouble_with_ghost &delta) const
Fills the "delta" array with the size of the cells owned by the processor in the requested direction.
double get_constant_delta(int direction) const
Returns the size of cells in a direction.
int get_phase_from_signed_independent_index(int signed_independent_index) const
int get_i_along_dir_perio(int direction, double coord_dir, Localisation loc) const
const ArrOfDouble & get_delta(int direction) const
Returns the array of mesh cell sizes in requested direction.
double get_origin(int direction) const
Returns the coordinate of the first node (global) of the mesh in the requested direction.
int get_signed_independent_index(int phase, int i, int j, int k) const
void initialize_splitting(Domaine_IJK &dom, int nproc_i, int nproc_j, int nproc_k, int process_grouping_i=1, int process_grouping_j=1, int process_grouping_k=1)
Buils a splitting of the given deometry on the requested number of processors in each direction.
int correct_perio_i_local(int direction, int i) const
int get_independent_index_from_signed_independent_index(int signed_independent_index) const
int get_nb_items_global(Localisation loc, int direction) const
Returns the number of local items (on this processor) for the given localisation in the requested dir...
void initialize_mapping(Domaine_IJK &dom, const ArrOfInt &slice_size_i, const ArrOfInt &slice_size_j, const ArrOfInt &slice_size_k, const IntTab &processor_mapping)
Creates a splitting of the domain by specifying the slice sizes and the processor mapping.
Vecteur3 get_coords_of_dof(int i, int j, int k, Localisation loc) const
Determines the dof of an element along a localisation.
int convert_ijk_cell_to_packed(const FixedVector< int, 3 > ijk) const
Converts the ijk index of an element to a cell index.
int get_nb_elem_tot(int direction) const
Returns the total (global) number of mesh cells in requested direction.
void get_slice_size(int direction, Localisation loc, ArrOfInt &tab) const
Returns the number of items of given location (elements, nodes, faces...) for all slices in the reque...
const ArrOfDouble & get_node_coordinates(int direction) const
Returns an array with the coordinates of all nodes in the mesh in requested direction.
int get_nb_nodes_local(int direction) const
Returns the number of nodes owned by this processor (generally equal to nb_elem_local()).
FixedVector< int, 3 > convert_packed_to_ijk_cell(int index) const
Convert the local index of an element to a vector with IJK indices.
void initialize_origin_deltas(double x0, double y0, double z0, const ArrOfDouble &delta_x, const ArrOfDouble &delta_y, const ArrOfDouble &delta_z, bool perio_x, bool perio_y, bool perio_z)
Initializes class elements given dataset's parameters.
void initialize_with_mapping(const ArrOfInt &slice_size_i, const ArrOfInt &slice_size_j, const ArrOfInt &slice_size_k, const IntTab &processor_mapping)
Creates a splitting of the domain by specifying the mapping.
Localisation
Localisation sub class.
Definition Domaine_IJK.h:53
void search_elem(const double &x, const double &y, const double &z, FixedVector< int, 3 > &ijk_global, FixedVector< int, 3 > &ijk_local, FixedVector< int, 3 > &ijk_me) const
Find the element which contains the item's coodirnates.
Int3 get_ijk_from_coord(double coord_x, double coord_y, double coord_z, Localisation loc) const
Base class for domains description. This class holds all the data shared by all domains and not sensi...
void nommer(const Nom &nom) override
Donne un nom a l'Objet_U Methode virtuelle a surcharger.
const Nom & le_nom() const override
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
void resize(int n, int new_ghost)
class Nom Une chaine de caractere pour nommer les objets de TRUST
Definition Nom.h:31
static int dimension
Definition Objet_U.h:99
virtual Entree & readOn(Entree &)
Lecture d'un Objet_U sur un flot d'entree Methode a surcharger.
Definition Objet_U.cpp:293
static double precision_geom
Definition Objet_U.h:86
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
static Sortie & Journal(int message_level=0)
Renvoie un objet statique de type Sortie qui sert de journal d'evenements.
Definition Process.cpp:588
static int nproc()
renvoie le nombre de processeurs dans le groupe courant Voir Comm_Group::nproc() et PE_Groups::curren...
Definition Process.cpp:104
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
Classe de base des flux de sortie.
Definition Sortie.h:52
_SIZE_ size_array() const
virtual void ref_array(TRUSTArray &, _SIZE_ start=0, _SIZE_ sz=-1)
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)
void ordonne_array()
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133