8#include <ipc/utils/eigen_ext.hpp>
12 using namespace basis;
14 using namespace utils;
18 class LocalThreadMatStorage
21 std::unique_ptr<MatrixCache>
cache =
nullptr;
22 ElementAssemblyValues
vals;
25 LocalThreadMatStorage() =
delete;
27 LocalThreadMatStorage(
const int buffer_size,
const int rows,
const int cols)
29 init(buffer_size, rows, cols);
32 LocalThreadMatStorage(
const int buffer_size,
const MatrixCache &c)
37 LocalThreadMatStorage(
const LocalThreadMatStorage &other)
42 LocalThreadMatStorage &operator=(
const LocalThreadMatStorage &other)
44 assert(other.cache !=
nullptr);
45 cache = other.cache->copy();
51 void init(
const int buffer_size,
const int rows,
const int cols)
55 cache = std::make_unique<SparseMatrixCache>();
56 cache->reserve(buffer_size);
57 cache->init(rows, cols);
60 void init(
const int buffer_size,
const MatrixCache &c)
64 cache->reserve(buffer_size);
69 class LocalThreadVecStorage
73 ElementAssemblyValues
vals;
76 LocalThreadVecStorage(
const int size)
83 class LocalThreadScalarStorage
87 ElementAssemblyValues
vals;
90 LocalThreadScalarStorage()
99 if (!body_params.is_array())
105 std::map<int, json> materials;
106 for (
int i = 0; i < body_params.size(); ++i)
108 json mat = body_params[i];
112 for (
int j = 0; j <
id.size(); ++j)
113 materials[
id[j]] = mat;
118 materials[mid] = mat;
122 std::set<int> missing;
124 std::map<int, int> body_element_count;
125 std::vector<int> eid_to_eid_in_body(body_ids.size());
126 for (
int e = 0; e < body_ids.size(); ++e)
128 const int bid = body_ids[e];
129 body_element_count.try_emplace(bid, 0);
130 eid_to_eid_in_body[e] = body_element_count[bid]++;
133 for (
int e = 0; e < body_ids.size(); ++e)
135 const int bid = body_ids[e];
136 const auto it = materials.find(bid);
137 if (it == materials.end())
143 const json &tmp = it->second;
147 for (
int bid : missing)
149 logger().warn(
"Missing material parameters for body {}", bid);
158 const bool is_volume,
160 const std::vector<ElementBases> &bases,
161 const std::vector<ElementBases> &gbases,
165 const bool is_mass)
const
169 const long int max_triplets_size = long(1e7);
170 const long int buffer_size = std::min(
long(max_triplets_size),
long(n_basis) *
size());
177 stiffness.resize(n_basis *
size(), n_basis *
size());
180 auto storage =
create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
182 const int n_bases = int(bases.size());
185 assert(
cache.is_mass() == is_mass);
193 for (
int e = start; e < end; ++e)
201 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
205 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
207 const int n_loc_bases = int(
vals.basis_values.size());
209 for (
int i = 0; i < n_loc_bases; ++i)
213 const auto &global_i =
vals.basis_values[i].global;
216 for (
int j = 0; j <= i; ++j)
220 const auto &global_j =
vals.basis_values[j].global;
224 assert(stiffness_val.size() ==
size() *
size());
228 for (
int n = 0; n <
size(); ++n)
230 for (
int m = 0; m <
size(); ++m)
232 const double local_value = stiffness_val(n *
size() + m);
235 for (
size_t ii = 0; ii < global_i.size(); ++ii)
237 const auto gi = global_i[ii].index *
size() + m;
238 const auto wi = global_i[ii].val;
240 for (
size_t jj = 0; jj < global_j.size(); ++jj)
242 const auto gj = global_j[jj].index *
size() + n;
243 const auto wj = global_j[jj].val;
246 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
249 local_storage.cache->add_value(e, gj, gi, local_value * wj * wi);
252 if (local_storage.cache->entries_size() >= max_triplets_size)
254 local_storage.cache->prune();
255 logger().trace(
"cleaning memory. Current storage: {}. mat nnz: {}", local_storage.cache->capacity(), local_storage.cache->non_zeros());
273 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
278 std::vector<LocalThreadMatStorage *> storages(storage.size());
280 for (
auto &local_storage : storage)
282 storages[index++] = &local_storage;
287 storages[i]->cache->prune();
290 logger().trace(
"done pruning triplets {}s...", timer.getElapsedTime());
293 std::vector<long int> offsets(storage.size());
296 long int triplet_count = 0;
297 for (
auto &local_storage : storage)
299 offsets[index++] = triplet_count;
300 triplet_count += local_storage.cache->triplet_count();
303 std::vector<Eigen::Triplet<double>> triplets;
305 assert(storages.size() >= 1);
306 if (storages[0]->
cache->is_dense())
310 Eigen::MatrixXd
tmp(stiffness);
311 for (
const LocalThreadMatStorage &local_storage : storage)
312 tmp += dynamic_cast<const DenseMatrixCache &>(*local_storage.
cache).mat();
313 stiffness =
tmp.sparseView();
314 stiffness.makeCompressed();
317 logger().trace(
"Serial assembly time: {}s...", timer.getElapsedTime());
319 else if (triplet_count >= triplets.max_size())
323 logger().warn(
"Cannot allocate space for triplets, switching to serial assembly.");
327 for (LocalThreadMatStorage &local_storage : storage)
328 stiffness += local_storage.
cache->get_matrix(false);
329 stiffness.makeCompressed();
332 logger().trace(
"Serial assembly time: {}s...", timer.getElapsedTime());
337 triplets.resize(triplet_count);
340 logger().trace(
"done allocate triplets {}s...", timer.getElapsedTime());
341 logger().trace(
"Triplets Count: {}", triplet_count);
346 const SparseMatrixCache &cache = dynamic_cast<const SparseMatrixCache &>(*storages[i]->cache);
347 long int offset = offsets[i];
349 std::copy(cache.entries().begin(), cache.entries().end(), triplets.begin() + offset);
350 offset += cache.entries().size();
352 if (cache.mat().nonZeros() > 0)
355 for (int k = 0; k < cache.mat().outerSize(); ++k)
357 for (Eigen::SparseMatrix<double>::InnerIterator it(cache.mat(), k); it; ++it)
359 assert(count < cache.mat().nonZeros());
360 triplets[offset + count++] = Eigen::Triplet<double>(it.row(), it.col(), it.value());
367 logger().trace(
"done concatenate triplets {}s...", timer.getElapsedTime());
371 stiffness.setFromTriplets(triplets.begin(), triplets.end());
374 logger().trace(
"done setFromTriplets assembly {}s...", timer.getElapsedTime());
377 catch (std::bad_alloc &ba)
391 const bool is_volume,
392 const int n_psi_basis,
393 const int n_phi_basis,
394 const std::vector<ElementBases> &psi_bases,
395 const std::vector<ElementBases> &phi_bases,
396 const std::vector<ElementBases> &gbases,
403 assert(phi_bases.size() == psi_bases.size());
405 const int max_triplets_size = int(1e7);
406 const int buffer_size = std::min(
long(max_triplets_size),
long(std::max(n_psi_basis, n_phi_basis)) * std::max(
rows(),
cols()));
409 stiffness.resize(n_phi_basis *
rows(), n_psi_basis *
cols());
412 auto storage =
create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
414 const int n_bases = int(phi_bases.size());
422 for (
int e = start; e < end; ++e)
426 psi_cache.
compute(e, is_volume, psi_bases[e], gbases[e], psi_vals);
427 phi_cache.
compute(e, is_volume, phi_bases[e], gbases[e], phi_vals);
431 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
432 local_storage.da = phi_vals.
det.array() *
quadrature.weights.array();
433 const int n_phi_loc_bases = int(phi_vals.
basis_values.size());
434 const int n_psi_loc_bases = int(psi_vals.
basis_values.size());
436 for (
int i = 0; i < n_psi_loc_bases; ++i)
440 for (
int j = 0; j < n_phi_loc_bases; ++j)
445 assert(stiffness_val.size() ==
rows() *
cols());
448 for (
int n = 0; n <
rows(); ++n)
450 for (
int m = 0; m <
cols(); ++m)
452 const double local_value = stiffness_val(n *
cols() + m);
454 for (
size_t ii = 0; ii < global_i.size(); ++ii)
456 const auto gi = global_i[ii].index *
cols() + m;
457 const auto wi = global_i[ii].val;
459 for (
size_t jj = 0; jj < global_j.size(); ++jj)
461 const auto gj = global_j[jj].index *
rows() + n;
462 const auto wj = global_j[jj].val;
464 local_storage.cache->add_value(e, gj, gi, local_value * wi * wj);
466 if (local_storage.cache->entries_size() >= max_triplets_size)
468 local_storage.cache->prune();
469 logger().debug(
"cleaning memory...");
481 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
485 for (LocalThreadMatStorage &local_storage : storage)
486 stiffness += local_storage.
cache->get_matrix(false);
487 stiffness.makeCompressed();
489 logger().trace(
"done merge assembly {}s...", timer.getElapsedTime());
496 const bool is_volume,
497 const std::vector<ElementBases> &bases,
498 const std::vector<ElementBases> &gbases,
502 const Eigen::MatrixXd &displacement,
503 const Eigen::MatrixXd &displacement_prev)
const
506 const int n_bases = int(bases.size());
512 for (
int e = start; e < end; ++e)
514 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
518 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
522 local_storage.val +=
val;
528 for (
const LocalThreadScalarStorage &local_storage : storage)
529 res += local_storage.val;
534 const bool is_volume,
535 const std::vector<ElementBases> &bases,
536 const std::vector<ElementBases> &gbases,
540 const Eigen::MatrixXd &displacement,
541 const Eigen::MatrixXd &displacement_prev)
const
544 const int n_bases = int(bases.size());
545 Eigen::VectorXd out(bases.size());
551 for (
int e = start; e < end; ++e)
553 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
557 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
567 is_volume, bases, gbases,
cache, t, dt, displacement, displacement_prev);
568 assert(std::abs(assemble_val - out.sum()) < std::max(1e-10 * assemble_val, 1e-10));
575 const bool is_volume,
577 const std::vector<ElementBases> &bases,
578 const std::vector<ElementBases> &gbases,
582 const Eigen::MatrixXd &displacement,
583 const Eigen::MatrixXd &displacement_prev,
584 Eigen::MatrixXd &rhs)
const
586 rhs.resize(n_basis *
size(), 1);
591 const int n_bases = int(bases.size());
596 for (
int e = start; e < end; ++e)
602 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
606 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
608 const int n_loc_bases = int(
vals.basis_values.size());
611 assert(
val.size() == n_loc_bases *
size());
613 for (
int j = 0; j < n_loc_bases; ++j)
615 const auto &global_j =
vals.basis_values[j].global;
618 for (
int m = 0; m <
size(); ++m)
620 const double local_value =
val(j *
size() + m);
622 for (
size_t jj = 0; jj < global_j.size(); ++jj)
624 const auto gj = global_j[jj].index *
size() + m;
625 const auto wj = global_j[jj].val;
627 local_storage.vec(gj) += local_value * wj;
641 for (
const LocalThreadVecStorage &local_storage : storage)
642 rhs += local_storage.
vec;
646 const bool is_volume,
648 const bool project_to_psd,
649 const std::vector<ElementBases> &bases,
650 const std::vector<ElementBases> &gbases,
651 const AssemblyValsCache &
cache,
654 const Eigen::MatrixXd &displacement,
655 const Eigen::MatrixXd &displacement_prev,
656 MatrixCache &mat_cache,
659 const int max_triplets_size = int(1e7);
660 const int buffer_size = std::min(
long(max_triplets_size),
long(n_basis) *
size());
666 mat_cache.init(n_basis *
size());
667 mat_cache.set_zero();
671 const int n_bases = int(bases.size());
678 for (
int e = start;
e < end; ++
e)
680 ElementAssemblyValues &
vals = local_storage.vals;
681 cache.compute(e, is_volume, bases[e], gbases[e],
vals);
685 assert(MAX_QUAD_POINTS == -1 ||
quadrature.weights.size() < MAX_QUAD_POINTS);
687 const int n_loc_bases = int(
vals.basis_values.size());
689 auto stiffness_val =
assemble_hessian(NonLinearAssemblerData(
vals, t, dt, displacement, displacement_prev, local_storage.da));
690 assert(stiffness_val.rows() == n_loc_bases *
size());
691 assert(stiffness_val.cols() == n_loc_bases *
size());
694 stiffness_val = ipc::project_to_psd(stiffness_val);
712 for (
int i = 0; i < n_loc_bases; ++i)
714 const auto &global_i =
vals.basis_values[i].global;
716 for (
int j = 0; j < n_loc_bases; ++j)
719 const auto &global_j =
vals.basis_values[j].global;
721 for (
int n = 0; n <
size(); ++n)
723 for (
int m = 0; m <
size(); ++m)
725 const double local_value = stiffness_val(i *
size() + m, j *
size() + n);
727 for (
size_t ii = 0; ii < global_i.size(); ++ii)
729 const auto gi = global_i[ii].index *
size() + m;
730 const auto wi = global_i[ii].val;
732 for (
size_t jj = 0; jj < global_j.size(); ++jj)
734 const auto gj = global_j[jj].index *
size() + n;
735 const auto wj = global_j[jj].val;
737 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
742 if (local_storage.cache->entries_size() >= max_triplets_size)
744 local_storage.cache->prune();
745 logger().debug(
"cleaning memory...");
757 logger().trace(
"done separate assembly {}s...", timer.getElapsedTime());
762 for (LocalThreadMatStorage &local_storage : storage)
764 local_storage.cache->prune();
765 mat_cache += *local_storage.cache;
767 hess = mat_cache.get_matrix();
770 logger().trace(
"done merge assembly {}s...", timer.getElapsedTime());
std::unique_ptr< MatrixCache > cache
ElementAssemblyValues vals
void set_materials(const std::vector< int > &body_ids, const json &body_params, const Units &units)
virtual void add_multimaterial(const int index, const json ¶ms, const Units &units)
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,...
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
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
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