21 class LocalThreadVecStorage
28 LocalThreadVecStorage(
const int size)
35 Eigen::MatrixXd refined_nodes(
const int dim,
const int i)
37 Eigen::MatrixXd A(dim + 1, dim);
48 A.col(0).array() += 1;
51 A.col(1).array() += 1;
58 throw std::runtime_error(
"Invalid node index");
72 A.col(0).array() += 1;
75 A.col(1).array() += 1;
78 A.col(2).array() += 1;
82 Eigen::VectorXd
tmp = 1 - A.col(1).array() - A.col(2).array();
83 A.col(2) += A.col(0) + A.col(1);
89 Eigen::VectorXd
tmp = 1. - A.col(1).array();
97 Eigen::VectorXd
tmp = A.col(0) + A.col(1);
98 A.col(1) = 1. - A.col(0).array();
104 Eigen::VectorXd
tmp = 1. - A.col(0).array() - A.col(1).array();
105 A.col(1) += A.col(2);
110 throw std::runtime_error(
"Invalid node index");
120 std::tuple<Eigen::MatrixXd, std::vector<int>> extract_subelement(
const Eigen::MatrixXd &pts,
const Tree &tree)
123 return {pts, std::vector<int>{0}};
125 const int dim = pts.cols();
127 std::vector<int> levels;
131 uv.setZero(dim+1, dim+1);
132 uv.rightCols(dim) = refined_nodes(dim, i);
134 uv.col(0) = 1. - uv.col(2).array() - uv.col(1).array();
136 uv.col(0) = 1. - uv.col(3).array() - uv.col(1).array() - uv.col(2).array();
138 Eigen::MatrixXd pts_ = uv * pts;
140 auto [
tmp, L] = extract_subelement(pts_, tree.
child(i));
145 out.conservativeResize(out.rows() +
tmp.rows(), Eigen::NoChange);
146 out.bottomRows(
tmp.rows()) =
tmp;
150 levels.insert(levels.end(), L.begin(), L.end());
152 return {out, levels};
157 Eigen::MatrixXd pts(dim + 1, dim);
167 auto [quad_points, levels] = extract_subelement(pts, tree);
173 tri_quadrature.get_quadrature(order, tmp);
174 tmp.points.conservativeResize(
tmp.points.rows(), dim + 1);
175 tmp.points.col(dim) = 1. -
tmp.points.col(0).array() -
tmp.points.col(1).array();
180 tet_quadrature.get_quadrature(order, tmp);
181 tmp.points.conservativeResize(
tmp.points.rows(), dim + 1);
182 tmp.points.col(dim) = 1. -
tmp.points.col(0).array() -
tmp.points.col(1).array() -
tmp.points.col(2).array();
185 quad.
points.resize(
tmp.size() * levels.size(), dim);
186 quad.
weights.resize(
tmp.size() * levels.size());
188 for (
int i = 0; i < levels.size(); i++)
190 quad.
points.middleRows(i *
tmp.size(),
tmp.size()) =
tmp.points * quad_points.middleRows(i * (dim + 1), dim + 1);
191 quad.
weights.segment(i *
tmp.size(),
tmp.size()) =
tmp.weights / pow(2, dim * levels[i]);
193 assert (fabs(quad.
weights.sum() -
tmp.weights.sum()) < 1e-8);
231 const Quadrature quad = refine_quadrature(tree, dim, quad_order);
237 logger().debug(
"New number of quadrature points: {}, level: {}", quad.
size(), tree.
depth());
240 ass_vals_cache.
update(invalidID, dim == 3, bs, gbs);
244 ElasticForm::ElasticForm(
const int n_bases,
245 std::vector<basis::ElementBases> &bases,
246 const std::vector<basis::ElementBases> &geom_bases,
249 const double t,
const double dt,
250 const bool is_volume,
251 const double jacobian_threshold,
255 geom_bases_(geom_bases),
256 assembler_(assembler),
257 ass_vals_cache_(ass_vals_cache),
259 jacobian_threshold_(jacobian_threshold),
260 check_inversion_(check_inversion),
262 is_volume_(is_volume)
264 if (assembler_.is_linear())
265 compute_cached_stiffness();
267 mat_cache_ = std::make_unique<utils::SparseMatrixCache>();
268 quadrature_hierarchy_.resize(bases_.size());
270 quadrature_order_ =
AssemblerUtils::quadrature_order(assembler_.name(), bases_[0].bases[0].order(), AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, is_volume_ ? 3 : 2);
272 if (check_inversion_ != ElementInversionCheck::Discrete)
275 x0.setZero(n_bases_ * (is_volume_ ? 3 : 2));
276 if (!is_step_collision_free(x0, x0))
280 int gbasis_order = 0;
281 for (
int e = 0;
e < bases_.size();
e++)
283 if (basis_order == 0)
284 basis_order = bases_[
e].bases.front().order();
285 else if (basis_order != bases_[e].bases.front().order())
286 log_and_throw_error(
"Non-uniform basis order not supported for conservative Jacobian check!!");
287 if (gbasis_order == 0)
288 gbasis_order = geom_bases_[
e].bases.front().order();
289 else if (gbasis_order != geom_bases_[e].bases.front().order())
290 log_and_throw_error(
"Non-uniform gbasis order not supported for conservative Jacobian check!!");
295 double ElasticForm::value_unweighted(
const Eigen::VectorXd &
x)
const
297 return assembler_.assemble_energy(
299 bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
302 Eigen::VectorXd ElasticForm::value_per_element_unweighted(
const Eigen::VectorXd &
x)
const
304 const Eigen::VectorXd out = assembler_.assemble_energy_per_element(
305 is_volume_, bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
306 assert(abs(out.sum() - value_unweighted(
x)) < std::max(1e-10 * out.sum(), 1e-10));
310 void ElasticForm::first_derivative_unweighted(
const Eigen::VectorXd &
x, Eigen::VectorXd &gradv)
const
312 Eigen::MatrixXd
grad;
313 assembler_.assemble_gradient(is_volume_, n_bases_, bases_, geom_bases_,
314 ass_vals_cache_, t_, dt_,
x, x_prev_, grad);
318 void ElasticForm::second_derivative_unweighted(
const Eigen::VectorXd &
x,
StiffnessMatrix &hessian)
const
322 hessian.resize(
x.size(),
x.size());
324 if (assembler_.is_linear())
326 assert(cached_stiffness_.rows() ==
x.size() && cached_stiffness_.cols() ==
x.size());
327 hessian = cached_stiffness_;
332 assembler_.assemble_hessian(
333 is_volume_, n_bases_, project_to_psd_, bases_,
334 geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_, *mat_cache_, hessian);
338 void ElasticForm::finish()
340 for (
auto &t : quadrature_hierarchy_)
344 double ElasticForm::max_step_size(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
346 if (check_inversion_ == ElementInversionCheck::Discrete)
349 const int dim = is_volume_ ? 3 : 2;
350 double step, invalidStep;
353 Tree subdivision_tree;
355 double transient_check_time = 0;
358 std::tie(step, invalidID, invalidStep, subdivision_tree) =
max_time_step(dim, bases_, geom_bases_, x0, x1);
361 logger().log(step == 0 ? spdlog::level::warn : (step == 1. ?
spdlog::level::trace :
spdlog::level::debug),
362 "Jacobian max step size: {} at element {}, invalid step size: {}, tree depth {}, runtime {} sec", step, invalidID, invalidStep, subdivision_tree.depth(), transient_check_time);
365 if (invalidID >= 0 && step <= 0.5)
367 auto& bs = bases_[invalidID];
368 auto& gbs = geom_bases_[invalidID];
369 if (quadrature_hierarchy_[invalidID].merge(subdivision_tree))
370 update_quadrature(invalidID, dim, quadrature_hierarchy_[invalidID], quadrature_order_, bs, gbs, ass_vals_cache_);
387 bool ElasticForm::is_step_collision_free(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
389 if (check_inversion_ == ElementInversionCheck::Discrete)
392 const auto [isvalid, id, tree] =
is_valid(is_volume_ ? 3 : 2, bases_, geom_bases_, x1);
396 bool ElasticForm::is_step_valid(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
399 Eigen::VectorXd
grad;
400 first_derivative(x1, grad);
401 if (
grad.array().isNaN().any())
415 void ElasticForm::solution_changed(
const Eigen::VectorXd &new_x)
419 void ElasticForm::compute_cached_stiffness()
421 if (assembler_.is_linear() && cached_stiffness_.size() == 0)
423 assembler_.assemble(is_volume_, n_bases_, bases_, geom_bases_,
424 ass_vals_cache_, t_, cached_stiffness_);
428 void ElasticForm::force_material_derivative(
const double t,
const Eigen::MatrixXd &
x,
const Eigen::MatrixXd &x_prev,
const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
430 const int dim = is_volume_ ? 3 : 2;
432 const int n_elements = int(bases_.size());
434 if (assembler_.name() ==
"ViscousDamping")
438 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
440 utils::maybe_parallel_for(n_elements, [&](
int start,
int end,
int thread_id) {
441 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
443 for (
int e = start;
e < end; ++
e)
446 ass_vals_cache_.compute(e, is_volume_, bases_[e], geom_bases_[e],
vals);
451 Eigen::MatrixXd u, grad_u, prev_u, prev_grad_u, p, grad_p;
452 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals,
x, u, grad_u);
453 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals, x_prev, prev_u, prev_grad_u);
454 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals, adjoint, p, grad_p);
456 for (
int q = 0; q < local_storage.da.size(); ++q)
458 Eigen::MatrixXd grad_p_i, grad_u_i, prev_grad_u_i;
463 Eigen::MatrixXd f_prime_dpsi, f_prime_dphi;
464 assembler::ViscousDamping::compute_dstress_dpsi_dphi(
OptAssemblerData(t, dt_, e,
quadrature.
points.row(q),
vals.val.row(q), grad_u_i), prev_grad_u_i, f_prime_dpsi, f_prime_dphi);
467 local_storage.vec(0) += -matrix_inner_product<double>(f_prime_dpsi, grad_p_i) * local_storage.da(q);
468 local_storage.vec(1) += -matrix_inner_product<double>(f_prime_dphi, grad_p_i) * local_storage.da(q);
473 for (
const LocalThreadVecStorage &local_storage : storage)
474 term += local_storage.
vec;
478 term.setZero(n_elements * 2, 1);
480 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
482 utils::maybe_parallel_for(n_elements, [&](
int start,
int end,
int thread_id) {
483 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
485 for (
int e = start;
e < end; ++
e)
488 ass_vals_cache_.compute(e, is_volume_, bases_[e], geom_bases_[e],
vals);
493 Eigen::MatrixXd u, grad_u, p, grad_p;
494 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals,
x, u, grad_u);
495 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals, adjoint, p, grad_p);
497 for (
int q = 0; q < local_storage.da.size(); ++q)
499 Eigen::MatrixXd grad_p_i, grad_u_i;
503 Eigen::MatrixXd f_prime_dmu, f_prime_dlambda;
507 local_storage.vec(e + n_elements) += -matrix_inner_product<double>(f_prime_dmu, grad_p_i) * local_storage.da(q);
508 local_storage.vec(e) += -matrix_inner_product<double>(f_prime_dlambda, grad_p_i) * local_storage.da(q);
513 for (
const LocalThreadVecStorage &local_storage : storage)
514 term += local_storage.
vec;
518 void ElasticForm::force_shape_derivative(
const double t,
const int n_verts,
const Eigen::MatrixXd &
x,
const Eigen::MatrixXd &x_prev,
const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
520 const int dim = is_volume_ ? 3 : 2;
521 const int actual_dim = (assembler_.name() ==
"Laplacian") ? 1 :
dim;
523 const int n_elements = int(bases_.size());
524 term.setZero(n_verts * dim, 1);
526 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
528 if (assembler_.name() ==
"ViscousDamping")
530 utils::maybe_parallel_for(n_elements, [&](
int start,
int end,
int thread_id) {
531 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
533 for (
int e = start;
e < end; ++
e)
536 ass_vals_cache_.compute(e, is_volume_, bases_[e], geom_bases_[e],
vals);
538 gvals.
compute(e, is_volume_,
vals.quadrature.points, geom_bases_[e], geom_bases_[e]);
543 Eigen::MatrixXd u, grad_u, prev_u, prev_grad_u, p, grad_p;
544 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals,
x, u, grad_u);
545 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals, x_prev, prev_u, prev_grad_u);
546 io::Evaluator::interpolate_at_local_vals(e, dim, dim,
vals, adjoint, p, grad_p);
548 Eigen::MatrixXd grad_u_i, grad_p_i, prev_grad_u_i;
549 Eigen::MatrixXd grad_v_i;
550 Eigen::MatrixXd stress_tensor, f_prime_gradu_gradv;
551 Eigen::MatrixXd f_prev_prime_prev_gradu_gradv;
553 for (
int q = 0; q < local_storage.da.size(); ++q)
559 for (
auto &v :
gvals.basis_values)
561 Eigen::MatrixXd stress_grad, stress_prev_grad;
564 for (
int d = 0; d <
dim; d++)
566 grad_v_i.setZero(dim, dim);
567 grad_v_i.row(d) = v.grad_t_m.row(q);
569 f_prime_gradu_gradv.setZero(dim, dim);
570 Eigen::MatrixXd
tmp = grad_u_i * grad_v_i;
571 for (
int i = 0; i < f_prime_gradu_gradv.rows(); i++)
572 for (
int j = 0; j < f_prime_gradu_gradv.cols(); j++)
573 for (
int k = 0; k <
tmp.rows(); k++)
574 for (
int l = 0; l <
tmp.cols(); l++)
575 f_prime_gradu_gradv(i, j) += stress_grad(i * dim + j, k * dim + l) *
tmp(k, l);
577 f_prev_prime_prev_gradu_gradv.setZero(dim, dim);
578 tmp = prev_grad_u_i * grad_v_i;
579 for (
int i = 0; i < f_prev_prime_prev_gradu_gradv.rows(); i++)
580 for (
int j = 0; j < f_prev_prime_prev_gradu_gradv.cols(); j++)
581 for (
int k = 0; k <
tmp.rows(); k++)
582 for (
int l = 0; l <
tmp.cols(); l++)
583 f_prev_prime_prev_gradu_gradv(i, j) += stress_prev_grad(i * dim + j, k * dim + l) *
tmp(k, l);
585 tmp = grad_v_i - grad_v_i.trace() * Eigen::MatrixXd::Identity(dim, dim);
586 local_storage.vec(v.global[0].index * dim + d) -= matrix_inner_product<double>(f_prime_gradu_gradv + f_prev_prime_prev_gradu_gradv + stress_tensor *
tmp.transpose(), grad_p_i) * local_storage.da(q);
595 utils::maybe_parallel_for(n_elements, [&](
int start,
int end,
int thread_id) {
596 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
598 for (
int e = start;
e < end; ++
e)
601 ass_vals_cache_.compute(e, is_volume_, bases_[e], geom_bases_[e],
vals);
603 gvals.
compute(e, is_volume_,
vals.quadrature.points, geom_bases_[e], geom_bases_[e]);
608 Eigen::MatrixXd u, grad_u, p, grad_p;
609 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim,
vals,
x, u, grad_u);
610 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim,
vals, adjoint, p, grad_p);
613 for (
int q = 0; q < local_storage.da.size(); ++q)
615 Eigen::MatrixXd grad_u_i, grad_p_i, stiffness_i;
618 grad_u_i = grad_u.row(q);
619 grad_p_i = grad_p.row(q);
629 for (
auto &v :
gvals.basis_values)
631 for (
int d = 0; d <
dim; d++)
633 Eigen::MatrixXd grad_v_i;
634 grad_v_i.setZero(dim, dim);
635 grad_v_i.row(d) = v.grad_t_m.row(q);
637 Eigen::MatrixXd stress_tensor, f_prime_gradu_gradv;
638 assembler_.compute_stress_grad_multiply_mat(
OptAssemblerData(t, dt_, e,
quadrature.
points.row(q),
vals.val.row(q), grad_u_i), grad_u_i * grad_v_i, stress_tensor, f_prime_gradu_gradv);
641 Eigen::MatrixXd
tmp = grad_v_i - grad_v_i.trace() * Eigen::MatrixXd::Identity(dim, dim);
642 local_storage.vec(v.global[0].index * dim + d) -= matrix_inner_product<double>(f_prime_gradu_gradv + stress_tensor *
tmp.transpose(), grad_p_i) * local_storage.da(q);
650 for (
const LocalThreadVecStorage &local_storage : storage)
651 term += local_storage.
vec;
ElementAssemblyValues vals
assembler::ElementAssemblyValues gvals
#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 per element basis values at given quadrature points and geometric mapping
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
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)
void vector2matrix(const Eigen::VectorXd &vec, Eigen::MatrixXd &mat)
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.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix