27 Eigen::MatrixXd refined_nodes(
const int dim,
const int i)
29 Eigen::MatrixXd A(dim + 1, dim);
40 A.col(0).array() += 1;
43 A.col(1).array() += 1;
50 throw std::runtime_error(
"Invalid node index");
64 A.col(0).array() += 1;
67 A.col(1).array() += 1;
70 A.col(2).array() += 1;
74 Eigen::VectorXd
tmp = 1 - A.col(1).array() - A.col(2).array();
75 A.col(2) += A.col(0) + A.col(1);
81 Eigen::VectorXd
tmp = 1. - A.col(1).array();
89 Eigen::VectorXd
tmp = A.col(0) + A.col(1);
90 A.col(1) = 1. - A.col(0).array();
96 Eigen::VectorXd
tmp = 1. - A.col(0).array() - A.col(1).array();
102 throw std::runtime_error(
"Invalid node index");
112 std::tuple<Eigen::MatrixXd, std::vector<int>> extract_subelement(
const Eigen::MatrixXd &pts,
const Tree &tree)
115 return {pts, std::vector<int>{0}};
117 const int dim = pts.cols();
119 std::vector<int> levels;
123 uv.setZero(dim + 1, dim + 1);
124 uv.rightCols(dim) = refined_nodes(dim, i);
126 uv.col(0) = 1. - uv.col(2).array() - uv.col(1).array();
128 uv.col(0) = 1. - uv.col(3).array() - uv.col(1).array() - uv.col(2).array();
130 Eigen::MatrixXd pts_ = uv * pts;
132 auto [
tmp, L] = extract_subelement(pts_, tree.
child(i));
137 out.conservativeResize(out.rows() +
tmp.rows(), Eigen::NoChange);
138 out.bottomRows(
tmp.rows()) =
tmp;
142 levels.insert(levels.end(), L.begin(), L.end());
144 return {out, levels};
149 Eigen::MatrixXd pts(dim + 1, dim);
159 auto [quad_points, levels] = extract_subelement(pts, tree);
165 tri_quadrature.get_quadrature(order, tmp);
166 tmp.points.conservativeResize(
tmp.points.rows(), dim + 1);
167 tmp.points.col(dim) = 1. -
tmp.points.col(0).array() -
tmp.points.col(1).array();
172 tet_quadrature.get_quadrature(order, tmp);
173 tmp.points.conservativeResize(
tmp.points.rows(), dim + 1);
174 tmp.points.col(dim) = 1. -
tmp.points.col(0).array() -
tmp.points.col(1).array() -
tmp.points.col(2).array();
177 quad.
points.resize(
tmp.size() * levels.size(), dim);
178 quad.
weights.resize(
tmp.size() * levels.size());
180 for (
int i = 0; i < levels.size(); i++)
182 quad.
points.middleRows(i *
tmp.size(),
tmp.size()) =
tmp.points * quad_points.middleRows(i * (dim + 1), dim + 1);
183 quad.
weights.segment(i *
tmp.size(),
tmp.size()) =
tmp.weights / pow(2, dim * levels[i]);
185 assert(fabs(quad.
weights.sum() -
tmp.weights.sum()) < 1e-8);
223 const Quadrature quad = refine_quadrature(tree, dim, quad_order);
229 logger().debug(
"New number of quadrature points: {}, level: {}", quad.
size(), tree.
depth());
232 ass_vals_cache.
update(invalidID, dim == 3, bs, gbs);
236 ElasticForm::ElasticForm(
const int n_bases,
237 std::vector<basis::ElementBases> &bases,
238 const std::vector<basis::ElementBases> &geom_bases,
241 const double t,
const double dt,
242 const bool is_volume,
243 const double jacobian_threshold,
247 geom_bases_(geom_bases),
248 assembler_(assembler),
249 ass_vals_cache_(ass_vals_cache),
251 jacobian_threshold_(jacobian_threshold),
252 check_inversion_(check_inversion),
254 is_volume_(is_volume)
256 if (assembler_.is_linear())
257 compute_cached_stiffness();
259 mat_cache_ = std::make_unique<utils::SparseMatrixCache>();
260 quadrature_hierarchy_.resize(bases_.size());
262 quadrature_order_ =
AssemblerUtils::quadrature_order(assembler_.name(), bases_[0].bases[0].order(), AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, is_volume_ ? 3 : 2);
264 if (check_inversion_ != ElementInversionCheck::Discrete)
267 x0.setZero(n_bases_ * (is_volume_ ? 3 : 2));
268 if (!is_step_collision_free(x0, x0))
272 int gbasis_order = 0;
273 for (
int e = 0;
e < bases_.size();
e++)
275 if (basis_order == 0)
276 basis_order = bases_[
e].bases.front().order();
277 else if (basis_order != bases_[e].bases.front().order())
278 log_and_throw_error(
"Non-uniform basis order not supported for conservative Jacobian check!!");
279 if (gbasis_order == 0)
280 gbasis_order = geom_bases_[
e].bases.front().order();
281 else if (gbasis_order != geom_bases_[e].bases.front().order())
282 log_and_throw_error(
"Non-uniform gbasis order not supported for conservative Jacobian check!!");
287 double ElasticForm::value_unweighted(
const Eigen::VectorXd &
x)
const
289 return assembler_.assemble_energy(
291 bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
294 Eigen::VectorXd ElasticForm::value_per_element_unweighted(
const Eigen::VectorXd &
x)
const
296 const Eigen::VectorXd out = assembler_.assemble_energy_per_element(
297 is_volume_, bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
298 assert(abs(out.sum() - value_unweighted(
x)) < std::max(1e-10 * out.sum(), 1e-10));
302 void ElasticForm::first_derivative_unweighted(
const Eigen::VectorXd &
x, Eigen::VectorXd &gradv)
const
304 Eigen::MatrixXd
grad;
305 assembler_.assemble_gradient(is_volume_, n_bases_, bases_, geom_bases_,
306 ass_vals_cache_, t_, dt_,
x, x_prev_, grad);
310 void ElasticForm::second_derivative_unweighted(
const Eigen::VectorXd &
x,
StiffnessMatrix &hessian)
const
314 hessian.resize(
x.size(),
x.size());
316 if (assembler_.is_linear())
318 assert(cached_stiffness_.rows() ==
x.size() && cached_stiffness_.cols() ==
x.size());
319 hessian = cached_stiffness_;
324 assembler_.assemble_hessian(
325 is_volume_, n_bases_, project_to_psd_, bases_,
326 geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_, *mat_cache_, hessian);
330 void ElasticForm::finish()
332 for (
auto &t : quadrature_hierarchy_)
336 double ElasticForm::max_step_size(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
338 if (check_inversion_ == ElementInversionCheck::Discrete)
341 const int dim = is_volume_ ? 3 : 2;
342 double step, invalidStep;
345 Tree subdivision_tree;
347 double transient_check_time = 0;
350 std::tie(step, invalidID, invalidStep, subdivision_tree) =
max_time_step(dim, bases_, geom_bases_, x0, x1);
353 logger().log(step == 0 ? spdlog::level::warn : (step == 1. ?
spdlog::level::trace :
spdlog::level::debug),
354 "Jacobian max step size: {} at element {}, invalid step size: {}, tree depth {}, runtime {} sec", step, invalidID, invalidStep, subdivision_tree.depth(), transient_check_time);
357 if (invalidID >= 0 && step <= 0.5)
359 auto &bs = bases_[invalidID];
360 auto &gbs = geom_bases_[invalidID];
361 if (quadrature_hierarchy_[invalidID].merge(subdivision_tree))
362 update_quadrature(invalidID, dim, quadrature_hierarchy_[invalidID], quadrature_order_, bs, gbs, ass_vals_cache_);
379 bool ElasticForm::is_step_collision_free(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
381 if (check_inversion_ == ElementInversionCheck::Discrete)
384 const auto [isvalid, id, tree] =
is_valid(is_volume_ ? 3 : 2, bases_, geom_bases_, x1);
388 bool ElasticForm::is_step_valid(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
391 Eigen::VectorXd
grad;
392 first_derivative(x1, grad);
393 if (
grad.array().isNaN().any())
407 void ElasticForm::solution_changed(
const Eigen::VectorXd &new_x)
411 void ElasticForm::compute_cached_stiffness()
413 if (assembler_.is_linear() && cached_stiffness_.size() == 0)
415 assembler_.assemble(is_volume_, n_bases_, bases_, geom_bases_,
416 ass_vals_cache_, t_, cached_stiffness_);
#define POLYFEM_SCOPED_TIMER(...)
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
Caches basis evaluation and geometric mapping at every element.
bool is_initialized() const
void update(const int el_index, const bool is_volume, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void set_quadrature(const QuadratureFunction &fun)
bool has_children() const
std::tuple< bool, int, Tree > is_valid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const double threshold)
std::tuple< double, int, double, Tree > max_time_step(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u1, const Eigen::VectorXd &u2, double precision)
spdlog::logger & logger()
Retrieves the current logger.
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix