TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Couplage_Parietal_PolyMAC_MPFA_helper.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 <Couplage_Parietal_PolyMAC_MPFA_helper.h>
17#include <Convection_Diffusion_Temperature.h>
18#include <Flux_interfacial_PolyMAC_HFV.h>
19#include <Echange_contact_PolyMAC_MPFA.h>
20#include <Op_Diff_PolyMAC_MPFA_Elem.h>
21#include <Echange_externe_impose.h>
22#include <Champ_Elem_PolyMAC_MPFA.h>
23#include <Flux_parietal_base.h>
24#include <Domaine_PolyMAC_MPFA.h>
25#include <Domaine_Cl_PolyMAC_family.h>
26#include <Milieu_composite.h>
27#include <Option_PolyMAC_family.h>
28#include <Neumann_paroi.h>
29#include <Pb_Multiphase.h>
30#include <Matrix_tools.h>
31#include <Array_tools.h>
32#include <functional>
33#include <cmath>
34#include <Matrix_tools.h>
35
40
42{
43 const Champ_Elem_PolyMAC_MPFA& ch = ref_cast(Champ_Elem_PolyMAC_MPFA, op_elem_->equation().inconnue());
44 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, op_elem_->domaine_poly());
45 d_nuc_.resize(0, ch.valeurs().line_size());
46 domaine.domaine().creer_tableau_elements(d_nuc_);
47}
48
49/* construction de s_dist : sommets du porbleme coincidant avec des sommets de problemes distants */
50void Couplage_Parietal_PolyMAC_MPFA_helper::init_s_dist() const
51{
52 if (s_dist_init_)
53 return; //deja fait
54
55 if (op_elem_->has_echange_contact())
56 {
57 const Conds_lim& cls = op_elem_->domaine_cl_poly().les_conditions_limites();
58 for (const auto &itr : cls)
59 if (sub_type(Echange_contact_PolyMAC_MPFA, itr.valeur()))
60 {
61 const Echange_contact_PolyMAC_MPFA& cl = ref_cast(Echange_contact_PolyMAC_MPFA, itr.valeur());
62 cl.init_op();
63
64 const Op_Diff_PolyMAC_MPFA_Elem *o_diff = &cl.o_diff.valeur();
65 cl.init_fs_dist();
66
67 for (auto &s_sb : cl.s_dist)
68 s_dist[s_sb.first][o_diff] = s_sb.second;
69 }
70 }
71
72 s_dist_init_ = 1;
73}
74
76{
77 if (som_ext_init_)
78 return; //deja fait
79
80 /* remplissage de op_ext : de proche en proche */
81 op_elem_->op_ext = { &(op_elem_.valeur()) };
82 init_s_dist();
83
84 /* construction de som_ext_{d, e, f} */
85 som_ext_d.resize(0, 2);
86 som_ext_d.append_line(0, 0);
87 som_ext_pe.resize(0, 2);
88 som_ext_pf.resize(0, 4);
89
90 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, op_elem_->domaine_poly());
91 auto s_dist_full = s_dist;
92
93 for (std::set<const Op_Diff_PolyMAC_MPFA_Elem*> op_ext_tbd = { &(op_elem_.valeur()) }; op_ext_tbd.size();)
94 {
95 const Op_Diff_PolyMAC_MPFA_Elem *op = *op_ext_tbd.begin();
96 op_ext_tbd.erase(op_ext_tbd.begin());
97
98 //elargissement de op_ext
99 if (op_elem_->has_echange_contact())
100 {
102 for (const auto &itr : cls)
103 if (sub_type(Echange_contact_PolyMAC_MPFA, itr.valeur()))
104 {
105 const Echange_contact_PolyMAC_MPFA& cl = ref_cast(Echange_contact_PolyMAC_MPFA, itr.valeur());
106
107 cl.init_op();
108
109 const Op_Diff_PolyMAC_MPFA_Elem *o_diff = &cl.o_diff.valeur();
110 if (std::find(op_elem_->op_ext.begin(), op_elem_->op_ext.end(), o_diff) == op_elem_->op_ext.end())
111 {
112 op_elem_->op_ext.push_back(o_diff);
113 op_ext_tbd.insert(o_diff);
114 }
115 }
116 }
117
118 //elargissement de s_dist_full : aargh...
119 op->couplage_parietal_helper().init_s_dist();
120 if (op != &(op_elem_.valeur()))
121 for (auto &&s_op_sb : s_dist_full)
122 if (s_op_sb.second.count(op))
123 for (auto &&op_sc : op->couplage_parietal_helper().s_dist[s_op_sb.second[op]])
124 if (!s_op_sb.second.count(op_sc.first))
125 s_op_sb.second[op_sc.first] = op_sc.second;
126 }
127
128
129 std::set<std::array<int, 2>> s_pe; // (pb, el)
130 std::set<std::array<int, 4>> s_pf; // (pb1, f1, pb2, f2)
131
132 /*
133 * XXX NO WORRIES : c'est std::vector<ref_array>
134 */
135 std::vector<std::reference_wrapper<const Domaine_PolyMAC_MPFA>> domaines;
136 std::vector<std::reference_wrapper<const IntTab>> fcl, e_f, f_s;
137 std::vector<std::reference_wrapper<const Static_Int_Lists>> som_elem;
138
139 for (auto &&op : op_elem_->op_ext)
140 domaines.push_back(std::ref(ref_cast(Domaine_PolyMAC_MPFA, op->equation().domaine_dis())));
141
142 for (auto &&op : op_elem_->op_ext)
143 fcl.push_back(std::ref(ref_cast(Champ_Elem_PolyMAC_MPFA, op->equation().inconnue()).fcl()));
144
145 for (auto &&zo : domaines)
146 {
147 e_f.push_back(std::ref(zo.get().elem_faces()));
148 f_s.push_back(std::ref(zo.get().face_sommets()));
149 som_elem.push_back(std::ref(zo.get().som_elem()));
150 }
151
152 /* autres CLs (hors Echange_contact) devant etre traitees par som_ext : Echange_impose_base, tout si Pb_Multiphase avec Flux_parietal_base */
153 const Conds_lim& cls = op_elem_->equation().domaine_Cl_dis().les_conditions_limites();
154 int has_flux = (sub_type(Energie_Multiphase, op_elem_->equation()) || sub_type(Convection_Diffusion_Temperature, op_elem_->equation())) &&
155 op_elem_->equation().probleme().has_correlation("flux_parietal");
156
157 for (int i = 0; i < cls.size(); i++)
158 if (has_flux || sub_type(Echange_contact_PolyMAC_MPFA, cls[i].valeur()))
159 for (int j = 0; j < ref_cast(Front_VF, cls[i]->frontiere_dis()).nb_faces(); j++)
160 {
161 const int f = ref_cast(Front_VF, cls[i]->frontiere_dis()).num_face(j);
162 for (int k = 0; k < f_s[0].get().dimension(1); k++)
163 {
164 const int s = f_s[0](f, k);
165 if (s < 0) continue;
166
167 if (!s_dist_full.count(s))
168 s_dist_full[s] = { }; //dans le std::map, mais pas d'operateurs distants!
169 }
170 }
171
172 for (const auto &s_op_sb : s_dist_full)
173 {
174 const int s = s_op_sb.first;
175
176 if (s < domaine.nb_som())
177 {
178 //elements
179 for (int i = 0; i < som_elem[0].get().get_list_size(s); i++)
180 {
181 std::array<int, 2> arr = { 0, som_elem[0](s, i) };
182 s_pe.insert( { arr }); //cote local
183 }
184
185 for (const auto &op_sb : s_op_sb.second) //cotes distants
186 {
187 const int iop = (int) (std::find(op_elem_->op_ext.begin(), op_elem_->op_ext.end(), op_sb.first) - op_elem_->op_ext.begin());
188
189 for (int i = 0; i < som_elem[iop].get().get_list_size(op_sb.second); i++)
190 {
191 std::array<int, 2> arr = { iop, som_elem[iop](op_sb.second, i) };
192 s_pe.insert( { arr });
193 }
194 }
195
196 //faces : celles des elements de s_pe
197 for (const auto &iop_e : s_pe)
198 {
199 const int iop = iop_e[0];
200 const int e = iop_e[1];
201 const int s_l = iop ? s_op_sb.second.at(op_elem_->op_ext[iop]) : s;
202
203 for (int i = 0; i < e_f[iop].get().dimension(1); i++)
204 {
205 const int f = e_f[iop](e, i);
206 if (f < 0) continue;
207
208 int ok = 0;
209
210 for (int j = 0; !ok && j < f_s[iop].get().dimension(1); j++)
211 {
212 const int sb = f_s[iop](f, j);
213 if (sb < 0) continue;
214
215 ok |= sb == s_l;
216 }
217
218 if (!ok || fcl[iop](f, 0) != 3)
219 continue; //face ne touchant pas le sommet ou non Echange_contact
220
221 const Echange_contact_PolyMAC_MPFA& cl = ref_cast(Echange_contact_PolyMAC_MPFA, op_elem_->op_ext[iop]->equation().domaine_Cl_dis().les_conditions_limites()[fcl[iop](f, 1)].valeur());
222
223 //operateur / face de l'autre cote
224 int o_iop = (int) (std::find(op_elem_->op_ext.begin(), op_elem_->op_ext.end(), &cl.o_diff.valeur()) - op_elem_->op_ext.begin());
225 int o_f = cl.f_dist(fcl[iop](f, 2));
226
227 std::array<int, 4> arr = { iop < o_iop ? iop : o_iop, iop < o_iop ? f : o_f, iop < o_iop ? o_iop : iop, iop < o_iop ? o_f : f };
228 s_pf.insert( { arr }); //stocke dans l'ordre
229 }
230 }
231
232 int mix = 0; //melange-t-on les composantes autour de ce sommet? oui si une equation Energie_Multiphase avec correlation de flux parietal est presente autour
233 for (const auto &pe : s_pe)
234 {
235 som_ext_pe.append_line(pe[0], pe[1]);
236 mix |= ( sub_type(Energie_Multiphase, op_elem_->op_ext[pe[0]]->equation()) ||
237 sub_type(Convection_Diffusion_Temperature, op_elem_->op_ext[pe[0]]->equation()) )
238 && op_elem_->op_ext[pe[0]]->equation().probleme().has_correlation("flux_parietal");
239 }
240
241 for (auto &&pf : s_pf)
242 som_ext_pf.append_line(pf[0], pf[1], pf[2], pf[3]);
243
244 ref_cast_non_const(IntTab, op_elem_->tab_som_ext()).append_line(s);
245 som_mix.append_line(mix);
246 som_ext_d.append_line(som_ext_pe.dimension(0), som_ext_pf.dimension(0));
247
248 s_pe.clear();
249 s_pf.clear();
250 }
251 }
252
253 som_ext_init_ = 1;
254}
255
256void Couplage_Parietal_PolyMAC_MPFA_helper::dimensionner_blocs(matrices_t matrices, const tabs_t& semi_impl) const
257{
258 const std::string nom_inco = op_elem_->equation().inconnue().le_nom().getString();
259 const Domaine_PolyMAC_MPFA& domaine = ref_cast(Domaine_PolyMAC_MPFA, op_elem_->domaine_poly());
260 const IntTab& f_e = domaine.face_voisins();
261
262 std::vector<Matrice_Morse*> mat(op_elem_->op_ext.size());
263
264 //une matrice potentielle a remplir par operateur de op_ext
265 for (int i = 0; i < (int) op_elem_->op_ext.size(); i++)
266 {
267 const auto& nom_inco_mat = i ? nom_inco + "/" + op_elem_->op_ext[i]->equation().probleme().le_nom().getString() : nom_inco;
268
269 mat[i] = matrices.count(nom_inco_mat) ? matrices.at(nom_inco_mat) : nullptr;
270 }
271
272 std::vector<int> N(op_elem_->op_ext.size()); //nombre de composantes par probleme de op_ext
273
274 std::vector<Stencil> stencil(op_elem_->op_ext.size()); //stencils par matrice
275
276 for (int i = 0; i < (int) op_elem_->op_ext.size(); i++)
277 {
278 stencil[i].resize(0, 2);
279 N[i] = op_elem_->op_ext[i]->equation().inconnue().valeurs().line_size();
280 }
281
282 IntTrav tpfa(0, N[0]); //pour suivre quels flux sont a deux points
283
284 domaine.creer_tableau_faces(tpfa);
285 tpfa = 1;
286
287 Cerr << "Op_Diff_PolyMAC_MPFA_Elem::dimensionner() : ";
288
289 //avec fgrad : parties hors Echange_contact (ne melange ni les problemes, ni les composantes)
290 for (int f = 0; f < domaine.nb_faces(); f++)
291 for (int i = 0; i < 2; i++)
292 {
293 const int e = f_e(f, i);
294 if (e < 0) continue;
295
296 if (e < domaine.nb_elem()) //stencil a l'element e
297 for (int j = op_elem_->tab_phif_d()(f); j < op_elem_->tab_phif_d()(f + 1); j++)
298 {
299 const int e_s = op_elem_->tab_phif_e()(j);
300
301 if (e_s < domaine.nb_elem_tot())
302 for (int n = 0; n < N[0]; n++)
303 {
304 stencil[0].append_line(N[0] * e + n, N[0] * e_s + n);
305
306 const int tmp = (e_s == f_e(f, 0)) || (e_s == f_e(f, 1)) || (op_elem_->tab_phif_c()(j, n) == 0);
307 tpfa(f, n) &= tmp;
308 }
309 }
310 }
311
312 //avec som_ext : partie Echange_contact -> melange toutes les composantes si som_mix = 1
313 for (int i = 0; i < op_elem_->tab_som_ext().dimension(0); i++)
314 for (int j = som_ext_d(i, 0); j < som_ext_d(i + 1, 0); j++)
315 {
316 const int e = som_ext_pe(j, 1);
317
318 if (!som_ext_pe(j, 0) && e < domaine.nb_elem())
319 for (int k = som_ext_d(i, 0); k < som_ext_d(i + 1, 0); k++)
320 {
321 const int p_s = som_ext_pe(k, 0);
322 const int e_s = som_ext_pe(k, 1);
323
324 for (int n = 0; n < N[0]; n++)
325 for (int m = (som_mix(i) ? 0 : n); m < (som_mix(i) ? N[p_s] : n + 1); m++)
326 stencil[p_s].append_line(N[0] * e + n, N[p_s] * e_s + m);
327 }
328 }
329
330 for (int i = 0; i < (int) op_elem_->op_ext.size(); i++)
331 if (mat[i])
332 {
333 tableau_trier_retirer_doublons(stencil[i]);
334 Matrice_Morse mat2;
335 Matrix_tools::allocate_morse_matrix(N[0] * domaine.nb_elem_tot(), N[i] * op_elem_->op_ext[i]->equation().domaine_dis().nb_elem_tot(), stencil[i], mat2);
336
337 if (mat[i]->nb_colonnes())
338 *mat[i] += mat2;
339 else
340 *mat[i] = mat2;
341 }
342
343 int n_sten = 0;
344 for (const auto &st : stencil)
345 n_sten += st.dimension(0); //n_sten : nombre total de points du stencil de l'operateur
346
347 const double elem_t = static_cast<double>(domaine.domaine().md_vector_elements()->nb_items_seq_tot()),
348 face_t = static_cast<double>(domaine.md_vector_faces()->nb_items_seq_tot());
349 Cerr << "width " << Process::mp_sum_as_double(n_sten) / (N[0] * elem_t) << " "
350 << mp_somme_vect_as_double(tpfa) * 100. / (N[0] * face_t) << "% TPFA " << finl;
351}
352
353void Couplage_Parietal_PolyMAC_MPFA_helper::ajouter_blocs(matrices_t matrices, DoubleTab& secmem, const tabs_t& semi_impl) const
354{
355 const std::string& nom_inco = op_elem_->equation().inconnue().le_nom().getString();
356
357 const int n_ext = (int) op_elem_->op_ext.size(), D = Objet_U::dimension, un = 1;
358
359 std::vector<Matrice_Morse*> mat(n_ext); //matrices
360
361 std::vector<int> N; //composantes
362
363 /*
364 * XXX NO WORRIES : c'est std::vector<ref_array>
365 */
366 std::vector<std::reference_wrapper<const Domaine_PolyMAC_MPFA>> domaine; //domaines
367 std::vector<std::reference_wrapper<const Conds_lim>> cls; //conditions aux limites
368 std::vector<std::reference_wrapper<const IntTab>> fcl, e_f, f_e, f_s; //tableaux "fcl", "elem_faces", "faces_voisins"
369 std::vector<std::reference_wrapper<const DoubleVect>> fs; //surfaces
370 std::vector<std::reference_wrapper<const DoubleTab>> inco, nf, xp, xs, xv, diffu; //inconnues, normales aux faces, positions elems / faces / sommets
371
372 int M = 0;
373 for (int i = 0; i < n_ext; M = std::max(M, N[i]), i++)
374 {
375 std::string nom_mat = i ? nom_inco + "/" + op_elem_->op_ext[i]->equation().probleme().le_nom().getString() : nom_inco;
376
377 mat[i] = !semi_impl.count(nom_inco) && matrices.count(nom_mat) ? matrices.at(nom_mat) : nullptr;
378
379 domaine.push_back(std::ref(ref_cast(Domaine_PolyMAC_MPFA, op_elem_->op_ext[i]->equation().domaine_dis())));
380
381 f_e.push_back(std::ref(domaine[i].get().face_voisins()));
382 e_f.push_back(std::ref(domaine[i].get().elem_faces()));
383 f_s.push_back(std::ref(domaine[i].get().face_sommets()));
384
385 fs.push_back(std::ref(domaine[i].get().face_surfaces()));
386 nf.push_back(std::ref(domaine[i].get().face_normales()));
387
388 xp.push_back(std::ref(domaine[i].get().xp()));
389 xv.push_back(std::ref(domaine[i].get().xv()));
390 xs.push_back(std::ref(domaine[i].get().domaine().coord_sommets()));
391
392 cls.push_back(std::ref(op_elem_->op_ext[i]->equation().domaine_Cl_dis().les_conditions_limites()));
393
394 diffu.push_back(ref_cast(Op_Diff_PolyMAC_MPFA_Elem, *(op_elem_->op_ext[i])).nu());
395
396 const Champ_Inc_base& ch_inc = op_elem_->op_ext[i]->has_champ_inco() ? op_elem_->op_ext[i]->mon_inconnue() : op_elem_->op_ext[i]->equation().inconnue();
397 const Champ_Elem_PolyMAC_MPFA& ch = ref_cast(Champ_Elem_PolyMAC_MPFA, ch_inc);
398
399 inco.push_back(std::ref(semi_impl.count(nom_mat) ? semi_impl.at(nom_mat) : ch.valeurs()));
400
401 N.push_back(inco[i].get().line_size());
402 fcl.push_back(std::ref(ch.fcl()));
403 }
404
405 const Domaine_PolyMAC_MPFA& domaine0 = domaine[0];
406
407 /* avec phif : flux hors Echange_contact -> mat[0] seulement */
408 DoubleTrav flux(N[0]);
409
410 for (int f = 0; f < domaine0.nb_faces(); f++)
411 {
412 flux = 0.;
413
414 for (int i = op_elem_->tab_phif_d()(f); i < op_elem_->tab_phif_d()(f + 1); i++)
415 {
416 const int eb = op_elem_->tab_phif_e()(i);
417 const int fb = eb - domaine0.nb_elem_tot();
418
419 if (fb < 0) //element
420 {
421 for (int n = 0; n < N[0]; n++)
422 flux(n) += op_elem_->tab_phif_c()(i, n) * fs[0](f) * inco[0](eb, n);
423
424 if (mat[0])
425 for (int j = 0; j < 2; j++)
426 {
427 const int e = f_e[0](f, j);
428 if (e < 0) continue;
429
430 if (e < domaine[0].get().nb_elem())
431 for (int n = 0; n < N[0]; n++) //derivees
432 (*mat[0])(N[0] * e + n, N[0] * eb + n) += (j ? 1 : -1) * op_elem_->tab_phif_c()(i, n) * fs[0](f);
433 }
434
435 }
436 else if (fcl[0](fb, 0) == 1 || fcl[0](fb, 0) == 2)
437 {
438 for (int n = 0; n < N[0]; n++) //Echange_impose_base
439 flux(n) += (op_elem_->tab_phif_c()(i, n) ? op_elem_->tab_phif_c()(i, n) * fs[0](f) * ref_cast(Echange_impose_base, cls[0].get()[fcl[0](fb, 1)].valeur()).T_ext(fcl[0](fb, 2), n) : 0);
440 }
441 else if (fcl[0](fb, 0) == 4)
442 {
443 for (int n = 0; n < N[0]; n++) //Neumann non homogene
444 flux(n) += (op_elem_->tab_phif_c()(i, n) ? op_elem_->tab_phif_c()(i, n) * fs[0](f) * ref_cast(Neumann_paroi, cls[0].get()[fcl[0](fb, 1)].valeur()).flux_impose(fcl[0](fb, 2), n) : 0);
445 }
446 else if (fcl[0](fb, 0) == 6)
447 {
448 for (int n = 0; n < N[0]; n++) //Dirichlet
449 flux(n) += (op_elem_->tab_phif_c()(i, n) ? op_elem_->tab_phif_c()(i, n) * fs[0](f) * ref_cast(Dirichlet, cls[0].get()[fcl[0](fb, 1)].valeur()).val_imp(fcl[0](fb, 2), n) : 0);
450 }
451 }
452
453 for (int j = 0; j < 2; j++)
454 {
455 const int e = f_e[0](f, j);
456 if (e < 0) continue;
457
458 if (e < domaine[0].get().nb_elem())
459 for (int n = 0; n < N[0]; n++) //second membre -> amont/aval
460 secmem(e, n) += (j ? -1 : 1) * flux(n);
461 }
462
463 if (f < domaine0.premiere_face_int())
464 for (int n = 0; n < N[0]; n++)
465 op_elem_->flux_bords()(f, n) = flux(n); //flux aux bords
466 }
467
468 d_nuc_ = 0.; //remise a zero du diametre de nucleation
469
470 // remplir les tabs ... mais seulement si besoin !
471 DoubleTab *pqpi = op_elem_->equation().sources().size() &&
472 sub_type(Flux_interfacial_PolyMAC_HFV, op_elem_->equation().sources().dernier().valeur()) ?
473 &ref_cast(Flux_interfacial_PolyMAC_HFV, op_elem_->equation().sources().dernier().valeur()).qpi() : nullptr;
474
475
476 /* avec som_ext : flux autour des sommets affectes par des Echange_contact */
477 double i3[3][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, eps = 1e-8, eps_g = 1e-6, fac[3];
478
479 std::vector<std::array<int, 2>> s_pe, s_pf; //listes { probleme, elements/bord}, { probleme, face } autour du sommet
480 std::map<std::array<int, 2>, std::array<int, 2>> m_pf; //connections Echange_contact : m_pf[{pb1, f1}] = { pb2, f2 }
481 std::vector<double> surf_fs, vol_es; //surfaces partielles des faces connectees au sommet (meme ordre que s_f)
482 std::vector<std::array<std::array<double, 3>, 2>> vec_fs; //pour chaque facette, base de (D-1) vecteurs permettant de la parcourir
483 std::vector<std::vector<int>> se_f; /* se_f[i][.] : faces connectees au i-eme element connecte au sommet s */
484 std::vector<int> type_f; //type_f[i] : type de la face i (numerotation du tableau fcl)
485
486 IntTrav i_efs, i_e, i_eq_flux, i_eq_cont, i_eq_pbm, piv(1); //indices dans les matrices : i_efs(i, j, n) -> composante n de la face j de l'elem i dans s_pe, i_e(i, n) -> indice de la phase n de l'elem i de s_pe
487
488 //i_eq_{flux,cont}(i, n) -> n-ieme equation de flux/de continuite a la face i de s_pf
489
490 //i_eq_pbm(i_efs(i, j, n)) -> n-ieme equation "flux = correlation" a la face j de l'elem i de s_pe (seulement si Pb_Multiphase)
491
492 DoubleTrav A, B, Ff, Fec, Qf, Qec, Tefs, C, X, Y, W(1), S, x_fs;
493
494 // Et pour les methodes span de la classe Saturation pour le flux parietal
495 std::vector<DoubleTrav> Ts_tab(n_ext), Sigma_tab(n_ext), Lvap_tab(n_ext);
496
497 for (int i = 0; i < n_ext; i++)
498 if (sub_type(Milieu_composite, op_elem_->op_ext[i]->equation().milieu()))
499 {
500 const Milieu_composite& milc = ref_cast(Milieu_composite, op_elem_->op_ext[i]->equation().milieu());
501
502 const int nbelem_tot = domaine[i].get().nb_elem_tot(), nb_max_sat = N[i] * (N[i] - 1) / 2; // oui !! suite arithmetique !!
503
504 if (op_elem_->op_ext[i]->equation().probleme().has_correlation("flux_parietal"))
505 {
506 Ts_tab[i].resize(nbelem_tot, nb_max_sat);
507 Sigma_tab[i].resize(nbelem_tot, nb_max_sat);
508 Lvap_tab[i].resize(nbelem_tot, nb_max_sat);
509
510 const DoubleTab& press = ref_cast(QDM_Multiphase, ref_cast(Pb_Multiphase, op_elem_->op_ext[i]->equation().probleme()).equation_qdm()).pression().passe();
511
512 for (int k = 0; k < N[i]; k++)
513 for (int l = k + 1; l < N[i]; l++)
514 if (milc.has_saturation(k, l))
515 {
516 Saturation_base& z_sat = milc.get_saturation(k, l);
517 const int ind_trav = (k * (N[i] - 1) - (k - 1) * (k) / 2) + (l - k - 1); // Et oui ! matrice triang sup !
518
519 // XXX XXX XXX
520 // Attention c'est dangereux ! on suppose pour le moment que le champ de pression a 1 comp. Par contre la taille de res est nb_max_sat*nbelem !!
521 // Aussi, on passe le Span le nbelem pour le champ de pression et pas nbelem_tot ....
522 assert(press.line_size() == 1);
523
524 // recuperer Tsat et sigma ...
525 const DoubleTab& sig = z_sat.get_sigma_tab(), &tsat = z_sat.get_Tsat_tab();
526
527 // fill in the good case
528 for (int ii = 0; ii < nbelem_tot; ii++)
529 {
530 Ts_tab[i](ii, ind_trav) = tsat(ii);
531 Sigma_tab[i](ii, ind_trav) = sig(ii);
532 }
533
534 z_sat.Lvap(press.get_span_tot() /* elem reel */, Lvap_tab[i].get_span_tot(), nb_max_sat, ind_trav);
535 }
536 }
537 }
538
539 for (int i_s = 0; i_s < op_elem_->tab_som_ext().dimension(0); i_s++)
540 {
541 const int s = op_elem_->tab_som_ext()(i_s);
542
543 if (s < domaine0.nb_som())
544 {
545 /* (pb, elem) connectes a s -> avec som_ext_pe (deja classes) */
546 s_pe.clear();
547 int n_e = 0;
548
549 for (int i = som_ext_d(i_s, 0); i < som_ext_d(i_s + 1, 0); i++, n_e++)
550 s_pe.push_back( { { som_ext_pe(i, 0), som_ext_pe(i, 1) } });
551
552 /* m_pf : correspondances par parois contact */
553 m_pf.clear();
554 for (int i = som_ext_d(i_s, 1); i < som_ext_d(i_s + 1, 1); i++)
555 for (int j = 0; j < 2; j++)
556 m_pf[ { { som_ext_pf(i, j ? 2 : 0), som_ext_pf(i, j ? 3 : 1) } }] = { { som_ext_pf(i, j ? 0 : 2), som_ext_pf(i, j ? 1 : 3) } };
557
558 /* faces, leurs surfaces partielles */
559 s_pf.clear();
560 surf_fs.clear();
561 vec_fs.clear();
562 se_f.resize(std::max(int(se_f.size()), n_e));
563
564 for (int i = 0; i < n_e; i++)
565 {
566 se_f[i].clear();
567 int p = s_pe[i][0];
568 int e = s_pe[i][1];
569 int sp = p ? s_dist[s].at(op_elem_->op_ext[p]) : s;
570
571 for (int j = 0; j < e_f[p].get().dimension(1); j++)
572 {
573 const int f = e_f[p](e, j);
574 if (f < 0) continue;
575
576 int sb = 0;
577 int k = 0;
578
579 for ( ; k < f_s[p].get().dimension(1); k++)
580 {
581 sb = f_s[p](f, k);
582 if (sb < 0) continue;
583
584 if (sb == sp)
585 break;
586 }
587
588 if (sb != sp)
589 continue; /* face de e non connectee a s -> on saute */
590
591 se_f[i].push_back(f); //faces connectees a (p, e)
592
593 //couple (p, f) de la face : si la face est un Echange_contact, alors on choisit le couple du pb d'indice le plus bas
594 std::array<int, 2> pf0 = { { p, f } };
595 std::array<int, 2> pf = m_pf.count(pf0) && m_pf[pf0] < pf0 ? m_pf[pf0] : pf0;
596
597 int l = (int) (std::lower_bound(s_pf.begin(), s_pf.end(), pf) - s_pf.begin());
598
599 if (l == (int) s_pf.size() || s_pf[l] != pf) /* si (p, f) n'est pas dans s_pf, on l'ajoute */
600 {
601 s_pf.insert(s_pf.begin() + l, pf); //(pb, face) -> dans s_pf
602 if (D < 3)
603 {
604 surf_fs.insert(surf_fs.begin() + l, fs[p](f) / 2);
605 vec_fs.insert(vec_fs.begin() + l, { { { xs[p](sp, 0) - xv[p](f, 0), xs[p](sp, 1) - xv[p](f, 1), 0 }, { 0, 0, 0 } } }); //2D -> facile
606 }
607 else
608 {
609 surf_fs.insert(surf_fs.begin() + l, 0);
610 vec_fs.insert(vec_fs.begin() + l, { { { 0, 0, 0 }, { 0, 0, 0 } } });
611
612 for (int m = 0; m < 2; m++) //3D -> deux sous-triangles
613 {
614 if (m == 1 || k > 0)
615 {
616 sb = f_s[p](f, m ? (k + 1 < f_s[p].get().dimension(1) && f_s[p](f, k + 1) >= 0 ? k + 1 : 0) : k - 1); //sommet suivant (m = 1) ou precedent avec k > 0 -> facile
617 }
618 else
619 {
620 for (int n = f_s[p].get().dimension(1) - 1; (sb = f_s[p](f, n)) == -1;)
621 n--; //sommet precedent avec k = 0 -> on cherche a partir de la fin
622 }
623
624 auto v = domaine0.cross(D, D, &xs[p](sp, 0), &xs[p](sb, 0), &xv[p](f, 0), &xv[p](f, 0)); //produit vectoriel (xs - xf)x(xsb - xf)
625
626 surf_fs[l] += std::fabs(domaine0.dot(&v[0], &nf[p](f, 0))) / fs[p](f) / 4; //surface a ajouter
627
628 for (int d = 0; d < D; d++)
629 vec_fs[l][m][d] = (xs[p](sp, d) + xs[p](sb, d)) / 2 - xv[p](f, d); //vecteur face -> arete
630 }
631 }
632 }
633 }
634 }
635
636 int n_f = (int) s_pf.size(); //nombre de faces
637
638 /* conversion de se_f en indices dans s_f */
639 for (int i = 0; i < n_e; i++)
640 {
641 int p = s_pe[i][0];
642 for (int j = 0; j < (int) se_f[i].size(); j++)
643 {
644 std::array<int, 2> pf0 = { p, se_f[i][j] };
645 std::array<int, 2> pf = m_pf.count(pf0) && m_pf[pf0] < pf0 ? m_pf[pf0] : pf0;
646
647 se_f[i][j] = (int) (std::lower_bound(s_pf.begin(), s_pf.end(), pf) - s_pf.begin());
648 }
649 }
650
651 /* volumes */
652 double vol_s = 0.;
653 vol_es.resize(n_e);
654
655 for (int i = 0; i < n_e; vol_s += vol_es[i], i++)
656 {
657 int p = s_pe[i][0];
658 int e = s_pe[i][1];
659 vol_es[i] = 0.;
660
661 for (int j = 0; j < (int) se_f[i].size(); j++)
662 {
663 int k = se_f[i][j];
664 int pb = s_pf[k][0];
665 int f = s_pf[k][1];
666 vol_es[i] += surf_fs[k] * std::fabs(domaine0.dot(&xp[p](e, 0), &nf[pb](f, 0), &xv[pb](f, 0))) / fs[pb](f) / D;
667 }
668 }
669
670 /* inconnues en paroi (i_efs), aux elements (i_e). On alloues toutes les composantes si som_mix = 1, une seule sinon */
671 int mix = som_mix(i_s), Nm = mix ? 1 : N[s_pe[0][0]]; //nombre total d'equations/variables aux faces, nombre total de variables aux elements, t_ec = t_e + 1, nombres divises par Nl
672
673 int n_ef = 0;
674 for (int i = 0; i < n_e; i++)
675 n_ef = std::max(n_ef, int(se_f[i].size())); //nombre max de faces par elem
676
677 i_efs.resize(n_e, n_ef, 1 + M * mix);
678 i_efs = -1;
679 int t_eq = 0;
680
681 for (int i = 0; i < n_e; i++)
682 {
683 int p = s_pe[i][0];
684 int e = s_pe[i][1];
685
686 for (int j = 0; j < (int) se_f[i].size(); j++)
687 {
688 int k = se_f[i][j];
689 for (int n = 0; n < (mix ? N[p] : 1); n++, t_eq++)
690 i_efs(i, j, n) = t_eq; //une temperature de paroi par phase
691
692 //si face de bord d'un Pb_Multiphase (hors frontiere ouverte), une inconnue supplementaire : la Tparoi (dans ce cas, mix = 1)
693 int f = s_pf[k][0] == p && (e == f_e[p](s_pf[k][1], 0) || e == f_e[p](s_pf[k][1], 1)) ? s_pf[k][1] : m_pf.at(s_pf[k])[1]; //numero de face cote e
694
695 if (( sub_type(Energie_Multiphase, op_elem_->op_ext[p]->equation())
696 || sub_type(Convection_Diffusion_Temperature, op_elem_->op_ext[p]->equation()) )
697 && op_elem_->op_ext[p]->equation().probleme().has_correlation("flux_parietal")
698 && fcl[p](f, 0) && fcl[p](f, 0) != 5)
699 {
700 i_efs(i, j, M) = t_eq++;
701 }
702 }
703 }
704
705 i_e.resize(n_e, mix ? M : 1);
706 i_e = -1;
707 int t_e = 0;
708 int t_ec = 1; // t_ec = t_e + 1, nombres divises par Nl
709
710 for (int i = 0; i < n_e; i++)
711 {
712 int p = s_pe[i][0];
713
714 for (int n = 0; n < (mix ? N[p] : 1); n++, t_e++, t_ec++)
715 i_e(i, n) = t_e;
716 }
717
718 /* equations */
719 type_f.resize(n_f);
720 i_eq_flux.resize(n_f, mix ? M : 1);
721 i_eq_flux = -1;
722 i_eq_cont.resize(n_f, mix ? M : 1);
723 i_eq_cont = -1;
724 int k = 0;
725
726 for (int i = 0; i < n_f; i++)
727 {
728 int p1 = s_pf[i][0];
729 int p2 = m_pf.count(s_pf[i]) ? m_pf[s_pf[i]][0] : p1;
730 int p12[2] = { p1, p2 };
731 int n12[2] = { N[p1], N[p2] }; //nombres de composantes de chaque cote
732
733 int f = s_pf[i][1];
734 type_f[i] = fcl[p1](f, 0); //type de face
735
736 if (type_f[i] && type_f[i] != 5)
737 for (int j = 0; j < 2; j++)
738 if ( (sub_type(Energie_Multiphase, op_elem_->op_ext[p12[j]]->equation())
739 || sub_type(Convection_Diffusion_Temperature, op_elem_->op_ext[p12[j]]->equation())) //si flux parietal et CL non Neumann, il n'y a qu'une valeur en paroi
740 && op_elem_->op_ext[p12[j]]->equation().probleme().has_correlation("flux_parietal"))
741 {
742 n12[j] = 1;
743 }
744
745 if (n12[0] != n12[1])
746 {
747 Cerr << "Op_Diff_PolyMAC_MPFA_Elem : incompatibility between " << op_elem_->op_ext[p1]->equation().probleme().le_nom() <<
748 " and " << op_elem_->op_ext[p2]->equation().probleme().le_nom() << " ( " << n12[0]
749 << " vs " << n12[1] << " components)!" << finl;
750
752 }
753 //equations de flux : tous types sauf Dirichlet et Echange_impose_base
754 if (type_f[i] != 6 && type_f[i] != 7 && type_f[i] != 1 && type_f[i] != 2)
755 for (int n = 0; n < (mix ? n12[0] : 1); n++)
756 {
757 i_eq_flux(i, n) = k;
758 k++;
759 }
760
761 //equations de continuite : tous types sauf Neumann
762 if (type_f[i] != 4 && type_f[i] != 5)
763 for (int n = 0; n < (mix ? n12[0] : 1); n++)
764 {
765 i_eq_cont(i, n) = k;
766 k++;
767 }
768 }
769
770 //si inconnues de paroi de Pb_Multiphase : equations dues aux correlations de flux (dans ce cas mix = 1)
771 if (mix)
772 {
773 i_eq_pbm.resize(t_eq);
774 i_eq_pbm = -1;
775
776 for (int i = 0; i < n_e; i++)
777 {
778 int p = s_pe[i][0];
779
780 for (int j = 0; j < (int) se_f[i].size(); j++)
781 if (i_efs(i, j, M) >= 0)
782 for (int n = 0; n < N[p]; n++)
783 {
784 i_eq_pbm(i_efs(i, j, n)) = k;
785 k++;
786 }
787 }
788 }
789
790 assert(k == t_eq); //a-ton bien autant d'equations que d'inconnues?
791
792 for (int essai = 0; essai < 3; essai++) /* essai 0 : MPFA O -> essai 1 : MPFA O avec x_fs mobiles -> essai 2 : MPFA symetrique (corecive, mais pas tres consistante) */
793 {
794 if (essai == 1) /* essai 1 : tentative de symmetrisation en deplacant les x_fs. Si mix = 1, on ne peut pas les deplacer independamment */
795 {
796 /* systeme lineaire */
797 const int nc = (D - 1) * n_f;
798 const int nl = D * (D - 1) / 2 * t_e;
799 const int n_m = std::max(nc, nl);
800
801 C.resize(Nm, nc, nl);
802 Y.resize(Nm, n_m);
803 C = 0.;
804 Y = 0.;
805
806 int il = 0;
807
808 for (int i = 0; i < n_e; i++)
809 {
810 int p = s_pe[i][0];
811 int e = s_pe[i][1];
812
813 for (int d = 0; d < D; d++)
814 for (int db = 0; db < d; db++, il += !mix)
815 for (int n = 0; n < N[p]; n++, il += mix)
816 for (int j = 0; j < (int) se_f[i].size(); j++)
817 {
818 k = se_f[i][j];
819 const int f = (s_pf[k][0] == p) ? s_pf[k][1] : m_pf[s_pf[k]][1]; //indice de face, num dans le probleme courant, amont/aval
820 const int sgn = e == f_e[p](f, 0) ? 1 : -1;
821
822 for (int l = 0; l < D; l++)
823 fac[l] = sgn * domaine0.nu_dot(&diffu[p].get(), e, n, &nf[p](f, 0), i3[l]) * surf_fs[k] / fs[p](f) / vol_es[i]; //vecteur lambda_e nf sortant * facteur commun
824
825 Y(!mix * n, il) += fac[d] * (xv[p](f, db) - xp[p](e, db)) - fac[db] * (xv[p](f, d) - xp[p](e, d)); //second membre
826
827 for (int l = 0; l < D - 1; l++)
828 C(!mix * n, (D - 1) * k + l, il) += fac[db] * vec_fs[k][l][d] - fac[d] * vec_fs[k][l][db]; //matrice
829 }
830 }
831
832
833 /* resolution -> DEGLSY */
834 int nw = -1, rk=-1, infoo=-1;
835 F77NAME(dgelsy)(&nl, &nc, &un, &C(0, 0, 0), &nl, &Y(0, 0), &n_m, &piv(0), &eps_g, &rk, &W(0), &nw, &infoo);
836
837 W.resize(nw = (int) (std::lrint(W(0))));
838 piv.resize(nc);
839
840 for (int n = 0; n < Nm; n++)
841 piv = 0, F77NAME(dgelsy)(&nl, &nc, &un, &C(n, 0, 0), &nl, &Y(n, 0), &n_m, &piv(0), &eps_g, &rk, &W(0), &nw, &infoo);
842
843 /* x_fs = xf + corrections */
844 x_fs.resize(Nm, n_f, D);
845 for (int n = 0; n < Nm; n++)
846 for (int i = 0; i < n_f; i++)
847 {
848 int p = s_pf[i][0];
849 int f = s_pf[i][1];
850
851 for (int d = 0; d < D; d++)
852 for (x_fs(n, i, d) = xv[p](f, d), k = 0; k < D - 1; k++)
853 x_fs(n, i, d) += std::min(std::max(Y(n, (D - 1) * i + k), 0.), 0.5) * vec_fs[i][k][d];
854 }
855 }
856
857 A.resize(Nm, t_eq, t_eq);
858 B.resize(Nm, t_ec = t_e + 1, t_eq);
859 Ff.resize(Nm, t_eq, t_eq);
860 Fec.resize(Nm, t_eq, t_ec); //systeme A.dT_efs = B.{dT_eb, 1}, flux sortant a chaque face
861
862 if (mix)
863 {
864 Qf.resize(n_e, t_eq, M, M);
865 Qec.resize(n_e, t_ec, M, M); //si Pb_Multiphase : flux paroi-interface
866 }
867
868 /* debut du Newton sur les T_efs : initialisation aux temperatures de mailles */
869 Tefs.resize(Nm, t_eq);
870
871 for (int i = 0; i < n_e; i++)
872 {
873 int p = s_pe[i][0];
874 int e = s_pe[i][1];
875
876 for (int j = 0; j < (int) se_f[i].size(); j++)
877 {
878 for (int n = 0; n < N[p]; n++)
879 Tefs(!mix * n, i_efs(i, j, mix * n)) = inco[p](e, n); //Tefs de chaque phase
880
881 if (mix && i_efs(i, j, M) >= 0)
882 Tefs(0, i_efs(i, j, M)) = inco[p](e, 0); //Tparoi : on prend la temperature de la phase 0 faute de mieux
883 }
884 }
885
886 int nonlinear = 0;
887 int cv = 0;
888
889 for (int it = 0; !cv && it < 100; it++) //Newton sur les Tefs. Si mix = 0 (pas de Pb_Multi), une seule iteration suffit
890 {
891 A = 0.;
892 B = 0.;
893 Ff = 0.;
894 Fec = 0.;
895 Qf = 0.;
896 Qec = 0.;
897
898 for (int i = 0; i < n_e; i++)
899 {
900 int p = s_pe[i][0];
901 int e = s_pe[i][1];
902 n_ef = (int) se_f[i].size();
903
904 C.resize(Nm, n_ef, D);
905 const int n_m = std::max(D, n_ef);
906
907 Y.resize(Nm, D, n_m);
908 X.resize(Nm, n_ef, D);
909
910 /* gradient dans e */
911 if (essai < 2) /* essais 0 et 1 : gradient consistant */
912 {
913 /* gradient dans (e, s) -> matrice / second membre M.x = Y du systeme (grad u)_i = sum_f b_{fi} (x_fs_i - x_e), avec x_fs le pt de continuite de u_fs */
914 for (int n = 0; n < Nm; n++)
915 for (int j = 0; j < n_ef; j++)
916 {
917 k = se_f[i][j];
918 const int pb = s_pf[k][0];
919 const int f = s_pf[k][1];
920
921 for (int d = 0; d < D; d++)
922 C(n, j, d) = (essai ? x_fs(n, k, d) : xv[pb](f, d)) - xp[p](e, d);
923 }
924
925 Y = 0.;
926
927 for (int n = 0; n < Nm; n++)
928 for (int d = 0; d < D; d++)
929 Y(n, d, d) = 1.;
930
931 int nw = -1, rk=-1, infoo=-1;
932
933 F77NAME(dgelsy)(&D, &n_ef, &D, &C(0, 0, 0), &D, &Y(0, 0, 0), &n_m, &piv(0), &eps_g, &rk, &W(0), &nw, &infoo);
934
935 nw = (int) (std::lrint(W(0)));
936
937 W.resize(nw);
938 piv.resize(n_m);
939
940 for (int n = 0; n < Nm; n++)
941 {
942 piv = 0;
943 F77NAME(dgelsy)(&D, &n_ef, &D, &C(n, 0, 0), &D, &Y(n, 0, 0), &n_m, &piv(0), &eps_g, &rk, &W(0), &nw, &infoo);
944 }
945
946 for (int n = 0; n < Nm; n++)
947 for (int j = 0; j < n_ef; j++)
948 for (int d = 0; d < D; d++)
949 X(n, j, d) = Y(n, d, j); /* pour pouvoir utiliser nu_dot */
950 }
951 else /* essai 2 : gradient non consistant */
952 {
953 for (int n = 0; n < Nm; n++)
954 for (int j = 0; j < n_ef; j++)
955 {
956 k = se_f[i][j];
957 const int pb = s_pf[k][0];
958 const int f = s_pf[k][1];
959 const int sgn = (pb == p) && (e == f_e[p](f, 0)) ? 1 : -1;
960
961 for (int d = 0; d < D; d++)
962 X(n, j, d) = surf_fs[k] / vol_es[i] * sgn * nf[pb](f, d) / fs[pb](f); /* essai 2 : gradient non consistant */
963 }
964 }
965
966 /* remplissage de A, B, Ff, Fec */
967 for (int j = 0; j < n_ef; j++)
968 {
969 k = se_f[i][j];
970 const int f = (s_pf[k][0] == p) &&
971 (e == f_e[p](s_pf[k][1], 0) || e == f_e[p](s_pf[k][1], 1)) ? s_pf[k][1] : m_pf[s_pf[k]][1]; //indice de face, numero de face local
972
973 const int sgn_l = (e == f_e[p](f, 0)) ? 1 : -1;
974 const int sgn = (p == s_pf[k][0]) && (f == s_pf[k][1]) ?
975 sgn_l : -1; //orientation de la face locale, de la face globale
976
977 /* flux : remplit Ff / Fec (format standard) dans tous les cas, et les equations de A/B donnees par i_eq_flux / i_eq_cont (format LAPACK) */
978 for (int l = 0; l < n_ef; l++)
979 {
980 for (int n = 0; n < N[p]; n++)
981 {
982 const double x = sgn_l * domaine0.nu_dot(&diffu[p].get(), e, n, &nf[p](f, 0), &X(!mix * n, l, 0)) / fs[p](f);
983 const double y = x * surf_fs[k];
984
985 /* stockage du flux sortant dans Ff / Fec */
986 Fec(!mix * n, i_efs(i, j, mix * n), t_e) += y * (Tefs(!mix * n, i_efs(i, l, mix * n)) - inco[p](e, n));
987
988 Ff(!mix * n, i_efs(i, j, mix * n), i_efs(i, l, mix * n)) += y;
989
990 Fec(!mix * n, i_efs(i, j, mix * n), i_e(i, mix * n)) -= y;
991
992 /* equations de flux : on contribue a celle de la phase (si elle existe), ou a defaut a celle de la phase 0 */
993 int i_eq = i_eq_flux(k, mix * n);
994
995 if (i_eq < 0)
996 i_eq = i_eq_flux(k, 0); /* ou a defaut a celle de la phase 0 */
997
998 if (i_eq >= 0)
999 {
1000 B(!mix * n, t_e, i_eq) += x * (Tefs(!mix * n, i_efs(i, l, mix * n)) - inco[p](e, n));
1001
1002 A(!mix * n, i_efs(i, l, mix * n), i_eq) -= x;
1003
1004 B(!mix * n, i_e(i, mix * n), i_eq) -= x;
1005 }
1006
1007 /* equations de continuite : on y contribue si on est l'amont d'un Echange_contact pour prendre en compte son coeff d'echange */
1008 i_eq = i_eq_cont(k, mix * n);
1009 if (i_eq < 0)
1010 i_eq = i_eq_cont(k, 0); /* ou a defaut a celle de la phase 0 */
1011
1012 if (sgn > 0 && (type_f[k] && type_f[k] < 4) && (i_eq >= 0) )
1013 {
1014
1015 const double h = (type_f[k] == 3) ? -1 /* inutile */:
1016 ref_cast(Echange_impose_base, cls[p].get()[fcl[p](f, 1)].valeur()).h_imp(fcl[p](f, 1), !mix || (i_eq == i_eq_cont(k, n)) ? n : 0);
1017
1018 const double invh = type_f[k] == 3 ? ref_cast(Echange_contact_PolyMAC_MPFA, cls[p].get()[fcl[p](f, 1)].valeur()).invh_paroi :
1019 1. / std::max(h, 1e-10);
1020
1021 B(!mix * n, t_e, i_eq) += invh * x * (Tefs(!mix * n, i_efs(i, l, mix * n)) - inco[p](e, n));
1022
1023 A(!mix * n, i_efs(i, l, mix * n), i_eq) -= invh * x;
1024
1025 B(!mix * n, i_e(i, mix * n), i_eq) -= invh * x;
1026 }
1027
1028 /* si Pb_Multiphase : partie "flux" de l'equation sur la correlation */
1029
1030 if (mix)
1031 {
1032 i_eq = i_eq_pbm(i_efs(i, j, mix * n));
1033 if (i_eq >= 0)
1034 {
1035 B(!mix * n, t_e, i_eq) += x * (Tefs(!mix * n, i_efs(i, l, mix * n)) - inco[p](e, n));
1036
1037 A(!mix * n, i_efs(i, l, mix * n), i_eq) -= x;
1038
1039 B(!mix * n, i_e(i, mix * n), i_eq) -= x;
1040 }
1041 }
1042 }
1043 }
1044
1045 /* autres equations : continuite, correlations */
1046 if (mix && i_efs(i, j, M) >= 0) /* inconnue de Tparoi -> Pb_Multiphase */
1047 {
1048 //equation de continuite : avec la Tparoi
1049 int i_eq = i_eq_cont(k, 0);
1050
1051 if (i_eq >= 0)
1052 {
1053 B(0, t_e, i_eq) += sgn * Tefs(0, i_efs(i, j, M));
1054
1055 A(0, i_efs(i, j, M), i_eq) -= sgn;
1056 }
1057
1058 //equations sur les correlations
1059 const Probleme_base& pbp = op_elem_->op_ext[p]->equation().probleme();
1060 const Flux_parietal_base& corr = ref_cast(Flux_parietal_base, pbp.get_correlation("Flux_parietal"));
1061
1062 const DoubleTab *alpha = sub_type(Pb_Multiphase, pbp) ? &ref_cast(Pb_Multiphase, pbp).equation_masse().inconnue().passe() : nullptr;
1063
1064 const DoubleTab& dh = pbp.milieu().diametre_hydraulique_elem(),
1065 &press = ref_cast(Navier_Stokes_std, pbp.equation(0)).pression().passe(),
1066 &vit = ref_cast(Navier_Stokes_std, pbp.equation(0)).inconnue().passe(),
1067 &lambda = pbp.milieu().conductivite().passe(),
1068 &mu = ref_cast(Fluide_base, pbp.milieu()).viscosite_dynamique().passe(),
1069 &rho = pbp.milieu().masse_volumique().passe(),
1070 &Cp = pbp.milieu().capacite_calorifique().passe();
1071
1072 /* uniforme fields ??? */
1073 const int Clambda = (lambda.dimension(0) == 1), Cmu = (mu.dimension(0) == 1),
1074 Crho = (rho.dimension(0) == 1), Ccp = (Cp.dimension(0) == 1);
1075
1078
1079 DoubleTrav Tf(N[p]), qpk(N[p]), dTf_qpk(N[p], N[p]),
1080 dTp_qpk(N[p]), qpi(N[p], N[p]), dTf_qpi(N[p], N[p], N[p]),
1081 dTp_qpi(N[p], N[p]), nv(N[p]), d_nuc(N[p]);
1082
1083
1084 /* fill inputs */
1085 in.N = N[p];
1086 in.f = f;
1087 in.y = (e == f_e[p](f, 0)) ? domaine[p].get().dist_face_elem0(f, e) : domaine[p].get().dist_face_elem1(f, e);
1088 in.D_h = dh(e);
1089 in.D_ch = dh(e);
1090 in.alpha = alpha ? &(*alpha)(e, 0) : nullptr;
1091 in.T = &Tf(0);
1092 in.p = press(e);
1093 in.v = nv.addr();
1094 in.Tp = Tefs(0, i_efs(i, j, M));
1095 in.lambda = &lambda(!Clambda * e, 0);
1096 in.mu = &mu(!Cmu * e, 0);
1097 in.rho = &rho(!Crho * e, 0);
1098 in.Cp = &Cp(!Ccp * e, 0);
1099
1100 if (corr.needs_saturation())
1101 {
1102 in.Lvap = &Lvap_tab[p](e, 0);
1103 in.Sigma = &Sigma_tab[p](e, 0);
1104 in.Tsat = &Ts_tab[p](e, 0);
1105 }
1106 else
1107 {
1108 in.Lvap = nullptr;
1109 in.Sigma = nullptr;
1110 in.Tsat = nullptr;
1111 }
1112
1113 /* init outputs */
1114 out.qpk = &qpk;
1115 out.dTf_qpk = &dTf_qpk;
1116 out.dTp_qpk = &dTp_qpk;
1117 out.qpi = &qpi;
1118 out.dTp_qpi = &dTp_qpi;
1119 out.dTf_qpi = &dTf_qpi;
1120 out.nonlinear = &nonlinear;
1121 out.d_nuc = &d_nuc;
1122
1123 for (int d = 0; d < D; d++)
1124 for (int n = 0; n < N[p]; n++)
1125 nv(n) += std::pow(vit(domaine[p].get().nb_faces_tot() + D * e + d, n), 2);
1126
1127 for (int n = 0; n < N[p]; n++)
1128 {
1129 nv(n) = sqrt(nv(n));
1130 Tf(n) = corr.T_at_wall() ? Tefs(0, i_efs(i, j, n)) : inco[p](e, n);
1131 }
1132
1133 // appel : on n'est implicite qu'en les temperatures
1134 corr.qp(in, out);
1135
1136 /* qpk : contributions aux equations sur les Tkp */
1137 for (int n = 0; n < N[p]; n++)
1138 {
1139 i_eq = i_eq_pbm(i_efs(i, j, n));
1140
1141 B(0, t_e, i_eq) -= qpk(n);
1142
1143 A(0, i_efs(i, j, M), i_eq) += dTp_qpk(n);
1144
1145 for (int m = 0; corr.T_at_wall() && m < N[p]; m++)
1146 A(0, i_efs(i, j, m), i_eq) += dTf_qpk(n, m);
1147 }
1148
1149 /* qpi : contribution a l'equation de flux a la face (si elle existe), contributions au tableau de flux paroi-interface */
1150 i_eq = i_eq_flux(k, 0);
1151 if (i_eq >= 0)
1152 for (int k1 = 0; k1 < N[p]; k1++)
1153 for (int k2 = k1 + 1; k2 < N[p]; k2++) //partie constante, derivee en Tp
1154 {
1155 B(0, t_e, i_eq) += qpi(k1, k2);
1156
1157 A(0, i_efs(i, j, M), i_eq) -= dTp_qpi(k1, k2);
1158
1159 for (int n = 0; corr.T_at_wall() && n < N[p]; n++)
1160 A(0, i_efs(i, j, n), i_eq) -= dTf_qpi(k1, k2, n);
1161 }
1162
1163 for (int k1 = 0; k1 < N[p]; k1++)
1164 for (int k2 = k1 + 1; k2 < N[p]; k2++) //partie constante, derivee en Tp
1165 {
1166 Qec(i, t_e, k1, k2) += surf_fs[k] * qpi(k1, k2);
1167
1168 Qf(i, i_efs(i, j, M), k1, k2) += surf_fs[k] * dTp_qpi(k1, k2);
1169
1170 for (int m = 0; corr.T_at_wall() && m < N[p]; m++)
1171 Qf(i, i_efs(i, j, m), k1, k2) += surf_fs[k] * dTf_qpi(k1, k2, m); //derivees en Tf
1172 }
1173
1174 if (d_nuc_.dimension(0) && !d_nuc_a_jour_)
1175 for (int k1 = 0; k1 < N[p]; k1++) // d_nuc depends on the temperature so must only be updated once when the temperature input of the wall flux correlation is the old temperature
1176 d_nuc_(e, k1) += d_nuc(k1), d_nuc_a_jour_ = 1;
1177 }
1178 else /* pas d'inconnue de Tparoi -> continuite composante par composante */
1179 {
1180 for (int n = 0; n < N[p]; n++)
1181 {
1182 const int i_eq = i_eq_cont(k, mix * n);
1183 if (i_eq >= 0) /* pas d'inconnue de Tparoi -> continuite composante par composante */
1184 {
1185 B(!mix * n, t_e, i_eq) += sgn * Tefs(!mix * n, i_efs(i, j, mix * n));
1186
1187 A(!mix * n, i_efs(i, j, mix * n), i_eq) -= sgn;
1188 }
1189 }
1190 }
1191
1192 /* contributions CLs : aux equations de flux si Neumann, a celles de continuite si Dirichlet ou Echange_impose */
1193 if (type_f[k] == 1 || type_f[k] == 2)
1194 for (int n = 0; n < N[p]; n++)
1195 {
1196 const int i_eq = i_eq_cont(k, mix * n);
1197 if (i_eq >= 0) //Echange_impose_base
1198 B(!mix * n, t_e, i_eq) -= ref_cast(Echange_impose_base, cls[p].get()[fcl[p](f, 1)].valeur()).T_ext(fcl[p](f, 2), n);
1199 }
1200
1201 if (type_f[k] == 6)
1202 for (int n = 0; n < N[p]; n++)
1203 {
1204 const int i_eq = i_eq_cont(k, mix * n);
1205 if (i_eq >= 0) //Dirichlet (non homogene)
1206 B(!mix * n, t_e, i_eq) -= ref_cast(Dirichlet, cls[p].get()[fcl[p](f, 1)].valeur()).val_imp(fcl[p](f, 2), n);
1207 }
1208
1209 if (type_f[k] == 4)
1210 for (int n = 0; n < N[p]; n++)
1211 {
1212 const int i_eq = i_eq_flux(k, mix * n);
1213 if (i_eq >= 0) //Neumann
1214 B(!mix * n, t_e, i_eq) -= ref_cast(Neumann, cls[p].get()[fcl[p](f, 1)].valeur()).flux_impose(fcl[p](f, 2), n);
1215 }
1216 }
1217 }
1218 /* resolution(s) -> DGELSY */
1219 int nw = -1, rk=-1, infoo=-1;
1220
1221 F77NAME(dgelsy)(&t_eq, &t_eq, &t_ec, &A(0, 0, 0), &t_eq, &B(0, 0, 0), &t_eq, &piv(0), &eps, &rk, &W(0), &nw, &infoo);
1222
1223 piv.resize(t_eq);
1224
1225 nw = (int) (std::lrint(W(0)));
1226 W.resize(nw);
1227
1228 for (int n = 0; n < Nm; n++)
1229 {
1230 piv = 0;
1231 F77NAME(dgelsy)(&t_eq, &t_eq, &t_ec, &A(n, 0, 0), &t_eq, &B(n, 0, 0), &t_eq, &piv(0), &eps, &rk, &W(0), &nw, &infoo);
1232 }
1233
1234 /* mise a jour des Tefs et convergence. Si nonlinear = 0, tout est lineaire -> pas besoin d'autres iterations */
1235 for (int n = 0; n < Nm; n++)
1236 {
1237 cv = 1;
1238 for (int i = 0; i < t_eq; i++)
1239 {
1240 Tefs(n, i) += B(n, t_e, i);
1241 cv &= !nonlinear || std::fabs(B(n, t_e, i)) < 1e-3;
1242 }
1243 }
1244 }
1245
1246 if (!cv && essai < 2)
1247 continue; //T_efs pas converge avec flux non coercif -> on essaie de stabiliser
1248 else if (!cv)
1249 {
1250 Cerr << "non-convergence des T_efs!" << finl;
1251 Process::exit();
1252 }
1253
1254 /* subbstitution dans des u_efs^n dans Fec et Qec */
1255 for (int n = 0; n < Nm; n++)
1256 for (int i = 0; i < t_eq; i++)
1257 for (int j = 0; j < t_ec; j++)
1258 for (k = 0; k < t_eq; k++)
1259 Fec(n, i, j) += Ff(n, i, k) * B(n, j, k);
1260
1261 if (mix)
1262 for (int i = 0; i < n_e; i++)
1263 for (int j = 0; j < t_ec; j++)
1264 for (k = 0; k < t_eq; k++)
1265 for (int k1 = 0; k1 < M; k1++)
1266 for (int k2 = k1 + 1; k2 < M; k2++)
1267 Qec(i, j, k1, k2) += Qf(i, k, k1, k2) * B(0, j, k);
1268
1269 /* A : forme(s) bilineaire */
1270 if (essai == 2)
1271 break; //pas la peine pour VFSYM
1272
1273 A.resize(Nm, t_e, t_e);
1274 A = 0.;
1275
1276 for (int m = 0; m < Nm; m++)
1277 for (int i = 0; i < n_e; i++)
1278 {
1279 const int p = s_pe[i][0];
1280
1281 for (int j = 0; j < (int) se_f[i].size(); j++)
1282 for (int n = 0; n < (mix ? N[p] : 1); n++)
1283 for (int l = 0; l < t_e; l++)
1284 A(m, i_e(i, n), l) -= Fec(m, i_efs(i, j, n), l);
1285 }
1286
1287
1288 /* symmetrisation */
1289 for (int n = 0; n < Nm; n++)
1290 for (int i = 0; i < t_e; i++)
1291 for (int j = 0; j <= i; j++)
1292 {
1293 A(n, i, j) = A(n, j, i) = (A(n, i, j) + A(n, j, i)) / 2.;
1294 }
1295
1296 /* v.p. la plus petite : DSYEV */
1297 int nw = -1, infoo=-1;
1298 F77NAME(DSYEV)("N", "U", &t_e, &A(0, 0, 0), &t_e, S.addr(), &W(0), &nw, &infoo);
1299
1300 nw = (int) (std::lrint(W(0)));
1301 W.resize(nw);
1302 S.resize(t_e);
1303 cv = 1;
1304
1305 for (int n = 0; n < Nm; n++)
1306 F77NAME(DSYEV)("N", "U", &t_e, &A(n, 0, 0), &t_e, &S(0), &W(0), &nw, &infoo), cv &= S(0) > -1e-8 * vol_s;
1307
1308 if (cv)
1309 break;
1310 }
1311
1312 /* contributions aux flux et aux matrices */
1313 for (int i = 0; i < n_e; i++)
1314 {
1315 const int e = s_pe[i][1];
1316
1317 if ((s_pe[i][0] == 0) && (e < domaine[0].get().nb_elem()))
1318 for (int j = 0; j < (int) se_f[i].size(); j++)
1319 {
1320 k = se_f[i][j];
1321 const int f = s_pf[k][0] ? m_pf[s_pf[k]][1] : s_pf[k][1];
1322
1323 for (int n = 0; n < N[0]; n++) //seulement celles du probleme courant
1324 {
1325 secmem(e, n) += Fec(!mix * n, i_efs(i, j, mix * n), t_e); //partie constante
1326
1327 if (f < domaine0.premiere_face_int())
1328 op_elem_->flux_bords()(f, n) += Fec(!mix * n, i_efs(i, j, mix * n), t_e);
1329
1330 for (k = 0; k < n_e; k++)
1331 {
1332 const int p = s_pe[k][0];
1333
1334 if (mat[p])
1335 {
1336 const int eb = s_pe[k][1];
1337
1338 for (int m = (mix ? 0 : n); m < (mix ? N[p] : n + 1); m++) // derivees : si on a la matrice
1339 (*mat[p])(N[0] * e + n, N[p] * eb + m) -= Fec(!mix * n, i_efs(i, j, mix * n), i_e(k, mix * m));
1340 }
1341 }
1342 }
1343 }
1344 }
1345
1346 /* contributions a qpi : partie constante seulement pour le moment */
1347 if (pqpi)
1348 for (int i = 0; i < n_e; i++)
1349 if (s_pe[i][0] == 0)
1350 {
1351 const int e = s_pe[i][1];
1352
1353 for (int k1 = 0; k1 < N[0]; k1++)
1354 for (int k2 = k1 + 1; k2 < N[0]; k2++)
1355 (*pqpi)(e, k1, k2) += Qec(i, t_e, k1, k2);
1356 }
1357 }
1358 }
1359
1360 if (pqpi)
1361 (*pqpi).echange_espace_virtuel();
1362}
: 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 & passe(int i=1)
Definition Champ_Proto.h:50
Domaine_Cl_dis_base & domaine_Cl_dis()
Renvoie le domaine des conditions aux limites discretisee dont l'objet fait partie.
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
void ajouter_blocs(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={}) const
void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={}) const
void associer(const Op_Diff_PolyMAC_MPFA_Elem &op)
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.
double dot(const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
int nb_faces() const
renvoie le nombre global de faces.
Definition Domaine_VF.h:471
std::array< double, 3 > cross(int dima, int dimb, const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
Definition Domaine_VF.h:711
int premiere_face_int() const
une face est interne ssi elle separe deux elements.
Definition Domaine_VF.h:463
int nb_elem_tot() const
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...
virtual const Milieu_base & milieu() const =0
Sources & sources()
Renvoie les termes sources asssocies a l'equation.
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.
classe Fluide_base Cette classe represente un d'un fluide incompressible ainsi que
Definition Fluide_base.h:38
classe Flux_parietal_base correlations de flux parietal de la forme
virtual int needs_saturation() const
virtual int T_at_wall() const =0
virtual void qp(const input_t &input, output_t &output) const =0
class Front_VF
Definition Front_VF.h:36
DoubleTab & get_sigma_tab()
Classe Matrice_Morse Represente une matrice M (creuse), non necessairement carree.
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)
virtual const Equation_base & equation(const std::string &nom_inc) const
virtual const Champ_Don_base & capacite_calorifique() const
Renvoie la capacite calorifique du milieu.
virtual const Champ_Don_base & conductivite() const
Renvoie la conductivite du milieu.
virtual const Champ_base & masse_volumique() const
Renvoie la masse volumique du milieu.
DoubleTab & diametre_hydraulique_elem()
Definition Milieu_base.h:70
Classe Milieu_composite Cette classe represente un fluide reel ainsi que.
bool has_saturation(int k, int l) const
Saturation_base & get_saturation(int k, int l) const
const Equation_base & equation() const
Renvoie la reference sur l'equation pointe par MorEqn::mon_equation.
Definition MorEqn.h:62
classe Navier_Stokes_std Cette classe porte les termes de l'equation de la dynamique
Classe Neumann_paroi Cette condition limite correspond a un flux impose pour l'equation de.
Classe Neumann Cette classe est la classe de base de la hierarchie des conditions aux limites de type...
Definition Neumann.h:31
static int dimension
Definition Objet_U.h:99
const Couplage_Parietal_PolyMAC_MPFA_helper & couplage_parietal_helper() const
classe Pb_Multiphase Cette classe represente un probleme de thermohydraulique multiphase de type "3*N...
const Equation_base & equation(int) const override
Renvoie l'equation d'hydraulique de type Navier_Stokes_std si i=0 Renvoie l'equation de la thermique ...
classe Probleme_base C'est un Probleme_U qui n'est pas un couplage.
const Correlation_base & get_correlation(std::string nom_correlation) const
virtual const Milieu_base & milieu() const
Renvoie le milieu physique associe au probleme.
virtual const Equation_base & equation(int) const =0
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 QDM_Multiphase Cette classe porte les termes de l'equation de la dynamique
DoubleTab & get_Tsat_tab()
void Lvap(const SpanD P, SpanD res, int ncomp=1, int ind=0) const
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
int line_size() const
Definition TRUSTVect.tpp:67
Span_ get_span_tot() override
Definition TRUSTVect.h:182