PolyFEM
Loading...
Searching...
No Matches
VarForm.cpp
Go to the documentation of this file.
2
4
7
25
26#include <igl/Timer.h>
30
31#include <fstream>
32#include <limits>
33#include <numeric>
34#include <spdlog/fmt/fmt.h>
35#include <paraviewo/VTMWriter.hpp>
36
37namespace polyfem::varform
38{
39 namespace
40 {
41
42 bool should_use_iso_parametric(const mesh::Mesh &mesh, const json &args)
43 {
44 if (mesh.has_poly())
45 return true;
46
47 if (args["space"]["basis_type"] == "Bernstein")
48 return false;
49
50 if (args["space"]["basis_type"] == "Spline")
51 return true;
52
53 if (mesh.is_rational())
54 return false;
55
56 if (args["space"]["use_p_ref"])
57 return false;
58
59 if (mesh.orders().size() <= 0)
60 {
61 if (args["space"]["discr_order"] == 1)
62 return true;
63 return args["space"]["advanced"]["isoparametric"];
64 }
65
66 if (mesh.orders().minCoeff() != mesh.orders().maxCoeff())
67 return false;
68
69 if (args["space"]["discr_order"] == mesh.orders().minCoeff())
70 return true;
71
72 return args["space"]["advanced"]["isoparametric"];
73 }
74
75 bool build_in_primitive_to_primitive(
76 const mesh::Mesh &mesh, const mesh::MeshNodes &mesh_nodes,
77 const Eigen::VectorXi &in_ordered_vertices,
78 const Eigen::MatrixXi &in_ordered_edges,
79 const Eigen::MatrixXi &in_ordered_faces,
80 Eigen::VectorXi &in_primitive_to_primitive)
81 {
82 // NOTE: Assume in_cells_to_cells is identity
83 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
84 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
85 const int num_face_nodes = mesh_nodes.num_face_nodes();
86 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
87 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
88
89 const long n_vertices = num_vertex_nodes;
90 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
91 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
92
93 in_primitive_to_primitive.setLinSpaced(num_in_primitives, 0, num_in_primitives - 1);
94
95 igl::Timer timer;
96
97 // ------------
98 // Map vertices
99 // ------------
100
101 if (in_ordered_vertices.rows() != n_vertices)
102 {
103 logger().warn("Node ordering disabled, in_ordered_vertices != n_vertices, {} != {}", in_ordered_vertices.rows(), n_vertices);
104 return false;
105 }
106
107 in_primitive_to_primitive.head(n_vertices) = in_ordered_vertices;
108
109 int in_offset = n_vertices;
110 int offset = mesh.n_vertices();
111
112 // ---------
113 // Map edges
114 // ---------
115
116 logger().trace("Building Mesh edges to IDs...");
117 timer.start();
118 const auto edges_to_ids = mesh.edges_to_ids();
119 if (in_ordered_edges.rows() != edges_to_ids.size())
120 {
121 logger().warn("Node ordering disabled, in_ordered_edges != edges_to_ids, {} != {}", in_ordered_edges.rows(), edges_to_ids.size());
122 return false;
123 }
124 timer.stop();
125 logger().trace("Done (took {}s)", timer.getElapsedTime());
126
127 logger().trace("Building in-edge to edge mapping...");
128 timer.start();
129 for (int in_ei = 0; in_ei < in_ordered_edges.rows(); in_ei++)
130 {
131 const std::pair<int, int> in_edge(
132 in_ordered_edges.row(in_ei).minCoeff(),
133 in_ordered_edges.row(in_ei).maxCoeff());
134 in_primitive_to_primitive[in_offset + in_ei] =
135 offset + edges_to_ids.at(in_edge); // offset edge ids
136 }
137 timer.stop();
138 logger().trace("Done (took {}s)", timer.getElapsedTime());
139
140 in_offset += mesh.n_edges();
141 offset += mesh.n_edges();
142
143 // ---------
144 // Map faces
145 // ---------
146
147 if (mesh.is_volume())
148 {
149 logger().trace("Building Mesh faces to IDs...");
150 timer.start();
151 const auto faces_to_ids = mesh.faces_to_ids();
152 if (in_ordered_faces.rows() != faces_to_ids.size())
153 {
154 logger().warn("Node ordering disabled, in_ordered_faces != faces_to_ids, {} != {}", in_ordered_faces.rows(), faces_to_ids.size());
155 return false;
156 }
157 timer.stop();
158 logger().trace("Done (took {}s)", timer.getElapsedTime());
159
160 logger().trace("Building in-face to face mapping...");
161 timer.start();
162 for (int in_fi = 0; in_fi < in_ordered_faces.rows(); in_fi++)
163 {
164 std::vector<int> in_face;
165 in_face.reserve(in_ordered_faces.cols());
166 for (int i = 0; i < in_ordered_faces.cols(); i++)
167 if (in_ordered_faces(in_fi, i) >= 0)
168 in_face.push_back(in_ordered_faces(in_fi, i));
169 std::sort(in_face.begin(), in_face.end());
170
171 in_primitive_to_primitive[in_offset + in_fi] =
172 offset + faces_to_ids.at(in_face); // offset face ids
173 }
174 timer.stop();
175 logger().trace("Done (took {}s)", timer.getElapsedTime());
176
177 in_offset += mesh.n_faces();
178 offset += mesh.n_faces();
179 }
180
181 return true;
182 }
183 } // namespace
184
185 QuadratureOrders VarForm::n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
186 {
188 const int n_b_samples_j = args["space"]["advanced"]["n_boundary_samples"];
189 const int boundary_order = std::max({discr_order, discr_orderq, gdiscr_order});
190 const int n_b_samples = std::max(n_b_samples_j, AssemblerUtils::quadrature_order("Mass", boundary_order, AssemblerUtils::BasisType::POLY, mesh_->dimension()));
191 return {{n_b_samples, n_b_samples}};
192 }
193
195 {
196 // FIXME check subclasses
197 stats.reset();
200 prepared_ = false;
201 problem = nullptr;
202 time_callback = nullptr;
203 mesh_ = nullptr;
204 checkpoint_reader_.reset();
206 }
207
209 const std::string &formulation,
210 const Units &units,
211 const json &args,
212 const std::string &out_path)
213 {
214 reset();
215
216 this->units = units;
217 this->args = args;
218
219 this->output_path = out_path;
221 }
222
223 void VarForm::set_mesh(std::unique_ptr<mesh::Mesh> mesh, const double loading_mesh_time)
224 {
226 mesh_ = std::move(mesh);
227 timings.loading_mesh_time = loading_mesh_time;
229 prepared_ = false;
230 if (!mesh_)
231 return;
232
234 }
235
236 void VarForm::set_geometry(mesh::LoadedGeometry geometry, const double loading_mesh_time)
237 {
238 set_obstacle(std::move(geometry.obstacle));
239 mesh_ = std::move(geometry.fem);
240 timings.loading_mesh_time = loading_mesh_time;
242 prepared_ = false;
243 if (mesh_)
245 }
246
248
250 VarForm &child,
251 const std::string &formulation,
252 const Units &units,
253 const json &args,
254 const std::string &out_path) const
255 {
256 child.init(formulation, units, args, out_path);
257 }
258
260 {
261 if (prepared_)
262 return;
263 if (!mesh_)
264 log_and_throw_error("Load the mesh first!");
265
266 mesh_->prepare_mesh();
268 build_basis(*mesh_, should_use_iso_parametric(*mesh_, args), args);
271 prepared_ = true;
272 }
273
275 mesh::Mesh &mesh,
276 const bool iso_parametric,
277 const Eigen::VectorXi &disc_orders,
278 const Eigen::VectorXi &disc_ordersq,
279 const std::string &basis_type,
280 const std::string &poly_basis_type,
281 const assembler::Assembler &space_assembler,
282 const int value_dim,
283 const int quadrature_order,
284 const int mass_quadrature_order,
285 const bool use_corner_quadrature,
286 const int n_harmonic_samples,
287 const int integral_constraints,
288 FESpace &space,
289 VarFormBoundaryState &boundary,
290 std::shared_ptr<GeometryMapping> geometry)
291 {
292 using namespace mesh;
293
294 const std::string space_assembler_name = space_assembler.name();
295 const bool build_geom_mapping = geometry == nullptr;
296
297 space.reset();
298 boundary.reset();
299
300 space.value_dim = value_dim;
301
302 space.bases = std::make_shared<std::vector<basis::ElementBases>>();
303 space.geometry = build_geom_mapping ? std::make_shared<GeometryMapping>() : std::move(geometry);
304 assert(space.geometry);
305
306 space.disc_orders = disc_orders;
307 space.disc_ordersq = disc_ordersq;
308
309 Eigen::MatrixXi geom_disc_orders;
310 if (build_geom_mapping && !iso_parametric)
311 {
312 if (mesh.orders().size() <= 0)
313 {
314 geom_disc_orders.resizeLike(space.disc_orders);
315 geom_disc_orders.setConstant(1);
316 }
317 else
318 geom_disc_orders = mesh.orders();
319
320 space.geometry->bases = std::make_shared<std::vector<basis::ElementBases>>();
321 space.geometry->disc_orders = geom_disc_orders;
322 }
323
324 Eigen::MatrixXi geom_disc_ordersq = geom_disc_orders;
325
326 logger().info("Building {} basis...", (build_geom_mapping ? (iso_parametric ? "isoparametric" : "not isoparametric") : "finite-element"));
327
328 igl::Timer timer;
329 timer.start();
330
331 const bool has_polys = mesh.has_poly();
332 std::map<int, basis::InterfaceData> poly_edge_to_data_geom;
333
334 const bool use_continuous_gbasis = true;
335
336 if (mesh.is_volume())
337 {
338 const Mesh3D &tmp_mesh = dynamic_cast<const Mesh3D &>(mesh);
339
340 if (basis_type == "Spline")
341 {
343 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
344 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
345 }
346 else
347 {
348 if (build_geom_mapping && !iso_parametric)
350 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
351 geom_disc_orders, geom_disc_ordersq, false, false, has_polys,
352 !use_continuous_gbasis, use_corner_quadrature,
353 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
354 space.geometry->mesh_nodes);
355
357 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
358 space.disc_orders, space.disc_ordersq,
359 basis_type == "Bernstein",
360 basis_type == "Serendipity",
361 has_polys, false, use_corner_quadrature,
362 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
363 }
364 }
365 else
366 {
367 const Mesh2D &tmp_mesh = dynamic_cast<const Mesh2D &>(mesh);
368
369 if (basis_type == "Spline")
370 {
372 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
373 *space.bases, boundary.local_boundary, space.poly_edge_to_data);
374 }
375 else
376 {
377 if (build_geom_mapping && !iso_parametric)
379 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
380 geom_disc_orders, false, false, has_polys,
381 !use_continuous_gbasis, use_corner_quadrature,
382 *space.geometry->bases, boundary.local_boundary, poly_edge_to_data_geom,
383 space.geometry->mesh_nodes);
384
386 tmp_mesh, space_assembler_name, quadrature_order, mass_quadrature_order,
387 space.disc_orders,
388 basis_type == "Bernstein",
389 basis_type == "Serendipity",
390 has_polys, false, use_corner_quadrature,
391 *space.bases, boundary.local_boundary, space.poly_edge_to_data, space.mesh_nodes);
392 }
393 }
394
395 const bool use_fe_space_as_geometry = build_geom_mapping ? iso_parametric : space.is_iso_parametric();
396 build_polygonal_basis(mesh, poly_basis_type, space_assembler,
397 use_fe_space_as_geometry,
398 quadrature_order,
399 mass_quadrature_order,
400 n_harmonic_samples,
401 integral_constraints,
402 space,
403 boundary);
404
405 if (build_geom_mapping)
406 {
407 if (iso_parametric)
408 space.geometry->init_from_fe_space(space);
409 else
410 {
411 assert(space.geometry->bases);
412 assert(space.geometry->n_bases > 0);
413 }
414 }
415
416 boundary.total_local_boundary.clear();
417 for (const auto &lb : boundary.local_boundary)
418 boundary.total_local_boundary.emplace_back(lb);
419
420 if (build_geom_mapping)
421 {
422 igl::Timer timer2;
423 logger().debug("Building node mapping...");
424 timer2.start();
425 build_node_mapping(mesh, basis_type, space, space.space_in_node_to_node, space.space_in_primitive_to_primitive);
426 timer2.stop();
427 logger().debug("Done (took {}s)", timer2.getElapsedTime());
428 }
429
430 logger().info("n_bases {}", space.n_bases);
431
432 timings.building_basis_time += timer.getElapsedTime();
433 logger().info(" took {}s", timings.building_basis_time);
434
435 logger().info("n bases: {}", space.n_bases);
436 }
437
439 const mesh::Mesh &mesh,
440 const std::string &poly_basis_type,
441 const assembler::Assembler &space_assembler,
442 bool iso_parametric,
443 const int quadrature_order,
444 const int mass_quadrature_order,
445 const int n_harmonic_samples,
446 const int integral_constraints,
447 varform::FESpace &space,
449 {
450 if (space.poly_edge_to_data.empty() && space.polys.empty())
451 {
453 return;
454 }
455
456 const std::string space_assembler_name = space_assembler.name();
457
458 igl::Timer timer;
459 timer.start();
460 logger().info("Computing polygonal basis...");
461
462 int new_bases = 0;
463 const int dim = space_assembler.is_tensor() ? mesh.dimension() : 1;
464 if (iso_parametric)
465 {
466 if (mesh.is_volume())
467 {
468 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
469 log_and_throw_error("Barycentric bases not supported in 3D");
470
471 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
472 assert(linear_assembler);
474 *linear_assembler,
475 n_harmonic_samples,
476 dynamic_cast<const mesh::Mesh3D &>(mesh),
477 space.n_bases,
478 quadrature_order,
479 mass_quadrature_order,
480 integral_constraints,
481 *space.bases,
482 *space.bases,
483 space.poly_edge_to_data,
484 space.polys_3d);
485 }
486 else
487 {
488 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
489 if (poly_basis_type == "MeanValue")
490 {
492 space_assembler.name(), dim, mesh_2d, space.n_bases,
493 quadrature_order,
494 mass_quadrature_order,
495 *space.bases, boundary.local_boundary, space.polys);
496 }
497 else if (poly_basis_type == "Wachspress")
498 {
500 space_assembler.name(), dim, mesh_2d, space.n_bases,
501 quadrature_order,
502 mass_quadrature_order,
503 *space.bases, boundary.local_boundary, space.polys);
504 }
505 else
506 {
507 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
508 assert(linear_assembler);
510 *linear_assembler,
511 n_harmonic_samples,
512 mesh_2d,
513 space.n_bases,
514 quadrature_order,
515 mass_quadrature_order,
516 integral_constraints,
517 *space.bases,
518 *space.bases,
519 space.poly_edge_to_data,
520 space.polys);
521 }
522 }
523 }
524 else
525 {
526 assert(space.geometry);
527 assert(space.geometry->bases);
528 if (mesh.is_volume())
529 {
530 if (poly_basis_type == "MeanValue" || poly_basis_type == "Wachspress")
531 log_and_throw_error("Barycentric bases not supported in 3D");
532
533 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
534 assert(linear_assembler);
536 *linear_assembler,
537 n_harmonic_samples,
538 dynamic_cast<const mesh::Mesh3D &>(mesh),
539 space.n_bases,
540 quadrature_order,
541 mass_quadrature_order,
542 integral_constraints,
543 *space.bases,
544 *space.geometry->bases,
545 space.poly_edge_to_data,
546 space.polys_3d);
547 }
548 else
549 {
550 const mesh::Mesh2D &mesh_2d = dynamic_cast<const mesh::Mesh2D &>(mesh);
551 if (poly_basis_type == "MeanValue")
552 {
554 space_assembler.name(), dim, mesh_2d, space.n_bases,
555 quadrature_order,
556 mass_quadrature_order,
557 *space.bases, boundary.local_boundary, space.polys);
558 }
559 else if (poly_basis_type == "Wachspress")
560 {
562 space_assembler.name(), dim, mesh_2d, space.n_bases,
563 quadrature_order,
564 mass_quadrature_order,
565 *space.bases, boundary.local_boundary, space.polys);
566 }
567 else
568 {
569 const auto *linear_assembler = dynamic_cast<const assembler::LinearAssembler *>(&space_assembler);
570 assert(linear_assembler);
572 *linear_assembler,
573 n_harmonic_samples,
574 mesh_2d,
575 space.n_bases,
576 quadrature_order,
577 mass_quadrature_order,
578 integral_constraints,
579 *space.bases,
580 *space.geometry->bases,
581 space.poly_edge_to_data,
582 space.polys);
583 }
584 }
585 }
586
587 timer.stop();
588 timings.computing_poly_basis_time = timer.getElapsedTime();
589 logger().info(" took {}s", timings.computing_poly_basis_time);
590
591 space.n_bases += new_bases;
592 }
593
595 Eigen::MatrixXd &sol,
596 const InitialConditionOverride *initial_condition_override,
597 const ForwardStepCallback &post_step)
598 {
599 prepare();
602 solve_problem(sol, initial_condition_override, post_step);
603 }
604
606 const mesh::Mesh &mesh,
607 const std::string &basis_type,
608 const FESpace &space,
609 Eigen::VectorXi &space_in_node_to_node,
610 Eigen::VectorXi &space_in_primitive_to_primitive) const
611 {
612 space_in_node_to_node.resize(0);
613 space_in_primitive_to_primitive.resize(0);
614
615 if (basis_type == "Spline")
616 {
617 logger().warn("Node ordering disabled, it dosent work for splines!");
618 return;
619 }
620
621 if (space.disc_orders.maxCoeff() >= 4 || space.disc_orders.maxCoeff() != space.disc_orders.minCoeff())
622 {
623 logger().warn("Node ordering disabled, it works only for p < 4 and uniform order!");
624 return;
625 }
626
627 if (!mesh.is_conforming())
628 {
629 logger().warn("Node ordering disabled, not supported for non-conforming meshes!");
630 return;
631 }
632
633 if (mesh.has_poly())
634 {
635 logger().warn("Node ordering disabled, not supported for polygonal meshes!");
636 return;
637 }
638
639 if (mesh.in_ordered_vertices().size() <= 0 || mesh.in_ordered_edges().size() <= 0 || (mesh.is_volume() && mesh.in_ordered_faces().size() <= 0))
640 {
641 logger().warn("Node ordering disabled, input vertices/edges/faces not computed!");
642 return;
643 }
644
645 if (!space.mesh_nodes)
646 {
647 logger().warn("Node ordering disabled, FE space does not expose mesh nodes!");
648 return;
649 }
650
651 igl::Timer timer;
652
653 logger().trace("Building in-primitive to primitive mapping...");
654 timer.start();
655 bool ok = build_in_primitive_to_primitive(
656 mesh, *space.mesh_nodes,
657 mesh.in_ordered_vertices(),
658 mesh.in_ordered_edges(),
659 mesh.in_ordered_faces(),
660 space_in_primitive_to_primitive);
661 timer.stop();
662 logger().trace("Done (took {}s)", timer.getElapsedTime());
663
664 if (!ok)
665 {
666 space_in_node_to_node.resize(0);
667 space_in_primitive_to_primitive.resize(0);
668 return;
669 }
670
671 const auto &tmp = space.mesh_nodes->in_ordered_vertices();
672 int max_tmp = -1;
673 for (auto v : tmp)
674 max_tmp = std::max(max_tmp, v);
675
676 space_in_node_to_node.resize(max_tmp + 1);
677 for (int i = 0; i < tmp.size(); ++i)
678 {
679 if (tmp[i] >= 0)
680 space_in_node_to_node[tmp[i]] = i;
681 }
682 }
683
685 const json &space_args,
686 const mesh::Mesh &mesh,
687 Eigen::VectorXi &disc_orders,
688 Eigen::VectorXi &disc_ordersq)
689 {
690 assign_discr_orders(space_args, -1, mesh, disc_orders, disc_ordersq);
691 }
692
694 const json &space_args,
695 const int fe_space_id,
696 const mesh::Mesh &mesh,
697 Eigen::VectorXi &disc_orders,
698 Eigen::VectorXi &disc_ordersq)
699 {
700 const auto assign_order = [&](const json &order_json, Eigen::VectorXi &orders) {
701 orders.resize(mesh.n_elements());
702
703 if (order_json.is_number_integer())
704 {
705 orders.setConstant(order_json);
706 }
707 else if (order_json.is_string())
708 {
709 const Eigen::MatrixXi tmp = resources_.read_int_matrix(order_json.get<std::string>());
710 assert(tmp.size() == orders.size());
711 assert(tmp.cols() == 1);
712 orders = tmp;
713 }
714 else if (order_json.is_array())
715 {
716 orders.setOnes();
717 std::map<int, int> body_orders;
718 bool has_matching_order = false;
719 for (const json &entry : order_json)
720 {
721 if (entry.contains("fe_space"))
722 {
723 const int entry_space_id = entry["fe_space"].get<int>();
724 if (entry_space_id >= 0 && fe_space_id < 0)
725 log_and_throw_error("FE-space-specific discretization orders require an FE space ID.");
726 if (entry_space_id >= 0 && entry_space_id != fe_space_id)
727 continue;
728 }
729
730 has_matching_order = true;
731 const int order = entry["order"];
732 if (!entry.contains("id") || (entry["id"].is_number_integer() && entry["id"].get<int>() < 0))
733 {
734 orders.setConstant(order);
735 continue;
736 }
737
738 for (const int id : utils::json_as_array<int>(entry["id"]))
739 {
740 body_orders[id] = order;
741 logger().trace("bid {}, discr {}", id, order);
742 }
743 }
744
745 if (!has_matching_order)
746 log_and_throw_error("Missing discretization order for FE space {}.", fe_space_id);
747
748 for (int e = 0; e < mesh.n_elements(); ++e)
749 {
750 const auto order = body_orders.find(mesh.get_body_id(e));
751 if (order != body_orders.end())
752 orders[e] = order->second;
753 }
754 }
755 else
756 {
757 log_and_throw_error("Discretization order must be a number, a path, or an array.");
758 }
759 };
760
761 assign_order(space_args["discr_order"], disc_orders);
762
763 const json &discr_orderq = space_args["discr_orderq"];
764 if (discr_orderq.is_number_integer() && discr_orderq.get<int>() < 0)
765 disc_ordersq = disc_orders;
766 else
767 assign_order(discr_orderq, disc_ordersq);
768
769 int max_prism_order = 0;
770 for (int e = 0; e < mesh.n_elements(); ++e)
771 {
772 if (mesh.is_prism(e))
773 max_prism_order = std::max({max_prism_order, disc_orders[e], disc_ordersq[e]});
774 }
775
776 if (max_prism_order > 0)
777 {
778 for (int e = 0; e < mesh.n_elements(); ++e)
779 {
780 if (mesh.is_simplex(e) || mesh.is_pyramid(e))
781 disc_orders[e] = max_prism_order;
782 }
783 }
784
785 logger().info(
786 "discretization orders: p=[{}, {}], q=[{}, {}]",
787 disc_orders.minCoeff(), disc_orders.maxCoeff(),
788 disc_ordersq.minCoeff(), disc_ordersq.maxCoeff());
789 }
790
791 void VarForm::save_json(const Eigen::MatrixXd &solution) const
792 {
793 const std::string out_path = resolve_output_path(args["output"]["json"]);
794 if (out_path.empty())
795 return;
796
797 std::ofstream file(out_path);
798 if (!file.is_open())
799 {
800 logger().error("Unable to save simulation JSON to {}", out_path);
801 return;
802 }
803 save_json(solution, file);
804 }
805
806 void VarForm::set_materials(assembler::Assembler &assembler, const int size) const
807 {
808 assert(mesh_ != nullptr);
809 assembler.set_size(size);
810
811 if (!utils::is_param_valid(args, "materials"))
812 return;
813
814 std::vector<int> body_ids(mesh_->n_elements());
815 for (int i = 0; i < mesh_->n_elements(); ++i)
816 body_ids[i] = mesh_->get_body_id(i);
817
818 assembler.set_materials(body_ids, args["materials"], units, resources_);
819 }
820
822 {
824 return;
825
826 const io::OutputSpace space = output_space();
827 if (space.mesh)
828 {
829 output_geometry_.init_sampler(*space.mesh, args["output"]["paraview"]["vismesh_rel_area"]);
830 output_geometry_.build_grid(*space.mesh, args["output"]["advanced"]["sol_on_grid"]);
831 }
833 }
834
843
845 {
846 return [this, &solution, fields = opts.fields](const io::OutputSample &sample) {
847 return output_fields(
848 sample, solution,
850 sample.requested_fields.empty() ? fields : sample.requested_fields});
851 };
852 }
853
855 {
856 if (!problem)
857 return 0;
858 if (problem->is_scalar())
859 return 1;
860 return mesh_ ? mesh_->dimension() : 0;
861 }
862
864 const double t0,
865 const double dt,
866 const int t,
867 const Eigen::MatrixXd &solution,
868 const time_integrator::ImplicitTimeIntegrator *time_integrator) const
869 {
870 const int global_t = output_file_index(t);
871 const std::string pattern = args["output"]["checkpoint"]["path"];
872 if (pattern.empty())
873 return;
874
875 json continuation = args;
876 continuation["time"]["t0"] = t0 + dt * t;
877 continuation["time"]["time_steps"] = std::max(0, args["time"]["time_steps"].get<int>() - t);
878 continuation["time"].erase("tend");
879 continuation["input"]["checkpoint"]["path"] = "";
880 io::CheckpointMetadata metadata;
881 metadata.formulation = name();
882 metadata.step = t;
883 metadata.time = t0 + dt * t;
884 metadata.dt = dt;
885 metadata.remaining_steps = continuation["time"]["time_steps"];
886 metadata.output_index = global_t;
887
888 io::CheckpointWriter writer(resolve_output_path(fmt::format(pattern, global_t)), continuation, metadata);
889 writer.write_mesh("/checkpoint/meshes/active", *mesh_);
890 serialize_checkpoint(writer, solution, metadata);
891 if (time_integrator)
892 time_integrator->serialize_checkpoint(writer, "/checkpoint/state/primary_integrator");
894 writer.finalize();
895 }
896
898 io::CheckpointWriter &writer,
899 const Eigen::MatrixXd &solution,
900 const io::CheckpointMetadata &) const
901 {
902 writer.write_matrix("/checkpoint/state/solution", solution);
903 }
904
905 void VarForm::deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution)
906 {
907 if (reader.metadata().formulation != name())
909 "Checkpoint formulation '{}' does not match '{}'.",
910 reader.metadata().formulation, name());
911 if (!reader.exists("/checkpoint/state/solution"))
912 log_and_throw_error("Checkpoint is missing formulation-owned solution state.");
913 solution = reader.read_matrix("/checkpoint/state/solution");
915 }
916
918 const std::shared_ptr<time_integrator::ImplicitTimeIntegrator> &integrator,
919 const std::string &group,
920 const double dt,
921 const std::string &ordering,
922 const FESpace &space,
923 const int block_size) const
924 {
926 return;
927 if (!integrator)
928 log_and_throw_error("Checkpoint requires integrator state at {}, but no integrator was constructed.", group);
929 const auto reorder = [this, &ordering, &space, block_size](Eigen::MatrixXd &value) {
932 checkpoint_reader_->get(), ordering, space,
933 block_size, 0, value);
934 };
935 integrator->deserialize_checkpoint(checkpoint_reader_->get(), group, dt, reorder);
936 }
937
939 io::CheckpointWriter &writer,
940 const std::string &name,
941 const FESpace &space) const
942 {
943 const Eigen::VectorXi ordering = checkpoint_ordering(space);
944 if (ordering.size() == 0)
945 return;
946 writer.write_int_vector(
947 "/checkpoint/state/orderings/" + name,
948 std::vector<int>(ordering.data(), ordering.data() + ordering.size()));
949 }
950
951 Eigen::VectorXi VarForm::checkpoint_ordering(const FESpace &space) const
952 {
953 if (!space.mesh_nodes || space.mesh_nodes->n_nodes() != space.n_bases)
954 return {};
955
956 std::vector<int> nodes(space.n_bases);
957 std::iota(nodes.begin(), nodes.end(), 0);
958 std::stable_sort(nodes.begin(), nodes.end(), [&space](const int lhs, const int rhs) {
959 const RowVectorNd left = space.mesh_nodes->node_position(lhs);
960 const RowVectorNd right = space.mesh_nodes->node_position(rhs);
961 for (int d = 0; d < left.size(); ++d)
962 {
963 if (left[d] < right[d])
964 return true;
965 if (left[d] > right[d])
966 return false;
967 }
968 return lhs < rhs;
969 });
970
971 return Eigen::Map<const Eigen::VectorXi>(nodes.data(), nodes.size());
972 }
973
974 bool VarForm::checkpoint_reorder_enabled() const
975 {
976 return args.contains("input") && args["input"].is_object()
977 && args["input"].contains("checkpoint") && args["input"]["checkpoint"].is_object()
978 && args["input"]["checkpoint"].value("reorder", false);
979 }
980
981 void VarForm::reorder_checkpoint_block(
982 const io::CheckpointReader &reader,
983 const std::string &name,
984 const FESpace &space,
985 const int block_size,
986 const int row_offset,
987 Eigen::MatrixXd &value) const
988 {
989 const Eigen::VectorXi current_ordering = checkpoint_ordering(space);
990 const std::string path = "/checkpoint/state/orderings/" + name;
991 if (!reader.exists(path))
992 {
993 if (checkpoint_reorder_enabled() || current_ordering.size() != 0)
994 log_and_throw_error("Checkpoint reordering requires {}.", path);
995 return;
996 }
997 if (block_size <= 0 || current_ordering.size() == 0)
998 log_and_throw_error("Checkpoint reordering is unavailable for the {} solution block.", name);
999
1000 const std::vector<int> stored_values = reader.read_int_vector(path);
1001 const Eigen::VectorXi stored_ordering = Eigen::Map<const Eigen::VectorXi>(
1002 stored_values.data(), stored_values.size());
1003 if (stored_ordering.size() != current_ordering.size())
1005 "Checkpoint {} ordering has {} nodes; the current space has {}.",
1006 name, stored_ordering.size(), current_ordering.size());
1007
1008 const auto validate_permutation = [&name](const Eigen::VectorXi &permutation, const std::string &source) {
1009 std::vector<bool> seen(permutation.size(), false);
1010 for (const int index : permutation)
1011 {
1012 if (index < 0 || index >= permutation.size() || seen[index])
1013 log_and_throw_error("{} {} ordering is not a permutation.", source, name);
1014 seen[index] = true;
1015 }
1016 };
1017 validate_permutation(stored_ordering, "Checkpoint");
1018 validate_permutation(current_ordering, "Current");
1019 if (!checkpoint_reorder_enabled())
1020 {
1021 if (stored_ordering != current_ordering)
1023 "Checkpoint {} ordering differs from the reconstructed space; enable input.checkpoint.reorder.",
1024 name);
1025 return;
1026 }
1027
1028 const int rows = current_ordering.size() * block_size;
1029 if (row_offset < 0 || row_offset + rows > value.rows())
1030 log_and_throw_error("Checkpoint {} solution block has invalid dimensions.", name);
1031 const Eigen::MatrixXd input_order = utils::unreorder_matrix(
1032 value.middleRows(row_offset, rows), stored_ordering, -1, block_size);
1033 value.middleRows(row_offset, rows) = utils::reorder_matrix(
1034 input_order, current_ordering, -1, block_size);
1035 }
1036
1037 void VarForm::validate_checkpoint_solution(
1038 const Eigen::MatrixXd &solution,
1039 const int expected_rows) const
1040 {
1041 if (solution.rows() != expected_rows || solution.cols() != 1)
1043 "Checkpoint solution for {} has dimensions {} x {}; expected {} x 1.",
1044 name(), solution.rows(), solution.cols(), expected_rows);
1045 }
1046
1047 void VarForm::save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
1048 {
1049 paraviewo::VTMWriter vtm(time);
1050 if (!save_timestep_to_vtm(time, t, dt, solution, vtm, ""))
1051 return;
1052
1053 const int global_t = output_file_index(t);
1054 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
1055 vtm.save(resolve_output_path(fmt::format(step_name + "{:d}.vtm", global_t)));
1056
1057 output_geometry_.save_pvd(
1058 resolve_output_path(args["output"]["paraview"]["file_name"]),
1059 [step_name](int i) { return fmt::format(step_name + "{:d}.vtm", i); },
1060 global_t, t0, dt, args["output"]["paraview"]["skip_frame"].get<int>());
1061 }
1062
1063 bool VarForm::save_timestep_to_vtm(
1064 const double time, const int t, const double dt,
1065 const Eigen::MatrixXd &solution, paraviewo::VTMWriter &vtm,
1066 const std::string &block_prefix) const
1067 {
1068 const io::OutputSpace space = output_space();
1069 if (!space.mesh || !args["output"]["advanced"]["save_time_sequence"])
1070 return false;
1071 const int global_t = output_file_index(t);
1072 if (global_t % args["output"]["paraview"]["skip_frame"].get<int>())
1073 return false;
1074
1075 ensure_output_sampler();
1076 logger().trace("Saving VTU...");
1077 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
1078 const auto opts = export_options(space);
1079 output_geometry_.save_vtu(
1080 resolve_output_path(fmt::format(step_name + "{:d}.vtu", global_t)),
1081 space, output_field_function(solution, opts), time, dt,
1082 opts, vtm, block_prefix);
1083 return true;
1084 }
1085
1086 void VarForm::save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
1087 {
1088 const io::OutputSpace space = output_space();
1089 if (!space.mesh || !args["output"]["advanced"]["save_solve_sequence_debug"].get<bool>())
1090 return;
1091
1092 const bool has_time = args.contains("time") && !args["time"].is_null();
1093 double dt = 1;
1094 if (has_time)
1095 dt = args["time"]["dt"];
1096
1097 ensure_output_sampler();
1098 const auto opts = export_options(space);
1099 output_geometry_.save_vtu(
1100 resolve_output_path(fmt::format("solve_{:d}.vtu", i)),
1101 space, output_field_function(solution, opts), t, dt,
1102 opts);
1103 }
1104
1105 void VarForm::notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
1106 {
1107 if (time_callback)
1108 time_callback(t, time_steps, t0 + dt * t, t0 + dt * time_steps);
1109 }
1110
1111 int VarForm::output_file_index(const int t) const
1112 {
1113 return t + output_index_offset_;
1114 }
1115
1116 std::string VarForm::resolve_output_path(const std::string &path) const
1117 {
1118 if (output_path.empty() || path.empty() || std::filesystem::path(path).is_absolute())
1119 {
1120 return path;
1121 }
1122 return std::filesystem::weakly_canonical(std::filesystem::path(output_path) / path).string();
1123 }
1124
1125 void VarForm::rebuild_node_positions(
1126 const std::vector<basis::ElementBases> &bases,
1127 const std::vector<int> &node_ids,
1128 std::vector<RowVectorNd> &positions)
1129 {
1130 positions.resize(node_ids.size());
1131 for (int n = 0; n < int(node_ids.size()); ++n)
1132 {
1133 const int node_id = node_ids[n];
1134 bool found = false;
1135 for (const auto &bs : bases)
1136 {
1137 for (const auto &b : bs.bases)
1138 {
1139 for (const auto &lg : b.global())
1140 {
1141 if (lg.index == node_id)
1142 {
1143 positions[n] = lg.node;
1144 found = true;
1145 break;
1146 }
1147 }
1148
1149 if (found)
1150 break;
1151 }
1152
1153 if (found)
1154 break;
1155 }
1156 assert(found);
1157 }
1158 }
1159} // namespace polyfem::varform
StiffnessMatrix source
void set_materials(const std::vector< int > &body_ids, const json &body_params, const Units &units, const io::ResourceIO &resources)
virtual bool is_tensor() const
virtual std::string name() const =0
virtual void set_size(const int size)
Definition Assembler.hpp:67
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)
Eigen::MatrixXd read_matrix(const std::string &path) const
bool exists(const std::string &path) const
std::vector< int > read_int_vector(const std::string &path) const
const CheckpointMetadata & metadata() const
void write_matrix(const std::string &path, const Eigen::MatrixXd &value)
void write_mesh(const std::string &group, const mesh::Mesh &mesh)
void embed_resources(const ResourceIO &resources)
void write_int_vector(const std::string &path, const std::vector< int > &value)
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
Definition OutData.cpp:2634
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
virtual Eigen::MatrixXi read_int_matrix(const std::string &path) const =0
std::unique_ptr< Mesh > fem
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:148
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:492
const Eigen::MatrixXi & in_ordered_edges() const
Order of the input edges.
Definition Mesh.hpp:653
virtual bool is_conforming() const =0
if the mesh is conforming
const Eigen::MatrixXi & orders() const
order of each element
Definition Mesh.hpp:268
bool is_simplex(const int el_id) const
checks if element is simplex
Definition Mesh.cpp:374
bool has_prism() const
checks if the mesh has prisms
Definition Mesh.hpp:584
bool is_prism(const int el_id) const
checks if element is a prism
Definition Mesh.cpp:379
bool is_linear() const
check if the mesh is linear
Definition Mesh.hpp:626
virtual bool is_volume() const =0
checks if mesh is volume
bool has_poly() const
checks if the mesh has polytopes
Definition Mesh.hpp:570
const Eigen::VectorXi & in_ordered_vertices() const
Order of the input vertices.
Definition Mesh.hpp:649
int dimension() const
utily for dimension
Definition Mesh.hpp:138
bool is_pyramid(const int el_id) const
checks if element is a pyramid
Definition Mesh.cpp:384
const Eigen::MatrixXi & in_ordered_faces() const
Order of the input edges.
Definition Mesh.hpp:657
Implicit time integrator of a second order ODE (equivently a system of coupled first order ODEs).
void serialize_checkpoint(io::CheckpointWriter &writer, const std::string &group) const
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
void reorder_checkpoint_block(const io::CheckpointReader &reader, const std::string &name, const FESpace &space, int block_size, int row_offset, Eigen::MatrixXd &value) const
Definition VarForm.cpp:981
int output_file_index(const int t) const
Definition VarForm.cpp:1111
void prepare()
Prepare all discretization and assembly data without running a solve.
Definition VarForm.cpp:259
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:261
virtual std::string name() const =0
Get the name of the variational formulation.
void save_step_state(const double t0, const double dt, const int t, const Eigen::MatrixXd &solution, const time_integrator::ImplicitTimeIntegrator *time_integrator) const
Definition VarForm.cpp:863
void write_checkpoint_ordering(io::CheckpointWriter &writer, const std::string &name, const FESpace &space) const
Definition VarForm.cpp:938
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:594
virtual io::OutputSpace output_space() const =0
Get the output space of the variational formulation, for output purposes.
virtual void set_obstacle(mesh::Obstacle &&obstacle)
Definition VarForm.cpp:247
virtual void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution)
Definition VarForm.cpp:905
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:273
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:684
Eigen::VectorXi checkpoint_ordering(const FESpace &space) const
Definition VarForm.cpp:951
io::OutStatsData stats
Definition VarForm.hpp:265
io::OutGeometryData::ExportOptions export_options(const io::OutputSpace &space) const
Definition VarForm.cpp:835
void set_geometry(mesh::LoadedGeometry geometry, double loading_mesh_time=0)
Definition VarForm.cpp:236
io::OutGeometryData output_geometry_
Definition VarForm.hpp:277
virtual void load_mesh(const mesh::Mesh &mesh, const json &args)=0
void init_child_varform(VarForm &child, const std::string &formulation, const Units &units, const json &args, const std::string &out_path) const
Definition VarForm.cpp:249
int problem_dimension() const
Get the problem dimension of the variational formulation, for output purposes.
Definition VarForm.cpp:854
virtual void assemble_mass_mat(const mesh::Mesh &mesh, const json &args)=0
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:844
void restore_checkpoint_integrator(const std::shared_ptr< time_integrator::ImplicitTimeIntegrator > &integrator, const std::string &group, double dt, const std::string &ordering, const FESpace &space, int block_size) const
Definition VarForm.cpp:917
virtual void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const
Definition VarForm.cpp:897
std::function< void(int, int, double, double)> time_callback
Definition VarForm.hpp:275
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:185
std::optional< std::reference_wrapper< const io::CheckpointReader > > checkpoint_reader_
Definition VarForm.hpp:271
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:438
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:274
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1116
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:223
void ensure_output_sampler() const
Definition VarForm.cpp:821
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:208
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:268
bool checkpoint_reorder_enabled() const
Definition VarForm.cpp:974
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.
const io::ResourceIO & resources_
Definition VarForm.hpp:283
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:605
void set_materials(assembler::Assembler &assembler, const int size) const
Definition VarForm.cpp:806
virtual void reset()=0
Definition VarForm.cpp:194
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.
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
Eigen::MatrixXd unreorder_matrix(const Eigen::MatrixXd &out, const Eigen::VectorXi &in_to_out, int in_blocks=-1, const int block_size=1)
Undo the reordering of row blocks in a matrix.
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:53
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