TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Assembleur_P_VDF.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 <Assembleur_P_VDF.h>
17#include <Domaine_Cl_VDF.h>
18#include <Domaine_VDF.h>
19#include <Periodique.h>
20#include <Symetrie.h>
21#include <Neumann_sortie_libre.h>
22#include <Dirichlet_entree_fluide_leaves.h>
23#include <Dirichlet_paroi_fixe.h>
24#include <Dirichlet_paroi_defilante.h>
25#include <Matrice_Bloc.h>
26#include <Option_VDF.h>
27#include <Champ_Fonc_Face_VDF.h>
28#include <Matrice_Morse_Sym.h>
29#include <Milieu_base.h>
30#include <Matrix_tools.h>
31#include <Pb_Multiphase.h>
32
33Implemente_instanciable_sans_constructeur(Assembleur_P_VDF,"Assembleur_P_VDF",Assembleur_base);
34
35Assembleur_P_VDF::Assembleur_P_VDF() : has_P_ref(0) { }
36
38{
39 return s << que_suis_je() << " " << le_nom() ;
40}
41
43{
45}
46
47/*! @brief Fills the array faces with the list of indices of the periodic faces in the face_voisins array.
48 *
49 * @brief Each periodic face appears twice in face_voisins (each face corresponds to the opposite face).
50 * Only the one of the two with the smaller index in the list of faces of each periodic boundary
51 * is stored in the array faces.
52 * Return value:
53 * number of periodic faces (equal to the size of the faces array).
54 *
55 */
57{
58 // First, largely overestimate the array size:
59 // number of boundary faces
60 const int nb_faces_bord = le_dom_VDF->nb_faces_bord();
61 faces.resize_array(nb_faces_bord);
62
63 // Search for periodic faces in the boundary conditions:
64 const Conds_lim& les_cl = le_dom_Cl_VDF->les_conditions_limites();
65 const int nb_cl = les_cl.size();
66 int nb_faces_periodiques = 0;
67 for (int num_cl = 0; num_cl < nb_cl; num_cl++)
68 {
69 const Cond_lim_base& la_cl = les_cl[num_cl].valeur();
70 // Select only the Periodique conditions
71 if ( ! sub_type(Periodique,la_cl))
72 continue;
73 const Periodique& la_cl_perio = ref_cast(Periodique, la_cl);
74 const Front_VF& frontiere = ref_cast(Front_VF, la_cl.frontiere_dis());
75 const int nb_faces_cl = frontiere.nb_faces();
76 const int num_premiere_face = frontiere.num_premiere_face();
77 for (int i = 0; i < nb_faces_cl; i++)
78 {
79 // Index of the opposite face in the boundary face array:
80 const int face_associee = la_cl_perio.face_associee(i);
81 if (face_associee > i)
82 {
83 const int num_face_global = num_premiere_face + i;
84 faces[nb_faces_periodiques] = num_face_global;
85 nb_faces_periodiques++;
86 }
87 }
88 }
89
90 // Final size of the faces array
91 faces.resize_array(nb_faces_periodiques);
92 return nb_faces_periodiques;
93}
94
95/*! @brief Determines the nonzero entries of the matrix and prepares the storage.
96 *
97 * @brief Sparse matrix of size nb_elements (rows) * nb_elem_tot (columns)
98 * Stored as a block matrix composed of two Morse matrices:
99 * * Square symmetric matrix nb_elements * nb_elements
100 * (contains the terms M(i,j) where i and j are indices of real elements)
101 * * Rectangular matrix nb_elements * (nb_elem_tot - nb_elem)
102 * (contains the terms M(i,j) where i is real and j is virtual)
103 *
104 */
106{
107 int i;
108 const Domaine_VDF& domaine_vdf = le_dom_VDF.valeur();
109 const IntTab& face_voisins = domaine_vdf.face_voisins();
110
111 // Count the total number of non-zero entries:
112 // square matrix: number of internal faces / 2 + nb_elem + nb periodic faces
113 // (each internal face gives one coefficient, there is one diagonal
114 // element and each periodic face also gives one coefficient)
115 // rectangular matrix: number of joint faces
116
117
118 // First step: count the number of non-zero entries per row
119 // For each row of the square matrix, number of non-zero entries
120 const int nb_elem = domaine_vdf.nb_elem();
121 const int nb_elem_tot = domaine_vdf.nb_elem_tot();
122 ArrOfInt carre_nb_non_zero(nb_elem);
123 // Same for the rectangular matrix
124 ArrOfInt rect_nb_non_zero(nb_elem);
125 // There is one element on the diagonal:
126 carre_nb_non_zero = 1;
127 rect_nb_non_zero = 0;
128 int carre_nb_non_zero_tot = nb_elem;
129 int rect_nb_non_zero_tot = 0;
130
131 // Plus one non-zero entry for each internal and periodic face
132 // (symmetric matrix, only the entry m(line,col) with col>line is stored)
133
134 ArrOfInt liste_faces_perio;
135 const int nb_faces_periodiques = liste_faces_periodiques(liste_faces_perio);
136 const int nb_faces_internes = domaine_vdf.nb_faces_internes();
137 const int premiere_face_interne = domaine_vdf.premiere_face_int();
138 for (i = 0; i < nb_faces_internes + nb_faces_periodiques; i++)
139 {
140 int face;
141 if (i < nb_faces_internes) // Trick to loop over internal and periodic faces
142 face = premiere_face_interne + i;
143 else
144 face = liste_faces_perio[i - nb_faces_internes];
145
146 int elem0 = face_voisins(face,0);
147 int elem1 = face_voisins(face,1);
148 if (elem0 > elem1)
149 {
150 int tmp = elem1;
151 elem1 = elem0;
152 elem0 = tmp;
153 }
154 if (elem0 < nb_elem) // elem0 is real
155 {
156 if (elem1 < nb_elem) // elem1 real
157 {
158 carre_nb_non_zero[elem0] ++;
159 carre_nb_non_zero_tot ++;
160 }
161 else // elem1 virtual
162 {
163 rect_nb_non_zero[elem0] ++;
164 rect_nb_non_zero_tot ++;
165 }
166 }
167 }
168
169 // Type and size the pressure matrix
170 la_matrice.typer("Matrice_Bloc");
171 Matrice_Bloc& matrice =ref_cast(Matrice_Bloc , la_matrice.valeur());
172 matrice.dimensionner(1,2);
173 matrice.get_bloc(0,0).typer("Matrice_Morse_Sym");
174 matrice.get_bloc(0,1).typer("Matrice_Morse");
175 Matrice_Morse_Sym& carre = ref_cast(Matrice_Morse_Sym ,matrice.get_bloc(0,0).valeur());
176 Matrice_Morse& rect = ref_cast(Matrice_Morse , matrice.get_bloc(0,1).valeur());
177
178 carre.dimensionner(nb_elem, carre_nb_non_zero_tot);
179 rect.dimensionner(nb_elem, nb_elem_tot - nb_elem, rect_nb_non_zero_tot);
180
181 {
182 const int nb_faces_bord = domaine_vdf.nb_faces_bord();
183 les_coeff_pression.resize_array(nb_faces_bord);
184 }
185 auto& carre_tab1 = carre.get_set_tab1();
186 auto& rect_tab1 = rect.get_set_tab1();
187
188 // Sparse matrix, Morse storage with Fortran indices:
189 // rows numbered 1..n, columns 1..m
190 // The k-th nonzero coefficient on row i (1<=i<=n) is (with 1<=k)
191 // M(i,j) = coeff_[tab1_[k]] in Fortran
192 // M(i,j) = coeff_[tab1_[k-1]-1] in C
193 // The column index j of this coefficient (1<=j<=m) is
194 // j = tab2_[tab1_[k]] in Fortran
195 // j = tab2_[tab1_[k-1]-1] in C
196 //
197 // Compute the index of the first coefficient on row i
198 // in the Morse index array of the two matrices (tab1_)
199 {
200 int indice = 1; // tab1_ contains a Fortran index (first element at 1)
201 for (i = 0; i < nb_elem; i++)
202 {
203 carre_tab1[i] = indice;
204 indice += carre_nb_non_zero[i];
205 }
206 carre_tab1[i] = indice;
207
208 indice = 1;
209 for (i = 0; i < nb_elem; i++)
210 {
211 rect_tab1[i] = indice;
212 indice += rect_nb_non_zero[i];
213 }
214 rect_tab1[i] = indice;
215 }
216
217 // Second step: fill tab2_ = column index of each non-zero term of the matrix
218 auto& carre_tab2 = carre.get_set_tab2();
219 auto& rect_tab2 = rect.get_set_tab2();
220
221 carre_tab2 = -1;
222 rect_tab2 = -1;
223
224 // Diagonal term:
225 for (i = 1; i <= nb_elem; i++)
226 carre_tab2[carre_tab1[i-1]-1] = i; // Fortran index 1<=i<=nb_elem
227
228 carre_nb_non_zero = 1; // Number of nonzero coefficients on each row
229 rect_nb_non_zero = 0;
230
231 // Off-diagonal terms:
232 for (int i_face = 0; i_face < nb_faces_internes + nb_faces_periodiques; i_face++)
233 {
234
235 // Compute the index of the face to process
236 const int face = (i_face < nb_faces_internes)
237 ? premiere_face_interne + i_face
238 : liste_faces_perio[i_face - nb_faces_internes];
239
240 int elem0 = face_voisins(face,0);
241 int elem1 = face_voisins(face,1);
242 if (elem0 > elem1)
243 {
244 int tmp = elem1;
245 elem1 = elem0;
246 elem0 = tmp;
247 }
248 assert(elem0 >= 0); // Verify that we have two neighboring elements
249 if (elem0 < nb_elem) // elem0 is real
250 {
251 const int ligne = elem0 + 1; // Fortran index
252 if (elem1 < nb_elem) // elem1 is real too
253 {
254 const int colonne = elem1 + 1; // Fortran index
255 const int n = carre_nb_non_zero[ligne-1]++;
256 const auto index = carre_tab1[ligne-1] + n; // Fortran index in tab2
257 carre_tab2[index - 1] = colonne;
258 }
259 else // elem1 is virtual
260 {
261 const int colonne = elem1 - nb_elem + 1; // Fortran index
262 const int n = rect_nb_non_zero[ligne-1]++;
263 const auto index = rect_tab1[ligne-1] + n; // Fortran index in tab2
264 rect_tab2[index - 1] = colonne;
265 }
266 }
267 }
268
269 return 1;
270}
271
272/*! @brief Computes the coefficients of the pressure matrix with a rho field.
273 *
274 * @brief If rho_ptr == 0, compute the matrix -div( porosity * grad P ),
275 * otherwise compute -div( porosity/rho grad P ) and *rho_ptr must be a Champ_Fonc_Face_VDF.
276 *
277 */
278
279int Assembleur_P_VDF::remplir(Matrice& la_matrice, const DoubleVect& volumes_entrelaces,const Champ_Don_base * rho_ptr)
280{
281 const Domaine_VDF& domaine_vdf = le_dom_VDF.valeur();
282 const IntTab& face_voisins = domaine_vdf.face_voisins();
283 const DoubleVect& face_surfaces = domaine_vdf.face_surfaces();
284 //const DoubleVect & volumes_entrelaces = domaine_vdf.volumes_entrelaces();
285 const DoubleVect& porosite_face = le_dom_Cl_VDF->equation().milieu().porosite_face();
286
287
288 const DoubleVect * valeurs_rho = 0;
289 if (rho_ptr)
290 {
291 assert(sub_type(Champ_Fonc_Face_VDF, *rho_ptr));
292 valeurs_rho = & (rho_ptr->valeurs());
293 }
294
295 // Shortcuts to the square part (real/real element coefficients)
296 // and the rectangular part (real/virtual elements) of the matrix
297 Matrice_Bloc& matrice = ref_cast(Matrice_Bloc, la_matrice.valeur());
298 Matrice_Morse_Sym& carre = ref_cast(Matrice_Morse_Sym, matrice.get_bloc(0,0).valeur());
299 Matrice_Morse& rect = ref_cast(Matrice_Morse, matrice.get_bloc(0,1).valeur());
300
301 const int nb_elem = domaine_vdf.nb_elem();
302 ArrOfInt carre_nb_non_zero(nb_elem);
303 ArrOfInt rect_nb_non_zero(nb_elem);
304 carre_nb_non_zero = 1;
305 rect_nb_non_zero = 0;
306
307 auto& carre_tab1 = carre.get_set_tab1();
308 auto& rect_tab1 = rect.get_set_tab1();
309 auto& carre_coeff = carre.get_set_coeff();
310 auto& rect_coeff = rect.get_set_coeff();
311
312 carre_coeff = 0.;
313 rect_coeff = 0.;
314
315 // Processing internal and periodic faces:
316 // For each face between two elements elem0 and elem1, there are four terms to add:
317 // M(elem0,elem0)
318 // M(elem0,elem1)
319 // M(elem1,elem1)
320 // M(elem1,elem0) (omitted because the matrix is stored as symmetric)
321
322 // Build the list of periodic faces
323 ArrOfInt liste_faces_perio;
324 const int nb_faces_periodiques = liste_faces_periodiques(liste_faces_perio);
325 const int nb_faces_internes = domaine_vdf.nb_faces_internes();
326 const int premiere_face_interne = domaine_vdf.premiere_face_int();
327 for (int i_face = 0; i_face < nb_faces_internes + nb_faces_periodiques; i_face++)
328 {
329
330 // Compute the index of the face to process
331 const int num_face = (i_face < nb_faces_internes)
332 ? premiere_face_interne + i_face
333 : liste_faces_perio[i_face - nb_faces_internes];
334 // Compute rho on this face
335 const double rho_face = (valeurs_rho) ? (*valeurs_rho)[num_face] : 1.;
336 // Compute the coefficient
337 const double surface = face_surfaces[num_face];
338 const double volume = volumes_entrelaces[num_face];
339 const double porosite = porosite_face[num_face];
340 const double coefficient = surface * surface * porosite / (volume * rho_face);
341 // Indices of the two neighboring elements (the smaller one in elem0)
342 int elem0 = face_voisins(num_face,0);
343 int elem1 = face_voisins(num_face,1);
344 if (elem0 > elem1)
345 {
346 int tmp = elem1;
347 elem1 = elem0;
348 elem0 = tmp;
349 }
350 if (elem0 < nb_elem)
351 {
352 // elem0 is real
353 const int ligne = elem0 + 1; // Fortran index
354 // Fortran index of the diagonal element (elem0, elem0)
355 const auto index_diag = carre_tab1[ligne-1];
356 carre_coeff[index_diag - 1] += coefficient;
357 if (elem1 < nb_elem)
358 {
359 // elem1 is real too
360 // Fortran index of the diagonal element (elem1, elem1)
361 const auto index_diag1 = carre_tab1[elem1]; // at row elem1+1
362 // Fortran index of the off-diagonal element (elem0, elem1)
363 const int n = carre_nb_non_zero[ligne-1]++;
364 const auto index = index_diag + n;
365 // Diagonal coefficient
366 carre_coeff[index_diag1 - 1] += coefficient;
367 // Off-diagonal coefficient
368 carre_coeff[index - 1] = - coefficient;
369 assert(carre.get_tab2()(index - 1) == elem1 + 1);
370 }
371 else
372 {
373 // elem1 is virtual
374 const int n = rect_nb_non_zero[ligne-1]++;
375 const auto index = rect_tab1[ligne-1] + n; // Fortran index in tab2
376 // Off-diagonal coefficient
377 rect_coeff[index - 1] = - coefficient;
378 assert(rect.get_tab2()(index - 1) == elem1 - nb_elem + 1);
379 }
380 }
381 }
382
383 // Processing the boundary conditions
384 const Conds_lim& les_cl = le_dom_Cl_VDF->les_conditions_limites();
385 const int nb_cl = les_cl.size();
386 for (int num_cl = 0; num_cl < nb_cl; num_cl++)
387 {
388 const Cond_lim_base& la_cl = les_cl[num_cl].valeur();
389 const Front_VF& la_front_dis = ref_cast(Front_VF,la_cl.frontiere_dis());
390
391 // Test on boundary conditions in 2D RZ (symmetry about the axis of revolution is required)
392 if (bidim_axi && !sub_type(Symetrie,la_cl))
393 {
394 const int ndeb = la_front_dis.num_premiere_face();
395 const int nfin = ndeb + la_front_dis.nb_faces();
396 if (nfin>ndeb && est_egal(face_surfaces[ndeb],0))
397 {
398 Cerr << "\nFirst face surface is smaller than PrecisionGeom = " << precision_geom << finl;
399 Cerr << "May be you have an error in the definition of the boundary conditions." << finl;
400 Cerr << "The axis of revolution for this 2D calculation is along Y." << finl;
401 Cerr << "So you must specify symmetry boundary condition (symetrie keyword) for the boundary " << la_front_dis.le_nom() << finl;
402 exit();
403 }
404 }
405
406 // For each boundary face between elem0 and a fictitious exterior element
407 // with imposed pressure P0, we have:
408 // grad P = (P(elem0) - P0) * surface / volume_entrelace
409 // elem0 is an unknown; P0 is added to the right-hand side in "modifier_secmem".
410 if (sub_type(Neumann_sortie_libre,la_cl))
411 {
412 has_P_ref = 1;
413 carre.set_est_definie(1);
414 const int ndeb = la_front_dis.num_premiere_face();
415 const int nfin = ndeb + la_front_dis.nb_faces();
416 for (int num_face = ndeb; num_face < nfin; num_face++)
417 {
418 // Compute rho on this face
419 const double rho_face = (valeurs_rho) ? (*valeurs_rho)[num_face] : 1.;
420 // Compute the coefficient to add to the matrix
421 const double surface = face_surfaces[num_face];
422 // Note: the staggered volume has a special value at the boundary
423 // (see Domaine_VDF::calculer_volumes_entrelaces())
424 const double volume = volumes_entrelaces[num_face];
425 const double porosite = porosite_face[num_face];
426 const double coefficient = Option_VDF::coeff_P_neumann * surface * surface * porosite / (volume * rho_face);
427 assert(coefficient > 0.);
428 // Index of the neighboring element (one is -1, the other is a real element)
429 const int elem0 = face_voisins(num_face, 0);
430 const int elem1 = face_voisins(num_face, 1);
431 assert(elem0 == -1 || elem1 == -1);
432 const int elem = elem0 + elem1 + 1;
433 // Add the coefficient to the matrix
434 assert(elem < nb_elem);
435 const auto index = carre_tab1[elem]; // Fortran index
436 carre_coeff[index - 1] += coefficient;
437 les_coeff_pression[num_face] = coefficient;
438 }
439 }
440 else
441 {
442 // For other boundary conditions, no additional term in the matrix
443 // (grad P dot n = 0 on the boundary,
444 // or time derivative of grad P dot n = 0 on the boundary)
445 }
446 }
447 has_P_ref = (int)mp_max(has_P_ref);
448
449 // Sanity check: no zero element on the diagonal
450 for (int i = 0; i < nb_elem; i++)
451 {
452 const auto index = carre_tab1[i];
453 const double coeff_diagonal = carre_coeff[index - 1];
454 if (coeff_diagonal == 0.)
455 {
456 // Cell i has no neighbor: pressure is arbitrary
457 carre_coeff[index - 1] = 1.;
458 }
459 }
460
461 carre.compacte();
462 rect.compacte();
463 return 1;
464}
465
466/*! @brief Modifies the right-hand side to apply boundary conditions.
467 *
468 * @brief The supported conditions are:
469 * Neumann_sortie_libre,
470 * Entree_fluide_vitesse_imposee,
471 * Dirichlet_paroi_defilante (nothing to do),
472 * Dirichlet_paroi_fixe (nothing to do),
473 * Symetrie (nothing to do)
474 *
475 */
477{
478 const Domaine_Cl_VDF& le_dom_cl = le_dom_Cl_VDF.valeur();
479 int nb_cond_lim = le_dom_cl.nb_cond_lim();
480
481 for (int indice_cl = 0; indice_cl < nb_cond_lim; indice_cl++)
482 {
483 const Cond_lim_base& la_cl_base =
484 le_dom_cl.les_conditions_limites(indice_cl).valeur();
485
486 const Front_VF& frontiere_vf = ref_cast(Front_VF, la_cl_base.frontiere_dis());
487
488 if (sub_type(Neumann_sortie_libre, la_cl_base))
489 {
491 frontiere_vf,
492 secmem);
493 }
494 else if (sub_type(Entree_fluide_vitesse_imposee, la_cl_base))
495 {
497 frontiere_vf,
498 secmem);
499 }
500 else if (sub_type(Dirichlet_paroi_defilante, la_cl_base))
501 {
502 // For a sliding wall, nothing to do.
503 }
504 else if (sub_type(Dirichlet_paroi_fixe, la_cl_base))
505 {
506 // Nothing to do either.
507 }
508 else if (sub_type(Symetrie, la_cl_base))
509 {
510 // Still nothing to do
511 }
512 else if (sub_type(Periodique, la_cl_base))
513 {
514 // Nothing to do
515 }
516 else
517 {
518 Cerr << "Error in Assembleur_P_VDF::modifier_secmem\n the boundary condition ";
519 Cerr << la_cl_base.que_suis_je() << " is not supported." << finl;
520 assert(0);
521 exit();
522 }
523 }
524 secmem.echange_espace_virtuel();
525 return 1;
526}
527
528/*! @brief Modifies the right-hand side of the pressure solver for a "Neumann_sortie_libre" condition.
529 *
530 * @brief Computation in "pressure increment" mode:
531 * add the pressure increment, i.e. zero (unsteady boundary condition not supported)
532 * Computation in "pressure" mode:
533 * Add the term Pimpose * surface / volume_entrelace to the right-hand side in the discretization of the
534 * pressure at the boundary (between an element elem0 and a fictitious exterior element with imposed pressure):
535 * grad P = (P(elem0) - Pimpose) * surface / volume_entrelace
536 *
537 */
538
540 const Front_VF& frontiere_vf,
541 DoubleTab& secmem)
542{
543 const Domaine_VDF& le_dom = le_dom_VDF.valeur();
544 const IntTab& face_voisins = le_dom.face_voisins();
546 {
547 /*
548 const Champ_front_base & champ_front = cond_lim.champ_front();
549 if (sub_type(Champ_front_instationnaire_base, champ_front)
550 || sub_type(Champ_front_var_instationnaire, champ_front)) {
551 Cerr << "Erreur dans Assembleur_P_VDF::modifier_secmem_pression_imposee\n ";
552 Cerr << champ_front.que_suis_je();
553 Cerr << " + resoudre_increment_pression non code" << finl;
554 assert(0);
555 exit();
556 } else {
557 // Champ stationnaire, on ajoute un increment de pression nul.
558 // So nothing to do.
559 }
560 */
561 }
562 else
563 {
564 const int nb_faces = frontiere_vf.nb_faces();
565 const int num_premiere_face = frontiere_vf.num_premiere_face();
566 for (int i = 0; i < nb_faces; i++)
567 {
568 const int num_face = num_premiere_face + i;
569 const double Pimp = cond_lim.flux_impose(i);
570 const double coef = les_coeff_pression[num_face] * Pimp;
571 const int elem = face_voisins(num_face, 0) + face_voisins(num_face, 1) + 1;
572 secmem[elem] += coef;
573 }
574 }
575}
576
577/*! @brief Modifies the right-hand side of the pressure system for an imposed velocity boundary condition.
578 *
579 * @brief If solving in pressure increment mode, ...
580 * otherwise nothing to do.
581 *
582 */
584 const Front_VF& frontiere_vf,
585 DoubleTab& secmem)
586{
587 const Champ_front_base& champ_front = cond_lim.champ_front();
588 const Domaine_VDF& le_dom = le_dom_VDF.valeur();
589 const DoubleVect& face_surfaces = le_dom.face_surfaces();
590 const IntTab& face_voisins = le_dom.face_voisins();
591
592 if (get_resoudre_en_u())
593 {
594 if (champ_front.instationnaire())
595 {
596 const DoubleTab& tab_gpoint = champ_front.derivee_en_temps();
597 int nb_dim = tab_gpoint.nb_dim();
598 bool ch_unif = (tab_gpoint.nb_dim()==1 || tab_gpoint.dimension(0)==1);
599 const int nb_faces = frontiere_vf.nb_faces();
600 const int num_premiere_face = frontiere_vf.num_premiere_face();
601 for (int i = 0; i < nb_faces; i++)
602 {
603 const int num_face = num_premiere_face + i;
604 const double surface = face_surfaces(num_face);
605 const int elem0 = face_voisins(num_face, 0);
606 const int elem1 = face_voisins(num_face, 1);
607 // gpoint is relative to the face normal (pointing towards elem1)
608 // Is the normal inward or outward?
609 const double signe = (elem0 < 0) ? 1. : -1.;
610 // Index of the element adjacent to the boundary face
611 const int elem = elem0 + elem1 + 1;
612 const int ori = le_dom.orientation(num_face);
613 const double gpoint = nb_dim==1 ? tab_gpoint(ori) : tab_gpoint(ch_unif ? 0 : i, ori);
614
615 secmem[elem] += signe * surface * gpoint;
616 }
617 }
618 else
619 {
620 // The boundary field is steady, nothing to do.
621 }
622 }
623 else
624 {
625 // Pressure resolution: the boundary condition is imposed elsewhere
626 }
627}
628
630{
631 // Projection :
632 double press_0;
633 if(!has_P_ref)
634 {
635 // Take the minimum pressure as the reference pressure
636 // to have the same reference pressure in sequential and parallel runs
637 press_0=DMAXFLOAT;
638 int nb_elem=le_dom_VDF->domaine().nb_elem();
639 for(int n=0; n<nb_elem; n++)
640 if (pression[n] < press_0)
641 press_0 = pression[n];
642 press_0 = mp_min(press_0);
643 pression -=press_0;
644 pression.echange_espace_virtuel();
645 }
646 return 1;
647}
648int Assembleur_P_VDF::assembler_mat(Matrice& matrice,const DoubleVect& volumes_entrelaces,int incr_pression,int resoudre_en_u)
649{
650 if (!matrice)
651 {
652 if (je_suis_maitre())
653 Cerr << "Assembling the pressure matrix: Assembleur_P_VDF::assembler" << finl;
654 // By default, solve in pressure increment
655 construire(matrice);
656 }
657 set_resoudre_increment_pression(incr_pression);
658 set_resoudre_en_u(resoudre_en_u);
659
660 remplir(matrice,volumes_entrelaces, 0);
661 return 1;
662}
663
664/*! @brief Assembles the pressure matrix M such that M*P = div(porosity * grad(P))
665 *
666 * @brief and computes the coefficients for modifier_secmem.
667 *
668 */
670{
671 if (je_suis_maitre())
672 Cerr << "Assembling the pressure matrix: Assembleur_P_VDF::assembler" << finl;
673 // By default, solve in pressure increment
676 construire(matrice);
677 const Domaine_VDF& domaine_vdf = le_dom_VDF.valeur();
678
679 const DoubleVect& volumes_entrelaces = domaine_vdf.volumes_entrelaces();
680 remplir(matrice,volumes_entrelaces, 0);
681 return 1;
682}
683
684/*! @brief Assembles the pressure matrix M such that M*P = div(porosity/rho * grad(P))
685 *
686 * @brief and computes the coefficients for modifier_secmem.
687 *
688 * @param matrice The matrix to assemble. Constraint: either the matrix has not yet been typed (in which case it is "constructed"), or it is the same as from the previous call.
689 * @param rho Density field.
690 */
692 const Champ_Don_base& rho)
693{
694 // assembler_rho_variable was introduced for front-tracking:
695 // must explicitly specify whether we solve in pressure increment
696 assert(get_resoudre_increment_pression() >= 0);
697 // same for solving in u
698 assert(get_resoudre_en_u() >= 0);
699 // If the matrix has not yet been typed, it must be constructed:
700 if (!matrice)
701 {
702 if (je_suis_maitre())
703 {
704 Cerr << "Assembling the pressure matrix: ";
705 Cerr << "Assembleur_P_VDF::assembler_rho_variable" << finl;
706 }
707 construire(matrice);
708 }
709 const Domaine_VDF& domaine_vdf = le_dom_VDF.valeur();
710
711 const DoubleVect& volumes_entrelaces = domaine_vdf.volumes_entrelaces();
712 remplir(matrice,volumes_entrelaces, & rho);
713 return 1;
714}
715
716/*! @brief Assembles the pressure matrix for a quasi-compressible fluid.
717 *
718 * @brief The matrix M is such that M*P = div( porosity * grad(P) ).
719 * The resoudre_increment_pression flag is set to zero if not yet assigned.
720 *
721 * @param tab_rho Density array.
722 * @return Always returns 1.
723 */
724int Assembleur_P_VDF::assembler_QC(const DoubleTab& tab_rho, Matrice& matrice)
725{
726 // Default for QC: solve in pressure, not in pressure increment.
728 {
731 }
732 if (!matrice)
733 {
734 if (je_suis_maitre())
735 {
736 Cerr << "Assembling the pressure matrix: ";
737 Cerr << "Assembleur_P_VDF::assembler_QC" << finl;
738 }
739 construire(matrice);
740 const Domaine_VDF& domaine_vdf = le_dom_VDF.valeur();
741
742 const DoubleVect& volumes_entrelaces = domaine_vdf.volumes_entrelaces();
743 remplir(matrice,volumes_entrelaces, 0);
744
745 Matrice_Bloc& matrice_bloc=ref_cast(Matrice_Bloc,matrice.valeur());
746 Matrice_Morse_Sym& la_matrice =ref_cast(Matrice_Morse_Sym,matrice_bloc.get_bloc(0,0).valeur());
747 if (la_matrice.get_est_definie()!=1)
748 {
749 if ((je_suis_maitre()) && (la_matrice.nb_lignes()==0) && (la_matrice.nb_colonnes()==0))
750 {
751 Cerr<<"Pressure matrix will not be defined."<<finl;
752 exit();
753 }
754
755 if ((la_matrice.nb_lignes()>0) && (la_matrice.nb_colonnes()>0))
756 {
757 Cerr<<"la_matrice(0,0)"<<la_matrice(0,0)<<finl;
758 Cerr<<"No imposed pressure --> P(0)=0"<<finl;
759 if (je_suis_maitre()) la_matrice(0,0) *= 2;
760 }
761 la_matrice.set_est_definie(1);
762 }
763 }
764 return 1;
765}
766
767/* equation sum_k alpha_k = 1 en Pb_Multiphase */
768void Assembleur_P_VDF::dimensionner_continuite(matrices_t matrices, int aux_only) const
769{
770 if (aux_only) return; //nothing to do
771 int e, n, N = ref_cast(Pb_Multiphase, le_dom_Cl_VDF->equation().probleme()).nb_phases(), ne_tot = le_dom_VDF->nb_elem_tot();
772 Stencil stencil(0, 2);
773
774 for (e = 0; e < le_dom_VDF->nb_elem(); e++)
775 for (n = 0; n < N; n++) stencil.append_line(e, N * e + n);
776 Matrix_tools::allocate_morse_matrix(ne_tot, N * ne_tot, stencil, *matrices.at("alpha"));
777}
778
779void Assembleur_P_VDF::assembler_continuite(matrices_t matrices, DoubleTab& secmem, int aux_only) const
780{
781 if (aux_only) return;
782 const DoubleTab& alpha = ref_cast(Pb_Multiphase, le_dom_Cl_VDF->equation().probleme()).equation_masse().inconnue().valeurs();
783 Matrice_Morse& mat = *matrices.at("alpha");
784 const DoubleVect& ve = le_dom_VDF->volumes(), &pe = le_dom_Cl_VDF->equation().milieu().porosite_elem();
785 int e, n, N = alpha.line_size();
786 /* right-hand side: multiply by porosity * volume so that the pressure system is symmetric in Cartesian coordinates */
787 for (e = 0; e < le_dom_VDF->nb_elem(); e++)
788 for (secmem(e) = -pe(e) * ve(e), n = 0; n < N; n++) secmem(e) += pe(e) * ve(e) * alpha(e, n);
789 /* matrice */
790 for (e = 0; e < le_dom_VDF->nb_elem(); e++)
791 for (n = 0; n < N; n++) mat(e, N * e + n) = -pe(e) * ve(e);
792}
793
794/* norme pour assembler_continuite */
796{
797 const DoubleVect& pe = le_dom_Cl_VDF->equation().milieu().porosite_elem(), &ve = le_dom_VDF->volumes();
798 DoubleTab norm(le_dom_VDF->nb_elem());
799 for (int e = 0; e < le_dom_VDF->nb_elem(); e++) norm(e) = pe(e) * ve(e);
800 return norm;
801}
802
804{
805 return le_dom_VDF.valeur();
806}
807
809{
810 return le_dom_Cl_VDF.valeur();
811}
812
814{
815 le_dom_VDF = ref_cast(Domaine_VDF, le_dom_dis);
816}
817
819{
820 le_dom_Cl_VDF = ref_cast(Domaine_Cl_VDF, le_dom_Cl_dis);
821}
822
824{
825 // CCa 30/04/99: not sure if anything needs to be done here
826 ;
827}
const Domaine_dis_base & domaine_dis_base() const override
int assembler_mat(Matrice &, const DoubleVect &, int incr_pression, int resoudre_en_u) override
void modifier_secmem_pression_imposee(const Neumann_sortie_libre &cond_lim, const Front_VF &frontiere_vf, DoubleTab &secmem)
Modifies the right-hand side of the pressure solver for a "Neumann_sortie_libre" condition.
int modifier_secmem(DoubleTab &) override
Modifies the right-hand side to apply boundary conditions.
int liste_faces_periodiques(ArrOfInt &faces)
Fills the array faces with the list of indices of the periodic faces in the face_voisins array.
int assembler_QC(const DoubleTab &, Matrice &) override
Assembles the pressure matrix for a quasi-compressible fluid.
int modifier_solution(DoubleTab &) override
void assembler_continuite(matrices_t matrices, DoubleTab &secmem, int aux_only=0) const override
void modifier_secmem_vitesse_imposee(const Entree_fluide_vitesse_imposee &cond_lim, const Front_VF &frontiere_vf, DoubleTab &secmem)
Modifies the right-hand side of the pressure system for an imposed velocity boundary condition.
DoubleTab norme_continuite() const override
int construire(Matrice &la_matrice)
Determines the nonzero entries of the matrix and prepares the storage.
int assembler_rho_variable(Matrice &, const Champ_Don_base &rho) override
Assembles the pressure matrix M such that M*P = div(porosity/rho * grad(P)).
const Domaine_Cl_dis_base & domaine_Cl_dis_base() const override
void associer_domaine_dis_base(const Domaine_dis_base &) override
void dimensionner_continuite(matrices_t matrices, int aux_only=0) const override
int assembler(Matrice &) override
Assembles the pressure matrix M such that M*P = div(porosity * grad(P)).
void associer_domaine_cl_dis_base(const Domaine_Cl_dis_base &) override
void completer(const Equation_base &) override
int remplir(Matrice &la_matrice, const DoubleVect &volumes_entrelaces, const Champ_Don_base *rho_ptr)
Computes the coefficients of the pressure matrix with a rho field.
ArrOfDouble les_coeff_pression
int get_resoudre_en_u() const
Returns the value of the resoudre_en_u_ flag (0 or 1) Returns -1 if the flag has not been initialized...
int set_resoudre_en_u(int flag)
Sets the value of the resoudre_en_u__ flag.
int get_resoudre_increment_pression() const
Returns the value of the resoudre_increment_pression_ flag (0 or 1) Returns -1 if the flag has not be...
int set_resoudre_increment_pression(int flag)
Sets the value of the resoudre_increment_pression_ flag.
class Champ_Don_base base class of Given Fields (not calculated)
DoubleTab & valeurs() override
Overrides Champ_base::valeurs() Returns the array of values.
class Champ_Fonc_Face_VDF
class Champ_front_base Base class for the hierarchy of boundary fields.
virtual const DoubleTab & derivee_en_temps() const
virtual bool instationnaire() const
class Cond_lim_base Base class for the hierarchy of classes that represent the different boundary con...
virtual Frontiere_dis_base & frontiere_dis()
Returns the discretized boundary to which the boundary conditions apply.
Champ_front_base & champ_front()
class Conds_lim This class represents a vector of boundary conditions.
Definition Conds_lim.h:32
Dirichlet_paroi_defilante Imposes the wall velocity in an equation of type Navier_Stokes.
Dirichlet_paroi_fixe Represents a fixed wall in a Navier-Stokes type equation.
class Domaine_Cl_VDF
class Domaine_Cl_dis_base Domaine_Cl_dis_base objects represent discretized boundary conditions
int nb_cond_lim() const
Returns the number of boundary conditions.
const Cond_lim & les_conditions_limites(int) const
Returns the i-th boundary condition.
class Domaine_VDF
Definition Domaine_VDF.h:61
int orientation(int) const override
inline DoubleVect& Domaine_VDF::porosite_face() {
virtual const DoubleVect & face_surfaces() const
Definition Domaine_VF.h:51
DoubleVect & volumes_entrelaces()
Definition Domaine_VF.h:99
int nb_faces_internes() const
A face is internal if and only if it separates two elements.
Definition Domaine_VF.h:532
int premiere_face_int() const
A face is internal if and only if it separates two elements.
Definition Domaine_VF.h:463
int face_voisins(int num_face, int i) const
Returns the neighbouring element of num_face in direction i.
Definition Domaine_VF.h:418
int nb_faces_bord() const
Returns the number of faces on which boundary conditions are applied:
Definition Domaine_VF.h:512
class Domaine_dis_base This class is the base of the hierarchy of discretized domains.
int nb_elem_tot() const
Entree_fluide_vitesse_imposee Special case of the class Dirichlet_entree_fluide.
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
class Equation_base The role of an equation is the calculation of one or more fields....
Probleme_base & probleme()
Returns the problem associated with the equation.
class Front_VF
Definition Front_VF.h:36
int nb_faces() const
Definition Front_VF.h:53
int num_premiere_face() const
Definition Front_VF.h:63
const Nom & le_nom() const override
Returns the name of the geometric boundary.
virtual void dimensionner(int N, int M)
virtual const Matrice & get_bloc(int i, int j) const
Matrice_Morse_Sym class - Represents a sparse symmetric matrix M stored in Morse format.
Matrice_Morse class - Represents a (sparse) matrix M, not necessarily square,.
auto & get_set_tab2()
const auto & get_tab2() const
void dimensionner(int n, _SIZE_ nnz)
Size the matrix with n lines and n columns and nnz zero-values coefficients.
auto & get_set_coeff()
int nb_colonnes() const override
Return local number of columns (=size on the current proc).
auto & get_set_tab1()
int nb_lignes() const override
Return local number of lines (=size on the current proc).
void compacte(int elim_coeff_nul=0)
Method to check/clean the Matrice_Morse matrix: -Suppress coefficient defined several times.
void set_est_definie(int)
int get_est_definie() const
Matrice class - Generic class in the matrix hierarchy.
Definition Matrice.h:34
static void allocate_morse_matrix(const int nb_lines, const int nb_columns, const Stencil &stencil, Matrice_Morse &matrix, const bool &attach_stencil_to_matrix=false)
Neumann_sortie_libre This class represents an open boundary without imposed velocity.
virtual double flux_impose(int i) const
Returns the value of the imposed flux on the i-th component of the field representing the flux at the...
Definition Neumann.cpp:35
const Nom & que_suis_je() const
Returns the string identifying the class.
Definition Objet_U.cpp:104
virtual Entree & readOn(Entree &)
Reads an Objet_U from an input stream. Virtual method to override.
Definition Objet_U.cpp:289
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
static int bidim_axi
Definition Objet_U.h:97
virtual Sortie & printOn(Sortie &) const
Writes the object to an output stream. Virtual method to override.
Definition Objet_U.cpp:278
static double coeff_P_neumann
Definition Option_VDF.h:31
Multiphase thermohydraulics problem of type "3*N equations":
const Equation_base & equation(int) const override
Returns the equation at index i (const version).
class Periodique This class represents a periodic boundary condition.
Definition Periodique.h:31
int face_associee(int i) const
Definition Periodique.h:35
static double mp_min(double)
Definition Process.cpp:391
static double mp_max(double)
Definition Process.cpp:379
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
static int je_suis_maitre()
Returns 1 if on the master processor of the current group (i.e. me() == 0), 0 otherwise.
Definition Process.cpp:82
Base class for output streams.
Definition Sortie.h:52
Symetrie On symmetry faces, the following properties hold:
Definition Symetrie.h:37
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
int nb_dim() const
Definition TRUSTTab.h:199
void append_line(_TYPE_)
Definition TRUSTTab.tpp:213
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
int line_size() const
Definition TRUSTVect.tpp:67
virtual void echange_espace_virtuel(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")