PolyFEM
Loading...
Searching...
No Matches
ElasticVarForm.cpp
Go to the documentation of this file.
1#include "ElasticVarForm.hpp"
2
4
6
10
12
16
18
21
23
25
30
31#include <algorithm>
32#include <map>
33#include <ostream>
34
35#include <igl/Timer.h>
36
37#include <spdlog/fmt/fmt.h>
38
39namespace polyfem::varform
40{
43 const Eigen::MatrixXd &solution,
44 const io::CheckpointMetadata &metadata) const
45 {
46 VarForm::serialize_checkpoint(writer, solution, metadata);
47 write_checkpoint_ordering(writer, "primary", space_);
48 }
49
51 const io::CheckpointReader &reader,
52 Eigen::MatrixXd &solution)
53 {
54 VarForm::deserialize_checkpoint(reader, solution);
57 reader, "primary", space_,
58 mesh_->dimension(), 0, solution);
59 }
60
62 {
64 space_.reset();
69 rhs_assembler_ = nullptr;
70 mass_.resize(0, 0);
71 pure_mass_.resize(0, 0);
72 avg_mass_ = 0;
73 rhs_.resize(0, 0);
74 primary_assembler_ = nullptr;
75 mass_assembler_ = nullptr;
76 pure_mass_assembler_ = nullptr;
77 t0 = 0;
78 time_steps = 0;
79 dt = 0;
80 }
81
82 void ElasticVarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
83 {
84 VarForm::init(formulation, units, args, out_path);
85 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
86
88 assert(primary_assembler_->name() == formulation);
89 if (args["solver"]["advanced"]["check_inversion"] == "Conservative")
90 {
91 if (auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(primary_assembler_))
92 elastic_assembler->set_use_robust_jacobian();
93 }
94 mass_assembler_ = std::make_shared<assembler::Mass>();
95 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
96
97 if (!args.contains("preset_problem"))
98 {
99 problem = std::make_shared<assembler::GenericTensorProblem>("GenericTensor");
100
101 problem->clear();
102 json tmp;
103 tmp["is_time_dependent"] = is_time_dependent;
104 problem->set_parameters(tmp, resources_);
105
106 // important for the BC
107
108 auto bc = args["boundary_conditions"];
109 problem->set_parameters(bc, resources_);
110 problem->set_parameters(args["initial_conditions"], resources_);
111 problem->set_parameters(args["output"], resources_);
112 }
113 else
114 {
115 if (args["preset_problem"]["type"] == "Kernel")
116 {
117 problem = std::make_shared<problem::KernelProblem>("Kernel", *primary_assembler_);
118 problem->clear();
119 problem::KernelProblem &kprob = *dynamic_cast<problem::KernelProblem *>(problem.get());
120 }
121 else
122 {
123 problem = problem::ProblemFactory::factory().get_problem(args["preset_problem"]["type"]);
124 problem->clear();
125 }
126 // important for the BC
127 problem->set_parameters(args["preset_problem"], resources_);
128 }
129
130 problem->set_units(*primary_assembler_, units);
131
132 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
133 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
134 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
135 }
136
137 void ElasticVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
138 {
141 pure_mass_assembler_->set_size(mass_assembler_->size());
142
143 problem->init(mesh);
144
145 if (assembler::MultiModel *mm = dynamic_cast<assembler::MultiModel *>(primary_assembler_.get()))
146 {
147 assert(args["materials"].is_array());
148
149 std::vector<std::string> materials(mesh.n_elements());
150
151 std::map<int, std::string> mats;
152
153 for (const auto &m : args["materials"])
154 mats[m["id"].get<int>()] = m["type"];
155
156 for (int i = 0; i < materials.size(); ++i)
157 materials[i] = mats.at(mesh.get_body_id(i));
158
159 mm->init_multimodels(materials);
160 }
161 }
162
163 void ElasticVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
164 {
165 build_elastic_basis(mesh, iso_parametric, args, -1);
166 }
167
168 void ElasticVarForm::build_elastic_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args, const int fe_space_id)
169 {
170 assert(problem);
171 assert(primary_assembler_);
172 assert(mass_assembler_);
173 assert(pure_mass_assembler_);
174
175 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
176 assign_discr_orders(args["space"], fe_space_id, mesh, space_disc_orders, space_disc_ordersq);
177
178 if (args["space"]["use_p_ref"])
179 {
181 mesh,
182 args["space"]["advanced"]["B"],
183 args["space"]["advanced"]["h1_formula"],
184 args["space"]["discr_order"],
185 args["space"]["advanced"]["discr_order_max"],
186 stats,
187 space_disc_orders);
188
189 logger().info("min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
190 }
191
193 mesh,
194 iso_parametric,
195 space_disc_orders,
196 space_disc_ordersq,
197 args["space"]["basis_type"],
198 args["space"]["poly_basis_type"],
200 mesh.dimension(),
201 args["space"]["advanced"]["quadrature_order"],
202 args["space"]["advanced"]["mass_quadrature_order"],
203 args["space"]["advanced"]["use_corner_quadrature"],
204 args["space"]["advanced"]["n_harmonic_samples"],
205 args["space"]["advanced"]["integral_constraints"],
206 space_,
207 boundary_);
208
209 problem->update_nodes(space_.space_in_node_to_node);
211
213 problem->setup_bc(
214 mesh, assembler::BoundaryKind::Dirichlet, fe_space_id,
217 problem->setup_bc(
218 mesh, assembler::BoundaryKind::Neumann, fe_space_id,
220
221 problem->setup_nodal_bc(
223 problem->setup_nodal_bc(
225
226 for (const int n_id : boundary_.dirichlet_nodes)
227 {
228 const int tag = mesh.get_node_id(n_id);
229 for (int d = 0; d < mesh.dimension(); ++d)
230 if (problem->is_nodal_dimension_dirichlet(n_id, tag, d, fe_space_id))
231 boundary_.boundary_nodes.push_back(n_id * mesh.dimension() + d);
232 }
234
237
238 const auto &current_bases = space_.geometry_basis_list();
239 if (args["space"]["advanced"]["count_flipped_els"])
240 stats.count_flipped_elements(mesh, current_bases);
241
242 const int n_samples = 10;
243 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
244
245 logger().info("flipped elements {}", stats.n_flipped);
246 logger().info("h: {}", stats.mesh_size);
247
248 if (space_.n_bases <= args["solver"]["advanced"]["cache_size"])
249 {
250 igl::Timer timer;
251 timer.start();
252 logger().info("Building cache...");
253 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
254 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
255 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
256 logger().info(" took {}s", timer.getElapsedTime());
257 }
258 else
259 {
263 }
264 }
265
267 {
268 json rhs_solver_params = args["solver"]["linear"];
269 if (!rhs_solver_params.contains("Pardiso"))
270 rhs_solver_params["Pardiso"] = {};
271 rhs_solver_params["Pardiso"]["mtype"] = -2;
272
273 rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
274 *primary_assembler_, *mesh_, nullptr,
278 args["space"]["advanced"]["bc_method"],
279 rhs_solver_params,
280 /*fe_space_id=*/-1);
281 }
282
284 {
285 igl::Timer timer;
286 json p_params = {};
287 p_params["formulation"] = primary_assembler_->name();
288 {
289 RowVectorNd min, max, delta;
290 mesh.bounding_box(min, max);
291 delta = (max - min) / 2. + min;
292 if (mesh.is_volume())
293 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
294 else
295 p_params["bbox_center"] = {delta(0), delta(1)};
296 }
297 problem->set_parameters(p_params, resources_);
298
299 rhs_.resize(0, 0);
300
301 timer.start();
302 logger().info("Assigning rhs...");
303
305 assert(rhs_assembler_ != nullptr);
306 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
307 rhs_ *= -1;
308
309 timings.assigning_rhs_time = timer.getElapsedTime();
310 logger().info(" took {}s", timings.assigning_rhs_time);
311 }
312
313 void ElasticVarForm::assemble_mass_mat(const mesh::Mesh &mesh, const json &args)
314 {
315 if (!problem->is_time_dependent())
316 {
317 avg_mass_ = 1;
319 if (!primary_assembler_->is_linear())
321 return;
322 }
323
324 mass_.resize(0, 0);
325
326 igl::Timer timer;
327 timer.start();
328 logger().info("Assembling mass mat...");
329
331 if (!primary_assembler_->is_linear())
333
334 assert(mass_.size() > 0);
335
336 avg_mass_ = 0;
337 for (int k = 0; k < mass_.outerSize(); ++k)
338 for (StiffnessMatrix::InnerIterator it(mass_, k); it; ++it)
339 {
340 assert(it.col() == k);
341 avg_mass_ += it.value();
342 }
343
344 avg_mass_ /= mass_.rows();
345 logger().info("average mass {}", avg_mass_);
346
347 if (args["solver"]["advanced"]["lump_mass_matrix"])
349
350 timer.stop();
351 timings.assembling_mass_mat_time = timer.getElapsedTime();
352 logger().info(" took {}s", timings.assembling_mass_mat_time);
353
354 stats.nn_zero = mass_.nonZeros();
355 stats.num_dofs = mass_.rows();
356 stats.mat_size = (long long)mass_.rows() * (long long)mass_.cols();
357 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
358 }
359
361 Eigen::MatrixXd &velocity,
362 const InitialConditionOverride *override,
363 const std::string &state_prefix) const
364 {
365 assert(rhs_assembler_ != nullptr);
366 if (override && override->velocity.size() != 0)
367 {
368 velocity = override->velocity;
369 assert(
370 velocity.rows() == space_.ndof() && velocity.cols() >= 1
371 && "Initial velocity override must match the simulation DOFs");
372 return;
373 }
374
375 rhs_assembler_->initial_velocity(velocity);
376 }
377
379 Eigen::MatrixXd &acceleration,
380 const InitialConditionOverride *override,
381 const std::string &state_prefix) const
382 {
383 assert(rhs_assembler_ != nullptr);
384 if (override && override->acceleration.size() != 0)
385 {
386 acceleration = override->acceleration;
387 assert(
388 acceleration.rows() == space_.ndof() && acceleration.cols() >= 1
389 && "Initial acceleration override must match the simulation DOFs");
390 return;
391 }
392
393 rhs_assembler_->initial_acceleration(acceleration);
394 }
395
397 Eigen::MatrixXd &solution,
398 const InitialConditionOverride *override,
399 const std::string &state_prefix) const
400 {
401 assert(rhs_assembler_ != nullptr);
402 if (override && override->solution.size() != 0)
403 {
404 solution = override->solution;
405 assert(
406 solution.rows() == space_.ndof() && solution.cols() >= 1
407 && "Initial solution override must match the simulation DOFs");
408 return;
409 }
410
411 if (problem->is_time_dependent())
412 rhs_assembler_->initial_solution(solution);
413 else
414 {
415 solution.resize(rhs_.size(), 1);
416 solution.setZero();
417 }
418 }
419
421 {
422 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
423 return n_boundary_samples(space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gdiscr_order);
424 }
425
427 {
428 if (!mesh_ || !space_.geometry)
429 return {};
430
431 const auto &nodes = space_.is_iso_parametric() ? space_.mesh_nodes : space_.geometry->mesh_nodes;
432 if (!nodes)
433 return {};
434
435 auto indices = nodes->primitive_to_node();
436 indices.resize(mesh_->n_vertices());
437 return indices;
438 }
439
441 {
442 const auto p2n = elastic_primitive_to_node();
443 const int n_geometry_bases = space_.geometry ? space_.geometry->n_bases : 0;
444 std::vector<int> indices(n_geometry_bases, -1);
445 for (int i = 0; i < int(p2n.size()); ++i)
446 {
447 if (p2n[i] >= 0 && p2n[i] < int(indices.size()))
448 indices[p2n[i]] = i;
449 }
450 return indices;
451 }
452
453 std::vector<io::OutputField> ElasticVarForm::elastic_output_fields(
454 const io::OutputSample &sample,
455 const Eigen::MatrixXd &solution,
456 const io::OutputFieldOptions &options,
457 const mesh::Obstacle *obstacle,
458 const time_integrator::ImplicitTimeIntegrator *time_integrator,
459 const std::vector<std::pair<std::string, std::shared_ptr<solver::Form>>> &named_forms,
460 const solver::Form *elastic_form,
461 const solver::ContactForm *contact_form) const
462 {
463 std::vector<io::OutputField> fields;
464 if (!mesh_ || !problem || solution.size() <= 0)
465 return fields;
466
467 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
468 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
469
470 const int actual_dim = problem->is_scalar() ? 1 : mesh_->dimension();
471 const auto &paraview_options = args["output"]["paraview"]["options"];
472 const bool material_params = paraview_options["material"];
473 const bool body_ids = paraview_options["body_ids"];
474 const bool velocity = paraview_options["velocity"];
475 const bool acceleration = paraview_options["acceleration"];
476 const bool forces = paraview_options["forces"] && !problem->is_scalar();
477 const bool tensor_values = paraview_options["tensor_values"] && !problem->is_scalar();
478 const bool scalar_values = paraview_options["scalar_values"];
479 const bool use_spline = args["space"]["basis_type"] == "Spline";
480 const bool explicit_fields = !options.fields.empty();
481
482 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
483 if (output_rows <= values.rows())
484 return;
485
486 const int previous_rows = values.rows();
487 values.conservativeResize(output_rows, values.cols());
488 values.bottomRows(output_rows - previous_rows).setZero();
489 };
490
491 const auto append_obstacle_values = [&](Eigen::MatrixXd &sampled_values, const Eigen::MatrixXd &dof_values) -> bool {
492 if (!obstacle || obstacle->n_vertices() <= 0)
493 {
494 resize_to_output_rows(sampled_values);
495 return sample.points.rows() == 0 || sample.points.rows() == sampled_values.rows();
496 }
497
498 const bool has_obstacle_rows =
499 sample.points.rows() == sampled_values.rows() + obstacle->n_vertices()
500 && sample.points.cols() == obstacle->v().cols()
501 && sample.points.bottomRows(obstacle->n_vertices()).isApprox(obstacle->v());
502
503 if (!has_obstacle_rows)
504 return sample.points.rows() == 0 || sample.points.rows() == sampled_values.rows();
505
506 sampled_values.conservativeResize(sampled_values.rows() + obstacle->n_vertices(), sampled_values.cols());
507 if (dof_values.rows() >= obstacle->ndof())
508 {
509 const Eigen::MatrixXd obstacle_values =
510 utils::unflatten(dof_values.bottomRows(obstacle->ndof()), sampled_values.cols());
511 if (obstacle_values.rows() == obstacle->n_vertices()
512 && obstacle_values.cols() == sampled_values.cols())
513 sampled_values.bottomRows(obstacle->n_vertices()) = obstacle_values;
514 else
515 sampled_values.bottomRows(obstacle->n_vertices()).setZero();
516 }
517 else
518 sampled_values.bottomRows(obstacle->n_vertices()).setZero();
519 return true;
520 };
521
522 const auto sample_dof_field = [&](const Eigen::MatrixXd &dof_values, const int field_dim, Eigen::MatrixXd &values) -> bool {
523 if (dof_values.size() <= 0 || field_dim <= 0)
524 return false;
525
526 if (has_element_samples)
527 {
528 values.resize(sample.local_points.rows(), field_dim);
529 for (int i = 0; i < sample.local_points.rows(); ++i)
530 {
531 const int element_id = sample.element_ids(i);
532 if (element_id < 0)
533 {
534 values.row(i).setZero();
535 continue;
536 }
537
538 Eigen::MatrixXd local_sol, local_grad;
541 element_id, sample.local_points.row(i), dof_values, local_sol, local_grad);
542
543 for (int d = 0; d < field_dim; ++d)
544 values(i, d) = local_sol(d);
545 }
546
547 return append_obstacle_values(values, dof_values);
548 }
549
550 if (sample.node_ids.size() > 0)
551 {
552 values.resize(sample.node_ids.size(), field_dim);
553 for (int i = 0; i < sample.node_ids.size(); ++i)
554 {
555 const int node_id = sample.node_ids(i);
556 for (int d = 0; d < field_dim; ++d)
557 {
558 const int dof = node_id * field_dim + d;
559 if (dof < 0 || dof >= dof_values.rows())
560 return false;
561 values(i, d) = dof_values(dof);
562 }
563 }
564
565 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
566 }
567
568 return false;
569 };
570
571 const auto append_sampled_dof_field = [&](const std::string &name, const Eigen::MatrixXd &dof_values, const int field_dim) {
572 Eigen::MatrixXd values;
573 if (sample_dof_field(dof_values, field_dim, values))
574 fields.push_back({name, values, io::OutputField::Association::Point});
575 };
576
577 const auto append_scalar_values = [&]() {
578 if (!scalar_values || problem->is_scalar() || !has_element_samples)
579 return;
580
581 const bool wants_scalar = options.fields.empty()
582 || options.export_field("von_mises")
583 || options.export_field("von_mises_avg");
584 if (!wants_scalar)
585 return;
586
587 std::vector<assembler::Assembler::NamedMatrix> point_values;
588 for (int i = 0; i < sample.local_points.rows(); ++i)
589 {
590 const int element_id = sample.element_ids(i);
591 if (element_id < 0)
592 continue;
593
594 std::vector<assembler::Assembler::NamedMatrix> local_values;
595 primary_assembler_->compute_scalar_value(
596 assembler::OutputData(sample.time, element_id, space_.basis_list()[element_id], space_.geometry_basis_list()[element_id], sample.local_points.row(i), solution),
597 local_values);
598
599 if (point_values.empty())
600 {
601 point_values.resize(local_values.size());
602 for (int k = 0; k < local_values.size(); ++k)
603 {
604 point_values[k].first = local_values[k].first;
605 point_values[k].second.setZero(output_rows, local_values[k].second.cols());
606 }
607 }
608
609 for (int k = 0; k < local_values.size(); ++k)
610 point_values[k].second.row(i) = local_values[k].second;
611 }
612
613 for (const auto &[name, values] : point_values)
614 {
615 if (options.export_field(name))
616 fields.push_back({name, values, io::OutputField::Association::Point});
617 }
618 };
619
620 const auto append_tensor_values = [&]() {
621 if (!tensor_values || problem->is_scalar() || !has_element_samples)
622 return;
623
624 const bool wants_tensor = options.fields.empty()
625 || options.export_field("cauchy_stess")
626 || options.export_field("pk1_stess")
627 || options.export_field("pk2_stess")
628 || options.export_field("F");
629 if (!wants_tensor)
630 return;
631
632 std::vector<assembler::Assembler::NamedMatrix> point_values;
633 for (int i = 0; i < sample.local_points.rows(); ++i)
634 {
635 const int element_id = sample.element_ids(i);
636 if (element_id < 0)
637 continue;
638
639 std::vector<assembler::Assembler::NamedMatrix> local_values;
640 primary_assembler_->compute_tensor_value(
641 assembler::OutputData(sample.time, element_id, space_.basis_list()[element_id], space_.geometry_basis_list()[element_id], sample.local_points.row(i), solution),
642 local_values);
643
644 if (point_values.empty())
645 {
646 point_values.resize(local_values.size());
647 for (int k = 0; k < local_values.size(); ++k)
648 {
649 point_values[k].first = local_values[k].first;
650 point_values[k].second.setZero(output_rows, local_values[k].second.cols());
651 }
652 }
653
654 for (int k = 0; k < local_values.size(); ++k)
655 point_values[k].second.row(i) = local_values[k].second;
656 }
657
658 for (const auto &[name, values] : point_values)
659 {
660 if (!options.export_field(name))
661 continue;
662
663 const int stride = mesh_->dimension();
664 assert(values.cols() % stride == 0);
665 for (int i = 0; i < values.cols(); i += stride)
666 {
667 const int ii = (i / stride) + 1;
668 fields.push_back({fmt::format("{:s}_{:d}", name, ii), values.middleCols(i, stride), io::OutputField::Association::Point});
669 }
670 }
671 };
672
673 const auto append_averaged_values = [&]() {
674 if (use_spline || problem->is_scalar() || !has_element_samples || (!scalar_values && !tensor_values))
675 return;
676
677 const bool wants_avg = options.fields.empty()
678 || options.export_field("von_mises_avg")
679 || options.export_field("cauchy_stess_avg")
680 || options.export_field("pk1_stess_avg")
681 || options.export_field("pk2_stess_avg")
682 || options.export_field("F_avg");
683 if (!wants_avg)
684 return;
685
686 Eigen::MatrixXd areas(space_.n_bases, 1);
687 areas.setZero();
688 std::vector<assembler::Assembler::NamedMatrix> tmp_s, tmp_t;
689 std::vector<Eigen::MatrixXd> avg_scalar, avg_tensor;
690
691 for (int e = 0; e < int(space_.basis_list().size()); ++e)
692 {
693 Eigen::MatrixXd local_pts;
694 if (mesh_->is_simplex(e))
695 {
696 if (mesh_->dimension() == 3)
697 autogen::p_nodes_3d(space_.disc_orders(e), local_pts);
698 else
699 autogen::p_nodes_2d(space_.disc_orders(e), local_pts);
700 }
701 else if (mesh_->is_cube(e))
702 {
703 if (mesh_->dimension() == 3)
704 autogen::q_nodes_3d(space_.disc_orders(e), local_pts);
705 else
706 autogen::q_nodes_2d(space_.disc_orders(e), local_pts);
707 }
708 else if (mesh_->is_prism(e))
709 {
710 autogen::prism_nodes_3d(space_.disc_orders(e), space_.disc_ordersq(e), local_pts);
711 }
712 else
713 {
714 continue;
715 }
716
717 const basis::ElementBases &bs = space_.basis_list()[e];
718 const basis::ElementBases &gbs = space_.geometry_basis_list()[e];
719
720 assembler::ElementAssemblyValues vals;
721 vals.compute(e, mesh_->is_volume(), bs, gbs);
722 const double area = (vals.det.array() * vals.quadrature.weights.array()).sum();
723
724 if (scalar_values)
725 primary_assembler_->compute_scalar_value(assembler::OutputData(sample.time, e, bs, gbs, local_pts, solution), tmp_s);
726 if (tensor_values)
727 primary_assembler_->compute_tensor_value(assembler::OutputData(sample.time, e, bs, gbs, local_pts, solution), tmp_t);
728
729 if (avg_scalar.empty() && !tmp_s.empty())
730 {
731 avg_scalar.resize(tmp_s.size());
732 for (auto &m : avg_scalar)
733 m.setZero(space_.n_bases, 1);
734 }
735 if (avg_tensor.empty() && !tmp_t.empty())
736 {
737 avg_tensor.resize(tmp_t.size());
738 for (auto &m : avg_tensor)
739 m.setZero(space_.n_bases, actual_dim * actual_dim);
740 }
741
742 for (size_t j = 0; j < bs.bases.size(); ++j)
743 {
744 const basis::Basis &b = bs.bases[j];
745 if (b.global().size() > 1)
746 continue;
747
748 const int index = b.global().front().index;
749 areas(index) += area;
750 for (int k = 0; k < tmp_s.size(); ++k)
751 avg_scalar[k](index) += tmp_s[k].second(j) * area;
752 for (int k = 0; k < tmp_t.size(); ++k)
753 avg_tensor[k].row(index) += tmp_t[k].second.row(j) * area;
754 }
755 }
756
757 for (auto &m : avg_scalar)
758 for (int i = 0; i < m.rows(); ++i)
759 if (areas(i) > 0)
760 m(i) /= areas(i);
761 for (auto &m : avg_tensor)
762 for (int i = 0; i < m.rows(); ++i)
763 if (areas(i) > 0)
764 m.row(i) /= areas(i);
765
766 for (int k = 0; k < tmp_s.size(); ++k)
767 {
768 const std::string name = fmt::format("{:s}_avg", tmp_s[k].first);
769 if (!options.export_field(name))
770 continue;
771
772 Eigen::MatrixXd sampled;
773 if (sample_dof_field(avg_scalar[k], 1, sampled))
774 fields.push_back({name, sampled, io::OutputField::Association::Point});
775 }
776
777 for (int k = 0; k < tmp_t.size(); ++k)
778 {
779 const std::string base_name = fmt::format("{:s}_avg", tmp_t[k].first);
780 if (!options.export_field(base_name))
781 continue;
782
783 Eigen::MatrixXd sampled;
784 if (!sample_dof_field(utils::flatten(avg_tensor[k]), actual_dim * actual_dim, sampled))
785 continue;
786
787 const int stride = mesh_->dimension();
788 for (int i = 0; i < sampled.cols(); i += stride)
789 {
790 const int ii = (i / stride) + 1;
791 fields.push_back({fmt::format("{:s}_{:d}", base_name, ii), sampled.middleCols(i, stride), io::OutputField::Association::Point});
792 }
793 }
794 };
795
796 const auto append_material_fields = [&]() {
797 if (!material_params || !has_element_samples)
798 return;
799
800 const auto &params = primary_assembler_->parameters();
801 std::map<std::string, Eigen::MatrixXd> param_values;
802 for (const auto &[p, _] : params)
803 param_values[p].setZero(output_rows, 1);
804 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
805
806 const auto &density = mass_assembler_->density();
807 for (int i = 0; i < sample.local_points.rows(); ++i)
808 {
809 const int element_id = sample.element_ids(i);
810 if (element_id < 0)
811 continue;
812
813 for (const auto &[p, func] : params)
814 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
815 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
816 }
817
818 for (const auto &[name, values] : param_values)
819 if (options.export_field(name))
820 fields.push_back({name, values, io::OutputField::Association::Point});
821 if (options.export_field("rho"))
822 fields.push_back({"rho", rhos, io::OutputField::Association::Point});
823 };
824
825 const auto append_body_ids = [&]() {
826 if (!body_ids || !options.export_field("body_ids") || !has_element_samples)
827 return;
828
829 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
830 for (int i = 0; i < sample.element_ids.size(); ++i)
831 {
832 const int element_id = sample.element_ids(i);
833 if (element_id >= 0)
834 ids(i) = mesh_->get_body_id(element_id);
835 }
836 fields.push_back({"body_ids", ids, io::OutputField::Association::Point});
837 };
838
839 const auto compute_traction_forces = [&]() {
840 Eigen::MatrixXd traction_forces;
841 traction_forces.setZero(space_.n_bases * actual_dim, 1);
842
843 Eigen::MatrixXd uv, points, normals;
844 Eigen::VectorXd weights;
845 Eigen::VectorXi global_primitive_ids;
846 assembler::ElementAssemblyValues vals;
847
848 for (const auto &lb : boundary_.total_local_boundary)
849 {
850 const int e = lb.element_id();
851 const bool has_samples = utils::BoundarySampler::boundary_quadrature(
852 lb, elastic_boundary_samples(), *mesh_, false, uv, points, normals, weights, global_primitive_ids);
853 if (!has_samples)
854 continue;
855
856 const basis::ElementBases &bs = space_.basis_list()[e];
857 const basis::ElementBases &gbs = space_.geometry_basis_list()[e];
858 vals.compute(e, mesh_->is_volume(), points, bs, gbs);
859
860 for (int n = 0; n < normals.rows(); ++n)
861 {
862 Eigen::MatrixXd deform_mat = Eigen::MatrixXd::Zero(actual_dim, actual_dim);
863 for (const auto &b : vals.basis_values)
864 {
865 for (const auto &g : b.global)
866 {
867 for (int d = 0; d < actual_dim; ++d)
868 deform_mat.row(d) += solution(g.index * actual_dim + d) * b.grad.row(n);
869 }
870 }
871
872 Eigen::MatrixXd trafo = vals.jac_it[n].inverse() + deform_mat;
873 normals.row(n) = normals.row(n) * trafo.inverse();
874 normals.row(n).normalize();
875 }
876
877 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
878 primary_assembler_->compute_tensor_value(assembler::OutputData(sample.time, e, bs, gbs, points, solution), tensor_flat);
879
880 for (long n = 0; n < vals.basis_values.size(); ++n)
881 {
882 const assembler::AssemblyValues &v = vals.basis_values[n];
883 const int g_index = v.global[0].index * actual_dim;
884 for (int q = 0; q < points.rows(); ++q)
885 {
886 assert(tensor_flat[0].first == "cauchy_stess");
887 Eigen::MatrixXd stress_tensor = utils::unflatten(tensor_flat[0].second.row(q), actual_dim);
888 traction_forces.block(g_index, 0, actual_dim, 1) += stress_tensor * normals.row(q).transpose() * v.val(q) * weights(q);
889 }
890 }
891 }
892
893 return traction_forces;
894 };
895
896 const auto append_traction_force = [&]() {
897 if (problem->is_scalar() || !explicit_fields || !options.export_field("traction_force"))
898 return;
899
900 if (has_element_samples && sample.normals.rows() == sample.local_points.rows() && sample.primitive_ids.size() == sample.local_points.rows())
901 {
902 const Eigen::MatrixXd displaced_normals = displaced_output_normals(sample, solution);
903 const Eigen::MatrixXd &normals = displaced_normals.rows() == sample.normals.rows() ? displaced_normals : sample.normals;
904 Eigen::MatrixXd values = Eigen::MatrixXd::Zero(output_rows, actual_dim);
905 for (int i = 0; i < sample.local_points.rows(); ++i)
906 {
907 const int element_id = sample.element_ids(i);
908 if (element_id < 0)
909 continue;
910
911 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
912 primary_assembler_->compute_tensor_value(
913 assembler::OutputData(sample.time, element_id, space_.basis_list()[element_id], space_.geometry_basis_list()[element_id], sample.local_points.row(i), solution),
914 tensor_flat);
915
916 assert(tensor_flat[0].first == "cauchy_stess");
917 Eigen::Map<Eigen::MatrixXd> tensor(tensor_flat[0].second.data(), actual_dim, actual_dim);
918 values.row(i) = normals.row(i) * tensor;
919
920 double area = 0;
921 const int primitive_id = sample.primitive_ids(i);
922 if (mesh_->is_volume())
923 {
924 if (mesh_->is_simplex(element_id))
925 area = mesh_->tri_area(primitive_id);
926 else if (mesh_->is_cube(element_id))
927 area = mesh_->quad_area(primitive_id);
928 else if (mesh_->is_prism(element_id))
929 area = mesh_->n_face_vertices(primitive_id) == 4 ? mesh_->quad_area(primitive_id) : mesh_->tri_area(primitive_id);
930 }
931 else
932 {
933 area = mesh_->edge_length(primitive_id);
934 }
935 values.row(i) *= area;
936 }
937 fields.push_back({"traction_force", values, io::OutputField::Association::Point});
938 return;
939 }
940
941 append_sampled_dof_field("traction_force", compute_traction_forces(), actual_dim);
942 };
943
944 append_scalar_values();
945 append_tensor_values();
946 append_averaged_values();
947 append_material_fields();
948 append_body_ids();
949
950 if ((paraview_options["jacobian_validity"] || (explicit_fields && options.export_field("validity")))
951 && has_element_samples
952 && sample.primitive_ids.size() == 0)
953 {
954 const auto invalid_elements = utils::count_invalid(
955 mesh_->dimension(), space_.basis_list(), space_.geometry_basis_list(), solution);
956 Eigen::MatrixXd validity = Eigen::MatrixXd::Zero(output_rows, 1);
957 for (int i = 0; i < sample.element_ids.size(); ++i)
958 {
959 validity(i) = std::find(
960 invalid_elements.begin(), invalid_elements.end(), sample.element_ids(i))
961 != invalid_elements.end();
962 }
963 fields.push_back({"validity", validity, io::OutputField::Association::Point});
964 }
965
966 if (problem->is_time_dependent())
967 {
968 if (velocity && options.export_field("velocity"))
969 append_sampled_dof_field(
970 "velocity",
971 time_integrator ? time_integrator->v_prev() : Eigen::VectorXd::Zero(solution.size()),
972 actual_dim);
973 if (acceleration && options.export_field("acceleration"))
974 append_sampled_dof_field(
975 "acceleration",
976 time_integrator ? time_integrator->a_prev() : Eigen::VectorXd::Zero(solution.size()),
977 actual_dim);
978 }
979
980 if (forces)
981 {
982 const double s = time_integrator ? time_integrator->acceleration_scaling() : 1;
983 for (const auto &[name, form] : named_forms)
984 {
985 const std::string field_name = name + "_forces";
986 if (!options.export_field(field_name))
987 continue;
988
989 Eigen::VectorXd force;
990 if (form && form->enabled())
991 {
992 form->first_derivative(solution, force);
993 force *= -1.0 / s;
994 }
995 else
996 {
997 force.setZero(solution.size());
998 }
999 append_sampled_dof_field(field_name, force, actual_dim);
1000 }
1001 }
1002
1003 append_traction_force();
1004
1005 if (explicit_fields && options.export_field("gradient_of_elastic_potential") && elastic_form)
1006 {
1007 Eigen::VectorXd potential_grad;
1008 elastic_form->first_derivative(solution, potential_grad);
1009 append_sampled_dof_field("gradient_of_elastic_potential", potential_grad, actual_dim);
1010 }
1011
1012 if (explicit_fields && options.export_field("gradient_of_contact_potential") && contact_form && contact_form->weight() > 0)
1013 {
1014 Eigen::VectorXd potential_grad;
1015 contact_form->first_derivative(solution, potential_grad);
1016 potential_grad *= -contact_form->barrier_stiffness() / contact_form->weight();
1017 append_sampled_dof_field("gradient_of_contact_potential", potential_grad, actual_dim);
1018 }
1019
1020 append_primary_output_fields(fields, sample, solution, options, obstacle);
1021 return fields;
1022 }
1023
1024 void ElasticVarForm::append_primary_output_fields(
1025 std::vector<io::OutputField> &fields,
1026 const io::OutputSample &sample,
1027 const Eigen::MatrixXd &solution,
1028 const io::OutputFieldOptions &options,
1029 const mesh::Obstacle *obstacle) const
1030 {
1031 if (!mesh_ || solution.size() <= 0)
1032 return;
1033
1034 const int dim = mesh_->dimension();
1035 const bool has_element_samples =
1036 sample.local_points.rows() > 0
1037 && sample.local_points.rows() == sample.element_ids.size();
1038 const bool export_solution_gradient =
1039 !options.fields.empty() && options.export_field("solution_gradient");
1040
1041 Eigen::MatrixXd values, gradients;
1042 if (has_element_samples)
1043 {
1044 values.resize(sample.local_points.rows(), dim);
1045 if (export_solution_gradient)
1046 gradients.resize(sample.local_points.rows(), dim * mesh_->dimension());
1047 for (int i = 0; i < sample.local_points.rows(); ++i)
1048 {
1049 const int element_id = sample.element_ids(i);
1050 if (element_id < 0)
1051 {
1052 values.row(i).setZero();
1053 if (gradients.rows() > 0)
1054 gradients.row(i).setZero();
1055 continue;
1056 }
1057
1058 Eigen::MatrixXd local_value, local_gradient;
1060 *mesh_, dim, space_.basis_list(), space_.geometry_basis_list(),
1061 element_id, sample.local_points.row(i), solution,
1062 local_value, local_gradient);
1063 values.row(i) = local_value;
1064 if (gradients.rows() > 0)
1065 gradients.row(i) = local_gradient;
1066 }
1067
1068 if (obstacle && obstacle->n_vertices() > 0
1069 && sample.points.rows() == values.rows() + obstacle->n_vertices()
1070 && sample.points.cols() == obstacle->v().cols()
1071 && sample.points.bottomRows(obstacle->n_vertices()).isApprox(obstacle->v()))
1072 {
1073 values.conservativeResize(values.rows() + obstacle->n_vertices(), Eigen::NoChange);
1074 if (solution.rows() >= obstacle->ndof())
1075 values.bottomRows(obstacle->n_vertices()) =
1076 utils::unflatten(solution.bottomRows(obstacle->ndof()), dim);
1077 else
1078 values.bottomRows(obstacle->n_vertices()).setZero();
1079 if (gradients.rows() > 0)
1080 {
1081 gradients.conservativeResize(values.rows(), Eigen::NoChange);
1082 gradients.bottomRows(obstacle->n_vertices()).setZero();
1083 }
1084 }
1085 }
1086 else if (sample.node_ids.size() > 0)
1087 {
1088 values.resize(sample.node_ids.size(), dim);
1089 for (int i = 0; i < sample.node_ids.size(); ++i)
1090 {
1091 for (int d = 0; d < dim; ++d)
1092 {
1093 const int dof = sample.node_ids(i) * dim + d;
1094 if (dof < 0 || dof >= solution.rows())
1095 return;
1096 values(i, d) = solution(dof);
1097 }
1098 }
1099 }
1100 else
1101 {
1102 return;
1103 }
1104
1105 if (sample.points.rows() > 0 && values.rows() != sample.points.rows())
1106 return;
1107 if (options.export_field("displacement"))
1108 fields.push_back({"displacement", values, io::OutputField::Association::Point});
1109 if (options.export_field("solution"))
1110 fields.push_back({"solution", values, io::OutputField::Association::Point});
1111 if (export_solution_gradient && gradients.rows() == values.rows())
1112 fields.push_back({"solution_gradient", gradients, io::OutputField::Association::Point});
1113
1114 if (options.export_field("displaced_normals"))
1115 {
1116 Eigen::MatrixXd normals = displaced_output_normals(sample, solution);
1117 if (normals.rows() == values.rows())
1118 fields.push_back({"displaced_normals", normals, io::OutputField::Association::Point});
1119 }
1120 }
1121
1122 Eigen::MatrixXd ElasticVarForm::displaced_output_normals(
1123 const io::OutputSample &sample,
1124 const Eigen::MatrixXd &solution) const
1125 {
1126 if (!mesh_
1127 || sample.normals.rows() == 0
1128 || sample.normals.rows() != sample.local_points.rows()
1129 || sample.local_points.rows() != sample.element_ids.size())
1130 return {};
1131
1132 const int dim = mesh_->dimension();
1133 Eigen::MatrixXd displaced_normals = sample.normals;
1134 for (int i = 0; i < sample.local_points.rows(); ++i)
1135 {
1136 const int element_id = sample.element_ids(i);
1137 if (element_id < 0)
1138 continue;
1139
1140 Eigen::MatrixXd local_value, local_gradient;
1142 *mesh_, dim, space_.basis_list(), space_.geometry_basis_list(),
1143 element_id, sample.local_points.row(i), solution,
1144 local_value, local_gradient);
1145
1146 Eigen::MatrixXd deformation = Eigen::MatrixXd::Identity(dim, dim);
1147 for (int d = 0; d < dim; ++d)
1148 deformation.row(d) += local_gradient.block(0, d * dim, 1, dim);
1149 displaced_normals.row(i) = sample.normals.row(i) * deformation.inverse();
1150 displaced_normals.row(i).normalize();
1151 }
1152 return displaced_normals;
1153 }
1154
1155 io::OutputSpace ElasticVarForm::output_space() const
1156 {
1157 Eigen::VectorXi output_orders = space_.disc_orders;
1158 if (mesh_ && space_.disc_ordersq.size() == space_.disc_orders.size())
1159 {
1160 for (int e = 0; e < output_orders.size(); ++e)
1161 {
1162 if (mesh_->is_prism(e))
1163 output_orders(e) = std::max(space_.disc_orders(e), space_.disc_ordersq(e));
1164 }
1165 }
1166
1167 return {
1168 mesh_.get(),
1169 &space_.geometry_basis_list(),
1170 output_orders,
1171 &space_.polys,
1172 &space_.polys_3d,
1173 &boundary_.total_local_boundary,
1174 nullptr,
1175 nullptr,
1176 &boundary_.dirichlet_nodes,
1177 &boundary_.dirichlet_nodes_position};
1178 }
1179
1180 io::OutStatsData ElasticVarForm::compute_errors(const Eigen::MatrixXd &solution)
1181 {
1182 if (!args["output"]["advanced"]["compute_error"])
1183 return stats;
1184
1185 double tend = 0;
1186 if (!args["time"].is_null())
1187 tend = args["time"]["tend"];
1188
1189 stats.compute_errors(space_.n_bases, space_.basis_list(), space_.geometry_basis_list(), *mesh_, *problem, tend, solution);
1190 return stats;
1191 }
1192
1193 void ElasticVarForm::export_data(const Eigen::MatrixXd &solution) const
1194 {
1195 const io::OutputSpace space = output_space();
1196 if (!space.mesh)
1197 {
1198 logger().error("Load the mesh first!");
1199 return;
1200 }
1201 if (solution.size() <= 0)
1202 {
1203 logger().error("Solve the problem first!");
1204 return;
1205 }
1206
1207 ensure_output_sampler();
1208
1209 const std::string vis_mesh_path = resolve_output_path(args["output"]["paraview"]["file_name"]);
1210 const bool has_time = args.contains("time") && !args["time"].is_null();
1211 double tend = has_time ? args["time"]["tend"].get<double>() : 1.0;
1212 double dt = 1;
1213 if (has_time)
1214 dt = args["time"]["dt"];
1215
1216 const auto opts = export_options(space);
1217 output_geometry_.export_data(
1218 space,
1219 output_field_function(solution, opts),
1220 has_time,
1221 tend, dt,
1222 opts,
1223 vis_mesh_path);
1224
1225 const std::string solution_path = resolve_output_path(args["output"]["data"]["solution"]);
1226 if (!solution_path.empty())
1227 {
1228 const int dim = mesh_->dimension();
1229 const int primary_ndof = std::min<int>(solution.rows(), space_.n_bases * dim);
1230 const Eigen::MatrixXd primary_solution = solution.topRows(primary_ndof);
1231 if (opts.reorder_output && space_.space_in_node_to_node.size() > 0)
1232 {
1233 const Eigen::MatrixXd nodal_solution = utils::unflatten(primary_solution, dim);
1234 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
1235 for (int input_node = 0; input_node < space_.space_in_node_to_node.size(); ++input_node)
1236 {
1237 const int node = space_.space_in_node_to_node(input_node);
1238 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
1239 reordered.row(input_node) = nodal_solution.row(node);
1240 }
1241 io::write_matrix(solution_path, reordered);
1242 }
1243 else
1244 {
1245 io::write_matrix(solution_path, primary_solution);
1246 }
1247 }
1248
1249 const std::string nodes_path = resolve_output_path(args["output"]["data"]["nodes"]);
1250 if (!nodes_path.empty())
1251 {
1252 Eigen::MatrixXd nodes = Eigen::MatrixXd::Zero(space_.n_bases, mesh_->dimension());
1253 for (const basis::ElementBases &element_bases : space_.basis_list())
1254 for (const basis::Basis &basis : element_bases.bases)
1255 for (const auto &global : basis.global())
1256 nodes.row(global.index) = global.node;
1257 io::write_matrix(nodes_path, nodes);
1258 }
1259
1260 const std::string stress_path = resolve_output_path(args["output"]["data"]["stress_mat"]);
1261 const std::string mises_path = resolve_output_path(args["output"]["data"]["mises"]);
1262 if ((!stress_path.empty() || !mises_path.empty()) && primary_assembler_)
1263 {
1264 Eigen::MatrixXd stress;
1265 Eigen::VectorXd mises;
1267 *mesh_, problem->is_scalar(), space_.basis_list(), space_.geometry_basis_list(),
1268 space_.disc_orders, space_.disc_ordersq, *primary_assembler_, solution, tend,
1269 stress, mises);
1270 if (!stress_path.empty())
1271 io::write_matrix(stress_path, stress);
1272 if (!mises_path.empty())
1273 io::write_matrix(mises_path, mises);
1274 }
1275 }
1276
1277 void ElasticVarForm::save_json(const Eigen::MatrixXd &solution, std::ostream &out) const
1278 {
1279 if (!mesh_)
1280 {
1281 logger().error("Load the mesh first!");
1282 return;
1283 }
1284 if (solution.size() <= 0)
1285 {
1286 logger().error("Solve the problem first!");
1287 return;
1288 }
1289
1290 logger().info("Saving json...");
1291 const int primary_size = space_.n_bases * mesh_->dimension();
1292 const Eigen::MatrixXd stats_solution =
1293 solution.rows() >= primary_size
1294 ? solution.topRows(primary_size).eval()
1295 : solution;
1296
1297 nlohmann::json j;
1298 stats.save_json(
1299 args, space_.n_bases, 0,
1300 stats_solution, *mesh_, space_.disc_orders, space_.disc_ordersq, *problem,
1301 timings, primary_assembler_ ? primary_assembler_->name() : name(), space_.is_iso_parametric(),
1302 args["output"]["advanced"]["sol_at_node"], j);
1303 out << j.dump(4) << std::endl;
1304 }
1305
1306 void ElasticVarForm::save_elastic_step_state(
1307 const double t0,
1308 const double dt,
1309 const int t,
1310 const Eigen::MatrixXd &solution,
1311 const time_integrator::ImplicitTimeIntegrator *time_integrator) const
1312 {
1313 if (!mesh_)
1314 return;
1315 save_step_state(t0, dt, t, solution, time_integrator);
1316 }
1317
1318 void ElasticVarForm::build_mesh_matrices(Eigen::MatrixXd &V, Eigen::MatrixXi &F) const
1319 {
1320 assert(mesh_);
1321 assert(space_.basis_list().size() == mesh_->n_elements());
1322 const size_t n_vertices = space_.n_bases - n_obstacle_vertices();
1323 const int dim = mesh_->dimension();
1324
1325 V.resize(n_vertices, dim);
1326 F.resize(space_.basis_list().size(), dim + 1);
1327
1328 for (int i = 0; i < space_.basis_list().size(); i++)
1329 {
1330 const basis::ElementBases &element = space_.basis_list()[i];
1331 for (int j = 0; j < element.bases.size(); j++)
1332 {
1333 const basis::Basis &basis = element.bases[j];
1334 assert(basis.global().size() == 1);
1335 V.row(basis.global()[0].index) = basis.global()[0].node;
1336 if (j < F.cols())
1337 F(i, j) = basis.global()[0].index;
1338 }
1339 }
1340 }
1341
1342} // namespace polyfem::varform
int V
ElementAssemblyValues vals
Definition Assembler.cpp:26
std::vector< std::pair< int, double > > weights
std::array< Matrix< int, 3, 3 >, 3 > space_
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
void init(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const bool is_mass=false)
computes the basis evaluation and geometric mapping for each of the given ElementBases in bases initi...
void init_empty(const bool is_mass=false)
initialize an empty cache.
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
std::vector< Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > > jac_it
Represents one basis function and its gradient.
Definition Basis.hpp:44
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
std::vector< Basis > bases
one basis function per node in the element
static void interpolate_at_local_vals(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const int el_index, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, Eigen::MatrixXd &result_grad)
interpolate solution and gradient at element (calls interpolate_at_local_vals with sol)
static void compute_stress_at_quadrature_points(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Assembler &assembler, const Eigen::MatrixXd &fun, const double t, Eigen::MatrixXd &result, Eigen::VectorXd &von_mises)
compute von mises stress at quadrature points for the function fun, also compute the interpolated fun...
double assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
all stats from polyfem
int n_flipped
number of flipped elements, compute only when using count_flipped_els (false by default)
void count_flipped_elements(const polyfem::mesh::Mesh &mesh, const std::vector< polyfem::basis::ElementBases > &gbases)
counts the number of flipped elements
Definition OutData.cpp:2834
void compute_mesh_size(const polyfem::mesh::Mesh &mesh_in, const std::vector< polyfem::basis::ElementBases > &bases_in, const int n_samples, const bool use_curved_mesh_size)
computes the mesh size, it samples every edges n_samples times uses curved_mesh_size (false by defaul...
Definition OutData.cpp:2751
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
double mesh_size
max edge lenght
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:148
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:492
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
virtual bool is_volume() const =0
checks if mesh is volume
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
Definition Mesh.cpp:346
int dimension() const
utily for dimension
Definition Mesh.hpp:138
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:475
const Eigen::MatrixXd & v() const
Definition Obstacle.hpp:45
static const ProblemFactory & factory()
std::shared_ptr< assembler::Problem > get_problem(const std::string &problem) const
static void p_refine(const mesh::Mesh &mesh, const double B, const bool h1_formula, const int base_p, const int discr_order_max, io::OutStatsData &stats, Eigen::VectorXi &disc_orders)
compute a priori prefinement
Definition APriori.cpp:242
Form representing the contact potential and forces.
Implicit time integrator of a second order ODE (equivently a system of coupled first order ODEs).
static bool boundary_quadrature(const mesh::LocalBoundary &local_boundary, const QuadratureOrders &order, const mesh::Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights, Eigen::VectorXi &global_primitive_ids)
void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args) override
assembler::AssemblyValsCache pure_mass_ass_vals_cache_
void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const override
void build_elastic_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args, const int fe_space_id)
std::vector< int > elastic_primitive_to_node() const
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< assembler::Assembler > primary_assembler_
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
std::vector< int > elastic_node_to_primitive() const
QuadratureOrders elastic_boundary_samples() const
assembler::AssemblyValsCache ass_vals_cache_
void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution) override
std::vector< io::OutputField > elastic_output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options, const mesh::Obstacle *obstacle, const time_integrator::ImplicitTimeIntegrator *time_integrator, const std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > &named_forms, const solver::Form *elastic_form, const solver::ContactForm *contact_form=nullptr) const
std::shared_ptr< assembler::HRZMass > pure_mass_assembler_
std::shared_ptr< assembler::Mass > mass_assembler_
std::shared_ptr< assembler::RhsAssembler > rhs_assembler_
assembler::AssemblyValsCache mass_ass_vals_cache_
void initial_velocity(Eigen::MatrixXd &velocity, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
void load_mesh(const mesh::Mesh &mesh, const json &args) override
void initial_acceleration(Eigen::MatrixXd &acceleration, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
void initial_solution(Eigen::MatrixXd &solution, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
void assemble_rhs(const mesh::Mesh &mesh) override
const std::vector< basis::ElementBases > & geometry_basis_list() const
Definition FESpace.hpp:115
std::shared_ptr< GeometryMapping > geometry
Geometric mapping used to integrate this FE space.
Definition FESpace.hpp:89
Eigen::VectorXi disc_orders
Primary polynomial degree for each mesh element.
Definition FESpace.hpp:71
Eigen::VectorXi disc_ordersq
Secondary polynomial degree for anisotropic bases, e.g. prisms.
Definition FESpace.hpp:74
int n_bases
Number of globally indexed scalar basis functions in the space.
Definition FESpace.hpp:65
Eigen::VectorXi space_in_node_to_node
Definition FESpace.hpp:91
const std::vector< basis::ElementBases > & basis_list() const
Definition FESpace.hpp:109
bool is_iso_parametric() const
Definition FESpace.hpp:104
std::shared_ptr< mesh::MeshNodes > mesh_nodes
Optional primitive-to-node mapping for this FE space.
Definition FESpace.hpp:86
void reorder_checkpoint_block(const io::CheckpointReader &reader, const std::string &name, const FESpace &space, int block_size, int row_offset, Eigen::MatrixXd &value) const
Definition VarForm.cpp:981
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1125
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:263
virtual std::string name() const =0
Get the name of the variational formulation.
void write_checkpoint_ordering(io::CheckpointWriter &writer, const std::string &name, const FESpace &space) const
Definition VarForm.cpp:938
virtual void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution)
Definition VarForm.cpp:905
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:275
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:684
io::OutStatsData stats
Definition VarForm.hpp:267
virtual void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const
Definition VarForm.cpp:897
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:185
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:274
virtual void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
Initialize the variational formulation with the given parameters.
Definition VarForm.cpp:208
void validate_checkpoint_solution(const Eigen::MatrixXd &solution, int expected_rows) const
Definition VarForm.cpp:1037
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:270
const io::ResourceIO & resources_
Definition VarForm.hpp:285
void set_materials(assembler::Assembler &assembler, const int size) const
Definition VarForm.cpp:806
virtual void reset()=0
Definition VarForm.cpp:194
str func
Definition p_bases.py:417
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void prism_nodes_3d(const int p, const int q, Eigen::MatrixXd &val)
void p_nodes_2d(const int p, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
Definition MatrixIO.cpp:43
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
std::vector< int > count_invalid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const unsigned max_iter)
Definition Jacobian.cpp:122
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
std::array< int, 2 > QuadratureOrders
Definition Types.hpp:19
nlohmann::json json
Definition Common.hpp:9
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
bool export_field(const std::string &field) const
Definition OutData.cpp:56
std::vector< std::string > fields
Eigen::VectorXi node_ids
Eigen::MatrixXd normals
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points
const mesh::Mesh * mesh
std::vector< RowVectorNd > neumann_nodes_position
Definition FESpace.hpp:163
std::vector< mesh::LocalBoundary > local_boundary
Definition FESpace.hpp:155
std::vector< mesh::LocalBoundary > local_neumann_boundary
Definition FESpace.hpp:156
std::vector< mesh::LocalBoundary > total_local_boundary
Definition FESpace.hpp:154
std::vector< RowVectorNd > dirichlet_nodes_position
Definition FESpace.hpp:161