12 Integrate a function over the reference tetrahedron (0,0), (1,0), (0,1).
14 x, y, z = sympy.symbols(
'x y z')
15 exact = sympy.integrate(
16 sympy.integrate(f, (y, 0, 1 - x)),
19 exact = sympy.integrate(
21 sympy.integrate(f, (z, 0, 1 - x - y)),
31 Integrate numerically over the reference tetrahedron.
33 x, y, z = sympy.symbols(
'x y z')
36 for i, w
in enumerate(scheme.weights):
37 res += w * f.subs([(x, scheme.points[i, 0]),
38 (y, scheme.points[i, 1]), (z, scheme.points[i, 2])])
44 List all existing schemes for tetrahedra.
46 L = [quadpy.tetrahedron.BeckersHaegemans(k)
for k
in [8, 9]] \
47 + [quadpy.tetrahedron.Gatermann()] \
48 + [quadpy.tetrahedron.GrundmannMoeller(k)
for k
in range(8)] \
49 + [quadpy.tetrahedron.HammerStroud(k)
for k
in [2, 3]] \
50 + [quadpy.tetrahedron.HammerMarloweStroud(k)
for k
in [1, 2, 3]] \
51 + [quadpy.tetrahedron.Keast(k)
for k
in range(11)] \
52 + [quadpy.tetrahedron.LiuVinokur(k)
for k
in range(1, 15)] \
53 + [quadpy.tetrahedron.MaeztuSainz()] \
54 + [quadpy.tetrahedron.NewtonCotesClosed(k)
for k
in range(1, 7)] \
55 + [quadpy.tetrahedron.NewtonCotesOpen(k)
for k
in range(7)] \
56 + [quadpy.tetrahedron.ShunnHam(k)
for k
in range(1, 7)] \
57 + [quadpy.tetrahedron.Stroud(k)
for k
in [
'T3 5-1',
'T3 7-1']] \
58 + [quadpy.tetrahedron.VioreanuRokhlin(k)
for k
in range(10)] \
59 + [quadpy.tetrahedron.Walkington(k)
for k
in [1, 2, 3, 5,
'p5', 7]] \
60 + [quadpy.tetrahedron.WilliamsShunnJameson()] \
61 + [quadpy.tetrahedron.WitherdenVincent(k)
for k
in [1, 2, 3, 5, 6, 7, 8, 9, 10]] \
62 + [quadpy.tetrahedron.XiaoGimbutas(k)
for k
in range(1, 16)] \
63 + [quadpy.tetrahedron.Yu(k)
for k
in range(1, 6)] \
64 + [quadpy.tetrahedron.ZhangCuiLiu(k)
for k
in [1, 2]] \
65 + [quadpy.tetrahedron.Zienkiewicz(k)
for k
in [4, 5]]
87 1. All its weights sums up to one;
88 2. All the weights are positive;
89 3. All its points are inside the reference tetrahedron;
90 4. No point lie on an face.
92 return math.isclose(numpy.sum(scheme.weights), 1.0, rel_tol=1e-10) \
93 and (relaxed
or (scheme.weights >= tol).all()) \
94 and (scheme.points >= 0).all() \
95 and (scheme.points[:, 0] + scheme.points[:, 1] + scheme.points[:, 2] <= 1 - tol).all() \
96 and (scheme.points[:, 0] >= tol).all() \
97 and (scheme.points[:, 1] >= tol).all() \
98 and (scheme.points[:, 2] >= tol).all()
103 Picks the best scheme for a given polynomial degree, following this strategy:
104 - Tries all schemes with the same order as required.
105 - Eliminate schemes with weights that do not sum up to 1, or points that lie
106 outside the reference triangle.
107 - Among the remaining schemes, compare the integration error over all bivariate
108 monomials of inferior order.
109 - Keeps only the scheme with total integration error lower than a certain threshold.
110 - Among those, keep the one with fewer number of integration points.
111 - Finally, break ties using the integration error accumulated over the monomials.
119 for scheme
in all_schemes:
120 if scheme.degree == order
and is_valid(scheme, relaxed=relaxed):
121 N = len(scheme.weights)
128 if not math.isclose(exact - approx, 0, abs_tol=threshold):
130 error += abs(exact - approx)
132 if (min_points
is None)
or N < min_points:
136 elif N == min_points
and error < best_error:
139 return best_scheme, best_error
144 Generate cpp code to fill points & weights.
147 for order, scheme
in selected_schemes:
150 except AttributeError:
151 name = scheme.__class__.__name__
153 case {order}: // {name}
154 points.resize({num_pts}, 3);
155 weights.resize({num_pts}, 1);
157 weights << {weights};
159 """.format(order=order,
161 num_pts=len(scheme.weights),
162 points=
', '.join(
'{:.64g}'.format(w)
163 for w
in scheme.points.flatten()),
164 weights=
', '.join(
'{:.64g}'.format(w)
for w
in scheme.weights))
165 res.append(textwrap.dedent(code))