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