TrioCFD 1.9.9_beta
TrioCFD documentation
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/* build s_dist: vertices of the problem coinciding with vertices of remote problems */
50void Couplage_Parietal_PolyMAC_MPFA_helper::init_s_dist() const
51{
52 if (s_dist_init_)
53 return; //already done
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; //already done
79
80 /* fill op_ext: step by step */
81 op_elem_->op_ext = { &(op_elem_.valeur()) };
82 init_s_dist();
83
84 /* build 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 //extension of 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 //extension of 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 : this is a 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 /* other BCs (excluding Echange_contact) to be handled by som_ext: Echange_impose_base, all if Pb_Multiphase with 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] = { }; //in the std::map, but no remote operators!
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 }); //local side
183 }
184
185 for (const auto &op_sb : s_op_sb.second) //remote sides
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: those of elements in 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 not touching the vertex or not 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 //operator / face on the other side
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 }); //stored in order
229 }
230 }
231
232 int mix = 0; //should we mix components around this vertex? yes if an Energie_Multiphase equation with a wall flux correlation is present around it
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 //one potential matrix to fill per operator in 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()); //number of components per problem in op_ext
273
274 std::vector<Stencil> stencil(op_elem_->op_ext.size()); //stencils per matrix
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]); //to track which fluxes are two-point
283
284 domaine.creer_tableau_faces(tpfa);
285 tpfa = 1;
286
287 Cerr << "Op_Diff_PolyMAC_MPFA_Elem::dimensionner() : ";
288
289 //with fgrad: parts outside Echange_contact (does not mix problems or components)
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 at 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 //with som_ext: Echange_contact part -> mixes all components if 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: total number of stencil points of the operator
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; //components
362
363 /*
364 * XXX NO WORRIES : this is a std::vector<ref_array>
365 */
366 std::vector<std::reference_wrapper<const Domaine_PolyMAC_MPFA>> domaine; //domains
367 std::vector<std::reference_wrapper<const Conds_lim>> cls; //boundary conditions
368 std::vector<std::reference_wrapper<const IntTab>> fcl, e_f, f_e, f_s; //arrays "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; //unknowns, face normals, element/face/vertex positions
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 /* using phif: fluxes excluding Echange_contact -> mat[0] only */
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++) //non-homogeneous Neumann
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++) //right-hand side -> upwind/downwind
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); //boundary fluxes
466 }
467
468 d_nuc_ = 0.; //reset nucleation diameter to zero
469
470 // fill the arrays ... but only if needed!
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 /* using som_ext: fluxes around vertices affected by 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; //lists { problem, element/boundary }, { problem, face } around the vertex
480 std::map<std::array<int, 2>, std::array<int, 2>> m_pf; //Echange_contact connections: m_pf[{pb1, f1}] = { pb2, f2 }
481 std::vector<double> surf_fs, vol_es; //partial surfaces of faces connected to the vertex (same order as s_f)
482 std::vector<std::array<std::array<double, 3>, 2>> vec_fs; //for each facet, a basis of (D-1) vectors to traverse it
483 std::vector<std::vector<int>> se_f; /* se_f[i][.]: faces connected to the i-th element connected to vertex s */
484 std::vector<int> type_f; //type_f[i]: type of face i (numbering from the fcl array)
485
486 IntTrav i_efs, i_e, i_eq_flux, i_eq_cont, i_eq_pbm, piv(1); //indices in matrices: i_efs(i, j, n) -> component n of face j of element i in s_pe, i_e(i, n) -> index of phase n of element i in s_pe
487
488 //i_eq_{flux,cont}(i, n) -> n-th flux/continuity equation at face i of s_pf
489
490 //i_eq_pbm(i_efs(i, j, n)) -> n-th "flux = correlation" equation at face j of element i in s_pe (only if Pb_Multiphase)
491
492 DoubleTrav A, B, Ff, Fec, Qf, Qec, Tefs, C, X, Y, W(1), S, x_fs;
493
494 // And for the span methods of the Saturation class for the wall flux
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; // yes!! arithmetic series!!
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); // yes! upper triangular matrix!
518
519 // XXX XXX XXX
520 // Warning: this is dangerous! We assume for now that the pressure field has 1 component. However, the size of res is nb_max_sat*nbelem !!
521 // Also, we pass the Span with nbelem for the pressure field, not nbelem_tot ....
522 assert(press.line_size() == 1);
523
524 // retrieve Tsat and 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() /* real elements */, 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) connected to s -> using som_ext_pe (already sorted) */
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: contact-wall correspondences */
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 and their partial surfaces */
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 of e not connected to s -> skip */
590
591 se_f[i].push_back(f); //faces connected to (p, e)
592
593 //(p, f) pair for the face: if it is an Echange_contact, choose the pair from the problem with the lowest index
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) /* if (p, f) is not in s_pf, add it */
600 {
601 s_pf.insert(s_pf.begin() + l, pf); //(pb, face) -> in 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); //next vertex (m = 1) or previous with k > 0 -> straightforward
617 }
618 else
619 {
620 for (int n = f_s[p].get().dimension(1) - 1; (sb = f_s[p](f, n)) == -1;)
621 n--; //previous vertex with k = 0 -> search from the end
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)); //cross product (xs - xf)x(xsb - xf)
625
626 surf_fs[l] += std::fabs(domaine0.dot(&v[0], &nf[p](f, 0))) / fs[p](f) / 4; //area to add
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); //vector face -> edge
630 }
631 }
632 }
633 }
634 }
635
636 int n_f = (int) s_pf.size(); //number of faces
637
638 /* convert se_f to indices in 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 /* wall unknowns (i_efs), element unknowns (i_e). All components are allocated if som_mix = 1, only one otherwise */
671 int mix = som_mix(i_s), Nm = mix ? 1 : N[s_pe[0][0]]; //total number of equations/variables at faces, total number of variables at elements, t_ec = t_e + 1, counts divided by 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())); //maximum number of faces per element
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; //one wall temperature per phase
691
692 //if boundary face of a Pb_Multiphase (excluding open boundary), one extra unknown: Tparoi (in this case, 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]; //face number on the e side
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, counts divided by 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); // type of each face
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] }; //number of components on each side
732
733 int f = s_pf[i][1];
734 type_f[i] = fcl[p1](f, 0); //face type
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())) //if wall flux and non-Neumann BC, there is only one wall value
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 //flux equations: all types except Dirichlet and 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 //continuity equations: all types except 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 //if Pb_Multiphase wall unknowns are present: equations from wall flux correlations (in this case 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); //do we have exactly as many equations as unknowns?
791
792 for (int essai = 0; essai < 3; essai++) /* attempt 0: MPFA O -> attempt 1: MPFA O with mobile x_fs -> attempt 2: symmetric MPFA (coercive, but not very consistent) */
793 {
794 if (essai == 1) /* attempt 1: symmetrization attempt by moving x_fs. If mix = 1, they cannot be moved independently */
795 {
796 /* linear system */
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]; //face index, local number in the current problem, upwind/downwind
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]; //vector lambda_e outgoing nf * common factor
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)); //right-hand side
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 /* solve -> DGELSY */
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); //system A.dT_efs = B.{dT_eb, 1}, outgoing flux at each face
861
862 if (mix)
863 {
864 Qf.resize(n_e, t_eq, M, M);
865 Qec.resize(n_e, t_ec, M, M); //if Pb_Multiphase: wall-interface flux
866 }
867
868 /* start of Newton iterations on T_efs: initialization to cell temperatures */
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 of each phase
880
881 if (mix && i_efs(i, j, M) >= 0)
882 Tefs(0, i_efs(i, j, M)) = inco[p](e, 0); //Tparoi: use phase-0 temperature as a fallback
883 }
884 }
885
886 int nonlinear = 0;
887 int cv = 0;
888
889 for (int it = 0; !cv && it < 100; it++) //Newton on Tefs. If mix = 0 (no Pb_Multiphase), a single iteration suffices
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 in e */
911 if (essai < 2) /* attempts 0 and 1: consistent gradient */
912 {
913 /* gradient in (e, s) -> matrix / right-hand side M.x = Y of the system (grad u)_i = sum_f b_{fi} (x_fs_i - x_e), where x_fs is the continuity point of 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); /* to be able to use nu_dot */
950 }
951 else /* attempt 2: non-consistent gradient */
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); /* attempt 2: non-consistent gradient */
963 }
964 }
965
966 /* fill 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]; //face index, local face number
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 of the local face, of the global face
976
977 /* flux: fills Ff / Fec (standard format) in all cases, and the A/B equations given by i_eq_flux / i_eq_cont (LAPACK format) */
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 /* store the outgoing flux in 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 /* flux equations: contribute to the equation of the phase (if it exists), or to that of phase 0 as a fallback */
993 int i_eq = i_eq_flux(k, mix * n);
994
995 if (i_eq < 0)
996 i_eq = i_eq_flux(k, 0); /* or to that of phase 0 as a fallback */
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 /* continuity equations: contribute if we are upstream of an Echange_contact to account for its exchange coefficient */
1008 i_eq = i_eq_cont(k, mix * n);
1009 if (i_eq < 0)
1010 i_eq = i_eq_cont(k, 0); /* or to that of phase 0 as a fallback */
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 /* unused */:
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 /* if Pb_Multiphase: "flux" part of the correlation equation */
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 /* other equations: continuity, correlations */
1046 if (mix && i_efs(i, j, M) >= 0) /* wall temperature unknown -> Pb_Multiphase */
1047 {
1048 //continuity equation: involving 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 for the 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 // call: implicit only in temperatures
1134 corr.qp(in, out);
1135
1136 /* qpk: contributions to equations on 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 to the face flux equation (if it exists), contributions to the wall-interface flux array */
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++) //constant part, derivative with respect to 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++) //constant part, derivative with respect to 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); //derivatives with respect to 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 /* no Tparoi unknown: continuity enforced component by component */
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) /* no Tparoi unknown: continuity enforced component by component */
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 /* BC contributions: to flux equations if Neumann, to continuity equations if Dirichlet or 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 /* solve -> 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 /* update Tefs and check convergence. If nonlinear = 0, everything is linear -> no further iterations needed */
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 not converged with non-coercive flux -> try stabilization
1248 else if (!cv)
1249 {
1250 Cerr << "non-convergence of T_efs!" << finl;
1251 Process::exit();
1252 }
1253
1254 /* substitute u_efs^n into Fec and 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: bilinear form(s) */
1270 if (essai == 2)
1271 break; //not needed for 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 /* symmetrize */
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 /* smallest eigenvalue: 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 to fluxes and 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++) //only those of the current problem
1324 {
1325 secmem(e, n) += Fec(!mix * n, i_efs(i, j, mix * n), t_e); //constant part
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++) // derivatives: if we have the matrix
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 to qpi: constant part only for now */
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
Class Champ_Inc_base.
DoubleTab & valeurs() override
Returns the array of field values at the current time.
virtual DoubleTab & passe(int i=1)
Definition Champ_Proto.h:50
Domaine_Cl_dis_base & domaine_Cl_dis()
Returns the domain of discretized boundary conditions to which the object belongs.
class Conds_lim This class represents a vector of boundary conditions.
Definition Conds_lim.h:32
Convection_Diffusion_Temperature Special case of 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)
Dirichlet This class is the base class of the hierarchy of Dirichlet-type boundary conditions.
Definition Dirichlet.h:31
const Cond_lim & les_conditions_limites(int) const
Returns the i-th boundary condition.
double dot(const double *a, const double *b, const double *ma=nullptr, const double *mb=nullptr) const
int nb_faces() const
Returns the total number of 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:710
int premiere_face_int() const
A face is internal if and only if it separates two elements.
Definition Domaine_VF.h:463
int nb_elem_tot() const
class : Echange_contact_PolyMAC_MPFA
Echange_impose_base: This boundary condition is used only for the energy equation.
Specialisation of Convection_Diffusion_std for multiphase flows where the transported scalar is tempe...
virtual const Milieu_base & milieu() const =0
Sources & sources()
Returns the source terms associated with the equation.
virtual const Champ_Inc_base & inconnue() const =0
virtual Domaine_Cl_dis_base & domaine_Cl_dis()
Returns the discretized boundary condition domain associated with the equation.
Probleme_base & probleme()
Returns the problem associated with the equation.
Base class for an incompressible fluid and its properties:
Definition Fluide_base.h:36
Base class for wall heat flux correlations of the form:
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()
Matrice_Morse class - Represents a (sparse) matrix M, not necessarily square,.
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
Returns the heat capacity of the medium (const version).
virtual const Champ_Don_base & conductivite() const
Returns the conductivity of the medium (const version).
virtual const Champ_base & masse_volumique() const
Returns the mass density of the medium (const version).
DoubleTab & diametre_hydraulique_elem()
Definition Milieu_base.h:70
Composite medium representing a multiphase fluid and its properties:
bool has_saturation(int k, int l) const
Saturation_base & get_saturation(int k, int l) const
const Equation_base & equation() const
Returns the reference to the equation pointed to by MorEqn::mon_equation.
Definition MorEqn.h:62
Navier_Stokes_std This class carries the terms of the momentum equation.
Classe Neumann_paroi This boundary condition corresponds to an imposed flux for the.
Classe Neumann This class is the base class of the hierarchy of Neumann-type boundary conditions.
Definition Neumann.h:31
static int dimension
Definition Objet_U.h:94
const Couplage_Parietal_PolyMAC_MPFA_helper & couplage_parietal_helper() const
Multiphase thermohydraulics problem of type "3*N equations":
const Equation_base & equation(int) const override
Returns the equation at index i (const version).
class Probleme_base It is a Probleme_U that is not a coupling.
const Correlation_base & get_correlation(std::string nom_correlation) const
virtual const Milieu_base & milieu() const
Returns the physical medium associated with the problem (const version).
virtual const Equation_base & equation(int) const =0
static double mp_sum_as_double(int v)
Definition Process.h:97
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
Carries the terms of the momentum equation for multiphase flow without turbulence modelling.
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