TrioCFD 1.9.8
TrioCFD documentation
Loading...
Searching...
No Matches
Op_Diff_PolyMAC_MPFA_Elem.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15
16#include <Convection_Diffusion_Temperature.h>
17#include <Flux_interfacial_PolyMAC_HFV.h>
18#include <Echange_contact_PolyMAC_MPFA.h>
19#include <Op_Diff_PolyMAC_MPFA_Elem.h>
20#include <Echange_externe_impose.h>
21#include <Champ_Elem_PolyMAC_MPFA.h>
22#include <Flux_parietal_base.h>
23#include <Domaine_PolyMAC_MPFA.h>
24#include <Domaine_Cl_PolyMAC_family.h>
25#include <Milieu_composite.h>
26#include <Option_PolyMAC_family.h>
27#include <Neumann_paroi.h>
28#include <Pb_Multiphase.h>
29#include <Matrix_tools.h>
30#include <Array_tools.h>
31#include <functional>
32#include <cmath>
33
34Implemente_instanciable_sans_constructeur(Op_Diff_PolyMAC_MPFA_Elem, "Op_Diff_PolyMAC_MPFA_Elem|Op_Diff_PolyMAC_MPFA_var_Elem", Op_Diff_PolyMAC_MPFA_base);
35Implemente_instanciable(Op_Dift_PolyMAC_MPFA_Elem, "Op_Dift_PolyMAC_MPFA_Elem_PolyMAC_MPFA|Op_Dift_PolyMAC_MPFA_var_Elem_PolyMAC_MPFA", Op_Diff_PolyMAC_MPFA_Elem);
36
41
43
45
47
49
51{
53
54 const Equation_base& eq = equation();
56 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, le_dom_poly_.valeur());
57
58 if (domaine.domaine().nb_joints() && domaine.domaine().joint(0).epaisseur() < 1)
59 {
60 Cerr << "Op_Diff_PolyMAC_MPFA_Elem : largeur de joint insuffisante (minimum 1)!" << finl;
62 }
63
64 flux_bords_.resize(domaine.premiere_face_int(), ch.valeurs().line_size());
65
66 /*
67 * XXX fill some useful bools !
68 */
69 is_pb_multi_ = sub_type(Pb_Multiphase, eq.probleme());
70 is_pb_coupl_ = eq.probleme().is_coupled();
71 has_flux_par_ = (sub_type(Energie_Multiphase, equation()) || sub_type(Convection_Diffusion_Temperature, equation()))
72 && equation().probleme().has_correlation("flux_parietal");
73
74 for (const auto &itr : la_zcl_poly_->les_conditions_limites())
75 if (sub_type(Echange_contact_PolyMAC_MPFA, itr.valeur()))
76 {
77 has_echange_contact_ = true;
78 break;
79 }
80
81 if ((has_echange_contact_ || has_flux_par_) && equation().diffusion_multi_scalaire())
82 Process::exit("Multi-scalar diffusion is not compatible with echange_contact or parietal flux coupling.");
83
84 if (has_echange_contact_ || has_flux_par_)
85 couplage_parietal_helper_.associer(*this);
86
87 if (has_flux_par_)
88 if (ref_cast(Flux_parietal_base, eq.probleme().get_correlation("Flux_parietal")).calculates_bubble_nucleation_diameter())
89 couplage_parietal_helper_.completer_d_nuc();
90}
91
93{
94 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, le_dom_poly_.valeur());
95 const Champ_Elem_PolyMAC_MPFA& ch = ref_cast(Champ_Elem_PolyMAC_MPFA, equation().inconnue());
96 const IntTab& e_f = domaine.elem_faces(), &fcl = ch.fcl();
97 const DoubleTab& nf = domaine.face_normales();
98
99 const DoubleTab& diffu = diffusivite_pour_pas_de_temps().valeurs(),
100 &lambda = diffusivite().valeurs();
101
102 const DoubleTab *alp = sub_type(Pb_Multiphase, equation().probleme()) ?
103 &ref_cast(Pb_Multiphase, equation().probleme()).equation_masse().inconnue().passe() :
105
106 const DoubleVect& pe = equation().milieu().porosite_elem(), &vf = domaine.volumes_entrelaces(), &ve = domaine.volumes();
107
108 update_nu();
109
110 double dt = 1e10;
111
112 const int N = equation().inconnue().valeurs().dimension(1), cD = (diffu.dimension(0) == 1), cL = (lambda.dimension(0) == 1);
113
114 DoubleTrav flux(N);
115
116 for (int e = 0; e < domaine.nb_elem(); e++)
117 {
118 flux = 0.;
119
120 for (int i = 0; i < e_f.dimension(1); i++)
121 {
122 const int f = e_f(e, i);
123 if (f < 0) continue;
124
125 if (!Option_PolyMAC_family::TRAITEMENT_AXI || (Option_PolyMAC_family::TRAITEMENT_AXI && !(fcl(f,0) == 4 || fcl(f,0) == 5)) )
126 for (int n = 0; n < N; n++)
127 flux(n) += domaine.nu_dot(&nu_, e, n, &nf(f, 0), &nf(f, 0)) / vf(f);
128 }
129
130 for (int n = 0; n < N; n++)
131 if ((!alp || (*alp)(e, n) > 1e-3) && flux(n)) /* sous 0.5e-6, on suppose que l'evanescence fait le job */
132 dt = std::min(dt, pe(e) * ve(e) * (alp ? (*alp)(e, n) : 1) * (lambda(!cL * e, n) / diffu(!cD * e, n)) / flux(n));
133
134 if (dt < 0)
135 Process::exit(que_suis_je() + " : negative dt_stab calculated !!");
136 }
137 return Process::mp_min(dt);
138}
139
141{
143 couplage_parietal_helper_.d_nuc_a_jour() = 0;
144}
145
147{
148 if (!couplage_parietal_helper_.d_nuc_a_jour())
149 {
150 Cerr << "Op_Diff_PolyMAC_MPFA_Elem : attempt to access d_nucleation (nucleate bubble diameter) before ajouter_blocs() has been called!" << finl
151 << "Please call assembler_blocs() on Energie_Multiphase before calling it on Masse_Multiphase." << finl;
153 }
154 return couplage_parietal_helper_.d_nuc();
155}
156
158{
159 if (som_ext_init_)
160 return; //deja fait
161
162 if (has_echange_contact_ || has_flux_par_)
163 couplage_parietal_helper_.init_op_ext();
164 else
165 {
166 op_ext = { this };
167 som_ext_init_ = 1;
168 }
169}
170
171/*! @brief Dimensions the matrix blocks for the linear system
172 *
173 * Allocates and sizes the sparse matrix structure for the diffusion operator.
174 * The method:
175 * - Determines the matrix stencil based on the discretization scheme
176 * - Accounts for variable diffusivity when computing the stencil
177 *
178 * @param matrices Map of matrices to be dimensioned
179 * @param semi_impl Map of semi-implicit fields
180 */
181void Op_Diff_PolyMAC_MPFA_Elem::dimensionner_blocs(matrices_t matrices, const tabs_t& semi_impl) const
182{
183 init_op_ext();
184 update_phif(!nu_constant_ or equation().domaine_dis().domaine().deformable()); //calcul de (nf.nu.grad T) : si nu variable, stencil complet
185
186 const std::string nom_inco = equation().inconnue().le_nom().getString();
187 if (semi_impl.count(nom_inco))
188 return; //semi-implicite -> rien a dimensionner
189
190 if (has_echange_contact_ || has_flux_par_)
191 {
192 couplage_parietal_helper_.dimensionner_blocs(matrices, semi_impl);
193 return;
194 }
195
196
197 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, le_dom_poly_.valeur());
198 const IntTab& f_e = domaine.face_voisins();
199
200 Matrice_Morse* mat = matrices.count(nom_inco) ? matrices.at(nom_inco) : nullptr;
201
202 const int N = equation().inconnue().valeurs().line_size(); //nombre de composantes
203
204 Stencil stencil; //stencils par matrice
205 stencil.resize(0, 2);
206
207
208 IntTrav tpfa(0, N); //pour suivre quels flux sont a deux points
209 domaine.creer_tableau_faces(tpfa);
210 tpfa = 1;
211
212 Cerr << "Op_Diff_PolyMAC_MPFA_Elem::dimensionner() : ";
213
214 //avec fgrad : parties hors Echange_contact (ne melange ni les problemes, ni les composantes)
215 for (int f = 0; f < domaine.nb_faces(); f++)
216 for (int i = 0; i < 2; i++)
217 {
218 const int e = f_e(f, i);
219 if (e < 0) continue;
220
221 if (e < domaine.nb_elem()) //stencil a l'element e
222 for (int j = phif_d(f); j < phif_d(f + 1); j++)
223 {
224 const int e_s = phif_e(j);
225
226 if (e_s < domaine.nb_elem_tot())
227 for (int n = 0; n < N; n++)
228 {
229 stencil.append_line(N * e + n, N * e_s + n);
230
231 const int tmp = (e_s == f_e(f, 0)) || (e_s == f_e(f, 1)) || (phif_c(j, n) == 0);
232 tpfa(f, n) &= tmp;
233 }
234 }
235 }
236
237 if (mat)
238 {
239 tableau_trier_retirer_doublons(stencil);
240 Matrice_Morse mat2;
241 Matrix_tools::allocate_morse_matrix(N * domaine.nb_elem_tot(), N * equation().domaine_dis().nb_elem_tot(), stencil, mat2);
242
243 if (mat->nb_colonnes())
244 *mat += mat2;
245 else
246 *mat = mat2;
247 }
248
249 int n_sten = stencil.dimension(0);
250
251 const double elem_t = static_cast<double>(domaine.domaine().md_vector_elements()->nb_items_seq_tot()),
252 face_t = static_cast<double>(domaine.md_vector_faces()->nb_items_seq_tot());
253 Cerr << "width " << Process::mp_sum_as_double(n_sten) / (N * elem_t) << " "
254 << mp_somme_vect_as_double(tpfa) * 100. / (N * face_t) << "% TPFA " << finl;
255}
256
257/*! @brief Assembles the diffusion contribution to the linear system
258 *
259 * This is the core method that computes and adds the diffusion terms to both
260 * the matrix and the right-hand side vector. The method:
261 * - Computes diffusive fluxes across all faces using the phi_f coefficients
262 * - Handles various boundary conditions (Dirichlet, Neumann, heat exchange)
263 * - Assembles matrix coefficients for implicit treatment
264 * - Updates the right-hand side with explicit contributions
265 * - Stores boundary fluxes for post-processing
266 *
267 * @param matrices Map of matrices to be filled
268 * @param secmem Right-hand side vector to update
269 * @param semi_impl Map of semi-implicit fields
270 */
271void Op_Diff_PolyMAC_MPFA_Elem::ajouter_blocs(matrices_t matrices, DoubleTab& secmem, const tabs_t& semi_impl) const
272{
273#ifdef _COMPILE_AVEC_PGCC_AVANT_22_7
274 Cerr << "Internal error with nvc++: Internal error: read_memory_region: not all expected entries were read." << finl;
276#else
277 init_op_ext();
278 update_phif(equation().domaine_dis().domaine().deformable());
279 if (has_echange_contact_ || has_flux_par_)
280 {
281 couplage_parietal_helper_.ajouter_blocs(matrices, secmem, semi_impl);
282 return;
283 }
284
285 const std::string& nom_inco = equation().inconnue().le_nom().getString();
286 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, equation().domaine_dis());
287 const Champ_Inc_base& ch_inc = has_champ_inco() ? mon_inconnue() : equation().inconnue();
288 const Champ_Elem_PolyMAC_MPFA& ch = ref_cast(Champ_Elem_PolyMAC_MPFA, ch_inc);
289
290 const DoubleTab& inco = semi_impl.count(nom_inco) ? semi_impl.at(nom_inco) : ch.valeurs();
291 const DoubleVect& fs = domaine.face_surfaces();
292
293 const IntTab& f_e = domaine.face_voisins(), &fcl = ch.fcl();
294
296
297 const int N = inco.line_size();
298
299 Matrice_Morse* mat = !semi_impl.count(nom_inco) && matrices.count(nom_inco) ? matrices.at(nom_inco) : nullptr;
300
301 /* avec phif : flux hors Echange_contact -> mat[0] seulement */
302 DoubleTrav flux(N);
303
304 for (int f = 0; f < domaine.nb_faces(); f++)
305 {
306 flux = 0.;
307
308 for (int i = phif_d(f); i < phif_d(f + 1); i++) //element
309 {
310 const int eb = phif_e(i);
311 const int fb = eb - domaine.nb_elem_tot();
312
313 if (fb < 0)
314 {
315 for (int n = 0; n < N; n++)
316 flux(n) += phif_c(i, n) * fs(f) * inco(eb, n);
317
318 if (mat) //derivees
319 for (int j = 0; j < 2; j++)
320 {
321 const int e = f_e(f, j);
322 if (e < 0) continue;
323
324 if (e < domaine.nb_elem())
325 for (int n = 0; n < N; n++)
326 (*mat)(N * e + n, N * eb + n) += (j ? 1 : -1) * phif_c(i, n) * fs(f);
327 }
328 }
329 else if (fcl(fb, 0) == 1 || fcl(fb, 0) == 2) //Echange_impose_base
330 {
331 for (int n = 0; n < N; n++)
332 flux(n) += (phif_c(i, n) ? phif_c(i, n) * fs(f) *
333 ref_cast(Echange_impose_base, cls[fcl(fb, 1)].valeur()).T_ext(fcl(fb, 2), n) : 0);
334 }
335 else if (fcl(fb, 0) == 4) //Neumann non homogene
336 {
337 for (int n = 0; n < N; n++)
338 flux(n) += (phif_c(i, n) ? phif_c(i, n) * fs(f) *
339 ref_cast(Neumann_paroi, cls[fcl(fb, 1)].valeur()).flux_impose(fcl(fb, 2), n) : 0);
340 }
341 else if (fcl(fb, 0) == 6) //Dirichlet
342 {
343 for (int n = 0; n < N; n++)
344 flux(n) += (phif_c(i, n) ? phif_c(i, n) * fs(f) *
345 ref_cast(Dirichlet, cls[fcl(fb, 1)].valeur()).val_imp(fcl(fb, 2), n) : 0);
346 }
347 }
348
349 for (int j = 0; j < 2; j++)
350 {
351 const int e = f_e(f, j);
352 if (e < 0) continue;
353
354 if (e < domaine.nb_elem())
355 for (int n = 0; n < N; n++) //second membre -> amont/aval
356 secmem(e, n) += (j ? -1 : 1) * flux(n);
357 }
358
359 if (f < domaine.premiere_face_int())
360 for (int n = 0; n < N; n++)
361 flux_bords_(f, n) = flux(n); //flux aux bords
362 }
363#endif
364}
365
: class Champ_Elem_PolyMAC_MPFA
const IntTab & fcl() const
Classe Champ_Inc_base.
DoubleTab & valeurs() override
Renvoie le tableau des valeurs du champ au temps courant.
virtual DoubleTab & valeurs()=0
classe Conds_lim Cette classe represente un vecteur de conditions aux limites.
Definition Conds_lim.h:32
classe Convection_Diffusion_Temperature Cas particulier de Convection_Diffusion_std
classe Dirichlet Cette classe est la classe de base de la hierarchie des conditions aux limites de ty...
Definition Dirichlet.h:31
const Cond_lim & les_conditions_limites(int) const
Renvoie la i-ieme condition aux limites.
classe : Echange_contact_PolyMAC_MPFA Outre le champ_front representant la temperature de paroi,
classe Echange_impose_base: Cette condition limite sert uniquement pour l'equation d'energie.
classe Energie_Multiphase Cas particulier de Convection_Diffusion_std pour un fluide quasi conpressib...
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....
virtual const Milieu_base & milieu() const =0
virtual const Champ_Inc_base & inconnue() const =0
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.
const Nom & le_nom() const override
Renvoie le nom du champ.
classe Flux_parietal_base correlations de flux parietal de la forme
Classe Matrice_Morse Represente une matrice M (creuse), non necessairement carree.
int nb_colonnes() const override
Return local number of columns (=size on the current proc).
static void allocate_morse_matrix(const int nb_lines, const int nb_columns, const Stencil &stencil, Matrice_Morse &matrix, const bool &attach_stencil_to_matrix=false)
DoubleVect & porosite_elem()
Definition Milieu_base.h:58
const Equation_base & equation() const
Renvoie la reference sur l'equation pointe par MorEqn::mon_equation.
Definition MorEqn.h:62
Classe Neumann_paroi Cette condition limite correspond a un flux impose pour l'equation de.
const std::string & getString() const
Definition Nom.h:92
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
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
const Champ_base & diffusivite() const override
void mettre_a_jour(double t) override
DOES NOTHING - to override in derived classes.
void completer() override
Associe l'operateur au domaine_dis, le domaine_Cl_dis, et a l'inconnue de son equation.
void ajouter_blocs(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={}) const override
Assembles the diffusion contribution to the linear system.
void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={}) const override
Dimensions the matrix blocks for the linear system.
double calculer_dt_stab() const override
Calcul dt_stab.
const DoubleTab & d_nucleation() const
class Op_Diff_PolyMAC_MPFA_base
void completer() override
Associe l'operateur au domaine_dis, le domaine_Cl_dis, et a l'inconnue de son equation.
void update_phif(int full_stencil=0) const
void mettre_a_jour(double t) override
DOES NOTHING - to override in derived classes.
std::vector< const Operateur_Diff_base * > op_ext
virtual const Champ_base & diffusivite_pour_pas_de_temps() const
Renvoie le champ_don correspondant a la vraie diffusivite du milieu qui sert pour le calcul du pas de...
DoubleTab flux_bords_
bool has_champ_inco() const
const Champ_Inc_base & mon_inconnue() const
classe Pb_Multiphase Cette classe represente un probleme de thermohydraulique multiphase de type "3*N...
int has_correlation(std::string nom_correlation) const
bool is_coupled() const
const Correlation_base & get_correlation(std::string nom_correlation) const
static double mp_min(double)
Definition Process.cpp:386
static double mp_sum_as_double(int v)
Definition Process.h:99
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
Classe de base des flux de sortie.
Definition Sortie.h:52
virtual const Champ_base & get_champ_masse_volumique() const
Renvoie le champ de masse volumique.
virtual void declare_support_masse_volumique(int ok)
Le constructeur d'une classe derivee qui se sert de la masse volumique doit appeler cette fonction av...
virtual int has_champ_masse_volumique() const
Renvoie 1 si la masse volumique a ete associee, 0 sinon.
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
void append_line(_TYPE_)
Definition TRUSTTab.tpp:213
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
int line_size() const
Definition TRUSTVect.tpp:67