PolyFEM
Loading...
Searching...
No Matches
Assembler.cpp
Go to the documentation of this file.
1#include "Assembler.hpp"
2#include "MatParams.hpp"
3
6
7#include <igl/Timer.h>
8
9#include <ipc/utils/eigen_ext.hpp>
10
11#include <algorithm>
12#include <cmath>
13
14namespace polyfem::assembler
15{
16 using namespace basis;
17 using namespace quadrature;
18 using namespace utils;
19
20 namespace
21 {
22 class LocalThreadMatStorage
23 {
24 public:
25 std::unique_ptr<MatrixCache> cache = nullptr;
26 ElementAssemblyValues vals;
28
29 LocalThreadMatStorage() = delete;
30
31 LocalThreadMatStorage(const int buffer_size, const int rows, const int cols)
32 {
33 init(buffer_size, rows, cols);
34 }
35
36 LocalThreadMatStorage(const int buffer_size, const MatrixCache &c)
37 {
38 init(buffer_size, c);
39 }
40
41 LocalThreadMatStorage(const LocalThreadMatStorage &other)
42 : cache(other.cache->copy()), vals(other.vals), da(other.da)
43 {
44 }
45
46 LocalThreadMatStorage &operator=(const LocalThreadMatStorage &other)
47 {
48 assert(other.cache != nullptr);
49 cache = other.cache->copy();
50 vals = other.vals;
51 da = other.da;
52 return *this;
53 }
54
55 void init(const int buffer_size, const int rows, const int cols)
56 {
57 // assert(rows == cols);
58 // cache = std::make_unique<DenseMatrixCache>();
59 cache = std::make_unique<SparseMatrixCache>();
60 cache->reserve(buffer_size);
61 cache->init(rows, cols);
62 }
63
64 void init(const int buffer_size, const MatrixCache &c)
65 {
66 if (cache == nullptr)
67 cache = c.copy();
68 cache->reserve(buffer_size);
69 cache->init(c);
70 }
71 };
72
73 class LocalThreadVecStorage
74 {
75 public:
76 Eigen::MatrixXd vec;
77 ElementAssemblyValues vals;
79
80 LocalThreadVecStorage(const int size)
81 {
82 vec.resize(size, 1);
83 vec.setZero();
84 }
85 };
86
87 class LocalThreadScalarStorage
88 {
89 public:
90 double val;
91 ElementAssemblyValues vals;
93
94 LocalThreadScalarStorage()
95 {
96 val = 0;
97 }
98 };
99
100 void split_mixed_solution(
101 const MixedNLAssembler::SolutionSplitter &split_solution,
102 const Eigen::MatrixXd &x,
103 const int phi_ndof,
104 const int psi_ndof,
105 Eigen::MatrixXd &x_phi,
106 Eigen::MatrixXd &x_psi)
107 {
108 assert(split_solution);
109
110 split_solution(x, x_phi, x_psi);
111
112 assert(x_phi.rows() == phi_ndof);
113 assert(x_phi.cols() == 1);
114 assert(x_psi.rows() == psi_ndof);
115 assert(x_psi.cols() == 1);
116 }
117
118 void split_mixed_previous_solution(
119 const MixedNLAssembler::SolutionSplitter &split_solution,
120 const Eigen::MatrixXd &x,
121 const int phi_ndof,
122 const int psi_ndof,
123 Eigen::MatrixXd &x_phi,
124 Eigen::MatrixXd &x_psi)
125 {
126 if (x.size() == 0)
127 {
128 x_phi.resize(0, 1);
129 x_psi.resize(0, 1);
130 return;
131 }
132
133 split_mixed_solution(split_solution, x, phi_ndof, psi_ndof, x_phi, x_psi);
134 }
135
136 void compute_mixed_element_values(
137 const int el_index,
138 const bool is_volume,
139 const ElementBases &psi_basis,
140 const ElementBases &phi_basis,
141 const ElementBases &gbasis,
142 const AssemblyValsCache &psi_cache,
143 const AssemblyValsCache &phi_cache,
144 ElementAssemblyValues &psi_vals,
145 ElementAssemblyValues &phi_vals)
146 {
147 psi_cache.compute(el_index, is_volume, psi_basis, gbasis, psi_vals);
148 phi_cache.compute(el_index, is_volume, phi_basis, gbasis, phi_vals);
149
150 const auto same_quadrature = [&]() {
151 if (psi_vals.quadrature.weights.size() != phi_vals.quadrature.weights.size())
152 return false;
153 if (psi_vals.quadrature.points.rows() != phi_vals.quadrature.points.rows()
154 || psi_vals.quadrature.points.cols() != phi_vals.quadrature.points.cols())
155 return false;
156 return (psi_vals.quadrature.points - phi_vals.quadrature.points).cwiseAbs().maxCoeff() < 1e-14;
157 };
158
159 if (same_quadrature())
160 return;
161
162 if (phi_vals.quadrature.weights.size() >= psi_vals.quadrature.weights.size())
163 {
164 const Quadrature quadrature = phi_vals.quadrature;
165 psi_vals.compute(el_index, is_volume, quadrature.points, psi_basis, gbasis);
166 psi_vals.quadrature = quadrature;
167 }
168 else
169 {
170 const Quadrature quadrature = psi_vals.quadrature;
171 phi_vals.compute(el_index, is_volume, quadrature.points, phi_basis, gbasis);
172 phi_vals.quadrature = quadrature;
173 }
174
175 assert(psi_vals.quadrature.weights.size() == phi_vals.quadrature.weights.size());
176 assert(psi_vals.quadrature.points.rows() == phi_vals.quadrature.points.rows());
177 }
178 } // namespace
179
180 void Assembler::set_materials(const std::vector<int> &body_ids, const json &body_params, const Units &units, const std::string &root_path)
181 {
182 if (!body_params.is_array())
183 {
184 this->add_multimaterial(0, body_params, units, root_path);
185 return;
186 }
187
188 std::map<int, json> materials;
189 for (int i = 0; i < body_params.size(); ++i)
190 {
191 json mat = body_params[i];
192 json id = mat["id"];
193 if (id.is_array())
194 {
195 for (int j = 0; j < id.size(); ++j)
196 materials[id[j]] = mat;
197 }
198 else
199 {
200 const int mid = id;
201 materials[mid] = mat;
202 }
203 }
204
205 std::set<int> missing;
206
207 std::map<int, int> body_element_count;
208 std::vector<int> eid_to_eid_in_body(body_ids.size());
209 for (int e = 0; e < body_ids.size(); ++e)
210 {
211 const int bid = body_ids[e];
212 body_element_count.try_emplace(bid, 0);
213 eid_to_eid_in_body[e] = body_element_count[bid]++;
214 }
215
216 for (int e = 0; e < body_ids.size(); ++e)
217 {
218 const int bid = body_ids[e];
219 const auto it = materials.find(bid);
220 if (it == materials.end())
221 {
222 missing.insert(bid);
223 continue;
224 }
225
226 json tmp = it->second;
227 tmp[MATERIAL_ELEMENT_INDEX] = eid_to_eid_in_body[e];
228 this->add_multimaterial(e, tmp, units, root_path);
229 }
230
231 for (int bid : missing)
232 {
233 logger().warn("Missing material parameters for body {}", bid);
234 }
235 }
236
240
242 const bool is_volume,
243 const int n_basis,
244 const std::vector<ElementBases> &bases,
245 const std::vector<ElementBases> &gbases,
247 const double t,
248 StiffnessMatrix &stiffness,
249 const bool is_mass) const
250 {
251 assert(size() > 0);
252
253 const long int max_triplets_size = long(1e7);
254 const long int buffer_size = std::min(long(max_triplets_size), long(n_basis) * size());
255 // #ifdef POLYFEM_WITH_TBB
256 // buffer_size /= tbb::task_scheduler_init::default_num_threads();
257 // #endif
258 // logger().trace("buffer_size {}", buffer_size);
259 try
260 {
261 stiffness.resize(n_basis * size(), n_basis * size());
262 stiffness.setZero();
263
264 auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
265
266 const int n_bases = int(bases.size());
267 igl::Timer timer;
268 timer.start();
269 assert(cache.is_mass() == is_mass);
270
271 // (potentially parallel) loop over elements
272 // Note that n_bases is the number of elements since ach ElementBases object stores
273 // all local basis functions on a given element
274 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
275 LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
276
277 for (int e = start; e < end; ++e)
278 {
279 ElementAssemblyValues &vals = local_storage.vals;
280 // igl::Timer timer; timer.start();
281 // vals.compute(e, is_volume, bases[e], gbases[e]);
282
283 // compute geometric mapping
284 // evaluate and store basis functions/their gradients at quadrature points
285 cache.compute(e, is_volume, bases[e], gbases[e], vals);
286
287 const Quadrature &quadrature = vals.quadrature;
288
289 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
290 local_storage.da = vals.det.array() * quadrature.weights.array();
291 const int n_loc_bases = int(vals.basis_values.size());
292
293 for (int i = 0; i < n_loc_bases; ++i)
294 {
295 // const AssemblyValues &values_i = vals.basis_values[i];
296 // const Eigen::MatrixXd &gradi = values_i.grad_t_m;
297 const auto &global_i = vals.basis_values[i].global;
298
299 // loop over other bases up to the current one, taking advantage of symmetry
300 for (int j = 0; j <= i; ++j)
301 {
302 // const AssemblyValues &values_j = vals.basis_values[j];
303 // const Eigen::MatrixXd &gradj = values_j.grad_t_m;
304 const auto &global_j = vals.basis_values[j].global;
305
306 // compute local entry in stiffness matrix
307 const auto stiffness_val = assemble(LinearAssemblerData(vals, t, i, j, local_storage.da));
308 assert(stiffness_val.size() == size() * size());
309
310 // igl::Timer t1; t1.start();
311 // loop over dimensions of the problem
312 for (int n = 0; n < size(); ++n)
313 {
314 for (int m = 0; m < size(); ++m)
315 {
316 const double local_value = stiffness_val(n * size() + m);
317
318 // loop over the global nodes corresponding to local element (useful for non-conforming cases)
319 for (size_t ii = 0; ii < global_i.size(); ++ii)
320 {
321 const auto gi = global_i[ii].index * size() + m;
322 const auto wi = global_i[ii].val;
323
324 for (size_t jj = 0; jj < global_j.size(); ++jj)
325 {
326 const auto gj = global_j[jj].index * size() + n;
327 const auto wj = global_j[jj].val;
328
329 // add local value to the global matrix (weighted by corresponding nodes)
330 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
331 if (j < i)
332 {
333 local_storage.cache->add_value(e, gj, gi, local_value * wj * wi);
334 }
335
336 if (local_storage.cache->entries_size() >= max_triplets_size)
337 {
338 local_storage.cache->prune();
339 logger().trace("cleaning memory. Current storage: {}. mat nnz: {}", local_storage.cache->capacity(), local_storage.cache->non_zeros());
340 }
341 }
342 }
343 }
344 }
345
346 // t1.stop();
347 // if (!vals.has_parameterization) { logger().trace("-- t1: " {}, t1.getElapsedTime()); }
348 }
349 }
350
351 // timer.stop();
352 // if (!vals.has_parameterization) { logger().trace("-- Timer: {}", timer.getElapsedTime()); }
353 }
354 });
355
356 timer.stop();
357 logger().trace("done separate assembly {}s...", timer.getElapsedTime());
358
359 // Assemble the stiffness matrix by concatenating the tuples in each local storage
360
361 // Collect thread storages
362 std::vector<LocalThreadMatStorage *> storages(storage.size());
363 long int index = 0;
364 for (auto &local_storage : storage)
365 {
366 storages[index++] = &local_storage;
367 }
368
369 timer.start();
370 maybe_parallel_for(storages.size(), [&](int i) {
371 storages[i]->cache->prune();
372 });
373 timer.stop();
374 logger().trace("done pruning triplets {}s...", timer.getElapsedTime());
375
376 // Prepares for parallel concatenation
377 std::vector<long int> offsets(storage.size());
378
379 index = 0;
380 long int triplet_count = 0;
381 for (auto &local_storage : storage)
382 {
383 offsets[index++] = triplet_count;
384 triplet_count += local_storage.cache->triplet_count();
385 }
386
387 std::vector<Eigen::Triplet<double>> triplets;
388
389 assert(storages.size() >= 1);
390 if (storages[0]->cache->is_dense())
391 {
392 timer.start();
393 // Serially merge local storages
394 Eigen::MatrixXd tmp(stiffness);
395 for (const LocalThreadMatStorage &local_storage : storage)
396 tmp += dynamic_cast<const DenseMatrixCache &>(*local_storage.cache).mat();
397 stiffness = tmp.sparseView();
398 stiffness.makeCompressed();
399 timer.stop();
400
401 logger().trace("Serial assembly time: {}s...", timer.getElapsedTime());
402 }
403 else if (triplet_count >= triplets.max_size())
404 {
405 // Serial fallback version in case the vector of triplets cannot be allocated
406
407 logger().warn("Cannot allocate space for triplets, switching to serial assembly.");
408
409 timer.start();
410 // Serially merge local storages
411 for (LocalThreadMatStorage &local_storage : storage)
412 stiffness += local_storage.cache->get_matrix(false); // will also prune
413 stiffness.makeCompressed();
414 timer.stop();
415
416 logger().trace("Serial assembly time: {}s...", timer.getElapsedTime());
417 }
418 else
419 {
420 timer.start();
421 triplets.resize(triplet_count);
422 timer.stop();
423
424 logger().trace("done allocate triplets {}s...", timer.getElapsedTime());
425 logger().trace("Triplets Count: {}", triplet_count);
426
427 timer.start();
428 // Parallel copy into triplets
429 maybe_parallel_for(storages.size(), [&](int i) {
430 const SparseMatrixCache &cache = dynamic_cast<const SparseMatrixCache &>(*storages[i]->cache);
431 long int offset = offsets[i];
432
433 std::copy(cache.entries().begin(), cache.entries().end(), triplets.begin() + offset);
434 offset += cache.entries().size();
435
436 if (cache.mat().nonZeros() > 0)
437 {
438 long int count = 0;
439 for (int k = 0; k < cache.mat().outerSize(); ++k)
440 {
441 for (Eigen::SparseMatrix<double>::InnerIterator it(cache.mat(), k); it; ++it)
442 {
443 assert(count < cache.mat().nonZeros());
444 triplets[offset + count++] = Eigen::Triplet<double>(it.row(), it.col(), it.value());
445 }
446 }
447 }
448 });
449
450 timer.stop();
451 logger().trace("done concatenate triplets {}s...", timer.getElapsedTime());
452
453 timer.start();
454 // Sort and assemble
455 stiffness.setFromTriplets(triplets.begin(), triplets.end());
456 timer.stop();
457
458 logger().trace("done setFromTriplets assembly {}s...", timer.getElapsedTime());
459 }
460 }
461 catch (std::bad_alloc &ba)
462 {
463 log_and_throw_error("bad alloc {}", ba.what());
464 }
465
466 // stiffness.resize(n_basis*size(), n_basis*size());
467 // stiffness.setFromTriplets(entries.begin(), entries.end());
468 }
469
473
475 const bool is_volume,
476 const int n_psi_basis,
477 const int n_phi_basis,
478 const std::vector<ElementBases> &psi_bases,
479 const std::vector<ElementBases> &phi_bases,
480 const std::vector<ElementBases> &gbases,
481 const AssemblyValsCache &psi_cache,
482 const AssemblyValsCache &phi_cache,
483 const double t,
484 StiffnessMatrix &stiffness) const
485 {
486 assert(size() > 0);
487 assert(phi_bases.size() == psi_bases.size());
488
489 const int max_triplets_size = int(1e7);
490 const int buffer_size = std::min(long(max_triplets_size), long(std::max(n_psi_basis, n_phi_basis)) * std::max(rows(), cols()));
491 // logger().debug("buffer_size {}", buffer_size);
492
493 stiffness.resize(n_phi_basis * rows(), n_psi_basis * cols());
494 stiffness.setZero();
495
496 auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, stiffness.rows(), stiffness.cols()));
497
498 const int n_bases = int(phi_bases.size());
499 igl::Timer timer;
500 timer.start();
501
502 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
503 LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
504 ElementAssemblyValues psi_vals, phi_vals;
505
506 for (int e = start; e < end; ++e)
507 {
508 // psi_vals.compute(e, is_volume, psi_bases[e], gbases[e]);
509 // phi_vals.compute(e, is_volume, phi_bases[e], gbases[e]);
510 psi_cache.compute(e, is_volume, psi_bases[e], gbases[e], psi_vals);
511 phi_cache.compute(e, is_volume, phi_bases[e], gbases[e], phi_vals);
512
513 const Quadrature &quadrature = phi_vals.quadrature;
514
515 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
516 local_storage.da = phi_vals.det.array() * quadrature.weights.array();
517 const int n_phi_loc_bases = int(phi_vals.basis_values.size());
518 const int n_psi_loc_bases = int(psi_vals.basis_values.size());
519
520 for (int i = 0; i < n_psi_loc_bases; ++i)
521 {
522 const auto &global_i = psi_vals.basis_values[i].global;
523
524 for (int j = 0; j < n_phi_loc_bases; ++j)
525 {
526 const auto &global_j = phi_vals.basis_values[j].global;
527
528 const auto stiffness_val = assemble(MixedAssemblerData(psi_vals, phi_vals, t, i, j, local_storage.da));
529 assert(stiffness_val.size() == rows() * cols());
530
531 // igl::Timer t1; t1.start();
532 for (int n = 0; n < rows(); ++n)
533 {
534 for (int m = 0; m < cols(); ++m)
535 {
536 const double local_value = stiffness_val(n * cols() + m);
537
538 for (size_t ii = 0; ii < global_i.size(); ++ii)
539 {
540 const auto gi = global_i[ii].index * cols() + m;
541 const auto wi = global_i[ii].val;
542
543 for (size_t jj = 0; jj < global_j.size(); ++jj)
544 {
545 const auto gj = global_j[jj].index * rows() + n;
546 const auto wj = global_j[jj].val;
547
548 local_storage.cache->add_value(e, gj, gi, local_value * wi * wj);
549
550 if (local_storage.cache->entries_size() >= max_triplets_size)
551 {
552 local_storage.cache->prune();
553 logger().debug("cleaning memory...");
554 }
555 }
556 }
557 }
558 }
559 }
560 }
561 }
562 });
563
564 timer.stop();
565 logger().trace("done separate assembly {}s...", timer.getElapsedTime());
566
567 timer.start();
568 // Serially merge local storages
569 for (LocalThreadMatStorage &local_storage : storage)
570 stiffness += local_storage.cache->get_matrix(false); // will also prune
571 stiffness.makeCompressed();
572 timer.stop();
573 logger().trace("done merge assembly {}s...", timer.getElapsedTime());
574
575 // stiffness.resize(n_basis*size(), n_basis*size());
576 // stiffness.setFromTriplets(entries.begin(), entries.end());
577 }
578
580 const bool is_volume,
581 const int n_psi_basis,
582 const int n_phi_basis,
583 const std::vector<ElementBases> &psi_bases,
584 const std::vector<ElementBases> &phi_bases,
585 const std::vector<ElementBases> &gbases,
586 const AssemblyValsCache &psi_cache,
587 const AssemblyValsCache &phi_cache,
588 const double t,
589 const double dt,
590 const Eigen::MatrixXd &x,
591 const Eigen::MatrixXd &x_prev,
592 const SolutionSplitter &split_solution) const
593 {
594 assert(size() > 0);
595 assert(rows() > 0);
596 assert(cols() > 0);
597 assert(phi_bases.size() == psi_bases.size());
598 assert(phi_bases.size() == gbases.size());
599
600 const int phi_ndof = n_phi_basis * rows();
601 const int psi_ndof = n_psi_basis * cols();
602 Eigen::MatrixXd x_phi, x_psi;
603 Eigen::MatrixXd x_phi_prev, x_psi_prev;
604 split_mixed_solution(split_solution, x, phi_ndof, psi_ndof, x_phi, x_psi);
605 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
606
607 auto storage = create_thread_storage(LocalThreadScalarStorage());
608 const int n_bases = int(phi_bases.size());
609
610 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
611 LocalThreadScalarStorage &local_storage = get_local_thread_storage(storage, thread_id);
612 ElementAssemblyValues &phi_vals = local_storage.vals;
613 ElementAssemblyValues psi_vals;
614
615 for (int e = start; e < end; ++e)
616 {
617 compute_mixed_element_values(
618 e, is_volume,
619 psi_bases[e], phi_bases[e], gbases[e],
620 psi_cache, phi_cache,
621 psi_vals, phi_vals);
622
623 const Quadrature &quadrature = phi_vals.quadrature;
624 assert(psi_vals.quadrature.weights.size() == quadrature.weights.size());
625 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
626 local_storage.da = phi_vals.det.array() * quadrature.weights.array();
627
628 local_storage.val += compute_energy(
629 MixedNonLinearAssemblerData(psi_vals, phi_vals, t, dt, x_phi, x_psi, x_phi_prev, x_psi_prev, local_storage.da));
630 }
631 });
632
633 double res = 0;
634 for (const LocalThreadScalarStorage &local_storage : storage)
635 res += local_storage.val;
636 return res;
637 }
638
640 const bool is_volume,
641 const int n_psi_basis,
642 const int n_phi_basis,
643 const std::vector<ElementBases> &psi_bases,
644 const std::vector<ElementBases> &phi_bases,
645 const std::vector<ElementBases> &gbases,
646 const AssemblyValsCache &psi_cache,
647 const AssemblyValsCache &phi_cache,
648 const double t,
649 const double dt,
650 const Eigen::MatrixXd &x,
651 const Eigen::MatrixXd &x_prev,
652 const SolutionSplitter &split_solution) const
653 {
654 assert(size() > 0);
655 assert(rows() > 0);
656 assert(cols() > 0);
657 assert(phi_bases.size() == psi_bases.size());
658 assert(phi_bases.size() == gbases.size());
659
660 const int phi_ndof = n_phi_basis * rows();
661 const int psi_ndof = n_psi_basis * cols();
662 Eigen::MatrixXd x_phi, x_psi;
663 Eigen::MatrixXd x_phi_prev, x_psi_prev;
664 split_mixed_solution(split_solution, x, phi_ndof, psi_ndof, x_phi, x_psi);
665 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
666
667 auto storage = create_thread_storage(LocalThreadScalarStorage());
668 const int n_bases = int(phi_bases.size());
669 Eigen::VectorXd out(phi_bases.size());
670
671 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
672 LocalThreadScalarStorage &local_storage = get_local_thread_storage(storage, thread_id);
673 ElementAssemblyValues &phi_vals = local_storage.vals;
674 ElementAssemblyValues psi_vals;
675
676 for (int e = start; e < end; ++e)
677 {
678 compute_mixed_element_values(
679 e, is_volume,
680 psi_bases[e], phi_bases[e], gbases[e],
681 psi_cache, phi_cache,
682 psi_vals, phi_vals);
683
684 const Quadrature &quadrature = phi_vals.quadrature;
685 assert(psi_vals.quadrature.weights.size() == quadrature.weights.size());
686 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
687 local_storage.da = phi_vals.det.array() * quadrature.weights.array();
688
689 out(e) = compute_energy(
690 MixedNonLinearAssemblerData(psi_vals, phi_vals, t, dt, x_phi, x_psi, x_phi_prev, x_psi_prev, local_storage.da));
691 }
692 });
693
694#ifndef NDEBUG
695 const double assemble_val = assemble_energy(
696 is_volume, n_psi_basis, n_phi_basis,
697 psi_bases, phi_bases, gbases,
698 psi_cache, phi_cache, t, dt, x, x_prev, split_solution);
699 assert(std::abs(assemble_val - out.sum()) < std::max(1e-10 * std::abs(assemble_val), 1e-10));
700#endif
701
702 return out;
703 }
704
706 const bool is_volume,
707 const int n_psi_basis,
708 const int n_phi_basis,
709 const std::vector<ElementBases> &psi_bases,
710 const std::vector<ElementBases> &phi_bases,
711 const std::vector<ElementBases> &gbases,
712 const AssemblyValsCache &psi_cache,
713 const AssemblyValsCache &phi_cache,
714 const double t,
715 const double dt,
716 const Eigen::MatrixXd &x,
717 const Eigen::MatrixXd &x_prev,
718 const SolutionSplitter &split_solution,
719 Eigen::MatrixXd &grad) const
720 {
721 assert(size() > 0);
722 assert(rows() > 0);
723 assert(cols() > 0);
724 assert(phi_bases.size() == psi_bases.size());
725 assert(phi_bases.size() == gbases.size());
726
727 const int phi_ndof = n_phi_basis * rows();
728 const int psi_ndof = n_psi_basis * cols();
729 Eigen::MatrixXd x_phi, x_psi;
730 Eigen::MatrixXd x_phi_prev, x_psi_prev;
731 split_mixed_solution(split_solution, x, phi_ndof, psi_ndof, x_phi, x_psi);
732 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
733
734 grad.resize(phi_ndof + psi_ndof, 1);
735 grad.setZero();
736
737 auto storage = create_thread_storage(LocalThreadVecStorage(grad.size()));
738 const int n_bases = int(phi_bases.size());
739
740 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
741 LocalThreadVecStorage &local_storage = get_local_thread_storage(storage, thread_id);
742 ElementAssemblyValues &phi_vals = local_storage.vals;
743 ElementAssemblyValues psi_vals;
744
745 for (int e = start; e < end; ++e)
746 {
747 compute_mixed_element_values(
748 e, is_volume,
749 psi_bases[e], phi_bases[e], gbases[e],
750 psi_cache, phi_cache,
751 psi_vals, phi_vals);
752
753 const Quadrature &quadrature = phi_vals.quadrature;
754 assert(psi_vals.quadrature.weights.size() == quadrature.weights.size());
755 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
756 local_storage.da = phi_vals.det.array() * quadrature.weights.array();
757
758 const Eigen::VectorXd local_grad = compute_gradient(
759 MixedNonLinearAssemblerData(psi_vals, phi_vals, t, dt, x_phi, x_psi, x_phi_prev, x_psi_prev, local_storage.da));
760
761 const int n_phi_loc_bases = int(phi_vals.basis_values.size());
762 const int n_psi_loc_bases = int(psi_vals.basis_values.size());
763 assert(local_grad.size() == n_phi_loc_bases * rows() + n_psi_loc_bases * cols());
764
765 for (int i = 0; i < n_phi_loc_bases; ++i)
766 {
767 const auto &global_i = phi_vals.basis_values[i].global;
768 for (int d = 0; d < rows(); ++d)
769 {
770 const double local_value = local_grad(i * rows() + d);
771 for (const auto &global : global_i)
772 local_storage.vec(global.index * rows() + d) += local_value * global.val;
773 }
774 }
775
776 const int local_psi_offset = n_phi_loc_bases * rows();
777 for (int i = 0; i < n_psi_loc_bases; ++i)
778 {
779 const auto &global_i = psi_vals.basis_values[i].global;
780 for (int d = 0; d < cols(); ++d)
781 {
782 const double local_value = local_grad(local_psi_offset + i * cols() + d);
783 for (const auto &global : global_i)
784 local_storage.vec(phi_ndof + global.index * cols() + d) += local_value * global.val;
785 }
786 }
787 }
788 });
789
790 for (const LocalThreadVecStorage &local_storage : storage)
791 grad += local_storage.vec;
792 }
793
795 const bool is_volume,
796 const int n_psi_basis,
797 const int n_phi_basis,
798 const bool project_to_psd,
799 const std::vector<ElementBases> &psi_bases,
800 const std::vector<ElementBases> &phi_bases,
801 const std::vector<ElementBases> &gbases,
802 const AssemblyValsCache &psi_cache,
803 const AssemblyValsCache &phi_cache,
804 const double t,
805 const double dt,
806 const Eigen::MatrixXd &x,
807 const Eigen::MatrixXd &x_prev,
808 const SolutionSplitter &split_solution,
809 MatrixCache &mat_cache,
810 StiffnessMatrix &hessian) const
811 {
812 assert(size() > 0);
813 assert(rows() > 0);
814 assert(cols() > 0);
815 assert(phi_bases.size() == psi_bases.size());
816 assert(phi_bases.size() == gbases.size());
817
818 const int phi_ndof = n_phi_basis * rows();
819 const int psi_ndof = n_psi_basis * cols();
820 const int total_ndof = phi_ndof + psi_ndof;
821 Eigen::MatrixXd x_phi, x_psi;
822 Eigen::MatrixXd x_phi_prev, x_psi_prev;
823 split_mixed_solution(split_solution, x, phi_ndof, psi_ndof, x_phi, x_psi);
824 split_mixed_previous_solution(split_solution, x_prev, phi_ndof, psi_ndof, x_phi_prev, x_psi_prev);
825
826 const int max_triplets_size = int(1e7);
827 const int buffer_size = std::min(max_triplets_size, total_ndof);
828
829 mat_cache.init(total_ndof);
830 mat_cache.set_zero();
831
832 auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, mat_cache));
833 const int n_bases = int(phi_bases.size());
834
835 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
836 LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
837 ElementAssemblyValues &phi_vals = local_storage.vals;
838 ElementAssemblyValues psi_vals;
839
840 for (int e = start; e < end; ++e)
841 {
842 compute_mixed_element_values(
843 e, is_volume,
844 psi_bases[e], phi_bases[e], gbases[e],
845 psi_cache, phi_cache,
846 psi_vals, phi_vals);
847
848 const Quadrature &quadrature = phi_vals.quadrature;
849 assert(psi_vals.quadrature.weights.size() == quadrature.weights.size());
850 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
851 local_storage.da = phi_vals.det.array() * quadrature.weights.array();
852
853 Eigen::MatrixXd local_hessian = compute_hessian(
854 MixedNonLinearAssemblerData(psi_vals, phi_vals, t, dt, x_phi, x_psi, x_phi_prev, x_psi_prev, local_storage.da));
855
856 const int n_phi_loc_bases = int(phi_vals.basis_values.size());
857 const int n_psi_loc_bases = int(psi_vals.basis_values.size());
858 const int local_size = n_phi_loc_bases * rows() + n_psi_loc_bases * cols();
859 assert(local_hessian.rows() == local_size);
860 assert(local_hessian.cols() == local_size);
861
862 if (project_to_psd)
863 local_hessian = ipc::project_to_psd(local_hessian);
864
865 const auto local_index_data = [&](
866 const int local_index,
867 const std::vector<basis::Local2Global> *&global,
868 int &component,
869 int &component_count,
870 int &global_offset) {
871 assert(local_index >= 0);
872 assert(local_index < local_size);
873
874 const int phi_local_size = n_phi_loc_bases * rows();
875 if (local_index < phi_local_size)
876 {
877 const int basis_index = local_index / rows();
878 global = &phi_vals.basis_values[basis_index].global;
879 component = local_index % rows();
880 component_count = rows();
881 global_offset = 0;
882 return;
883 }
884
885 const int psi_local_index = local_index - phi_local_size;
886 const int basis_index = psi_local_index / cols();
887 global = &psi_vals.basis_values[basis_index].global;
888 component = psi_local_index % cols();
889 component_count = cols();
890 global_offset = phi_ndof;
891 };
892
893 for (int i = 0; i < local_size; ++i)
894 {
895 const std::vector<basis::Local2Global> *global_i = nullptr;
896 int component_i = -1;
897 int component_count_i = -1;
898 int global_offset_i = -1;
899 local_index_data(i, global_i, component_i, component_count_i, global_offset_i);
900
901 for (int j = 0; j < local_size; ++j)
902 {
903 const double local_value = local_hessian(i, j);
904 if (local_value == 0)
905 continue;
906
907 const std::vector<basis::Local2Global> *global_j = nullptr;
908 int component_j = -1;
909 int component_count_j = -1;
910 int global_offset_j = -1;
911 local_index_data(j, global_j, component_j, component_count_j, global_offset_j);
912
913 for (const auto &gi : *global_i)
914 {
915 const int row = global_offset_i + gi.index * component_count_i + component_i;
916 for (const auto &gj : *global_j)
917 {
918 const int col = global_offset_j + gj.index * component_count_j + component_j;
919 local_storage.cache->add_value(e, row, col, local_value * gi.val * gj.val);
920 }
921 }
922
923 if (local_storage.cache->entries_size() >= max_triplets_size)
924 {
925 local_storage.cache->prune();
926 logger().debug("cleaning memory...");
927 }
928 }
929 }
930 }
931 });
932
933 for (LocalThreadMatStorage &local_storage : storage)
934 {
935 local_storage.cache->prune();
936 mat_cache += *local_storage.cache;
937 }
938 hessian = mat_cache.get_matrix();
939 }
940
942 const bool is_volume,
943 const std::vector<ElementBases> &bases,
944 const std::vector<ElementBases> &gbases,
946 const double t,
947 const double dt,
948 const Eigen::MatrixXd &displacement,
949 const Eigen::MatrixXd &displacement_prev) const
950 {
951 auto storage = create_thread_storage(LocalThreadScalarStorage());
952 const int n_bases = int(bases.size());
953
954 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
955 LocalThreadScalarStorage &local_storage = get_local_thread_storage(storage, thread_id);
956 ElementAssemblyValues &vals = local_storage.vals;
957
958 for (int e = start; e < end; ++e)
959 {
960 cache.compute(e, is_volume, bases[e], gbases[e], vals);
961
962 const Quadrature &quadrature = vals.quadrature;
963
964 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
965 local_storage.da = vals.det.array() * quadrature.weights.array();
966
967 const double val = compute_energy(NonLinearAssemblerData(vals, t, dt, displacement, displacement_prev, local_storage.da));
968 local_storage.val += val;
969 }
970 });
971
972 double res = 0;
973 // Serially merge local storages
974 for (const LocalThreadScalarStorage &local_storage : storage)
975 res += local_storage.val;
976 return res;
977 }
978
980 const bool is_volume,
981 const std::vector<ElementBases> &bases,
982 const std::vector<ElementBases> &gbases,
984 const double t,
985 const double dt,
986 const Eigen::MatrixXd &displacement,
987 const Eigen::MatrixXd &displacement_prev) const
988 {
989 auto storage = create_thread_storage(LocalThreadScalarStorage());
990 const int n_bases = int(bases.size());
991 Eigen::VectorXd out(bases.size());
992
993 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
994 LocalThreadScalarStorage &local_storage = get_local_thread_storage(storage, thread_id);
995 ElementAssemblyValues &vals = local_storage.vals;
996
997 for (int e = start; e < end; ++e)
998 {
999 cache.compute(e, is_volume, bases[e], gbases[e], vals);
1000
1001 const Quadrature &quadrature = vals.quadrature;
1002
1003 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
1004 local_storage.da = vals.det.array() * quadrature.weights.array();
1005
1006 const double val = compute_energy(NonLinearAssemblerData(vals, t, dt, displacement, displacement_prev, local_storage.da));
1007 out[e] = val;
1008 }
1009 });
1010
1011#ifndef NDEBUG
1012 const double assemble_val = assemble_energy(
1013 is_volume, bases, gbases, cache, t, dt, displacement, displacement_prev);
1014 assert(std::abs(assemble_val - out.sum()) < std::max(1e-10 * assemble_val, 1e-10));
1015#endif
1016
1017 return out;
1018 }
1019
1021 const bool is_volume,
1022 const int n_basis,
1023 const std::vector<ElementBases> &bases,
1024 const std::vector<ElementBases> &gbases,
1025 const AssemblyValsCache &cache,
1026 const double t,
1027 const double dt,
1028 const Eigen::MatrixXd &displacement,
1029 const Eigen::MatrixXd &displacement_prev,
1030 Eigen::MatrixXd &rhs) const
1031 {
1032 rhs.resize(n_basis * size(), 1);
1033 rhs.setZero();
1034
1035 auto storage = create_thread_storage(LocalThreadVecStorage(rhs.size()));
1036
1037 const int n_bases = int(bases.size());
1038
1039 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
1040 LocalThreadVecStorage &local_storage = get_local_thread_storage(storage, thread_id);
1041
1042 for (int e = start; e < end; ++e)
1043 {
1044 // igl::Timer timer; timer.start();
1045
1046 ElementAssemblyValues &vals = local_storage.vals;
1047 // vals.compute(e, is_volume, bases[e], gbases[e]);
1048 cache.compute(e, is_volume, bases[e], gbases[e], vals);
1049
1050 const Quadrature &quadrature = vals.quadrature;
1051
1052 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
1053 local_storage.da = vals.det.array() * quadrature.weights.array();
1054 const int n_loc_bases = int(vals.basis_values.size());
1055
1056 const auto val = assemble_gradient(NonLinearAssemblerData(vals, t, dt, displacement, displacement_prev, local_storage.da));
1057 assert(val.size() == n_loc_bases * size());
1058
1059 for (int j = 0; j < n_loc_bases; ++j)
1060 {
1061 const auto &global_j = vals.basis_values[j].global;
1062
1063 // igl::Timer t1; t1.start();
1064 for (int m = 0; m < size(); ++m)
1065 {
1066 const double local_value = val(j * size() + m);
1067
1068 for (size_t jj = 0; jj < global_j.size(); ++jj)
1069 {
1070 const auto gj = global_j[jj].index * size() + m;
1071 const auto wj = global_j[jj].val;
1072
1073 local_storage.vec(gj) += local_value * wj;
1074 }
1075 }
1076
1077 // t1.stop();
1078 // if (!vals.has_parameterization) { logger().trace("-- t1: ", t1.getElapsedTime()); }
1079 }
1080
1081 // timer.stop();
1082 // if (!vals.has_parameterization) { logger().trace("-- Timer: ", timer.getElapsedTime()); }
1083 }
1084 });
1085
1086 // Serially merge local storages
1087 for (const LocalThreadVecStorage &local_storage : storage)
1088 rhs += local_storage.vec;
1089 }
1090
1092 const bool is_volume,
1093 const int n_basis,
1094 const bool project_to_psd,
1095 const std::vector<ElementBases> &bases,
1096 const std::vector<ElementBases> &gbases,
1097 const AssemblyValsCache &cache,
1098 const double t,
1099 const double dt,
1100 const Eigen::MatrixXd &displacement,
1101 const Eigen::MatrixXd &displacement_prev,
1102 MatrixCache &mat_cache,
1103 StiffnessMatrix &hess) const
1104 {
1105 const int max_triplets_size = int(1e7);
1106 const int buffer_size = std::min(long(max_triplets_size), long(n_basis) * size());
1107 // logger().trace("buffer_size {}", buffer_size);
1108
1109 // hess.resize(n_basis * size(), n_basis * size());
1110 // hess.setZero();
1111
1112 mat_cache.init(n_basis * size());
1113 mat_cache.set_zero();
1114
1115 auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, mat_cache));
1116
1117 const int n_bases = int(bases.size());
1118 igl::Timer timer;
1119 timer.start();
1120
1121 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
1122 LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
1123
1124 for (int e = start; e < end; ++e)
1125 {
1126 // Thread-owned scratch: keep `vals`/`da` local to this loop body
1127 // rather than aliasing the shared per-thread `local_storage`.
1128 // The reused `local_storage.vals` was the site of a cross-thread
1129 // use-after-free: its <Dynamic,3> basis-gradient buffers get
1130 // resized as element types change on hybrid meshes (tet 10 /
1131 // pyramid 14 / prism 18), so one worker could free/realloc a
1132 // buffer another was still reading. Only the accumulation cache
1133 // needs to persist per thread.
1134 ElementAssemblyValues vals;
1136 cache.compute(e, is_volume, bases[e], gbases[e], vals);
1137
1138 const Quadrature &quadrature = vals.quadrature;
1139
1140 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
1141 da = vals.det.array() * quadrature.weights.array();
1142 const int n_loc_bases = int(vals.basis_values.size());
1143
1144 auto stiffness_val = assemble_hessian(NonLinearAssemblerData(vals, t, dt, displacement, displacement_prev, da));
1145 assert(stiffness_val.rows() == n_loc_bases * size());
1146 assert(stiffness_val.cols() == n_loc_bases * size());
1147
1148 if (project_to_psd)
1149 stiffness_val = ipc::project_to_psd(stiffness_val);
1150
1151 // bool has_nan = false;
1152 // for(int k = 0; k < stiffness_val.size(); ++k)
1153 // {
1154 // if(std::isnan(stiffness_val(k)))
1155 // {
1156 // has_nan = true;
1157 // break;
1158 // }
1159 // }
1160
1161 // if(has_nan)
1162 // {
1163 // local_storage.entries.emplace_back(0, 0, std::nan(""));
1164 // break;
1165 // }
1166
1167 for (int i = 0; i < n_loc_bases; ++i)
1168 {
1169 const auto &global_i = vals.basis_values[i].global;
1170
1171 for (int j = 0; j < n_loc_bases; ++j)
1172 // for(int j = 0; j <= i; ++j)
1173 {
1174 const auto &global_j = vals.basis_values[j].global;
1175
1176 for (int n = 0; n < size(); ++n)
1177 {
1178 for (int m = 0; m < size(); ++m)
1179 {
1180 const double local_value = stiffness_val(i * size() + m, j * size() + n);
1181
1182 for (size_t ii = 0; ii < global_i.size(); ++ii)
1183 {
1184 const auto gi = global_i[ii].index * size() + m;
1185 const auto wi = global_i[ii].val;
1186
1187 for (size_t jj = 0; jj < global_j.size(); ++jj)
1188 {
1189 const auto gj = global_j[jj].index * size() + n;
1190 const auto wj = global_j[jj].val;
1191
1192 local_storage.cache->add_value(e, gi, gj, local_value * wi * wj);
1193 // if (j < i) {
1194 // local_storage.entries.emplace_back(gj, gi, local_value * wj * wi);
1195 // }
1196
1197 if (local_storage.cache->entries_size() >= max_triplets_size)
1198 {
1199 local_storage.cache->prune();
1200 logger().debug("cleaning memory...");
1201 }
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209 });
1210
1211 timer.stop();
1212 logger().trace("done separate assembly {}s...", timer.getElapsedTime());
1213
1214 timer.start();
1215
1216 // Serially merge local storages
1217 for (LocalThreadMatStorage &local_storage : storage)
1218 {
1219 local_storage.cache->prune();
1220 mat_cache += *local_storage.cache;
1221 }
1222 hess = mat_cache.get_matrix();
1223
1224 timer.stop();
1225 logger().trace("done merge assembly {}s...", timer.getElapsedTime());
1226 }
1227
1229 {
1230#ifdef POLYFEM_WITH_MISO
1231 use_robust_jacobian = true;
1232#else
1233 logger().error("Enable Bezier library to use the robust Jacobian check!");
1234#endif
1235 }
1236} // namespace polyfem::assembler
Eigen::MatrixXd vec
Definition Assembler.cpp:76
double val
Definition Assembler.cpp:90
QuadratureVector da
Definition Assembler.cpp:27
std::unique_ptr< MatrixCache > cache
Definition Assembler.cpp:25
ElementAssemblyValues vals
Definition Assembler.cpp:26
Quadrature quadrature
int x
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 &params, 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,...
stores per element basis values at given quadrature points and geometric mapping
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 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
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
Used for test only.
constexpr const char * MATERIAL_ELEMENT_INDEX
Definition MatParams.hpp:9
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.
Definition Logger.cpp:44
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:17
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24