PolyFEM
Loading...
Searching...
No Matches
tetrahedron.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2
3import math
4import textwrap
5import numpy
6import sympy
7import quadpy
8
9
11 """
12 Integrate a function over the reference tetrahedron (0,0), (1,0), (0,1).
13 """
14 x, y, z = sympy.symbols('x y z')
15 exact = sympy.integrate(
16 sympy.integrate(f, (y, 0, 1 - x)),
17 (x, 0, 1)
18 )
19 exact = sympy.integrate(
20 sympy.integrate(
21 sympy.integrate(f, (z, 0, 1 - x - y)),
22 (y, 0, 1 - x)
23 ),
24 (x, 0, 1)
25 )
26 return exact
27
28
29def integrate_approx(f, scheme):
30 """
31 Integrate numerically over the reference tetrahedron.
32 """
33 x, y, z = sympy.symbols('x y z')
34 vol = 1.0 / 6.0 # volume of the reference tetrahedron
35 res = 0
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])])
39 return vol * res
40
41
43 """
44 List all existing schemes for tetrahedra.
45 """
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]]
66
67 return L
68
69
71 """
72 Generate trivariate monomials up to given order.
73 """
74 monoms = []
75 x, y, z = sympy.symbols('x y z')
76 for i in range(0, order + 1):
77 for j in range(0, order + 1):
78 for k in range(0, order + 1):
79 if i + j + k <= order:
80 monoms.append(x**i * y**j * z**k)
81 return monoms
82
83
84def is_valid(scheme, tol=1e-6, relaxed=False):
85 """
86 A scheme is valid if:
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.
91 """
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()
99
100
101def pick_scheme(all_schemes, order, relaxed=False):
102 """
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.
112 """
113 monoms = generate_monomials(order)
114 print(monoms)
115 min_points = None
116 best_error = None
117 best_scheme = None
118 threshold = 1e-12 # Only accept quadrature rules with this much precision
119 for scheme in all_schemes:
120 if scheme.degree == order and is_valid(scheme, relaxed=relaxed):
121 N = len(scheme.weights)
122 ok = True
123 error = 0
124 for poly in monoms:
125 exact = integrate_exact(poly)
126 approx = integrate_approx(poly, scheme)
127 # print(abs(exact - approx), tol)
128 if not math.isclose(exact - approx, 0, abs_tol=threshold):
129 ok = False
130 error += abs(exact - approx)
131 if ok:
132 if (min_points is None) or N < min_points:
133 min_points = N
134 best_error = error
135 best_scheme = scheme
136 elif N == min_points and error < best_error:
137 best_error = error
138 best_scheme = scheme
139 return best_scheme, best_error
140
141
142def generate_cpp(selected_schemes):
143 """
144 Generate cpp code to fill points & weights.
145 """
146 res = []
147 for order, scheme in selected_schemes:
148 try:
149 name = scheme.name
150 except AttributeError:
151 name = scheme.__class__.__name__
152 code = """\
153 case {order}: // {name}
154 points.resize({num_pts}, 3);
155 weights.resize({num_pts}, 1);
156 points << {points};
157 weights << {weights};
158 break;
159 """.format(order=order,
160 name=name,
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))
166 return ''.join(res)
167
168
169def main():
170 all_schemes = list_schemes()
171 selected = []
172 for order in range(1, 16):
173 print("order", order)
174 scheme, error = pick_scheme(all_schemes, order)
175 if scheme is None:
176 scheme, error = pick_scheme(all_schemes, order, relaxed=True)
177 assert scheme is not None
178 print(error)
179 selected.append((order, scheme))
180 code = generate_cpp(selected)
181 with open('../auto_tetrahedron.ipp', 'w') as f:
182 f.write(code)
183
184
185if __name__ == '__main__':
186 main()
generate_cpp(selected_schemes)
is_valid(scheme, tol=1e-6, relaxed=False)
generate_monomials(order)
integrate_approx(f, scheme)
pick_scheme(all_schemes, order, relaxed=False)