#ifndef SIMPLEX_MESH_POISSON_H #define SIMPLEX_MESH_POISSON_H #include #include #include #include #include /** This is a class that solves poisson equation -\nabla u = f. Discretization: mat* x =b. **/ namespace ZIRAN { template class SimplexMeshPoisson { public: typedef Vector TV; typedef Matrix TM; typedef Vector VEC; SimplexMesh& tet_mesh; StdVector& X; StdVector element_measure; StdVector b_scale; StdVector> grad_N; T unit_simplex_measure; Eigen::SparseMatrix mat; int n; //The total number of dofs VEC& x; VEC& b; StdVector& dirichlet; StdVector& dirichlet_value; SimplexMeshPoisson(SimplexMesh& mesh, StdVector& X, VEC& x, VEC& b, StdVector& dirichlet_in, StdVector& dirichlet_value_in) : tet_mesh(mesh) , X(X) , element_measure(mesh.numberElements()) , b_scale(mesh.numberElements()) , grad_N(mesh.numberElements()) , n(X.size()) , x(x) , b(b) , dirichlet(dirichlet_in) , dirichlet_value(dirichlet_value_in) { ZIRAN_ASSERT(n > 0); T d_factorial = (T)1; for (int i = 0; i < dim; i++) d_factorial *= (T)(i + 1); T unit_simplex_measure = (T)1 / d_factorial; //set up Dm_inverse and interpolating function derivatives (wrt rest configuration) TM dm; T scale = (T)1 / (T)((dim + 1) * (dim + 2)); for (size_t e = 0; e < tet_mesh.numberElements(); e++) { dS(e, dm); element_measure[e] = unit_simplex_measure * dm.determinant(); b_scale[e] = element_measure[e] * scale; TM dm_inv_trans = dm.inverse().transpose(); grad_N[e] << -dm_inv_trans.rowwise().sum(), dm_inv_trans; } //build matrix mat.resize(n, n); mat.reserve(Eigen::VectorXi::Constant(n, 27).transpose()); for (size_t e = 0; e < tet_mesh.numberElements(); e++) { for (int i = 0; i <= dim; i++) { for (int j = 0; j <= dim; j++) { mat.coeffRef(tet_mesh.index(e, i), tet_mesh.index(e, j)) += grad_N[e].col(i).dot(grad_N[e].col(j)) * element_measure[e]; } } } mat.makeCompressed(); initialize_x(); } void initialize_x() { x.setZero(); for (size_t i = 0; i < dirichlet.size(); i++) x(dirichlet[i]) = dirichlet_value[i]; } void dS(const size_t element, TM& result) { for (int i = 0; i < dim; i++) result.col(i) = X[tet_mesh.index(element, i + 1)] - X[tet_mesh.index(element, 0)]; } //set b based on the analytic rhs void setRHS(const VEC& rhs) { b.resize(X.size()); b.setZero(); for (size_t e = 0; e < tet_mesh.numberElements(); e++) { for (int i = 0; i <= dim; i++) { for (int j = 0; j <= dim; j++) { T this_scale = b_scale[e] * ((i == j) + 1); b(tet_mesh.index(e, i)) += this_scale * rhs(tet_mesh.index(e, j)); } } } } int solve() { EigenSparseKrylovMatrix eska(mat); DirichletDofProjection projection_function(dirichlet); eska.setProjection(projection_function); JacobiPreconditioner preconditioner(mat); eska.setPreconditioner(preconditioner); Minres, VEC> minres(10000); return minres.solve(eska, x, b, false); } //after solving the poission for \u(x), get \grad \u(x) for each tetrahedron void findGradient(StdVector>& gradPhi) { gradPhi.resize(tet_mesh.numberElements()); for (size_t e = 0; e < tet_mesh.numberElements(); e++) { gradPhi[e].setZero(); for (int i = 0; i <= dim; i++) gradPhi[e] += x(tet_mesh.index(e, i)) * grad_N[e].col(i); } } //after solving the poission for \u(x), get normalized \grad \u(x) for each tetrahedron void findNormalizedGradient(StdVector>& gradPhi) { gradPhi.resize(tet_mesh.numberElements()); for (size_t e = 0; e < tet_mesh.numberElements(); e++) { gradPhi[e].setZero(); for (int i = 0; i <= dim; i++) gradPhi[e] += x(tet_mesh.index(e, i)) * grad_N[e].col(i); if (gradPhi[e].norm() < 16 * std::numeric_limits::epsilon()) gradPhi[e].setZero(); else gradPhi[e].normalize(); } } }; } // namespace ZIRAN #endif