PolyFEM
Loading...
Searching...
No Matches
VarForm.cpp
Go to the documentation of this file.
2
4
8
26
27#include <igl/Timer.h>
31
32#include <fstream>
33#include <limits>
34
35#include <spdlog/fmt/fmt.h>
36
37namespace polyfem::varform
38{
40 const std::string &state_path,
41 const std::string &x_name,
42 const bool reorder,
43 const Eigen::VectorXi &in_node_to_node,
44 const int dim,
45 Eigen::MatrixXd &x)
46 {
47 if (state_path.empty())
48 return false;
49
50 if (!io::read_matrix(state_path, x_name, x))
51 {
52 logger().debug("Unable to read initial {} from file ({})", x_name, state_path);
53 return false;
54 }
55
56 if (reorder)
57 {
58 const int ndof = in_node_to_node.size() * dim;
59 x.topRows(ndof) = utils::reorder_matrix(x.topRows(ndof), in_node_to_node, -1, dim);
60 }
61
62 return true;
63 }
64
65 namespace
66 {
67
68 bool should_use_iso_parametric(const mesh::Mesh &mesh, const json &args)
69 {
70 if (mesh.has_poly())
71 return true;
72
73 if (args["space"]["basis_type"] == "Bernstein")
74 return false;
75
76 if (args["space"]["basis_type"] == "Spline")
77 return true;
78
79 if (mesh.is_rational())
80 return false;
81
82 if (args["space"]["use_p_ref"])
83 return false;
84
85 if (args["boundary_conditions"]["periodic_boundary"]["enabled"].get<bool>())
86 return false;
87
88 if (mesh.orders().size() <= 0)
89 {
90 if (args["space"]["discr_order"] == 1)
91 return true;
92 return args["space"]["advanced"]["isoparametric"];
93 }
94
95 if (mesh.orders().minCoeff() != mesh.orders().maxCoeff())
96 return false;
97
98 if (args["space"]["discr_order"] == mesh.orders().minCoeff())
99 return true;
100
101 return args["space"]["advanced"]["isoparametric"];
102 }
103
105 void build_in_node_to_in_primitive(const mesh::Mesh &mesh, const mesh::MeshNodes &mesh_nodes,
106 Eigen::VectorXi &in_node_to_in_primitive,
107 Eigen::VectorXi &in_node_offset)
108 {
109 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
110 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
111 const int num_face_nodes = mesh_nodes.num_face_nodes();
112 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
113
114 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
115
116 const long n_vertices = num_vertex_nodes;
117 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
118 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
119
120 in_node_to_in_primitive.resize(num_nodes);
121 in_node_offset.resize(num_nodes);
122
123 // Only one node per vertex, so this is an identity map.
124 in_node_to_in_primitive.head(num_vertex_nodes).setLinSpaced(num_vertex_nodes, 0, num_vertex_nodes - 1); // vertex nodes
125 in_node_offset.head(num_vertex_nodes).setZero();
126
127 int prim_offset = n_vertices;
128 int node_offset = num_vertex_nodes;
129 auto foo = [&](const int num_prims, const int num_prim_nodes) {
130 if (num_prims <= 0 || num_prim_nodes <= 0)
131 return;
132 const Eigen::VectorXi range = Eigen::VectorXi::LinSpaced(num_prim_nodes, 0, num_prim_nodes - 1);
133 // TODO: This assumes isotropic degree of element.
134 const int node_per_prim = num_prim_nodes / num_prims;
135
136 in_node_to_in_primitive.segment(node_offset, num_prim_nodes) =
137 range.array() / node_per_prim + prim_offset;
138
139 in_node_offset.segment(node_offset, num_prim_nodes) =
140 range.unaryExpr([&](const int x) { return x % node_per_prim; });
141
142 prim_offset += num_prims;
143 node_offset += num_prim_nodes;
144 };
145
146 foo(mesh.n_edges(), num_edge_nodes);
147 foo(mesh.n_faces(), num_face_nodes);
148 foo(mesh.n_cells(), num_cell_nodes);
149 }
150
151 bool build_in_primitive_to_primitive(
152 const mesh::Mesh &mesh, const mesh::MeshNodes &mesh_nodes,
153 const Eigen::VectorXi &in_ordered_vertices,
154 const Eigen::MatrixXi &in_ordered_edges,
155 const Eigen::MatrixXi &in_ordered_faces,
156 Eigen::VectorXi &in_primitive_to_primitive)
157 {
158 // NOTE: Assume in_cells_to_cells is identity
159 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
160 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
161 const int num_face_nodes = mesh_nodes.num_face_nodes();
162 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
163 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
164
165 const long n_vertices = num_vertex_nodes;
166 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
167 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
168
169 in_primitive_to_primitive.setLinSpaced(num_in_primitives, 0, num_in_primitives - 1);
170
171 igl::Timer timer;
172
173 // ------------
174 // Map vertices
175 // ------------
176
177 if (in_ordered_vertices.rows() != n_vertices)
178 {
179 logger().warn("Node ordering disabled, in_ordered_vertices != n_vertices, {} != {}", in_ordered_vertices.rows(), n_vertices);
180 return false;
181 }
182
183 in_primitive_to_primitive.head(n_vertices) = in_ordered_vertices;
184
185 int in_offset = n_vertices;
186 int offset = mesh.n_vertices();
187
188 // ---------
189 // Map edges
190 // ---------
191
192 logger().trace("Building Mesh edges to IDs...");
193 timer.start();
194 const auto edges_to_ids = mesh.edges_to_ids();
195 if (in_ordered_edges.rows() != edges_to_ids.size())
196 {
197 logger().warn("Node ordering disabled, in_ordered_edges != edges_to_ids, {} != {}", in_ordered_edges.rows(), edges_to_ids.size());
198 return false;
199 }
200 timer.stop();
201 logger().trace("Done (took {}s)", timer.getElapsedTime());
202
203 logger().trace("Building in-edge to edge mapping...");
204 timer.start();
205 for (int in_ei = 0; in_ei < in_ordered_edges.rows(); in_ei++)
206 {
207 const std::pair<int, int> in_edge(
208 in_ordered_edges.row(in_ei).minCoeff(),
209 in_ordered_edges.row(in_ei).maxCoeff());
210 in_primitive_to_primitive[in_offset + in_ei] =
211 offset + edges_to_ids.at(in_edge); // offset edge ids
212 }
213 timer.stop();
214 logger().trace("Done (took {}s)", timer.getElapsedTime());
215
216 in_offset += mesh.n_edges();
217 offset += mesh.n_edges();
218
219 // ---------
220 // Map faces
221 // ---------
222
223 if (mesh.is_volume())
224 {
225 logger().trace("Building Mesh faces to IDs...");
226 timer.start();
227 const auto faces_to_ids = mesh.faces_to_ids();
228 if (in_ordered_faces.rows() != faces_to_ids.size())
229 {
230 logger().warn("Node ordering disabled, in_ordered_faces != faces_to_ids, {} != {}", in_ordered_faces.rows(), faces_to_ids.size());
231 return false;
232 }
233 timer.stop();
234 logger().trace("Done (took {}s)", timer.getElapsedTime());
235
236 logger().trace("Building in-face to face mapping...");
237 timer.start();
238 for (int in_fi = 0; in_fi < in_ordered_faces.rows(); in_fi++)
239 {
240 std::vector<int> in_face(in_ordered_faces.cols());
241 for (int i = 0; i < in_face.size(); i++)
242 in_face[i] = in_ordered_faces(in_fi, i);
243 std::sort(in_face.begin(), in_face.end());
244
245 in_primitive_to_primitive[in_offset + in_fi] =
246 offset + faces_to_ids.at(in_face); // offset face ids
247 }
248 timer.stop();
249 logger().trace("Done (took {}s)", timer.getElapsedTime());
250
251 in_offset += mesh.n_faces();
252 offset += mesh.n_faces();
253 }
254
255 return true;
256 }
257 } // namespace
258
259 QuadratureOrders VarForm::n_boundary_samples(const int discr_order, const int gdiscr_order) const
260 {
262 const int n_b_samples_j = args["space"]["advanced"]["n_boundary_samples"];
263 const int boundary_order = std::max(discr_order, gdiscr_order);
264 const int n_b_samples = std::max(n_b_samples_j, AssemblerUtils::quadrature_order("Mass", boundary_order, AssemblerUtils::BasisType::POLY, mesh_->dimension()));
265 return {{n_b_samples, n_b_samples}};
266 }
267
269 {
270 // FIXME check subclasses
271 stats.reset();
274 prepared_ = false;
275 problem = nullptr;
276 time_callback = nullptr;
277 mesh_ = nullptr;
278 }
279
280 void VarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
281 {
282 reset();
283
284 this->units = units;
285 this->args = args;
286
287 if (utils::is_param_valid(args, "root_path"))
288 root_path = args["root_path"].get<std::string>();
289 else
290 root_path = "";
291
292 this->output_path = out_path;
294 }
295
296 void VarForm::set_mesh(std::unique_ptr<mesh::Mesh> mesh, const double loading_mesh_time)
297 {
298 mesh_ = std::move(mesh);
299 timings.loading_mesh_time = loading_mesh_time;
301 prepared_ = false;
302 if (!mesh_)
303 return;
304
306 }
307
309 {
310 if (prepared_)
311 return;
312 if (!mesh_)
313 log_and_throw_error("Load the mesh first!");
314
315 mesh_->prepare_mesh();
317 build_basis(*mesh_, should_use_iso_parametric(*mesh_, args), args);
320 prepared_ = true;
321 }
322
324 mesh::Mesh &mesh,
325 const bool iso_parametric,
326 const Eigen::VectorXi &disc_orders,
327 const std::string &basis_type,
328 const std::string &poly_basis_type,
329 const assembler::Assembler &space_assembler,
330 const int value_dim,
331 const int quadrature_order,
332 const int mass_quadrature_order,
333 const bool use_corner_quadrature,
334 const int n_harmonic_samples,
335 const int integral_constraints,
336 FESpace &space,
337 VarFormBoundaryState &boundary,
338 std::shared_ptr<GeometryMapping> geometry)
339 {
340 using namespace mesh;
341
342 const std::string space_assembler_name = space_assembler.name();
343 const bool build_geom_mapping = geometry == nullptr;
344
345 space.reset();
346 boundary.reset();
347
348 space.value_dim = value_dim;
349
350 space.bases = std::make_shared<std::vector<basis::ElementBases>>();
351 space.geometry = build_geom_mapping ? std::make_shared<GeometryMapping>() : std::move(geometry);
352 assert(space.geometry);
353
354 space.disc_orders = disc_orders;
355 space.disc_ordersq = disc_orders;
356
357 Eigen::MatrixXi geom_disc_orders;
358 if (build_geom_mapping && !iso_parametric)
359 {
360 if (mesh.orders().size() <= 0)
361 {
362 geom_disc_orders.resizeLike(space.disc_orders);
363 geom_disc_orders.setConstant(1);
364 }
365 else
366 geom_disc_orders = mesh.orders();
367
368 space.geometry->bases = std::make_shared<std::vector<basis::ElementBases>>();
369 space.geometry->disc_orders = geom_disc_orders;
370 }
371
372 Eigen::MatrixXi geom_disc_ordersq = geom_disc_orders;
373
374 logger().info("Building {} basis...", (build_geom_mapping ? (iso_parametric ? "isoparametric" : "not isoparametric") : "finite-element"));
375
376 igl::Timer timer;
377 timer.start();
378
379 const bool has_polys = mesh.has_poly();
380 std::map<int, basis::InterfaceData> poly_edge_to_data_geom;
381
382 const bool use_continuous_gbasis = true;
383
384 if (mesh.is_volume())
385 {
386 const Mesh3D &tmp_mesh = dynamic_cast<const Mesh3D &>(mesh);
387
388 if (basis_type == "Spline")
389 {
391 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
392 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
393 }
394 else
395 {
396 if (build_geom_mapping && !iso_parametric)
398 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
399 geom_disc_orders, geom_disc_ordersq, false, false, has_polys,
400 !use_continuous_gbasis, use_corner_quadrature,
401 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
402 space.geometry->mesh_nodes);
403
405 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
406 space.disc_orders, space.disc_ordersq,
407 basis_type == "Bernstein",
408 basis_type == "Serendipity",
409 has_polys, false, use_corner_quadrature,
410 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
411 }
412 }
413 else
414 {
415 const Mesh2D &tmp_mesh = dynamic_cast<const Mesh2D &>(mesh);
416
417 if (basis_type == "Spline")
418 {
420 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
421 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
422 }
423 else
424 {
425 if (build_geom_mapping && !iso_parametric)
427 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
428 geom_disc_orders, false, false, has_polys,
429 !use_continuous_gbasis, use_corner_quadrature,
430 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
431 space.geometry->mesh_nodes);
432
434 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
435 space.disc_orders,
436 basis_type == "Bernstein",
437 basis_type == "Serendipity",
438 has_polys, false, use_corner_quadrature,
439 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
440 }
441 }
442
443 const bool use_fe_space_as_geometry = build_geom_mapping ? iso_parametric : space.is_iso_parametric();
444 build_polygonal_basis(mesh, poly_basis_type, space_assembler,
445 use_fe_space_as_geometry,
446 quadrature_order,
447 mass_quadrature_order,
448 n_harmonic_samples,
449 integral_constraints,
450 space,
451 boundary);
452
453 if (build_geom_mapping)
454 {
455 if (iso_parametric)
456 space.geometry->init_from_fe_space(space);
457 else
458 {
459 assert(space.geometry->bases);
460 assert(space.geometry->n_bases > 0);
461 }
462 }
463
464 boundary.total_local_boundary.clear();
465 for (const auto &lb : boundary.local_boundary)
466 boundary.total_local_boundary.emplace_back(lb);
467
468 if (build_geom_mapping)
469 {
470 igl::Timer timer2;
471 logger().debug("Building node mapping...");
472 timer2.start();
473 build_node_mapping(mesh, basis_type, space, space.space_in_node_to_node, space.space_in_primitive_to_primitive);
474 timer2.stop();
475 logger().debug("Done (took {}s)", timer2.getElapsedTime());
476 }
477
478 logger().info("n_bases {}", space.n_bases);
479
480 timings.building_basis_time += timer.getElapsedTime();
481 logger().info(" took {}s", timings.building_basis_time);
482
483 logger().info("n bases: {}", space.n_bases);
484 }
485
487 const mesh::Mesh &mesh,
488 const std::string &poly_basis_type,
489 const assembler::Assembler &space_assembler,
490 bool iso_parametric,
491 const int quadrature_order,
492 const int mass_quadrature_order,
493 const int n_harmonic_samples,
494 const int integral_constraints,
495 varform::FESpace &space,
497 {
498 if (space.poly_edge_to_data.empty() && space.polys.empty())
499 {
501 return;
502 }
503
504 const std::string space_assembler_name = space_assembler.name();
505
506 igl::Timer timer;
507 timer.start();
508 logger().info("Computing polygonal basis...");
509
510 int new_bases = 0;
511 const int dim = space_assembler.is_tensor() ? mesh.dimension() : 1;
512 if (iso_parametric)
513 {
514 if (mesh.is_volume())
515 {
516 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
517 log_and_throw_error("Barycentric bases not supported in 3D");
518
519 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
520 assert(linear_assembler);
522 *linear_assembler,
523 n_harmonic_samples,
524 dynamic_cast<const mesh::Mesh3D &>(mesh),
525 space.n_bases,
526 quadrature_order,
527 mass_quadrature_order,
528 integral_constraints,
529 *space.bases,
530 *space.bases,
531 space.poly_edge_to_data,
532 space.polys_3d);
533 }
534 else
535 {
536 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
537 if (poly_basis_type == "MeanValue")
538 {
540 space_assembler.name(), dim, mesh_2d, space.n_bases,
541 quadrature_order,
542 mass_quadrature_order,
543 *space.bases, boundary.local_boundary, space.polys);
544 }
545 else if (poly_basis_type == "Wachspress")
546 {
548 space_assembler.name(), dim, mesh_2d, space.n_bases,
549 quadrature_order,
550 mass_quadrature_order,
551 *space.bases, boundary.local_boundary, space.polys);
552 }
553 else
554 {
555 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
556 assert(linear_assembler);
558 *linear_assembler,
559 n_harmonic_samples,
560 mesh_2d,
561 space.n_bases,
562 quadrature_order,
563 mass_quadrature_order,
564 integral_constraints,
565 *space.bases,
566 *space.bases,
567 space.poly_edge_to_data,
568 space.polys);
569 }
570 }
571 }
572 else
573 {
574 assert(space.geometry);
575 assert(space.geometry->bases);
576 if (mesh.is_volume())
577 {
578 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
579 log_and_throw_error("Barycentric bases not supported in 3D");
580
581 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
582 assert(linear_assembler);
584 *linear_assembler,
585 n_harmonic_samples,
586 dynamic_cast<const mesh::Mesh3D &>(mesh),
587 space.n_bases,
588 quadrature_order,
589 mass_quadrature_order,
590 integral_constraints,
591 *space.bases,
592 *space.geometry->bases,
593 space.poly_edge_to_data,
594 space.polys_3d);
595 }
596 else
597 {
598 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
599 if (poly_basis_type == "MeanValue")
600 {
602 space_assembler.name(), dim, mesh_2d, space.n_bases,
603 quadrature_order,
604 mass_quadrature_order,
605 *space.bases, boundary.local_boundary, space.polys);
606 }
607 else if (poly_basis_type == "Wachspress")
608 {
610 space_assembler.name(), dim, mesh_2d, space.n_bases,
611 quadrature_order,
612 mass_quadrature_order,
613 *space.bases, boundary.local_boundary, space.polys);
614 }
615 else
616 {
617 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
618 assert(linear_assembler);
620 *linear_assembler,
621 n_harmonic_samples,
622 mesh_2d,
623 space.n_bases,
624 quadrature_order,
625 mass_quadrature_order,
626 integral_constraints,
627 *space.bases,
628 *space.geometry->bases,
629 space.poly_edge_to_data,
630 space.polys);
631 }
632 }
633 }
634
635 timer.stop();
636 timings.computing_poly_basis_time = timer.getElapsedTime();
637 logger().info(" took {}s", timings.computing_poly_basis_time);
638
639 space.n_bases += new_bases;
640 }
641
642 void VarForm::solve(Eigen::MatrixXd &sol)
643 {
644 prepare();
645 solve_problem(sol);
646 }
647
649 const mesh::Mesh &mesh,
650 const std::string &basis_type,
651 const FESpace &space,
652 Eigen::VectorXi &space_in_node_to_node,
653 Eigen::VectorXi &space_in_primitive_to_primitive) const
654 {
655 space_in_node_to_node.resize(0);
656 space_in_primitive_to_primitive.resize(0);
657
658 if (basis_type == "Spline")
659 {
660 logger().warn("Node ordering disabled, it dosent work for splines!");
661 return;
662 }
663
664 if (space.disc_orders.maxCoeff() >= 4 || space.disc_orders.maxCoeff() != space.disc_orders.minCoeff())
665 {
666 logger().warn("Node ordering disabled, it works only for p < 4 and uniform order!");
667 return;
668 }
669
670 if (!mesh.is_conforming())
671 {
672 logger().warn("Node ordering disabled, not supported for non-conforming meshes!");
673 return;
674 }
675
676 if (mesh.has_poly())
677 {
678 logger().warn("Node ordering disabled, not supported for polygonal meshes!");
679 return;
680 }
681
682 if (mesh.in_ordered_vertices().size() <= 0 || mesh.in_ordered_edges().size() <= 0 || (mesh.is_volume() && mesh.in_ordered_faces().size() <= 0))
683 {
684 logger().warn("Node ordering disabled, input vertices/edges/faces not computed!");
685 return;
686 }
687
688 if (!space.mesh_nodes)
689 {
690 logger().warn("Node ordering disabled, FE space does not expose mesh nodes!");
691 return;
692 }
693
694 const int num_vertex_nodes = space.mesh_nodes->num_vertex_nodes();
695 const int num_edge_nodes = space.mesh_nodes->num_edge_nodes();
696 const int num_face_nodes = space.mesh_nodes->num_face_nodes();
697 const int num_cell_nodes = space.mesh_nodes->num_cell_nodes();
698
699 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
700 const long n_vertices = num_vertex_nodes;
701 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
702 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
703
704 igl::Timer timer;
705
706 logger().trace("Building in-node to in-primitive mapping...");
707 timer.start();
708 Eigen::VectorXi in_node_to_in_primitive;
709 Eigen::VectorXi in_node_offset;
710 build_in_node_to_in_primitive(mesh, *space.mesh_nodes, in_node_to_in_primitive, in_node_offset);
711 timer.stop();
712 logger().trace("Done (took {}s)", timer.getElapsedTime());
713
714 logger().trace("Building in-primitive to primitive mapping...");
715 timer.start();
716 bool ok = build_in_primitive_to_primitive(
717 mesh, *space.mesh_nodes,
718 mesh.in_ordered_vertices(),
719 mesh.in_ordered_edges(),
720 mesh.in_ordered_faces(),
721 space_in_primitive_to_primitive);
722 timer.stop();
723 logger().trace("Done (took {}s)", timer.getElapsedTime());
724
725 if (!ok)
726 {
727 space_in_node_to_node.resize(0);
728 space_in_primitive_to_primitive.resize(0);
729 return;
730 }
731
732 const auto &tmp = space.mesh_nodes->in_ordered_vertices();
733 int max_tmp = -1;
734 for (auto v : tmp)
735 max_tmp = std::max(max_tmp, v);
736
737 space_in_node_to_node.resize(max_tmp + 1);
738 for (int i = 0; i < tmp.size(); ++i)
739 {
740 if (tmp[i] >= 0)
741 space_in_node_to_node[tmp[i]] = i;
742 }
743 }
744
745 void VarForm::assign_discr_orders(const json &discr_order, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders)
746 {
747 disc_orders.resize(mesh.n_elements());
748
749 if (discr_order.is_number_integer())
750 {
751 disc_orders.setConstant(discr_order);
752 }
753 else if (discr_order.is_string())
754 {
755 const std::string discr_orders_path = utils::resolve_path(discr_order, root_path);
756 Eigen::MatrixXi tmp;
757 io::read_matrix(discr_orders_path, tmp);
758 assert(tmp.size() == disc_orders.size());
759 assert(tmp.cols() == 1);
760 disc_orders = tmp;
761 }
762 else if (discr_order.is_array())
763 {
764 const auto b_discr_orders = discr_order;
765
766 std::map<int, int> b_orders;
767 for (size_t i = 0; i < b_discr_orders.size(); ++i)
768 {
769 assert(b_discr_orders[i]["id"].is_array() || b_discr_orders[i]["id"].is_number_integer());
770
771 const int order = b_discr_orders[i]["order"];
772 for (const int id : utils::json_as_array<int>(b_discr_orders[i]["id"]))
773 {
774 b_orders[id] = order;
775 logger().trace("bid {}, discr {}", id, order);
776 }
777 }
778
779 for (int e = 0; e < mesh.n_elements(); ++e)
780 {
781 const int bid = mesh.get_body_id(e);
782 const auto order = b_orders.find(bid);
783 if (order == b_orders.end())
784 {
785 logger().debug("Missing discretization order for body {}; using 1", bid);
786 b_orders[bid] = 1;
787 disc_orders[e] = 1;
788 }
789 else
790 {
791 disc_orders[e] = order->second;
792 }
793 }
794 }
795 else
796 {
797 logger().error("space/discr_order must be either a number a path or an array");
798 throw std::runtime_error("invalid json");
799 }
800 }
801
802 void VarForm::save_json(const Eigen::MatrixXd &solution) const
803 {
804 const std::string out_path = resolve_output_path(args["output"]["json"]);
805 if (out_path.empty())
806 return;
807
808 std::ofstream file(out_path);
809 if (!file.is_open())
810 {
811 logger().error("Unable to save simulation JSON to {}", out_path);
812 return;
813 }
814 save_json(solution, file);
815 }
816
817 void VarForm::set_materials(assembler::Assembler &assembler, const int size) const
818 {
819 assert(mesh_ != nullptr);
820 assembler.set_size(size);
821
822 if (!utils::is_param_valid(args, "materials"))
823 return;
824
825 std::vector<int> body_ids(mesh_->n_elements());
826 for (int i = 0; i < mesh_->n_elements(); ++i)
827 body_ids[i] = mesh_->get_body_id(i);
828
829 assembler.set_materials(body_ids, args["materials"], units, root_path);
830 }
831
833 {
835 return;
836
837 const io::OutputSpace space = output_space();
838 if (space.mesh)
839 {
840 output_geometry_.init_sampler(*space.mesh, args["output"]["paraview"]["vismesh_rel_area"]);
841 output_geometry_.build_grid(*space.mesh, args["output"]["advanced"]["sol_on_grid"]);
842 }
844 }
845
854
856 {
857 return [this, &solution, fields = opts.fields](const io::OutputSample &sample) {
858 return output_fields(
859 sample, solution,
861 sample.requested_fields.empty() ? fields : sample.requested_fields});
862 };
863 }
864
866 {
867 if (!problem)
868 return 0;
869 if (problem->is_scalar())
870 return 1;
871 return mesh_ ? mesh_->dimension() : 0;
872 }
873
874 std::shared_ptr<time_integrator::BDF> VarForm::make_bdf_time_integrator() const
875 {
876 const json &config = args["time"]["integrator"];
877 const std::string type = config.is_object() ? config.at("type").get<std::string>() : config.get<std::string>();
878
879 if (type == "ImplicitEuler" || type == "implict_euler")
880 return std::make_shared<time_integrator::BDF>();
881
882 if (!utils::StringUtils::startswith(type, "BDF"))
883 log_and_throw_error("First-order transient formulations require ImplicitEuler or BDF, got {}.", type);
884
885 auto integrator = std::make_shared<time_integrator::BDF>(
886 type == "BDF" ? 1 : std::stoi(type.substr(3)));
887 if (config.is_object() && config.contains("steps"))
888 integrator->set_parameters(config);
889 return integrator;
890 }
891
893 const double t0,
894 const double dt,
895 const int t,
896 const time_integrator::ImplicitTimeIntegrator *time_integrator,
897 const bool rest_mesh_written) const
898 {
899 const int global_t = output_file_index(t);
900 const std::string state_path = resolve_output_path(fmt::format(args["output"]["data"]["state"], global_t));
901 if (!state_path.empty() && time_integrator)
902 time_integrator->save_state(state_path);
903
904 save_restart_json(t0, dt, t, rest_mesh_written);
905 }
906
907 void VarForm::save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
908 {
909 const io::OutputSpace space = output_space();
910 if (!space.mesh || !args["output"]["advanced"]["save_time_sequence"])
911 return;
912 const int global_t = output_file_index(t);
913 if (global_t % args["output"]["paraview"]["skip_frame"].get<int>())
914 return;
915
917
918 logger().trace("Saving VTU...");
919 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
920 const auto opts = export_options(space);
922 resolve_output_path(fmt::format(step_name + "{:d}.vtu", global_t)),
923 space, output_field_function(solution, opts), time, dt,
924 opts);
925
927 resolve_output_path(args["output"]["paraview"]["file_name"]),
928 [step_name](int i) { return fmt::format(step_name + "{:d}.vtm", i); },
929 global_t, t0, dt, args["output"]["paraview"]["skip_frame"].get<int>());
930 }
931
932 void VarForm::save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
933 {
934 const io::OutputSpace space = output_space();
935 if (!space.mesh || !args["output"]["advanced"]["save_solve_sequence_debug"].get<bool>())
936 return;
937
938 const bool has_time = args.contains("time") && !args["time"].is_null();
939 double dt = 1;
940 if (has_time)
941 dt = args["time"]["dt"];
942
944 const auto opts = export_options(space);
946 resolve_output_path(fmt::format("solve_{:d}.vtu", i)),
947 space, output_field_function(solution, opts), t, dt,
948 opts);
949 }
950
951 void VarForm::notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
952 {
953 if (time_callback)
954 time_callback(t, time_steps, t0 + dt * t, t0 + dt * time_steps);
955 }
956
957 void VarForm::save_restart_json(const double t0, const double dt, const int t, const bool rest_mesh_written) const
958 {
959 const std::string restart_json_path = args["output"]["restart_json"];
960 if (restart_json_path.empty())
961 return;
962
963 const int global_t = output_file_index(t);
964
965 json restart_json;
966 restart_json["root_path"] = root_path;
967 restart_json["common"] = root_path;
968 restart_json["time"] = {{"t0", t0 + dt * t}};
969 restart_json["output"] = {{"data", {{"file_index_offset", global_t}}}};
970
971 restart_json["space"] = R"({
972 "remesh": {
973 "collapse": {
974 "abs_max_edge_length": -1,
975 "rel_max_edge_length": -1
976 }
977 }
978 })"_json;
979
980 const double starting_min_edge_length = stats.min_edge_length;
981 restart_json["space"]["remesh"]["collapse"]["abs_max_edge_length"] = std::min(
982 args["space"]["remesh"]["collapse"]["abs_max_edge_length"].get<double>(),
983 starting_min_edge_length * args["space"]["remesh"]["collapse"]["rel_max_edge_length"].get<double>());
984 restart_json["space"]["remesh"]["collapse"]["rel_max_edge_length"] = std::numeric_limits<float>::max();
985
986 std::string rest_mesh_path = args["output"]["data"]["rest_mesh"].get<std::string>();
987 if (!rest_mesh_path.empty())
988 {
989 if (!rest_mesh_written)
990 logger().warn("Restart JSON for {} references a rest mesh that this formulation does not write.", name());
991
992 rest_mesh_path = resolve_output_path(fmt::format(args["output"]["data"]["rest_mesh"], global_t));
993
994 std::vector<json> patch;
995 if (args["geometry"].is_array())
996 {
997 const std::vector<json> in_geometry = args["geometry"];
998 for (int i = 0; i < in_geometry.size(); ++i)
999 {
1000 if (!in_geometry[i]["is_obstacle"].get<bool>())
1001 {
1002 patch.push_back({
1003 {"op", "remove"},
1004 {"path", fmt::format("/geometry/{}", i)},
1005 });
1006 }
1007 }
1008
1009 const int remaining_geometry = in_geometry.size() - patch.size();
1010 assert(remaining_geometry >= 0);
1011
1012 patch.push_back({
1013 {"op", "add"},
1014 {"path", fmt::format("/geometry/{}", remaining_geometry > 0 ? "0" : "-")},
1015 {"value",
1016 {
1017 {"mesh", rest_mesh_path},
1018 }},
1019 });
1020 }
1021 else
1022 {
1023 assert(args["geometry"].is_object());
1024 patch.push_back({
1025 {"op", "remove"},
1026 {"path", "/geometry"},
1027 });
1028 patch.push_back({
1029 {"op", "replace"},
1030 {"path", "/geometry"},
1031 {"value",
1032 {
1033 {"mesh", rest_mesh_path},
1034 }},
1035 });
1036 }
1037
1038 restart_json["patch"] = patch;
1039 }
1040
1041 restart_json["input"] = {{
1042 "data",
1043 {
1044 {"state", resolve_output_path(fmt::format(args["output"]["data"]["state"], global_t))},
1045 },
1046 }};
1047
1048 std::ofstream file(resolve_output_path(fmt::format(restart_json_path, global_t)));
1049 file << restart_json;
1050 }
1051
1052 int VarForm::output_file_index(const int t) const
1053 {
1054 return t + args["output"]["data"]["file_index_offset"].get<int>();
1055 }
1056
1057 std::string VarForm::resolve_input_path(const std::string &path, const bool only_if_exists) const
1058 {
1059 return utils::resolve_path(path, root_path, only_if_exists);
1060 }
1061
1062 std::string VarForm::resolve_output_path(const std::string &path) const
1063 {
1064 if (output_path.empty() || path.empty() || std::filesystem::path(path).is_absolute())
1065 {
1066 return path;
1067 }
1068 return std::filesystem::weakly_canonical(std::filesystem::path(output_path) / path).string();
1069 }
1070
1072 const std::vector<basis::ElementBases> &bases,
1073 const std::vector<int> &node_ids,
1074 std::vector<RowVectorNd> &positions)
1075 {
1076 positions.resize(node_ids.size());
1077 for (int n = 0; n < int(node_ids.size()); ++n)
1078 {
1079 const int node_id = node_ids[n];
1080 bool found = false;
1081 for (const auto &bs : bases)
1082 {
1083 for (const auto &b : bs.bases)
1084 {
1085 for (const auto &lg : b.global())
1086 {
1087 if (lg.index == node_id)
1088 {
1089 positions[n] = lg.node;
1090 found = true;
1091 break;
1092 }
1093 }
1094
1095 if (found)
1096 break;
1097 }
1098
1099 if (found)
1100 break;
1101 }
1102 assert(found);
1103 }
1104 }
1105} // namespace polyfem::varform
int x
virtual bool is_tensor() const
virtual std::string name() const =0
virtual void set_size(const int size)
Definition Assembler.hpp:64
void set_materials(const std::vector< int > &body_ids, const json &body_params, const Units &units, const std::string &root_path)
Definition Assembler.cpp:97
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_order, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over triangles, Q1, Q2 over quads).
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_orderp, const int discr_orderq, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over tets, Q1, Q2 over hes).
static int build_bases(const std::string &assembler_name, const int dim, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, Eigen::MatrixXd > &mapped_boundary)
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_edge_to_data, std::map< int, Eigen::MatrixXd > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh3D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_face_to_data, std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data)
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data)
static int build_bases(const std::string &assembler_name, const int dim, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, Eigen::MatrixXd > &mapped_boundary)
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
Definition OutData.cpp:1630
void save_pvd(const std::string &name, const std::function< std::string(int)> &vtu_names, int time_steps, double t0, double dt, int skip_frame=1) const
save a PVD of a time dependent simulation
Definition OutData.cpp:1617
void save_vtu(const std::string &path, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const double dt, const ExportOptions &opts) const
saves the vtu file for time t
Definition OutData.cpp:1183
void init_sampler(const polyfem::mesh::Mesh &mesh, const double vismesh_rel_area)
unitalize the ref element sampler
Definition OutData.cpp:1625
timers from polyfem.
double loading_mesh_time
time to load the mesh
double building_basis_time
time to construct the basis
double computing_poly_basis_time
time to build the polygonal/polyhedral bases
void reset()
clears all stats
Definition OutData.cpp:1806
void compute_mesh_stats(const polyfem::mesh::Mesh &mesh)
compute stats (counts els type, mesh lenght, etc), step 1 of solve
Definition OutData.cpp:2017
double min_edge_length
min 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 n_vertices() const =0
number of vertices
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
const Eigen::MatrixXi & in_ordered_edges() const
Order of the input edges.
Definition Mesh.hpp:659
bool is_rational() const
check if curved mesh has rational polynomials elements
Definition Mesh.hpp:289
virtual bool is_conforming() const =0
if the mesh is conforming
const Eigen::MatrixXi & orders() const
order of each element
Definition Mesh.hpp:285
bool has_prism() const
checks if the mesh has prisms
Definition Mesh.hpp:590
bool is_linear() const
check if the mesh is linear
Definition Mesh.hpp:632
virtual bool is_volume() const =0
checks if mesh is volume
bool has_poly() const
checks if the mesh has polytopes
Definition Mesh.hpp:576
const Eigen::VectorXi & in_ordered_vertices() const
Order of the input vertices.
Definition Mesh.hpp:655
int dimension() const
utily for dimension
Definition Mesh.hpp:153
virtual int n_cells() const =0
number of cells
virtual int n_faces() const =0
number of faces
const Eigen::MatrixXi & in_ordered_faces() const
Order of the input edges.
Definition Mesh.hpp:663
virtual int n_edges() const =0
number of edges
Implicit time integrator of a second order ODE (equivently a system of coupled first order ODEs).
virtual void save_state(const std::string &state_path) const
Save the values of , , and .
A finite-element space for one scalar- or vector-valued field.
Definition FESpace.hpp:59
std::shared_ptr< std::vector< basis::ElementBases > > bases
Per-element basis data.
Definition FESpace.hpp:68
std::shared_ptr< GeometryMapping > geometry
Geometric mapping used to integrate this FE space.
Definition FESpace.hpp:89
int value_dim
Number of field components per scalar basis function.
Definition FESpace.hpp:62
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
std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > polys_3d
Physical vertices and face connectivity for 3D polyhedral elements.
Definition FESpace.hpp:83
std::map< int, Eigen::MatrixXd > polys
Physical boundary samples for 2D polygonal elements.
Definition FESpace.hpp:80
std::map< int, basis::InterfaceData > poly_edge_to_data
Polygonal-basis construction data, indexed by element ID.
Definition FESpace.hpp:77
Eigen::VectorXi space_in_primitive_to_primitive
Definition FESpace.hpp:92
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
virtual void solve_problem(Eigen::MatrixXd &sol)=0
int output_file_index(const int t) const
Definition VarForm.cpp:1052
void save_restart_json(const double t0, const double dt, const int t, const bool rest_mesh_written) const
Definition VarForm.cpp:957
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Definition VarForm.cpp:1057
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1071
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:191
virtual std::string name() const =0
Get the name of the variational formulation.
void solve(Eigen::MatrixXd &sol)
Solve the variational formulation and store the solution in the given matrix.
Definition VarForm.cpp:642
virtual io::OutputSpace output_space() const =0
Get the output space of the variational formulation, for output purposes.
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:203
io::OutStatsData stats
Definition VarForm.hpp:195
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:951
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:39
io::OutGeometryData::ExportOptions export_options(const io::OutputSpace &space) const
Definition VarForm.cpp:846
io::OutGeometryData output_geometry_
Definition VarForm.hpp:207
virtual void load_mesh(const mesh::Mesh &mesh, const json &args)=0
int problem_dimension() const
Get the problem dimension of the variational formulation, for output purposes.
Definition VarForm.cpp:865
virtual void assemble_mass_mat(const mesh::Mesh &mesh, const json &args)=0
void save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:932
virtual void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)=0
io::OutputFieldFunction output_field_function(const Eigen::MatrixXd &solution, const io::OutGeometryData::ExportOptions &opts) const
Definition VarForm.cpp:855
std::function< void(int, int, double, double)> time_callback
Definition VarForm.hpp:205
void build_polygonal_basis(const mesh::Mesh &mesh, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, bool iso_parametric, const int quadrature_order, const int mass_quadrature_order, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary)
Definition VarForm.cpp:486
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1062
void set_mesh(std::unique_ptr< mesh::Mesh > mesh, const double loading_mesh_time=0)
Set the mesh for the variational formulation.
Definition VarForm.cpp:296
void ensure_output_sampler() const
Definition VarForm.cpp:832
std::shared_ptr< time_integrator::BDF > make_bdf_time_integrator() const
Definition VarForm.cpp:874
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:907
virtual void assemble_rhs(const mesh::Mesh &mesh)=0
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:280
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:323
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:198
virtual std::vector< io::OutputField > output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options) const =0
Get the output fields of the variational formulation, for output purposes.
void save_step_state(const double t0, const double dt, const int t, const time_integrator::ImplicitTimeIntegrator *time_integrator, const bool rest_mesh_written=false) const
Definition VarForm.cpp:892
QuadratureOrders n_boundary_samples(const int discr_order, const int gdiscr_order) const
Definition VarForm.cpp:259
virtual void save_json(const Eigen::MatrixXd &solution, std::ostream &out) const =0
Save the solution to a JSON file, for output purposes.
void build_node_mapping(const mesh::Mesh &mesh, const std::string &basis_type, const FESpace &space, Eigen::VectorXi &space_in_node_to_node, Eigen::VectorXi &space_in_primitive_to_primitive) const
Definition VarForm.cpp:648
void set_materials(assembler::Assembler &assembler, const int size) const
Definition VarForm.cpp:817
virtual void reset()=0
Definition VarForm.cpp:268
void assign_discr_orders(const json &discr_order, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders)
Definition VarForm.cpp:745
bool read_matrix(const std::string &path, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &mat)
Reads a matrix from a file. Determines the file format based on the path's extension.
Definition MatrixIO.cpp:18
std::function< std::vector< OutputField >(const OutputSample &)> OutputFieldFunction
bool startswith(const std::string &str, const std::string &prefix)
Eigen::MatrixXd reorder_matrix(const Eigen::MatrixXd &in, const Eigen::VectorXi &in_to_out, int out_blocks=-1, const int block_size=1)
Reorder row blocks in a matrix.
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
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
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
std::vector< std::string > fields
Definition OutData.hpp:29
const mesh::Mesh * mesh
Temporary compatibility wrapper for boundary data belonging to one FE space.
Definition FESpace.hpp:152
std::vector< mesh::LocalBoundary > local_boundary
Definition FESpace.hpp:155
std::vector< mesh::LocalBoundary > total_local_boundary
Definition FESpace.hpp:154