TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
Op_Diff_DG_Elem.cpp
1/****************************************************************************
2 * Copyright (c) 2024, CEA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9 *
10 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 *
14 *****************************************************************************/
15
16#include <Modele_turbulence_scal_base.h>
17#include <Echange_externe_impose.h>
18#include <Op_Diff_DG_Elem.h>
19#include <Dirichlet_homogene.h>
20#include <Domaine_Cl_DG.h>
21#include <Champ_Elem_DG.h>
22#include <Schema_Temps_base.h>
23#include <Champ_front_calc.h>
24#include <Domaine_DG.h>
25#include <communications.h>
26#include <Synonyme_info.h>
27#include <Probleme_base.h>
28#include <Neumann_paroi.h>
29#include <Matrix_tools.h>
30#include <Array_tools.h>
31#include <TRUSTLists.h>
32#include <Dirichlet.h>
33#include <cmath>
34#include <Quadrature_base.h>
35#include <Champ_front_txyz.h>
36#include <Champ_front_softanalytique.h>
37#include <BasisFunction.h>
38
39Implemente_instanciable(Op_Diff_DG_Elem, "Op_Diff_DG_Elem", Op_Diff_DG_base);
40
42
44
45
46/**
47 * @brief Finalizes operator setup after all associations have been made.
48 *
49 * @details Calls the parent completer(), then:
50 * - Casts the unknown field to Champ_Elem_DG and checks that the ghost cell layer
51 * is at least 1 element thick (required for face-neighbour communication in DG).
52 * - Initializes the face boundary flux array flux_bords_ with the correct number
53 * of components (2 for Transport_K_Epsilon, otherwise the line size of the unknown).
54 * - If the operator is a turbulent diffusion operator (name starts with "Op_Dift"),
55 * retrieves the turbulent conductivity from the turbulence model and registers it
56 * as the turbulent diffusivity via associer_diffusivite_turbulente().
57 */
59{
61 const Champ_Elem_DG& ch = ref_cast(Champ_Elem_DG, equation().inconnue());
62 const Domaine_DG& domaine = le_dom_dg_.valeur();
63 if (domaine.domaine().nb_joints() && domaine.domaine().joint(0).epaisseur() < 1)
64 Cerr << "Op_Diff_DG_Elem : ghost cell layer width too small, minimum 1" << finl, Process::exit();
65 ch.fcl();
66 int nb_comp = (equation().que_suis_je() == "Transport_K_Epsilon") ? 2 : ch.valeurs().line_size();
67 flux_bords_.resize(domaine.premiere_face_int(), nb_comp);
68
69 if (!que_suis_je().debute_par("Op_Dift"))
70 return;
71
72 const RefObjU& modele_turbulence = equation().get_modele(TURBULENCE);
73 const Modele_turbulence_scal_base& mod_turb = ref_cast(Modele_turbulence_scal_base, modele_turbulence.valeur());
74 const Champ_Fonc_base& lambda_t = mod_turb.conductivite_turbulente();
76}
77
78/**
79 * @brief Builds the sparsity pattern of the DG diffusion matrix in a Matrice_Morse.
80 *
81 * @details The matrix couples each element to all its face-neighbours as given by the
82 * pre-computed sorted stencil (Domaine_DG::get_stencil_sorted()). The global index
83 * space is built from the BasisFunction index map indices_glob_elem, so that each
84 * element contributes a block of nb_basis_func * dim rows and the column range of a
85 * row spans nb_basis_func columns per neighbour element (including itself).
86 *
87 * The method fills tab1 (row pointers) in a first pass, then tab2 (column indices)
88 * in a second pass, and finally marks the stencil as sorted.
89 *
90 * @param la_matrice The Matrice_Morse whose sparsity pattern is to be set.
91 */
92void Op_Diff_DG_Elem::dimensionner(Matrice_Morse& la_matrice) const // TODO a remonter dans Op_DG_Elem
93{
94
95 const Nom& nom_inco = equation().inconnue().le_nom();
96 int nordre = Option_DG::Get_order_for(nom_inco);
97 int dim = equation().inconnue().is_vectorial() ? Objet_U::dimension : 1;
98
99 const Domaine_DG& domaine = le_dom_dg_.valeur();
100
101 const BasisFunction& bfunc = domaine.get_basisFunction(nordre);
102 const int nb_basis_func = bfunc.nb_bfunc();
103
104 const IntTab& indices_glob_elem =bfunc.indices_glob_elem(dim);
105
106 int nb_elem_tot = le_dom_dg_->nb_elem_tot();
107 int size_inc = indices_glob_elem(nb_elem_tot);
108
109 const Stencil& stencil_sorted = domaine.get_stencil_sorted();
110 const int nb_stencil_max = stencil_sorted.dimension(1);
111
112 la_matrice.dimensionner(size_inc, size_inc, 0);
113
114 auto& tab1 = la_matrice.get_set_tab1();
115 auto& tab2 = la_matrice.get_set_tab2();
116 auto& coeff = la_matrice.get_set_coeff();
117 coeff = 0;
118
119 int col, indice;
120
121 tab1(0) = 1;
122 for (int nelem = 0; nelem < nb_elem_tot; nelem++)
123 {
124 int nb_indices_line = 0;
125 for (int k = 0 ; k < nb_stencil_max; k++)
126 {
127 if (stencil_sorted(nelem, k) < 0)
128 break;
129 nb_indices_line += nb_basis_func;
130 }
131 for (int k = 0; k < nb_basis_func * dim; k++)
132 tab1(indices_glob_elem(nelem) + k + 1) = nb_indices_line + tab1(indices_glob_elem(nelem) + k);
133 }
134
135 la_matrice.dimensionner(size_inc, tab1(size_inc) - 1);
136
137 for (int nelem = 0; nelem < nb_elem_tot; nelem++)
138 {
139 auto row = tab1[indices_glob_elem(nelem)]-1 ;
140 auto nb_indices_line = tab1[indices_glob_elem(nelem)+1] - tab1[indices_glob_elem(nelem)];
141 indice = 0;
142 for (int d = 0; d < dim; d++)
143 {
144 for (int i = 0; i < nb_basis_func; i++)
145 {
146 for (int k = 0; k < nb_stencil_max; k++)
147 {
148 if (stencil_sorted(nelem, k) < 0)
149 break;
150 col = indices_glob_elem(stencil_sorted(nelem, k)) + 1;
151 for (int j = 0 ; j < nb_basis_func; j++)
152 tab2[row + indice + j + k*nb_basis_func] = col + j + d*nb_basis_func;
153 }
154 indice += nb_indices_line;
155 }
156 }
157 }
158 la_matrice.is_sorted_stencil();
159 assert(la_matrice.is_sorted_stencil());
160}
161
162/**
163 * @brief Sizes the block matrices used by the interface_blocs assembly mechanism.
164 *
165 * @details If the unknown is treated semi-implicitly (its name appears in semi_impl),
166 * no matrix needs to be dimensioned and the method returns immediately.
167 * Otherwise, for each external operator registered in op_ext (used for monolithic
168 * thermal coupling), the corresponding sub-matrix is sized by calling dimensionner().
169 * Cross-problem coupling (i > 0) is not yet implemented and throws at runtime.
170 *
171 * @param matrices Map of matrix name → Matrice_Morse pointer to be sized.
172 * @param semi_impl Map of semi-implicit field names to their current values.
173 */
174void Op_Diff_DG_Elem::dimensionner_blocs(matrices_t matrices, const tabs_t& semi_impl) const
175{
176 const std::string nom_inco = equation().inconnue().le_nom().getString();
177 if (semi_impl.count(nom_inco))
178 return; // semi-implicite -> rien a dimensionner
179
180 init_op_ext(); // TODO DG a completer
181 int n_ext = (int)op_ext.size(); // pour la thermique monolithique
182
183 std::vector<Matrice_Morse *> mat(n_ext);
184 for (int i = 0; i < n_ext; i++)
185 {
186 std::string nom_mat = i ? nom_inco + "/" + op_ext[i]->equation().probleme().le_nom().getString() : nom_inco;
187 mat[i] = matrices.count(nom_mat) ? matrices.at(nom_mat) : nullptr;
188 if (!mat[i])
189 continue;
190 Matrice_Morse mat2;
191 if (i == 0)
192 dimensionner(mat2);
193 else
194 throw; // TODO DG for dimensionner_terme_croises
195
196 mat[i]->nb_colonnes() ? *mat[i] += mat2 : *mat[i] = mat2;
197 }
198}
199
200/**
201 * @brief Assembles the SIP diffusion operator into the matrix and right-hand side.
202 *
203 * @details This is the core assembly routine. It proceeds in three stages:
204 *
205 * **1. Volume integrals (stiffness term)**
206 * For each element, the term integral of nu * grad(phi_i) . grad(phi_j) is integrated
207 * using the element quadrature rule and accumulated into the diagonal block of the
208 * matrix and into secmem.
209 *
210 * **2. Internal face integrals (SIP terms)**
211 * For each internal face shared by elem0 and elem1:
212 * - *Penalty term*: gamma * (eta_F/h_T) * integral of phi_i * phi_j, added to the
213 * diagonal blocks (same-element test and trial) and subtracted from the off-diagonal
214 * blocks (cross-element), enforcing the jump penalization symmetrically.
215 * - *Consistency + symmetry terms*: 0.5 * integral of { nu * grad(phi_i) } . n * phi_j,
216 * assembled into all four (elem0/elem1) x (elem0/elem1) block combinations with the
217 * appropriate sign to yield a symmetric bilinear form.
218 *
219 * **3. Boundary face integrals (Dirichlet enforcement)**
220 * For boundary faces flagged as Dirichlet (fcl flag > 5), the same penalty and
221 * consistency terms are applied to the single adjacent element, imposing the boundary
222 * condition weakly in the SIP sense. The Dirichlet value contribution to secmem is
223 * handled separately by contribuer_au_second_membre().
224 *
225 * Both isotropic and anisotropic diffusivities are supported: for anisotropic cases,
226 * nu_F is computed as the normal projection of the diffusivity tensor onto the face.
227 *
228 * The DOF ordering within an element is: phi0.ex, phi1.ex, ..., phi0.ey, phi1.ey, ...
229 * for vector fields (nb_bfunc DOFs per spatial direction).
230 *
231 * @param matrices Map of matrix name → Matrice_Morse pointer to accumulate into.
232 * @param secmem Right-hand side array to accumulate into.
233 * @param semi_impl Map of semi-implicit field values (matrix assembly is skipped if the
234 * unknown is present here).
235 */
236void Op_Diff_DG_Elem::ajouter_blocs(matrices_t matrices, DoubleTab& secmem, const tabs_t& semi_impl) const
237{
238
239 const Nom& nom_inco = equation().inconnue().le_nom();
240 const std::string& nom_inco_str = equation().inconnue().le_nom().getString();
241 const DoubleTab& inco = semi_impl.count(nom_inco_str) ? semi_impl.at(nom_inco_str) : equation().inconnue().valeurs();
242 Matrice_Morse *mat = matrices.count(nom_inco_str) ? matrices.at(nom_inco_str) : nullptr;
243
244 update_nu();
245
246 const Domaine_DG& domaine = le_dom_dg_.valeur();
247 const IntTab& face_voisins = domaine.face_voisins();
248
249 const Champ_Elem_DG& ch = ref_cast(Champ_Elem_DG, equation().inconnue());
250 const int dim = ch.is_vectorial() ? Objet_U::dimension : 1;
251
252 int order = Option_DG::Get_order_for(nom_inco);
253
254 const BasisFunction& bfunc = le_dom_dg_->get_basisFunction(order);
255 const int nb_bfunc = bfunc.nb_bfunc();
256 const IntTab& indices_glob_elem = bfunc.indices_glob_elem(dim);
257
258 const DoubleTab& eta_F = bfunc.get_eta_facet(); // Compute the penalisation coefficient
259
260 const int quad_order = bfunc.get_default_quadrature_order();
261 const Quadrature_base& quad = domaine.get_quadrature(quad_order);
262 int nb_pts_integ_max = quad.nb_pts_integ_max();
263 double coeff;
264
265 DoubleTab grad_fbase_elem(nb_bfunc, nb_pts_integ_max, Objet_U::dimension);
266 DoubleTab diffusion(nb_pts_integ_max);
267
268 for (int e = 0; e < le_dom_dg_->nb_elem(); e++)
269 {
270 bfunc.eval_grad_bfunc(quad, e, grad_fbase_elem);
271 int ind_elem = indices_glob_elem(e);
272 for (int i = 0; i < nb_bfunc; i++)
273 for (int j = 0; j < nb_bfunc; j++)
274 {
275 diffusion = 0.;
276 for (int k = 0; k < quad.nb_pts_integ(e); k++)
277 for (int d = 0; d < Objet_U::dimension; d++)
278 {
279 bool ori = is_aniso_ ? d : 0;
280 diffusion(k) += nu(e, ori) * grad_fbase_elem(i, k, d) * grad_fbase_elem(j, k, d);
281 }
282
283 coeff = quad.compute_integral_on_elem(e, diffusion);
284 for (int d_base = 0; d_base < dim; d_base++)
285 {
286 if (mat)
287 (*mat)(ind_elem + i + d_base * nb_bfunc, ind_elem + j + d_base * nb_bfunc) += coeff;
288 secmem(e, i + d_base * nb_bfunc) -= coeff * inco(e, j + d_base * nb_bfunc);
289 }
290 }
291 }
292
293 int nb_pts_int_fac = quad.nb_pts_integ_facets();
294 int premiere_face_int = domaine.premiere_face_int();
295 const DoubleTab& face_normales = domaine.face_normales();
296
297 int elem0, elem1;
298
299 DoubleTab product(nb_pts_int_fac);
300 DoubleTab scalar_product(nb_pts_int_fac);
301
302 DoubleTab fbase0(nb_bfunc, nb_pts_int_fac);
303 DoubleTab fbase1(nb_bfunc, nb_pts_int_fac);
304
305 DoubleTab grad_fbase0(nb_bfunc, nb_pts_int_fac, Objet_U::dimension);
306 DoubleTab grad_fbase1(nb_bfunc, nb_pts_int_fac, Objet_U::dimension);
307
308 for (int f = premiere_face_int; f < domaine.nb_faces(); f++)
309 {
310
311 elem0 = face_voisins(f, 0);
312 elem1 = face_voisins(f, 1);
313
314 int ind_elem0 = indices_glob_elem(elem0);
315 int ind_elem1 = indices_glob_elem(elem1);
316
317 double sur_f = domaine.face_surfaces(f);
318
319 double h_T = sqrt(std::min(domaine.carre_pas_maille(elem0), domaine.carre_pas_maille(elem1))); // TODO possibilite de prendre moyenne harmonique (stabilite)
320 double invh_T = 1. / h_T;
321
322 double nu_0 = 0., nu_1 = 0.;
323 if (is_aniso_)
324 {
325 for (int d = 0; d < Objet_U::dimension; d++)
326 {
327 nu_0 += nu(elem0, d) * face_normales(f, d) * face_normales(f, d);
328 nu_1 += nu(elem1, d) * face_normales(f, d) * face_normales(f, d);
329 }
330 nu_0 /= sur_f * sur_f;
331 nu_1 /= sur_f * sur_f;
332 }
333 else
334 {
335 nu_0 = nu(elem0, 0);
336 nu_1 = nu(elem1, 0);
337 }
338 double gamma = 2 * nu_0 * nu_1 / (nu_0 + nu_1);
339
340 //*****************//
341 // penalizing term //
342 //*****************//
343 for (int i_elem = 0; i_elem < 2; i_elem++)
344 {
345 int elem = face_voisins(f, i_elem);
346 int ind_elem = indices_glob_elem(elem);
347
348 bfunc.eval_bfunc_on_facets(quad, elem, f, fbase0);
349
350 for (int i = 0; i < nb_bfunc; i++)
351 for (int j = 0; j < nb_bfunc; j++)
352 {
353 for (int k = 0; k < nb_pts_int_fac; k++)
354 product(k) = fbase0(i, k) * fbase0(j, k); // TODO DG kronecker ?
355
356 coeff = gamma * eta_F(f) * invh_T * quad.compute_integral_on_facet(f, product);
357 for (int d_base = 0; d_base < dim; d_base++)
358 {
359 if (mat)
360 (*mat)(ind_elem + i + d_base * nb_bfunc, ind_elem + j + d_base * nb_bfunc) += coeff;
361 secmem(elem, i + d_base * nb_bfunc) -= coeff * inco(elem, j + d_base * nb_bfunc);
362 }
363 }
364 }
365
366 // crossed_term
367 bfunc.eval_bfunc_on_facets(quad, elem0, f, fbase0);
368 bfunc.eval_bfunc_on_facets(quad, elem1, f, fbase1);
369
370 for (int i = 0; i < nb_bfunc; i++)
371 for (int j = 0; j < nb_bfunc; j++)
372 {
373 for (int k = 0; k < nb_pts_int_fac; k++)
374 product(k) = fbase0(i, k) * fbase1(j, k);
375
376 double integral = quad.compute_integral_on_facet(f, product);
377 coeff = gamma * eta_F(f) * invh_T * integral;
378 for (int d_base = 0; d_base < dim; d_base++)
379 {
380 if (mat)
381 {
382 (*mat)(ind_elem0 + i + d_base * nb_bfunc, ind_elem1 + j + d_base * nb_bfunc) -= coeff;
383 (*mat)(ind_elem1 + j + d_base * nb_bfunc, ind_elem0 + i + d_base * nb_bfunc) -= coeff; // symmetry
384 }
385 secmem(elem0, i + d_base * nb_bfunc) += coeff * inco(elem1, j + d_base * nb_bfunc);
386 secmem(elem1, j + d_base * nb_bfunc) += coeff * inco(elem0, i + d_base * nb_bfunc); // symmetry
387 }
388 }
389
390 //****************//
391 // symmetric term //
392 //****************//
393 bfunc.eval_grad_bfunc_on_facets(quad, elem0, f, grad_fbase0);
394 bfunc.eval_grad_bfunc_on_facets(quad, elem1, f, grad_fbase1);
395
396 for (int i = 0; i < nb_bfunc; i++)
397 {
398 scalar_product = 0.;
399 for (int k = 0; k < nb_pts_int_fac; k++)
400 for (int d = 0; d < Objet_U::dimension; d++)
401 {
402 bool ori = is_aniso_ ? d : 0;
403 scalar_product(k) += nu(elem0, ori) * face_normales(f, d) / sur_f * grad_fbase0(i, k, d);
404 }
405
406 for (int j = 0; j < nb_bfunc; j++)
407 {
408 for (int k = 0; k < nb_pts_int_fac; k++)
409 product(k) = scalar_product(k) * fbase0(j, k);
410 double integral = quad.compute_integral_on_facet(f, product);
411 for (int d_base = 0; d_base < dim; d_base++)
412 {
413 if (mat)
414 {
415 (*mat)(ind_elem0 + i + d_base * nb_bfunc, ind_elem0 + j + d_base * nb_bfunc) -= 0.5 * integral;
416 (*mat)(ind_elem0 + j + d_base * nb_bfunc, ind_elem0 + i + d_base * nb_bfunc) -= 0.5 * integral; // symmetry
417 }
418 secmem(elem0, i + d_base * nb_bfunc) += 0.5 * integral * inco(elem0, j + d_base * nb_bfunc);
419 secmem(elem0, j + d_base * nb_bfunc) += 0.5 * integral * inco(elem0, i + d_base * nb_bfunc); // symmetry
420 }
421 }
422
423 for (int j = 0; j < nb_bfunc; j++)
424 {
425 for (int k = 0; k < nb_pts_int_fac; k++)
426 product(k) = scalar_product(k) * fbase1(j, k);
427 double integral = quad.compute_integral_on_facet(f, product);
428 for (int d_base = 0; d_base < dim; d_base++)
429 {
430 if (mat)
431 {
432 (*mat)(ind_elem0 + i + d_base * nb_bfunc, ind_elem1 + j + d_base * nb_bfunc) += 0.5 * integral;
433 (*mat)(ind_elem1 + j + d_base * nb_bfunc, ind_elem0 + i + d_base * nb_bfunc) += 0.5 * integral; // symmetry
434 }
435 secmem(elem0, i + d_base * nb_bfunc) -= 0.5 * integral * inco(elem1, j + d_base * nb_bfunc);
436 secmem(elem1, j + d_base * nb_bfunc) -= 0.5 * integral * inco(elem0, i + d_base * nb_bfunc); // symmetry
437 }
438 }
439
440 scalar_product = 0.;
441 for (int k = 0; k < nb_pts_int_fac; k++)
442 for (int d = 0; d < Objet_U::dimension; d++)
443 {
444 bool ori = is_aniso_ ? d : 0;
445 scalar_product(k) += nu(elem1, ori) * face_normales(f, d) / sur_f * grad_fbase1(i, k, d);
446 }
447
448 for (int j = 0; j < nb_bfunc; j++)
449 {
450 for (int k = 0; k < nb_pts_int_fac; k++)
451 product(k) = scalar_product(k) * fbase1(j, k);
452 double integral = quad.compute_integral_on_facet(f, product);
453 for (int d_base = 0; d_base < dim; d_base++)
454 {
455 if (mat)
456 {
457 (*mat)(ind_elem1 + i + d_base * nb_bfunc, ind_elem1 + j + d_base * nb_bfunc) += 0.5 * integral;
458 (*mat)(ind_elem1 + j + d_base * nb_bfunc, ind_elem1 + i + d_base * nb_bfunc) += 0.5 * integral; // symmetry
459 }
460 secmem(elem1, i + d_base * nb_bfunc) -= 0.5 * integral * inco(elem1, j + d_base * nb_bfunc);
461 secmem(elem1, j + d_base * nb_bfunc) -= 0.5 * integral * inco(elem1, i + d_base * nb_bfunc); // symmetry
462 }
463 }
464
465 for (int j = 0; j < nb_bfunc; j++)
466 {
467 for (int k = 0; k < nb_pts_int_fac; k++)
468 product(k) = scalar_product(k) * fbase0(j, k);
469 double integral = quad.compute_integral_on_facet(f, product);
470 for (int d_base = 0; d_base < dim; d_base++)
471 {
472 if (mat)
473 {
474 (*mat)(ind_elem1 + i + d_base * nb_bfunc, ind_elem0 + j + d_base * nb_bfunc) -= 0.5 * integral;
475 (*mat)(ind_elem0 + j + d_base * nb_bfunc, ind_elem1 + i + d_base * nb_bfunc) -= 0.5 * integral; // symmetry
476 }
477 secmem(elem1, i + d_base * nb_bfunc) += 0.5 * integral * inco(elem0, j + d_base * nb_bfunc);
478 secmem(elem0, j + d_base * nb_bfunc) += 0.5 * integral * inco(elem1, i + d_base * nb_bfunc); // symmetry
479 }
480 }
481 }
482 }
483
484 /* Treatment of the boundary conditions */
485
486 for (int f = 0; f < premiere_face_int; f++) // For the boundary
487 {
488
489 if (ch.fcl()(f, 0) > 5)
490 {
491 int elem = face_voisins(f, 0); // The cell that have one facet on the boundary
492 int ind_elem = indices_glob_elem(elem);
493
494 bfunc.eval_bfunc_on_facets(quad, elem, f, fbase0);
495 bfunc.eval_grad_bfunc_on_facets(quad, elem, f, grad_fbase0);
496
497 double h_T = sqrt(domaine.carre_pas_maille(elem));
498 double invh_T = 1. / h_T; // TODO regarder penalisation remplacer h_T par h_F
499 double sur_f = domaine.face_surfaces(f);
500 double nu_F = 0.;
501 if (is_aniso_)
502 {
503 for (int d = 0; d < Objet_U::dimension; d++)
504 nu_F += nu(elem, d) * face_normales(f, d) * face_normales(f, d);
505 nu_F /= sur_f * sur_f;
506 }
507 else
508 nu_F = nu(elem, 0);
509
510 for (int i = 0; i < nb_bfunc; i++)
511 {
512 scalar_product = 0.;
513 for (int k = 0; k < nb_pts_int_fac; k++)
514 for (int d = 0; d < Objet_U::dimension; d++)
515 {
516 bool ori = is_aniso_ ? d : 0;
517 scalar_product(k) += nu(elem, ori) * face_normales(f, d) / sur_f * grad_fbase0(i, k, d);
518 }
519
520 for (int j = 0; j < nb_bfunc; j++)
521 {
522 for (int k = 0; k < nb_pts_int_fac; k++)
523 product(k) = fbase0(i, k) * fbase0(j, k); // TODO DG kronecker ?
524
525 coeff = nu_F * eta_F(f) * invh_T * quad.compute_integral_on_facet(f, product);
526 for (int d_base = 0; d_base < dim; d_base++)
527 {
528 if (mat)
529 (*mat)(ind_elem + i + d_base * nb_bfunc, ind_elem + j + d_base * nb_bfunc) += coeff;
530 secmem(elem, i + d_base * nb_bfunc) -= coeff * inco(elem, j + d_base * nb_bfunc);
531 }
532 for (int k = 0; k < nb_pts_int_fac; k++)
533 product(k) = scalar_product(k) * fbase0(j, k);
534
535 double integral = quad.compute_integral_on_facet(f, product);
536 for (int d_base = 0; d_base < dim; d_base++)
537 {
538 if (mat)
539 {
540 (*mat)(ind_elem + i + d_base * nb_bfunc, ind_elem + j + d_base * nb_bfunc) -= integral;
541 (*mat)(ind_elem + j + d_base * nb_bfunc, ind_elem + i + d_base * nb_bfunc) -= integral;
542 }
543 secmem(elem, i + d_base * nb_bfunc) += integral * inco(elem, j + d_base * nb_bfunc);
544 secmem(elem, j + d_base * nb_bfunc) += integral * inco(elem, i + d_base * nb_bfunc);
545 }
546 }
547 }
548 }
549 }
550 contribuer_au_second_membre(secmem); // TODO DG a integrer proprement dans la boucle
551}
552
553void Op_Diff_DG_Elem::dimensionner_termes_croises(Matrice_Morse& matrice, const Probleme_base& autre_pb, int nl, int nc) const
554{
555 // TODO pour problemes croises
556 throw;
557}
558
559void Op_Diff_DG_Elem::ajouter_termes_croises(const DoubleTab& inco, const Probleme_base& autre_pb, const DoubleTab& autre_inco, DoubleTab& resu) const
560{
561 throw;
562 // TODO idem above
563}
564
565void Op_Diff_DG_Elem::contribuer_termes_croises(const DoubleTab& inco, const Probleme_base& autre_pb, const DoubleTab& autre_inco, Matrice_Morse& matrice) const
566{
567 // TODO idem above
568 throw;
569}
570
571/**
572 * @brief Adds boundary condition contributions to the right-hand side.
573 *
574 * @details Loops over all boundary faces and, depending on the boundary condition type,
575 * adds the appropriate weak enforcement term to resu:
576 *
577 * - **Neumann / Neumann_paroi**: the imposed flux g_N is integrated against each basis
578 * function on the boundary face:
579 * resu(elem, i) += integral of phi_i * g_N
580 * Point-wise flux values are retrieved either from a Champ_front_var_instationnaire
581 * (using valeur_au_temps_et_au_point) or directly from Neumann::flux_impose().
582 *
583 * - **Dirichlet** (weak SIP enforcement): the boundary value g_D enters two terms:
584 * - Symmetry: -integral of { nu * grad(phi_i) } . n * g_D
585 * - Penalty: (nu_F * eta_F / h_T) * integral of phi_i * g_D
586 * The Dirichlet value is retrieved either point-wise (Champ_front_var_instationnaire)
587 * or face-wise (Dirichlet::val_imp_au_temps()).
588 *
589 * - **Dirichlet_homogene**: no contribution (homogeneous condition, nothing to add).
590 *
591 * Both isotropic and anisotropic diffusivities are handled for the Dirichlet terms.
592 * The use of Champ_front_softanalytique is explicitly forbidden and triggers an error.
593 *
594 * @param resu The right-hand side array to accumulate boundary contributions into.
595 */
597{
598
599 const Domaine_DG& domaine = le_dom_dg_.valeur();
600
601 int nb_bords = domaine.nb_front_Cl();
602
603 const IntTab& face_voisins = domaine.face_voisins();
604 const DoubleTab& face_normales = domaine.face_normales();
605
606 const Nom& nom_inco = equation().inconnue().le_nom();
607 int order = Option_DG::Get_order_for(nom_inco);
608 int dim = equation().inconnue().is_vectorial() ? Objet_U::dimension : 1;
609
610 const BasisFunction& bfunc = le_dom_dg_->get_basisFunction(order);
611 const int nb_bfunc = bfunc.nb_bfunc();
612
613 assert(dim*nb_bfunc == equation().inconnue().valeurs().line_size());
614
615 const int quad_order = bfunc.get_default_quadrature_order();
616 const Quadrature_base& quad = domaine.get_quadrature(quad_order);
617 const DoubleTab& integ_points_facets = quad.get_integ_points_facets();
618 int nb_pts_int_fac = integ_points_facets.dimension(1);
619
620 DoubleTab fbase(nb_bfunc, nb_pts_int_fac);
621 DoubleTab grad_fbase(nb_bfunc, nb_pts_int_fac, Objet_U::dimension);
622 DoubleTab scalar_product_dim(dim, nb_pts_int_fac); // DoubleTab used for storing scalar products of the RHS and the basis functions in x, y, (z)
623 DoubleTab scalar_product(nb_pts_int_fac); // DoubleTab used for reftab scalar_product_dim for a given dimension
624 // Les conditions aux limites pour le second membre
625 const DoubleTab& eta_F = bfunc.get_eta_facet(); // Compute the penalisation coefficient
626 int ind_face;
627 for (int n_bord = 0; n_bord < nb_bords; n_bord++)
628 {
629 const Cond_lim& la_cl = la_zcl_dg_->les_conditions_limites(n_bord);
630 const Front_VF& le_bord = ref_cast(Front_VF, la_cl->frontiere_dis());
631 int num1f = 0;
632 int num2f = le_bord.nb_faces();
633 double xk = 0., yk = 0., zk = 0.;
634 double temps = equation().schema_temps().temps_courant();
635 double sur_f = 0.;
636
637 if (sub_type(Champ_front_softanalytique, la_cl.valeur().champ_front()))
638 {
639 Cerr << "You have to use a Champ_front_fonc_txyz and not " << la_cl.valeur().champ_front().que_suis_je() << finl;
640 exit();
641 }
642
643 bool avec_valeur_aux_points = false;
644 if (sub_type(Champ_front_var_instationnaire, la_cl.valeur().champ_front()))
645 {
646 const Champ_front_var_instationnaire& ch_txyz = ref_cast(Champ_front_var_instationnaire, la_cl.valeur().champ_front());
647 avec_valeur_aux_points = ch_txyz.valeur_au_temps_et_au_point_disponible();
648 }
649
650 if (sub_type(Neumann, la_cl.valeur()))
651 {
652 if (avec_valeur_aux_points)
653 {
654 if (sub_type(Champ_front_var_instationnaire, la_cl.valeur().champ_front()))
655 {
656 const Champ_front_var_instationnaire& champ_front =
657 ref_cast(Champ_front_var_instationnaire, la_cl.valeur().champ_front());
658
659 for (int ind_faceb = num1f; ind_faceb < num2f; ind_faceb++)
660 {
661 ind_face = le_bord.num_face(ind_faceb);
662 int elem = face_voisins(ind_face, 0); // The cell that have one facet on the boundary
663
664 bfunc.eval_bfunc_on_facets(quad, elem, ind_face, fbase);
665
666 for (int i = 0; i < nb_bfunc; i++)
667 {
668 scalar_product_dim = 0.;
669 for (int k = 0; k < nb_pts_int_fac; k++)
670 {
671
672 // Coordonnees des points d'integration
673 xk = integ_points_facets(ind_face, k, 0);
674 yk = integ_points_facets(ind_face, k, 1);
675 if (dimension == 3)
676 zk = integ_points_facets(ind_face, k, 2);
677 for (int d_base = 0; d_base < dim; d_base++)
678 {
679 double flux_impose_k = champ_front.valeur_au_temps_et_au_point(temps, 0, xk, yk, zk, d_base);
680 scalar_product_dim(d_base, k) = fbase(i, k) * flux_impose_k;
681 }
682 }
683 for (int d_base = 0; d_base < dim; d_base++)
684 {
685 scalar_product.ref_array(scalar_product_dim, d_base*nb_pts_int_fac, nb_pts_int_fac);
686 resu(elem, i + d_base * nb_bfunc) += quad.compute_integral_on_facet(ind_face, scalar_product);
687 }
688 }
689 }
690 }
691 }
692 else
693 {
694 const Neumann& neumann = ref_cast(Neumann, la_cl.valeur());
695
696 for (int ind_faceb = num1f; ind_faceb < num2f; ind_faceb++)
697 {
698 ind_face = le_bord.num_face(ind_faceb);
699 int elem = face_voisins(ind_face, 0); // The cell that have one facet on the boundary
700
701 bfunc.eval_bfunc_on_facets(quad, elem, ind_face, fbase);
702
703 for (int i = 0; i < nb_bfunc; i++)
704 {
705 scalar_product_dim = 0.;
706 for (int k = 0; k < nb_pts_int_fac; k++)
707 {
708 for (int d_base = 0; d_base < dim; d_base++)
709 {
710 double flux_impose_k = neumann.flux_impose(ind_faceb, d_base);
711 scalar_product_dim(d_base, k) = fbase(i, k) * flux_impose_k;
712 }
713 }
714 for (int d_base = 0; d_base < dim; d_base++)
715 {
716 scalar_product.ref_array(scalar_product_dim, d_base*nb_pts_int_fac, nb_pts_int_fac);
717 resu(elem, i + d_base * nb_bfunc) += quad.compute_integral_on_facet(ind_face, scalar_product);
718 }
719 }
720 }
721 }
722 }
723 else if (sub_type(Dirichlet_homogene, la_cl.valeur()))
724 {
725 // On ne fait rien et c'est normal
726 }
727 else if (sub_type(Dirichlet, la_cl.valeur()))
728 {
729
730 if (avec_valeur_aux_points)
731 {
732 if (sub_type(Champ_front_var_instationnaire, la_cl.valeur().champ_front()))
733 {
734 const Champ_front_var_instationnaire& champ_front =
735 ref_cast(Champ_front_var_instationnaire, la_cl.valeur().champ_front());
736
737 for (int ind_faceb = num1f; ind_faceb < num2f; ind_faceb++)
738 {
739
740 ind_face = le_bord.num_face(ind_faceb);
741
742 int elem = face_voisins(ind_face, 0); // The cell that have one facet on the boundary
743
744 bfunc.eval_bfunc_on_facets(quad, elem, ind_face, fbase);
745 bfunc.eval_grad_bfunc_on_facets(quad, elem, ind_face, grad_fbase);
746
747 sur_f = domaine.face_surfaces(ind_face);
748
749 double h_T = sqrt(domaine.carre_pas_maille(elem));
750 double invh_T = 1. / h_T; // TODO regarder penalisation remplacer h_T par h_F
751 double nu_F = 0.;
752 if (is_aniso_)
753 {
754 for (int d = 0; d < Objet_U::dimension; d++)
755 nu_F += nu(elem, d) * face_normales(ind_face, d) * face_normales(ind_face, d);
756 nu_F /= sur_f * sur_f;
757 }
758 else
759 nu_F = nu(elem, 0);
760
761 for (int i = 0; i < nb_bfunc; i++)
762 {
763 double u_bord_k = 0.;
764 scalar_product_dim = 0.;
765 for (int k = 0; k < nb_pts_int_fac; k++)
766 {
767 // Coordonnees des points d'integration
768 xk = integ_points_facets(ind_face, k, 0);
769 yk = integ_points_facets(ind_face, k, 1);
770 if (dimension == 3)
771 zk = integ_points_facets(ind_face, k, 2);
772
773 for (int d_base = 0; d_base < dim; d_base++)
774 {
775 u_bord_k = champ_front.valeur_au_temps_et_au_point(temps, 0, xk, yk, zk, d_base);
776 for (int d = 0; d < Objet_U::dimension; d++)
777 {
778 bool ori = is_aniso_ ? d : 0;
779 scalar_product_dim(d_base, k) -= nu(elem, ori) * face_normales(ind_face, d) / sur_f * grad_fbase(i, k, d) * u_bord_k;
780 }
781 scalar_product_dim(d_base, k) += nu_F * eta_F(ind_face) * invh_T * u_bord_k * fbase(i, k); // \eta/H_F \int g \vvec_h
782 }
783 }
784 for (int d_base = 0; d_base < dim; d_base++)
785 {
786 scalar_product.ref_array(scalar_product_dim, d_base*nb_pts_int_fac, nb_pts_int_fac);
787 resu(elem, i + d_base * nb_bfunc) += quad.compute_integral_on_facet(ind_face, scalar_product);
788 }
789 }
790 }
791 }
792 }
793 else
794 {
795 const Dirichlet& dirichlet = ref_cast(Dirichlet, la_cl.valeur());
796
797 for (int ind_faceb = num1f; ind_faceb < num2f; ind_faceb++)
798 {
799
800 ind_face = le_bord.num_face(ind_faceb);
801
802 int elem = face_voisins(ind_face, 0); // The cell that have one facet on the boundary
803
804 bfunc.eval_bfunc_on_facets(quad, elem, ind_face, fbase);
805 bfunc.eval_grad_bfunc_on_facets(quad, elem, ind_face, grad_fbase);
806
807 sur_f = domaine.face_surfaces(ind_face);
808
809 double h_T = sqrt(domaine.carre_pas_maille(elem));
810 double invh_T = 1. / h_T; // TODO regarder penalisation remplacer h_T par h_F
811 double nu_F = 0.;
812 if (is_aniso_)
813 {
814 for (int d = 0; d < Objet_U::dimension; d++)
815 nu_F += nu(elem, d) * face_normales(ind_face, d) * face_normales(ind_face, d);
816 nu_F /= sur_f * sur_f;
817 }
818 else
819 nu_F = nu(elem, 0);
820
821 for (int i = 0; i < nb_bfunc; i++)
822 {
823 scalar_product_dim = 0.;
824 for (int d_base = 0; d_base < dim; d_base++)
825 {
826 double u_bord = dirichlet.val_imp_au_temps(temps, ind_faceb, d_base);
827 for (int k = 0; k < nb_pts_int_fac; k++)
828 {
829 for (int d = 0; d < Objet_U::dimension; d++)
830 {
831 bool ori = is_aniso_ ? d : 0;
832 scalar_product_dim(d_base, k) -= nu(elem, ori) * face_normales(ind_face, d) / sur_f * grad_fbase(i, k, d) * u_bord;
833 }
834 scalar_product_dim(d_base, k) += nu_F * eta_F(ind_face) * invh_T * u_bord * fbase(i, k); // \eta/H_F \int g \vvec_h
835 }
836 scalar_product.ref_array(scalar_product_dim, d_base*nb_pts_int_fac, nb_pts_int_fac);
837 resu(elem, i + d_base * nb_bfunc) += quad.compute_integral_on_facet(ind_face, scalar_product);
838 }
839 }
840 }
841 }
842 }
843 else
844 {
845 }
846 }
847}
Manages the local polynomial basis functions for Discontinuous Galerkin elements.
void eval_grad_bfunc(const Quadrature_base &quad, const int &nelem, DoubleTab &fbasis) const
Evaluates the gradients of all basis functions at the element quadrature points.
void eval_grad_bfunc_on_facets(const Quadrature_base &quad, const int &nelem, const int &num_face, DoubleTab &grad_fbasis) const
Evaluates the gradients of all basis functions of element nelem at the quadrature points of face num_...
void eval_bfunc_on_facets(const Quadrature_base &quad, const int &nelem, const int &num_face, DoubleTab &grad_fbasis) const
Evaluates all basis functions of element nelem at the quadrature points of face num_face.
const IntTab & indices_glob_elem(const int dim=1) const
const DoubleTab & get_eta_facet() const
const int & nb_bfunc() const
const int & get_default_quadrature_order() const
classe Champ_Fonc_base Classe de base des champs qui sont fonction d'une grandeur calculee
const IntTab & fcl() const
DoubleTab & valeurs() override
Renvoie le tableau des valeurs du champ au temps courant.
classe Champ_front_softanalytique Classe derivee de Champ_front_var qui represente les
classe Champ_front_var_instationnaire Classe derivee de Champ_front_var qui represente les champs aux
virtual double valeur_au_temps_et_au_point(double temps, int som, double x, double y, double z, int comp) const
classe Cond_lim Classe generique servant a representer n'importe quelle classe
Definition Cond_lim.h:31
Classe Dirichlet_homogene Cette classe est la classe de base de la hierarchie des conditions aux limi...
classe Dirichlet Cette classe est la classe de base de la hierarchie des conditions aux limites de ty...
Definition Dirichlet.h:31
virtual double val_imp_au_temps(double temps, int i) const
Renvoie la valeur imposee sur la i-eme composante du champ a la frontiere au temps precise.
Definition Dirichlet.cpp:54
int face_voisins(int num_face, int i) const
renvoie l'element voisin de numface dans la direction i.
Definition Domaine_VF.h:418
int nb_front_Cl() const
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
virtual const RefObjU & get_modele(Type_modele type) const
virtual const Champ_Inc_base & inconnue() const =0
Schema_Temps_base & schema_temps()
Renvoie le schema en temps associe a l'equation.
const Nom & le_nom() const override
Renvoie le nom du champ.
bool is_vectorial() const
Definition Field_base.h:82
class Front_VF
Definition Front_VF.h:36
int nb_faces() const
Definition Front_VF.h:53
int num_face(const int) const
Definition Front_VF.h:68
Classe Matrice_Morse Represente une matrice M (creuse), non necessairement carree.
Classe Modele_turbulence_scal_base Cette classe represente un modele de turbulence pour une equation ...
const Champ_Fonc_base & conductivite_turbulente() const
const Equation_base & equation() const
Renvoie la reference sur l'equation pointe par MorEqn::mon_equation.
Definition MorEqn.h:62
Classe Neumann Cette classe est la classe de base de la hierarchie des conditions aux limites de type...
Definition Neumann.h:31
virtual double flux_impose(int i) const
Renvoie la valeur du flux impose sur la i-eme composante du champ representant le flux a la frontiere...
Definition Neumann.cpp:35
class Nom Une chaine de caractere pour nommer les objets de TRUST
Definition Nom.h:31
const std::string & getString() const
Definition Nom.h:92
static int dimension
Definition Objet_U.h:99
const Nom & que_suis_je() const
renvoie la chaine identifiant la classe.
Definition Objet_U.cpp:104
virtual Entree & readOn(Entree &)
Lecture d'un Objet_U sur un flot d'entree Methode a surcharger.
Definition Objet_U.cpp:293
virtual Sortie & printOn(Sortie &) const
Ecriture de l'objet sur un flot de sortie Methode a surcharger.
Definition Objet_U.cpp:282
Concrete DG diffusion operator acting on element-based unknowns.
void contribuer_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, Matrice_Morse &matrice) const override
void ajouter_blocs(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={}) const override
Assembles the SIP diffusion operator into the matrix and right-hand side.
void ajouter_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, DoubleTab &resu) const override
void completer() override
Finalizes operator setup after all associations have been made.
void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={}) const override
Sizes the block matrices used by the interface_blocs assembly mechanism.
void contribuer_au_second_membre(DoubleTab &resu) const override
Adds boundary condition contributions to the right-hand side.
void dimensionner_termes_croises(Matrice_Morse &, const Probleme_base &autre_pb, int nl, int nc) const override
void dimensionner(Matrice_Morse &mat) const override
Builds the sparsity pattern of the DG diffusion matrix in a Matrice_Morse.
This class provides the common infrastructure shared by all DG diffusion operators in TRUST....
double nu(int i, int compo) const
void completer() override
Finalizes the operator setup after all associations have been made.
void update_nu() const
Updates the cached effective diffusivity field nu_ by combining molecular and turbulent contributions...
void associer_diffusivite_turbulente(const Champ_Fonc_base &)
std::vector< const Operateur_Diff_base * > op_ext
virtual void init_op_ext() const
DoubleTab flux_bords_
static int Get_order_for(const Nom &n)
Definition Option_DG.cpp:70
classe Probleme_base C'est un Probleme_U qui n'est pas un couplage.
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
int nb_pts_integ(int e) const
double compute_integral_on_facet(int num_facet, Parser_U &parser) const
const DoubleTab & get_integ_points_facets() const
int nb_pts_integ_max() const
double compute_integral_on_elem(int num_elem, Parser_U &parser) const
int nb_pts_integ_facets() const
double temps_courant() const
Renvoie le temps courant.
Classe de base des flux de sortie.
Definition Sortie.h:52
void ref_array(TRUSTArray< _TYPE_, _SIZE_ > &, _SIZE_ start=0, _SIZE_ sz=-1) override
Definition TRUSTTab.tpp:332
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
int line_size() const
Definition TRUSTVect.tpp:67
const Objet_U & valeur() const
Definition TRUST_Ref.h:134