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#include <spdlog/fmt/fmt.h>
34#include <paraviewo/VTMWriter.hpp>
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 (mesh.orders().size() <= 0)
85 {
86 if (args["space"]["discr_order"] == 1)
87 return true;
88 return args["space"]["advanced"]["isoparametric"];
89 }
90
91 if (mesh.orders().minCoeff() != mesh.orders().maxCoeff())
92 return false;
93
94 if (args["space"]["discr_order"] == mesh.orders().minCoeff())
95 return true;
96
97 return args["space"]["advanced"]["isoparametric"];
98 }
99
101 void build_in_node_to_in_primitive(const mesh::Mesh &mesh, const mesh::MeshNodes &mesh_nodes,
102 Eigen::VectorXi &in_node_to_in_primitive,
103 Eigen::VectorXi &in_node_offset)
104 {
105 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
106 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
107 const int num_face_nodes = mesh_nodes.num_face_nodes();
108 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
109
110 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
111
112 const long n_vertices = num_vertex_nodes;
113 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
114 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
115
116 in_node_to_in_primitive.resize(num_nodes);
117 in_node_offset.resize(num_nodes);
118
119 // Only one node per vertex, so this is an identity map.
120 in_node_to_in_primitive.head(num_vertex_nodes).setLinSpaced(num_vertex_nodes, 0, num_vertex_nodes - 1); // vertex nodes
121 in_node_offset.head(num_vertex_nodes).setZero();
122
123 int prim_offset = n_vertices;
124 int node_offset = num_vertex_nodes;
125 auto foo = [&](const int num_prims, const int num_prim_nodes) {
126 if (num_prims <= 0 || num_prim_nodes <= 0)
127 return;
128 const Eigen::VectorXi range = Eigen::VectorXi::LinSpaced(num_prim_nodes, 0, num_prim_nodes - 1);
129 // TODO: This assumes isotropic degree of element.
130 const int node_per_prim = num_prim_nodes / num_prims;
131
132 in_node_to_in_primitive.segment(node_offset, num_prim_nodes) =
133 range.array() / node_per_prim + prim_offset;
134
135 in_node_offset.segment(node_offset, num_prim_nodes) =
136 range.unaryExpr([&](const int x) { return x % node_per_prim; });
137
138 prim_offset += num_prims;
139 node_offset += num_prim_nodes;
140 };
141
142 foo(mesh.n_edges(), num_edge_nodes);
143 foo(mesh.n_faces(), num_face_nodes);
144 foo(mesh.n_cells(), num_cell_nodes);
145 }
146
147 bool build_in_primitive_to_primitive(
148 const mesh::Mesh &mesh, const mesh::MeshNodes &mesh_nodes,
149 const Eigen::VectorXi &in_ordered_vertices,
150 const Eigen::MatrixXi &in_ordered_edges,
151 const Eigen::MatrixXi &in_ordered_faces,
152 Eigen::VectorXi &in_primitive_to_primitive)
153 {
154 // NOTE: Assume in_cells_to_cells is identity
155 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
156 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
157 const int num_face_nodes = mesh_nodes.num_face_nodes();
158 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
159 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
160
161 const long n_vertices = num_vertex_nodes;
162 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
163 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
164
165 in_primitive_to_primitive.setLinSpaced(num_in_primitives, 0, num_in_primitives - 1);
166
167 igl::Timer timer;
168
169 // ------------
170 // Map vertices
171 // ------------
172
173 if (in_ordered_vertices.rows() != n_vertices)
174 {
175 logger().warn("Node ordering disabled, in_ordered_vertices != n_vertices, {} != {}", in_ordered_vertices.rows(), n_vertices);
176 return false;
177 }
178
179 in_primitive_to_primitive.head(n_vertices) = in_ordered_vertices;
180
181 int in_offset = n_vertices;
182 int offset = mesh.n_vertices();
183
184 // ---------
185 // Map edges
186 // ---------
187
188 logger().trace("Building Mesh edges to IDs...");
189 timer.start();
190 const auto edges_to_ids = mesh.edges_to_ids();
191 if (in_ordered_edges.rows() != edges_to_ids.size())
192 {
193 logger().warn("Node ordering disabled, in_ordered_edges != edges_to_ids, {} != {}", in_ordered_edges.rows(), edges_to_ids.size());
194 return false;
195 }
196 timer.stop();
197 logger().trace("Done (took {}s)", timer.getElapsedTime());
198
199 logger().trace("Building in-edge to edge mapping...");
200 timer.start();
201 for (int in_ei = 0; in_ei < in_ordered_edges.rows(); in_ei++)
202 {
203 const std::pair<int, int> in_edge(
204 in_ordered_edges.row(in_ei).minCoeff(),
205 in_ordered_edges.row(in_ei).maxCoeff());
206 in_primitive_to_primitive[in_offset + in_ei] =
207 offset + edges_to_ids.at(in_edge); // offset edge ids
208 }
209 timer.stop();
210 logger().trace("Done (took {}s)", timer.getElapsedTime());
211
212 in_offset += mesh.n_edges();
213 offset += mesh.n_edges();
214
215 // ---------
216 // Map faces
217 // ---------
218
219 if (mesh.is_volume())
220 {
221 logger().trace("Building Mesh faces to IDs...");
222 timer.start();
223 const auto faces_to_ids = mesh.faces_to_ids();
224 if (in_ordered_faces.rows() != faces_to_ids.size())
225 {
226 logger().warn("Node ordering disabled, in_ordered_faces != faces_to_ids, {} != {}", in_ordered_faces.rows(), faces_to_ids.size());
227 return false;
228 }
229 timer.stop();
230 logger().trace("Done (took {}s)", timer.getElapsedTime());
231
232 logger().trace("Building in-face to face mapping...");
233 timer.start();
234 for (int in_fi = 0; in_fi < in_ordered_faces.rows(); in_fi++)
235 {
236 std::vector<int> in_face(in_ordered_faces.cols());
237 for (int i = 0; i < in_face.size(); i++)
238 in_face[i] = in_ordered_faces(in_fi, i);
239 std::sort(in_face.begin(), in_face.end());
240
241 in_primitive_to_primitive[in_offset + in_fi] =
242 offset + faces_to_ids.at(in_face); // offset face ids
243 }
244 timer.stop();
245 logger().trace("Done (took {}s)", timer.getElapsedTime());
246
247 in_offset += mesh.n_faces();
248 offset += mesh.n_faces();
249 }
250
251 return true;
252 }
253 } // namespace
254
255 QuadratureOrders VarForm::n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
256 {
258 const int n_b_samples_j = args["space"]["advanced"]["n_boundary_samples"];
259 const int boundary_order = std::max({discr_order, discr_orderq, gdiscr_order});
260 const int n_b_samples = std::max(n_b_samples_j, AssemblerUtils::quadrature_order("Mass", boundary_order, AssemblerUtils::BasisType::POLY, mesh_->dimension()));
261 return {{n_b_samples, n_b_samples}};
262 }
263
265 {
266 // FIXME check subclasses
267 stats.reset();
270 prepared_ = false;
271 problem = nullptr;
272 time_callback = nullptr;
273 mesh_ = nullptr;
274 }
275
276 void VarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
277 {
278 reset();
279
280 this->units = units;
281 this->args = args;
282
283 if (utils::is_param_valid(args, "root_path"))
284 root_path = args["root_path"].get<std::string>();
285 else
286 root_path = "";
287
288 this->output_path = out_path;
290 }
291
292 void VarForm::set_mesh(std::unique_ptr<mesh::Mesh> mesh, const double loading_mesh_time)
293 {
294 mesh_ = std::move(mesh);
295 timings.loading_mesh_time = loading_mesh_time;
297 prepared_ = false;
298 if (!mesh_)
299 return;
300
302 }
303
305 {
306 if (prepared_)
307 return;
308 if (!mesh_)
309 log_and_throw_error("Load the mesh first!");
310
311 mesh_->prepare_mesh();
313 build_basis(*mesh_, should_use_iso_parametric(*mesh_, args), args);
316 prepared_ = true;
317 }
318
320 mesh::Mesh &mesh,
321 const bool iso_parametric,
322 const Eigen::VectorXi &disc_orders,
323 const Eigen::VectorXi &disc_ordersq,
324 const std::string &basis_type,
325 const std::string &poly_basis_type,
326 const assembler::Assembler &space_assembler,
327 const int value_dim,
328 const int quadrature_order,
329 const int mass_quadrature_order,
330 const bool use_corner_quadrature,
331 const int n_harmonic_samples,
332 const int integral_constraints,
333 FESpace &space,
334 VarFormBoundaryState &boundary,
335 std::shared_ptr<GeometryMapping> geometry)
336 {
337 using namespace mesh;
338
339 const std::string space_assembler_name = space_assembler.name();
340 const bool build_geom_mapping = geometry == nullptr;
341
342 space.reset();
343 boundary.reset();
344
345 space.value_dim = value_dim;
346
347 space.bases = std::make_shared<std::vector<basis::ElementBases>>();
348 space.geometry = build_geom_mapping ? std::make_shared<GeometryMapping>() : std::move(geometry);
349 assert(space.geometry);
350
351 space.disc_orders = disc_orders;
352 space.disc_ordersq = disc_ordersq;
353
354 Eigen::MatrixXi geom_disc_orders;
355 if (build_geom_mapping && !iso_parametric)
356 {
357 if (mesh.orders().size() <= 0)
358 {
359 geom_disc_orders.resizeLike(space.disc_orders);
360 geom_disc_orders.setConstant(1);
361 }
362 else
363 geom_disc_orders = mesh.orders();
364
365 space.geometry->bases = std::make_shared<std::vector<basis::ElementBases>>();
366 space.geometry->disc_orders = geom_disc_orders;
367 }
368
369 Eigen::MatrixXi geom_disc_ordersq = geom_disc_orders;
370
371 logger().info("Building {} basis...", (build_geom_mapping ? (iso_parametric ? "isoparametric" : "not isoparametric") : "finite-element"));
372
373 igl::Timer timer;
374 timer.start();
375
376 const bool has_polys = mesh.has_poly();
377 std::map<int, basis::InterfaceData> poly_edge_to_data_geom;
378
379 const bool use_continuous_gbasis = true;
380
381 if (mesh.is_volume())
382 {
383 const Mesh3D &tmp_mesh = dynamic_cast<const Mesh3D &>(mesh);
384
385 if (basis_type == "Spline")
386 {
388 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
389 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
390 }
391 else
392 {
393 if (build_geom_mapping && !iso_parametric)
395 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
396 geom_disc_orders, geom_disc_ordersq, false, false, has_polys,
397 !use_continuous_gbasis, use_corner_quadrature,
398 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
399 space.geometry->mesh_nodes);
400
402 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
403 space.disc_orders, space.disc_ordersq,
404 basis_type == "Bernstein",
405 basis_type == "Serendipity",
406 has_polys, false, use_corner_quadrature,
407 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
408 }
409 }
410 else
411 {
412 const Mesh2D &tmp_mesh = dynamic_cast<const Mesh2D &>(mesh);
413
414 if (basis_type == "Spline")
415 {
417 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
418 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
419 }
420 else
421 {
422 if (build_geom_mapping && !iso_parametric)
424 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
425 geom_disc_orders, false, false, has_polys,
426 !use_continuous_gbasis, use_corner_quadrature,
427 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
428 space.geometry->mesh_nodes);
429
431 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
432 space.disc_orders,
433 basis_type == "Bernstein",
434 basis_type == "Serendipity",
435 has_polys, false, use_corner_quadrature,
436 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
437 }
438 }
439
440 const bool use_fe_space_as_geometry = build_geom_mapping ? iso_parametric : space.is_iso_parametric();
441 build_polygonal_basis(mesh, poly_basis_type, space_assembler,
442 use_fe_space_as_geometry,
443 quadrature_order,
444 mass_quadrature_order,
445 n_harmonic_samples,
446 integral_constraints,
447 space,
448 boundary);
449
450 if (build_geom_mapping)
451 {
452 if (iso_parametric)
453 space.geometry->init_from_fe_space(space);
454 else
455 {
456 assert(space.geometry->bases);
457 assert(space.geometry->n_bases > 0);
458 }
459 }
460
461 boundary.total_local_boundary.clear();
462 for (const auto &lb : boundary.local_boundary)
463 boundary.total_local_boundary.emplace_back(lb);
464
465 if (build_geom_mapping)
466 {
467 igl::Timer timer2;
468 logger().debug("Building node mapping...");
469 timer2.start();
470 build_node_mapping(mesh, basis_type, space, space.space_in_node_to_node, space.space_in_primitive_to_primitive);
471 timer2.stop();
472 logger().debug("Done (took {}s)", timer2.getElapsedTime());
473 }
474
475 logger().info("n_bases {}", space.n_bases);
476
477 timings.building_basis_time += timer.getElapsedTime();
478 logger().info(" took {}s", timings.building_basis_time);
479
480 logger().info("n bases: {}", space.n_bases);
481 }
482
484 const mesh::Mesh &mesh,
485 const std::string &poly_basis_type,
486 const assembler::Assembler &space_assembler,
487 bool iso_parametric,
488 const int quadrature_order,
489 const int mass_quadrature_order,
490 const int n_harmonic_samples,
491 const int integral_constraints,
492 varform::FESpace &space,
494 {
495 if (space.poly_edge_to_data.empty() && space.polys.empty())
496 {
498 return;
499 }
500
501 const std::string space_assembler_name = space_assembler.name();
502
503 igl::Timer timer;
504 timer.start();
505 logger().info("Computing polygonal basis...");
506
507 int new_bases = 0;
508 const int dim = space_assembler.is_tensor() ? mesh.dimension() : 1;
509 if (iso_parametric)
510 {
511 if (mesh.is_volume())
512 {
513 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
514 log_and_throw_error("Barycentric bases not supported in 3D");
515
516 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
517 assert(linear_assembler);
519 *linear_assembler,
520 n_harmonic_samples,
521 dynamic_cast<const mesh::Mesh3D &>(mesh),
522 space.n_bases,
523 quadrature_order,
524 mass_quadrature_order,
525 integral_constraints,
526 *space.bases,
527 *space.bases,
528 space.poly_edge_to_data,
529 space.polys_3d);
530 }
531 else
532 {
533 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
534 if (poly_basis_type == "MeanValue")
535 {
537 space_assembler.name(), dim, mesh_2d, space.n_bases,
538 quadrature_order,
539 mass_quadrature_order,
540 *space.bases, boundary.local_boundary, space.polys);
541 }
542 else if (poly_basis_type == "Wachspress")
543 {
545 space_assembler.name(), dim, mesh_2d, space.n_bases,
546 quadrature_order,
547 mass_quadrature_order,
548 *space.bases, boundary.local_boundary, space.polys);
549 }
550 else
551 {
552 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
553 assert(linear_assembler);
555 *linear_assembler,
556 n_harmonic_samples,
557 mesh_2d,
558 space.n_bases,
559 quadrature_order,
560 mass_quadrature_order,
561 integral_constraints,
562 *space.bases,
563 *space.bases,
564 space.poly_edge_to_data,
565 space.polys);
566 }
567 }
568 }
569 else
570 {
571 assert(space.geometry);
572 assert(space.geometry->bases);
573 if (mesh.is_volume())
574 {
575 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
576 log_and_throw_error("Barycentric bases not supported in 3D");
577
578 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
579 assert(linear_assembler);
581 *linear_assembler,
582 n_harmonic_samples,
583 dynamic_cast<const mesh::Mesh3D &>(mesh),
584 space.n_bases,
585 quadrature_order,
586 mass_quadrature_order,
587 integral_constraints,
588 *space.bases,
589 *space.geometry->bases,
590 space.poly_edge_to_data,
591 space.polys_3d);
592 }
593 else
594 {
595 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
596 if (poly_basis_type == "MeanValue")
597 {
599 space_assembler.name(), dim, mesh_2d, space.n_bases,
600 quadrature_order,
601 mass_quadrature_order,
602 *space.bases, boundary.local_boundary, space.polys);
603 }
604 else if (poly_basis_type == "Wachspress")
605 {
607 space_assembler.name(), dim, mesh_2d, space.n_bases,
608 quadrature_order,
609 mass_quadrature_order,
610 *space.bases, boundary.local_boundary, space.polys);
611 }
612 else
613 {
614 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
615 assert(linear_assembler);
617 *linear_assembler,
618 n_harmonic_samples,
619 mesh_2d,
620 space.n_bases,
621 quadrature_order,
622 mass_quadrature_order,
623 integral_constraints,
624 *space.bases,
625 *space.geometry->bases,
626 space.poly_edge_to_data,
627 space.polys);
628 }
629 }
630 }
631
632 timer.stop();
633 timings.computing_poly_basis_time = timer.getElapsedTime();
634 logger().info(" took {}s", timings.computing_poly_basis_time);
635
636 space.n_bases += new_bases;
637 }
638
640 Eigen::MatrixXd &sol,
641 const InitialConditionOverride *initial_condition_override,
642 const ForwardStepCallback &post_step)
643 {
644 prepare();
645 solve_problem(sol, initial_condition_override, post_step);
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
746 const json &space_args,
747 const mesh::Mesh &mesh,
748 Eigen::VectorXi &disc_orders,
749 Eigen::VectorXi &disc_ordersq)
750 {
751 assign_discr_orders(space_args, -1, mesh, disc_orders, disc_ordersq);
752 }
753
755 const json &space_args,
756 const int fe_space_id,
757 const mesh::Mesh &mesh,
758 Eigen::VectorXi &disc_orders,
759 Eigen::VectorXi &disc_ordersq)
760 {
761 const auto assign_order = [&](const json &order_json, Eigen::VectorXi &orders) {
762 orders.resize(mesh.n_elements());
763
764 if (order_json.is_number_integer())
765 {
766 orders.setConstant(order_json);
767 }
768 else if (order_json.is_string())
769 {
770 const std::string orders_path = utils::resolve_path(order_json, root_path);
771 Eigen::MatrixXi tmp;
772 io::read_matrix(orders_path, tmp);
773 assert(tmp.size() == orders.size());
774 assert(tmp.cols() == 1);
775 orders = tmp;
776 }
777 else if (order_json.is_array())
778 {
779 orders.setOnes();
780 std::map<int, int> body_orders;
781 bool has_matching_order = false;
782 for (const json &entry : order_json)
783 {
784 if (entry.contains("fe_space"))
785 {
786 const int entry_space_id = entry["fe_space"].get<int>();
787 if (entry_space_id >= 0 && fe_space_id < 0)
788 log_and_throw_error("FE-space-specific discretization orders require an FE space ID.");
789 if (entry_space_id >= 0 && entry_space_id != fe_space_id)
790 continue;
791 }
792
793 has_matching_order = true;
794 const int order = entry["order"];
795 if (!entry.contains("id") || (entry["id"].is_number_integer() && entry["id"].get<int>() < 0))
796 {
797 orders.setConstant(order);
798 continue;
799 }
800
801 for (const int id : utils::json_as_array<int>(entry["id"]))
802 {
803 body_orders[id] = order;
804 logger().trace("bid {}, discr {}", id, order);
805 }
806 }
807
808 if (!has_matching_order)
809 log_and_throw_error("Missing discretization order for FE space {}.", fe_space_id);
810
811 for (int e = 0; e < mesh.n_elements(); ++e)
812 {
813 const auto order = body_orders.find(mesh.get_body_id(e));
814 if (order != body_orders.end())
815 orders[e] = order->second;
816 }
817 }
818 else
819 {
820 log_and_throw_error("Discretization order must be a number, a path, or an array.");
821 }
822 };
823
824 assign_order(space_args["discr_order"], disc_orders);
825
826 const json &discr_orderq = space_args["discr_orderq"];
827 if (discr_orderq.is_number_integer() && discr_orderq.get<int>() < 0)
828 disc_ordersq = disc_orders;
829 else
830 assign_order(discr_orderq, disc_ordersq);
831
832 int max_prism_order = 0;
833 for (int e = 0; e < mesh.n_elements(); ++e)
834 {
835 if (mesh.is_prism(e))
836 max_prism_order = std::max({max_prism_order, disc_orders[e], disc_ordersq[e]});
837 }
838
839 if (max_prism_order > 0)
840 {
841 for (int e = 0; e < mesh.n_elements(); ++e)
842 {
843 if (mesh.is_simplex(e) || mesh.is_pyramid(e))
844 disc_orders[e] = max_prism_order;
845 }
846 }
847
848 logger().info(
849 "discretization orders: p=[{}, {}], q=[{}, {}]",
850 disc_orders.minCoeff(), disc_orders.maxCoeff(),
851 disc_ordersq.minCoeff(), disc_ordersq.maxCoeff());
852 }
853
854 void VarForm::save_json(const Eigen::MatrixXd &solution) const
855 {
856 const std::string out_path = resolve_output_path(args["output"]["json"]);
857 if (out_path.empty())
858 return;
859
860 std::ofstream file(out_path);
861 if (!file.is_open())
862 {
863 logger().error("Unable to save simulation JSON to {}", out_path);
864 return;
865 }
866 save_json(solution, file);
867 }
868
869 void VarForm::set_materials(assembler::Assembler &assembler, const int size) const
870 {
871 assert(mesh_ != nullptr);
872 assembler.set_size(size);
873
874 if (!utils::is_param_valid(args, "materials"))
875 return;
876
877 std::vector<int> body_ids(mesh_->n_elements());
878 for (int i = 0; i < mesh_->n_elements(); ++i)
879 body_ids[i] = mesh_->get_body_id(i);
880
881 assembler.set_materials(body_ids, args["materials"], units, root_path);
882 }
883
885 {
887 return;
888
889 const io::OutputSpace space = output_space();
890 if (space.mesh)
891 {
892 output_geometry_.init_sampler(*space.mesh, args["output"]["paraview"]["vismesh_rel_area"]);
893 output_geometry_.build_grid(*space.mesh, args["output"]["advanced"]["sol_on_grid"]);
894 }
896 }
897
906
908 {
909 return [this, &solution, fields = opts.fields](const io::OutputSample &sample) {
910 return output_fields(
911 sample, solution,
913 sample.requested_fields.empty() ? fields : sample.requested_fields});
914 };
915 }
916
918 {
919 if (!problem)
920 return 0;
921 if (problem->is_scalar())
922 return 1;
923 return mesh_ ? mesh_->dimension() : 0;
924 }
925
927 const double t0,
928 const double dt,
929 const int t,
930 const time_integrator::ImplicitTimeIntegrator *time_integrator,
931 const bool rest_mesh_written) const
932 {
933 const int global_t = output_file_index(t);
934 const std::string state_path = resolve_output_path(fmt::format(args["output"]["data"]["state"], global_t));
935 if (!state_path.empty() && time_integrator)
936 time_integrator->save_state(state_path);
937
938 save_restart_json(t0, dt, t, rest_mesh_written);
939 }
940
941 void VarForm::save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
942 {
943 paraviewo::VTMWriter vtm(time);
944 if (!save_timestep_to_vtm(time, t, dt, solution, vtm, ""))
945 return;
946
947 const int global_t = output_file_index(t);
948 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
949 vtm.save(resolve_output_path(fmt::format(step_name + "{:d}.vtm", global_t)));
950
952 resolve_output_path(args["output"]["paraview"]["file_name"]),
953 [step_name](int i) { return fmt::format(step_name + "{:d}.vtm", i); },
954 global_t, t0, dt, args["output"]["paraview"]["skip_frame"].get<int>());
955 }
956
958 const double time, const int t, const double dt,
959 const Eigen::MatrixXd &solution, paraviewo::VTMWriter &vtm,
960 const std::string &block_prefix) const
961 {
962 const io::OutputSpace space = output_space();
963 if (!space.mesh || !args["output"]["advanced"]["save_time_sequence"])
964 return false;
965 const int global_t = output_file_index(t);
966 if (global_t % args["output"]["paraview"]["skip_frame"].get<int>())
967 return false;
968
970 logger().trace("Saving VTU...");
971 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
972 const auto opts = export_options(space);
974 resolve_output_path(fmt::format(step_name + "{:d}.vtu", global_t)),
975 space, output_field_function(solution, opts), time, dt,
976 opts, vtm, block_prefix);
977 return true;
978 }
979
980 void VarForm::save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
981 {
982 const io::OutputSpace space = output_space();
983 if (!space.mesh || !args["output"]["advanced"]["save_solve_sequence_debug"].get<bool>())
984 return;
985
986 const bool has_time = args.contains("time") && !args["time"].is_null();
987 double dt = 1;
988 if (has_time)
989 dt = args["time"]["dt"];
990
992 const auto opts = export_options(space);
994 resolve_output_path(fmt::format("solve_{:d}.vtu", i)),
995 space, output_field_function(solution, opts), t, dt,
996 opts);
997 }
998
999 void VarForm::notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
1000 {
1001 if (time_callback)
1002 time_callback(t, time_steps, t0 + dt * t, t0 + dt * time_steps);
1003 }
1004
1005 void VarForm::save_restart_json(const double t0, const double dt, const int t, const bool rest_mesh_written) const
1006 {
1007 const std::string restart_json_path = args["output"]["restart_json"];
1008 if (restart_json_path.empty())
1009 return;
1010
1011 const int global_t = output_file_index(t);
1012
1013 json restart_json;
1014 restart_json["root_path"] = root_path;
1015 restart_json["common"] = root_path;
1016 restart_json["time"] = {{"t0", t0 + dt * t}};
1017 restart_json["output"] = {{"data", {{"file_index_offset", global_t}}}};
1018
1019 restart_json["space"] = R"({
1020 "remesh": {
1021 "collapse": {
1022 "abs_max_edge_length": -1,
1023 "rel_max_edge_length": -1
1024 }
1025 }
1026 })"_json;
1027
1028 const double starting_min_edge_length = stats.min_edge_length;
1029 restart_json["space"]["remesh"]["collapse"]["abs_max_edge_length"] = std::min(
1030 args["space"]["remesh"]["collapse"]["abs_max_edge_length"].get<double>(),
1031 starting_min_edge_length * args["space"]["remesh"]["collapse"]["rel_max_edge_length"].get<double>());
1032 restart_json["space"]["remesh"]["collapse"]["rel_max_edge_length"] = std::numeric_limits<float>::max();
1033
1034 std::string rest_mesh_path = args["output"]["data"]["rest_mesh"].get<std::string>();
1035 if (!rest_mesh_path.empty())
1036 {
1037 if (!rest_mesh_written)
1038 logger().warn("Restart JSON for {} references a rest mesh that this formulation does not write.", name());
1039
1040 rest_mesh_path = resolve_output_path(fmt::format(args["output"]["data"]["rest_mesh"], global_t));
1041
1042 std::vector<json> patch;
1043 if (args["geometry"].is_array())
1044 {
1045 const std::vector<json> in_geometry = args["geometry"];
1046 for (int i = 0; i < in_geometry.size(); ++i)
1047 {
1048 if (!in_geometry[i]["is_obstacle"].get<bool>())
1049 {
1050 patch.push_back({
1051 {"op", "remove"},
1052 {"path", fmt::format("/geometry/{}", i)},
1053 });
1054 }
1055 }
1056
1057 const int remaining_geometry = in_geometry.size() - patch.size();
1058 assert(remaining_geometry >= 0);
1059
1060 patch.push_back({
1061 {"op", "add"},
1062 {"path", fmt::format("/geometry/{}", remaining_geometry > 0 ? "0" : "-")},
1063 {"value",
1064 {
1065 {"mesh", rest_mesh_path},
1066 }},
1067 });
1068 }
1069 else
1070 {
1071 assert(args["geometry"].is_object());
1072 patch.push_back({
1073 {"op", "remove"},
1074 {"path", "/geometry"},
1075 });
1076 patch.push_back({
1077 {"op", "replace"},
1078 {"path", "/geometry"},
1079 {"value",
1080 {
1081 {"mesh", rest_mesh_path},
1082 }},
1083 });
1084 }
1085
1086 restart_json["patch"] = patch;
1087 }
1088
1089 restart_json["input"] = {{
1090 "data",
1091 {
1092 {"state", resolve_output_path(fmt::format(args["output"]["data"]["state"], global_t))},
1093 },
1094 }};
1095
1096 std::ofstream file(resolve_output_path(fmt::format(restart_json_path, global_t)));
1097 file << restart_json;
1098 }
1099
1100 int VarForm::output_file_index(const int t) const
1101 {
1102 return t + args["output"]["data"]["file_index_offset"].get<int>();
1103 }
1104
1105 std::string VarForm::resolve_input_path(const std::string &path, const bool only_if_exists) const
1106 {
1107 return utils::resolve_path(path, root_path, only_if_exists);
1108 }
1109
1110 std::string VarForm::resolve_output_path(const std::string &path) const
1111 {
1112 if (output_path.empty() || path.empty() || std::filesystem::path(path).is_absolute())
1113 {
1114 return path;
1115 }
1116 return std::filesystem::weakly_canonical(std::filesystem::path(output_path) / path).string();
1117 }
1118
1120 const std::vector<basis::ElementBases> &bases,
1121 const std::vector<int> &node_ids,
1122 std::vector<RowVectorNd> &positions)
1123 {
1124 positions.resize(node_ids.size());
1125 for (int n = 0; n < int(node_ids.size()); ++n)
1126 {
1127 const int node_id = node_ids[n];
1128 bool found = false;
1129 for (const auto &bs : bases)
1130 {
1131 for (const auto &b : bs.bases)
1132 {
1133 for (const auto &lg : b.global())
1134 {
1135 if (lg.index == node_id)
1136 {
1137 positions[n] = lg.node;
1138 found = true;
1139 break;
1140 }
1141 }
1142
1143 if (found)
1144 break;
1145 }
1146
1147 if (found)
1148 break;
1149 }
1150 assert(found);
1151 }
1152 }
1153} // 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:2634
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:2621
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:2162
void init_sampler(const polyfem::mesh::Mesh &mesh, const double vismesh_rel_area)
unitalize the ref element sampler
Definition OutData.cpp:2629
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:2810
void compute_mesh_stats(const polyfem::mesh::Mesh &mesh)
compute stats (counts els type, mesh lenght, etc), step 1 of solve
Definition OutData.cpp:3021
double min_edge_length
min edge lenght
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:49
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:174
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:525
const Eigen::MatrixXi & in_ordered_edges() const
Order of the input edges.
Definition Mesh.hpp:686
bool is_rational() const
check if curved mesh has rational polynomials elements
Definition Mesh.hpp:300
virtual bool is_conforming() const =0
if the mesh is conforming
const Eigen::MatrixXi & orders() const
order of each element
Definition Mesh.hpp:296
bool is_simplex(const int el_id) const
checks if element is simplex
Definition Mesh.cpp:507
bool has_prism() const
checks if the mesh has prisms
Definition Mesh.hpp:617
bool is_prism(const int el_id) const
checks if element is a prism
Definition Mesh.cpp:512
bool is_linear() const
check if the mesh is linear
Definition Mesh.hpp:659
virtual bool is_volume() const =0
checks if mesh is volume
bool has_poly() const
checks if the mesh has polytopes
Definition Mesh.hpp:603
const Eigen::VectorXi & in_ordered_vertices() const
Order of the input vertices.
Definition Mesh.hpp:682
int dimension() const
utily for dimension
Definition Mesh.hpp:164
virtual int n_cells() const =0
number of cells
virtual int n_faces() const =0
number of faces
bool is_pyramid(const int el_id) const
checks if element is a pyramid
Definition Mesh.cpp:517
const Eigen::MatrixXi & in_ordered_faces() const
Order of the input edges.
Definition Mesh.hpp:690
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
int output_file_index(const int t) const
Definition VarForm.cpp:1100
void save_restart_json(const double t0, const double dt, const int t, const bool rest_mesh_written) const
Definition VarForm.cpp:1005
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Definition VarForm.cpp:1105
void prepare()
Prepare all discretization and assembly data without running a solve.
Definition VarForm.cpp:304
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1119
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:214
virtual std::string name() const =0
Get the name of the variational formulation.
void solve(Eigen::MatrixXd &sol, const InitialConditionOverride *initial_condition_override=nullptr, const ForwardStepCallback &post_step={})
Solve the variational formulation and store the solution in the given matrix.
Definition VarForm.cpp:639
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:226
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:745
io::OutStatsData stats
Definition VarForm.hpp:218
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:999
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:898
io::OutGeometryData output_geometry_
Definition VarForm.hpp:230
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:917
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:980
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:907
std::function< void(int, int, double, double)> time_callback
Definition VarForm.hpp:228
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:255
virtual void solve_problem(Eigen::MatrixXd &sol, const InitialConditionOverride *initial_condition_override, const ForwardStepCallback &post_step)=0
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:483
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:319
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1110
bool save_timestep_to_vtm(const double time, const int t, const double dt, const Eigen::MatrixXd &solution, paraviewo::VTMWriter &vtm, const std::string &block_prefix) const
Definition VarForm.cpp:957
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:292
void ensure_output_sampler() const
Definition VarForm.cpp:884
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:941
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:276
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:221
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:926
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:869
virtual void reset()=0
Definition VarForm.cpp:264
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.
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:49
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:34
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