28 Eigen::MatrixXd refined_nodes(
const int dim,
const int i)
30 Eigen::MatrixXd A(dim + 1, dim);
41 A.col(0).array() += 1;
44 A.col(1).array() += 1;
51 throw std::runtime_error(
"Invalid node index");
65 A.col(0).array() += 1;
68 A.col(1).array() += 1;
71 A.col(2).array() += 1;
75 Eigen::VectorXd
tmp = 1. - A.col(0).array() - A.col(1).array();
83 Eigen::VectorXd
tmp = 1. - A.col(0).array();
90 Eigen::VectorXd tmp0 = A.col(0);
91 Eigen::VectorXd tmp1 = A.col(1);
93 A.col(1) = 1. - tmp0.array() - tmp1.array();
94 A.col(2) += tmp0 + tmp1;
99 Eigen::VectorXd
tmp = A.col(1);
101 A.col(1) = 1. -
tmp.array();
106 throw std::runtime_error(
"Invalid node index");
116 std::tuple<Eigen::MatrixXd, std::vector<int>> extract_subelement(
const Eigen::MatrixXd &pts,
const Tree &tree)
119 return {pts, std::vector<int>{0}};
121 const int dim = pts.cols();
123 std::vector<int> levels;
127 uv.setZero(dim + 1, dim + 1);
128 uv.rightCols(dim) = refined_nodes(dim, i);
130 uv.col(0) = 1. - uv.col(2).array() - uv.col(1).array();
132 uv.col(0) = 1. - uv.col(3).array() - uv.col(1).array() - uv.col(2).array();
134 Eigen::MatrixXd pts_ = uv * pts;
136 auto [
tmp, L] = extract_subelement(pts_, tree.
child(i));
141 out.conservativeResize(out.rows() +
tmp.rows(), Eigen::NoChange);
142 out.bottomRows(
tmp.rows()) =
tmp;
146 levels.insert(levels.end(), L.begin(), L.end());
148 return {out, levels};
153 Eigen::MatrixXd pts(dim + 1, dim);
163 auto [quad_points, levels] = extract_subelement(pts, tree);
169 tri_quadrature.get_quadrature(order, tmp);
170 tmp.points.conservativeResize(
tmp.points.rows(), dim + 1);
171 tmp.points.col(dim) = 1. -
tmp.points.col(0).array() -
tmp.points.col(1).array();
179 const int safe_order = (order == 4) ? 5 : order;
180 tet_quadrature.get_quadrature(safe_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);
201 const Quadrature quad = refine_quadrature(tree, dim, quad_order);
207 logger().debug(
"New number of quadrature points: {}, level: {}", quad.
size(), tree.
depth());
210 ass_vals_cache.
update(invalidID, dim == 3, bs, gbs);
214 ElasticForm::ElasticForm(
const int n_bases,
215 std::vector<basis::ElementBases> &bases,
216 const std::vector<basis::ElementBases> &geom_bases,
219 const double t,
const double dt,
220 const bool is_volume,
221 const double jacobian_threshold,
223 const unsigned conservative_max_iter)
226 geom_bases_(geom_bases),
227 assembler_(assembler),
228 ass_vals_cache_(ass_vals_cache),
230 jacobian_threshold_(jacobian_threshold),
231 check_inversion_(check_inversion),
232 conservative_max_iter_(conservative_max_iter),
234 is_volume_(is_volume)
236 if (assembler_.is_linear())
237 compute_cached_stiffness();
239 mat_cache_ = std::make_unique<utils::SparseMatrixCache>();
240 quadrature_hierarchy_.resize(bases_.size());
242 quadrature_order_ =
AssemblerUtils::quadrature_order(assembler_.name(), bases_[0].bases[0].order(), AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, is_volume_ ? 3 : 2);
244 if (check_inversion_ != ElementInversionCheck::Discrete)
247 x0.setZero(n_bases_ * (is_volume_ ? 3 : 2));
248 if (!is_step_collision_free(x0, x0))
252 int gbasis_order = 0;
253 for (
int e = 0;
e < bases_.size();
e++)
255 if (basis_order == 0)
256 basis_order = bases_[
e].bases.front().order();
257 else if (basis_order != bases_[e].bases.front().order())
258 log_and_throw_error(
"Non-uniform basis order not supported for conservative Jacobian check!!");
259 if (gbasis_order == 0)
260 gbasis_order = geom_bases_[
e].bases.front().order();
261 else if (gbasis_order != geom_bases_[e].bases.front().order())
262 log_and_throw_error(
"Non-uniform gbasis order not supported for conservative Jacobian check!!");
269 const int dim = is_volume_ ? 3 : 2;
270 for (
int e = 0;
e < (int)bases_.size();
e++)
271 update_quadrature(e, dim, quadrature_hierarchy_[e], quadrature_order_, bases_[e], geom_bases_[e], ass_vals_cache_);
275 double ElasticForm::value_unweighted(
const Eigen::VectorXd &
x)
const
277 return assembler_.assemble_energy(
279 bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
282 Eigen::VectorXd ElasticForm::value_per_element_unweighted(
const Eigen::VectorXd &
x)
const
284 const Eigen::VectorXd out = assembler_.assemble_energy_per_element(
285 is_volume_, bases_, geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_);
286 assert(abs(out.sum() - value_unweighted(
x)) < std::max(1e-10 * out.sum(), 1e-10));
290 void ElasticForm::first_derivative_unweighted(
const Eigen::VectorXd &
x, Eigen::VectorXd &gradv)
const
292 Eigen::MatrixXd
grad;
293 assembler_.assemble_gradient(is_volume_, n_bases_, bases_, geom_bases_,
294 ass_vals_cache_, t_, dt_,
x, x_prev_, grad);
298 void ElasticForm::second_derivative_unweighted(
const Eigen::VectorXd &
x,
StiffnessMatrix &hessian)
const
302 hessian.resize(
x.size(),
x.size());
304 if (assembler_.is_linear())
306 assert(cached_stiffness_.rows() ==
x.size() && cached_stiffness_.cols() ==
x.size());
307 hessian = cached_stiffness_;
312 assembler_.assemble_hessian(
313 is_volume_, n_bases_, project_to_psd_, bases_,
314 geom_bases_, ass_vals_cache_, t_, dt_,
x, x_prev_, *mat_cache_, hessian);
318 void ElasticForm::finish()
320 for (
auto &t : quadrature_hierarchy_)
322 pending_refinement_.reset();
325 double ElasticForm::max_step_size(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
327 if (check_inversion_ == ElementInversionCheck::Discrete)
330 const int dim = is_volume_ ? 3 : 2;
332 double step, invalidStep;
335 Tree subdivision_tree;
337 double transient_check_time = 0;
340 std::tie(step, invalidID, invalidStep, subdivision_tree) =
max_time_step(dim, bases_, geom_bases_, x0, x1, .25, 0., conservative_max_iter_);
343 logger().log(step == 0 ? spdlog::level::err : (step == 1. ?
spdlog::level::trace :
spdlog::level::debug),
344 "Jacobian max step size: {} at element {}, invalid step size: {}, runtime {} sec, tree depth {}", step, invalidID, invalidStep, transient_check_time, subdivision_tree.depth());
347 if (invalidID >= 0 && step == 0)
354 commit_refinement(invalidID, subdivision_tree);
355 pending_refinement_.reset();
357 else if (invalidID >= 0 && step <= 0.5)
361 pending_refinement_ = std::make_pair(invalidID, std::move(subdivision_tree));
365 pending_refinement_.reset();
371 bool ElasticForm::is_step_collision_free(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
373 if (check_inversion_ == ElementInversionCheck::Discrete)
376 const auto [isvalid, id, tree] =
is_valid(is_volume_ ? 3 : 2, bases_, geom_bases_, x1, 0., conservative_max_iter_);
380 bool ElasticForm::is_step_valid(
const Eigen::VectorXd &x0,
const Eigen::VectorXd &x1)
const
383 Eigen::VectorXd
grad;
384 first_derivative(x1, grad);
385 if (
grad.array().isNaN().any())
399 void ElasticForm::post_step(
const polysolve::nonlinear::PostStepData &data)
401 if (!pending_refinement_.has_value())
404 auto [id, subdivision_tree] = *pending_refinement_;
405 commit_refinement(
id, subdivision_tree);
407 pending_refinement_.reset();
410 void ElasticForm::commit_refinement(
const int id,
utils::Tree &subdivision_tree)
const
412 const int dim = is_volume_ ? 3 : 2;
416 if (quadrature_hierarchy_[
id].merge(subdivision_tree))
418 auto &bs = bases_[id];
419 auto &gbs = geom_bases_[id];
420 update_quadrature(
id, dim, quadrature_hierarchy_[
id], quadrature_order_, bs, gbs, ass_vals_cache_);
424 void ElasticForm::compute_cached_stiffness()
426 if (assembler_.is_linear() && cached_stiffness_.size() == 0)
428 assembler_.assemble(is_volume_, n_bases_, bases_, geom_bases_,
429 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, const unsigned max_iter)
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, double threshold, const unsigned max_iter)
spdlog::logger & logger()
Retrieves the current logger.
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix