8#include <ipc/utils/eigen_ext.hpp>
15 using namespace basis;
17 using namespace utils;
21 class LocalThreadMatStorage
24 std::unique_ptr<MatrixCache>
cache =
nullptr;
25 ElementAssemblyValues
vals;
28 LocalThreadMatStorage() =
delete;
30 LocalThreadMatStorage(
const int buffer_size,
const int rows,
const int cols)
32 init(buffer_size, rows, cols);
35 LocalThreadMatStorage(
const int buffer_size,
const MatrixCache &c)
40 LocalThreadMatStorage(
const LocalThreadMatStorage &other)
45 LocalThreadMatStorage &operator=(
const LocalThreadMatStorage &other)
47 assert(other.cache !=
nullptr);
48 cache = other.cache->copy();
54 void init(
const int buffer_size,
const int rows,
const int cols)
58 cache = std::make_unique<SparseMatrixCache>();
59 cache->reserve(buffer_size);
60 cache->init(rows, cols);
63 void init(
const int buffer_size,
const MatrixCache &c)
67 cache->reserve(buffer_size);
72 class LocalThreadVecStorage
76 ElementAssemblyValues
vals;
79 LocalThreadVecStorage(
const int size)
86 class LocalThreadScalarStorage
90 ElementAssemblyValues
vals;
93 LocalThreadScalarStorage()
99 void split_mixed_solution(
101 const Eigen::MatrixXd &
x,
104 Eigen::MatrixXd &x_phi,
105 Eigen::MatrixXd &x_psi)
107 assert(split_solution);
109 split_solution(
x, x_phi, x_psi);
111 assert(x_phi.rows() == phi_ndof);
112 assert(x_phi.cols() == 1);
113 assert(x_psi.rows() == psi_ndof);
114 assert(x_psi.cols() == 1);
117 void split_mixed_previous_solution(
119 const Eigen::MatrixXd &
x,
122 Eigen::MatrixXd &x_phi,
123 Eigen::MatrixXd &x_psi)
132 split_mixed_solution(split_solution,
x, phi_ndof, psi_ndof, x_phi, x_psi);
135 void compute_mixed_element_values(
137 const bool is_volume,
138 const ElementBases &psi_basis,
139 const ElementBases &phi_basis,
140 const ElementBases &gbasis,
141 const AssemblyValsCache &psi_cache,
142 const AssemblyValsCache &phi_cache,
143 ElementAssemblyValues &psi_vals,
144 ElementAssemblyValues &phi_vals)
146 psi_cache.compute(el_index, is_volume, psi_basis, gbasis, psi_vals);
147 phi_cache.compute(el_index, is_volume, phi_basis, gbasis, phi_vals);
149 const auto same_quadrature = [&]() {
150 if (psi_vals.quadrature.weights.size() != phi_vals.quadrature.weights.size())
152 if (psi_vals.quadrature.points.rows() != phi_vals.quadrature.points.rows()
153 || psi_vals.quadrature.points.cols() != phi_vals.quadrature.points.cols())
155 return (psi_vals.quadrature.points - phi_vals.quadrature.points).cwiseAbs().maxCoeff() < 1
e-14;
158 if (same_quadrature())
161 if (phi_vals.quadrature.weights.size() >= psi_vals.quadrature.weights.size())
163 const Quadrature
quadrature = phi_vals.quadrature;
164 psi_vals.compute(el_index, is_volume,
quadrature.
points, psi_basis, gbasis);
169 const Quadrature
quadrature = psi_vals.quadrature;
170 phi_vals.compute(el_index, is_volume,
quadrature.
points, phi_basis, gbasis);
174 assert(psi_vals.quadrature.weights.size() == phi_vals.quadrature.weights.size());
175 assert(psi_vals.quadrature.points.rows() == phi_vals.quadrature.points.rows());
181 if (!body_params.is_array())
187 std::map<int, json> materials;
188 for (
int i = 0; i < body_params.size(); ++i)
190 json mat = body_params[i];
194 for (
int j = 0; j <
id.size(); ++j)
195 materials[
id[j]] = mat;
200 materials[mid] = mat;
204 std::set<int> missing;
206 std::map<int, int> body_element_count;
207 std::vector<int> eid_to_eid_in_body(body_ids.size());
208 for (
int e = 0; e < body_ids.size(); ++e)
210 const int bid = body_ids[e];
211 body_element_count.try_emplace(bid, 0);
212 eid_to_eid_in_body[e] = body_element_count[bid]++;
215 for (
int e = 0; e < body_ids.size(); ++e)
217 const int bid = body_ids[e];
218 const auto it = materials.find(bid);
219 if (it == materials.end())
225 const json &tmp = it->second;
229 for (
int bid : missing)
231 logger().warn(
"Missing material parameters for body {}", bid);
240 const bool is_volume,
242 const std::vector<ElementBases> &bases,
243 const std::vector<ElementBases> &gbases,
247 const bool is_mass)
const
251 const long int max_triplets_size = long(1e7);
252 const long int buffer_size = std::min(
long(max_triplets_size),
long(n_basis) *
size());
259 stiffness.resize(n_basis *
size(), n_basis *
size());
262 auto storage =
create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
264 const int n_bases = int(bases.size());
267 assert(
cache.is_mass() == is_mass);
275 for (
int e = start; e < end; ++e)
283 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
287 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
289 const int n_loc_bases = int(
vals.basis_values.size());
291 for (
int i = 0; i < n_loc_bases; ++i)
295 const auto &global_i =
vals.basis_values[i].global;
298 for (
int j = 0; j <= i; ++j)
302 const auto &global_j =
vals.basis_values[j].global;
306 assert(stiffness_val.size() ==
size() *
size());
310 for (
int n = 0; n <
size(); ++n)
312 for (
int m = 0; m <
size(); ++m)
314 const double local_value = stiffness_val(n *
size() + m);
317 for (
size_t ii = 0; ii < global_i.size(); ++ii)
319 const auto gi = global_i[ii].index *
size() + m;
320 const auto wi = global_i[ii].val;
322 for (
size_t jj = 0; jj < global_j.size(); ++jj)
324 const auto gj = global_j[jj].index *
size() + n;
325 const auto wj = global_j[jj].val;
328 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
331 local_storage.cache->add_value(e, gj, gi, local_value * wj * wi);
334 if (local_storage.cache->entries_size() >= max_triplets_size)
336 local_storage.cache->prune();
337 logger().trace(
"cleaning memory. Current storage: {}. mat nnz: {}", local_storage.cache->capacity(), local_storage.cache->non_zeros());
355 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
360 std::vector<LocalThreadMatStorage *> storages(storage.size());
362 for (
auto &local_storage : storage)
364 storages[index++] = &local_storage;
369 storages[i]->cache->prune();
372 logger().trace(
"done pruning triplets {}s...", timer.getElapsedTime());
375 std::vector<long int> offsets(storage.size());
378 long int triplet_count = 0;
379 for (
auto &local_storage : storage)
381 offsets[index++] = triplet_count;
382 triplet_count += local_storage.cache->triplet_count();
385 std::vector<Eigen::Triplet<double>> triplets;
387 assert(storages.size() >= 1);
388 if (storages[0]->
cache->is_dense())
392 Eigen::MatrixXd
tmp(stiffness);
393 for (
const LocalThreadMatStorage &local_storage : storage)
394 tmp += dynamic_cast<const DenseMatrixCache &>(*local_storage.
cache).mat();
395 stiffness =
tmp.sparseView();
396 stiffness.makeCompressed();
399 logger().trace(
"Serial assembly time: {}s...", timer.getElapsedTime());
401 else if (triplet_count >= triplets.max_size())
405 logger().warn(
"Cannot allocate space for triplets, switching to serial assembly.");
409 for (LocalThreadMatStorage &local_storage : storage)
410 stiffness += local_storage.
cache->get_matrix(false);
411 stiffness.makeCompressed();
414 logger().trace(
"Serial assembly time: {}s...", timer.getElapsedTime());
419 triplets.resize(triplet_count);
422 logger().trace(
"done allocate triplets {}s...", timer.getElapsedTime());
423 logger().trace(
"Triplets Count: {}", triplet_count);
428 const SparseMatrixCache &cache = dynamic_cast<const SparseMatrixCache &>(*storages[i]->cache);
429 long int offset = offsets[i];
431 std::copy(cache.entries().begin(), cache.entries().end(), triplets.begin() + offset);
432 offset += cache.entries().size();
434 if (cache.mat().nonZeros() > 0)
437 for (int k = 0; k < cache.mat().outerSize(); ++k)
439 for (Eigen::SparseMatrix<double>::InnerIterator it(cache.mat(), k); it; ++it)
441 assert(count < cache.mat().nonZeros());
442 triplets[offset + count++] = Eigen::Triplet<double>(it.row(), it.col(), it.value());
449 logger().trace(
"done concatenate triplets {}s...", timer.getElapsedTime());
453 stiffness.setFromTriplets(triplets.begin(), triplets.end());
456 logger().trace(
"done setFromTriplets assembly {}s...", timer.getElapsedTime());
459 catch (std::bad_alloc &ba)
473 const bool is_volume,
474 const int n_psi_basis,
475 const int n_phi_basis,
476 const std::vector<ElementBases> &psi_bases,
477 const std::vector<ElementBases> &phi_bases,
478 const std::vector<ElementBases> &gbases,
485 assert(phi_bases.size() == psi_bases.size());
487 const int max_triplets_size = int(1e7);
488 const int buffer_size = std::min(
long(max_triplets_size),
long(std::max(n_psi_basis, n_phi_basis)) * std::max(
rows(),
cols()));
491 stiffness.resize(n_phi_basis *
rows(), n_psi_basis *
cols());
494 auto storage =
create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
496 const int n_bases = int(phi_bases.size());
504 for (
int e = start; e < end; ++e)
508 psi_cache.
compute(e, is_volume, psi_bases[e], gbases[e], psi_vals);
509 phi_cache.
compute(e, is_volume, phi_bases[e], gbases[e], phi_vals);
513 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
514 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
515 const int n_phi_loc_bases = int(phi_vals.
basis_values.size());
516 const int n_psi_loc_bases = int(psi_vals.
basis_values.size());
518 for (
int i = 0; i < n_psi_loc_bases; ++i)
522 for (
int j = 0; j < n_phi_loc_bases; ++j)
527 assert(stiffness_val.size() ==
rows() *
cols());
530 for (
int n = 0; n <
rows(); ++n)
532 for (
int m = 0; m <
cols(); ++m)
534 const double local_value = stiffness_val(n *
cols() + m);
536 for (
size_t ii = 0; ii < global_i.size(); ++ii)
538 const auto gi = global_i[ii].index *
cols() + m;
539 const auto wi = global_i[ii].val;
541 for (
size_t jj = 0; jj < global_j.size(); ++jj)
543 const auto gj = global_j[jj].index *
rows() + n;
544 const auto wj = global_j[jj].val;
546 local_storage.cache->add_value(e, gj, gi, local_value * wi * wj);
548 if (local_storage.cache->entries_size() >= max_triplets_size)
550 local_storage.cache->prune();
551 logger().debug(
"cleaning memory...");
563 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
567 for (LocalThreadMatStorage &local_storage : storage)
568 stiffness += local_storage.
cache->get_matrix(false);
569 stiffness.makeCompressed();
571 logger().trace(
"done merge assembly {}s...", timer.getElapsedTime());
578 const bool is_volume,
579 const int n_psi_basis,
580 const int n_phi_basis,
581 const std::vector<ElementBases> &psi_bases,
582 const std::vector<ElementBases> &phi_bases,
583 const std::vector<ElementBases> &gbases,
588 const Eigen::MatrixXd &
x,
589 const Eigen::MatrixXd &x_prev,
595 assert(phi_bases.size() == psi_bases.size());
596 assert(phi_bases.size() == gbases.size());
598 const int phi_ndof = n_phi_basis *
rows();
599 const int psi_ndof = n_psi_basis *
cols();
600 Eigen::MatrixXd x_phi, x_psi;
601 Eigen::MatrixXd x_phi_prev, x_psi_prev;
602 split_mixed_solution(split_solution,
x, phi_ndof, psi_ndof, x_phi, x_psi);
603 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
606 const int n_bases = int(phi_bases.size());
613 for (
int e = start; e < end; ++e)
615 compute_mixed_element_values(
617 psi_bases[e], phi_bases[e], gbases[e],
618 psi_cache, phi_cache,
623 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
624 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
632 for (
const LocalThreadScalarStorage &local_storage : storage)
633 res += local_storage.val;
638 const bool is_volume,
639 const int n_psi_basis,
640 const int n_phi_basis,
641 const std::vector<ElementBases> &psi_bases,
642 const std::vector<ElementBases> &phi_bases,
643 const std::vector<ElementBases> &gbases,
648 const Eigen::MatrixXd &
x,
649 const Eigen::MatrixXd &x_prev,
655 assert(phi_bases.size() == psi_bases.size());
656 assert(phi_bases.size() == gbases.size());
658 const int phi_ndof = n_phi_basis *
rows();
659 const int psi_ndof = n_psi_basis *
cols();
660 Eigen::MatrixXd x_phi, x_psi;
661 Eigen::MatrixXd x_phi_prev, x_psi_prev;
662 split_mixed_solution(split_solution,
x, phi_ndof, psi_ndof, x_phi, x_psi);
663 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
666 const int n_bases = int(phi_bases.size());
667 Eigen::VectorXd out(phi_bases.size());
674 for (
int e = start; e < end; ++e)
676 compute_mixed_element_values(
678 psi_bases[e], phi_bases[e], gbases[e],
679 psi_cache, phi_cache,
684 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
685 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
694 is_volume, n_psi_basis, n_phi_basis,
695 psi_bases, phi_bases, gbases,
696 psi_cache, phi_cache, t, dt,
x, x_prev, split_solution);
697 assert(std::abs(assemble_val - out.sum()) < std::max(1e-10 * std::abs(assemble_val), 1e-10));
704 const bool is_volume,
705 const int n_psi_basis,
706 const int n_phi_basis,
707 const std::vector<ElementBases> &psi_bases,
708 const std::vector<ElementBases> &phi_bases,
709 const std::vector<ElementBases> &gbases,
714 const Eigen::MatrixXd &
x,
715 const Eigen::MatrixXd &x_prev,
717 Eigen::MatrixXd &grad)
const
722 assert(phi_bases.size() == psi_bases.size());
723 assert(phi_bases.size() == gbases.size());
725 const int phi_ndof = n_phi_basis *
rows();
726 const int psi_ndof = n_psi_basis *
cols();
727 Eigen::MatrixXd x_phi, x_psi;
728 Eigen::MatrixXd x_phi_prev, x_psi_prev;
729 split_mixed_solution(split_solution,
x, phi_ndof, psi_ndof, x_phi, x_psi);
730 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
732 grad.resize(phi_ndof + psi_ndof, 1);
736 const int n_bases = int(phi_bases.size());
743 for (
int e = start; e < end; ++e)
745 compute_mixed_element_values(
747 psi_bases[e], phi_bases[e], gbases[e],
748 psi_cache, phi_cache,
753 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
754 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
759 const int n_phi_loc_bases = int(phi_vals.
basis_values.size());
760 const int n_psi_loc_bases = int(psi_vals.
basis_values.size());
761 assert(local_grad.size() == n_phi_loc_bases *
rows() + n_psi_loc_bases *
cols());
763 for (
int i = 0; i < n_phi_loc_bases; ++i)
766 for (
int d = 0; d <
rows(); ++d)
768 const double local_value = local_grad(i *
rows() + d);
769 for (
const auto &global : global_i)
770 local_storage.vec(global.index *
rows() + d) += local_value * global.val;
774 const int local_psi_offset = n_phi_loc_bases *
rows();
775 for (
int i = 0; i < n_psi_loc_bases; ++i)
778 for (
int d = 0; d <
cols(); ++d)
780 const double local_value = local_grad(local_psi_offset + i *
cols() + d);
781 for (
const auto &global : global_i)
782 local_storage.vec(phi_ndof + global.index *
cols() + d) += local_value * global.val;
788 for (
const LocalThreadVecStorage &local_storage : storage)
789 grad += local_storage.vec;
793 const bool is_volume,
794 const int n_psi_basis,
795 const int n_phi_basis,
796 const bool project_to_psd,
797 const std::vector<ElementBases> &psi_bases,
798 const std::vector<ElementBases> &phi_bases,
799 const std::vector<ElementBases> &gbases,
804 const Eigen::MatrixXd &
x,
805 const Eigen::MatrixXd &x_prev,
813 assert(phi_bases.size() == psi_bases.size());
814 assert(phi_bases.size() == gbases.size());
816 const int phi_ndof = n_phi_basis *
rows();
817 const int psi_ndof = n_psi_basis *
cols();
818 const int total_ndof = phi_ndof + psi_ndof;
819 Eigen::MatrixXd x_phi, x_psi;
820 Eigen::MatrixXd x_phi_prev, x_psi_prev;
821 split_mixed_solution(split_solution,
x, phi_ndof, psi_ndof, x_phi, x_psi);
822 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
824 const int max_triplets_size = int(1e7);
825 const int buffer_size = std::min(max_triplets_size, total_ndof);
827 mat_cache.
init(total_ndof);
831 const int n_bases = int(phi_bases.size());
838 for (
int e = start; e < end; ++e)
840 compute_mixed_element_values(
842 psi_bases[e], phi_bases[e], gbases[e],
843 psi_cache, phi_cache,
848 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
849 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
854 const int n_phi_loc_bases = int(phi_vals.
basis_values.size());
855 const int n_psi_loc_bases = int(psi_vals.
basis_values.size());
856 const int local_size = n_phi_loc_bases *
rows() + n_psi_loc_bases *
cols();
857 assert(local_hessian.rows() == local_size);
858 assert(local_hessian.cols() == local_size);
861 local_hessian = ipc::project_to_psd(local_hessian);
863 const auto local_index_data = [&](
864 const int local_index,
865 const std::vector<basis::Local2Global> *&global,
867 int &component_count,
868 int &global_offset) {
869 assert(local_index >= 0);
870 assert(local_index < local_size);
872 const int phi_local_size = n_phi_loc_bases *
rows();
873 if (local_index < phi_local_size)
875 const int basis_index = local_index /
rows();
877 component = local_index %
rows();
878 component_count =
rows();
883 const int psi_local_index = local_index - phi_local_size;
884 const int basis_index = psi_local_index /
cols();
886 component = psi_local_index %
cols();
887 component_count =
cols();
888 global_offset = phi_ndof;
891 for (
int i = 0; i < local_size; ++i)
893 const std::vector<basis::Local2Global> *global_i =
nullptr;
894 int component_i = -1;
895 int component_count_i = -1;
896 int global_offset_i = -1;
897 local_index_data(i, global_i, component_i, component_count_i, global_offset_i);
899 for (
int j = 0; j < local_size; ++j)
901 const double local_value = local_hessian(i, j);
902 if (local_value == 0)
905 const std::vector<basis::Local2Global> *global_j =
nullptr;
906 int component_j = -1;
907 int component_count_j = -1;
908 int global_offset_j = -1;
909 local_index_data(j, global_j, component_j, component_count_j, global_offset_j);
911 for (
const auto &gi : *global_i)
913 const int row = global_offset_i + gi.index * component_count_i + component_i;
914 for (
const auto &gj : *global_j)
916 const int col = global_offset_j + gj.index * component_count_j + component_j;
917 local_storage.cache->add_value(e, row, col, local_value * gi.val * gj.val);
921 if (local_storage.cache->entries_size() >= max_triplets_size)
923 local_storage.cache->prune();
924 logger().debug(
"cleaning memory...");
931 for (LocalThreadMatStorage &local_storage : storage)
933 local_storage.cache->prune();
934 mat_cache += *local_storage.cache;
940 const bool is_volume,
941 const std::vector<ElementBases> &bases,
942 const std::vector<ElementBases> &gbases,
946 const Eigen::MatrixXd &displacement,
947 const Eigen::MatrixXd &displacement_prev)
const
950 const int n_bases = int(bases.size());
956 for (
int e = start; e < end; ++e)
958 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
962 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
966 local_storage.val +=
val;
972 for (
const LocalThreadScalarStorage &local_storage : storage)
973 res += local_storage.val;
978 const bool is_volume,
979 const std::vector<ElementBases> &bases,
980 const std::vector<ElementBases> &gbases,
984 const Eigen::MatrixXd &displacement,
985 const Eigen::MatrixXd &displacement_prev)
const
988 const int n_bases = int(bases.size());
989 Eigen::VectorXd out(bases.size());
995 for (
int e = start; e < end; ++e)
997 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
1001 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
1002 local_storage.da =
vals.det.array() *
quadrature.weights.array();
1011 is_volume, bases, gbases,
cache, t, dt, displacement, displacement_prev);
1012 assert(std::abs(assemble_val - out.sum()) < std::max(1e-10 * assemble_val, 1e-10));
1019 const bool is_volume,
1021 const std::vector<ElementBases> &bases,
1022 const std::vector<ElementBases> &gbases,
1026 const Eigen::MatrixXd &displacement,
1027 const Eigen::MatrixXd &displacement_prev,
1028 Eigen::MatrixXd &rhs)
const
1030 rhs.resize(n_basis *
size(), 1);
1035 const int n_bases = int(bases.size());
1040 for (
int e = start; e < end; ++e)
1046 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
1050 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
1051 local_storage.da =
vals.det.array() *
quadrature.weights.array();
1052 const int n_loc_bases = int(
vals.basis_values.size());
1055 assert(
val.size() == n_loc_bases *
size());
1057 for (
int j = 0; j < n_loc_bases; ++j)
1059 const auto &global_j =
vals.basis_values[j].global;
1062 for (
int m = 0; m <
size(); ++m)
1064 const double local_value =
val(j *
size() + m);
1066 for (
size_t jj = 0; jj < global_j.size(); ++jj)
1068 const auto gj = global_j[jj].index *
size() + m;
1069 const auto wj = global_j[jj].val;
1071 local_storage.vec(gj) += local_value * wj;
1085 for (
const LocalThreadVecStorage &local_storage : storage)
1086 rhs += local_storage.
vec;
1090 const bool is_volume,
1092 const bool project_to_psd,
1093 const std::vector<ElementBases> &bases,
1094 const std::vector<ElementBases> &gbases,
1095 const AssemblyValsCache &
cache,
1098 const Eigen::MatrixXd &displacement,
1099 const Eigen::MatrixXd &displacement_prev,
1100 MatrixCache &mat_cache,
1103 const int max_triplets_size = int(1e7);
1104 const int buffer_size = std::min(
long(max_triplets_size),
long(n_basis) *
size());
1110 mat_cache.init(n_basis *
size());
1111 mat_cache.set_zero();
1115 const int n_bases = int(bases.size());
1122 for (
int e = start;
e < end; ++
e)
1124 ElementAssemblyValues &
vals = local_storage.vals;
1125 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
1129 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
1130 local_storage.da =
vals.det.array() *
quadrature.weights.array();
1131 const int n_loc_bases = int(
vals.basis_values.size());
1133 auto stiffness_val =
assemble_hessian(NonLinearAssemblerData(
vals, t, dt, displacement, displacement_prev, local_storage.da));
1134 assert(stiffness_val.rows() == n_loc_bases *
size());
1135 assert(stiffness_val.cols() == n_loc_bases *
size());
1138 stiffness_val = ipc::project_to_psd(stiffness_val);
1156 for (
int i = 0; i < n_loc_bases; ++i)
1158 const auto &global_i =
vals.basis_values[i].global;
1160 for (
int j = 0; j < n_loc_bases; ++j)
1163 const auto &global_j =
vals.basis_values[j].global;
1165 for (
int n = 0; n <
size(); ++n)
1167 for (
int m = 0; m <
size(); ++m)
1169 const double local_value = stiffness_val(i *
size() + m, j *
size() + n);
1171 for (
size_t ii = 0; ii < global_i.size(); ++ii)
1173 const auto gi = global_i[ii].index *
size() + m;
1174 const auto wi = global_i[ii].val;
1176 for (
size_t jj = 0; jj < global_j.size(); ++jj)
1178 const auto gj = global_j[jj].index *
size() + n;
1179 const auto wj = global_j[jj].val;
1181 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
1186 if (local_storage.cache->entries_size() >= max_triplets_size)
1188 local_storage.cache->prune();
1189 logger().debug(
"cleaning memory...");
1201 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
1206 for (LocalThreadMatStorage &local_storage : storage)
1208 local_storage.cache->prune();
1209 mat_cache += *local_storage.cache;
1211 hess = mat_cache.get_matrix();
1214 logger().trace(
"done merge assembly {}s...", timer.getElapsedTime());
1219#ifdef POLYFEM_WITH_MISO
1222 logger().error(
"Enable Bezier library to use the robust Jacobian check!");
std::unique_ptr< MatrixCache > cache
ElementAssemblyValues vals
void set_materials(const std::vector< int > &body_ids, const json &body_params, const Units &units, const std::string &root_path)
virtual void add_multimaterial(const int index, const json ¶ms, const Units &units, const std::string &root_path)
Caches basis evaluation and geometric mapping at every element.
void compute(const int el_index, const bool is_volume, const basis::ElementBases &basis, const basis::ElementBases &gbasis, ElementAssemblyValues &vals) const
retrieves cached basis evaluation and geometric for the given element if it doesn't exist,...
void set_use_robust_jacobian()
stores per element basis values at given quadrature points and geometric mapping
std::vector< AssemblyValues > basis_values
quadrature::Quadrature quadrature
void assemble(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, StiffnessMatrix &stiffness, const bool is_mass=false) const override
assembles the stiffness matrix for the given basis the bilinear form (local assembler) is encoded by ...
virtual int rows() const =0
void assemble(const bool is_volume, const int n_psi_basis, const int n_phi_basis, const std::vector< basis::ElementBases > &psi_bases, const std::vector< basis::ElementBases > &phi_bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &psi_cache, const AssemblyValsCache &phi_cache, const double t, StiffnessMatrix &stiffness) const
virtual int cols() const =0
void assemble_gradient(const bool is_volume, const int n_psi_basis, const int n_phi_basis, const std::vector< basis::ElementBases > &psi_bases, const std::vector< basis::ElementBases > &phi_bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &psi_cache, const AssemblyValsCache &phi_cache, const double t, const double dt, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const SolutionSplitter &split_solution, Eigen::MatrixXd &grad) const
virtual int rows() const =0
virtual double compute_energy(const MixedNonLinearAssemblerData &data) const =0
void assemble_hessian(const bool is_volume, const int n_psi_basis, const int n_phi_basis, const bool project_to_psd, const std::vector< basis::ElementBases > &psi_bases, const std::vector< basis::ElementBases > &phi_bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &psi_cache, const AssemblyValsCache &phi_cache, const double t, const double dt, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const SolutionSplitter &split_solution, utils::MatrixCache &mat_cache, StiffnessMatrix &hessian) const
Eigen::VectorXd assemble_energy_per_element(const bool is_volume, const int n_psi_basis, const int n_phi_basis, const std::vector< basis::ElementBases > &psi_bases, const std::vector< basis::ElementBases > &phi_bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &psi_cache, const AssemblyValsCache &phi_cache, const double t, const double dt, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const SolutionSplitter &split_solution) const
virtual Eigen::VectorXd compute_gradient(const MixedNonLinearAssemblerData &data) const =0
virtual Eigen::MatrixXd compute_hessian(const MixedNonLinearAssemblerData &data) const =0
std::function< void(const Eigen::MatrixXd &x, Eigen::MatrixXd &x_phi, Eigen::MatrixXd &x_psi)> SolutionSplitter
double assemble_energy(const bool is_volume, const int n_psi_basis, const int n_phi_basis, const std::vector< basis::ElementBases > &psi_bases, const std::vector< basis::ElementBases > &phi_bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &psi_cache, const AssemblyValsCache &phi_cache, const double t, const double dt, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const SolutionSplitter &split_solution) const
virtual int cols() const =0
double assemble_energy(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev) const override
Eigen::VectorXd assemble_energy_per_element(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev) const override
virtual double compute_energy(const NonLinearAssemblerData &data) const =0
void assemble_gradient(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev, Eigen::MatrixXd &rhs) const override
void assemble_hessian(const bool is_volume, const int n_basis, const bool project_to_psd, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev, utils::MatrixCache &mat_cache, StiffnessMatrix &grad) const override
abstract class used for caching
virtual void set_zero()=0
virtual StiffnessMatrix get_matrix(const bool compute_mapping=true)=0
virtual void init(const size_t size)=0
auto & get_local_thread_storage(Storages &storage, int thread_id)
auto create_thread_storage(const LocalStorage &initial_local_storage)
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
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