#include #include #include #include "gtest/gtest.h" namespace mm { /// Represents the Biharmonic operator template struct Biharmonic : public Operator> { typedef Vec vec; static std::string type_name() { return format("Biharmonic<%d>", dim); } static std::string name() { return type_name(); } template double applyAt0(const RBFBasis, vec>&, int index, const std::vector& support, double scale) const { static_assert(k != -1, "If dynamic k is desired it can be obtained from basis.rbf()."); double r = support[index].norm(); return k*(k-2)*(dim+k-2)*(dim+k-4)*ipow(r) / ipow<4>(scale); } double applyAt0(const Monomials& mon, int idx, const std::vector& q, double s) const { double result = 0; std::array orders; for (int d = 0; d < dim; ++d) { orders[d] = 0; } for (int d = 0; d < dim; ++d) { orders[d] = 4; result += mon.evalOpAt0(idx, Derivative(orders), q); orders[d] = 0; for (int d2 = 0; d2 < d; ++d2) { orders[d] = 2; orders[d2] = 2; result += 2*mon.evalOpAt0(idx, Derivative(orders), q); orders[d] = 0; orders[d2] = 0; } } return result / ipow<4>(s); } }; TEST(End2end, CustomOpBiharmonic) { typedef Vec vec; BallShape b(0.0, 1.0); double dx = 0.05; DomainDiscretization domain = b.discretizeBoundaryWithStep(dx); auto fn = [=](const vec&) { return dx; }; GeneralFill fill; fill.seed(1337); fill(domain, fn); auto gh = domain.addGhostNodes(dx); int N = domain.size(); Monomials mon(2); int n = 2*mon.size(); domain.findSupport(FindClosest(n)); RBFFD, vec, ScaleToClosest> approx({}, mon); std::tuple, Der1s<2>> ops; RaggedShapeStorage storage; storage.resize(domain.supportSizes()); computeShapes(domain, approx, domain.interior(), std::tuple>(), &storage); computeShapes(domain, approx, domain.boundary(), std::tuple>(), &storage); Eigen::SparseMatrix M(N, N); M.reserve(Range(N, n)); Eigen::VectorXd rhs(N); rhs.setZero(); auto op = storage.implicitOperators(M, rhs); for (int i : domain.interior()) { op.apply>(i) = 0.0; } for (int i : domain.boundary()) { op.neumann(i, domain.normal(i)) = 1.0; op.value(i, gh[i]) = 1.0; } Eigen::SparseLU solver; solver.compute(M); ScalarFieldd u = solver.solve(rhs); // analytical = 1/2*(1+sum(positions.^2))'; ScalarFieldd analytical(N); for (int i = 0; i < domain.size(); ++i) { analytical[i] = 0.5*(1+domain.pos(i).squaredNorm()); } double e = (u - analytical).lpNorm<1>() / analytical.lpNorm<1>(); EXPECT_LT(e, 1e-8); } } // namespace mm