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