TRUST 1.9.8
HPC thermohydraulic platform
Loading...
Searching...
No Matches
BasisFunction.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 <BasisFunction.h>
17#include <TRUSTTab_parts.h>
18#include <Matrix_tools.h>
19#include <Array_tools.h>
20
21/**
22 * @brief Initializes the basis function object for a given DG domain and polynomial order.
23 *
24 * @details Sets up the global DOF index table indices_glob_elem_, selects the default
25 * quadrature order (3 for order 1, 5 for order 2), and, if orthonormalization is
26 * requested, allocates and builds the block-diagonal transition matrix via
27 * allocate_transition_matrix() and build_transition_matrix(). Finally computes the
28 * element and face stabilization parameters via compute_stab_param().
29 *
30 * @param dom The DG domain providing mesh geometry and connectivity.
31 * @param order Polynomial order of the basis (0, 1, or 2).
32 * @param gram_schmidt If true, the basis is L2-orthonormalized via Gram-Schmidt.
33 */
34void BasisFunction::initialize(const Domaine_DG& dom, const int& order, const bool& gram_schmidt)
35{
36 dom_ = dom;
37 order_ = order;
39 is_orthonormalized_ = false;
41
42 int nb_elem_tot = dom_->nb_elem_tot();
43 indices_glob_elem_.resize(nb_elem_tot+1);
45 for (int e = 0; e < nb_elem_tot; e++)
47 default_quad_order_ = 3*(order_==1)+5*(order_==2);
48
49 if (is_diagonal_)
50 {
53 }
54
55
56
57 // computation of the stabilization parameters
59}
60
61/**
62 * @brief Allocates the sparsity pattern of the block-diagonal transition matrix.
63 *
64 * @details Builds a stencil with a full nb_bfunc x nb_bfunc dense block for each
65 * element, then calls Matrix_tools::allocate_morse_matrix() to size transition_matrix_.
66 * The transition matrix is later filled by build_transition_matrix() with the
67 * Gram-Schmidt change-of-basis coefficients.
68 */
70{
71 Stencil indice(0, 2);
72 int nb_elem_tot = dom_->nb_elem_tot();
73
74 int current_indice = 0;
75 for (int e = 0; e < nb_elem_tot; e++)
76 {
77 for (int i = 0; i < nb_bfunc_; i++ )
78 for (int j = 0; j < nb_bfunc_; j++ )
79 indice.append_line( current_indice+i, current_indice+j);
80 current_indice+=nb_bfunc_;
81 }
82
83 int size_inc = indices_glob_elem_(nb_elem_tot);
84
85 tableau_trier_retirer_doublons(indice);
87}
88
89/**
90 * @brief Computes and stores the Gram-Schmidt orthonormalization coefficients.
91 *
92 * @details Iterates over all elements, evaluates the raw monomial basis at the element
93 * quadrature points, then calls gramSchmidt() to orthonormalize the basis in-place and
94 * record the change-of-basis coefficients in transition_matrix_. Sets is_orthonormalized_
95 * to true upon completion.
96 */
98{
99 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
100
101 const DoubleVect& ve = domaine.volumes();
102 int current_indice = 0;
103 const Quadrature_base& quad = domaine.get_quadrature(default_quad_order_);
104 const IntTab& tab_pts_integ= quad.get_tab_nb_pts_integ();
105 int nb_pts_integ_max = quad.nb_pts_integ_max();
106 DoubleTab fbase(nb_bfunc_, nb_pts_integ_max);
107
108 for (int e = 0; e < domaine.nb_elem_tot(); e++)
109 {
110 int nb_pts_integ = tab_pts_integ(e);
111
112 eval_bfunc(quad, e, fbase);
113 gramSchmidt(fbase, quad, e, current_indice, nb_pts_integ, ve(e), 0);
114
115 current_indice+=nb_bfunc_;
116 }
117 is_orthonormalized_ = true;
118
119}
120
121/**
122 * @brief Recursively orthonormalizes the local basis using the modified Gram-Schmidt process.
123 *
124 * @details At step `index`, the function:
125 * 1. Projects fbase[index] onto all previously orthonormalized vectors fbase[0..index-1]
126 * and subtracts those projections, making fbase[index] orthogonal to all previous ones.
127 * 2. Normalizes fbase[index] by its L2 norm (integral divided by element volume).
128 * 3. Records each operation as a row in transition_matrix_ so that orthonormalize()
129 * can re-apply the same transform to any future raw basis evaluation without
130 * repeating the quadrature.
131 * 4. Recurses to process index+1.
132 *
133 * @param fbase Raw basis values at quadrature points (nb_bfunc x nb_pts_integ),
134 * modified in-place to become orthonormal.
135 * @param quad Quadrature rule used to compute inner products.
136 * @param num_elem Global element index (used to query the quadrature).
137 * @param current_indice First global DOF index of this element in transition_matrix_.
138 * @param nb_pts_integ Number of active quadrature points for this element.
139 * @param volume Volume of the element, used for normalization.
140 * @param index Current basis function index being orthonormalized (0-based).
141 */
142void BasisFunction::gramSchmidt(DoubleTab& fbase, const Quadrature_base& quad, const int& num_elem, const int& current_indice, const int& nb_pts_integ, const double& volume, int index)
143{
144 if (index >= nb_bfunc_) return;
145
146 DoubleTab product(nb_pts_integ);
147 transition_matrix_(current_indice+index, current_indice+index) = 1.;
148
149 for (int j = 0; j < index; ++j)
150 {
151 for (int k = 0; k < nb_pts_integ ; k++)
152 product(k) = fbase(index, k) * fbase(j, k);
153
154 double numerateur = quad.compute_integral_on_elem(num_elem, product);
155
156 for (int k = 0; k < nb_pts_integ ; k++)
157 product(k) = fbase(j, k) * fbase(j, k);
158
159 double denominateur = quad.compute_integral_on_elem(num_elem, product);
160
161 double projection = numerateur / denominateur;
162
163 for (int k = 0; k < nb_pts_integ ; k++)
164 fbase(index,k) -= projection * fbase(j,k);
165 for (int l = 0; l < j+1; l++)
166 transition_matrix_(current_indice+index, current_indice+l) -= projection*transition_matrix_(current_indice+j, current_indice+l);
167 }
168
169 // Normalize the basis
170 for (int k = 0; k < nb_pts_integ ; k++)
171 product(k) = fbase(index, k) * fbase(index, k);
172 double norm = quad.compute_integral_on_elem(num_elem, product)/volume;
173 for (int j = 0; j < index+1 ; j++)
174 transition_matrix_(current_indice+index, current_indice+j) /= sqrt(norm);
175 for (int k = 0; k < nb_pts_integ ; k++)
176 fbase(index, k) /= sqrt(norm);
177
178 // Recursive for the next index
179 gramSchmidt(fbase, quad, num_elem, current_indice, nb_pts_integ, volume, index + 1);
180}
181
182/**
183 * @brief Computes the local L2 mass matrix M_ij = integral of phi_i * phi_j on element nelem.
184 *
185 * @param quad Quadrature rule used for integration.
186 * @param nelem Index of the element.
187 * @return A nb_bfunc x nb_bfunc dense matrix containing the local mass matrix.
188 */
190{
191 int nb_pts_integ_max = quad.nb_pts_integ_max();
192 const IntTab& tab_pts_integ= quad.get_tab_nb_pts_integ();
193 DoubleTab fbase(nb_bfunc_, nb_pts_integ_max);
194 DoubleTab product(nb_pts_integ_max);
195 Matrice_Dense loc_mass_mat(nb_bfunc_, nb_bfunc_);
196
197 eval_bfunc(quad, nelem, fbase);
198 for (int i=0; i<nb_bfunc_; i++)
199 {
200 for (int j=0; j<nb_bfunc_; j++)
201 {
202 product = 0.;
203 for (int k = 0; k < tab_pts_integ(nelem) ; k++)
204 product(k) = fbase(i, k) * fbase(j, k);
205
206 loc_mass_mat(i, j) = quad.compute_integral_on_elem(nelem, product);
207 }
208 }
209 return loc_mass_mat;
210}
211
212/**
213 * @brief Evaluates all basis functions at the element quadrature points.
214 *
215 * @details Fills fbasis(i, k) with the value of the i-th basis function at the k-th
216 * quadrature point of element nelem, using the scaled monomial basis centered at the
217 * element barycenter. If the basis is orthonormalized, the transition matrix is applied
218 * via orthonormalize().
219 *
220 * @param quad Quadrature rule providing integration point coordinates.
221 * @param nelem Element index.
222 * @param fbasis Output array of shape (nb_bfunc, nb_pts_integ_max), filled in-place.
223 *
224 * @note Only 2D and orders 0-2 are implemented. Throws for order > 2 or 3D.
225 */
226void BasisFunction::eval_bfunc(const Quadrature_base& quad, const int& nelem, DoubleTab& fbasis) const
227{
228 const DoubleTab& integ_points = quad.get_integ_points();
229
230 assert(fbasis.dimension(0) == nb_bfunc_ && fbasis.dimension(1) == quad.nb_pts_integ_max());
231
232
233 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
234 const DoubleTab& xp = domaine.xp(); // barycentre elem
235
236 if (Objet_U::dimension == 2)
237 {
238 double invh = 1./sqrt(domaine.carre_pas_maille(nelem));
239
240 for (int pt = 0; pt < quad.nb_pts_integ(nelem); pt++)
241 {
242 fbasis(0, pt) = 1;
243 if (order_ == 0) continue;
244
245 fbasis(1, pt) = (integ_points(quad.ind_pts_integ(nelem) + pt, 0) - xp(nelem,0))*invh;
246 fbasis(2, pt) = (integ_points(quad.ind_pts_integ(nelem) + pt, 1) - xp(nelem,1))*invh;
247
248 if (order_ == 1) continue;
249 fbasis(3, pt) = (integ_points(quad.ind_pts_integ(nelem) + pt, 0) - xp(nelem,0))*invh*(integ_points(quad.ind_pts_integ(nelem) + pt, 1) - xp(nelem,1))*invh; //xy
250 fbasis(4, pt) = (integ_points(quad.ind_pts_integ(nelem) + pt, 0) - xp(nelem,0))*invh*(integ_points(quad.ind_pts_integ(nelem) + pt, 0) - xp(nelem,0))*invh; //x2
251 fbasis(5, pt) = (integ_points(quad.ind_pts_integ(nelem) + pt, 1) - xp(nelem,1))*invh*(integ_points(quad.ind_pts_integ(nelem) + pt, 1) - xp(nelem,1))*invh; //y2
252 if (order_ == 2) continue;
253 throw;
254 }
255 }
256 else if (Objet_U::dimension == 3)
257 throw; //TODO
258 else
260
262 orthonormalize(nelem, quad.nb_pts_integ(nelem), fbasis);
263}
264
265/**
266 * @brief Applies the pre-computed Gram-Schmidt transition matrix to a raw basis evaluation.
267 *
268 * @details Iterates over basis functions in reverse order (exploiting the upper-triangular
269 * structure of the transition matrix) and replaces each raw value with the linear
270 * combination given by the corresponding row of transition_matrix_. Handles both 2D
271 * arrays (scalar basis: fbasis(i, k)) and 3D arrays (gradient basis: fbasis(i, k, d)).
272 *
273 * @param nelem Element index, used to locate the block in transition_matrix_.
274 * @param nb_pts_integ Number of quadrature points to transform.
275 * @param fbasis Basis (or gradient) array to transform in-place.
276 */
277void BasisFunction::orthonormalize(const int& nelem, const int& nb_pts_integ, DoubleTab& fbasis) const
278{
279 int current_indice = indices_glob_elem_(nelem);
280
281 double multvect;
282
283 for (int i=nb_bfunc_-1; i>=0; i--) // reverse to take advantage of the triangular matrix
284 {
285 for (int k = 0; k < nb_pts_integ; k++)
286 {
287 if (fbasis.nb_dim() == 2)
288 {
289 multvect = 0.;
290 for (int j=0; j<i+1; j++)
291 multvect += transition_matrix_(current_indice+i,current_indice+j)*fbasis(j,k);
292 fbasis(i,k) = multvect;
293 }
294 else if (fbasis.nb_dim() == 3) // grad cases
295 {
296 for (int l = 0; l < Objet_U::dimension ; l++)
297 {
298 multvect = 0.;
299 for (int j=0; j<i+1; j++)
300 multvect += transition_matrix_(current_indice+i,current_indice+j)*fbasis(j,k,l);
301 fbasis(i,k,l) = multvect;
302 }
303 }
304 }
305 }
306}
307
308/**
309 * @brief Evaluates all basis functions at a set of arbitrary coordinates.
310 *
311 * @details Same polynomial evaluation as the quadrature-based overload but uses
312 * a caller-provided coordinate array instead of the quadrature point table.
313 * Useful for post-processing or point-wise evaluations.
314 *
315 * @param coords Input coordinates array of shape (nb_points, dimension).
316 * @param nelem Element index (used for barycenter and mesh size).
317 * @param fbasis Output array of shape (nb_bfunc, nb_points), filled in-place.
318 *
319 * @note Only 2D and orders 0-2 are implemented. Throws for order > 2 or 3D.
320 */
321void BasisFunction::eval_bfunc(const DoubleTab& coords, const int& nelem, DoubleTab& fbasis) const
322{
323
324 assert(fbasis.dimension(0) == nb_bfunc_);
325
326 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
327 const DoubleTab& xp = domaine.xp(); // barycentre elem
328
329 const Quadrature_base& quad = domaine.get_quadrature();
330 int nb_points = quad.nb_pts_integ(nelem);
331
332 if (Objet_U::dimension == 2)
333 {
334 double invh = 1./sqrt(domaine.carre_pas_maille(nelem));
335
336 for (int pt = 0; pt < nb_points; pt++)
337 {
338 fbasis(0, pt) = 1;
339 if (order_ == 0) continue;
340
341 fbasis(1, pt) = (coords(pt, 0) - xp(nelem,0))*invh;
342 fbasis(2, pt) = (coords(pt, 1) - xp(nelem,1))*invh;
343
344 if (order_ == 1) continue;
345
346 fbasis(3, pt) = (coords(pt, 0) - xp(nelem,0))*invh*(coords(pt, 1) - xp(nelem,1))*invh;
347 fbasis(4, pt) = (coords(pt, 0) - xp(nelem,0))*invh*(coords(pt, 0) - xp(nelem,0))*invh;
348 fbasis(5, pt) = (coords(pt, 1) - xp(nelem,1))*invh*(coords(pt, 1) - xp(nelem,1))*invh;
349
350 if (order_ == 2) continue;
351 throw;
352 }
353 }
354 else if (Objet_U::dimension == 3)
355 throw; //TODO
356 else
358
360 orthonormalize(nelem, nb_points, fbasis);
361}
362
363/* @brief Evaluation of the divergence of the basis functions (need to indicate which scalar component we are using) on integration points for elements
364 *
365 * @param quad Quadrature used to evaluate the basis functions
366 * @param nelem Index of the element
367 * @param dim Scalar component for which the divergence is computed
368 * @param div_fbasis Output tab containing the divergence of the basis functions in dimension dim at integration points
369 */
370void BasisFunction::eval_div_bfunc(const Quadrature_base& quad, const int& nelem, DoubleTab& div_fbasis) const
371{
372 int nb_pts_integ_max = div_fbasis.dimension(2);
373 DoubleTab grad_fbase_elem(nb_bfunc_, nb_pts_integ_max, Objet_U::dimension);
374 eval_grad_bfunc(quad, nelem, grad_fbase_elem);
375 for(int dim=0; dim<Objet_U::dimension; dim++)
376 for (int i=0; i<nb_bfunc_; i++)
377 for (int j=0; j<quad.nb_pts_integ(nelem); j++)
378 div_fbasis(dim,i,j) = grad_fbase_elem(i,j,dim);
379
380}
381
382/**
383 * @brief Evaluates the gradients of all basis functions at the element quadrature points.
384 *
385 * @details Fills grad_fbasis(i, k, d) with the d-th component of grad(phi_i) at the
386 * k-th quadrature point of element nelem. The constant basis function (index 0) has a
387 * zero gradient (left implicitly as zero by the caller's initialization). If the basis
388 * is orthonormalized, the transition matrix is applied via orthonormalize().
389 *
390 * @param quad Quadrature rule providing integration point coordinates.
391 * @param nelem Element index.
392 * @param grad_fbasis Output array of shape (nb_bfunc, nb_pts_integ_max, dimension), filled in-place.
393 *
394 * @note Only 2D and orders 1-2 are implemented. Throws for order > 2 or 3D.
395 */
396void BasisFunction::eval_grad_bfunc(const Quadrature_base& quad, const int& nelem, DoubleTab& grad_fbasis) const
397{
398// const DoubleTab& integ_points_on_facets = quad.get_integ_points_facets();
399 assert(grad_fbasis.dimension(0) == nb_bfunc_ && grad_fbasis.dimension(1) == quad.nb_pts_integ_max() && grad_fbasis.dimension(2) == Objet_U::dimension);
400
401 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
402
403 int ind_elem=quad.ind_pts_integ(nelem);
404 const DoubleTab& integ_points = quad.get_integ_points();
405 const DoubleTab& xp = domaine.xp(); // barycentre elem
406
407 if (Objet_U::dimension == 2)
408 {
409 double invh = 1./sqrt(domaine.carre_pas_maille(nelem));
410
411 for (int pt = 0; pt < quad.nb_pts_integ(nelem); pt++)
412 {
413
414 grad_fbasis(1, pt, 0) = invh;
415 grad_fbasis(1, pt, 1) = 0.;
416 grad_fbasis(2, pt, 0) = 0.;
417 grad_fbasis(2, pt, 1) = invh;
418
419 if (order_ == 1) continue;
420 grad_fbasis(3, pt, 0) = invh*(integ_points(ind_elem+pt, 1) - xp(nelem,1))*invh;
421 grad_fbasis(3, pt, 1) = invh*(integ_points(ind_elem+pt, 0) - xp(nelem,0))*invh;
422 grad_fbasis(4, pt, 0) = 2*invh*(integ_points(ind_elem+pt, 0) - xp(nelem,0))*invh;
423 grad_fbasis(4, pt, 1) = 0.;
424 grad_fbasis(5, pt, 0) = 0.;
425 grad_fbasis(5, pt, 1) = 2*invh*(integ_points(ind_elem+pt, 1) - xp(nelem,1))*invh;
426
427 if (order_ == 2) continue;
428
429 throw;
430 }
431 }
432 else if (Objet_U::dimension == 3)
433 throw; //TODO
434 else
436
438 orthonormalize(nelem, quad.nb_pts_integ(nelem), grad_fbasis);
439}
440
441/**
442 * @brief Evaluates all basis functions of element nelem at the quadrature points of face num_face.
443 *
444 * @details Same polynomial as eval_bfunc() but uses the face quadrature point coordinates
445 * instead of the element interior points. The basis is still centered at the element
446 * barycenter, so the evaluation is consistent with the interior values across the face.
447 * If the basis is orthonormalized, the transition matrix is applied via orthonormalize().
448 *
449 * @param quad Quadrature rule providing face integration point coordinates.
450 * @param nelem Element index (provides barycenter and mesh size).
451 * @param num_face Face index (selects the row in the face quadrature point table).
452 * @param fbasis Output array of shape (nb_bfunc, nb_pts_integ_facets), filled in-place.
453 *
454 * @note Only 2D and orders 0-2 are implemented. Throws for order > 2 or 3D.
455 */
456void BasisFunction::eval_bfunc_on_facets(const Quadrature_base& quad, const int& nelem, const int& num_face, DoubleTab& fbasis) const
457{
458 const DoubleTab& integ_points_on_facets = quad.get_integ_points_facets();
459 int nb_pts_integ_on_facets = quad.nb_pts_integ_facets();
460
461 assert(fbasis.dimension(0) == nb_bfunc_ && fbasis.dimension(1) == nb_pts_integ_on_facets);
462
463 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
464 const DoubleTab& xp = domaine.xp(); // barycentre elem
465
466 if (Objet_U::dimension == 2)
467 {
468 double invh = 1./sqrt(domaine.carre_pas_maille(nelem));
469
470 for (int pt = 0; pt < nb_pts_integ_on_facets; pt++)
471 {
472 fbasis(0, pt) = 1;
473 if (order_ == 0) continue;
474
475 fbasis(1, pt) = (integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh;
476 fbasis(2, pt) = (integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh;
477
478 if (order_ == 1) continue;
479 fbasis(3, pt) = (integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh*(integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh;
480 fbasis(4, pt) = (integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh*(integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh;
481 fbasis(5, pt) = (integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh*(integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh;
482 if (order_ == 2) continue;
483 throw;
484 }
485 }
486 else if (Objet_U::dimension == 3)
487 throw; //TODO
488 else
490
492 orthonormalize(nelem, nb_pts_integ_on_facets, fbasis);
493}
494
495/**
496 * @brief Evaluates the gradients of all basis functions of element nelem at the quadrature points of face num_face.
497 *
498 * @details Same gradient formulas as eval_grad_bfunc() but evaluated at face quadrature
499 * points. Used in the SIP consistency and symmetry terms where the normal flux
500 * { nu * grad(phi_i) } . n must be integrated over a face. If the basis is
501 * orthonormalized, the transition matrix is applied via orthonormalize().
502 *
503 * @param quad Quadrature rule providing face integration point coordinates.
504 * @param nelem Element index (provides barycenter and mesh size).
505 * @param num_face Face index (selects the row in the face quadrature point table).
506 * @param grad_fbasis Output array of shape (nb_bfunc, nb_pts_integ_facets, dimension), filled in-place.
507 *
508 * @note Only 2D and orders 1-2 are implemented. Throws for order > 2 or 3D.
509 */
510void BasisFunction::eval_grad_bfunc_on_facets(const Quadrature_base& quad, const int& nelem, const int& num_face, DoubleTab& grad_fbasis) const
511{
512
513 const DoubleTab& integ_points_on_facets = quad.get_integ_points_facets();
514 int nb_pts_integ_on_facets = quad.nb_pts_integ_facets();
515 assert(grad_fbasis.dimension(0) == nb_bfunc_ && grad_fbasis.dimension(1) == nb_pts_integ_on_facets && grad_fbasis.dimension(2) == Objet_U::dimension );
516
517 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
518 const DoubleTab& xp = domaine.xp(); // barycentre elem
519
520
521 if (Objet_U::dimension == 2)
522 {
523 double invh = 1./sqrt(domaine.carre_pas_maille(nelem));
524
525 for (int pt = 0; pt < nb_pts_integ_on_facets; pt++)
526 {
527
528 grad_fbasis(1, pt, 0) = invh;
529 grad_fbasis(1, pt, 1) = 0.;
530 grad_fbasis(2, pt, 0) = 0.;
531 grad_fbasis(2, pt, 1) = invh;
532
533 if (order_ == 1) continue;
534
535 grad_fbasis(3, pt, 0) = invh*(integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh; //xy
536 grad_fbasis(3, pt, 1) = invh*(integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh; //xy
537 grad_fbasis(4, pt, 0) = 2*invh*(integ_points_on_facets(num_face, pt, 0) - xp(nelem,0))*invh; //xx
538 grad_fbasis(4, pt, 1) = 0.; //xy
539 grad_fbasis(5, pt, 0) = 0.; //xy
540 grad_fbasis(5, pt, 1) = 2*invh*(integ_points_on_facets(num_face, pt, 1) - xp(nelem,1))*invh; //xy
541 if (order_ == 2) continue;
542 throw;
543 }
544 }
545 else if (Objet_U::dimension == 3)
546 throw; //TODO
547 else
549
551 orthonormalize(nelem, nb_pts_integ_on_facets, grad_fbasis);
552}
553
554/**
555 * @brief Evaluates the divergence of the basis functions at the quadrature points of face num_face.
556 *
557 * @details Computes the gradient via eval_grad_bfunc_on_facets() and then rearranges the
558 * result so that div_fbasis(d, i, k) = d(phi_i)/dx_d at the k-th face quadrature point.
559 * This permuted layout is used when assembling divergence-based operators.
560 *
561 * @param quad Quadrature rule providing face integration point coordinates.
562 * @param nelem Element index.
563 * @param num_face Face index.
564 * @param div_fbasis Output array of shape (nb_bfunc, nb_pts_integ_facets), filled in-place.
565 */
566void BasisFunction::eval_div_bfunc_on_facets(const Quadrature_base& quad, const int& nelem, const int& num_face, DoubleTab& div_fbasis) const
567{
568 assert(nb_bfunc_ == div_fbasis.dimension(0));
569 int nb_pts_integ_max = div_fbasis.dimension(1);
570 DoubleTab grad_fbase_elem(nb_bfunc_, nb_pts_integ_max, Objet_U::dimension);
571 eval_grad_bfunc_on_facets(quad, nelem, num_face, grad_fbase_elem);
572 for(int dim=0; dim<Objet_U::dimension; dim++)
573 for (int i=0; i<nb_bfunc_; i++)
574 for (int j=0; j<quad.nb_pts_integ(nelem); j++)
575 div_fbasis(dim,i,j) = grad_fbase_elem(i,j,dim);
576}
577
578/**
579 * @brief Computes the inverse of the local L2 mass matrix for element nelem.
580 *
581 * @details Two strategies are used depending on the polynomial order:
582 * - **Order 1 (2D)**: The 3x3 mass matrix has an analytic block structure. The (0,0)
583 * entry is 1/volume, and the lower-right 2x2 block (linear DOFs) is inverted
584 * analytically using the determinant of the moment integrals sum_x2, sum_y2, sum_xy.
585 * - **Order 2 (2D)**: The full 6x6 mass matrix is assembled by build_local_mass_matrix()
586 * and then numerically inverted via Matrice_Dense::inverse().
587 *
588 * @param quad Quadrature rule used to compute the mass matrix entries.
589 * @param nelem Element index.
590 * @return A nb_bfunc x nb_bfunc dense matrix containing M^{-1}.
591 *
592 * @note 3D is not yet implemented and throws at runtime.
593 */
594const Matrice_Dense BasisFunction::eval_invMassMatrix(const Quadrature_base& quad, const int& nelem) const
595{
596
597 // TODo DG adapt to high order quadrature ? possible ? or inverse mass matrix with specific quadrature ?
598
599 const DoubleTab& weights = quad.get_weights();
600 int nb_pts_integ_max = quad.nb_pts_integ_max();
601
603
604 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
605 const DoubleVect& ve = domaine.volumes();
606
607 DoubleTab fbase(nb_bfunc_, nb_pts_integ_max);
608
609 eval_bfunc(quad, nelem, fbase);
610
611 if (Objet_U::dimension == 2 && order_==1) // TODO: When general order, make it local to the cell
612 {
613 double invV = 1./ve(nelem);
614 matrice(0, 0) = invV;
615
616 matrice(0, 1) = 0.;
617 matrice(0, 2) = 0.;
618
619 matrice(1, 0) = 0.;
620 matrice(2, 0) = 0.;
621
622 double sumx2 = 0.;
623 double sumy2 = 0.;
624 double sumxy = 0.;
625
626 for (int f = 0; f < quad.nb_pts_integ(nelem); f++)
627 {
628 sumx2 += weights(quad.ind_pts_integ(nelem)+f)*ve(nelem)*fbase(1,f)*fbase(1,f);
629 sumxy += weights(quad.ind_pts_integ(nelem)+f)*ve(nelem)*fbase(1,f)*fbase(2,f);;
630 sumy2 += weights(quad.ind_pts_integ(nelem)+f)*ve(nelem)*fbase(2,f)*fbase(2,f);
631 }
632
633 double inv_det = 1./(sumy2*sumx2 - sumxy*sumxy);
634
635 matrice(1, 1) = sumy2*inv_det;
636 matrice(2, 2) = sumx2*inv_det;
637
638 matrice(1, 2) = -sumxy*inv_det;
639 matrice(2, 1) = -sumxy*inv_det;
640 }
641 else if (Objet_U::dimension == 2 && order_==2)
642 {
643 matrice=build_local_mass_matrix(quad, nelem); // Creation of the local mass matrix.
644 matrice.inverse(); // Inversion of the local mass matrix.
645 }
646 else if (Objet_U::dimension == 3)
647 throw; //TODO
648 else
650
651 return matrice;
652}
653
654/**
655 * @brief Computes the SIP penalty stabilization parameters eta_elem and eta_facet.
656 *
657 * @details Element parameters eta_elem(e) depend on the polynomial order and the
658 * element geometry:
659 * - **Triangles**: eta = (6/pi) * sigma^2 * (p+1)*(p+2), where sigma is the element
660 * shape parameter from Domaine_DG::get_sig(). This is a theoretically grounded
661 * lower bound for coercivity of the SIP bilinear form on triangles.
662 * - **Other polygons/polyhedra**: eta = 10*p^2, a conservative estimate pending a
663 * more geometry-aware formula.
664 *
665 * Face parameters eta_facet(f) are derived from the adjacent element values:
666 * - **Boundary faces**: eta_facet = eta_elem of the single adjacent element.
667 * - **Internal faces**: eta_facet = harmonic mean of the two adjacent element values,
668 * providing a balanced penalty that accounts for differing element sizes or orders
669 * on each side.
670 */
672{
673 const Domaine_DG& domaine = ref_cast(Domaine_DG,dom_.valeur());
674 int nb_elem_tot = domaine.nb_elem_tot();
675 eta_elem.resize(nb_elem_tot);
676 eta_facet.resize(domaine.nb_faces());
677 const IntTab& nfaces_elem = domaine.get_nfaces_elem(); // IntTab that indicate the number of facet of each elem
678
679 // Computation of the stabilisation parameters for triangle
680 const DoubleTab& sig=domaine.get_sig();
681 for (int e = 0; e < nb_elem_tot; e++)
682 {
683 double ordre = get_order();
684 if(nfaces_elem(e)==3) // triangle
685 {
686 eta_elem(e) = 6. / M_PI * (sig(e)*sig(e))*(ordre + 1.)*(ordre + 2.);
687 }
688 else // Polyhedra
689 {
690 eta_elem(e) = 10*ordre*ordre; // TODO: calculate a more optimized pen. coeff.
691 }
692 }
693
694 for (unsigned int f = 0; f < (unsigned int) domaine.premiere_face_int(); f++) // For the boundary
695 {
696 int elem0 = domaine.face_voisins(f, 0);
697 eta_facet(f) = eta_elem(elem0); // EtaF=EtaT
698 }
699
700 for (unsigned int f = domaine.premiere_face_int(); f < (unsigned int) domaine.nb_faces(); f++)
701 {
702 unsigned int elem0 = domaine.face_voisins(f, 0);
703 unsigned int elem1 = domaine.face_voisins(f, 1);
704 double eta_t0 = eta_elem(elem0);
705 double eta_t1 = eta_elem(elem1);
706 eta_facet(f) = 2. * eta_t0 * eta_t1 / (eta_t0 + eta_t1); // Harmonique mean
707 }
708
709 /*for (int e = 0; e < domaine.nb_elem(); e++)
710 {
711 eta_elem(e) = 50.; // Todo DG : replace for generic order
712 }
713 for (unsigned int f = 0; f < (unsigned int) domaine.nb_faces(); f++) // For the boundary
714 {
715 eta_facet(f) = 10.; // EtaF=EtaT
716 }*/
717}
718
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 orthonormalize(const int &nelem, const int &nb_pts_integ, DoubleTab &fbasis) const
Applies the pre-computed Gram-Schmidt transition matrix to a raw basis evaluation.
DoubleTab eta_elem
Matrice_Morse transition_matrix_
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.
void allocate_transition_matrix()
Allocates the sparsity pattern of the block-diagonal transition matrix.
void eval_bfunc(const Quadrature_base &quad, const int &nelem, DoubleTab &fbasis) const
Evaluates all basis functions at the element quadrature points.
void eval_div_bfunc(const Quadrature_base &quad, const int &nelem, DoubleTab &div_fbasis) const
void gramSchmidt(DoubleTab &fbase, const Quadrature_base &quad, const int &num_elem, const int &current_indice, const int &nb_pts_integ, const double &volume, int index)
Recursively orthonormalizes the local basis using the modified Gram-Schmidt process.
void initialize(const Domaine_DG &dom, const int &order, const bool &gram_schmidt)
Initializes the basis function object for a given DG domain and polynomial order.
void build_transition_matrix()
Computes and stores the Gram-Schmidt orthonormalization coefficients.
DoubleTab eta_facet
const int & get_order() const
void compute_stab_param()
Computes the SIP penalty stabilization parameters eta_elem and eta_facet.
const Matrice_Dense eval_invMassMatrix(const Quadrature_base &quad, const int &nelem) const
Computes the inverse of the local L2 mass matrix for element nelem.
IntTab indices_glob_elem_
const Matrice_Dense build_local_mass_matrix(const Quadrature_base &quad, const int nelem) const
Computes the local L2 mass matrix M_ij = integral of phi_i * phi_j on element nelem.
void eval_div_bfunc_on_facets(const Quadrature_base &quad, const int &nelem, const int &num_face, DoubleTab &div_fbasis) const
Evaluates the divergence of the basis functions at the quadrature points of face num_face.
const bool & gram_schmidt() const
Definition Domaine_DG.h:66
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)
static int dimension
Definition Objet_U.h:99
static int Nb_col_from_order(const int order)
Definition Option_DG.cpp:81
static void exit(int exit_code=-1)
Routine de sortie de TRUST dans une region Kokkos.
Definition Process.cpp:455
int ind_pts_integ(int e) const
int nb_pts_integ(int e) const
const DoubleTab & get_weights() const
const DoubleTab & get_integ_points_facets() const
const DoubleTab & get_integ_points() const
int nb_pts_integ_max() const
const IntTab & get_tab_nb_pts_integ() const
double compute_integral_on_elem(int num_elem, Parser_U &parser) const
int nb_pts_integ_facets() const
int nb_dim() const
Definition TRUSTTab.h:199
void append_line(_TYPE_)
Definition TRUSTTab.tpp:213
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133