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