-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
triangle_quadrature_test.cc
180 lines (147 loc) · 5.21 KB
/
triangle_quadrature_test.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include "drake/multibody/triangle_quadrature/triangle_quadrature.h"
#include <algorithm>
#include <numeric>
#include <gtest/gtest.h>
#include "drake/common/test_utilities/expect_throws_message.h"
#include "drake/multibody/triangle_quadrature/gaussian_triangle_quadrature_rule.h"
using Vector2d = drake::Vector2<double>;
namespace drake {
namespace multibody {
namespace {
GTEST_TEST(TriangleQuadrature, GaussianQuadratureRuleThrowsAboveMaxOrder) {
// TODO(edrumwri): Consider adding a max_order() method to the
// TriangleQuadratureRule class (if more quadrature rules are added).
const int max_order = 5;
DRAKE_EXPECT_THROWS_MESSAGE(GaussianTriangleQuadratureRule(max_order + 1),
".*quadrature only supported up to fifth order.*");
}
GTEST_TEST(TriangleQuadrature, WeightsSumToUnity) {
for (int order = 1; order <= 5; ++order) {
GaussianTriangleQuadratureRule rule(order);
const std::vector<double>& weights = rule.weights();
const double sum = std::accumulate(weights.begin(), weights.end(), 0.0);
const double tol = weights.size() *
std::numeric_limits<double>::epsilon();
EXPECT_NEAR(sum, 1.0, tol);
}
}
GTEST_TEST(TriangleQuadrature, BarycentricCoordsConsistent) {
for (int order = 1; order <= 5; ++order) {
GaussianTriangleQuadratureRule rule(order);
const std::vector<Vector2d>& quadrature_points = rule.quadrature_points();
for (const Vector2d& p : quadrature_points)
EXPECT_LE(p[0] + p[1], 1.0 + std::numeric_limits<double>::epsilon());
}
}
// A class for tests from TEST04 of:
// NOLINTNEXTLINE(whitespace/line_length)
// https://people.sc.fsu.edu/~jburkardt/f_src/triangle_dunavant_rule/triangle_dunavant_rule_prb_output.txt
// where each result, integrated over the domain of the unit triangle, should
// equal unity (1.0).
class UnityQuadratureTest : public ::testing::Test {
public:
void TestForUnityResultFromStartingOrder(
const std::function<double(const Vector2d&)>& f, int starting_order) {
// Test Gaussian quadrature rules from starting_order through 5.
for (int order = starting_order; order <= 5; ++order) {
GaussianTriangleQuadratureRule rule(order);
// Compute the integral over the unit triangle (0, 0), (1, 0), (0, 1).
const int num_weights = static_cast<int>(rule.weights().size());
double result = TriangleQuadrature<double, double>::Integrate(
f, rule, 0.5 /* triangle area */);
EXPECT_NEAR(result, 1.0,
5 * num_weights * std::numeric_limits<double>::epsilon()) << order;
}
}
};
// First test from TEST04.
TEST_F(UnityQuadratureTest, FirstOrder1) {
// Function to be integrated: 2.0
auto f = [](const Vector2d& p) -> double {
return 2.0;
};
TestForUnityResultFromStartingOrder(f, 1);
}
// Second test from TEST04.
TEST_F(UnityQuadratureTest, FirstOrder2) {
// Function to be integrated: 6.0y
auto f = [](const Vector2d& p) -> double {
return 6.0 * p[1];
};
TestForUnityResultFromStartingOrder(f, 1);
}
// Eleventh test from TEST04.
TEST_F(UnityQuadratureTest, FirstOrder3) {
// Function to be integrated: 6x
auto f = [](const Vector2d& p) -> double {
return 6 * p[0];
};
TestForUnityResultFromStartingOrder(f, 1);
}
// Third test from TEST04 of:
TEST_F(UnityQuadratureTest, SecondOrder1) {
// Function to be integrated: 12y²
auto f = [](const Vector2d& p) -> double {
return 12 * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 2);
}
// Twelfth test from TEST04 of:
TEST_F(UnityQuadratureTest, SecondOrder2) {
// Function to be integrated: 24xy
auto f = [](const Vector2d& p) -> double {
return 24 * p[0] * p[1];
};
TestForUnityResultFromStartingOrder(f, 2);
}
// Fourth test from TEST04 of:
TEST_F(UnityQuadratureTest, ThirdOrder1) {
// Function to be integrated: 20y³
auto f = [](const Vector2d& p) -> double {
return 20 * p[1] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 3);
}
// Thirteenth test from TEST04 of:
TEST_F(UnityQuadratureTest, ThirdOrder2) {
// Function to be integrated: 60xy²
auto f = [](const Vector2d& p) -> double {
return 60 * p[0] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 3);
}
// Fifth test from TEST04 of:
TEST_F(UnityQuadratureTest, FourthOrder1) {
// Function to be integrated: 30y⁴
auto f = [](const Vector2d& p) -> double {
return 30 * p[1] * p[1] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 4);
}
// Fourteenth test from TEST04 of:
TEST_F(UnityQuadratureTest, FourthOrder2) {
// Function to be integrated: 120xy³
auto f = [](const Vector2d& p) -> double {
return 120 * p[0] * p[1] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 4);
}
// Fifth test from TEST04 of:
TEST_F(UnityQuadratureTest, FifthOrder1) {
// Function to be integrated: 42y⁵
auto f = [](const Vector2d& p) -> double {
return 42 * p[1] * p[1] * p[1] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 5);
}
// Fifteenth test from TEST04 of:
TEST_F(UnityQuadratureTest, FifthOrder2) {
// Function to be integrated: 210xy⁴
auto f = [](const Vector2d& p) -> double {
return 210 * p[0] * p[1] * p[1] * p[1] * p[1];
};
TestForUnityResultFromStartingOrder(f, 5);
}
} // namespace
} // namespace multibody
} // namespace drake