TrioCFD 1.9.8
TrioCFD documentation
Loading...
Searching...
No Matches
Navier_Stokes_FT_Disc.cpp
1/****************************************************************************
2* Copyright (c) 2024, 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 <Parametre_implicite.h>
17#include <Navier_Stokes_FT_Disc.h>
18#include <Probleme_FT_Disc_gen.h>
19#include <Modele_turbulence_hyd_null.h>
20#include <Discret_Thyd.h>
21#include <Operateur_Diff_base.h>
22#include <Transport_Interfaces_FT_Disc.h>
23#include <Fluide_Diphasique.h>
24#include <Fluide_Incompressible.h>
25#include <Assembleur_base.h>
26#include <TRUST_Vector.h>
27#include <Schema_Temps_base.h>
28#include <Domaine_VDF.h>
29#include <Debog.h>
30#include <Domaine_VF.h>
31#include <Terme_Source_Constituant_Vortex_VEF_Face.h>
32#include <TRUSTTrav.h>
33#include <Matrice_Morse_Sym.h>
34#include <Matrice_Bloc.h>
35#include <Param.h>
36#include <Vecteur3.h>
37#include <Domaine_Cl_VDF.h>
38#include <Connex_components.h>
39#include <EcritureLectureSpecial.h>
40#include <Avanc.h>
41
42
43#define NS_VERBOSE 0 // To activate verbose mode on err ...
44
45Implemente_instanciable(Navier_Stokes_FT_Disc, "Navier_Stokes_FT_Disc", Navier_Stokes_Turbulent);
46
47/*! @brief Calcul de champ_rho_faces_ et champ_rho_elem_ en fonction de l'indicatrice: rho_elem_ = indicatrice * ( rho(phase_1) - rho(phase_0) ) + rho(phase_0)
48 *
49 * rho_faces_ = 0.5 * (rho_elem(voisin_0) + rho_elem(voisin_1))
50 * Le calcul des viscosites cinematique et dynamique est pour l'instant le suivant:
51 * nu_elem = indicatrice * ( nu(phase_1) - nu(phase_0) ) + nu(phase_0)
52 * mu_elem = nu_elem * rho_elem
53 *
54 */
55static void FT_disc_calculer_champs_rho_mu_nu_dipha(const Domaine_dis_base& domaine_dis_base, const Fluide_Diphasique& fluide, const DoubleVect& indicatrice_elem, DoubleVect& rho_elem,
56 DoubleVect& nu_elem, DoubleVect& mu_elem, DoubleVect& rho_faces)
57{
58 const Fluide_Incompressible& phase_0 = fluide.fluide_phase(0);
59 const Fluide_Incompressible& phase_1 = fluide.fluide_phase(1);
60 const DoubleTab& tab_rho_phase_0 = phase_0.masse_volumique().valeurs();
61 const DoubleTab& tab_rho_phase_1 = phase_1.masse_volumique().valeurs();
62 const double rho_phase_0 = tab_rho_phase_0(0, 0);
63 const double rho_phase_1 = tab_rho_phase_1(0, 0);
64 const double delta_rho = rho_phase_1 - rho_phase_0;
65 const DoubleTab& tab_nu_phase_0 = phase_0.viscosite_cinematique().valeurs();
66 const DoubleTab& tab_nu_phase_1 = phase_1.viscosite_cinematique().valeurs();
67 const double nu_phase_0 = tab_nu_phase_0(0, 0);
68 const double nu_phase_1 = tab_nu_phase_1(0, 0);
69 const double delta_nu = nu_phase_1 - nu_phase_0;
70 const double mu_phase_0 = nu_phase_0 * rho_phase_0;
71 const double mu_phase_1 = nu_phase_1 * rho_phase_1;
72 const double delta_mu = mu_phase_1 - mu_phase_0;
73 double mu = 0.;
74 const int formule_mu = fluide.formule_mu();
75
76 // Calcul de rho, nu, mu aux elements
77 {
78 const int n = indicatrice_elem.size();
79 assert(n == rho_elem.size());
80 assert(n == nu_elem.size());
81 assert(n == mu_elem.size());
82 for (int i = 0; i < n; i++)
83 {
84 const double indic = indicatrice_elem[i];
85 const double rho = indic * delta_rho + rho_phase_0;
86 rho_elem[i] = rho;
87 double nu = indic * delta_nu + nu_phase_0;
88 switch(formule_mu)
89 {
90 case 0: // standard default method (will be removed)
91 {
92 mu = nu * rho;
93 }
94 break;
95 case 1: // Arithmetic average
96 {
97 mu = indic * delta_mu + mu_phase_0;
98 }
99 break;
100 case 2: // Harmonic average
101 {
102 mu = (mu_phase_0 * mu_phase_1) / (mu_phase_1 - indic * delta_mu);
103 }
104 break;
105 default:
106 {
107 Cerr << "The method specified for formule_mu in not recognized. \n" << finl;
108 Cerr << "you can choose : standard, arithmetic or harmonic. \n" << finl;
110 }
111 }
112 mu_elem[i] = mu;
113 nu = mu / rho;
114 nu_elem[i] = nu;
115 }
116 rho_elem.echange_espace_virtuel();
117 mu_elem.echange_espace_virtuel();
118 nu_elem.echange_espace_virtuel();
119 }
120
121 // Calcul de rho aux faces (on suppose que la vitesse est aux faces)
122 {
123 assert(rho_elem.size() == domaine_dis_base.nb_elem());
124 const IntTab& face_voisins = domaine_dis_base.face_voisins();
125 const int nfaces = face_voisins.dimension(0);
126 assert(rho_faces.size() == nfaces);
127 for (int i = 0; i < nfaces; i++)
128 {
129 const int elem0 = face_voisins(i, 0);
130 const int elem1 = face_voisins(i, 1);
131 double rho = 0.;
132 if (elem0 >= 0)
133 rho = rho_elem[elem0];
134 if (elem1 >= 0)
135 rho += rho_elem[elem1];
136 if (elem0 >= 0 && elem1 >= 0)
137 rho *= 0.5;
138 rho_faces[i] = rho;
139 }
140 rho_faces.echange_espace_virtuel();
141 }
142}
143
144static void FT_disc_calculer_champs_rho_mu_nu_mono(const Domaine_dis_base& zdis, const Fluide_Incompressible& fluide, Champ_Fonc_base& champ_rho_elem_, Champ_Don_base& champ_mu_, Champ_Don_base& champ_nu_,
145 Champ_Fonc_base& champ_rho_faces)
146{
147
148 if (sub_type(Champ_Uniforme,champ_rho_elem_) && (sub_type(Champ_Uniforme, champ_nu_)))
149 {
150 const DoubleTab& tab_rho_phase_0 = fluide.masse_volumique().valeurs();
151 const double rho = tab_rho_phase_0(0, 0);
152 const DoubleTab& tab_nu_phase_0 = fluide.viscosite_cinematique().valeurs();
153 const double nu = tab_nu_phase_0(0, 0);
154 const double mu = nu * rho;
155 champ_rho_elem_.valeurs() = rho;
156 champ_nu_.valeurs() = nu;
157 champ_mu_.valeurs() = mu;
158 champ_rho_faces.valeurs() = rho;
159 }
160 else
161 {
162 const int nb_el = zdis.domaine().nb_elem();
163 const Domaine_VF& zvf = ref_cast(Domaine_VF, zdis);
164 const IntTab& face_vois = zvf.face_voisins();
165 const DoubleVect& vol = zvf.volumes();
166 const int nb_faces = zvf.nb_faces();
167 int elem1, elem2;
168 double volume;
169 //
170
171 IntVect les_polys(nb_el);
172 for (int i = 0; i < nb_el; i++)
173 les_polys(i) = i;
174
175 const DoubleTab& cg = zvf.xp();
176 DoubleTab& val_rho = champ_rho_elem_.valeurs();
177 DoubleTab& val_nu = champ_nu_.valeurs();
178 DoubleTab& val_mu = champ_mu_.valeurs();
179 DoubleTab& val_rho_faces = champ_rho_faces.valeurs();
180
181 fluide.masse_volumique().valeur_aux_elems(cg, les_polys, val_rho);
182 fluide.viscosite_dynamique().valeur_aux_elems(cg, les_polys, val_mu);
183 val_rho.echange_espace_virtuel();
184 val_mu.echange_espace_virtuel();
185
186 for (int el = 0; el < nb_el; el++)
187 val_nu(el) = val_mu(el) / val_rho(el);
188 val_nu.echange_espace_virtuel();
189
190 for (int fac = 0; fac < nb_faces; fac++)
191 {
192 elem1 = face_vois(fac, 0);
193 elem2 = face_vois(fac, 1);
194 val_rho_faces(fac) = 0.;
195 volume = 0.;
196 if (elem1 != -1)
197 {
198 val_rho_faces(fac) += val_rho(elem1) * vol(elem1);
199 volume += vol(elem1);
200 }
201 if (elem2 != -1)
202 {
203 val_rho_faces(fac) += val_rho(elem2) * vol(elem2);
204 volume += vol(elem2);
205 }
206 val_rho_faces(fac) /= volume;
207 }
208
209 val_rho_faces.echange_espace_virtuel();
210 }
211}
212
214{
216}
217
219{
220 terme_diffusif.associer_diffusivite(champ_mu_);
222}
223
224/*! @brief for PDI IO: retrieve name, type and dimensions of the fields to save/restore
225 *
226 */
227std::vector<YAML_data> Navier_Stokes_FT_Disc::data_a_sauvegarder() const
228{
229 std::vector<YAML_data> data = Navier_Stokes_Turbulent::data_a_sauvegarder();
231 {
232 std::vector<YAML_data> particles = particles_eulerian_id_number_post_->data_a_sauvegarder();
233 data.insert(data.end(), particles.begin(), particles.end());
234 }
235 return data;
236}
237
239{
240 int bytes=0;
243 bytes += particles_eulerian_id_number_post_->sauvegarder(os);
244 return bytes;
245}
246
248{
251 {
252 double temps = schema_temps().temps_courant();
253 Nom ident_id_part(particles_eulerian_id_number_post_->le_nom());
254 ident_id_part += particles_eulerian_id_number_post_->que_suis_je();
255 ident_id_part += probleme().domaine().le_nom();
256 ident_id_part += Nom(temps,probleme().reprise_format_temps());
257 avancer_fichier(is, ident_id_part);
258 particles_eulerian_id_number_post_->reprendre(is);
259 DoubleTab& particles_eulerian_id_number_post = particles_eulerian_id_number_post_->valeurs();
260 int nb_elem_tot=particles_eulerian_id_number_post.dimension(0);
261 for (int elem=0; elem<nb_elem_tot; elem++) particles_eulerian_id_number_(elem)= static_cast<int>(particles_eulerian_id_number_post(elem));
262 }
263 return 1;
264}
265
266
268{
270 param.ajouter_flag("boussinesq_approximation", &variables_internes().is_boussinesq_);
271 param.ajouter_flag("new_mass_source", &variables_internes().new_mass_source_);
272 param.ajouter_flag("shift_secmem2", &variables_internes().shift_secmem2_);
273 param.ajouter_flag("matrice_pression_invariante", &variables_internes().matrice_pression_invariante);
274 param.ajouter_non_std("equation_interfaces_vitesse_imposee", (this));
275 param.ajouter_non_std("equations_interfaces_vitesse_imposee", (this));
276 param.ajouter_non_std("equation_interfaces_proprietes_fluide", (this));
277 param.ajouter("clipping_courbure_interface", &variables_internes().clipping_courbure_interface);
278 param.ajouter_non_std("equation_temperature_mpoint", (this));
279 param.ajouter_non_std("equation_temperature_mpoint_vapeur", (this));
280 param.ajouter_non_std("terme_gravite", (this));
281 param.ajouter("equations_concentration_source_vortex", &variables_internes().equations_concentration_source_fluide_);
282 param.ajouter_non_std("repulsion_aux_bords", (this));
283 param.ajouter_non_std("penalisation_forcage", (this));
284 param.ajouter_flag("mpoint_inactif_sur_qdm", &variables_internes().mpoint_inactif);
285 param.ajouter_flag("mpoint_vapeur_inactif_sur_qdm", &variables_internes().mpointv_inactif);
286 param.ajouter("correction_courbure_ordre", &variables_internes().correction_courbure_ordre_);
287 param.ajouter_non_std("interpol_indic_pour_dI_dt", (this));
288 param.ajouter_non_std("OutletCorrection_pour_dI_dt", (this));
289 param.ajouter_flag("correction_diffusion_pch", &correction_diffusion_pch_);
290}
291
293{
294 if (mot == "diffusion")
295 {
296 terme_diffusif.associer_diffusivite(diffusivite_pour_transport());
297 Cerr << "Reading and typing of the diffusion operator : " << finl;
298 // Si on a lu le modele de turbulence et qu'il est nul,
299 // alors on utilise l'operateur de diffusion standard.
300 if (le_modele_turbulence // L'operateur a ete type (donc lu)
301 && sub_type(Modele_turbulence_hyd_null, le_modele_turbulence.valeur()))
302 {
303 is >> terme_diffusif; // Operateur de diffusion standard (non turbulent)
305 {
306 Cerr << " WARNING : standard diffusion operator used for front_tracking\n";
307 Cerr << " the transposed term grad(v) is missing !!!" << finl;
308 }
309 }
310 else
312
313 // Le coefficient de diffusion est une viscosite dynamique.
314 // Il faut le diviser par rho pour calculer le pas de temps de stabilite.
315 terme_diffusif->associer_champ_masse_volumique(champ_rho_elem_.valeur());
316
317 // Pareil avec le modele de turbulence
318 if (le_modele_turbulence)
319 le_modele_turbulence->associer_champ_masse_volumique(champ_rho_elem_.valeur());
320 return 1;
321 }
322 else if ((mot == "equation_interfaces_vitesse_imposee") || (mot == "equation_interfaces_proprietes_fluide"))
323 {
324 Motcle nom_equation;
325 is >> nom_equation;
326 Cerr << mot << " equation : " << nom_equation << finl;
328
329 if (mot == "equation_interfaces_vitesse_imposee")
330 {
331 if (variables_internes().ref_eq_interf_vitesse_imposee.size() > 0)
332 {
333 Cerr << "Error: You have already a equation_interfaces_vitesse_imposee defined." << finl;
334 Cerr << "Since 1.6.4 TRUST version, you need to use the following syntax when" << finl;
335 Cerr << "using several equation_interfaces_vitesse_imposee equations:" << finl;
336 Cerr << "equations_interfaces_vitesse_imposee number_of_equations equation_name_one equation_name_two ..." << finl;
338 }
339 else
340 {
341 Cerr << "===============================================================================" << finl;
342 Cerr << "Warning: You are using a future obsolete syntax for defining a solid interface:" << finl;
343 Cerr << "equation_interfaces_vitesse_imposee " << nom_equation << finl;
344 Cerr << "Should be written:" << finl;
345 Cerr << "equations_interfaces_vitesse_imposee 1 " << nom_equation << finl;
346 Cerr << "===============================================================================" << finl;
347 }
348 variables_internes().ref_eq_interf_vitesse_imposee.add(eq);
349 }
350 else if (mot == "equation_interfaces_proprietes_fluide")
351 variables_internes().ref_eq_interf_proprietes_fluide = eq;
352 return 1;
353 }
354 else if (mot == "equations_interfaces_vitesse_imposee")
355 {
356 Noms na;
357 is >> na;
358 variables_internes().ref_eq_interf_vitesse_imposee.reset();
359 for (int i = 0; i < na.size(); i++)
360 {
362 variables_internes().ref_eq_interf_vitesse_imposee.add(eq);
363 }
364 return 1;
365 }
366 else if (mot == "equation_temperature_mpoint")
367 {
368 Nom nom_equation;
369 is >> nom_equation;
370 Cerr << " equation : " << nom_equation << finl;
371 const Equation_base& eq = probleme_ft().get_equation_by_name(nom_equation);
373 {
374 Cerr << " Error : equation is not of type Convection_Diffusion_Temperature_FT_Disc" << finl;
376 }
377 variables_internes().ref_equation_mpoint_ = ref_cast(Convection_Diffusion_Temperature_FT_Disc, eq);
378 return 1;
379 }
380 else if (mot == "equation_temperature_mpoint_vapeur")
381 {
382 Nom nom_equation;
383 is >> nom_equation;
384 Cerr << " equation : " << nom_equation << finl;
385 const Equation_base& eq = probleme_ft().get_equation_by_name(nom_equation);
387 {
388 Cerr << " Error : equation is not of type Convection_Diffusion_Temperature_FT_Disc" << finl;
390 }
391 variables_internes().ref_equation_mpoint_vap_ = ref_cast(Convection_Diffusion_Temperature_FT_Disc, eq);
392 return 1;
393 }
394 else if (mot == "terme_gravite")
395 {
396 Motcles mots;
397 mots.add("rho_g"); // Terme source volumique traditionnel avec courants parasites
398 mots.add("grad_i"); // Terme source "front-tracking" sans courants parasites mais pbs en VEF.
399 Motcle motbis;
400 is >> motbis;
401 Cerr << "Reading terme_gravite : " << motbis << finl;
402 const int r = mots.search(motbis);
403 switch(r)
404 {
405 case 0:
406 variables_internes().terme_gravite_ = Navier_Stokes_FT_Disc_interne::GRAVITE_RHO_G;
407 break;
408 case 1:
409 variables_internes().terme_gravite_ = Navier_Stokes_FT_Disc_interne::GRAVITE_GRAD_I;
410 break;
411 default:
412 Cerr << "Error " << mots << "was expected whereas " << motbis << " has been found." << finl;
413 barrier();
415 }
416 return 1;
417 }
418 else if (mot == "repulsion_aux_bords")
419 {
420 is_repulsion = true;
421 is >> minx;
422 is >> maxx;
423 is >> pente;
424 Cerr << "Interfaces repulsion on the boundaries for : " << minx << " " << maxx << finl;
425 return 1;
426 }
427 else if (mot == "penalisation_forcage")
428 {
429 variables_internes().is_penalized = 1;
430 variables_internes().is_explicite = 0;
431 variables_internes().eta = 1.e-12;
432 Cerr << "Navier_Stokes_FT_Disc : option penalisation_forcage" << finl;
433 Motcles mots;
434 mots.add("pression_reference"); // Valeur reference pour penalisation L2 pression
435 mots.add("domaine_flottant_fluide"); // Traitement local Dirichlet pression si les CL pression sont toutes en Neumann homogene
436 Motcle motbis;
437 is >> motbis;
438 Motcle accouverte = "{", accfermee = "}";
439 if (motbis == accouverte)
440 {
441 is >> motbis;
442 while (motbis != accfermee)
443 {
444 int rang = mots.search(motbis);
445 switch(rang)
446 {
447 case 0:
448 {
449 is >> variables_internes().p_ref_pena;
450 Cerr << "Lecture de la pression de reference : " << variables_internes().p_ref_pena << finl;
451 break;
452 }
453 case 1:
454 {
455 variables_internes().is_pfl_flottant = 1;
456 is >> variables_internes().x_pfl_imp;
457 is >> variables_internes().y_pfl_imp;
458 if (Objet_U::dimension == 3)
459 is >> variables_internes().z_pfl_imp;
460 Cerr << "Domaine flottant fluide" << finl;
461 Cerr << "Lecture de la position du point de reference pression fluide : " << variables_internes().x_pfl_imp << " " << variables_internes().y_pfl_imp << " "
462 << variables_internes().z_pfl_imp << finl;
463 break;
464 }
465 default:
466 Cerr << "Erreur, on attendait " << mots << "On a trouve : " << motbis << finl;
467 barrier();
469 }
470 is >> motbis;
471 }
472 }
473 else
474 {
475 Cerr << "Erreur a la lecture des parametres de la penalisation du forcage " << finl;
476 Cerr << "On attendait : " << accouverte << finl;
478 }
479 }
480 else if (mot == "interpol_indic_pour_dI_dt")
481 {
482 Motcles motcles2(9);
483 motcles2[0] = "interp_standard";
484 motcles2[1] = "interp_modifiee";
485 motcles2[2] = "interp_ai_based";
486 motcles2[3] = "interp_standard_uvext";
487 motcles2[4] = "interp_modifiee_uvext";
488 motcles2[5] = "interp_ai_based_uvext";
489 motcles2[6] = "interp_standard_uiext";
490 motcles2[7] = "interp_modifiee_uiext";
491 motcles2[8] = "interp_ai_based_uiext";
492 Motcle motlu;
493 is >> motlu;
494 Cerr << mot << " " << motlu << finl;
495 Cout << "Setting the type of interpolation for calculer_dI_dt to " << motlu << "." << finl;
496 int rang = motcles2.search(motlu);
497 switch(rang)
498 {
500 {
501 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_STANDARD;
503 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the historical way" << " where a mean value + upwind is used." << finl;
504 return 1;
505 }
507 {
508 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE;
510 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the field indicatrice_faces" << " as defined by the interfacial transport option." << finl;
511 return 1;
512 }
514 {
515 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED;
517 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the interfacial area" << " and on the normal to the interface." << finl;
518 return 1;
519 }
521 {
522 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_STANDARD_UVEXT;
524 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the historical way" << " where a mean value + upwind is used." << " Additionally, uv_ext is used."
525 << finl;
526 return 1;
527 }
529 {
530 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE_UVEXT;
532 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the field indicatrice_faces" << " as defined by the interfacial transport option."
533 << " Additionally, uv_ext is used." << finl;
534 return 1;
535 }
537 {
538 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED_UVEXT;
540 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the interfacial area" << " and on the normal to the interface." << " Additionally, uv_ext is used."
541 << finl;
542 return 1;
543 }
545 {
546 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_STANDARD_UIEXT;
548 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the historical way" << " where a mean value + upwind is used." << " Additionally, ui_ext is used."
549 << finl;
550 return 1;
551 }
553 {
554 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE_UIEXT;
556 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the field indicatrice_faces" << " as defined by the interfacial transport option."
557 << " Additionally, ui_ext is used." << finl;
558 return 1;
559 }
561 {
562 variables_internes_.type_interpol_indic_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED_UIEXT;
564 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the interfacial area" << " and on the normal to the interface." << " Additionally, ui_ext is used."
565 << finl;
566 return 1;
567 }
568 default:
569 Cerr << "Transport_Interfaces_FT_Disc::lire\n" << "The options for methode_transport are :\n" << motcles2;
571 }
572 }
573 else if (mot == "OutletCorrection_pour_dI_dt")
574 {
575 Motcles motcles2(4);
576 motcles2[0] = "no_correction";
577 motcles2[1] = "CORRECTION_GHOST_INDIC";
578 motcles2[2] = "zero_net_flux_on_mixed_cells";
579 motcles2[3] = "zero_out_flux_on_mixed_cells";
580 Motcle motlu;
581 is >> motlu;
582 Cerr << mot << " " << motlu << finl;
583 Cout << "Setting the type of correction at outlet BC for calculer_dI_dt to " << motlu << "." << finl;
584 int rang = motcles2.search(motlu);
585 switch(rang)
586 {
588 {
589 variables_internes_.OutletCorrection_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::NO_CORRECTION;
591 Cerr << " No correction of div(chi u) at exit (historical way)" << finl;
592 return 1;
593 }
595 {
596 variables_internes_.OutletCorrection_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::CORRECTION_GHOST_INDIC;
598 Cerr << " Correction of chi in ghost cells (virtually)" << finl;
599 return 1;
600 }
602 {
603 variables_internes_.OutletCorrection_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::ZERO_NET_FLUX_ON_MIXED_CELLS;
605 Cerr << " correction of div(chi u) at exit : zero divergence on cells touching outlet." << finl;
606 return 1;
607 }
609 {
610 variables_internes_.OutletCorrection_pour_dI_dt_ = Navier_Stokes_FT_Disc_interne::ZERO_OUT_FLUX_ON_MIXED_CELLS;
612 {
613 Cerr << " correction of div(chi u) at exit : zero vapour mass flux on cells touching outlet." << finl;
614 Cerr << " This is a bad option because it does not let vapour get out explicitly (prevents interface contact)" << finl;
615 Cerr << " Should not be used or with great care." << finl;
616 // Process::exit();
617 }
618 return 1;
619 }
620 default:
621 Cerr << "Transport_Interfaces_FT_Disc::lire\n" << "The options for methode_transport are :\n" << motcles2;
623 }
624 }
625 else
627 return 1;
628}
630{
631 return champ_mu_;
632}
633
635{
636 if (!sub_type(Probleme_FT_Disc_gen, un_probleme))
637 {
638 Cerr << "Error for the method Navier_Stokes_FT_Disc::associer_pb_base\n";
639 Cerr << " Navier_Stokes_FT_Disc equation must be associated to\n";
640 Cerr << " a Probleme_FT_Disc_gen problem type\n";
642 }
643 probleme_ft_ = ref_cast(Probleme_FT_Disc_gen, un_probleme);
645}
646
648{
649 const OBS_PTR(Fluide_Diphasique) &fluide_dipha = variables_internes().ref_fluide_diphasique;
650 if (!fluide_dipha)
651 {
652 Cerr << "Error for the method Navier_Stokes_FT_Disc::fluide_diphasique()\n";
653 Cerr << " The fluid has not been associated\n";
654 Cerr << " (check that the fluid is of Fluide_Diphasique type)" << finl;
655 assert(0);
657 }
658 return fluide_dipha.valeur();
659}
660
662{
663 if (sub_type(Fluide_Diphasique, un_fluide))
664 variables_internes().ref_fluide_diphasique = ref_cast(Fluide_Diphasique, un_fluide);
665 else
667}
668
670{
671 if (variables_internes().ref_fluide_diphasique)
672 return variables_internes().ref_fluide_diphasique.valeur();
673 else
675}
676
678{
679 if (variables_internes().ref_fluide_diphasique)
680 return variables_internes().ref_fluide_diphasique.valeur();
681 else
683}
684
685/*! @brief Discretisation des champs utilises dans cette equation.
686 *
687 * Fonction appelee par Probleme_base::discretiser.
688 * B. Mathieu : a titre experimental, au lieu de dupliquer les noms
689 * des champs ici et dans "a_pour_champ_Fonc", on stocke
690 * les champs dans une liste. (voir a_pour_champ_fonc).
691 *
692 */
694{
696 const Discretisation_base& dis = discretisation();
697 const double temps = schema_temps().temps_courant();
698 const Domaine_dis_base& mon_dom_dis = domaine_dis();
699 LIST(OBS_PTR(Champ_base)) &champs_compris = variables_internes().liste_champs_compris;
700
701 dis.discretiser_champ("champ_elem", mon_dom_dis, "diffusivite", "m^2/s", 1 /* composantes */, temps, champ_nu_);
702 champs_compris.add(champ_nu_.valeur());
703 //Nouvelle formulation
704 champs_compris_.ajoute_champ(champ_nu_);
705
706 dis.discretiser_champ("champ_elem", mon_dom_dis, "viscosite_dynamique", "kg/m.s", 1 /* composantes */, temps, champ_mu_);
707 champs_compris.add(champ_mu_.valeur());
708 champs_compris_.ajoute_champ(champ_mu_);
709
710 dis.discretiser_champ("champ_elem", mon_dom_dis, "masse_volumique", "kg/m3", 1 /* composantes */, temps, champ_rho_elem_);
711 champs_compris.add(champ_rho_elem_.valeur());
712 champs_compris_.ajoute_champ(champ_rho_elem_);
713
714 // La masse volumique discretisee sur les volumes de controle de la vitesse
715 dis.discretiser_champ("vitesse", mon_dom_dis, "masse_volumique_vnodes", "kg/m3", 1 /* composantes */, temps, champ_rho_faces_);
716 champs_compris.add(champ_rho_faces_.valeur());
717 champs_compris_.ajoute_champ(champ_rho_faces_);
718
719 // Variables internes
720 dis.discretiser_champ("pression", mon_dom_dis, "second_membre_projection", "", 1 /* composantes */, temps, variables_internes().second_membre_projection);
721 champs_compris.add(variables_internes().second_membre_projection.valeur());
722 champs_compris_.ajoute_champ(variables_internes().second_membre_projection);
723 dis.discretiser_champ("champ_elem", mon_dom_dis, "second_membre_projection_jump", "", 1 /* composantes */, temps, variables_internes().second_membre_projection_jump_);
724 champs_compris.add(variables_internes().second_membre_projection_jump_.valeur());
725 champs_compris_.ajoute_champ(variables_internes().second_membre_projection_jump_);
726 dis.discretiser_champ("vitesse", mon_dom_dis, "gradient_pression_interne", "m/s2", -1 /* nb composantes par defaut */, temps, variables_internes().gradient_pression);
727 champs_compris.add(variables_internes().gradient_pression.valeur());
728 champs_compris_.ajoute_champ(variables_internes().gradient_pression);
729 dis.discretiser_champ("vitesse", mon_dom_dis, "derivee_u_etoile", "", -1 /* nb composantes par defaut */, temps, variables_internes().derivee_u_etoile);
730 champs_compris.add(variables_internes().derivee_u_etoile.valeur());
731 champs_compris_.ajoute_champ(variables_internes().derivee_u_etoile);
732 dis.discretiser_champ("vitesse", mon_dom_dis, "terme_diffusion_vitesse", "", -1 /* nb composantes par defaut */, temps, variables_internes().terme_diffusion);
733 champs_compris.add(variables_internes().terme_diffusion.valeur());
734 champs_compris_.ajoute_champ(variables_internes().terme_diffusion);
735 dis.discretiser_champ("vitesse", mon_dom_dis, "terme_convection_vitesse", "", -1 /* nb composantes par defaut */, temps, variables_internes().terme_convection);
736 champs_compris.add(variables_internes().terme_convection.valeur());
737 champs_compris_.ajoute_champ(variables_internes().terme_convection);
738 dis.discretiser_champ("vitesse", mon_dom_dis, "terme_source_vitesse", "", -1 /* nb composantes par defaut */, temps, variables_internes().terme_source);
739 champs_compris.add(variables_internes().terme_source.valeur());
740 champs_compris_.ajoute_champ(variables_internes().terme_source);
741 dis.discretiser_champ("vitesse", mon_dom_dis, "terme_source_interfaces", "", -1 /* nb composantes par defaut */, temps, variables_internes().terme_source_interfaces);
742 champs_compris.add(variables_internes().terme_source_interfaces.valeur());
743 champs_compris_.ajoute_champ(variables_internes().terme_source_interfaces);
744 if (dis.que_suis_je() == "VEFPreP1B")
745 {
746 dis.discretiser_champ("pression", mon_dom_dis, "indicatrice_p1b", "", 1 /* composantes */, temps, variables_internes().indicatrice_p1b);
747 champs_compris.add(variables_internes().indicatrice_p1b.valeur());
748 champs_compris_.ajoute_champ(variables_internes().indicatrice_p1b);
749 }
750
751 dis.discretiser_champ("vitesse", mon_dom_dis, "gradient_indicatrice", "", -1 /* nb composantes par defaut */, temps, variables_internes().gradient_indicatrice);
752 champs_compris.add(variables_internes().gradient_indicatrice.valeur());
753 champs_compris_.ajoute_champ(variables_internes().gradient_indicatrice);
754 dis.discretiser_champ("vitesse", mon_dom_dis, "potentiel_faces", "", 1 /* composante */, temps, variables_internes().potentiel_faces);
755 champs_compris.add(variables_internes().potentiel_faces.valeur());
756 champs_compris_.ajoute_champ(variables_internes().potentiel_faces);
757 dis.discretiser_champ("champ_elem", mon_dom_dis, "potentiel_elements", "", 1 /* composante */, temps, variables_internes().potentiel_elements);
758 champs_compris.add(variables_internes().potentiel_elements.valeur());
759 champs_compris_.ajoute_champ(variables_internes().potentiel_elements);
760 dis.discretiser_champ("vitesse", mon_dom_dis, "vitesse_delta_interface", "m/s", -1 /* nb composantes par defaut */, 1, temps, variables_internes().delta_u_interface);
761 // Pour pouvoir faire filtrer_L2:
762 variables_internes().delta_u_interface->associer_eqn(*this);
763 champs_compris.add(variables_internes().delta_u_interface.valeur());
764 champs_compris_.ajoute_champ(variables_internes().delta_u_interface);
765 dis.discretiser_champ("pression", mon_dom_dis, "pression_laplacien_d", "", 1 /* composante */, temps, variables_internes().laplacien_d);
766 champs_compris.add(variables_internes().laplacien_d.valeur());
767 champs_compris_.ajoute_champ(variables_internes().laplacien_d);
768 dis.discretiser_champ("temperature", mon_dom_dis, "temperature_mpoint", "", 1 /* composante */, temps, variables_internes().mpoint);
769 champs_compris.add(variables_internes().mpoint.valeur());
770 champs_compris_.ajoute_champ(variables_internes().mpoint);
771 dis.discretiser_champ("temperature", mon_dom_dis, "temperature_mpointv", "", 1 /* composante */, temps, variables_internes().mpoint_vap);
772 champs_compris.add(variables_internes().mpoint_vap.valeur());
773 champs_compris_.ajoute_champ(variables_internes().mpoint_vap);
774 // Pour variation temporelle dI_dt
775 dis.discretiser_champ("pression", mon_dom_dis, "derivee_temporelle_indicatrice", "", 1 /* composante */, temps, variables_internes().derivee_temporelle_indicatrice);
776 champs_compris.add(variables_internes().derivee_temporelle_indicatrice.valeur());
777 champs_compris_.ajoute_champ(variables_internes().derivee_temporelle_indicatrice);
778
779 // Eulerian Interfacial area :
780 dis.discretiser_champ("champ_elem", mon_dom_dis, "interfacial_area", "m2", 1 /* composante */, temps, variables_internes().ai);
781 champs_compris.add(variables_internes().ai.valeur());
782 champs_compris_.ajoute_champ(variables_internes().ai);
783
784 // Velocity jump "u0" computed for phase 0 :
785 Nom nom = Nom("vitesse_jump0_") + le_nom();
786 dis.discretiser_champ("vitesse", mon_dom_dis, nom, "m/s", -1 /* nb composantes par defaut */, 1, temps, variables_internes().vitesse_jump0_);
787 variables_internes().vitesse_jump0_->associer_eqn(*this);
788 champs_compris.add(variables_internes().vitesse_jump0_.valeur());
789 champs_compris_.ajoute_champ(variables_internes().vitesse_jump0_);
790
791 // for fpi module
792 const OBS_PTR(Fluide_Diphasique) &fluide_dipha = variables_internes().ref_fluide_diphasique;
793 if (fluide_dipha)
794 {
796 set_id_fluid_phase(fluide_diphasique().get_id_fluid_phase());
798 }
799
800 const Domaine_VF& domain_vf=ref_cast(Domaine_VF,domaine_dis());
801 Noms names(1);
802 Noms units(1);
803 names[0]="cells";
804 units[0]="";
805 dis.discretiser_champ("champ_elem", domain_vf, scalaire, names, units, 1,
806 schema_temps().temps_courant(),particles_eulerian_id_number_post_);
807 particles_eulerian_id_number_post_->nommer("PARTICLES_EULERIAN_ID_NUMBER");
808 champs_compris.add(particles_eulerian_id_number_post_.valeur());
809 champs_compris_.ajoute_champ(particles_eulerian_id_number_post_);
810
811 dis.discretiser_champ("vitesse", mon_dom_dis,
812 "contact_force_source_term", "",
813 1 /* 1 nb composante */, temps,
814 variables_internes().contact_force_source_term);
815 champs_compris.add(variables_internes().contact_force_source_term.valeur());
816 champs_compris_.ajoute_champ(variables_internes().contact_force_source_term);
817 // Velocity jump "u1" computed for phase 1 :
818 Nom nom1 = Nom("vitesse_jump1_") + le_nom();
819 dis.discretiser_champ("vitesse", mon_dom_dis, nom1, "m/s", -1 /* nb composantes par defaut */, 1, temps, variables_internes().vitesse_jump1_);
820 variables_internes().vitesse_jump1_->associer_eqn(*this);
821 champs_compris.add(variables_internes().vitesse_jump1_.valeur());
822 champs_compris_.ajoute_champ(variables_internes().vitesse_jump1_);
823}
824
825/*! @brief Methode surchargee de Navier_Stokes_std, appelee par Navier_Stokes_std::discretiser().
826 *
827 * L'assembleur pression est particulier pour le front-tracking
828 * en VEF (en attendant qu'on factorise tous ces assembleurs pression)
829 *
830 */
832{
833 const Discretisation_base& dis = discretisation();
834 if (dis.que_suis_je() == "VDF")
835 {
836 // Assembleur pression generique (prevu pour rho_variable)
837 assembleur_pression_.typer("Assembleur_P_VDF");
838 }
839 else if (dis.que_suis_je() == "VEFPreP1B")
840 {
841 // Assembleur pression generique (prevu pour rho_variable)
842 assembleur_pression_.typer("Assembleur_P_VEFPreP1B");
843 }
844
845 Assembleur_base& assembleur = assembleur_pression_.valeur();
847 // B. Mathieu, 07_2004 : premier jet de la methode, on resout en pression.
848 // Version actuelle : pas d'increment de pression
850 assembleur.set_resoudre_en_u(1);
851}
852
853/*! @brief methode appelee par Navier_Stokes_std::preparer_calcul.
854 *
855 * ..
856 *
857 */
859{
861 Cerr << "Navier_Stokes_FT_Disc::projeter does nothing" << finl;
862}
863
864
866{
867 // Si le mot cle equation_interfaces_proprietes_fluide a ete specifie,
868 // l'indicatrice de phase correspondant a l'equation sert a calculer
869 // les proprietes physiques du milieu.
870 // Sinon, le milieu doit etre de type Fluide_Incompressible.
871 OBS_PTR(Transport_Interfaces_FT_Disc) &ref_equation = variables_internes().ref_eq_interf_proprietes_fluide;
872 if (ref_equation)
873 {
874 // Couplage Navier-Stokes / Fluide : les interfaces determinent la position des phases.
875 // On utilise les proprietes du fluide diphasique
876 if (Process::je_suis_maitre()) // TODO: Attention, comment savoir si on vient de reprendre et que faire?
877 Cerr << "Initialisation of the physical properties (rho, mu, ...)\n" << " based on the indicatrice field of the equation " << ref_equation->le_nom() << finl;
878
879 // TODO : suite au refactor, on utilise indicatrice_cache (via get_indicatrice) au lieu de indicatrice refeq_transport.valeur().inconnue()
880 // Elles ont normalement toutes 2 ete calculees.
881 // TODO : Supprimer cet heritage.
882 FT_disc_calculer_champs_rho_mu_nu_dipha(domaine_dis(), fluide_diphasique(), ref_equation->get_indicatrice().valeurs(), // indicatrice
883 champ_rho_elem_->valeurs(), champ_nu_->valeurs(), champ_mu_->valeurs(), champ_rho_faces_->valeurs());
884 }
885 else
886 {
887 // Pas de couplage Navier-Stokes / Fluide : le fluide est monophasique.
888 const Fluide_Incompressible& phase_0 = ref_cast(Fluide_Incompressible, milieu());
889 const Domaine_dis_base& zdis = domaine_dis();
890 FT_disc_calculer_champs_rho_mu_nu_mono(zdis, phase_0, champ_rho_elem_, champ_mu_, champ_nu_, champ_rho_faces_);
891 }
892}
893
894/*! @brief methode appelee par Probleme_base::preparer_calcul()
895 *
896 */
898{
899 Cerr << "Navier_Stokes_FT_Disc::preparer_calcul()" << finl;
901
902 le_modele_turbulence->preparer_calcul();
903
904 {
905 // Si le mot cle equation_interfaces_proprietes_fluide a ete specifie,
906 // l'indicatrice de phase correspondant a l'equation sert a calculer
907 // les proprietes physiques du milieu.
908 // Sinon, le milieu doit etre de type Fluide_Incompressible.
909
910 OBS_PTR(Transport_Interfaces_FT_Disc) &ref_equation = variables_internes().ref_eq_interf_proprietes_fluide;
911
912 if (ref_equation)
913 {
914
915 // Couplage Navier-Stokes / Fluide : les interfaces determinent la position des phases.
916 // On utilise les proprietes du fluide diphasique
917
919 {
920 Cerr << "Initialisation of the physical properties (rho, mu, ...)\n" << " based on the indicatrice field of the equation " << ref_equation->le_nom() << finl;
921
922 }
923 FT_disc_calculer_champs_rho_mu_nu_dipha(domaine_dis(), fluide_diphasique(), ref_equation->get_indicatrice().valeurs(), // indicatrice
924 champ_rho_elem_->valeurs(), champ_nu_->valeurs(), champ_mu_->valeurs(), champ_rho_faces_->valeurs());
925 const OWN_PTR(Collision_Model_FT_base)& ptr_collision_model=ref_equation.valeur().get_ptr_collision_model();
927 {
928 compute_particles_eulerian_id_number(ptr_collision_model); // swap in T_I_FT_D::preparer_calcul
929 const double temps = schema_temps().temps_courant();
930 const Discretisation_base& dis = discretisation();
931 const Domaine_dis_base& mon_domaine_dis = domaine_dis();
932 LIST(OBS_PTR(Champ_base)) & champs_compris = variables_internes().liste_champs_compris;
933 Post_Processing_Hydrodynamic_Forces& post_process_hydro_forces=
934 ref_equation.valeur().get_post_process_hydro_forces();
935 if (post_process_hydro_forces.get_is_compute_forces_Stokes_th())
936 {
937 dis.discretiser_champ("vitesse", mon_domaine_dis,
938 "vitesse_stokes_th", "m/s",
939 3 , temps,
940 velocity_field_Stokes_th_);
941 champs_compris.add(velocity_field_Stokes_th_.valeur());
942 champs_compris_.ajoute_champ(velocity_field_Stokes_th_);
943
944 dis.discretiser_champ("pression", mon_domaine_dis,
945 "pression_stokes_th", "pa",
946 1 , temps,
947 pressure_field_Stokes_th_);
948 champs_compris.add(pressure_field_Stokes_th_.valeur());
949 champs_compris_.ajoute_champ(pressure_field_Stokes_th_);
950 }
951 }
952 }
953 else
954 {
955
956 // Pas de couplage Navier-Stokes / Fluide : le fluide est monophasique.
957
958 const Fluide_Incompressible& phase_0 = ref_cast(Fluide_Incompressible, milieu());
959 const Domaine_dis_base& zdis = domaine_dis();
960 FT_disc_calculer_champs_rho_mu_nu_mono(zdis, phase_0, champ_rho_elem_, champ_mu_, champ_nu_, champ_rho_faces_);
961 }
962
963 // Premiere version :les termes sources sont homogenes a rho*v
964 // sources().associer_champ_rho(champ_rho_elem_.valeur());
965 // Nouvelle version: les termes "sources()" sont homogenes a v.
966 // on ne fait rien.
967 }
968
970 DoubleTab& tab_vitesse = inconnue().valeurs();
971
972 // On assemble la matrice de pression pour la premiere fois.
973 assembleur_pression()->assembler_rho_variable(matrice_pression_, champ_rho_faces_.valeur());
974 // Informe le solveur que la matrice a change :
975 solveur_pression()->reinit();
976 // Calcul du second membre :
977 // div(vpoint) a l'interieur du domaine,
978 // prise en compte des conditions aux limites de pression/vitesse
979 DoubleTab& secmem = variables_internes().second_membre_projection->valeurs();
980 divergence.calculer(tab_vitesse, secmem);
981 secmem *= -1;
982 // Il faut faire ceci car on ne resout pas en "increment de pression":
983 assembleur_pression_->modifier_secmem(secmem);
984
985 // Ajout pour la sauvegarde au premier pas de temps si reprise
986 la_pression->changer_temps(schema_temps().temps_courant());
987
988 // Resolution du systeme en pression : calcul de la_pression
989 solveur_pression_.resoudre_systeme(matrice_pression_.valeur(), secmem, la_pression->valeurs());
990 assembleur_pression_->modifier_solution(la_pression->valeurs());
991 // Calcul d(u)/dt = vpoint + 1/rho*grad(P)
992 DoubleTab& gradP = variables_internes().gradient_pression->valeurs();
993 gradient.calculer(la_pression->valeurs(), gradP);
994 solveur_masse->appliquer(gradP);
995 // Correction de la vitesse :
996 if (projection_a_faire()) // Temporaire pour permettre de ne pas resoudre NS avec mettant operateurs nuls et projection_initiale 0
997 {
998 int i, j;
999 const DoubleTab& rho_faces = champ_rho_faces_->valeurs();
1000 const int n = tab_vitesse.dimension(0);
1001 const int m = tab_vitesse.line_size();
1002
1003 for (i = 0; i < n; i++)
1004 for (j = 0; j < m; j++)
1005 tab_vitesse(i, j) -= gradP(i, j) / rho_faces(i);
1006
1007 tab_vitesse.echange_espace_virtuel();
1008 }
1009
1010 if (le_traitement_particulier)
1011 {
1012 if (le_traitement_particulier->support_ok())
1013 le_traitement_particulier->associer_champ_masse_volumique(champ_rho_faces_.valeur());
1014 le_traitement_particulier->preparer_calcul_particulier();
1015 }
1016
1017
1018 return 1;
1019}
1020
1022{
1023 return variables_internes().is_penalized;
1024}
1025
1027{
1028 return variables_internes().shift_secmem2_;
1029}
1030
1032{
1033 return variables_internes().new_mass_source_;
1034}
1035
1037{
1038 return variables_internes().ai->valeurs();
1039}
1040
1042{
1043 return variables_internes().ai->valeurs();
1044}
1045
1047{
1048 return variables_internes().mpoint->valeurs();
1049}
1050
1052{
1053 return variables_internes().mpoint->valeurs();
1054}
1055
1057{
1059
1060 // Preparation des champs utilises pour le calcul des derivees en temps
1061 // S'il n'y a pas d'equation de transport des interfaces entre phases fluides,
1062 // on ne recalcule pas les proprietes.
1064
1065 champ_rho_elem_->mettre_a_jour(temps);
1066 champ_rho_faces_->mettre_a_jour(temps);
1067 champ_mu_->mettre_a_jour(temps);
1068 champ_nu_->mettre_a_jour(temps);
1070 {
1071 particles_eulerian_id_number_post_->mettre_a_jour(temps);
1072 DoubleTab& tab = particles_eulerian_id_number_post_->valeurs();
1073 const Domaine_VF& domain_vf=ref_cast(Domaine_VF,domaine_dis());
1074 for (int elem=0; elem<domain_vf.nb_elem(); elem++) tab[elem]=particles_eulerian_id_number_[elem];
1075 }
1076}
1077
1082
1083
1084/*! @brief Calcul des forces de tension superficielles (F_sigma) et de la partie a rotationnel non nul de la gravite (G_rot) (si GRAVITE_GRAD_I) :
1085 *
1086 * F_sigma = INTEGRALE sur le volume de controle (
1087 * sigma_aux_faces * courbure_aux_faces * gradient(indicatrice)
1088 * + gradient_sigma )
1089 * G_rot = INTEGRALE sur le volume de controle (
1090 * phi * gradient(rho) ) (avec phi = potentiel de pesanteur)
1091 *
1092 * @param (gradient_indicatrice) le gradient de l'indicatrice issu de l'operateur "gradient", donc homogene a l'integrale du gradient sur les volumes de controle de la vitesse.
1093 * @param (potentiel_faces) un champ aux faces a une composante, ou on stocke le "potentiel aux faces"
1094 * @param (champ) le champ aux faces (meme discretisation que la vitesse) ou on stocke le terme source des forces superficielles.
1095 */
1096//
1097// The method is no longer const because it changes a member of variables_internes() to store the Eulerian interfacial Area.
1098void Navier_Stokes_FT_Disc::calculer_champ_forces_superficielles(const Maillage_FT_Disc& maillage, const Champ_base& gradient_indicatrice, Champ_base& potentiel_elements, Champ_base& potentiel_faces,
1099 Champ_base& champ)
1100{
1101 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
1102 // Nombre de faces
1103 const int nb_faces = domaine_vf.nb_faces();
1104 {
1105 // Le champ et le gradient de l'indicatrice doivent etre aux faces
1106 assert(champ.valeurs().dimension(0) == nb_faces);
1107 assert(potentiel_faces.valeurs().dimension(0) == nb_faces);
1108 assert(gradient_indicatrice.valeurs().dimension(0) == nb_faces);
1109 }
1110 const int dim = Objet_U::dimension;
1111
1112 // Calcul du "potentiel aux sommets du maillage"
1113 ArrOfDouble potentiel_sommets;
1114 potentiel_sommets.resize_array(maillage.nb_sommets(), RESIZE_OPTIONS::NOCOPY_NOINIT);
1115
1116 // (ce potentiel est constant sur chaque portion connexe d'interface si
1117 // l'interface est a l'equilibre).
1118 // courbure * sigma + potentiel_gravite_phase_1 - potentiel_gravite_phase_0
1119 {
1120 // Pour l'instant, la tension de surface est constante
1121 const Fluide_Diphasique& fluide_dipha = fluide_diphasique();
1122 const double sigma = fluide_dipha.sigma();
1123
1124 const int n = maillage.nb_sommets();
1125 const ArrOfDouble& courbure_sommets = maillage.get_update_courbure_sommets();
1126 //Calcul du "potentiel aux sommets"
1127 int i;
1128 {
1129 const double clipping_courbure_max = variables_internes().clipping_courbure_interface;
1130 int clip_counter = 0;
1131 for (i = 0; i < n; i++)
1132 {
1133 double c = courbure_sommets[i];
1134 // Clipping de la courbure: si la courbure est superieure a la
1135 // valeur maxi autorisee, on limite (permet de ne pas plomber le
1136 // pas de temps s'il y a une singularite geometrique dans le maillage)
1137 if (std::fabs(c) > clipping_courbure_max)
1138 {
1139 clip_counter++;
1140 c = ((c > 0) ? 1. : -1.) * clipping_courbure_max;
1141 }
1142 potentiel_sommets[i] = c * sigma;
1143 }
1144 clip_counter = check_int_overflow(mp_sum(clip_counter));
1145 if (clip_counter > 0 && je_suis_maitre())
1146 {
1147 Cerr << "Navier_Stokes_FT_Disc::calculer_champ_forces_superficielles : clip_count " << clip_counter << finl;
1148 }
1149 }
1150 //Ajout des termes de gravite
1151 if (variables_internes().terme_gravite_ == Navier_Stokes_FT_Disc_interne::GRAVITE_GRAD_I)
1152 {
1153 if (milieu().a_gravite())
1154 {
1155 const double rho_0 = fluide_dipha.fluide_phase(0).masse_volumique().valeurs()(0, 0);
1156 const double rho_1 = fluide_dipha.fluide_phase(1).masse_volumique().valeurs()(0, 0);
1157 const double delta_rho = rho_1 - rho_0;
1158
1159 // Pour l'instant : gravite uniforme g => phi(s) = - x scalaire g
1160 const DoubleTab& gravite = milieu().gravite().valeurs();
1161 if (gravite.nb_dim() != 2 || gravite.line_size() != dim)
1162 {
1163 Cerr << "Error for the method calculer_champ_forces_superficielles\n";
1164 Cerr << "gravite.dimension(1) != Objet_U::dimension" << finl;
1165 Process::exit();
1166 }
1167 const DoubleTab& sommets = maillage.sommets();
1168
1169 for (i = 0; i < n; i++)
1170 {
1171 double p = 0.;
1172 for (int j = 0; j < dim; j++)
1173 p += sommets(i, j) * gravite(0, j);
1174
1175 if (is_repulsion)
1176 {
1177 double dx = 0.;
1178 double px = sommets(i, 0);
1179 if (px > maxx)
1180 dx = px - maxx;
1181 else if (px < minx)
1182 dx = minx - px;
1183
1184 double dy = 0.;
1185 double py = sommets(i, 1);
1186 if (py > maxx)
1187 dy = py - maxx;
1188 else if (py < minx)
1189 dy = minx - py;
1190
1191 p += sqrt(dx * dx + dy * dy) * pente;
1192 }
1193
1194 potentiel_sommets[i] -= p * delta_rho;
1195 }
1196 }
1197 }
1198 }
1199
1200 // Calcul du "potentiel aux elements" :
1201 DoubleTab poids(potentiel_elements.valeurs());
1202 {
1203 // Tableau des facettes du maillage interfaces
1204 const IntTab& facettes = maillage.facettes();
1205 // Le tableau "potentiel aux faces" a remplir :
1206 DoubleTab& valeurs_potentiel_elements = potentiel_elements.valeurs();
1207
1208 const ArrOfDouble& surface_facettes = maillage.get_update_surface_facettes();
1209
1210 valeurs_potentiel_elements = 0.;
1211 // Somme des poids des contributions ajoutees aux faces
1212 poids = 0.;
1213
1214 // Boucle sur les faces de l'interface
1215 const Intersections_Elem_Facettes& intersections = maillage.intersections_elem_facettes();
1216 const ArrOfInt& index_facette_elem = intersections.index_facette();
1217 double pot[3] = { 0., 0., 0. };
1218
1219 int fa7;
1220 const int nb_facettes = maillage.nb_facettes();
1221
1222 for (fa7 = 0; fa7 < nb_facettes; fa7++)
1223 {
1224 // Potentiel aux sommets de la facette:
1225 pot[0] = potentiel_sommets[facettes(fa7, 0)];
1226 pot[1] = potentiel_sommets[facettes(fa7, 1)];
1227 if (dim == 3)
1228 pot[2] = potentiel_sommets[facettes(fa7, 2)];
1229 // Boucle sur les elements traverses par la facette
1230 int index = index_facette_elem[fa7];
1231 const double surface_facette = surface_facettes[fa7];
1232 while (index >= 0)
1233 {
1234 const Intersections_Elem_Facettes_Data& data = intersections.data_intersection(index);
1235 // Calcul du potentiel au centre de l'intersection
1236 double p = 0.;
1237 int i;
1238 for (i = 0; i < 3; i++)
1239 p += data.barycentre_[i] * pot[i];
1240 // La contribution aux faces est ponderee par la surface d'intersection
1241#ifdef AVEC_BUG_SURFACES
1242 const double surf = data.surface_intersection_;
1243#else
1244 const double surf = data.fraction_surface_intersection_ * surface_facette;
1245#endif
1246 p *= surf;
1247
1248 // Ajout de la contribution a l'element
1249 const int element = data.numero_element_;
1250 valeurs_potentiel_elements(element) += p;
1251 poids(element) += surf;
1252
1253 index = data.index_element_suivant_;
1254 }
1255 }
1256
1257 valeurs_potentiel_elements.echange_espace_virtuel();
1258 poids.echange_espace_virtuel();
1259
1260 if (champ.valeurs().line_size() > 1) // VEF ?
1261 {
1262 // Compute a potential on elements that are neighbours of
1263 // elements containing an interface :
1264 // For VEF, the gradient of the indicator function can be
1265 // non zero on the faces of these elements.
1266 int element;
1267 const int nb_elements = poids.dimension(0);
1268 const IntTab& face_voisins = le_dom_dis->face_voisins();
1269 const Domaine_VF& domainevf = ref_cast(Domaine_VF, le_dom_dis.valeur());
1270 const IntTab& elem_faces = domainevf.elem_faces();
1271 const int nb_faces_par_element = elem_faces.line_size();
1272 DoubleVect copie_valeurs_potentiel_elements(valeurs_potentiel_elements);
1273 DoubleVect copie_poids(poids);
1274 for (element = 0; element < nb_elements; element++)
1275 {
1276 double potential = 0.; // sum of potentials of neighbouring elements
1277 double p = 0.; // sum of weights of neighbouring elements
1278 int i_face;
1279 for (i_face = 0; i_face < nb_faces_par_element; i_face++)
1280 {
1281 const int face = elem_faces(element, i_face);
1282 const int elem_voisin_0 = face_voisins(face, 0);
1283 const int elem_voisin_1 = face_voisins(face, 1);
1284 const int elem_voisin = elem_voisin_0 + elem_voisin_1 - element;
1285 if (elem_voisin >= 0) // Not a boundary of the domain ?
1286 {
1287 potential += copie_valeurs_potentiel_elements(elem_voisin);
1288 p += copie_poids(elem_voisin);
1289 }
1290 }
1291 const double old_weight = copie_poids(element);
1292 // Do not change values for elements that contain an interface:
1293 if (p > 0. && old_weight == 0.)
1294 {
1295 // Decrease weight so that values on faces adjacent to elements
1296 // containing an interface are almost untouched.
1297 static double reduction_factor = 0.1;
1298 potential = potential * reduction_factor;
1299 p = p * reduction_factor;
1300 // Assign value
1301 valeurs_potentiel_elements(element) = potential;
1302 poids(element) = p;
1303 }
1304 }
1305 valeurs_potentiel_elements.echange_espace_virtuel();
1306 poids.echange_espace_virtuel();
1307 Debog::verifier("Navier_Stokes_FT_Disc::calculer_champ_forces_superficielles poids:", poids);
1308 }
1309 }
1310
1311 // I take the opportunity to store the Eulerian Interfacial Area...
1312 DoubleTab& interfacial_area = get_set_interfacial_area();
1313 interfacial_area = poids;
1314 {
1315 // Boucle sur les faces
1316 int face;
1317 DoubleTab& valeurs_potentiel_faces = potentiel_faces.valeurs();
1318 valeurs_potentiel_faces = 0.;
1319 const DoubleTab& valeurs_potentiel_elements = potentiel_elements.valeurs();
1320 const int nb_faces_pot = valeurs_potentiel_faces.dimension(0);
1321 const IntTab& face_voisins = le_dom_dis->face_voisins();
1322 for (face = 0; face < nb_faces_pot; face++)
1323 {
1324 double p = 0.; // Somme des poids des deux elements voisins
1325 double pot = 0.; // Somme des potentiels
1326 // Boucle sur les deux elements voisins de la face
1327 for (int i = 0; i < 2; i++)
1328 {
1329 int element = face_voisins(face, i);
1330 if (element >= 0)
1331 {
1332 // If neighbour exists (not a boundary face)
1333 p += poids(element);
1334 pot += valeurs_potentiel_elements(element);
1335 }
1336 }
1337 if (p > 0.)
1338 valeurs_potentiel_faces(face) = pot / p;
1339 }
1340 valeurs_potentiel_faces.echange_espace_virtuel();
1341 Debog::verifier("Navier_Stokes_FT_Disc::calculer_champ_forces_superficielles valeurs_potentiel_faces:", valeurs_potentiel_faces);
1342 }
1343
1344 // Derniere operation : calcul du champ
1345 // champ = potentiel_faces * gradient_indicatrice
1346 {
1347 const DoubleTab& valeurs_potentiel_faces = potentiel_faces.valeurs();
1348 const DoubleTab& valeurs_gradient_i = gradient_indicatrice.valeurs();
1349 DoubleTab& valeurs_champ = champ.valeurs();
1350 const int nb_compo = valeurs_champ.line_size(); // 1 for VDF, 3 for VEF
1351
1352 for (int face = 0; face < nb_faces; face++)
1353 {
1354 const double p = valeurs_potentiel_faces(face);
1355 for (int i = 0; i < nb_compo; i++)
1356 valeurs_champ(face, i) = valeurs_gradient_i(face, i) * p;
1357 }
1358
1359 valeurs_champ.echange_espace_virtuel();
1360 Debog::verifier("Navier_Stokes_FT_Disc::calculer_champ_forces_superficielles valeurs_champ:", valeurs_champ);
1361 }
1362}
1363
1364double compute_indic_ghost(const int elem, const int num_face, const double indic, const int normale_sortante_au_domaine, const Domaine_VF& domVF, const Maillage_FT_Disc& maillage)
1365{
1366 double indic_ghost = 0.;
1367 ArrOfDouble normale(3), normale_face(3); // Normale sortante de I=0 vers I=1
1368 const double face_surface = domVF.face_surfaces(num_face); // == 0. ? 1. : domVF.face_surfaces(num_face);
1369 if (est_egal(face_surface, 0.))
1370 {
1371 // On est sur l'axe de rotation du bidim_axi :
1372 return indic;
1373 }
1374 for (int i = 0; i < Objet_U::dimension; i++)
1375 normale_face[i] = domVF.face_normales(num_face, i) / face_surface; // pour normalisation
1376 const double norm = maillage.compute_normale_element(elem, true /* NORMALIZE */, normale);
1377 if (est_egal(norm, 0.))
1378 // L'element est pure, non traverse.
1379 return indic;
1380 // normale_sortante_au_domaine pour regler le signe du prodscal avec la normale_SORTANTE
1381 const double prodscal = normale_sortante_au_domaine * dotproduct_array(normale, normale_face);
1382 double val = 1. / sqrt(2.); // Could be improved. Corresponds to cubic cell and Indic=0.5
1383 if (prodscal < -val)
1384 indic_ghost = 0.;
1385 else if (prodscal < 0)
1386 // Linear interp to indic if 0
1387 indic_ghost = indic * (1 + prodscal / val); // prodscal is negative
1388 else if (prodscal < val)
1389 indic_ghost = indic + (1. - indic) * (prodscal / val);
1390 else
1391 indic_ghost = 1.;
1392 return indic_ghost;
1393}
1394
1395/*! @brief Calcul du gradient de l'indicatrice.
1396 *
1397 * Ce gradient est utilise pour calculer le second membre de l'equation de qdm,
1398 * contenant les termes de tension de surface.
1399 * En VEF, on commence par creer un champ P1B a partir du champ P0
1400 * et on calcule le gradient.
1401 * Design de classe a revoir pour separer VDF et VEF...
1402 *
1403 * Parametre : indicatrice
1404 * Signification : un champ aux elements (l'espace virtuel doit etre a jour)
1405 * Parametre : gradient_i
1406 * Signification : un champ discretise comme la vitesse dans lequel
1407 * on met gradient(indicatrice).
1408 *
1409 */
1410#include<Neumann_sortie_libre.h>
1411#include<Dirichlet.h>
1412#include<Dirichlet_homogene.h>
1413#include<Periodique.h>
1414#include<Symetrie.h>
1415#include<Sortie_libre_rho_variable.h>
1416void Navier_Stokes_FT_Disc::calculer_gradient_indicatrice(const Champ_base& indicatrice, const DoubleTab& distance_interface_sommets, Champ_base& gradient_i)
1417{
1418 if (gradient_i.que_suis_je() == "Champ_Fonc_Face")
1419 {
1420 gradient.calculer(indicatrice.valeurs(), gradient_i.valeurs());
1421 }
1422 else
1423 {
1424 int i;
1425 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
1426 const Domaine& domaine = domaine_vf.domaine();
1427 const int nb_elem_tot = domaine.nb_elem_tot();
1428 //const int nb_sommets = domaine.nb_som();
1429 //const IntTab & les_elems = domaine.les_elems();
1430 //const int nb_sommets_par_element = les_elems.dimension(1);
1431
1432 // Calcul d'une indicatrice p1bulle
1433 DoubleTab& indic_p1b = variables_internes().indicatrice_p1b->valeurs();
1434 // Verification du support du champ indicatrice_p1b
1435 if (dimension == 2 && indic_p1b.size_totale() != nb_elem_tot + domaine.nb_som_tot())
1436 {
1437 Cerr << "The method Navier_Stokes_FT_Disc::calculer_gradient_indicatrice is developped" << finl;
1438 Cerr << "only for an indicatrice field discretized like P0+P1 for the 2D dimension case." << finl;
1439 Cerr << "Please change the discretization." << finl;
1440 Process::exit();
1441 }
1442 if (dimension == 3 && indic_p1b.size_totale() != nb_elem_tot + domaine.nb_som_tot() + domaine.nb_aretes_tot() && indic_p1b.size_totale() != nb_elem_tot + domaine.nb_som_tot())
1443 {
1444 Cerr << "The method Navier_Stokes_FT_Disc::calculer_gradient_indicatrice is developped" << finl;
1445 Cerr << "only for an indicatrice field discretized like P0+P1 or P0+P1+Pa for the 3D dimension case." << finl;
1446 Cerr << "Please change the discretization." << finl;
1447 Process::exit();
1448 }
1449
1450 // Le champ P1bulle contient
1451 // nelements valeurs au centre des elements
1452 // suivies de
1453 // nsommets valeurs aux sommets
1454 indic_p1b = 0.;
1455 // Valeurs aux centres des elements = indicatrice a l'element
1456 // On recopie des valeurs sur les elements virtuels car on en
1457 // a besoin pour le calcul des valeurs aux sommets.
1458 for (i = 0; i < nb_elem_tot; i++)
1459 {
1460 indic_p1b(i) = indicatrice.valeurs()(i);
1461 }
1462
1463 indic_p1b.echange_espace_virtuel();
1464
1465 gradient.calculer(indic_p1b, gradient_i.valeurs());
1466 }
1467
1468 const bool ghost_correction = (variables_internes_.OutletCorrection_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::CORRECTION_GHOST_INDIC);
1469 if (ghost_correction)
1470 {
1471 // Correction du gradient a la ligne de contact :
1472 const DoubleTab& inco = indicatrice.valeurs();
1473 DoubleTab& resu = gradient_i.valeurs();
1474 const int nbdimr = resu.line_size();
1475 // assert_espace_virtuel_vect(inco);
1476 const Domaine_VF& zvf = ref_cast(Domaine_VF, domaine_dis());
1477 const Domaine_Cl_dis_base& zcldis = domaine_Cl_dis();
1478 const DoubleVect& face_surfaces = zvf.face_surfaces();
1479 const IntTab& face_voisins = domaine_dis().face_voisins();
1480
1481 double coef;
1482 int n0, n1;
1483 // Boucle sur les bords pour traiter les conditions aux limites
1484 int ndeb, nfin, num_face;
1485 for (int n_bord = 0; n_bord < zvf.nb_front_Cl(); n_bord++)
1486 {
1487 // pour chaque Condition Limite on regarde son type
1488 // Si face de Dirichlet ou de Symetrie on ne fait rien
1489 // Si face de Neumann on calcule la contribution au terme source
1490
1491 const Cond_lim& la_cl = zcldis.les_conditions_limites(n_bord);
1492 //Cerr << que_suis_je() << "::calculer_gradient_indicatrice() correction du gradient a la CL : " << la_cl.valeur() << finl;
1493 const Front_VF& le_bord = ref_cast(Front_VF, la_cl->frontiere_dis());
1494 ndeb = le_bord.num_premiere_face();
1495 nfin = ndeb + le_bord.nb_faces();
1496
1497 // Correction periodicite :
1498 if (sub_type(Periodique, la_cl.valeur()))
1499 {
1500 for (num_face = ndeb; num_face < nfin; num_face++)
1501 {
1502 n0 = face_voisins(num_face, 0);
1503 n1 = face_voisins(num_face, 1);
1504 if (!est_egal(n0, n1))
1505 {
1506 Cerr << "Periodic boundary condition with FT is not supported yet." << finl;
1507 Process::exit();
1508 }
1509 coef = face_surfaces(num_face); //*porosite_surf(num_face);
1510 for (int k = 0; k < nbdimr; k++)
1511 {
1512 const int normale_sortante_au_domaine = (n0 == -1) ? 1 : -1; // Si on a le ghost dans la case 0, la normale sortante pointe vers "x-", on met donc -1 dans normale_sortante_au_domaine
1513 const double dSk = normale_sortante_au_domaine * zvf.face_normales(num_face, k); // its magnitude is the surface.
1514 // dSk/coef should be either 0 or 1...
1515 assert(dSk / coef > -10. * Objet_U::precision_geom * Objet_U::precision_geom);
1516 Cerr << "Check if sign of nk is compatible with the expression" << finl;
1517 const double nk = zvf.face_normales(num_face, k);
1518 Cerr << "Check if sign of nk is compatible with the expression" << finl;
1519 Cerr << "nk=" << nk << finl;
1520 Cerr << "dSk=" << dSk << finl;
1521 Cerr << "num_face=" << num_face << finl;
1522 Cerr << "voisin 0/1 =" << zvf.face_voisins(num_face, 0) << " " << zvf.face_voisins(num_face, 1) << finl;
1523 Cerr << "code to validate" << finl;
1524 Process::exit();
1525 resu(num_face, k) = dSk * (inco(n1) - inco(n0));
1526 }
1527 }
1528 }
1529 else if (sub_type(Symetrie, la_cl.valeur())) { /* Do nothing */ }
1530 else if ((sub_type(Dirichlet, la_cl.valeur())) || (sub_type(Neumann_sortie_libre, la_cl.valeur())) || (sub_type(Dirichlet_homogene, la_cl.valeur())))
1531 {
1532 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
1533 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
1534 const Maillage_FT_Disc& maillage = eq_transport.maillage_interface();
1535 const Intersections_Elem_Facettes& intersections = maillage.intersections_elem_facettes();
1536 const ArrOfInt& index_elem = intersections.index_elem();
1537 for (num_face = ndeb; num_face < nfin; num_face++)
1538 {
1539 n0 = face_voisins(num_face, 0);
1540 n1 = face_voisins(num_face, 1);
1541 int elem = n0 + n1 + 1;
1542 //double surface_totale = 0.;
1543 for (int k = 0; k < nbdimr; k++)
1544 resu(num_face, k) = 0.;
1545 if (index_elem[elem] < 0)
1546 // element is pure
1547 continue;
1548 {
1549 const double indic = inco(elem);
1550 int normale_sortante_au_domaine = (n0 == -1) ? -1 : 1; // Si on a le ghost dans la case 0, la normale sortante pointe vers "x-", on met donc -1 dans normale_sortante_au_domaine
1551 const double indic_ghost = compute_indic_ghost(elem, num_face, indic, normale_sortante_au_domaine, zvf, maillage);
1552 //const double delta = zvdf.dim_elem(elem, orientation(num_face));
1553 //const double volume = zvdf.volumes(elem);
1554 coef = face_surfaces(num_face); //*porosite_surf(num_face);
1555 if (nbdimr == 1)
1556 {
1557 resu(num_face) = coef * normale_sortante_au_domaine * (indic_ghost - indic);
1558 assert(std::abs(coef - zvf.face_normales(num_face, zvf.orientation(num_face))) < Objet_U::precision_geom * Objet_U::precision_geom);
1559 }
1560 else
1561 {
1562 for (int k = 0; k < nbdimr; k++)
1563 {
1564 normale_sortante_au_domaine = 1; // En VEF, la normale dSk est toujours sortante?
1565 const double dSk = normale_sortante_au_domaine * zvf.face_normales(num_face, k); // its magnitude is the surface.
1566 // dSk/coef should be either 0 or 1...
1567 assert(dSk / coef > -10. * Objet_U::precision_geom * Objet_U::precision_geom);
1568 Cerr << "Check if sign of nk is compatible with the expression" << finl;
1569 Cerr << "nk=" << dSk / coef << finl;
1570 Cerr << "num_face=" << num_face << finl;
1571 Cerr << "voisin 0/1 =" << zvf.face_voisins(num_face, 0) << " " << zvf.face_voisins(num_face, 1) << finl;
1572 //Process::exit();
1573 resu(num_face, k) = dSk * (indic_ghost - indic);
1574 }
1575 }
1576 }
1577 }
1578 }
1579 // Fin de la boucle for
1580 }
1581 }
1582}
1583
1585{
1586 // Correction du gradient a la sortie (car celui-ci ne doit pas se baser sur la CL de pression):
1587 // On prefere mettre la valeur d'en face qu'une valeur abherante. -> comme cela, la divergence (plus tard) tendra vers 0)
1588 const Domaine_VF& zvf = ref_cast(Domaine_VF, domaine_dis());
1589 const IntTab& elem_faces = zvf.elem_faces();
1590 const IntTab& face_voisins = zvf.face_voisins();
1591 const Domaine_Cl_dis_base& zcldis = domaine_Cl_dis();
1592 // Boucle sur les bords pour traiter les conditions aux limites
1593 for (int n_bord = 0; n_bord < zvf.nb_front_Cl(); n_bord++)
1594 {
1595 // pour chaque Condition Limite on regarde son type
1596 // Si face de Dirichlet ou de Symetrie on ne fait rien
1597 // Si face de Neumann on calcule la contribution au terme source
1598 const Cond_lim& la_cl = zcldis.les_conditions_limites(n_bord);
1599 const Front_VF& le_bord = ref_cast(Front_VF, la_cl->frontiere_dis());
1600 const int ndeb = le_bord.num_premiere_face();
1601 const int nfin = ndeb + le_bord.nb_faces();
1602 if ((sub_type(Dirichlet, la_cl.valeur())) || (sub_type(Neumann_sortie_libre, la_cl.valeur())) || (sub_type(Dirichlet_homogene, la_cl.valeur())))
1603 {
1604 // const Domaine_VDF& zvdf = ref_cast(Domaine_VDF, zvf);
1605 // Cerr << que_suis_je() << "::calculer_delta_u_interface() correction de u0 gradient(phi) a la CL : " << la_cl.valeur() << finl;
1606 const int nb_faces = elem_faces.dimension(1);
1607 for (int num_face = ndeb; num_face < nfin; num_face++)
1608 {
1609 const int elem = face_voisins(num_face, 0) + face_voisins(num_face, 1) + 1;
1610 int idx_face_de_lelem = 0;
1611 for (idx_face_de_lelem = 0; idx_face_de_lelem < nb_faces; idx_face_de_lelem++)
1612 {
1613 if (elem_faces(elem, idx_face_de_lelem) == num_face)
1614 break; // Face found
1615 }
1616 if (nb_faces == idx_face_de_lelem)
1617 {
1618 Cerr << "Face is not found!! " << finl;
1619 Process::exit();
1620 }
1621 const int num_face_den_face = elem_faces(elem, (idx_face_de_lelem + Objet_U::dimension) % nb_faces);
1622 // const int elem_voisin = face_voisins(num_face_den_face,0)+face_voisins(num_face_den_face,1)-elem;
1623 // const double d_elem = dist[elem];
1624 // const double d_voisin = dist[elem_voisin];
1625 // const double delta = zvdf.dist_face(num_face, num_face_den_face , zvdf.orientation(num_face));
1626 // Il faudrait lineariser a l'ordre 2 car cela sert aussi au calcul de laplacien_d.
1627 // On extrapole le gradient mais on ne peut pas faire mieux car on a un petit stencil a 2 points sur dist.
1628 for (int c = 0; c < u0.line_size(); c++)
1629 u0(num_face, c) = u0(num_face_den_face, c); //*(1.+xxxxx);
1630 }
1631 }
1632 }
1633}
1634
1635int get_num_face_den_face(const int num_face, const int elem, const IntTab& elem_faces)
1636{
1637 const int nb_faces = elem_faces.dimension(1);
1638 int idx_face_de_lelem = 0;
1639 for (idx_face_de_lelem = 0; idx_face_de_lelem < nb_faces; idx_face_de_lelem++)
1640 {
1641 if (elem_faces(elem, idx_face_de_lelem) == num_face)
1642 break; // Face found
1643 }
1644 if (nb_faces == idx_face_de_lelem)
1645 {
1646 Cerr << "Face is not found!! " << finl;
1647 Process::exit();
1648 }
1649 const int num_face_den_face = elem_faces(elem, (idx_face_de_lelem + Objet_U::dimension) % nb_faces);
1650 return num_face_den_face;
1651}
1652
1653/*! @brief Calcul du saut de vitesse a l'interface du au changement de phase
1654 *
1655 * phase_pilote = -1: u-u0 = champ de vitesse de deplacement de l'interface
1656 * phase_pilote = 0 : u+u0 = champ de vitesse de la phase 0
1657 * phase_pilote = 1 : u+u0 = champ de vitesse de la phase 1
1658 * ordre = 0 : pas de prise en compte de la correction en courbure
1659 * ordre = 1 : prise en compte de la correction en courbure a l'ordre 1
1660 * ordre = 2 : prise en compte de la correction en courbure a l'ordre 2
1661 *
1662 */
1663void Navier_Stokes_FT_Disc::calculer_delta_u_interface(Champ_base& champ_u0, int phase_pilote, int ordre, const bool future_or_past)
1664{
1665 DoubleTab& u0 = future_or_past ? champ_u0.futur() : champ_u0.valeurs();
1666 const Fluide_Diphasique& fluide_dipha = fluide_diphasique();
1667 const Fluide_Incompressible& phase_0 = fluide_dipha.fluide_phase(0);
1668 const Fluide_Incompressible& phase_1 = fluide_dipha.fluide_phase(1);
1669 const DoubleTab& tab_rho_phase_0 = phase_0.masse_volumique().valeurs();
1670 const DoubleTab& tab_rho_phase_1 = phase_1.masse_volumique().valeurs();
1671 const double rho_0 = tab_rho_phase_0(0, 0);
1672 const double rho_1 = tab_rho_phase_1(0, 0);
1673 //const double delta_un_sur_rho = 1. / rho_1 - 1. / rho_0;
1674 const double un_sur_rho_0 = 1. / rho_0;
1675 const double un_sur_rho_1 = 1. / rho_1;
1676
1677 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
1678 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
1679
1680 // Pour permettre de calculer mpoint, mais sans l'utiliser pour le deplacement de l'interface,
1681 // il suffit de ne pas mettre le mot cle " equation_temperature_mpoint temp" dans le jdd.
1682 const int nn = variables_internes().second_membre_projection->valeurs().dimension(0); // nombre d'elems
1683 DoubleTab mpoint;
1684 mpoint.resize(nn);
1685 mpoint = 0.; //pour initialiser
1686
1687 if (variables_internes().ref_equation_mpoint_)
1688 {
1689 const DoubleTab& mp = variables_internes().ref_equation_mpoint_->get_mpoint();
1690 // Si inactif, on ne prend pas en compte sa contribution dans le calcul de delta_u:
1691 if (!variables_internes().mpoint_inactif)
1692 mpoint = mp;
1693 }
1694 if (variables_internes().ref_equation_mpoint_vap_)
1695 {
1696 const DoubleTab& mpv = variables_internes().ref_equation_mpoint_vap_->get_mpoint();
1697 // Si inactif, on ne prend pas en compte sa contribution dans le calcul de delta_u:
1698 if (!variables_internes().mpointv_inactif)
1699 mpoint += mpv;
1700 }
1701
1702 // GB2023.10.10 : I don't understand why I did a distinction only on phase_pilote == 1 (should be the same when it's phase_pilote == 0
1703 // for instance in the convection of temperature in the vapour...
1704 // BESIDES, the "switch (phase_pilote)" makes no sense if only one phase_pilote is used.
1705 if ((variables_internes().new_mass_source_))
1706 {
1707 assert(future_or_past == false); // Code jamais teste en true
1708 const DoubleTab& normale_elements = future_or_past ? eq_transport.get_normale_interface().futur() : eq_transport.get_normale_interface().valeurs();
1709 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
1710 const Champ_base& ch_indic = refeq_transport->get_indicatrice();
1711 const DoubleTab& indicatrice = ch_indic.valeurs();
1712 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
1713 const IntTab& face_voisins = domaine_vf.face_voisins();
1714 const int nb_faces = face_voisins.dimension(0);
1715 const int nb_elem_reel = interfacial_area.size_reelle();
1716 const int dim = Objet_U::dimension;
1717 const DoubleTab& xp = domaine_vf.xp(); // centres de gravite des elements
1718 const DoubleTab& xv = domaine_vf.xv(); // centres de gravite des faces.
1719
1720 // Auxiere velocity used to compute delt_U if shift_secmeme2 is activated
1721 DoubleTab u_aux(u0);
1722 u_aux = 0.;
1723 u_aux.echange_espace_virtuel();
1724
1725 u0 = 0.;
1727
1728 if (u0.line_size() == dim) // vef
1729 {
1730 Cerr << "Using option new_mass_source is not possible yet in VEF. Contact us. " << finl;
1731 Process::exit();
1732 for (int face = 0; face < nb_faces; face++)
1733 for (int j = 0; j < dim; j++)
1734 {
1735 double x = 0.; // a coder...
1736 u0(face, j) *= x;
1737 }
1738 }
1739 else
1740 {
1741 const Domaine_VDF& zvdf = ref_cast(Domaine_VDF, domaine_dis());
1742 const IntVect& orientation = zvdf.orientation();
1743 for (int face = 0; face < nb_faces; face++)
1744 {
1745 const int dir = orientation[face];
1746 // const double surface = domaine_vf.face_surfaces(face);
1747 const int e1 = face_voisins(face, 0);
1748 const int e2 = face_voisins(face, 1);
1749 const double xf = xv(face, dir);
1750 if (Objet_U::bidim_axi && fabs(xf) <= DMINFLOAT && (dir == 0))
1751 {
1752 // We are on the symmetry axis => surface = 0.;
1753 u0(face) = 0.; // there is no normal velocity at a symmetry axis
1754 continue;
1755 }
1756 //double x = 0.;
1757 double xx = 0.;
1758 double xx_aux = 0.;
1759 double nb_elem_effective = 0.;
1760 // Si on n'est pas au bord...
1761 if (e1 >= 0 && e1< nb_elem_reel )
1762 {
1763 const double nx = -normale_elements(e1, dir);
1764 //x = c*secmem2[e1]*nx;
1765 const double ai = interfacial_area(e1);
1766 // nx pointe vers le liquide (sortant de phase 0)
1767 if ((fabs(ai) > DMINFLOAT) && (fabs(nx) > DMINFLOAT))
1768 {
1769 // distance positive on the vapour side (chi_0 = 1, ie indicatrice = 0)
1770 // normale_elements: normalised normal vector of interface towards INSIDE bubble
1771 // nx is then negative => d and distance to interface are in inverse sign
1772 nb_elem_effective += 1.;
1773 const double d = (xf - xp(e1, dir)) * nx;
1774 switch(phase_pilote)
1775 {
1776 case -1:
1777 {
1778 // Champ de vitesse tel que u - u0 soit continu et egal a la
1779 // vitesse de deplacement de l'interface
1780 // !!!! ATTENTION: BY (BAD) CONVENTION !!!u_interface = u_ns - u_jump
1781 // !!!! SIGNE OK: WITH SECMEM2 AND DEPLCAEMENT OF MARKER
1783 {
1784 xx = - un_sur_rho_1 * mpoint[e1] * nx;
1785 xx_aux = - un_sur_rho_0 * mpoint[e1] * nx;
1786 }
1787 else
1788 {
1789 const double un_sur_rho = (d > 0.) ? un_sur_rho_0 : un_sur_rho_1;
1790 xx = - un_sur_rho * mpoint[e1] * nx;
1791 //Cerr << "diff " << x << " " << xx << finl;
1792 }
1793 break;
1794 }
1795 case 0:
1796 {
1797 // Champ de vitesse tel que u + u0 soit continu et
1798 // u+u0 = la vitesse de la phase 0 dans la phase 0
1800 {
1801 xx = (un_sur_rho_1 - un_sur_rho_0)* mpoint[e1] * nx;
1802 xx_aux = 0.;
1803 }
1804 else
1805 {
1806 const double p = (d > 0.) ? 0. : (un_sur_rho_1 - un_sur_rho_0);
1807 xx = p * mpoint[e1] * nx;
1808 //Cerr << "face " << face << " " << xx << finl;
1809 }
1810 break;
1811 }
1812 case 1:
1813 {
1814 // Champ de vitesse tel que u + u0 soit continu et
1815 // u+u0 = la vitesse de la phase 1 dans la phase 1
1817 {
1818 xx = 0.;
1819 xx_aux = (un_sur_rho_0 - un_sur_rho_1)* mpoint[e1] * nx;
1820 }
1821 else
1822 {
1823 const double p = (d > 0.) ? (un_sur_rho_0 - un_sur_rho_1) : 0.;
1824 xx = p * mpoint[e1] * nx;
1825 //Cerr << "face " << face << " " << xx << finl;
1826 }
1827 break;
1828 }
1829 default:
1830 Cerr << "Error for the method Navier_Stokes_FT_Disc::calculer_delta_u_interface phase_pilote" << finl;
1831 Process::exit();
1832 }
1833 }
1834 }
1835 // We ADD contribution of e2 if not a boundary to xx
1836 if (e2 >= 0 && e1< nb_elem_reel)
1837 {
1838 const double nx = -normale_elements(e2, dir);
1839 //x += c*secmem2[e2]*normale_elements(e2, dir);
1840 const double ai = interfacial_area(e2);
1841 if ((fabs(ai) > DMINFLOAT) && (fabs(nx) > DMINFLOAT))
1842 {
1843 nb_elem_effective += 1.;
1844 // distance positive on the vapour side (chi_0 = 1, ie indicatrice = 0)
1845 const double d = (xf - xp(e2, dir)) * nx;
1846 switch(phase_pilote)
1847 {
1848 case -1:
1849 {
1850 // Champ de vitesse tel que u - u0 soit continu et egal a la
1851 // vitesse de deplacement de l'interface
1852 // !!!! ATTENTION: BY (BAD) CONVENTION !!!u_interface = u_ns - u_jump
1853 // !!!! SIGNE OK: WITH SECMEM2 AND DEPLCAEMENT OF MARKER
1855 {
1856 xx += -un_sur_rho_1 * mpoint[e2] * nx;
1857 xx_aux += -un_sur_rho_0 * mpoint[e2] * nx;
1858 }
1859 else
1860 {
1861 const double un_sur_rho = (d > 0.) ? un_sur_rho_0 : un_sur_rho_1;
1862 xx += -un_sur_rho * mpoint[e2] * nx;
1863 //Cerr << "diff2 " << x << " " << xx << finl;
1864 }
1865 break;
1866 }
1867 case 0:
1868 {
1869 // Champ de vitesse tel que u + u0 soit continu et
1870 // u+u0 = la vitesse de la phase 0 dans la phase 0
1872 {
1873 xx += (un_sur_rho_1 - un_sur_rho_0) * mpoint[e2] * nx;
1874 xx_aux += 0.;
1875 }
1876 else
1877 {
1878 const double p = (d > 0.) ? 0. : (un_sur_rho_1 - un_sur_rho_0);
1879 xx += p * mpoint[e2] * nx;
1880 //Cerr << "face2 " << face << " " << xx << finl;
1881 }
1882 break;
1883 }
1884 case 1:
1885 {
1886 // Champ de vitesse tel que u + u0 soit continu et
1887 // u+u0 = la vitesse de la phase 1 dans la phase 1
1889 {
1890 xx += 0.;
1891 xx_aux += (un_sur_rho_0 - un_sur_rho_1) * mpoint[e2] * nx;
1892 }
1893 else
1894 {
1895 const double p = (d > 0.) ? (un_sur_rho_0 - un_sur_rho_1) : 0.;
1896 xx += p * mpoint[e2] * nx;
1897 }
1898 //Cerr << "face2 " << face << " " << xx << finl;
1899 break;
1900 }
1901 default:
1902 Cerr << "Error for the method Navier_Stokes_FT_Disc::calculer_delta_u_interface phase_pilote" << finl;
1903 Process::exit();
1904 }
1905 }
1906
1907 }
1908 xx = est_egal(nb_elem_effective, 0.) ? 0. : xx/ nb_elem_effective;
1909 xx_aux = est_egal(nb_elem_effective, 0.) ? 0. : xx_aux/ nb_elem_effective;
1910 u0(face) = xx;
1911 u_aux(face) = xx_aux;
1912 }
1913
1914 // GB 2023.10.10. On inclined interfaces, we realised that some configurations (interface topologies)
1915 // can lead to using faces where the velocity jump delta_u has not been extended
1916 // for the velocity interpolation at the markers. This leads to a misprediction of delta_u_i
1917 // u0.echange_espace_virtuel();
1918 u_aux.echange_espace_virtuel();
1919
1920 // Pour parcourir les elements qui sont coupes par une facette "facette":
1921 const Maillage_FT_Disc& mesh = eq_transport.maillage_interface();
1922 const Intersections_Elem_Facettes& intersections = mesh.intersections_elem_facettes();
1923 const ArrOfInt& index_facette = intersections.index_facette();
1924 const Domaine_VF& zvf = ref_cast(Domaine_VF, domaine_dis());
1925 const IntTab& elem_faces = zvf.elem_faces();
1926 const int nb_faces_per_elem = elem_faces.line_size();
1927 for (int facette = 0; facette < mesh.nb_facettes(); facette++)
1928 {
1929 int index = index_facette[facette];
1930 while (index >= 0)
1931 {
1932 const Intersections_Elem_Facettes_Data& data = intersections.data_intersection(index);
1933 const int elem = data.numero_element_;
1934 // elem is mixed (crossed by an interface)
1935 // loop on its faces to find a neighbour:
1936 for (int idx_face_de_lelem = 0; idx_face_de_lelem < nb_faces_per_elem; idx_face_de_lelem++)
1937 {
1938 const int face_of_elem = elem_faces(elem, idx_face_de_lelem);
1939 // The neighbour :
1940 const int e1 = face_voisins(face_of_elem, 0) + face_voisins(face_of_elem, 1) - elem;
1941 if ((e1 >= 0) && (fabs(interfacial_area(e1)) < DMINFLOAT))
1942 {
1943 // e1 exists (ie face is not a boundary) and is pure
1944 // face_of_elem is between elem and e1
1945 const int num_face_den_face = get_num_face_den_face(face_of_elem, e1, elem_faces);
1946 if (fabs(u0(num_face_den_face)) < DMINFLOAT)
1947 {
1948 // There is no velocity on the face in front of "face_of_e1" (which is adjacent to a mixed element)
1949 // we should extrapolate the other one there.
1950 // (ie we assume that this other face was zero because the 2nd neighbour (rang 2) is pure too.
1952 u0(num_face_den_face) = est_egal(indicatrice(e1), 0.) ? u_aux(face_of_elem) : u0(face_of_elem);
1953 else
1954 u0(num_face_den_face) = u0(face_of_elem);
1955 }
1956 }
1957 }
1958 index = data.index_element_suivant_;
1959 }
1960 }
1961 // u0.echange_espace_virtuel();
1962 // we do a EV_SOMME_ECHANGE become initially. virtual part are ALL zero! DO NOT change virtual for the moment
1964 }
1965 return;
1966 }
1967
1968 // Distance a l'interface discretisee aux elements:
1969 assert(future_or_past == false); // Code jamais teste en true
1970 const DoubleTab& dist = future_or_past ? eq_transport.get_distance_interface().futur() : eq_transport.get_distance_interface().valeurs();
1971 DoubleTab phi = calculer_div_normale_interface().valeurs();
1972 {
1973 const int n = phi.dimension(0);
1974 for (int i = 0; i < n; i++)
1975 {
1976 double d = dist(i);
1977 double p = 0.;
1978 if (d >= -1e20)
1979 {
1980 const double div_n = phi(i);
1981 // Distance calculee pour cet element ?
1982 // Calcul de la fonction phi pour cet element :
1983 const double mp = mpoint[i];
1984 switch(ordre)
1985 {
1986 case 0:
1987 // Pas de prise en compte de la correction en courbure
1988 p = d * mp;
1989 break;
1990 case 1:
1991 // Prise en compte de la correction en courbure a l'ordre 1
1992 p = d * (1. - 0.5 * div_n * d) * mp;
1993 break;
1994 case 2:
1995 // Prise en compte de la correction en courbure a l'ordre 2
1996 p = d * (1. - 0.5 * div_n * d + div_n * div_n * d * d / 6.) * mp;
1997 break;
1998 default:
1999 Cerr << "Error for the method Navier_Stokes_FT_Disc::calculer_delta_u_interface ordre" << finl;
2000 Process::exit();
2001 }
2002 switch(phase_pilote)
2003 {
2004 case -1:
2005 // Champ de vitesse tel que u - u0 soit continu et egal a la !!!! ATTENTION: BY (BAD) CONVENTION !!!u_interface = u_ns - u_jump
2006 // vitesse de deplacement de l'interface !!!! SIGNE OK: WITH SECMEM2 AND DEPLCAEMENT OF MARKER
2007 if (d < 0)
2008 p *= un_sur_rho_0;
2009 else
2010 p *= un_sur_rho_1;
2011 break;
2012 case 0:
2013 // Champ de vitesse tel que u + u0 soit continu et
2014 // u+u0 = la vitesse de la phase 0 dans la phase 0
2015 if (d < 0)
2016 p = 0.; // dans la phase 0
2017 else
2018 p *= (un_sur_rho_0 - un_sur_rho_1); // GB BugFix 2020/10/09
2019 break;
2020 case 1:
2021 // Champ de vitesse tel que u + u0 soit continu et
2022 // u+u0 = la vitesse de la phase 1 dans la phase 1
2023 if (d < 0)
2024 p *= (un_sur_rho_1 - un_sur_rho_0); // GB BugFix 2020/10/09
2025 else
2026 p = 0.; // dans la phase 1
2027 break;
2028 default:
2029 Cerr << "Error for the method Navier_Stokes_FT_Disc::calculer_delta_u_interface phase_pilote" << finl;
2030 Process::exit();
2031 }
2032 }
2033 phi(i) = p;
2034 }
2036 }
2037
2038 // Gradient de phi:
2039 if (champ_u0.que_suis_je() == "Champ_Face")
2040 {
2041 gradient.calculer(phi, u0);
2043 }
2044 else
2045 {
2046 Cerr << "Error for the method Navier_Stokes_FT_Disc::calculer_delta_u_interface\n" << " Non code pour " << champ_u0.que_suis_je() << finl;
2047 Process::exit();
2048 }
2049
2050 // On annule la vitesse calculee pour les faces adjacentes a un element
2051 // invalide.
2052 {
2053 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
2054 const IntTab& face_voisins = domaine_vf.face_voisins();
2055 const int nb_faces = domaine_vf.nb_faces();
2056 ;
2057 for (int i = 0; i < nb_faces; i++)
2058 {
2059 for (int j = 0; j < 2; j++)
2060 {
2061 const int elem = face_voisins(i, j);
2062 if (elem >= 0 && dist(elem) < -1e20)
2063 {
2064 u0(i) = 0.;
2065 break;
2066 }
2067 }
2068 }
2069 }
2071 solveur_masse->appliquer(u0);
2072}
2073
2075{
2076
2077 DoubleTab secmem2_loc(secmem2);
2078 DoubleTab secmem2_tmp(secmem2);
2079 secmem2_tmp = 0.;
2080 secmem2_loc = 0.;
2081 secmem2_tmp.echange_espace_virtuel();
2082 secmem2_loc.echange_espace_virtuel();
2083
2084 // const DoubleTab& interfacial_area = variables_internes().ai.valeur().valeurs();
2085 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
2086 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport;
2087 // use indicatrice to tell if mixed cell BUT NOT ai
2088 // as ai and indicatrice may not always at the same time, i.e., NO FULL EXPLICIT
2089 const DoubleTab& indicatrice = eq_transport.inconnue().valeurs();
2090 // TODO : GB : Voir avec Linkai pourquoi inconnue() plutot que get_indicatrice()
2091 // const DoubleTab& indicatrice = eq_transport.get_indicatrice().valeurs();
2092
2093 const Domaine_VDF& zvdf = ref_cast(Domaine_VDF, domaine_dis());
2094 const IntVect& orientation = zvdf.orientation();
2095 const DoubleTab& normale_elements = eq_transport.get_normale_interface().valeurs();
2096
2097 DoubleTab nb_voisions_vap(secmem2);
2098 nb_voisions_vap = 0.;
2099 nb_voisions_vap.echange_espace_virtuel();
2100
2101 // const DoubleTab& interfacial_area = variables_internes().ai.valeur().valeurs();
2102 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
2103 const IntTab& face_voisins = domaine_vf.face_voisins();
2104 const IntTab& elem_faces = domaine_vf.elem_faces();
2105
2106 // const int nb_elem = secmem2.dimension; // nombre d'elems
2107 const int nb_elem = elem_faces.dimension(0);
2108
2109 ArrOfInt list_bc_elems; //elem at BC axis
2110 ArrOfInt list_bc_faces; //face at BC axis
2111 for (int i=0; i< nb_elem; i++)
2112 {
2113 if (!est_egal(indicatrice[i],0) && !est_egal(indicatrice[i],1)) // inteface cells ai non null
2114 {
2115 secmem2_loc[i] = secmem2[i]; // secmem2_loc: the same value of secmem2 except the virtual cells (where, value is 0)
2116 const int nb_voisins = elem_faces.dimension(1);
2117 for (int iv=0; iv < nb_voisins; iv++)
2118 {
2119 const int num_face2 = elem_faces(i,iv);
2120 int elem_voisin = face_voisins(num_face2, 0) + face_voisins(num_face2, 1) - i;
2121 if (elem_voisin<0) // We hit a CL
2122 {
2123 // if they are at BC axis
2124 list_bc_faces.append_array(num_face2);
2125 list_bc_elems.append_array(i);
2126 continue;
2127 }
2128 if ( est_egal(indicatrice[elem_voisin],0) )
2129 {
2130 nb_voisions_vap[i] += 1;
2131 }
2132 }
2133
2134 }
2135 }
2136
2137 nb_voisions_vap.echange_espace_virtuel();
2138
2139
2140 ArrOfInt list_bc_elems_nv; //neighbor elem of interface cells at BC and not vapeur neighboor, do not redistribute it
2141
2142 // try to find interface cell (having vapor neighbor) in another direction
2143 for (int iv=0; iv < list_bc_elems.size_array(); iv++)
2144 {
2145 const int elem_loc = list_bc_elems[iv];
2146 if (est_egal(nb_voisions_vap[elem_loc],0))
2147 {
2148 const int face_loc = list_bc_faces[iv];
2149 const int dir = orientation[face_loc];
2150 const double nx = normale_elements(elem_loc, 1-dir);
2151
2152 int a = (-nx< 0) ? 0 : Objet_U::dimension;
2153 int b = 1-dir; // if the normal is along y, korient=1 and we want 1 for x
2154 int iface = a + b;
2155
2156 int num_face_next = elem_faces(elem_loc,iface);
2157 int elem = elem_loc;
2158
2159 bool found_elem = 0;
2160
2161 for (int count = 0; count<5; count++)
2162 {
2163 elem = face_voisins(num_face_next, 0) + face_voisins(num_face_next, 1) - elem;
2164 if (elem==-1) // we reach another BC
2165 {
2166 break;
2167 }
2168 if (!est_egal(nb_voisions_vap[elem],0)) // can not find another receive cell after 5 iteration
2169 {
2170 secmem2_loc[elem] += secmem2_loc[elem_loc];
2171 secmem2_loc[elem_loc] = 0.;
2172 found_elem = 1;
2173 break;
2174 }
2175 }
2176
2177 if (!found_elem)
2178 {
2179 list_bc_elems_nv.append_array(elem_loc);
2180 }
2181 }
2182 }
2183
2184
2185 for (int iv=0; iv < list_bc_elems_nv.size_array(); iv++)
2186 {
2187 const int elem_loc = list_bc_elems_nv[iv];
2188 nb_voisions_vap[elem_loc] += 1;
2189 }
2190
2191
2192
2193 MD_Vector_tools::echange_espace_virtuel(secmem2_loc, MD_Vector_tools::EV_SOMME); // we do a EV_SOMME_ECHANGE become initially. virtual part are ALL zero! DO NOT change virtual for the moment
2194
2195 nb_voisions_vap.echange_espace_virtuel();
2196
2197
2198
2199 double sum_loc = 0.;
2200 double sum_orig = 0.;
2201
2202 // check all 1. secmem2_loc is restributed; 2. check som of secmem2_tmp and secmem2 are the same
2203 // check: all secmem2_loc[i] != 0 => nb_voisions_vap[i] !=0
2204 for (int i=0; i< nb_elem; i++)
2205 {
2206 sum_loc += secmem2_loc[i];
2207 sum_orig += secmem2[i];
2208 }
2209
2210 // Reduce 2 mp_sum calls to 1 by using mp_sum_for_each
2211 Process::mp_sum_for_each(sum_loc, sum_orig);
2212
2213 if (abs(sum_loc-sum_orig) > 0.01*abs(sum_orig) )
2214 {
2215 Cerr<< "Balance of seconde memeber 2: "<<finl;
2216 Cerr<< " sum been pre-restributed-0: "<< sum_loc << " origianl sum: "<<sum_orig<<finl;
2217 Process::exit("[Navier_Stokes_FT_Disc::shift_secmem2]: !!! shift second member failed");
2218 }
2219
2220 // another EV_somme_echange will be used in the following, make sur the virtual part is 0.
2221 const int nb_reel = secmem2_loc.size_reelle();
2222 const int nb_tot = secmem2_loc.size_totale();
2223 for (int i=nb_reel; i<nb_tot; i++)
2224 secmem2_loc[i] = 0.;
2225
2226 // verifier coherence: for a interfaciel cell has no pure vapor neighbor (its neighbor are all interface)
2227 // check at least one of these cells has pure vapor neighbor cells
2228 for (int i=0; i< nb_elem; i++)
2229 {
2230 if (!est_egal(indicatrice[i],0) && !est_egal(indicatrice[i],1)) // interface cells = ai non null
2231 {
2232 if ( est_egal(nb_voisions_vap[i],0) && !est_egal(secmem2_loc[i],0)) // interface cell without vap neighber
2233 {
2234 int nb_voisin_mix_usefuel = 0;
2235 ArrOfInt list_useful_elems;
2236 const int nb_voisins = elem_faces.dimension(1);
2237 for (int iv=0; iv < nb_voisins; iv++)
2238 {
2239 const int num_face2 = elem_faces(i,iv);
2240 int elem_voisin = face_voisins(num_face2, 0) + face_voisins(num_face2, 1) - i;
2241 if (elem_voisin<0) // We hit a CL
2242 continue;
2243
2244 if (!est_egal(nb_voisions_vap[elem_voisin],0) )
2245 {
2246 nb_voisin_mix_usefuel += 1;
2247 list_useful_elems.append_array(elem_voisin);
2248 }
2249 }
2250
2251
2252 if (est_egal(nb_voisin_mix_usefuel,0))
2253 {
2254 Process::exit( "[Navier_Stokes_FT_Disc::shift_secmem2]: !!! ALL neigher of one elem has no vap neighers" );
2255 }
2256
2257 for (int iv=0; iv < list_useful_elems.size_array(); iv++)
2258 {
2259 const int elem_loc = list_useful_elems[iv];
2260 secmem2_loc[elem_loc] += secmem2_loc[i]/nb_voisin_mix_usefuel;
2261 }
2262 secmem2_loc[i] = 0;
2263 }
2264 }
2265 }
2266
2267 // secmem2_loc.echange_espace_virtuel();
2268 // MD_Vector_tools::echange_espace_virtuel(secmem2_loc, EV_SOMME)
2270
2271 sum_loc = 0.;
2272 // check all 1. secmem2_loc is restributed; 2. check som of secmem2_tmp and secmem2 are the same
2273 // check: all secmem2_loc[i] != 0 => nb_voisions_vap[i] !=0
2274 for (int i=0; i< nb_elem; i++)
2275 {
2276 // if (!est_egal(indicatrice[i],0) && !est_egal(indicatrice[i],1)) // interface cells = ai non null
2277 sum_loc += secmem2_loc[i];
2278 if ( (!est_egal(secmem2_loc[i],0)) && est_egal(nb_voisions_vap[i],0))
2279 {
2280 Cerr<< " secmem2_loc non nulle: "<< secmem2_loc[i] << " nb_voisions_vap "<<nb_voisions_vap[i]<<finl;
2281 Process::exit("[Navier_Stokes_FT_Disc::shift_secmem2]: !!! shift second member failed");
2282 }
2283 }
2284
2285 sum_loc = Process::mp_sum (sum_loc);
2286
2287 if (abs(sum_loc-sum_orig) > 0.01*abs(sum_orig) )
2288 {
2289 Cerr<< "Balance of seconde memeber 2: "<<finl;
2290 Cerr<< " sum been pre-restributed-1: "<< sum_loc << " origianl sum: "<<sum_orig<<finl;
2291 Process::exit("[Navier_Stokes_FT_Disc::shift_secmem2]: !!! shift second member failed");
2292 }
2293
2294
2295 // inline void call_echange_espace_virtuel(TRUSTVect<_TYPE_>& v, MD_Vector_tools::Operations_echange opt)
2296 /**
2297 {
2298 if (secmem2_loc.get_md_vector().non_nul())
2299 {
2300 statistiques().begin_count(echange_vect_counter_);
2301 MD_Vector_renumber::echange_espace_virtuel_(secmem2_loc.get_md_vector(), secmem2_loc, Echange_EV_Options(Echange_EV_Options::SUM));
2302 statistiques().end_count(echange_vect_counter_);
2303 }
2304 //else Cerr << "Warning: A call to ::echange_espace_virtuel() is done on a non-distributed vector." << finl; /Process::exit();
2305 }
2306 */
2307
2308
2309 // shift secmem2 to to pure vapor cells
2310 for (int i=0; i< nb_elem; i++)
2311 {
2312 if (!est_egal(indicatrice[i],0) && !est_egal(indicatrice[i],1)) // interface cells = ai non null
2313 {
2314 if ( !est_egal(nb_voisions_vap[i],0) ) // interface cell with vap neighber
2315 {
2316 const int nb_voisins = elem_faces.dimension(1);
2317 for (int iv=0; iv < nb_voisins; iv++)
2318 {
2319 const int num_face2 = elem_faces(i,iv);
2320 int elem_voisin = face_voisins(num_face2, 0) + face_voisins(num_face2, 1) - i;
2321 if (elem_voisin<0) // We hit a CL
2322 continue;
2323 if (est_egal(indicatrice[elem_voisin],0) )
2324 {
2325 secmem2_tmp[elem_voisin] += secmem2_loc[i]/nb_voisions_vap[i];
2326 }
2327 }
2328 // secmem2_loc[i] = 0;
2329 }
2330 }
2331 }
2332
2333 for (int iv=0; iv < list_bc_elems_nv.size_array(); iv++)
2334 {
2335 const int elem_loc = list_bc_elems_nv[iv];
2336 secmem2_tmp[elem_loc] += secmem2_loc[elem_loc]/nb_voisions_vap[elem_loc];
2337 }
2338
2340
2341
2342 double sum_tmp = 0.;
2343
2344
2345 // check all 1. secmem2_loc is restributed; 2. check som of secmem2_tmp and secmem2 are the same
2346 for (int i=0; i< nb_elem; i++)
2347 {
2348 sum_tmp += secmem2_tmp[i]; // not in interface cell anymore
2349 }
2350
2351 sum_tmp = Process::mp_sum (sum_tmp);
2352
2353
2354 if (abs(sum_tmp-sum_orig) > 0.01*abs(sum_orig) )
2355 {
2356 Cerr<< "Balance of seconde memeber 2: "<<finl;
2357 Cerr<< " sum been restributed: "<< sum_tmp << " origianl sum: "<<sum_orig<<finl;
2358 Process::exit("[Navier_Stokes_FT_Disc::shift_secmem2]: !!! shift second member failed");
2359 }
2360 else
2361 secmem2 = secmem2_tmp;
2362}
2363
2364
2365double calculer_indicatrice_face_privilegie_pure(const DoubleTab& indicatrice, const IntTab& face_voisins, const int num_face)
2366{
2367 const int elem0 = face_voisins(num_face, 0);
2368 const int elem1 = face_voisins(num_face, 1);
2369 double indic_0 = (elem0 >= 0) ? indicatrice[elem0] : indicatrice[elem1];
2370 double indic_1 = (elem1 >= 0) ? indicatrice[elem1] : indicatrice[elem0];
2371 // Decentrage : si la face est adjacente a une face monophasique,
2372 // prendre la phase pure pour toute la face:
2373 if (indic_0 == 0. || indic_0 == 1.)
2374 indic_1 = indic_0;
2375 if (indic_1 == 0. || indic_1 == 1.)
2376 indic_0 = indic_1;
2377 const double indic_face = (indic_0 + indic_1) * 0.5;
2378 return indic_face;
2379}
2380
2381double calculer_indicatrice_face_based_on_ai(const DoubleTab& indicatrice, const DoubleTab& indicatrice_faces, const IntTab& face_voisins, const Domaine_VF& domaine_vf,
2382 const DoubleTab& interfacial_area, const DoubleTab& normale_elements, const int face, const int dim)
2383{
2384 double indic_face = 0.;
2385 int v;
2386 for (v = 0; v < 2; v++)
2387 {
2388 const int elem = face_voisins(face, v);
2389 if (elem >= 0)
2390 {
2391 // If a neighbour is pure, we use that value at the face and stop further calculation.
2392 const double indic = indicatrice[elem]; // This is the value of chi_1 (ie =1 in phase 1!)
2393 //if (indic == 0. || indic == 1.)
2394 if (indic <= 5e-3 || indic >= 1. - 5e-3)
2395 {
2396 indic_face = 1 - indic; // We want chi of phase_0
2397 break;
2398 }
2399 else
2400 {
2401 const double surface = domaine_vf.face_surfaces(face);
2402 //const DoubleVect& volumes = domaine_vf.volumes();
2403 const double ai = interfacial_area(elem); // nx pointe vers le liquide (sortant de phase 0)
2404 if (fabs(ai) > DMINFLOAT)
2405 {
2406 double x = 0.;
2407 if (dim > 1) // dim==1 en VDF; sinon VEF
2408 {
2409 for (int j = 0; j < dim; j++)
2410 {
2411 const double dSj = domaine_vf.face_normales(face, j);
2412 const double nx = normale_elements(elem, j);
2413 // produit scalaire :
2414 x += dSj * nx;
2415 x *= ai / surface;
2416 // Que/comment Choisir?
2417 indic_face += 1 - x;
2418 // indic_face += x;
2419 Cerr << "Never tested. To be verified. It should depend on a scalar product with the vect (xp-xv)" << finl;
2420 Process::exit();
2421 }
2422 }
2423 else
2424 {
2425 // En VDF, l'acces a orientation permet d'eviter le calcul du produit scalaire.
2426 const Domaine_VDF& zvdf = ref_cast(Domaine_VDF, domaine_vf);
2427 const IntVect& orientation = zvdf.orientation();
2428 const int dir = orientation[face];
2429 const double nx = normale_elements(elem, dir);
2430 // Assumes a cube, nx larger than diag means we can use the method rather safely
2431 if (nx > 0.707)
2432 {
2433 x = ai / surface * nx;
2434 // On suppose que v0 est a gauche et v1 a droite!!!
2435 if (v == 0)
2436 indic_face += 1 - x; // This way, we build chi_0 because normale points towards chi_1
2437 else
2438 indic_face += x;
2439 }
2440 else
2441 {
2442 // L'interface croise probablement la face d'en face et la methode ne marche plus.
2443 // on revient a la methode classique :
2444 indic_face += (1. - indicatrice_faces[face]);
2445 }
2446 }
2447 }
2448 else
2449 {
2450 Cerr << " WTF, c'est impossible" << finl;
2451 Process::exit();
2452 }
2453 }
2454 }
2455 else
2456 {
2457 // The only neighbour to the face :
2458 const int elem_voisin = face_voisins(face, 1 - v); // The other one is accessed by 1-v
2459 const double indic = indicatrice[elem_voisin]; // This is the value of chi_1 (ie =1 in phase 1!)
2460 indic_face = 1 - indic; // We want chi of phase_0
2461 break; // c'est important pour le if d'apres.
2462 }
2463 }
2464 if (v == 2)
2465 // On n'a pas touche le break, on est donc passe 2 fois. donc :
2466 indic_face *= 0.5;
2467
2468 // assert((indic_face >=0) && (indic_face<=1.));
2469 // ca arrive des petits derapages..
2470 if (indic_face < 0)
2471 indic_face = 0.;
2472 if (indic_face > 1.)
2473 indic_face = 1.;
2474 return indic_face;
2475}
2476
2477void correct_indicatrice_face_bord(const int num_face, const Maillage_FT_Disc& maillage, const Domaine_VF& zvf, const IntTab& face_voisins, const DoubleTab& indicatrice, const bool privilegie_pure,
2478 double& indic_face)
2479{
2480 // Correction de l'indicatrice face :
2481 const int n0 = face_voisins(num_face, 0);
2482 const int n1 = face_voisins(num_face, 1);
2483 if ((n0 == -1) or (n1 == -1))
2484 {
2485 // On a boundary face
2486 const int outward_normal = (n0 == -1) ? -1 : 1;
2487 const int elem = n0 + n1 + 1;
2488 const double indic_ghost = compute_indic_ghost(elem, num_face, indicatrice(elem), outward_normal, zvf, maillage);
2489 indic_face = indic_ghost;
2490 if ((privilegie_pure) && (est_egal(indicatrice(elem), 1.) || est_egal(indicatrice(elem), 0.)))
2491 {
2492 indic_face = indicatrice(elem);
2493 }
2494 }
2495}
2496
2497// Calcul de l'integrale de dI_dt sur chaque element du maillage.
2498// Le tableau dI_dt doit avoir la bonne structure. L'espace virtuel est
2499// mis a jour. La method n'est plus const a cause des options
2500// INTERP_MODIFIEE et AI_BASED qui recalculent indicatrice_faces.
2501// !!! Note L. WEI 09.01.2025
2502// !!! phase 0: vapour, phase 1: liquid; I: phase indicator of LIQUID
2503// !!! the real eq. solved : dI/dt = div.[(rho_v/(rho_v-rho_l) - I) U] !!! Not the same as in existed paper/draft ...
2504void Navier_Stokes_FT_Disc::calculer_dI_dt(DoubleVect& dI_dt, const DoubleTab& tab_vitesse) //const
2505{
2506 const double rho_0 = fluide_diphasique().fluide_phase(0).masse_volumique().valeurs()(0, 0);
2507 const double rho_1 = fluide_diphasique().fluide_phase(1).masse_volumique().valeurs()(0, 0);
2508 const double delta_rho = rho_0 - rho_1;
2509
2510 double rho_0_sur_delta_rho = 0.;
2511 if (delta_rho != 0)
2512 rho_0_sur_delta_rho = rho_0 / delta_rho;
2513
2514 const IntTab& face_voisins = domaine_dis().face_voisins();
2515
2516 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
2517 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
2518 const DoubleTab& indicatrice = eq_transport.inconnue().valeurs();
2519 const Maillage_FT_Disc& maillage = eq_transport.maillage_interface();
2520 const Domaine_VF& domVF = ref_cast(Domaine_VF, domaine_dis());
2521 //const IntVect& orientation = ref_cast(Domaine_VF, domaine_dis()).orientation();
2522 // const DoubleTab& indicatrice = variables_internes().ref_eq_interf_proprietes_fluide->inconnue().valeurs();
2523 DoubleTab tmp(tab_vitesse); // copie du tableau des vitesses de ns
2524 const int dim = tab_vitesse.line_size();
2525 const int n = tab_vitesse.dimension(0);
2526
2527 // On cree un tableau avec la meme structure que la pression
2528 DoubleTab resu;
2529 resu.copy(variables_internes().second_membre_projection->valeurs(), RESIZE_OPTIONS::NOCOPY_NOINIT);
2530 resu = 0.;
2531
2532 //On utilise un operateur de divergence temporaire et pas celui porte par l equation
2533 //pour ne pas modifier les flux_bords_ rempli au cours de Navier_Stokes_std::mettre_a_jour
2534 Operateur_Div div_tmp;
2535 div_tmp.associer_eqn(*this);
2536 div_tmp.typer();
2537 div_tmp.l_op_base().associer_eqn(*this);
2538 div_tmp->completer();
2539
2540 if ((variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_STANDARD_UVEXT)
2541 || (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE_UVEXT)
2542 || (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED_UVEXT))
2543 {
2544 // Avec changement de phase, on veut reconstruire u_vap (ie phase 0)
2545 // Prise en compte du terme source div(u) du changement de phase
2546 if (variables_internes().ref_equation_mpoint_ || variables_internes().ref_equation_mpoint_vap_)
2547 {
2548 // vitesse_jump0_ is Champ_Inc but never turns the wheel! So the field is always in .valeurs()
2549 calculer_delta_u_interface(variables_internes().vitesse_jump0_, 0, variables_internes().correction_courbure_ordre_ /* ordre de la correction en courbure */);
2550 // u+u0 = champ de vitesse de la phase 0
2551 tmp += variables_internes().vitesse_jump0_->valeurs();
2552 }
2553 }
2554 if ((variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_STANDARD_UIEXT)
2555 || (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE_UIEXT)
2556 || (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED_UIEXT))
2557 {
2558 // Reconstruction d'un champ de vitesse interfaciale (ie phase 1)
2559 // Prise en compte du terme source div(u) du changement de phase
2560 if (variables_internes().ref_equation_mpoint_ || variables_internes().ref_equation_mpoint_vap_)
2561 {
2562 // vitesse_jump0_ is Champ_Inc but never turns the wheel! So the field is always in .valeurs()
2563 calculer_delta_u_interface(variables_internes().delta_u_interface, -1, variables_internes().correction_courbure_ordre_ /* ordre de la correction en courbure */);
2564 // u-dui = champ de vitesse d'interface
2565 tmp -= variables_internes().delta_u_interface->valeurs();
2566
2567 // Question: il y a un assert_espace_virtuel_vect dans divergence.calculer,
2568 // mais l'operateur n'a normalement pas besoin de l'espace virtuel !
2569 // La ligne suivante devrait pouvoir etre retiree:
2571
2572 div_tmp->calculer(tmp, resu);
2573 for (int i = 0; i < resu.size_array(); i++)
2574 resu[i] *= indicatrice[i];
2575 }
2576 }
2577
2578 // if shift_secmem2 activated; By default [INTERP_MODIFIEE], use divergence free u_l [liquid velocity] to advect the interface
2579 // the phase change effet will be counted in the end
2580 if (is_shift_secmem2_activated() && (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE) )
2581 {
2582 calculer_delta_u_interface(variables_internes().vitesse_jump1_, 1, variables_internes().correction_courbure_ordre_ /* ordre de la correction en courbure */);
2583 // u+dui = champ de vitesse d'interface
2584 tmp += variables_internes().vitesse_jump1_->valeurs();
2586 }
2587
2588 const bool ghost_correction = (variables_internes_.OutletCorrection_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::CORRECTION_GHOST_INDIC);
2589 switch(variables_internes_.type_interpol_indic_pour_dI_dt_)
2590 {
2593 {
2594 for (int i = 0; i < n; i++)
2595 {
2596 double indic_face = calculer_indicatrice_face_privilegie_pure(indicatrice, face_voisins, i);
2597 if (ghost_correction)
2598 correct_indicatrice_face_bord(i, maillage, domVF, face_voisins, indicatrice, true /* privilegie_pure */, indic_face);
2599 const double x = rho_0_sur_delta_rho - indic_face;
2600 for (int j = 0; j < dim; j++)
2601 tmp(i, j) *= x;
2602 }
2603 break;
2604 }
2606 {
2607 for (int i = 0; i < n; i++)
2608 {
2609 double indic_face = calculer_indicatrice_face_privilegie_pure(indicatrice, face_voisins, i);
2610 if (ghost_correction)
2611 correct_indicatrice_face_bord(i, maillage, domVF, face_voisins, indicatrice, true /* privilegie_pure */, indic_face);
2612 const double x = -indic_face;
2613 for (int j = 0; j < dim; j++)
2614 tmp(i, j) *= x;
2615 }
2616 break;
2617 }
2620 {
2621 refeq_transport->update_indicatrice_faces();
2622 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
2623 for (int i = 0; i < n; i++)
2624 {
2625 double indic_face = indicatrice_faces(i);
2626 if (ghost_correction)
2627 correct_indicatrice_face_bord(i, maillage, domVF, face_voisins, indicatrice, false, indic_face);
2628 const double x = rho_0_sur_delta_rho - indic_face;
2629 for (int j = 0; j < dim; j++)
2630 tmp(i, j) *= x;
2631 }
2632 break;
2633 }
2635 {
2636 refeq_transport->update_indicatrice_faces();
2637 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
2638 for (int i = 0; i < n; i++)
2639 {
2640 double indic_face = indicatrice_faces(i);
2641 if (ghost_correction)
2642 correct_indicatrice_face_bord(i, maillage, domVF, face_voisins, indicatrice, false, indic_face);
2643 const double x = -indic_face;
2644 for (int j = 0; j < dim; j++)
2645 tmp(i, j) *= x;
2646 }
2647 break;
2648 }
2652 {
2653 refeq_transport->update_indicatrice_faces();
2654 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
2655 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
2657 Cerr << " The interpolation of indicatrice to faces in calculer_dI_dt is based on the interfacial area" << " and on the normal to the interface." << finl;
2658
2659 const DoubleTab& normale_elements = eq_transport.get_normale_interface().valeurs();
2660 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
2661
2662#if NS_VERBOSE
2663 const DoubleTab& xp = domaine_vf.xp(); // centres de gravite des elements
2664 const DoubleTab& xv = domaine_vf.xv(); // centres de gravite des faces.
2665#endif
2666 // On fait la moyenne des 2 valeurs calculees sur les voisins
2667 // ATTENTION, ici on veut la valeur de chiv (cad chi_0) a la face.
2668 for (int face = 0; face < n; face++)
2669 {
2670 double indic_face = calculer_indicatrice_face_based_on_ai(indicatrice, indicatrice_faces, face_voisins, domaine_vf, interfacial_area, normale_elements, face, dim);
2671 if (ghost_correction)
2672 correct_indicatrice_face_bord(face, maillage, domVF, face_voisins, indicatrice, false, indic_face);
2673
2674#if NS_VERBOSE
2675 const double val = 1.-indicatrice_faces[face]; // indicatrice_faces=chi_1 whereas indic_face=chi_0 !!! Hence, the "1.-"
2676 if (fabs(indic_face-val)>DMINFLOAT)
2677 {
2678 const int elem0 = face_voisins(face, 0);
2679 const int elem1 = face_voisins(face, 1);
2680 double indic_0 = (elem0 >= 0) ? indicatrice[elem0] : indicatrice[elem1];
2681 double indic_1 = (elem1 >= 0) ? indicatrice[elem1] : indicatrice[elem0];
2682 if (elem0>=0)
2683 Cerr << "xp0["<< elem0<< "]: " << xp(elem0, 0) << " " << xp(elem0, 1) << " " << finl;
2684 else
2685 Cerr << "xp0: bord!" <<finl;
2686 if (elem1>=0)
2687 Cerr << "xp1["<< elem1<< "]: " << xp(elem1, 0) << " " << xp(elem1, 1) << " " << finl;
2688 else
2689 Cerr << "xp1: bord!" <<finl;
2690
2691 Cerr << "xv: " << xv(face, 0) << " " << xv(face, 1) << " " << finl;
2692 Cerr << "voisins (ou ghost): " << indic_0 << " " << indic_1 << finl;
2693 Cerr << "GB whats up?face="<< face <<" indic / val / diff " << indic_face << " " << val << " " << indic_face-val << finl;
2694 }
2695#endif
2696 // chi_v * u_v_ext
2697 for (int j = 0; j < dim; j++)
2698 tmp(face, j) *= indic_face;
2699 }
2700 break;
2701 }
2702 default:
2703 Cerr << " Navier_Stokes_FT_Disc::calculer_dI_dt \n" << " unknown case?" << finl;
2704 Process::exit();
2705 }
2706
2707 if (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_AI_BASED_UIEXT)
2708 tmp *= -1;
2709
2710 // Question: il y a un assert_espace_virtuel_vect dans divergence.calculer,
2711 // mais l'operateur n'a normalement pas besoin de l'espace virtuel !
2712 // La ligne suivante devrait pouvoir etre retiree:
2714
2715 div_tmp->ajouter(tmp, resu);
2716
2717 // Correction des flux bords :
2718 // resu = int_V div(tmp) dv avec: tmp = (rho_0_sur_delta_rho - indic_face)*vitesse_ns
2719 // (a voir qui est indic_face -> celle de phase1?)
2720 // Les flux aux bords dans les mailles diphasiques avec sortie libre sont mal calcules si l'indic_phase l'est mal.
2721 // (car il est difficile de savoir si l'indic_face est en faite encore pure et qu'il ne sort que d'une phase
2722 // dans certaines mailles mixtes).
2723 // Or, les flux_bord ont deja affecte resu par l'operation :
2724 // resu += flux // ...
2725 // Plusieurs options sont testees :
2726 // - On fait rien (NO_CORRECTION)
2727 // - On fait la correction de l'indic_face avant (avant le calcul de tmp qui est en entree de la div) (CORRECTION_GHOST_INDIC)
2728 // - On suppose que globalement, les cellules touchant le bord sortie sont
2729 // a flux de masse phasique nulle (ce qui rentre sort). (ZERO_NET_FLUX_ON_MIXED_CELLS)
2730 // Cela signifie que le flux sur la face de bord est l'oppose des flux internes.
2731 // Concretement, il suffit de faire "resu(elem) =0." dans les mailles concernees.
2732 // - On recalcule le flux sur la face de bord des mailles mixtes et on retranche celui qui avait ete ajoute (ZERO_OUT_FLUX_ON_MIXED_CELLS)
2733 // Cela ne laisse pas sortir la vapeur, ce n'est donc pas bon.
2734 switch(variables_internes_.OutletCorrection_pour_dI_dt_)
2735 {
2738 {
2739 break;
2740 }
2743 {
2744 const Domaine_VDF& zvdf = ref_cast(Domaine_VDF, domaine_dis());
2745 const Domaine_Cl_dis_base& zcldis = domaine_Cl_dis();
2746 const Domaine_Cl_VDF& zclvdf = ref_cast(Domaine_Cl_VDF, zcldis);
2747 const DoubleVect& face_surfaces = zvdf.face_surfaces();
2748 // Boucle sur les bords pour traiter les conditions aux limites
2749 int ndeb, nfin;
2750 for (int n_bord = 0; n_bord < zvdf.nb_front_Cl(); n_bord++)
2751 {
2752 // pour chaque Condition Limite on regarde son type
2753 // Si face de Dirichlet ou de Symetrie on ne fait rien
2754 // Si face de Neumann on calcule la contribution au terme source
2755 const Cond_lim& la_cl = zclvdf.les_conditions_limites(n_bord);
2756 //Cerr << que_suis_je() << "::calculer_dI_dt() correction du dIdt a la CL : " << la_cl.valeur() << finl;
2757 const Front_VF& le_bord = ref_cast(Front_VF, la_cl->frontiere_dis());
2758 ndeb = le_bord.num_premiere_face();
2759 nfin = ndeb + le_bord.nb_faces();
2760
2761 // Correction sortie libre :
2762 // On ne sait pas bien calculer la correction de volume liee a dIdt a la sortie libre.
2763 // On prefere donc l'annuler dans cet element:
2764 if ((sub_type(Dirichlet, la_cl.valeur())) || (sub_type(Neumann_sortie_libre, la_cl.valeur())) || (sub_type(Dirichlet_homogene, la_cl.valeur())))
2765 {
2766 for (int num_face = ndeb; num_face < nfin; num_face++)
2767 {
2768 const int n0 = face_voisins(num_face, 0);
2769 const int n1 = face_voisins(num_face, 1);
2770 const int elem = n0 + n1 + 1;
2771 const double indic = indicatrice(elem);
2772 if (indic * (1 - indic) > 1e-6) // In a mixed cell!
2773 {
2774 const double coef = face_surfaces(num_face); //*porosite_surf(num_face);
2775 if (variables_internes_.OutletCorrection_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::ZERO_NET_FLUX_ON_MIXED_CELLS)
2776 resu(elem) = 0.;
2777 else if (variables_internes_.OutletCorrection_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::ZERO_OUT_FLUX_ON_MIXED_CELLS)
2778 {
2779 for (int j = 0; j < dim; j++)
2780 {
2781 const int outward_normal = (n0 == -1) ? -1 : 1;
2782 double flux_bord_calcule_par_operateur_div = 0.;
2783 flux_bord_calcule_par_operateur_div = outward_normal * coef * tmp(num_face, j);
2784 resu(elem) -= flux_bord_calcule_par_operateur_div; // On RETRANCHE le flux qui avait ete pris a l'etape de calcul du div
2785 }
2786 }
2787 else
2788 {
2789 Process::exit();
2790 }
2791
2792 }
2793 }
2794 }
2795 }
2796 // Fin de la boucle for
2797 break;
2798 }
2799 default:
2800 Cerr << "unexpected" << finl;
2801 Process::exit();
2802 }
2803
2804 // Extraction des valeurs
2805 const int nb_elem = domaine_dis().nb_elem();
2806 assert(nb_elem == dI_dt.size());
2807
2808 // Simple copie
2809 dI_dt.inject_array(resu, nb_elem);
2810
2811 // L'integrale de div sur l'element est dimension * la valeur aux elements
2812 // renvoyee par l'operateur.
2813 // Les valeurs aux elements sont au debut du tableau resu.
2814 if (tab_vitesse.line_size() > 1) // i.e. VEF
2815 dI_dt *= dimension;
2816
2817#if NS_VERBOSE
2818 {
2819 Cerr << "[BEFORE-PCH] Locally, the maximum of dI_dt is : " << dI_dt.mp_max_abs_vect() << finl;
2820 const double temps = schema_temps().temps_courant();
2821 double sum = 0.;
2822 for (int i=0; i< nb_elem; i++)
2823 sum += dI_dt[i];
2824 Cerr << "[BEFORE-PCH] " << temps << " The sum is : " << sum << " [not valid in //]" << finl;
2825 }
2826#endif
2827
2828 switch(variables_internes_.type_interpol_indic_pour_dI_dt_)
2829 {
2834 {
2835 // dI_dt contains div(chi_v * u_v^ext) * vol_cell (sign to be checked!!)
2836 // Integrated, this is equal to 0 or to the integral of chi_f entering or leaving the domain through boundaries...
2837 // It is lacking the phase-change contribution
2838#define CHECK 0
2839#if CHECK
2840 {
2841 Cerr << "Checking if secmem2 is correctly filled in that case. Check it if you need it..." <<finl;
2842 if (0) Process::exit();
2843 DoubleTab& secmem2 = variables_internes().second_membre_projection_jump_.valeurs();
2844
2845 //if (variables_internes().ref_equation_mpoint_.non_nul())
2846 // variables_internes().ref_equation_mpoint_->calculer_mpoint(variables_internes().mpoint.valeur());
2847 //if (variables_internes().ref_equation_mpoint_vap_.non_nul())
2848 // variables_internes().ref_equation_mpoint_vap_->calculer_mpoint(variables_internes().mpoint_vap.valeur());
2849
2851 const Fluide_Incompressible& phase_0 = fluide.fluide_phase(0);
2852 const Fluide_Incompressible& phase_1 = fluide.fluide_phase(1);
2853 const DoubleTab& tab_rho_phase_0 = phase_0.masse_volumique().valeurs();
2854 const DoubleTab& tab_rho_phase_1 = phase_1.masse_volumique().valeurs();
2855 const double rho_phase_0 = tab_rho_phase_0(0,0);
2856 const double rho_phase_1 = tab_rho_phase_1(0,0);
2857 const double jump_inv_rho = 1./rho_phase_1 - 1./rho_phase_0;
2858
2859 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
2860 // Pas une ref, mais un tableau de travail local dans lequel on peut ajouter mointv
2861 // DoubleTab mpoint = variables_internes().mpoint->valeurs();
2862 DoubleTab mpoint = variables_internes().ref_equation_mpoint_->get_mpoint();
2863 if (variables_internes().ref_equation_mpoint_vap_)
2864 {
2865 //const DoubleTab& mpointv = variables_internes().mpoint_vap->valeurs();
2866 const DoubleTab& mpointv = variables_internes().ref_equation_mpoint_vap_->get_mpoint();
2867 for (int elem = 0; elem < nb_elem; elem++)
2868 mpoint[elem] += mpointv[elem];
2869 }
2870 for (int elem = 0; elem < nb_elem; elem++)
2871 {
2872 double diff = (secmem2[elem] - jump_inv_rho*interfacial_area[elem]*mpoint[elem]);
2873 if (fabs(diff)>DMINFLOAT)
2874 {
2875 Cerr << "Problem, secmem2 is not filled properly in that case? "
2876 << "elem= " << elem << " diff=" << diff <<finl;
2877 Process::exit();
2878 }
2879 }
2880 }
2881#endif
2882 if ((bool(variables_internes().ref_equation_mpoint_)) && !variables_internes().mpoint_inactif)
2883 {
2884 // Is it necessary to recompute them? no
2885 //variables_internes().ref_equation_mpoint_->calculer_mpoint(variables_internes().mpoint.valeur());
2886 const double un_sur_rho_0 = 1. / rho_0;
2887
2888 // We can't use secmem2 because we can't undo jump_inv_rho as it may be 0. !!!
2889 // DoubleTab& secmem2 = variables_internes().second_membre_projection_jump_.valeurs();
2890 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
2891 // const DoubleVect& volumes = domaine_vf.volumes();
2892 // Pas une ref, mais un tableau de travail local dans lequel on peut ajouter mointv
2893 DoubleTab mpoint = variables_internes().ref_equation_mpoint_->get_mpoint();
2894 if (variables_internes().ref_equation_mpoint_vap_)
2895 {
2896 // Is it necessary to recompute them? no
2897 //variables_internes().ref_equation_mpoint_vap_->calculer_mpoint(variables_internes().mpoint_vap.valeur());
2898 const DoubleTab& mpointv = variables_internes().ref_equation_mpoint_vap_->get_mpoint();
2899 for (int elem = 0; elem < nb_elem; elem++)
2900 mpoint[elem] += mpointv[elem];
2901 }
2902
2903 // At this point in the algorithm, mpoint has not been augmented by the CL contribution yet, even though
2904 // the TCL contribution (micro and meso) are already computed and associated to elements.
2905 // It is done so because just earlier in this method, we call calculer_delta_u_interface
2906 // to compute a continuous extension of velocity (to displace the interface markers).
2907
2908 // Adding the TCL contribution to the **local** DoubleTab mpoint
2909 // (it is thus temporary for the moment and will be done on the real table later at the second call to
2910 // Triple_Line_Model_FT_Disc::corriger_mpoint)
2911 // The difference if we correct it here directly is subtile. It is probably fine for the extension of
2912 // the interface velocity, but the extension for the convective velocity in the temperature has not been done yet.
2913 // It may cause trouble in the convection then, but the GFM method kind of erase those cells.
2914 if (probleme_ft().tcl().is_activated())
2915 {
2916 Cerr << "[TCL] Contact line model activated in volume correction" << finl;
2917 probleme_ft().tcl().corriger_mpoint(mpoint);
2918 }
2919
2920 for (int elem = 0; elem < nb_elem; elem++)
2921 {
2922 // By convention, mpoint is positive in condensation. Hence, mpoint >0 is responsible for dIv_dt < 0 => a minus sign!
2923 // But, \nabla \chi_v = -n_v \delta_i. => another minus sign!
2924 // ==> Consequently, it's a "+"
2925 // Besides, ai = \int_cell delta^i dv => It's homogeneous to the integral, there's no need for an additional "*volumes[elem]"
2926 // It can be directly summed to the divergence computed before.
2927 const double x = mpoint[elem] * interfacial_area[elem] * un_sur_rho_0;
2928 dI_dt[elem] += x;
2929 }
2930 }
2931 break;
2932 }
2936 {
2937 if (probleme_ft().tcl().is_activated() && (!is_shift_secmem2_activated()) )
2938 {
2939 const double un_sur_rho_0 = 1. / rho_0;
2940 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
2941 Cerr << "[TCL] Contact line model activated in volume correction" << finl;
2942 const ArrOfInt& tcl_elems = probleme_ft().tcl().elems();
2943 const ArrOfDouble& tcl_mp = probleme_ft().tcl().mp();
2944 // We accounted for the contribution that is in Ui but not the contribution from TCL yet:
2945 for (int idx = 0; idx < tcl_elems.size_array(); idx++)
2946 {
2947 const int elem = tcl_elems[idx];
2948 // By convention, mpoint is positive in condensation. Hence, mpoint >0 is responsible for dIv_dt < 0 => a minus sign!
2949 // But, \nabla \chi_v = -n_v \delta_i. => another minus sign!
2950 // ==> Consequently, it's a "+"
2951 // Besides, ai = \int_cell delta^i dv => It's homogeneous to the integral, there's no need for an additional "*volumes[elem]"
2952 // It can be directly summed to the divergence computed before.
2953 const double x = tcl_mp[idx] * interfacial_area[elem] * un_sur_rho_0;
2954 dI_dt[elem] += x;
2955 }
2956 }
2957 break;
2958 }
2959 default:
2960 break;
2961 }
2962
2963#if NS_VERBOSE
2964 {
2965 Cerr << "[AFTER-PCH] Locally, the maximum of dI_dt is : " << dI_dt.mp_max_abs_vect() << finl;
2966 const double temps = schema_temps().temps_courant();
2967 double sum = 0.;
2968 for (int i=0; i< nb_elem; i++)
2969 sum += dI_dt[i];
2970 Cerr << "[AFTER-PCH] " << temps << " The sum is : " << sum << " [not valid in //]" << finl;
2971 }
2972#endif
2973
2974 if (is_shift_secmem2_activated() && (variables_internes_.type_interpol_indic_pour_dI_dt_ == Navier_Stokes_FT_Disc_interne::INTERP_MODIFIEE) )
2975 {
2976 const double un_sur_rho_1 = 1. / rho_1;
2977 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
2978 DoubleTab mpoint(interfacial_area); // copie du tableau des vitesses de ns
2979 mpoint = 0.;
2980 if (variables_internes().ref_equation_mpoint_)
2981 {
2982 const DoubleTab& mp = variables_internes().ref_equation_mpoint_->get_mpoint();
2983 // Si inactif, on ne prend pas en compte sa contribution dans le calcul de delta_u:
2984 if (!variables_internes().mpoint_inactif)
2985 mpoint = mp;
2986 }
2987 if (variables_internes().ref_equation_mpoint_vap_)
2988 {
2989 const DoubleTab& mpv = variables_internes().ref_equation_mpoint_vap_->get_mpoint();
2990 // Si inactif, on ne prend pas en compte sa contribution dans le calcul de delta_u:
2991 if (!variables_internes().mpointv_inactif)
2992 mpoint += mpv;
2993 }
2994 for (int i = 0; i < nb_elem; i++)
2995 dI_dt[i] += mpoint[i]*un_sur_rho_1*interfacial_area[i];
2996 }
2997
2998 dI_dt.echange_espace_virtuel();
2999}
3000
3001// Description:
3002// Compute in one Eulerian cell, the average of Front properties in it :
3003// normale, bary_facettes_dans_elem and surface_tot are area-weighted averages in the given cell.
3004// Warning : normale is not a UNIT vector!
3005void compute_normale_barycenter_area_in_cell(const int elem, const Maillage_FT_Disc& mesh, Vecteur3& normale, Vecteur3& bary_facettes_dans_elem, double& surface_tot)
3006{
3007 const Intersections_Elem_Facettes& intersections = mesh.intersections_elem_facettes();
3008 const IntTab& facettes = mesh.facettes();
3009 const DoubleTab& sommets = mesh.sommets();
3010 const ArrOfDouble& surface_facettes = mesh.get_update_surface_facettes();
3011 const DoubleTab& normale_facettes = mesh.get_update_normale_facettes();
3012
3013 surface_tot = 0.;
3014 normale = 0.;
3015 bary_facettes_dans_elem = 0.;
3016 // Get the begining index defining the position in the list index_elem that contains information
3017 // relative to the current element :
3018 int index = intersections.index_elem()[elem];
3019 if (index < 0)
3020 return; // No facette in this element.
3021
3022 // Loop over the facettes crossing the element
3023 while (index >= 0)
3024 {
3025 // Accessing the structure containing all the relevant information for facette number fa7
3026 // Beware, fraction_surface_intersection_ gives the fraction of the facette that is within the current element.
3027 const Intersections_Elem_Facettes_Data& data = intersections.data_intersection(index);
3028 const int fa7 = data.numero_facette_;
3029 const double surface_facette = surface_facettes[fa7];
3030 const double surf = data.fraction_surface_intersection_ * surface_facette;
3031 // We compute the (real) coordinates of barycenter of the fraction of facette within the elem
3032 // from the Barycentric Coordinates stored in data.barycentre_
3033 // (see the web for more information on Barycentric or Areal coordinates)
3034 // coord_barycentre_fraction will contain real coordinates (x,y,z) of the barycenter
3035 Vecteur3 coord_barycentre_fraction(0., 0., 0.);
3036 for (int dir = 0; dir < 3; dir++)
3037 {
3038 const double nx = normale_facettes(fa7, dir);
3039 normale[dir] += nx * surf;
3040 }
3041 for (int isom = 0; isom < 3; isom++)
3042 {
3043 const int num_som = facettes(fa7, isom); // numero du sommet dans le tableau sommets
3044 const double bary_som = data.barycentre_[isom];
3045 for (int dir = 0; dir < 3; dir++)
3046 coord_barycentre_fraction[dir] += bary_som * sommets(num_som, dir);
3047
3048 }
3049 coord_barycentre_fraction *= surf;
3050 surface_tot += surf;
3051 bary_facettes_dans_elem += coord_barycentre_fraction; // This is done for all the 3 components.
3052
3053 index = data.index_facette_suivante_;
3054 }
3055
3056 if (surface_tot > 0.)
3057 {
3058 normale *= 1. / surface_tot;
3059 bary_facettes_dans_elem *= 1. / surface_tot;
3060 }
3061 else
3062 {
3063 normale = 0.;
3064 Cerr << " Error in compute_normale_barycenter_area_in_cell (Navier_Stokes_FT_Disc.cpp)." << finl;
3065 Cerr << "The element " << elem << " only contains facettes of surface=0, so that surface_totale is zero!" << finl;
3066 Cerr << "What a mess for the barycentre? ..." << finl;
3067 // assert(0);
3068 Process::exit();
3069 bary_facettes_dans_elem = 0.;
3070 }
3071#if NS_VERBOSE
3072 const double norm = normale[0]*normale[0] + normale[1]*normale[1] + normale[2]*normale[2];
3073 if (norm<0.9)
3074 {
3075 Cerr << " In Navier_Stokes_FT_Disc.cpp compute_normale_barycenter_area_in_cell." << finl;
3076 Cerr << "Small normal : " << count << " facettes dans l'element " << elem
3077 << ". surface_tot = "<< surface_tot << "Norm**2 = " << norm << finl;
3078 }
3079#endif
3080
3081}
3082
3084 const DoubleVect& volumes_entrelaces, const IntVect& orientation, const DoubleTab& indicatrice, const ArrOfDouble& g, // Vect3
3085 DoubleTab& gravite_face) const
3086{
3087 const int phase_eq = eq.get_phase();
3088 const DoubleTab& temperature_eq = eq.inconnue().valeurs();
3089 const Fluide_Incompressible& fluide_phase_eq = fluide_dipha.fluide_phase(phase_eq);
3090 const DoubleTab& tab_beta_th_phase_eq = fluide_phase_eq.beta_t().valeurs();
3091 const double beta_th_phase_eq = tab_beta_th_phase_eq(0, 0);
3092
3093 for (int face = 0; face < gravite_face.dimension(0); face++)
3094 {
3095 const int elem0 = face_voisins(face, 0);
3096 const int elem1 = face_voisins(face, 1);
3097 double coef = 0.;
3098 // On suppose la ref T0 egale a Tsat
3099 //
3100 // Pour les mailles monophasiques, on peut faire simplement l'hypothese que T = chi_k T_k
3101 // Dans les mailles diphasiques, T = chi_k T_k est une hypothese discutable.
3102 // Pour les mailles diphasiques, on pourrait envisager une reconstruction plus precise de la
3103 // temperature monofluide a partir du gradient (cad de mpoint).
3104 // Neglected in first approximation. we simply compute T = chi_k T_k
3105 if (elem0 >= 0)
3106 {
3107 double chi = (2 * phase_eq - 1) * indicatrice[elem0] + 1 - phase_eq;
3108 double T_eq = temperature_eq[elem0];
3109 coef = chi * T_eq;
3110 }
3111 if (elem1 >= 0)
3112 {
3113 double chi = (2 * phase_eq - 1) * indicatrice[elem1] + 1 - phase_eq;
3114 double T_eq = temperature_eq[elem1];
3115 coef += chi * T_eq;
3116 }
3117 if (elem0 >= 0 && elem1 >= 0) // Not a boundary of the domain ?
3118 coef *= 0.5;
3119 gravite_face(face) -= volumes_entrelaces(face) * g(orientation[face]) * coef * beta_th_phase_eq;
3120 }
3121}
3122
3123/*! @brief Calcul de la derivee en temps de la vitesse.
3124 *
3125 */
3127{
3128 vpoint = 0.;
3129
3130 // =====================================================================
3131 // Methode de projection :
3132 // Premiere etape : calcul de u_etoile (tous les termes de N.S. sauf la pression)
3133
3134 // Contribution des operateurs diffusion et convection :
3135 // Operateur de diffusion : valeurs discretes homogenes a
3136 // / d \ //
3137 // INTEGRALE | -- (rho * v) | //
3138 // (sur le volume de controle) \ dt / //
3139 // B.M. 08/2004 : on envoie la vitesse "v" a l'operateur qui calcule
3140 // div (mu * (grad(v)+tr(grad(v))))
3141 // (on a associe "mu" a la "diffusivite" de l'operateur,
3142 // voir Navier_Stokes_FT_Disc::lire)
3143 terme_diffusif.calculer(la_vitesse->valeurs(), variables_internes().terme_diffusion->valeurs());
3144 if (correction_diffusion_pch_)
3145 {
3146 DoubleTab& diffusion = variables_internes().terme_diffusion->valeurs();
3147 DoubleTab diffusion_liq(diffusion);
3148 DoubleTab diffusion_vap(diffusion);
3149 OBS_PTR(Transport_Interfaces_FT_Disc) & refeq_transport =
3150 variables_internes().ref_eq_interf_proprietes_fluide;
3151 if (refeq_transport)
3152 {
3153 //const Champ_base& indicatrice = refeq_transport.valeur().get_indicatrice();
3154 int phase_liq = 1;
3155 int phase_vap = 0;
3156 if (variables_internes().ref_equation_mpoint_)
3157 {
3158 const Convection_Diffusion_Temperature_FT_Disc& eq_temp = variables_internes().ref_equation_mpoint_.valeur();
3159 phase_liq = eq_temp.get_phase();
3160 terme_diffusif.calculer(eq_temp.vitesse_pour_transport().valeurs(),
3161 diffusion_liq);
3162 }
3163 if (variables_internes().ref_equation_mpoint_vap_)
3164 {
3165 const Convection_Diffusion_Temperature_FT_Disc& eq_temp = variables_internes().ref_equation_mpoint_vap_.valeur();
3166 phase_vap = eq_temp.get_phase();
3167 terme_diffusif.calculer(eq_temp.vitesse_pour_transport().valeurs(),
3168 diffusion_vap);
3169 }
3170
3171 if ((phase_liq == 1) && (phase_vap == 0))
3172 {
3173 // ok
3174 }
3175 else
3176 {
3177 Cerr << "error bad weighting below" << finl;
3178 Process::exit();
3179 }
3180 refeq_transport->update_indicatrice_faces();
3181 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
3182 const DoubleTab& dist_faces = refeq_transport.valeur().get_update_distance_interface_faces().valeurs();
3183 for (int i = 0; i < diffusion.dimension(0) ; i++)
3184 {
3185 const double indic_f = indicatrice_faces(i);
3186 // if ((est_different(indicatrice_faces(i), 0.)) && (est_different(indicatrice_faces(i), 1.)) )
3187 if (std::fabs(dist_faces(i))< 3.*1.414e-6)
3188 {
3189 diffusion(i) = (indic_f)* diffusion_liq(i) + (1-indic_f)* diffusion_vap(i) ;
3190 }
3191 }
3192 }
3193 }
3194 solveur_masse->appliquer(variables_internes().terme_diffusion->valeurs());
3195
3196 // Termes sources : gravite et tension de surface,
3197 // valeurs discretes homogenes a
3198 // / d \ //
3199 // INTEGRALE | -- (rho * v) | //
3200 // (sur le volume de controle) \ dt / //
3201
3202 {
3203 // Si une equation de transport est associee aux proprietes du fluide,
3204 // on ajoute le terme de tension de surface.
3205 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
3206
3207 if (refeq_transport)
3208 {
3209 const Champ_base& indicatrice = refeq_transport->get_indicatrice();
3210 Champ_base& gradient_i = variables_internes().gradient_indicatrice.valeur();
3211 // Note:
3212 // On appelle la version const de maillage_interface() (qui est publique) car
3213 // on passe par const Transport_Interfaces_FT_Disc :
3214 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3215 const Maillage_FT_Disc& maillage = eq_transport.maillage_interface();
3216 const DoubleTab& distance_interface_sommets = eq_transport.get_update_distance_interface_sommets();
3217
3218 calculer_gradient_indicatrice(indicatrice, distance_interface_sommets, gradient_i);
3219 calculer_champ_forces_superficielles(maillage, gradient_i, variables_internes().potentiel_elements, variables_internes().potentiel_faces, variables_internes().terme_source_interfaces);
3220 if (is_solid_particle_ && eq_transport.get_ptr_collision_model())
3221 compute_eulerian_field_contact_forces(maillage, indicatrice);
3222 }
3223 else
3224 {
3225 variables_internes().terme_source_interfaces->valeurs() = 0.;
3226 }
3227 }
3228 solveur_masse->appliquer(variables_internes().terme_source_interfaces->valeurs());
3229 if (is_solid_particle_) solveur_masse->appliquer(variables_internes().contact_force_source_term->valeurs());
3230
3231 // Autres termes sources (acceleration / repere mobile)
3232 // Valeurs homogenes a
3233 // / d \ //
3234 // INTEGRALE | -- (v) | //
3235 // (sur le volume de controle) \ dt / //
3236 // (voir "preparer_calcul", commentaire sources().associer_rho...)
3237 variables_internes().terme_source->valeurs() = 0.;
3238 les_sources.ajouter(variables_internes().terme_source->valeurs());
3239 solveur_masse->appliquer(variables_internes().terme_source->valeurs());
3240
3241 // Operateur de convection : valeurs discretes homogenes a
3242 // / d \ //
3243 // INTEGRALE | -- (v) | //
3244 // (sur le volume de controle) \ dt / //
3245 // B.M. 08/2004 : on transporte "v" et non "rho_v"...
3246
3247 DoubleTab& terme_convection_valeurs = variables_internes().terme_convection->valeurs();
3248 bool calcul_explicite = false;
3249 if (parametre_equation_ && sub_type(Parametre_implicite, parametre_equation_.valeur()))
3250 {
3251 Parametre_implicite& param2 = ref_cast(Parametre_implicite, parametre_equation_.valeur());
3252 calcul_explicite = param2.calcul_explicite();
3253 }
3254 if (schema_temps().diffusion_implicite() && !calcul_explicite)
3255 {
3256 terme_convection_valeurs = 0;
3257 derivee_en_temps_conv(terme_convection_valeurs, la_vitesse->valeurs());
3258 }
3259 else
3260 {
3261 terme_convectif.calculer(la_vitesse->valeurs(), terme_convection_valeurs);
3262 }
3263 solveur_masse->appliquer(variables_internes().terme_convection->valeurs());
3264
3265 // Ajout des differentes contributions a vpoint :
3266 const DoubleTab& tab_rho_faces = champ_rho_faces_->valeurs();
3267 const DoubleVect& volumes_entrelaces = ref_cast(Domaine_VF, domaine_dis()).volumes_entrelaces();
3268 const DoubleTab& tab_diffusion = variables_internes().terme_diffusion->valeurs();
3269 const DoubleTab& termes_sources_interf = variables_internes().terme_source_interfaces->valeurs();
3270 const DoubleTab& termes_sources = variables_internes().terme_source->valeurs();
3271 const DoubleTab& tab_convection = variables_internes().terme_convection->valeurs();
3272 const DoubleTab& contact_force_source_term = variables_internes().contact_force_source_term->valeurs();
3273 const int n = vpoint.dimension(0);
3274 const int nbdim1 = (vpoint.line_size() == 1);
3275 const int m = vpoint.line_size();
3276
3277 DoubleTab gravite_face(inconnue().valeurs());
3278 if (milieu().a_gravite())
3279 {
3280 ArrOfDouble g(dimension);
3281 // Pour l'instant : gravite uniforme g => phi(s) = - x scalaire g
3282 const DoubleTab& gravite = milieu().gravite().valeurs();
3283 if (gravite.nb_dim() != 2 || gravite.line_size() != dimension)
3284 {
3285 Cerr << "Error for calculer_champ_forces_superficielles\n";
3286 Cerr << " gravite.line_size() != Objet_U::dimension" << finl;
3287 Process::exit();
3288 }
3289 for (int j = 0; j < dimension; j++)
3290 g[j] = gravite(0, j);
3291
3292 // On multiplie par les volumes entrelaces et on applique ensuite le solveur masse
3293 // (traitement special des CL de Dirichlet)
3294 if (nbdim1)
3295 {
3296 const IntTab& face_voisins = le_dom_dis->face_voisins();
3297 const IntVect& orientation = ref_cast(Domaine_VDF, domaine_dis()).orientation();
3298 if (variables_internes().terme_gravite_ == Navier_Stokes_FT_Disc_interne::GRAVITE_RHO_G)
3299 {
3300 for (int face = 0; face < n; face++)
3301 gravite_face(face, 0) = volumes_entrelaces(face) * g[orientation[face]];
3302 }
3303 else
3304 {
3305 gravite_face = 0.; // En gradI, on ne prend pas directement la gravite
3306 }
3307 // Boussinesq Approximation in use :
3308 if (variables_internes().is_boussinesq_)
3309 {
3310 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
3311 if (!refeq_transport)
3312 {
3313 Cerr << "Trying to use Boussinesq approximation on a 2phase flow when the transport equation is not specified" << finl;
3314 Process::exit();
3315 }
3316 const DoubleTab& indicatrice = refeq_transport->get_indicatrice().valeurs();
3317 // First phase with temperature :
3318 if (variables_internes().ref_equation_mpoint_)
3319 {
3320 compute_boussinesq_additional_gravity(variables_internes().ref_equation_mpoint_.valeur(), fluide_diphasique(), face_voisins, volumes_entrelaces, orientation, indicatrice, g,
3321 gravite_face);
3322 }
3323
3324 // Second phase with temperature :
3325 if (variables_internes().ref_equation_mpoint_vap_)
3326 {
3327 compute_boussinesq_additional_gravity(variables_internes().ref_equation_mpoint_vap_.valeur(), fluide_diphasique(), face_voisins, volumes_entrelaces, orientation, indicatrice, g,
3328 gravite_face);
3329 }
3330 // The end of boussinesq force source term.
3331 }
3332 // End of if nbdim1 that is VDF.
3333 }
3334 else
3335 {
3336 // VEF case:
3337 if (variables_internes().is_boussinesq_)
3338 {
3339 Cerr << "Trying to use Boussinesq approximation on a 2phase flow in VEF? Not yet available. Ask TRUST support." << finl;
3340 Process::exit();
3341 }
3342 if (variables_internes().terme_gravite_ == Navier_Stokes_FT_Disc_interne::GRAVITE_RHO_G)
3343 {
3344 for (int face = 0; face < n; face++)
3345 for (int dim = 0; dim < m; dim++)
3346 gravite_face(face, dim) = volumes_entrelaces(face) * g[dim];
3347 }
3348 else
3349 {
3350 gravite_face = 0.; // En gradI, on ne prend pas directement la gravite
3351 }
3352 }
3353 solveur_masse->appliquer(gravite_face);
3354 }
3355 else
3356 {
3357 // Pas de gravite :
3358 gravite_face = 0.;
3359 }
3360
3361 IntTab flag_gradP(n);
3362 IntTab coef_TSF(n);
3363 coef_TSF = 1;
3364 int flag_diff;
3365 DoubleTab& gradP = variables_internes().gradient_pression->valeurs();
3366 if (schema_temps().diffusion_implicite())
3367 {
3368 //on calcule gradP (pour le qdm)
3369 gradient.calculer(la_pression->valeurs(), gradP);
3370 solveur_masse->appliquer(gradP);
3371 // si variables_internes().is_penalized=1 alors variables_internes().is_explicite=0
3372 if (variables_internes().is_penalized)
3373 flag_gradP = 0; //(grad P raide)
3374 else
3375 flag_gradP = 1;
3376 // on ajoute pas la diffusion cela sera fait par Gradient_conjugue_diff_impl
3377 // sauf si !is_explicite (terme forcage vitesse implicite; resolution conjointe forcage diffusion)
3378 // car dans ce cas la vitesse a imposer a besoin d'une bonne approximation de vpoint
3379 if (!variables_internes().is_explicite && !calcul_explicite)
3380 flag_diff = 1;
3381 else
3382 flag_diff = 0;
3383 }
3384 else
3385 {
3386 flag_gradP = 0;
3387 flag_diff = 1;
3388 }
3389
3390 bool interf_vitesse_imposee_ok = false;
3391 int nb_eqs = variables_internes().ref_eq_interf_vitesse_imposee.size();
3392 int nb_eq_non_nul = 0;
3393 for (int k = 0; k < nb_eqs; k++)
3394 {
3395 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3396
3397 if (refeq_transport)
3398 nb_eq_non_nul += 1;
3399 }
3400 DoubleTab terme_mul;
3401 if (nb_eq_non_nul == nb_eqs && nb_eqs != 0)
3402 {
3403 interf_vitesse_imposee_ok = true;
3404 terme_mul.copy(champ_rho_faces_->valeurs(), RESIZE_OPTIONS::COPY_INIT);
3405 terme_mul = 0.;
3406 }
3407
3408 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport_2pha = variables_internes().ref_eq_interf_proprietes_fluide;
3409 if (refeq_transport_2pha && interf_vitesse_imposee_ok && variables_internes().is_penalized)
3410 {
3411 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
3412 const Domaine_VF& domaine_vdf = ref_cast(Domaine_VDF, domaine_dis());
3413 const IntTab& face_voisins = domaine_vf.face_voisins();
3414 const IntTab& elem_faces = domaine_vf.elem_faces();
3415 const IntVect& orientation = domaine_vdf.orientation();
3416 const int nb_faces_elem = elem_faces.line_size();
3417 for (int k = 0; k < nb_eqs; k++)
3418 {
3419 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3420 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
3421 for (int i = 0; i < face_voisins.dimension(0); i++)
3422 {
3423 if (indicatrice_faces(i) > 0.)
3424 {
3425 flag_gradP(i) = 0;
3426 coef_TSF(i) = 0; //annulation local terme source interf
3427 const int ori = orientation(i);
3428 const int voisin0 = face_voisins(i, 0);
3429 if (voisin0 >= 0)
3430 {
3431 int face_visavi = elem_faces(voisin0, ori) + elem_faces(voisin0, ori + Objet_U::dimension) - i;
3432 for (int i_face = 0; i_face < nb_faces_elem; i_face++)
3433 {
3434 const int face = elem_faces(voisin0, i_face);
3435 if (indicatrice_faces(face) == 0. && face == face_visavi)
3436 {
3437 flag_gradP(face) = 0;
3438 coef_TSF(face) = 0; //annulation local terme source interf
3439 }
3440 }
3441 }
3442 const int voisin1 = face_voisins(i, 1);
3443 if (voisin1 >= 0)
3444 {
3445 int face_visavi = elem_faces(voisin1, ori) + elem_faces(voisin1, ori + Objet_U::dimension) - i;
3446 for (int i_face = 0; i_face < nb_faces_elem; i_face++)
3447 {
3448 const int face = elem_faces(voisin1, i_face);
3449 if (indicatrice_faces(face) == 0. && face == face_visavi)
3450 {
3451 flag_gradP(face) = 0;
3452 coef_TSF(face) = 0; //annulation local terme source interf
3453 }
3454 }
3455 }
3456 }
3457 }
3458 }
3459 }
3460
3461 // Ajout des differentes contributions a vpoint :
3462 for (int i = 0; i < n; i++)
3463 {
3464 const double rho_face = tab_rho_faces(i);
3465
3466 for (int j = 0; j < m; j++)
3467 vpoint(i, j) = (-flag_gradP(i) * gradP(i, j) + flag_diff * tab_diffusion(i, j) + coef_TSF(i) * termes_sources_interf(i, j)+is_solid_particle_*contact_force_source_term(i))
3468 / rho_face + tab_convection(i, j) + termes_sources(i, j) + gravite_face(i, j);
3469 }
3470 vpoint.echange_espace_virtuel();
3471
3472 // si terme forcage vitesse explicite => J'ai tout, je peux resoudre (Gradient_conjugue_diff_impl)
3473 if (schema_temps().diffusion_implicite() && !calcul_explicite && variables_internes().is_explicite)
3474 {
3475 DoubleTab derivee(la_vitesse->valeurs());
3476 // on indique au solveur masse de diviser par rho en plus du volume car l'operateur de diffusion renvoit qqqc en rho d u/dt
3477 solveur_masse->set_name_of_coefficient_temporel(champ_rho_faces_->le_nom());
3478
3479 DoubleTrav tt(vpoint);
3480 tt = vpoint;
3481 derivee = inconnue().valeurs();
3483
3484 solveur_masse->set_name_of_coefficient_temporel("no_coeff");
3485
3486 vpoint = derivee;
3487 // on retire le gradient si on ne penalise pas:
3488 for (int i = 0; i < n; i++)
3489 for (int j = 0; j < m; j++)
3490 vpoint(i, j) += gradP(i, j) / tab_rho_faces(i);
3491 }
3492
3493 const int nfaces = vpoint.dimension_tot(0);
3494
3495 if (interf_vitesse_imposee_ok)
3496 {
3497 int compteur_vimp_regul = 0;
3498 DoubleTrav vpoint0(vpoint);
3499 vpoint0 = vpoint;
3500 terme_mul = 1.0;
3501 // S'il y a une equation de transport avec vitesse imposee, on impose:
3502 DoubleTrav forces_tot(vpoint);
3503 int nb_eqs_bis = variables_internes().ref_eq_interf_vitesse_imposee.size();
3504 for (int k = 0; k < nb_eqs_bis; k++)
3505 {
3506 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3507
3508 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3509
3510 const DoubleTab& inco_val = inconnue().valeurs();
3511 const DoubleTab& rho_faces = champ_rho_faces_->valeurs();
3512 DoubleTab& source_val = variables_internes().terme_source->valeurs();
3513 const double temps = schema_temps().temps_courant();
3514 const double dt = schema_temps().pas_de_temps();
3515
3516 //On ajoute un terme source a vpoint pour imposer au fluide la vitesse de l interface
3517 //source_val est rempli (peut etre postraite)
3518 eq_transport.modifier_vpoint_pour_imposer_vit(inco_val, vpoint0, vpoint, rho_faces, source_val, temps, dt, variables_internes().is_explicite, variables_internes().eta);
3519 source_val.echange_espace_virtuel();
3520 forces_tot += source_val;
3521
3522 // Afin de savoir s il existe des interfaces IBC/fluide regularisees.
3523 // Si oui, on ne penalise que pour indic=1.
3524 if (eq_transport.get_vimp_regul())
3525 compteur_vimp_regul++;
3526 //calcul du terme 1 + somme Xs / eta
3527 if (!variables_internes().is_explicite)
3528 {
3529 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
3530 for (int i = 0; i < nfaces; i++)
3531 {
3532 if ((eq_transport.get_vimp_regul() == 0 && indicatrice_faces(i) > 0.) || (eq_transport.get_vimp_regul() == 1 && indicatrice_faces(i) == 1.))
3533 terme_mul(i) += 1. / variables_internes().eta;
3534 }
3535 }
3536 }
3537 terme_mul.echange_espace_virtuel();
3538 Debog::verifier("Navier_Stokes_FT_Disc::derivee_en_temps_inco terme_mul:", terme_mul);
3539
3540 if (schema_temps().diffusion_implicite())
3541 {
3542 // si !variables_internes().is_explicite (terme forcage implicite) on retire la diffusion explicite
3543 // de vpoint (implicitee dans Gradient_conjugue_diff_impl_IBC)
3544 if (!variables_internes().is_explicite && !calcul_explicite)
3545 {
3546 const DoubleTab& rho_faces = champ_rho_faces_->valeurs();
3547 const DoubleTab& diffusion = variables_internes().terme_diffusion->valeurs();
3548
3549 for (int i = 0; i < vpoint.dimension(0); i++)
3550 for (int j = 0; j < vpoint.line_size(); j++)
3551 vpoint(i, j) -= diffusion(i, j) / rho_faces(i);
3552
3553 vpoint.echange_espace_virtuel();
3554 DoubleTab derivee(la_vitesse->valeurs());
3555 // on indique au solveur masse de diviser par rho en plus du volume car l'operateur de diffusion renvoit qqqc en rho d u/dt
3556 {
3557 solveur_masse->set_name_of_coefficient_temporel(champ_rho_faces_->le_nom());
3558
3559 DoubleTrav tt(vpoint);
3560 tt = vpoint;
3561 Equation_base::Gradient_conjugue_diff_impl(tt, derivee, terme_mul);
3562
3563 solveur_masse->set_name_of_coefficient_temporel("no_coeff");
3564 }
3565 vpoint = derivee;
3566
3567 // on retire le gradient
3568 const int nbis = vpoint.dimension(0);
3569 const int mbis = vpoint.line_size();
3570 for (int i = 0; i < nbis; i++)
3571 for (int j = 0; j < mbis; j++)
3572 vpoint(i, j) += (flag_gradP(i) * gradP(i, j)) / rho_faces(i);
3573 }
3574 vpoint.echange_espace_virtuel();
3575 }
3576 else
3577 {
3578 // si on implicite le calcul de vpoint
3579 // On divise vpoint par le terme multiplicatif calcule avant
3580 if (!variables_internes().is_explicite)
3581 {
3582 const int mbis = vpoint.line_size();
3583 // calcul de vpoint : vpoint / (1 + somme Xs/eta )
3584 for (int i = 0; i < nfaces; i++)
3585 for (int j = 0; j < mbis; j++)
3586 vpoint(i, j) /= terme_mul(i);
3587
3588 vpoint.echange_espace_virtuel();
3589 }
3590
3591 }
3592 Debog::verifier("Navier_Stokes_FT_Disc::derivee_en_temps_inco vpoint:", vpoint);
3593
3594 // Dans le cas penalize + vitesse imposee regularisee,
3595 // seuls les ddl tels que indic_faces=1 sont penalisees, les
3596 // autres sont forces avec un DF :
3597 if (!variables_internes().is_explicite && compteur_vimp_regul)
3598 {
3599 vpoint0 = vpoint;
3600 // S'il y a une equation de transport avec vitesse imposee, on impose:
3601 DoubleTrav forces_totbis(vpoint);
3602 for (int k = 0; k < nb_eqs_bis; k++)
3603 {
3604 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3605
3606 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3607
3608 const DoubleTab& inco_val = inconnue().valeurs();
3609 const DoubleTab& rho_faces = champ_rho_faces_->valeurs();
3610 DoubleTab& source_val = variables_internes().terme_source->valeurs();
3611 const double temps = schema_temps().temps_courant();
3612 const double dt = schema_temps().pas_de_temps();
3613
3614 //On ajoute un terme source a vpoint pour imposer au fluide la vitesse de l interface
3615 //source_val est rempli (peut etre postraite)
3616 eq_transport.modifier_vpoint_pour_imposer_vit(inco_val, vpoint0, vpoint, rho_faces, source_val, temps, dt,/* is_explicite */1, /* eta */1.);
3617 source_val.echange_espace_virtuel();
3618 forces_totbis += source_val;
3619 }
3620 }
3621 vpoint.echange_espace_virtuel();
3622
3623 //Calcul des efforts exerces par le fluide sur chaque interface
3624 //Attention valable si les ibc ne se chevauchent pas
3625 if (limpr())
3626 {
3627 const DoubleTab& tab_rho_facesbis = champ_rho_faces_->valeurs();
3628 for (int k = 0; k < nb_eqs_bis; k++)
3629 {
3630 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3631 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3632 eq_transport.calcul_effort_fluide_interface(vpoint, tab_rho_facesbis, forces_tot, variables_internes().is_explicite, variables_internes().eta);
3633 forces_tot.echange_espace_virtuel();
3634 }
3635 }
3636 }
3637
3638 // Assemblage de la matrice INTEGRALE ( div(1/rho * grad(P)) ) :
3639 // (volume de controle pression)
3640 // Si l'option "matrice_pression_invariante" est activee, on ne recalcule
3641 // pas la matrice :
3642 if (!interf_vitesse_imposee_ok)
3643 {
3644 if (!variables_internes().matrice_pression_invariante)
3645 {
3646 assembleur_pression()->assembler_rho_variable(matrice_pression_, champ_rho_faces_.valeur());
3647 // On a modifie la matrice, il faut reinitialiser le solveur
3648 // (recalcul des preconditionnement, factorisation pour Cholesky, etc...)
3649 solveur_pression()->reinit();
3650 }
3651 }
3652
3653 // ====================================================================
3654 // Methode de projection :
3655 // Deuxieme etape : projection du champ de vitesse sur le sous-espace
3656 // a divergence nulle.
3657 // Trouver la pression P telle que
3658 // d(u)/dt = vpoint - 1/rho * grad(P)
3659 // div( d(u)/dt ) = 0
3660 // Soit :
3661 // div(1/rho * grad(P)) = div(vpoint)
3662
3663 // Calcul du second membre :
3664 // div(vpoint) a l'interieur du domaine,
3665 // prise en compte des conditions aux limites de pression/vitesse
3666
3667 DoubleTab& secmem = variables_internes().second_membre_projection->valeurs();
3668 DoubleTab& secmem2 = variables_internes().second_membre_projection_jump_->valeurs();
3669 const int nb_elem = secmem2.dimension(0);
3670 const double dt = schema_temps().pas_de_temps();
3671 const DoubleTab& inco = inconnue().valeurs();
3672 // secmem = div(U/dt+vpoint) = div(U(n+1)/dt)
3673 DoubleTab du(inco);
3674 du /= dt;
3675 du += vpoint;
3676 divergence.calculer(du, secmem);
3677 secmem *= -1;
3678#if NS_VERBOSE
3679 double int_sec_mem = 0;
3680 for (int elem = 0; elem < secmem.dimension(0); elem++)
3681 int_sec_mem +=secmem(elem);
3682 Cerr << "Secmem before tcl= " << int_sec_mem << finl;
3683#endif
3684 // Prise en compte des sources de constituant (terme source dans une equation concentration)
3685 // Pour ne pas faire figurer la source deux fois, je la mets uniquement dans l'equation
3686 // de concentration... elle produit alors un terme source de div_u dans Navier_Stokes:
3687 const Noms& noms_eq = variables_internes().equations_concentration_source_fluide_;
3688 for (int i_eq = 0; i_eq < noms_eq.size(); i_eq++)
3689 {
3690 const Equation_base& eq = probleme().get_equation_by_name(noms_eq[i_eq]);
3691 for (int i_source = 0; i_source < eq.sources().size(); i_source++)
3692 {
3693 if (!sub_type(Terme_Source_Constituant_Vortex_VEF_Face, eq.sources()[i_source].valeur()))
3694 continue;
3695
3697 src.ajouter_terme_div_u(secmem, schema_temps().pas_de_temps());
3698 }
3699
3700 }
3701 secmem.echange_espace_virtuel();
3702
3703 // Prise en compte du terme source div(u) du changement de phase
3704 if (variables_internes().ref_equation_mpoint_ || variables_internes().ref_equation_mpoint_vap_)
3705 {
3706 // GB2016 : Le calcul de mpoint ci-dessous me semble inutile car il est fait au debut de calculer_delta_u_interface:
3707 // GB2016 : Mais si je ne le fais pas, j'ai parfois : 'vx.get_md_vector() == md' dans calculer_delta_u_interface
3708
3709 // GB2022 : Je ne comprend toujours pas bien pourquoi, mais il faut calculer les mpoints
3710 // pour avoir un cas FTD_Boiling_bubble avec une extension correcte
3711 // (sinon le champ postraite de T est moche dans l'extension)
3712
3713 // GB2024 : TODO avec un bon cas test, on devrait pouvoir comprendre et supprimer ce bloc.
3714 {
3715 if (variables_internes().ref_equation_mpoint_)
3716 variables_internes().ref_equation_mpoint_->calculer_mpoint(variables_internes().mpoint.valeur());
3717 if (variables_internes().ref_equation_mpoint_vap_)
3718 variables_internes().ref_equation_mpoint_vap_->calculer_mpoint(variables_internes().mpoint_vap.valeur());
3719 }
3720 // Pas une ref, mais un tableau de travail local dans lequel on peut ajouter mointv
3721 DoubleTab mpoint = variables_internes().ref_equation_mpoint_->get_mpoint();
3722 if (variables_internes().ref_equation_mpoint_vap_)
3723 {
3724 const DoubleTab& mpointv = variables_internes().ref_equation_mpoint_vap_->get_mpoint();
3725 for (int elem = 0; elem < nb_elem; elem++)
3726 mpoint[elem] += mpointv[elem];
3727 }
3728 // We can compute delta_u_interface:
3729 // depending on the option, either historical or new, the calculation may be based on the values filled in secmem2
3730 // Disscussion with G.B 16/01/25: We need a smooth delta_u_interface to move the markers <=> before account the TCL model
3731 // delta_u_interface is NOLY computed one time, at NS-equation; get_delta_vitesse_interface can be used to asses it.
3732 // Besides, delta_u_interface is Champ_Inc but never turns the wheel! So the field is always in .valeurs()
3733 calculer_delta_u_interface(variables_internes().delta_u_interface, -1 /* vitesse de l'interface */, variables_internes().correction_courbure_ordre_ /* ordre de la correction en courbure */);
3734
3735 const Fluide_Diphasique& fluide_diph = fluide_diphasique();
3736 const Fluide_Incompressible& phase_0 = fluide_diph.fluide_phase(0);
3737 const Fluide_Incompressible& phase_1 = fluide_diph.fluide_phase(1);
3738 const DoubleTab& tab_rho_phase_0 = phase_0.masse_volumique().valeurs();
3739 const DoubleTab& tab_rho_phase_1 = phase_1.masse_volumique().valeurs();
3740 const double rho_phase_0 = tab_rho_phase_0(0, 0);
3741 const double rho_phase_1 = tab_rho_phase_1(0, 0);
3742 const double jump_inv_rho = 1. / rho_phase_1 - 1. / rho_phase_0;
3743 if (variables_internes().new_mass_source_)
3744 {
3745
3746 const DoubleTab& interfacial_area = variables_internes().ai->valeurs();
3747 for (int elem = 0; elem < nb_elem; elem++)
3748 secmem2[elem] = jump_inv_rho * interfacial_area[elem] * mpoint[elem];
3749 }
3750 else
3751 {
3752 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
3753 const IntTab& face_voisins = domaine_vf.face_voisins();
3754 const IntTab& elem_faces = domaine_vf.elem_faces();
3755 const int nb_faces_elem = elem_faces.line_size();
3756 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
3757 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3758#if NS_VERBOSE
3759 const DoubleTab& indicatrice = eq_transport.inconnue().valeurs();
3760#endif
3761 // Distance a l'interface discretisee aux elements:
3762 const DoubleTab& distance = eq_transport.get_distance_interface().valeurs();
3763 divergence.calculer(variables_internes().delta_u_interface->valeurs(), secmem2);
3764 // On ne conserve que la divergence des elements traverses par l'interface
3765 for (int elem = 0; elem < nb_elem; elem++)
3766 {
3767 const double dist = distance(elem);
3768 int i_face = -1;
3769 if (dist < -1e20)
3770 {
3771 // Distance invalide: on est loin de l'interface
3772 i_face = nb_faces_elem;
3773 }
3774 else
3775 {
3776 // Y a-t-il un voisin pour lequel la distance est de signe oppose
3777 for (i_face = 0; i_face < nb_faces_elem; i_face++)
3778 {
3779 const int face = elem_faces(elem, i_face);
3780 const int voisin = face_voisins(face, 0) + face_voisins(face, 1) - elem;
3781 if (voisin >= 0)
3782 {
3783 const double d = distance(voisin);
3784 if (d > -1e20 && d * dist < 0.)
3785 {
3786#if NS_VERBOSE
3787 Cerr << "Compa "<< secmem2[elem] << " " << jump_inv_rho*interfacial_area[elem]*mpoint[elem] << finl;
3788#endif
3789 break; // Changement de signe
3790 }
3791 }
3792 }
3793 }
3794 if (i_face == nb_faces_elem)
3795 {
3796 // Tous les voisins sont du meme cote de l'interface
3797 secmem2(elem) = 0.;
3798#if NS_VERBOSE
3799 if(interfacial_area[elem]>DMINFLOAT)
3800 {
3801 Cerr << "[WARNING] secmem2 is set to zero in element whereas phase is not pure (indic= "
3802 << indicatrice[elem] << "). This is because the choice is based on the signs of distance." << finl;
3803 // Indeed, in a diagonal case, for the cell with "x", indic can be close to (but not) pure and all neighbouring
3804 // cells can still have the same distance.
3805 // __________
3806 // | | /| |
3807 // |__|/_|__|
3808 // | /| x| |
3809 // |/_|__|__|
3810 }
3811#endif
3812 }
3813 }
3814 }
3815
3816 /* double int_sec_mem2 = 0.;
3817 for (int elem = 0; elem < nb_elem; elem++)
3818 {
3819 int_sec_mem2 +=secmem2(elem);
3820 int_sec_mem +=secmem(elem);
3821 }
3822 Cerr << "Integral of secmem2 before TCL and /DT : " << int_sec_mem2 << finl; */
3823
3824 // Now that the correction "corriger_mpoint" is performed directly into Convection_Diffusion_Temperature_FT_Disc,
3825 // it is no longer required to compute it here. The correction should propagate to calculer_delta_vitesse (in the discreete sense),
3826 // and subsequently to secmem2. So that in the end, the whole TCL contribution should be accounted for naturally.
3827 // However, in the discretized version, It is still required to correct secmem2 even though mpoint was corrected itself.
3828 // It is because of the way secmem is computed (as the div( delta u)) that is using part of cells not crossed by the interface
3829 // (in the wall-normal direction). It results in an underestimation of the real TCL contribution...
3830 if (probleme_ft().tcl().is_activated())
3831 {
3832 Cerr << "[TCL] Contact line model is activated" << finl;
3833
3834 const Triple_Line_Model_FT_Disc& tcl = ref_cast(Triple_Line_Model_FT_Disc, probleme_ft().tcl());
3835
3836 const ArrOfInt& elems_with_CL_contrib = tcl.elems();
3837 // const ArrOfInt& faces_with_CL_contrib = probleme_ft().tctl().boundary_faces();
3838 const ArrOfDouble& Q_from_CL = tcl.Q();
3839 // const ArrOfDouble& mpoint_from_CL = probleme_ft().tcl().mp();
3840
3841 // ArrOfInt elems_with_CL_contrib;
3842 // ArrOfInt faces_with_CL_contrib;
3843 // ArrOfDouble mpoint_from_CL;
3844 // ArrOfDouble Q_from_CL;
3845 // GB. 18/12/19. This call is actually the one filling the TCL tables (elems_, mp_ and Q_);
3846 // probleme_ft().tcl().compute_TCL_fluxes_in_all_boundary_cells(elems_with_CL_contrib,
3847 // faces_with_CL_contrib,
3848 // mpoint_from_CL,
3849 // Q_from_CL);
3850
3851 // Correct the field mpoint in wall-adjacent cells to account for TCL model:
3852 // ---> It's not added to mpoint now. Its contribution is added in
3853 // Convection_Diffusion_Temperature_FT_Disc::derivee_en_temps_inco
3854 // to be after the evaluation of the extended velocities (interfacial and liquid)
3855 // and their interpolation. That way, interpolation can still operate on a smooth field.
3856 // DoubleTab& mpoint = variables_internes().mpoint->valeurs();
3857 // probleme_ft().tcl().corriger_mpoint(elems_with_CL_contrib,mpoint_from_CL,mpoint);
3858
3859 const double Lvap = fluide_diph.chaleur_latente();
3860 const double coef = jump_inv_rho / Lvap;
3861 // Correct the secmem2 contribution due to TCL :
3862 if (!variables_internes().mpoint_inactif)
3863 probleme_ft().tcl().corriger_secmem(coef, secmem2);
3864
3865 const int check_consistency = 1; // local option to check that secmem2 in near-wall cell is actually well calculated
3866 if (check_consistency)
3867 {
3868 Cerr << "Verifying Contact line model consistency" << finl;
3869 double error = 0.;
3870 const int nb_contact_line_contribution = elems_with_CL_contrib.size_array();
3871 for (int idx = 0; idx < nb_contact_line_contribution; idx++)
3872 {
3873 const int elem = elems_with_CL_contrib[idx];
3874 const double sec = secmem2(elem);
3875 double Q = 0.;
3876 // Go through the list to find all occurences of elem;
3877 for (int idx2 = 0; idx2 < nb_contact_line_contribution; idx2++)
3878 {
3879 if (elem == elems_with_CL_contrib[idx2])
3880 {
3881 Q += Q_from_CL[idx2];
3882 }
3883 }
3884 const double value = coef * Q;
3885
3886 // sec and value should be the same:
3887 error += fabs(sec - value);
3888 if (fabs(sec - value) > 1.e-12) // changed from 1.e-12 to 1.e-7 ---- for test
3889 {
3890 Cerr << "local difference sec-value=" << sec << " - " << value << " = " << (sec - value) << finl;
3891 }
3892 }
3893
3894 if (error > 1.e-8)
3895 {
3896 Cerr << "Final error : " << error << " is fatal!" << finl;
3897 Process::exit();
3898 }
3899
3900 }
3901 }
3902
3903 secmem2 /= schema_temps().pas_de_temps();
3904
3905 if (variables_internes().shift_secmem2_)
3906 {
3907 shift_secmem2(secmem2);
3908 }
3909 secmem += secmem2;
3910 secmem.echange_espace_virtuel();
3911#if NS_VERBOSE
3912 double int_sec_mem2 = 0;
3913 double int_sec_mem = 0;
3914 for (int elem = 0; elem < nb_elem; elem++)
3915 {
3916 int_sec_mem2 +=secmem2(elem);
3917 int_sec_mem +=secmem(elem);
3918 }
3919 Cerr << "Integral of secmem2 after TCL and /DT : " << int_sec_mem2 << finl;
3920 Cerr << "Integral of secmem after TCL and /DT : " << int_sec_mem << finl;
3921#endif
3922 }
3923
3924 OWN_PTR(Champ_Fonc_base) champ_rho_faces_modifie(champ_rho_faces_);
3925 DoubleTab& rho_faces_modifie = champ_rho_faces_modifie->valeurs();
3926
3927 if (interf_vitesse_imposee_ok)
3928 {
3929
3930 // On verifie s il existe des interfaces IBC/fluide regularisees.
3931 int compteur_vimp_regul = 0;
3932 int nb_eqs_bis = variables_internes().ref_eq_interf_vitesse_imposee.size();
3933 for (int k = 0; k < nb_eqs_bis; k++)
3934 {
3935 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3936 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3937
3938 if (eq_transport.get_vimp_regul() == 1)
3939 compteur_vimp_regul++;
3940 }
3941
3942 // Creation d'un nouveau rho qui prend en compte les vitesses imposees dans
3943 // le terme de forcage
3944 // Si des interfaces IBC/fluide sont regularisees, la projection devient classique
3945 // (a ameliorer potentiellement).
3946 // Mais dans le cas ou on a une interface diphasique, on bloque toutes les vitesses impossees
3947 // en PDF (regularise ou non)
3948 int modif_rho_true = 0;
3949 if (variables_internes().is_penalized && (compteur_vimp_regul == 0 || refeq_transport_2pha))
3950 {
3951 for (int i = 0; i < nfaces; i++)
3952 {
3953 if (compteur_vimp_regul != 0)
3954 {
3955 for (int k = 0; k < nb_eqs; k++)
3956 {
3957 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
3958 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
3959 const DoubleTab& indicatrice_faces = eq_transport.get_indicatrice_faces().valeurs();
3960 if (eq_transport.get_vimp_regul() == 1 && indicatrice_faces(i) > 0.0 && indicatrice_faces(i) != 1.)
3961 terme_mul(i) += 1.0 / variables_internes().eta;
3962 }
3963 }
3964 rho_faces_modifie(i) *= terme_mul(i);
3965 }
3966 rho_faces_modifie.echange_espace_virtuel();
3967 modif_rho_true = 1;
3968 }
3969 Debog::verifier("Navier_Stokes_FT_Disc::derivee_en_temps_inco rho_faces_modifie:", rho_faces_modifie);
3970
3971 // Assemblage de la matrice INTEGRALE ( div(1/rho * grad(P)) ) :
3972 // (volume de controle pression)
3973
3974 assembleur_pression()->assembler_rho_variable(matrice_pression_, champ_rho_faces_modifie.valeur());
3975
3976 //Penalization L2 de la pression si necessaire
3977 if (modif_rho_true == 1 && variables_internes().p_ref_pena != -1.e40)
3978 {
3979 // On se base sur le nombre de composantes par faces pour la discretisation
3980 Matrice_Morse_Sym& matrice_valeurs = (vpoint.line_size() == 1 ? ref_cast(Matrice_Morse_Sym, (ref_cast(Matrice_Bloc, matrice_pression_.valeur())).get_bloc(0,0).valeur()) // VDF (1)
3981 :
3982 ref_cast(Matrice_Morse_Sym, matrice_pression_.valeur())); // VEF (>1)
3983 DoubleTab& pressu = la_pression->valeurs();
3984 assert(nb_elem == champ_rho_elem_->valeurs().dimension(0));
3985 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
3986 const Domaine& mon_dom = domaine_dis().domaine();
3987 const IntTab& elem_faces = domaine_vf.elem_faces();
3988 const int nb_faces_elem = elem_faces.line_size();
3989 const int nb_sommet = mon_dom.nb_som_elem();
3990 int numero_global_som, ligne_mat;
3991 int point_fluide_dirichlet = -1;
3992 if (variables_internes().is_pfl_flottant)
3993 {
3994 if (Objet_U::dimension == 3)
3995 {
3996 point_fluide_dirichlet = mon_dom.chercher_elements(variables_internes().x_pfl_imp, variables_internes().y_pfl_imp, variables_internes().z_pfl_imp);
3997 }
3998 else
3999 {
4000 point_fluide_dirichlet = mon_dom.chercher_elements(variables_internes().x_pfl_imp, variables_internes().y_pfl_imp);
4001 }
4002 if (mp_max(point_fluide_dirichlet) == -1)
4003 {
4004 Cerr << "Point de reference pression fluide situe en dehors du domaine !" << finl;
4005 Process::exit();
4006 }
4007 }
4008 for (int e = 0; e < nb_elem; e++)
4009 {
4010 int nbfglob = 0;
4011 int nbfpena = 0;
4012 for (int f = 0; f < nb_faces_elem; f++)
4013 {
4014 int fglob = elem_faces(e, f);
4015 if (fglob >= 0)
4016 {
4017 nbfglob += 1;
4018 int dejafait = 0;
4019 for (int k = 0; k < nb_eqs_bis && dejafait == 0; k++)
4020 {
4021 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
4022 const DoubleTab& indicatrice_faces = refeq_transport->get_indicatrice_faces().valeurs();
4023 if (indicatrice_faces(fglob) > 0.0)
4024 {
4025 dejafait = 1;
4026 nbfpena += 1;
4027 }
4028 }
4029 }
4030 }
4031 if (nbfpena == nbfglob && nbfpena != 0)
4032 {
4033 matrice_valeurs(e, e) += 1.0 / variables_internes().eta;
4034 secmem(e) += variables_internes().p_ref_pena / variables_internes().eta;
4035 pressu(e) = variables_internes().p_ref_pena;
4036 if (vpoint.line_size() > 1) // VEF
4037 {
4038 for (int somloc = 0; somloc < nb_sommet; somloc++)
4039 {
4040 numero_global_som = mon_dom.sommet_elem(e, somloc);
4041 ligne_mat = nb_elem + numero_global_som;
4042 // matrice_valeurs(ligne_mat,ligne_mat) += 1.0 / variables_internes().eta;
4043 pressu(ligne_mat) = 0.0;
4044 }
4045 }
4046 }
4047 if (point_fluide_dirichlet == e)
4048 {
4049 if (nbfpena != nbfglob && nbfpena != 0)
4050 {
4051 // On impose une reference de pression (p_ref)sur un bord
4052 secmem(e) += matrice_valeurs(e, e) * variables_internes().p_ref_pena / (float(nbfglob - nbfpena));
4053 matrice_valeurs(e, e) *= float(nbfglob - nbfpena + 1) / (float(nbfglob - nbfpena));
4054 }
4055 else
4056 {
4057 Cerr << "Point de reference pression fluide non situe dans une cellule fluide voisin d'une IBC !" << finl;
4058 Cerr << "Nb faces IBC = " << nbfpena << finl;
4059 Process::exit();
4060 }
4061 }
4062 }
4063 secmem.echange_espace_virtuel();
4064 pressu.echange_espace_virtuel();
4065 }
4066
4067 // On a modifie la matrice, il faut reinitialiser le solveur
4068 // (recalcul des preconditionnement, factorisation pour Cholesky, etc...)
4069 solveur_pression()->reinit();
4070 }
4071
4072 assembleur_pression_->modifier_secmem(secmem);
4073
4074 // Resolution du systeme en pression : calcul de la_pression
4075 statistics().begin_count(STD_COUNTERS::system_solver,statistics().get_last_opened_counter_level()+1);
4076 solveur_pression_->resoudre_systeme(matrice_pression_.valeur(), secmem, la_pression->valeurs());
4077 statistics().end_count(STD_COUNTERS::system_solver);
4078
4079 assembleur_pression_->modifier_solution(la_pression->valeurs());
4080 // Calcul d(u)/dt = vpoint + 1/rho*grad(P)
4081 gradient.calculer(la_pression->valeurs(), gradP);
4082 solveur_masse->appliquer(gradP);
4083
4084 // Correction de vpoint :
4085 if (projection_a_faire()) // Temporaire pour permettre de ne pas resoudre NS avec mettant operateurs nuls et projection_initiale 0
4086 {
4087 const int nbis = vpoint.dimension(0);
4088 const int mbis = vpoint.line_size();
4089 for (int i = 0; i < nbis; i++)
4090 for (int j = 0; j < mbis; j++)
4091 vpoint(i, j) -= gradP(i, j) / rho_faces_modifie(i);
4092
4093 vpoint.echange_espace_virtuel();
4094 }
4095
4096 // Calcul des efforts sur les IBCs et impression dans un fichier
4097 if (interf_vitesse_imposee_ok && limpr())
4098 {
4099 const DoubleTab& rho = champ_rho_faces_->valeurs();
4100
4101 DoubleTrav forces_tot_2(vpoint);
4102 DoubleTrav pressure_part(vpoint);
4103 DoubleTrav diffusion_part(vpoint);
4104 DoubleTrav diffusion_part2(vpoint);
4105
4106 //Calcul de la vitesse au temps n+1
4107 DoubleTab vv(vpoint);
4108 vv *= schema_temps().pas_de_temps();
4109 vv += inconnue().valeurs();
4110
4111 // Terme de diffusion
4112 terme_diffusif.calculer(vv, variables_internes().terme_diffusion->valeurs());
4113 variables_internes().terme_diffusion->valeurs().echange_espace_virtuel();
4114 solveur_masse->appliquer(variables_internes().terme_diffusion->valeurs());
4115 const DoubleTab& diffusion = variables_internes().terme_diffusion->valeurs();
4116 // Terme de convection
4117 DoubleTrav trav(variables_internes().terme_convection->valeurs());
4118 derivee_en_temps_conv(trav, la_vitesse->valeurs());
4119 variables_internes().terme_convection->valeurs() = trav;
4120 solveur_masse->appliquer(variables_internes().terme_convection->valeurs());
4121 const DoubleTab& convection = variables_internes().terme_convection->valeurs();
4122 const int nbis = vpoint.dimension(0);
4123 const int mbis = vpoint.line_size();
4124
4125 for (int i = 0; i < nbis; i++)
4126 for (int j = 0; j < mbis; j++)
4127 {
4128 pressure_part(i, j) = gradP(i, j);
4129 diffusion_part(i, j) = -rho(i) * convection(i, j) - diffusion(i, j);
4130 diffusion_part2(i, j) = - diffusion(i, j);
4131 forces_tot_2(i, j) = pressure_part(i, j) + diffusion_part(i, j);
4132 }
4133
4134 int nb_eqs_bis = variables_internes().ref_eq_interf_vitesse_imposee.size();
4135 for (int k = 0; k < nb_eqs_bis; k++)
4136 {
4137 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_vitesse_imposee[k];
4138 Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
4139 // Impression des efforts
4140 eq_transport.impr_effort_fluide_interface(forces_tot_2, pressure_part, diffusion_part, diffusion_part2);
4141 }
4142 }
4143
4144 return vpoint;
4145}
4146
4148{
4149 return probleme_ft_.valeur();
4150}
4151
4153{
4154 return probleme_ft_.valeur();
4155}
4156
4157/*! @brief In Front Tracking, pression is in Pa and so pression_pa field <=> pression field
4158 *
4159 */
4164
4165const Navier_Stokes_FT_Disc_interne& Navier_Stokes_FT_Disc::variables_internes() const
4166{
4167 return variables_internes_;
4168}
4169Navier_Stokes_FT_Disc_interne& Navier_Stokes_FT_Disc::variables_internes()
4170{
4171 return variables_internes_;
4172}
4173
4174/*! @brief Si le champ de vitesse est discontinu (calcul avec changement de phase), renvoie un pointeur vers le champ delta_v de "discontinuite", tel que
4175 *
4176 * inconnue - delta_v = vitesse de deplacement des interfaces
4177 * (voir Transport_Interfaces_FT_Disc::deplacer_maillage_v_fluide())
4178 * Si pas de changement de phase, renvoie un pointeur nul.
4179 *
4180 */
4182{
4183 if (variables_internes().ref_equation_mpoint_ ||
4184 variables_internes().ref_equation_mpoint_vap_)
4185 return &(variables_internes().delta_u_interface.valeur());
4186 else
4187 return 0;
4188}
4189// Description : calcul de div(n) (la courbure discretisee sur le maillage volumique)
4190// Faudrait deplacer cette methode dans transport interfaces...
4192{
4193 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().ref_eq_interf_proprietes_fluide;
4194 const Transport_Interfaces_FT_Disc& eq_transport = refeq_transport.valeur();
4195 // Distance a l'interface discretisee aux elements:
4196 const DoubleTab& dist = eq_transport.get_distance_interface().valeurs();
4197
4198 DoubleTab& phi = variables_internes().laplacien_d->valeurs();
4199 DoubleTab u0(inconnue().valeurs());
4200
4201 phi = dist;
4202
4203 const int n = phi.dimension(0);
4204 for (int i = 0; i < n; i++)
4205 {
4206 if (phi(i) < -1e20)
4207 phi(i) = 0;
4208 }
4210 gradient.calculer(phi, u0);
4212 solveur_masse->appliquer(u0);
4213
4214 // Calcul de integrale(div(u0)) sur les mailles:
4215 divergence.calculer(u0, phi);
4216 // Division par le volume des mailles:
4217 const DoubleVect& volumes = ref_cast(Domaine_VF, domaine_dis()).volumes();
4218 for (int i = 0; i < n; i++)
4219 {
4220 const double p = phi(i);
4221 if (p != 0.)
4222 {
4223 const double v = volumes[i];
4224 phi(i) = p / v;
4225 }
4226 }
4227
4228 return variables_internes().laplacien_d.valeur();
4229}
4230
4232{
4233 return champ_rho_faces_;
4234}
4235
4236//Renvoie 1 si l option GRAVITE_RHO_G est activee 0 sinon
4238{
4239 if (variables_internes().terme_gravite_ == Navier_Stokes_FT_Disc_interne::GRAVITE_RHO_G)
4240 return 1;
4241 else
4242 return 0;
4243}
4244
4245/*
4246 void Navier_Stokes_FT_Disc::corriger_mpoint()
4247 {
4248 if (probleme_ft().tcl().is_activated())
4249 {
4250 DoubleTab& mpoint = variables_internes().mpoint->valeurs();
4251 probleme_ft().tcl().corriger_mpoint(mpoint);
4252 }
4253 }
4254 */
4255
4257 const OWN_PTR(Collision_Model_FT_base)& collision_model_ptr)
4258{
4259 const Domaine_VF& domaine_vf = ref_cast(Domaine_VF, domaine_dis());
4260 const int nb_elem=domaine_vf.nb_elem();
4261 OBS_PTR(Transport_Interfaces_FT_Disc) &refeq_transport = variables_internes().\
4262 ref_eq_interf_proprietes_fluide;
4263 const DoubleTab& volumic_phase_indicator_function = refeq_transport->\
4264 get_indicatrice().valeurs();
4265 const int& id_fluid_phase=fluide_diphasique().get_id_fluid_phase();
4266
4267 for (int elem = 0; elem < nb_elem; elem++)
4268 {
4269 if (collision_model_ptr &&
4270 collision_model_ptr.valeur().get_is_force_on_two_phase_elem()==1)
4271 particles_eulerian_id_number_(elem) = (volumic_phase_indicator_function[elem]
4272 == id_fluid_phase) ? -1 : 1;
4273 else
4274 particles_eulerian_id_number_(elem) = (volumic_phase_indicator_function[elem]
4275 != 1 - id_fluid_phase ) ? -1 : 1;
4276 }
4277 particles_eulerian_id_number_.echange_espace_virtuel();
4278 const IntTab& elem_faces = domaine_vf.elem_faces();
4279 const IntTab& faces_elem = domaine_vf.face_voisins();
4280 const int nb_local_connex_components = search_connex_components_local(elem_faces,
4281 faces_elem, particles_eulerian_id_number_);
4282 compute_global_connex_components(particles_eulerian_id_number_, nb_local_connex_components);
4283 particles_eulerian_id_number_.echange_espace_virtuel();
4284}
4285
4286void Navier_Stokes_FT_Disc::swap_particles_eulerian_id_number(const ArrOfInt& gravity_center_elem)
4287{
4288 const int nb_elem=particles_eulerian_id_number_.dimension(0);
4289 const int nb_elem_tot=particles_eulerian_id_number_.dimension_tot(0);
4290 const int nb_particles_tot=gravity_center_elem.size_array();
4291 // We associate the particle lagrangian ID number to the particle eulerian ID number
4292 // The link between lagrange and euler ID numbers is made with the gravity center of each particle.
4293 // Example for 3 particles:
4294 // gravity_center_elem (1025, 56, 4058) -> result of chercher_elements(particles_position_collision,
4295 // gravity_center_elem)
4296 // if id_number=1, elem=56. particles_eulerian_id_number(56)=2 (example) ->
4297 // result of compute_global_connex_components
4298 // Then, particle_lagrangian_id_number(1)=2
4299 IntVect particle_lagrangian_id_number(nb_particles_tot);
4300 particle_lagrangian_id_number=0;
4301 for (int id_number=0; id_number<nb_particles_tot; id_number++)
4302 {
4303 int elem=gravity_center_elem(id_number); // the particle id_number has its gravity
4304 // center inside elem
4305 if (elem>-1 && elem < nb_elem_tot)
4306 particle_lagrangian_id_number(particles_eulerian_id_number_(elem))=id_number;
4307
4308 }
4309 // now, each eulerian id number is linked to a lagrangian id number
4310 mp_max_for_each_item(particle_lagrangian_id_number);
4311 // swap
4312 for (int elem = 0; elem < nb_elem; elem++)
4313 {
4314 if (particles_eulerian_id_number_(elem) != -1)
4315 {
4317 particle_lagrangian_id_number(particles_eulerian_id_number_(elem));
4318 }
4319 }
4320 particles_eulerian_id_number_.echange_espace_virtuel();
4321}
4322
4324(const Maillage_FT_Disc& mesh, const Champ_base& field_volumic_phase_indicator_function)
4325{
4326 Cerr << "Navier_Stokes_FT_Disc::compute_eulerian_field_contact_forces" << finl;
4327
4328 statistics().create_custom_counter("compute_eulerian_field_contact_forces",3,"FrontTracking");
4329 statistics().begin_count("compute_eulerian_field_contact_forces",statistics().get_last_opened_counter_level()+1);
4330
4331 auto& eq_transport=variables_internes().ref_eq_interf_proprietes_fluide.valeur();
4332 const auto& eq_transport_const=variables_internes().ref_eq_interf_proprietes_fluide.valeur();
4333 const Navier_Stokes_FT_Disc& eq_ns = *this;
4334 Collision_Model_FT_base& collision_model=eq_transport.get_set_collision_model();
4335 //const OWN_PTR(Collision_Model_FT_base)& collision_model_ptr=eq_transport.get_ptr_collision_model();
4336 const DoubleTab& particles_position=eq_transport.get_particles_position();
4337 const DoubleTab& particles_velocity=eq_transport.get_particles_velocity();
4338 const Fluide_Diphasique& two_phase_fluid = fluide_diphasique();
4339
4340 // Step 1: Collision detection
4341 //const ArrOfInt& gravity_center_elem = eq_transport.get_gravity_center_elem();
4342 int& nb_dt_Verlet = collision_model.get_set_nb_dt_Verlet();
4343 const int nb_dt_compute_Verlet_ = collision_model.get_nb_dt_compute_Verlet();
4344 const double delta_t=schema_temps().pas_de_temps();
4345 const double nb_dt=schema_temps().nb_pas_dt();
4346 if (collision_model.is_Verlet_activated())
4347 {
4348 if (nb_dt_Verlet >= nb_dt_compute_Verlet_ || nb_dt == 0) // we compute it at every restart
4349 collision_model.research_collision_pairs_Verlet(eq_ns,eq_transport_const);
4350 nb_dt_Verlet++;
4351 }
4352 // Step 2: Contact forces computation
4353 collision_model.compute_lagrangian_contact_forces(two_phase_fluid,
4354 particles_position,
4355 particles_velocity,
4356 delta_t);
4357
4358
4359 // Step 3: conservation of eulerian id number
4360 // to discretize lagrangian collision force on the eulerian field
4361 // we need to update the particle eulerian ID number.
4362 // The procedure calls methods: search_connex_components_local
4363 // and compute_global_connex_components. During the procedure
4364 // particles eulerian ID number may change .A given particle will
4365 // therefore have a different Eulerian identification number from that
4366 // of the previous time step. To conserve the particle eulerian ID number
4367 // we use the particle lagrangian ID number.
4368 /*
4369 compute_particles_eulerian_id_number(collision_model_ptr);
4370 swap_particles_eulerian_id_number(gravity_center_elem);
4371 */
4372 // Step 4: Discretisation of the lagrangian contact forces to the eulerian field
4373 const Domaine_VF& domain_vf=ref_cast(Domaine_VF,domaine_dis());
4374 const DoubleTab& volumic_phase_indicator_function_face =
4375 eq_transport.get_indicatrice_faces().valeurs();
4376 DoubleTab& contact_force = variables_internes().contact_force_source_term->valeurs();
4377 contact_force=0;
4379 volumic_phase_indicator_function_face,
4380 domain_vf,
4382 contact_force);
4383
4384 statistics().end_count("compute_eulerian_field_contact_forces");
4385}
int set_resoudre_en_u(int flag)
Definit la valeur du drapeau resoudre_en_u__.
virtual void associer_domaine_dis_base(const Domaine_dis_base &)=0
int set_resoudre_increment_pression(int flag)
Definit la valeur du drapeau resoudre_increment_pression_.
classe Champ_Don_base classe de base des Champs donnes (non calcules)
void mettre_a_jour(double temps) override
Mise a jour en temps.
DoubleTab & valeurs() override
Surcharge Champ_base::valeurs() Renvoie le tableau des valeurs.
classe Champ_Fonc_base Classe de base des champs qui sont fonction d'une grandeur calculee
void mettre_a_jour(double temps) override
Mise a jour en temps du champ.
DoubleTab & valeurs() override
Renvoie le tableau des valeurs du champ au temps courant.
virtual DoubleTab & futur(int i=1)
Definition Champ_Proto.h:47
virtual DoubleTab & valeurs()=0
classe Champ_Uniforme Represente un champ constant dans l'espace et dans le temps.
classe Champ_base Cette classe est la base de la hierarchie des champs.
Definition Champ_base.h:43
virtual DoubleTab & valeur_aux_elems(const DoubleTab &positions, const IntVect &les_polys, DoubleTab &valeurs) const
provoque une erreur ! doit etre surchargee par les classes derivees
: class Collision_Model_FT
const int & get_nb_dt_compute_Verlet() const
virtual void discretize_contact_forces_eulerian_field(const DoubleTab &volumic_phase_indicator_function, const Domaine_VF &domain_vf, const IntTab &particles_eulerian_id_number, DoubleTab &contact_force_source_term)=0
void research_collision_pairs_Verlet(const Navier_Stokes_FT_Disc &eq_ns, const Transport_Interfaces_FT_Disc &eq_transport)
virtual void compute_lagrangian_contact_forces(const Fluide_Diphasique &two_phase_fluid, const DoubleTab &particles_position, const DoubleTab &particles_velocity, const double &deltat_simu)=0
const int & get_is_force_on_two_phase_elem() const
classe Cond_lim Classe generique servant a representer n'importe quelle classe
Definition Cond_lim.h:31
const Champ_Inc_base & inconnue() const override
static void verifier(const char *const msg, double)
Definition Debog.cpp:21
Classe Dirichlet_homogene Cette classe est la classe de base de la hierarchie des conditions aux limi...
classe Dirichlet Cette classe est la classe de base de la hierarchie des conditions aux limites de ty...
Definition Dirichlet.h:31
classe Discretisation_base Cette classe represente un schema de discretisation en espace,...
void discretiser_champ(const Motcle &directive, const Domaine_dis_base &z, const Nom &nom, const Nom &unite, int nb_comp, int nb_pas_dt, double temps, OWN_PTR(Champ_Inc_base)&champ, const Nom &sous_type=NOM_VIDE) const
int nb_som_elem() const
Renvoie le nombre de sommets des elements geometriques constituants le domaine.
Definition Domaine.h:474
int_t nb_elem_tot() const
Definition Domaine.h:132
SmallArrOfTID_t & chercher_elements(const DoubleTab &pos, SmallArrOfTID_t &elem, int reel=0) const
Recherche des elements contenant les points dont les coordonnees sont specifiees.
Definition Domaine.cpp:405
virtual void creer_tableau_elements(Array_base &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT) const
creation d'un tableau parallele de valeurs aux elements.
Definition Domaine.cpp:851
int_t nb_elem() const
Definition Domaine.h:131
int_t sommet_elem(int_t i, int j) const
Renvoie le numero (global) du j-ieme sommet du i-ieme element.
Definition Domaine.h:136
class Domaine_Cl_VDF
classe Domaine_Cl_dis_base Les objets Domaine_Cl_dis_base representent les conditions aux limites
const Cond_lim & les_conditions_limites(int) const
Renvoie la i-ieme condition aux limites.
class Domaine_VDF
Definition Domaine_VDF.h:64
int orientation(int) const override
inline DoubleVect& Domaine_VDF::porosite_face() {
class Domaine_VF
Definition Domaine_VF.h:44
virtual const DoubleVect & face_surfaces() const
Definition Domaine_VF.h:51
int nb_faces() const
renvoie le nombre global de faces.
Definition Domaine_VF.h:471
virtual double face_normales(int face, int comp) const
Definition Domaine_VF.h:47
double xv(int num_face, int k) const
Definition Domaine_VF.h:76
double volumes(int i) const
Definition Domaine_VF.h:113
virtual const IntVect & orientation() const
Definition Domaine_VF.h:646
int elem_faces(int i, int j) const
renvoie le numero de le ieme face de la maille num_elem la facon dont ces faces sont numerotees est
Definition Domaine_VF.h:543
double xp(int num_elem, int k) const
Definition Domaine_VF.h:77
int face_voisins(int num_face, int i) const
renvoie l'element voisin de numface dans la direction i.
Definition Domaine_VF.h:418
const Nom & le_nom() const override
Donne le nom de l'Objet_U Methode a surcharger : renvoie "neant" dans cette implementation.
classe Domaine_dis_base Cette classe est la base de la hierarchie des domaines discretisees.
virtual IntTab & face_voisins()
int nb_front_Cl() const
const Domaine & domaine() const
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
classe Equation_base Le role d'une equation est le calcul d'un ou plusieurs champs....
const Nom & le_nom() const override
Renvoie le nom de l'equation.
virtual void associer_milieu_base(const Milieu_base &)=0
virtual const Milieu_base & milieu() const =0
DoubleTab & derivee_en_temps_conv(DoubleTab &, const DoubleTab &)
Add convection term In: solution is the unknown I.
Sources & sources()
Renvoie les termes sources asssocies a l'equation.
const Discretisation_base & discretisation() const
Renvoie la discretisation associee a l'equation.
LIST(RefObjU) liste_modeles_
Sources les_sources
virtual int preparer_calcul()
Tout ce qui ne depend pas des autres problemes eventuels.
virtual Domaine_Cl_dis_base & domaine_Cl_dis()
Renvoie le domaine des conditions aux limite discretisee associee a l'equation.
Probleme_base & probleme()
Renvoie le probleme associe a l'equation.
int limpr() const
Demande au schema en temps si il faut effectuer une impression.
Schema_Temps_base & schema_temps()
Renvoie le schema en temps associe a l'equation.
void Gradient_conjugue_diff_impl(DoubleTrav &secmem, DoubleTab &solution)
virtual void discretiser()
Discretise l'equation.
Champs_compris champs_compris_
Domaine_dis_base & domaine_dis()
Renvoie le domaine discretise associe a l'equation.
double chaleur_latente() const
const Fluide_Incompressible & fluide_phase(int la_phase) const
const int & get_id_fluid_phase() const
classe Fluide_Incompressible Cette classe represente un d'un fluide incompressible ainsi que
const Champ_Don_base & viscosite_dynamique() const
Definition Fluide_base.h:60
const Champ_Don_base & viscosite_cinematique() const
Definition Fluide_base.h:58
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
: class Intersections_Elem_Facettes
const ArrOfInt & index_elem() const
Renvoie un tableau de taille domaine.
const ArrOfInt & index_facette() const
Renvoie un tableau de taille "nombre de facettes de l'interface" pour un element 0 <= facette < nb_fa...
const Intersections_Elem_Facettes_Data & data_intersection(int index) const
Renvoie les donnees de l'intersection stockee a l'indice "index" dans le tableau "data" ( 0 <= index ...
static void echange_espace_virtuel(IntVect &, Operations_echange opt=ECHANGE_EV, IsExchangeBlocking is_exchange_blocking=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")
: class Maillage_FT_Disc Cette classe decrit un maillage:
int nb_sommets() const
renvoie le nombre de sommets (reels et virtuels) (egal a sommets().
const DoubleTab & sommets() const
renvoie le tableau des sommets (reels et virtuels) dimension(0) = nombre de sommets,
int nb_facettes() const
renvoie le nombre de facettes (reelles et virtuelles) (egal a facettes().
virtual const DoubleTab & get_update_normale_facettes() const
Calcule la grandeur demandee, stocke le resultat dans un tableau interne a la classe et renvoie le re...
virtual double compute_normale_element(const int elem, const bool normalize, ArrOfDouble &normale) const
const Intersections_Elem_Facettes & intersections_elem_facettes() const
virtual const ArrOfDouble & get_update_surface_facettes() const
Calcule la grandeur demandee, stocke le resultat dans un tableau interne a la classe et renvoie le re...
virtual const ArrOfDouble & get_update_courbure_sommets() const
Calcule la grandeur demandee, stocke le resultat dans un tableau interne a la classe et renvoie le re...
const IntTab & facettes() const
renvoie le tableau des facettes (reelles et virtuelles) dimension(0) = nombre de facettes,
Classe Matrice_Morse_Sym Represente une matrice M (creuse) symetrique stockee au format Morse.
classe Milieu_base Cette classe est la base de la hierarchie des milieux (physiques)
Definition Milieu_base.h:50
virtual const Champ_Don_base & beta_t() const
Renvoie beta_t du milieu.
virtual const Champ_base & masse_volumique() const
Renvoie la masse volumique du milieu.
virtual const Champ_Don_base & gravite() const
Renvoie la gravite du milieu si elle a ete associe provoque une erreur sinon.
void associer_eqn(const Equation_base &)
Associe une equation a l'objet.
Definition MorEqn.cpp:28
Une chaine de caractere (Nom) en majuscules.
Definition Motcle.h:26
Un tableau d'objets de la classe Motcle.
Definition Motcle.h:63
int search(const Motcle &t) const
Definition Motcle.cpp:321
const DoubleTab & get_mpoint() const
void associer_milieu_base(const Milieu_base &fluide) override
virtual void calculer_gradient_indicatrice(const Champ_base &indicatrice, const DoubleTab &distance_interface_sommets, Champ_base &gradient_i)
Calcul du gradient de l'indicatrice.
DoubleTab & derivee_en_temps_inco(DoubleTab &vpoint) override
Calcul de la derivee en temps de la vitesse.
void projeter() override
methode appelee par Navier_Stokes_std::preparer_calcul.
const SolveurSys & get_solveur_pression() const
virtual const Champ_base * get_delta_vitesse_interface() const
Si le champ de vitesse est discontinu (calcul avec changement de phase), renvoie un pointeur vers le ...
void discretiser_assembleur_pression() override
Methode surchargee de Navier_Stokes_std, appelee par Navier_Stokes_std::discretiser().
void set_id_fluid_phase(const int &id_fluid_phase)
void calculer_delta_u_interface(Champ_base &u0, int phase_pilote, int ordre, const bool future_or_past=false)
Calcul du saut de vitesse a l'interface du au changement de phase.
virtual void calculer_champ_forces_superficielles(const Maillage_FT_Disc &maillage, const Champ_base &gradient_indicatrice, Champ_base &potentiel_elements, Champ_base &potentiel_faces, Champ_base &champ)
Calcul des forces de tension superficielles (F_sigma) et de la partie a rotationnel non nul de la gra...
void compute_particles_eulerian_id_number(const OWN_PTR(Collision_Model_FT_base)&collision_model_ptr)
const DoubleTab & get_interfacial_area() const
void mettre_a_jour(double temps) override
La valeur de l'inconnue sur le pas de temps a ete calculee.
int preparer_calcul() override
methode appelee par Probleme_base::preparer_calcul()
void associer_pb_base(const Probleme_base &probleme) override
S'associe au Probleme passe en parametre.
const int & get_is_penalized() const
void compute_boussinesq_additional_gravity(const Convection_Diffusion_Temperature_FT_Disc &eq, const Fluide_Diphasique &fluide_diphasique, const IntTab &face_voisins, const DoubleVect &volumes_entrelaces, const IntVect &orientation, const DoubleTab &indicatrice, const ArrOfDouble &g, DoubleTab &gravite_face) const
OWN_PTR(Champ_Fonc_base) champ_rho_elem_
const Milieu_base & milieu() const override
OBS_PTR(Probleme_FT_Disc_gen) probleme_ft_
virtual const Probleme_FT_Disc_gen & probleme_ft() const
void shift_secmem2(DoubleTab &shift_secmem2)
void compute_eulerian_field_contact_forces(const Maillage_FT_Disc &mesh, const Champ_base &field_volumic_phase_indicator_function)
int reprendre(Entree &) override
On reprend l'inconnue a partir d'un flot d'entree.
void set_param(Param &titi) const override
virtual void calculer_dI_dt(DoubleVect &dI_dt, const DoubleTab &tab_vitesse)
virtual const Fluide_Diphasique & fluide_diphasique() const
int sauvegarder(Sortie &) const override
On sauvegarde l'inconnue, puis les sources sur un flot de sortie.
const Champ_Don_base & diffusivite_pour_transport() const override
void set_is_solid_particle(const bool &is_solid_particle)
void calculer_la_pression_en_pa() override
In Front Tracking, pression is in Pa and so pression_pa field <=> pression field.
void discretiser() override
Discretisation des champs utilises dans cette equation.
virtual const Champ_base & calculer_div_normale_interface()
void correct_at_exit_bad_gradient(DoubleTab &u0) const
friend class Post_Processing_Hydrodynamic_Forces
std::vector< YAML_data > data_a_sauvegarder() const override
for PDI IO: retrieve name, type and dimensions of the fields to save/restore
void mettre_a_jour_physical_properties(double temps)
void swap_particles_eulerian_id_number(const ArrOfInt &gravity_center_elem)
const Champ_Fonc_base & champ_rho_faces() const
int lire_motcle_non_standard(const Motcle &, Entree &) override
Lecture des parametres de type non simple d'un objet_U a partir d'un flot d'entree.
classe Navier_Stokes_Turbulent Cette classe represente l'equation de la dynamique pour un fluide
std::vector< YAML_data > data_a_sauvegarder() const override
for PDI IO: retrieve name, type and dimensions of the fields to save/restore
void mettre_a_jour(double) override
Effecttue une mise a jour en temps de l'equation.
int sauvegarder(Sortie &) const override
Sauvegarde l'equation (et son modele de turbulence) sur un flot de sortie.
int reprendre(Entree &) override
Reprise de l'equation et de son modele de turbulence a partir d'un flot d'entree.
void set_param(Param &titi) const override
int lire_motcle_non_standard(const Motcle &, Entree &) override
Lecture des parametres de type non simple d'un objet_U a partir d'un flot d'entree.
Entree & lire_op_diff_turbulent(Entree &is)
Operateur_Diff terme_diffusif
Operateur_Grad gradient
Operateur_Conv terme_convectif
const Champ_Inc_base & inconnue() const override
Renvoie la vitesse (champ inconnue de l'equation) (version const).
const Fluide_base & fluide() const
Renvoie le fluide incompressible (milieu physique de l'equation) associe a l'equation.
void associer_pb_base(const Probleme_base &) override
S'associe au probleme: apelle Equation_base::associer_pb_base(const Probleme_base&).
Operateur_Div divergence
virtual int projection_a_faire()
SolveurSys & solveur_pression()
Renvoie le solveur en pression (version const).
classe Neumann_sortie_libre Cette classe represente une frontiere ouverte sans vitesse imposee
class Nom Une chaine de caractere pour nommer les objets de TRUST
Definition Nom.h:31
Un tableau de chaine de caracteres (VECT(Nom)).
Definition Noms.h:26
static int dimension
Definition Objet_U.h:99
const Nom & que_suis_je() const
renvoie la chaine identifiant la classe.
Definition Objet_U.cpp:104
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
static int bidim_axi
Definition Objet_U.h:102
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
classe Operateur_Div Classe generique de la hierarchie des operateurs calculant la divergence
Operateur_base & l_op_base() override
Renvoie l'objet sous-jacent upcaste en Operateur_base.
void typer() override
Type l'operateur: se type "Op_div_"+discretisation()+.
DoubleTab & calculer(const DoubleTab &, DoubleTab &) const override
Initialise le tableau passe en parametre avec la contribution de l'operateur.
DoubleTab & ajouter(const DoubleTab &, DoubleTab &) const override
Ajoute la contribution de l'operateur au tableau passe en parametre.
void associer_eqn(const Equation_base &)
Associe une equation a l'operateur.
virtual void completer()
Met a jour les references des objets associes a l'operateur.
Helper class to factorize the readOn method of Objet_U classes.
Definition Param.h:112
void ajouter_flag(const char *keyword, const bool *value)
Register a boolean flag whose mere presence switches it to true.
Definition Param.cpp:474
void ajouter(const char *keyword, const int *value, Param::Nature nat=Param::OPTIONAL)
Register an integer parameter.
Definition Param.cpp:364
void ajouter_non_std(const char *keyword, const Objet_U *value, Param::Nature nat=Param::OPTIONAL)
Register a keyword handled by Objet_U::lire_motcle_non_standard.
Definition Param.cpp:489
classe Parametre_implicite Un objet Parametre_implicite est un objet regroupant les differentes
classe Periodique Cette classe represente une condition aux limites periodique.
Definition Periodique.h:31
virtual const Transport_Interfaces_FT_Disc & equation_interfaces(const Motcle &nom) const
const Equation_base & get_equation_by_name(const Nom &le_nom) const override
(B. Math): Methode virtuelle ajoutee pour les problemes ayant plusieurs equations de meme type (Probl...
const Triple_Line_Model_FT_Disc & tcl() const
classe Probleme_base C'est un Probleme_U qui n'est pas un couplage.
const Domaine & domaine() const
Renvoie le domaine associe au probleme.
virtual const Equation_base & get_equation_by_name(const Nom &) const
(B. Math): Methode virtuelle ajoutee pour les problemes ayant plusieurs equations de meme type (Probl...
static void mp_max_for_each_item(TRUSTArray< _TYPE_ > &x, int n=-1)
Definition Process.cpp:196
static int check_int_overflow(trustIdType)
Definition Process.cpp:428
static void mp_sum_for_each(T &arg1, T &arg2)
C++14 compatible mp_sum_for_each: combine multiple mp_sum calls into one collective operation Usage: ...
Definition Process.cpp:207
static double mp_max(double)
Definition Process.cpp:376
static double mp_sum(double)
Calcule la somme de x sur tous les processeurs du groupe courant.
Definition Process.cpp:146
static void barrier()
Synchronise tous les processeurs du groupe courant (attend que tous les processeurs soient arrives a ...
Definition Process.cpp:136
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
double temps_courant() const
Renvoie le temps courant.
double pas_de_temps() const
Renvoie le pas de temps (delta_t) courant.
int nb_pas_dt() const
Renvoie le nombre de pas de temps effectues.
class SolveurSys Un SolveurSys represente n'importe qu'elle classe
Definition SolveurSys.h:32
Classe de base des flux de sortie.
Definition Sortie.h:52
classe Symetrie Sur les faces de symetrie on a les proprietes suivantes:
Definition Symetrie.h:37
void append_array(_TYPE_ valeur)
_SIZE_ size_array() const
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)
int nb_dim() const
Definition TRUSTTab.h:199
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
_SIZE_ dimension_tot(int) const override
Definition TRUSTTab.tpp:160
void copy(const TRUSTTab &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:622
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
_SIZE_ size() const
Definition TRUSTVect.tpp:45
_SIZE_ size_totale() const
Definition TRUSTVect.tpp:61
int line_size() const
Definition TRUSTVect.tpp:67
_TYPE_ mp_max_abs_vect(Mp_vect_options opt=VECT_REAL_ITEMS) const
Definition TRUSTVect.h:160
_SIZE_ size_reelle() const
Definition TRUSTVect.tpp:27
virtual void echange_espace_virtuel(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")
void ajouter_terme_div_u(DoubleVect &secmem_pression, double dt) const
virtual const Champ_base & get_indicatrice_faces()
virtual const DoubleTab & get_update_distance_interface_sommets() const
Renvoi de la distance signee entre l'interface et les sommets du maillage eulerien.
void impr_effort_fluide_interface(DoubleTab &source_val, DoubleTab &pressure_part, DoubleTab &friction_part, DoubleTab &diff_part)
const Champ_Inc_base & inconnue() const override
virtual const Champ_base & get_normale_interface() const
const Maillage_FT_Disc & maillage_interface() const
void calcul_effort_fluide_interface(const DoubleTab &vpoint, const DoubleTab &rho_faces, DoubleTab &source_val, const int is_explicite, const double eta)
virtual const Champ_base & get_distance_interface() const
void modifier_vpoint_pour_imposer_vit(const DoubleTab &inco_val, DoubleTab &vpoint0, DoubleTab &vpoint, const DoubleTab &rho_faces, DoubleTab &terme_source, const double temps, const double dt, const int is_explicite, const double eta) override
void corriger_secmem(const double coef, DoubleTab &secmem2) const
void corriger_mpoint(DoubleTab &mpoint) const