#include #include #include "gtest/gtest.h" namespace mm { TEST(Domains, BallContainsBasic1D) { BallShape cs1({3}, 1); EXPECT_TRUE(cs1.contains({3.5})); EXPECT_TRUE(cs1.contains({4})); EXPECT_FALSE(cs1.contains({1})); EXPECT_FALSE(cs1.contains({5})); } TEST(Domains, BallContainsBasic2D) { BallShape cs2({-1, 2}, 5); EXPECT_TRUE(cs2.contains({-0.9, 1.8})); EXPECT_TRUE(cs2.contains({-1.1, 2.2})); EXPECT_TRUE(cs2.contains({0, 3})); EXPECT_TRUE(cs2.contains({4, 2})); EXPECT_TRUE(cs2.contains({4 + 1e-13, 2})); EXPECT_FALSE(cs2.contains({-8, 3})); EXPECT_FALSE(cs2.contains({12, 3})); } TEST(Domains, BallContainsBasic3D) { BallShape cs3({4, -2, 0}, 4); EXPECT_TRUE(cs3.contains({4, 2, 0})); EXPECT_TRUE(cs3.contains({4, -2, 3})); EXPECT_FALSE(cs3.contains({10, 3, 4})); EXPECT_FALSE(cs3.contains({-5, 3, 0})); } TEST(Domains, BallDiscretizeBoundary1d) { BallShape b(-1, 0.4); DomainDiscretization d = b.discretizeBoundaryWithStep(0.1); auto p = d.positions(); std::sort(p.begin(), p.end()); EXPECT_EQ(-1.4, p[0][0]); EXPECT_EQ(-0.6, p[1][0]); } TEST(Domains, BallDiscretizeBoundary2d) { BallShape c({-1, 2.2}, 1.7); auto d = c.discretizeBoundaryWithStep(0.5); auto p = d.positions(); std::vector expected = { {0.7, 2.2}, {0.6311380551446455, 2.678945346630431}, {0.4301310058130081, 3.119089389674516}, {0.1132632477069846, 3.484774276402239}, {-0.293794477896793, 3.746374392102681}, {-0.7580647749354152, 3.882696451197586}, {-1.241935225064585, 3.882696451197586}, {-1.706205522103207, 3.746374392102681}, {-2.113263247706985, 3.484774276402239}, {-2.430131005813008, 3.119089389674516}, {-2.631138055144645, 2.678945346630431}, {-2.7, 2.2}, {-2.631138055144646, 1.72105465336957}, {-2.430131005813008, 1.280910610325484}, {-2.113263247706985, 0.9152257235977614}, {-1.706205522103207, 0.6536256078973188}, {-1.241935225064585, 0.5173035488024147}, {-0.7580647749354157, 0.5173035488024145}, {-0.2937944778967937, 0.6536256078973186}, {0.113263247706985, 0.9152257235977614}, {0.4301310058130081, 1.280910610325485}, {0.6311380551446455, 1.72105465336957}}; double tol = 1e-15; ASSERT_EQ(expected.size(), p.size()); int n = expected.size(); for (int i = 0; i < n; ++i) { EXPECT_NEAR(expected[i][0], p[i][0], tol); EXPECT_NEAR(expected[i][1], p[i][1], tol); } } TEST(Domains, BallDiscretizeBoundary3d) { BallShape domain3d(0, 1); double dx = 0.05; double tol = 0.05; double s = 0; double c = 0; auto discretization3d = domain3d.discretizeBoundaryWithStep(dx); KDTree tree(discretization3d.positions()); for (int i = 0; i < discretization3d.size(); ++i) { Range distances2 = tree.query(discretization3d.pos(i), 2).second; s += std::sqrt(distances2[1]); c += 1; } EXPECT_NEAR(dx, s/c, dx*tol); // 5% error tolerance } TEST(Domains, BallDiscretizeBoundaryWithDensity2d) { BallShape domain2d(0, 1); double dx1 = 0.05; double dx2 = 0.01; double tol = 0.05; double s1 = 0; double c1 = 0; double s2 = 0; double c2 = 0; auto fill = [=](const Vec2d& p) { return (p[0] < 0.0) ? dx1 : dx2; }; auto discretization2d = domain2d.discretizeBoundaryWithDensity(fill); KDTree tree(discretization2d.positions()); for (int i = 0; i < discretization2d.size(); ++i) { if (discretization2d.pos(i, 0) < 0.0 - tol) { Range distances2 = tree.query(discretization2d.pos(i), 2).second; s1 += std::sqrt(distances2[1]); c1 += 1; } else if (discretization2d.pos(i, 0) > 0.0 + tol) { Range distances2 = tree.query(discretization2d.pos(i), 2).second; s2 += std::sqrt(distances2[1]); c2 += 1; } } EXPECT_NEAR(dx1, s1/c1, dx1*tol); // 5% error tolerance EXPECT_NEAR(dx2, s2/c2, dx2*tol); } TEST(Domains, BallDiscretizeBoundaryWithDensity3d) { // TODO(jureslak): this test fails if tolerance is 0.05, but it shouldn't. // Some generated points are too close. double tol = 0.1; double s1 = 0; double c1 = 0; double s2 = 0; double c2 = 0; double dx1 = 0.2; double dx2 = 0.1; BallShape domain3d(0, 1); auto fill = [=](const Vec3d& p) { return (p[2] < 0.0) ? dx1 : dx2; }; // z-coordinate auto discretization3d = domain3d.discretizeBoundaryWithDensity(fill); // Make kd tree, iterate over the points on the boundary and average the distance between them. KDTree tree(discretization3d.positions()); for (int i = 0; i < discretization3d.size(); ++i) { if (discretization3d.pos(i, 2) < 0.0 - tol) { Range distances2 = tree.query(discretization3d.pos(i), 2).second; s1 += std::sqrt(distances2[1]); c1 += 1; } else if (discretization3d.pos(i, 2) > 0.0 + tol) { Range distances2 = tree.query(discretization3d.pos(i), 2).second; s2 += std::sqrt(distances2[1]); c2 += 1; } } EXPECT_NEAR(dx1, s1/c1, dx1*tol); // 5% error tolerance EXPECT_NEAR(dx2, s2/c2, dx2*tol); } TEST(Domains, BallShapeNormalsRandom1d) { BallShape cd1({3}, 1); auto discr1d = cd1.discretizeWithStep(0.1); for (int i : discr1d.boundary()) { Vec1d n = (discr1d.pos(i) - cd1.center()).normalized(); EXPECT_DOUBLE_EQ(n[0], discr1d.normal(i)[0]); } } TEST(Domains, BallShapeNormalsRandom2d) { BallShape cd2({-1, 2}, 5); auto discr2d = cd2.discretizeWithStep(0.1); double tol = 1e-15; for (int i : discr2d.boundary()) { Vec2d n = (discr2d.pos(i) - cd2.center()).normalized(); EXPECT_NEAR(n[0], discr2d.normal(i)[0], tol); EXPECT_NEAR(n[1], discr2d.normal(i)[1], tol); } } TEST(Domains, BallShapeNormalsRandom3d) { BallShape cd3({4, -2, 0}, 4); auto discr3d = cd3.discretizeBoundaryWithStep(0.2); double tol = 1e-15; for (int i : discr3d.boundary()) { Vec3d n = (discr3d.pos(i) - cd3.center()).normalized(); EXPECT_NEAR(n[0], discr3d.normal(i)[0], tol); EXPECT_NEAR(n[1], discr3d.normal(i)[1], tol); EXPECT_NEAR(n[2], discr3d.normal(i)[2], tol); } } TEST(Domains, BallDiscretize1d) { BallShape b(-1, 0.4); DomainDiscretization d = b.discretizeWithStep(0.1); auto p = d.positions(); std::sort(p.begin(), p.end()); std::vector expected = {-1.4, -1.3, -1.2, -1.1, -1, -0.9, -0.8, -0.7, -0.6}; double tol = 1e-15; ASSERT_EQ(expected.size(), p.size()); int n = expected.size(); for (int i = 0; i < n; ++i) { EXPECT_NEAR(expected[i], p[i][0], tol); } } TEST(Domains, BallDiscretize2d) { BallShape b({-1, 2.7}, 0.5); DomainDiscretization d = b.discretizeWithStep(0.1); auto p = d.positions(); int n = p.size(); for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { ASSERT_GE((p[i]-p[j]).norm(), 0.095); // due to integer number of nodes } } } TEST(Domains, BallDiscretize4d) { BallShape> b(0, 0.5); EXPECT_DEATH(b.discretizeWithStep(0.1), "This domain does not support filling with density"); auto fill = [=](const Vec&) { return 0.1; }; EXPECT_DEATH(b.discretizeBoundaryWithDensity(fill), "This domain does not support filling with density"); } TEST(Domains, BallDscretizeBoundaryWithStep4d) { typedef Vec vec; BallShape b(0, 1); double dx = 0.15; // Chosen to generate approximately 6000 points double tol = 0.05; double s = 0; double c = 0; auto d = b.discretizeBoundaryWithStep(dx); KDTree tree(d.positions()); for (int i = 0; i < d.size(); ++i) { Range distances2 = tree.query(d.pos(i), 2).second; s += std::sqrt(distances2[1]); c += 1; } EXPECT_NEAR(dx, s/c, dx*tol); // 5 % error tolerance } TEST(Domains, DISABLED_BallShapeUsageExample) { /// [BallShape usage example] BallShape ball({-1, 0}, 7); if (ball.contains({0.5, 1.3})) { // do something } auto d = ball.discretizeWithStep(0.1); std::cout << ball << std::endl; /// [BallShape usage example] (void) d; // remove unused warning } } // namespace mm