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