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