libMesh
quadrature_gm.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 #include "libmesh/quadrature_gm.h"
20 #include "libmesh/quadrature_gauss.h"
21 #include "libmesh/enum_quadrature_type.h"
22 #include "libmesh/int_range.h"
23 
24 namespace libMesh
25 {
26 
27 // See also the file:
28 // quadrature_gm_2D.C
29 // quadrature_gm_3D.C
30 // for additional implementation.
31 
33 {
34  return QGRUNDMANN_MOLLER;
35 }
36 
37 
38 std::unique_ptr<QBase> QGrundmann_Moller::clone() const
39 {
40  return std::make_unique<QGrundmann_Moller>(*this);
41 }
42 
43 
44 void QGrundmann_Moller::init_1D(const ElemType, unsigned int)
45 {
46  QGauss gauss1D(1, get_order());
47 
48  // Swap points and weights with the about-to-be destroyed rule.
49  _points.swap(gauss1D.get_points());
50  _weights.swap(gauss1D.get_weights());
51 }
52 
53 
54 
55 
56 
57 void QGrundmann_Moller::gm_rule(unsigned int s, unsigned int dim)
58 {
59  // Only dim==2 or dim==3 is allowed
60  libmesh_assert(dim==2 || dim==3);
61 
62  // A GM rule of index s can integrate polynomials of degree 2*s+1 exactly
63  const unsigned int degree = 2*s+1;
64 
65  // The number of points for rule of index s is
66  // (dim+1+s)! / (dim+1)! / s!
67  // In 3D, this is = 1/24 * Pi_{i=1}^4 (s+i)
68  // In 2D, this is = 1/6 * Pi_{i=1}^3 (s+i)
69  const unsigned int n_pts = dim==2 ? (s+3)*(s+2)*(s+1) / 6 : (s+4)*(s+3)*(s+2)*(s+1) / 24;
70  //libMesh::out << "n_pts=" << n_pts << std::endl;
71 
72  // Allocate space for points and weights
73  _points.resize(n_pts);
74  _weights.resize(n_pts);
75 
76  // (-1)^i -> This one flips sign at each iteration of the i-loop below.
77  int one_pm=1;
78 
79  // Where we store all the integer point compositions/permutations
80  std::vector<std::vector<unsigned int>> permutations;
81 
82  // Index into the vector where we should start adding the next round of points/weights
83  std::size_t offset=0;
84 
85  // Implement the GM formula 4.1 on page 286 of the paper
86  for (unsigned int i=0; i<=s; ++i)
87  {
88  // Get all the ordered compositions (and their permutations)
89  // of |beta| = s-i into dim+1 parts
90  compose_all(s-i, dim+1, permutations);
91  //libMesh::out << "n. permutations=" << permutations.size() << std::endl;
92 
93  for (auto p : index_range(permutations))
94  {
95  // We use the first dim entries of each permutation to
96  // construct an integration point.
97  for (unsigned int j=0; j<dim; ++j)
98  _points[offset+p](j) =
99  static_cast<Real>(2.*permutations[p][j] + 1.) /
100  static_cast<Real>( degree + dim - 2.*i );
101  }
102 
103  // Compute the weight for this i, being careful to avoid overflow.
104  // This technique is borrowed from Burkardt's code as well.
105  // Use once for each of the points obtained from the permutations array.
106  Real weight = one_pm;
107 
108  // This for loop needs to run for dim, degree, or dim+degree-i iterations,
109  // whichever is largest.
110  const unsigned int weight_loop_index =
111  std::max(dim, std::max(degree, degree+dim-i))+1;
112 
113  for (unsigned int j=1; j<weight_loop_index; ++j)
114  {
115  if (j <= degree) // Accumulate (d+n-2i)^d term
116  weight *= static_cast<Real>(degree+dim-2*i);
117 
118  if (j <= 2*s) // Accumulate 2^{-2s}
119  weight *= 0.5;
120 
121  if (j <= i) // Accumulate (i!)^{-1}
122  weight /= static_cast<Real>(j);
123 
124  if (j <= degree+dim-i) // Accumulate ( (d+n-i)! )^{-1}
125  weight /= static_cast<Real>(j);
126  }
127 
128  // This is the weight for each of the points computed previously
129  for (auto j : index_range(permutations))
130  _weights[offset+j] = weight;
131 
132  // Change sign for next iteration
133  one_pm = -one_pm;
134 
135  // Update offset for the next set of points
136  offset += permutations.size();
137  }
138 }
139 
140 
141 
142 
143 // This routine for computing compositions and their permutations is
144 // originally due to:
145 //
146 // Albert Nijenhuis, Herbert Wilf,
147 // Combinatorial Algorithms for Computers and Calculators,
148 // Second Edition,
149 // Academic Press, 1978,
150 // ISBN: 0-12-519260-6,
151 // LC: QA164.N54.
152 //
153 // The routine is deceptively simple: I still don't understand exactly
154 // why it works, but it does.
155 void QGrundmann_Moller::compose_all(unsigned int s, // number to be compositioned
156  unsigned int p, // # of partitions
157  std::vector<std::vector<unsigned int>> & result)
158 {
159  // Clear out results remaining from previous calls
160  result.clear();
161 
162  // Allocate storage for a workspace. The workspace will periodically
163  // be copied into the result container.
164  std::vector<unsigned int> workspace(p);
165 
166  // The first result is always (s,0,...,0)
167  workspace[0] = s;
168  result.push_back(workspace);
169 
170  // the value of the first non-zero entry
171  unsigned int head_value=s;
172 
173  // When head_index=-1, it refers to "off the front" of the array. Therefore,
174  // this needs to be a regular int rather than unsigned. I initially tried to
175  // do this with head_index unsigned and an else statement below, but then there
176  // is the special case: (1,0,...,0) which does not work correctly.
177  int head_index = -1;
178 
179  // At the end, all the entries will be in the final slot of workspace
180  while (workspace.back() != s)
181  {
182  // If the previous head value is still larger than 1, reset the index
183  // to "off the front" of the array
184  if (head_value > 1)
185  head_index = -1;
186 
187  // Either move the index onto the front of the array or on to
188  // the next value.
189  head_index++;
190 
191  // Get current value of the head entry
192  head_value = workspace[head_index];
193 
194  // Put a zero into the head_index of the array. If head_index==0,
195  // this will be overwritten in the next line with head_value-1.
196  workspace[head_index] = 0;
197 
198  // The initial entry gets the current head value, minus 1.
199  // If head_value > 1, the next loop iteration will start back
200  // at workspace[0] again.
201  libmesh_assert_greater (head_value, 0);
202  workspace[0] = head_value - 1;
203 
204  // Increment the head+1 value
205  workspace[head_index+1] += 1;
206 
207  // Save this composition in the results
208  result.push_back(workspace);
209  }
210 }
211 
212 } // namespace libMesh
ElemType
Defines an enum for geometric element types.
virtual void init_1D(const ElemType, unsigned int) override
In 1D, use a Gauss rule.
Definition: quadrature_gm.C:44
unsigned int dim
virtual QuadratureType type() const override
Definition: quadrature_gm.C:32
const std::vector< Real > & get_weights() const
Definition: quadrature.h:160
QuadratureType
Defines an enum for currently available quadrature rules.
The libMesh namespace provides an interface to certain functionality in the library.
std::vector< Point > _points
The locations of the quadrature points in reference element space.
Definition: quadrature.h:383
std::vector< Real > _weights
The quadrature weights.
Definition: quadrature.h:389
void gm_rule(unsigned int s, unsigned int dim)
This routine is called from init_2D() and init_3D().
Definition: quadrature_gm.C:57
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition: mesh_tools.C:436
libmesh_assert(ctx)
Order get_order() const
Definition: quadrature.h:218
virtual std::unique_ptr< QBase > clone() const override
Definition: quadrature_gm.C:38
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< Point > & get_points() const
Definition: quadrature.h:148
This class implements specific orders of Gauss quadrature.
void compose_all(unsigned int s, unsigned int p, std::vector< std::vector< unsigned int >> &result)
Routine which generates p-compositions of a given order, s, as well as permutations thereof...
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:111