LCOV - code coverage report
Current view: top level - src/quadrature - quadrature_nodal_2D.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 42 45 93.3 %
Date: 2025-08-19 19:27:09 Functions: 1 1 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/quadrature_nodal.h"
      22             : 
      23             : #include "libmesh/enum_to_string.h"
      24             : #include "libmesh/face_c0polygon.h"
      25             : #include "libmesh/quadrature_trap.h"
      26             : #include "libmesh/quadrature_simpson.h"
      27             : 
      28             : namespace libMesh
      29             : {
      30             : 
      31        1972 : void QNodal::init_2D()
      32             : {
      33             : #if LIBMESH_DIM > 1
      34             : 
      35        1972 :   switch (_type)
      36             :     {
      37             : 
      38         142 :     case QUAD4:
      39             :     case QUADSHELL4:
      40             :     case TRI3:
      41             :     case TRISHELL3:
      42             :       {
      43         146 :         QTrap rule(/*dim=*/2, /*ignored*/_order);
      44         142 :         rule.init(*this);
      45           4 :         _points.swap (rule.get_points());
      46           4 :         _weights.swap(rule.get_weights());
      47           4 :         return;
      48             :       }
      49             : 
      50          36 :     case QUAD8:
      51             :     case QUADSHELL8:
      52             :       {
      53             :         // A rule with 8 points which is exact for linears, and
      54             :         // naturally produces a lumped approximation to the mass
      55             :         // matrix. The quadrature points are numbered the same way as
      56             :         // the reference element nodes.
      57         980 :         _points =
      58             :           {
      59             :             Point(-1,-1), Point(+1,-1), Point(+1,+1), Point(-1,+1),
      60             :             Point(0.,-1), Point(+1,0.), Point(0.,+1), Point(-1,0.)
      61         526 :           };
      62             : 
      63             :         // The weights for the Quad8 nodal quadrature rule are
      64             :         // obtained from the following specific steps. Other
      65             :         // "serendipity" type rules are obtained similarly.
      66             :         //
      67             :         // 1.) Due to the symmetry of the bi-unit square domain, we
      68             :         // first note that there are only two "classes" of node in the
      69             :         // Quad8: vertices (with associated weight wv) and edges (with
      70             :         // associated weight we).
      71             :         //
      72             :         // 2.) In order for such a nodal quadrature rule to be exact
      73             :         // for constants, the weights must sum to the area of the
      74             :         // reference element, and therefore we must have:
      75             :         // 4*wv + 4*we = 4 --> wv + we = 1
      76             :         //
      77             :         // 3.) Due to symmetry (once again), such a rule is then
      78             :         // automatically exact for the linear polynomials "x" and "y",
      79             :         // regardless of the values of wv and we.
      80             :         //
      81             :         // 4.) We therefore still have two unknowns and one equation.
      82             :         // Attempting to additionally make the nodal quadrature rule
      83             :         // exact for the quadratic polynomials "x^2" and "y^2" leads
      84             :         // to a rule with negative weights, namely:
      85             :         // wv = -1/3
      86             :         // we = 4/3
      87             :         // Note: these are the same values one obtains by integrating
      88             :         // the corresponding Quad8 Lagrange basis functions over the
      89             :         // reference element.
      90             :         //
      91             :         // Since the weights appear on the diagonal of the nodal
      92             :         // quadrature rule's approximation to the mass matrix, rules
      93             :         // with negative weights yield an indefinite mass matrix,
      94             :         // i.e. one with both positive and negative eigenvalues. Rules
      95             :         // with negative weights can also produce a negative integral
      96             :         // approximation to a strictly positive integrand, which may
      97             :         // be unacceptable in some situations.
      98             :         //
      99             :         // 5.) Requiring all positive weights therefore restricts the
     100             :         // nodal quadrature rule to only be exact for linears. But
     101             :         // there is still one free parameter remaining, and thus we
     102             :         // need some other criterion in order to complete the
     103             :         // description of the rule.
     104             :         //
     105             :         // Here, we have decided to choose the quadrature weights in
     106             :         // such a way that the resulting nodal quadrature mass matrix
     107             :         // approximation is "proportional" to the true mass matrix's
     108             :         // diagonal entries for the reference element. We therefore
     109             :         // pose the following constrained optimization problem:
     110             :         //
     111             :         //     { min_{wv, we} |diag(M) - C*diag(W)|^2
     112             :         // (O) {
     113             :         //     { subject to wv + we = 1
     114             :         //
     115             :         // where:
     116             :         // * M is the true mass matrix
     117             :         // * W is the nodal quadrature approximation to the mass
     118             :         //   matrix. In this particular case:
     119             :         //   diag(W) = [wv,wv,wv,wv,we,we,we,we]
     120             :         // * C = tr(M) / vol(E) is the ratio between the trace of the
     121             :         //   true mass matrix and the volume of the reference
     122             :         //   element. For all Lagrange finite elements, we have C<1.
     123             :         //
     124             :         // 6.) The optimization problem (O) is solved directly by
     125             :         // substituting the algebraic constraint into the functional
     126             :         // which is to be minimized, then setting the derivative with
     127             :         // respect to the remaining parameter equal to zero and
     128             :         // solving. In the Quad8 and Hex20 cases, there is only one
     129             :         // free parameter, while in the Prism15 case there are two
     130             :         // free parameters, so a 2x2 system of linear equations must
     131             :         // be solved.
     132          36 :         Real wv = Real(12) / 79;
     133          36 :         Real we = Real(67) / 79;
     134             : 
     135         526 :         _weights = {wv, wv, wv, wv, we, we, we, we};
     136             : 
     137         526 :         return;
     138             :       }
     139             : 
     140         885 :     case QUAD9:
     141             :     case QUADSHELL9:
     142             :     case TRI6:
     143             :       {
     144         947 :         QSimpson rule(/*dim=*/2, /*ignored*/_order);
     145         885 :         rule.init(*this);
     146          62 :         _points.swap (rule.get_points());
     147          62 :         _weights.swap(rule.get_weights());
     148          62 :         return;
     149             :       }
     150             : 
     151          26 :     case TRI7:
     152             :       {
     153             :         // We can't exactly represent cubics with only seven nodes,
     154             :         // but with w_i = integral(phi_i) for Lagrange shape functions
     155             :         // phi_i, we not only get exact integrals of every Lagrange
     156             :         // shape function, including the cubic bubble, we also get
     157             :         // exact integrals of the rest of P^3 too.
     158         666 :          _points =
     159             :           {
     160             :             Point(0.,0.), Point(+1,0.), Point(0.,+1), Point(.5,0.),
     161             :             Point(.5,.5), Point(0.,.5), Point(1/Real(3),1/Real(3))
     162         359 :           };
     163             : 
     164          26 :         Real wv = Real(1)/40;
     165          26 :         Real we = Real(1)/15;
     166         359 :         _weights = {wv, wv, wv, we, we, we, Real(9)/40};
     167         359 :         return;
     168             :       }
     169             : 
     170             :       //---------------------------------------------
     171             :       // Arbitrary polygon quadrature rules
     172          60 :     case C0POLYGON:
     173             :       {
     174             :         // C0Polygon requires the newer Quadrature API
     175          60 :         if (!_elem)
     176           0 :           libmesh_error();
     177             : 
     178           5 :         libmesh_assert(_elem->type() == C0POLYGON);
     179             : 
     180           5 :         const C0Polygon & poly = *cast_ptr<const C0Polygon *>(_elem);
     181             : 
     182           5 :         const unsigned int ns = poly.n_sides();
     183          60 :         const Real pi_over_ns = libMesh::pi / ns;
     184          60 :         const Real master_poly_area = ns * 0.25 / tan(pi_over_ns);
     185             : 
     186          60 :         const unsigned int nn = poly.n_nodes();
     187          60 :         const Real weight = master_poly_area / nn;
     188          60 :         _weights.resize(nn, weight);
     189             : 
     190          60 :         _points.resize(poly.n_nodes());
     191         360 :         for (auto n : make_range(nn))
     192         300 :           _points[n] = poly.master_point(n);
     193             : 
     194           5 :         return;
     195             :       }
     196             : 
     197           0 :     default:
     198           0 :       libmesh_error_msg("Element type not supported!:" << Utility::enum_to_string(_type));
     199             :     }
     200             : #endif
     201             : }
     202             : 
     203             : } // namespace libMesh

Generated by: LCOV version 1.14