PolyFEM
Loading...
Searching...
No Matches
FluidVarForm.cpp
Go to the documentation of this file.
1#include "FluidVarForm.hpp"
2
28
29#include <polysolve/linear/FEMSolver.hpp>
30#include <polysolve/nonlinear/Solver.hpp>
31
32namespace polyfem::varform
33{
34 using namespace varform::internal;
35
38 const Eigen::MatrixXd &solution,
39 const io::CheckpointMetadata &metadata) const
40 {
41 VarForm::serialize_checkpoint(writer, solution, metadata);
42 write_checkpoint_ordering(writer, "primary", space_);
43 write_checkpoint_ordering(writer, "pressure", pressure_space_);
44 }
45
47 const io::CheckpointReader &reader,
48 Eigen::MatrixXd &solution)
49 {
50 VarForm::deserialize_checkpoint(reader, solution);
53 reader, "primary", space_,
54 mesh_->dimension(), 0, solution);
56 reader, "pressure", pressure_space_,
57 1, primary_ndof(), solution);
58 }
59
61 {
63 space_.reset();
73 rhs_assembler_ = nullptr;
74 mass_.resize(0, 0);
75 pure_mass_.resize(0, 0);
76 avg_mass_ = 0;
77 rhs_.resize(0, 0);
78 primary_assembler_ = nullptr;
79 mass_assembler_ = nullptr;
80 pure_mass_assembler_ = nullptr;
81 mixed_assembler_ = nullptr;
82 pressure_assembler_ = nullptr;
83 use_avg_pressure = true;
84 t0 = 0;
85 time_steps = 0;
86 dt = 0;
87 time_integrator = nullptr;
88 }
89
90 void FluidVarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
91 {
92 for (const std::string field : {"pressure_boundary", "pressure_cavity"})
93 {
94 const json::json_pointer path("/boundary_conditions/" + field);
95 if (args.contains(path) && !args.at(path).empty())
96 log_and_throw_error("{} does not support boundary_conditions.{}.", formulation, field);
97 }
98
99 VarForm::init(formulation, units, args, out_path);
100 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
101 const json &discr_orders = args.at("space").at("discr_order");
102
103 const json &materials = args.at("materials");
104 if (materials.is_array() && materials.empty())
105 log_and_throw_error("Fluid formulations require at least one material.");
106 const json &first_material = materials.is_array() ? materials.at(0) : materials;
107 velocity_space_id_ = first_material.at("velocity_space_id").get<int>();
108 pressure_space_id_ = first_material.at("pressure_space_id").get<int>();
110 log_and_throw_error("Fluid velocity and pressure must use different FE space IDs.");
111
112 if (discr_orders.is_array())
113 {
114 bool has_velocity_space = false;
115 bool has_pressure_space = false;
116 for (const json &entry : discr_orders)
117 {
118 const int fe_space_id = entry.at("fe_space").get<int>();
119 has_velocity_space |= fe_space_id == velocity_space_id_;
120 has_pressure_space |= fe_space_id == pressure_space_id_;
121 }
122 if (!has_velocity_space || !has_pressure_space)
123 log_and_throw_error("Fluid discretization-order lists must explicitly name the velocity and pressure FE spaces.");
124 }
125
126 if (materials.is_array())
127 {
128 for (const json &material : materials)
129 {
130 if (material.at("velocity_space_id").get<int>() != velocity_space_id_
131 || material.at("pressure_space_id").get<int>() != pressure_space_id_)
132 log_and_throw_error("All fluid materials must use the same velocity and pressure FE space IDs.");
133 }
134 }
135
137 mass_assembler_ = std::make_shared<assembler::Mass>();
138 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
141
142 if (!args.contains("preset_problem"))
143 {
144 problem = std::make_shared<assembler::GenericTensorProblem>("GenericTensor");
145 problem->clear();
146
147 json tmp;
148 tmp["is_time_dependent"] = is_time_dependent;
149 problem->set_parameters(tmp, resources_);
150
151 auto bc = args["boundary_conditions"];
152 problem->set_parameters(bc, resources_);
153 problem->set_parameters(args["initial_conditions"], resources_);
154 problem->set_parameters(args["output"], resources_);
155 }
156 else
157 {
158 if (args["preset_problem"]["type"] == "Kernel")
159 {
160 problem = std::make_shared<problem::KernelProblem>("Kernel", *primary_assembler_);
161 problem->clear();
162 }
163 else
164 {
165 problem = problem::ProblemFactory::factory().get_problem(args["preset_problem"]["type"]);
166 problem->clear();
167 }
168 problem->set_parameters(args["preset_problem"], resources_);
169 }
170
171 problem->set_units(*primary_assembler_, units);
172
173 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
174 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
175 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
176
177 assert(primary_assembler_->is_fluid());
178 }
179
180 void FluidVarForm::save_json(const Eigen::MatrixXd &solution, std::ostream &out) const
181 {
182 if (!mesh_)
183 {
184 logger().error("Load the mesh first!");
185 return;
186 }
187 if (solution.size() <= 0)
188 {
189 logger().error("Solve the problem first!");
190 return;
191 }
192
193 logger().info("Saving json...");
194 const int primary_size = primary_ndof();
195 const Eigen::MatrixXd stats_solution =
196 solution.rows() >= primary_size
197 ? solution.topRows(primary_size).eval()
198 : solution;
199
200 nlohmann::json j;
203 stats_solution, *mesh_, space_.disc_orders, space_.disc_ordersq, *problem,
205 args["output"]["advanced"]["sol_at_node"], j);
206 out << j.dump(4) << std::endl;
207 }
208
210 {
211 Eigen::VectorXi output_orders = space_.disc_orders;
212 if (mesh_ && space_.disc_ordersq.size() == space_.disc_orders.size())
213 {
214 for (int e = 0; e < output_orders.size(); ++e)
215 {
216 if (mesh_->is_prism(e))
217 output_orders(e) = std::max(space_.disc_orders(e), space_.disc_ordersq(e));
218 }
219 }
220
221 return {
222 mesh_.get(),
224 output_orders,
225 &space_.polys,
228 nullptr,
229 nullptr,
232 }
233
234 io::OutStatsData FluidVarForm::compute_errors(const Eigen::MatrixXd &solution)
235 {
236 if (!args["output"]["advanced"]["compute_error"])
237 return stats;
238
239 double tend = 0;
240 if (!args["time"].is_null())
241 tend = args["time"]["tend"];
242
243 Eigen::MatrixXd velocity, pressure;
244 split_solution(solution, velocity, pressure);
246 return stats;
247 }
248
249 void FluidVarForm::export_data(const Eigen::MatrixXd &solution) const
250 {
251 const io::OutputSpace space = output_space();
252 if (!space.mesh)
253 {
254 logger().error("Load the mesh first!");
255 return;
256 }
257 if (solution.size() <= 0)
258 {
259 logger().error("Solve the problem first!");
260 return;
261 }
262
264
265 const std::string vis_mesh_path = resolve_output_path(args["output"]["paraview"]["file_name"]);
266 const bool has_time = args.contains("time") && !args["time"].is_null();
267 double tend = has_time ? args["time"]["tend"].get<double>() : 1.0;
268 double dt = 1;
269 if (has_time)
270 dt = args["time"]["dt"];
271
272 const auto opts = export_options(space);
274 space,
275 output_field_function(solution, opts),
276 has_time,
277 tend, dt,
278 opts,
279 vis_mesh_path);
280
281 Eigen::MatrixXd velocity, pressure;
282 split_solution(solution, velocity, pressure);
283
284 const std::string solution_path = resolve_output_path(args["output"]["data"]["solution"]);
285 if (!solution_path.empty())
286 {
287 const int primary_rows = std::min<int>(velocity.rows(), primary_ndof());
288 const Eigen::MatrixXd primary_solution = velocity.topRows(primary_rows);
289 if (opts.reorder_output && space_.space_in_node_to_node.size() > 0)
290 {
291 const Eigen::MatrixXd nodal_solution = utils::unflatten(primary_solution, mesh_->dimension());
292 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
293 for (int input_node = 0; input_node < space_.space_in_node_to_node.size(); ++input_node)
294 {
295 const int node = space_.space_in_node_to_node(input_node);
296 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
297 reordered.row(input_node) = nodal_solution.row(node);
298 }
299 io::write_matrix(solution_path, reordered);
300 }
301 else
302 {
303 io::write_matrix(solution_path, primary_solution);
304 }
305 }
306
307 const std::string nodes_path = resolve_output_path(args["output"]["data"]["nodes"]);
308 if (!nodes_path.empty())
309 {
310 Eigen::MatrixXd nodes = Eigen::MatrixXd::Zero(space_.n_bases, mesh_->dimension());
311 for (const basis::ElementBases &element_bases : space_.basis_list())
312 for (const basis::Basis &basis : element_bases.bases)
313 for (const auto &global : basis.global())
314 nodes.row(global.index) = global.node;
315 io::write_matrix(nodes_path, nodes);
316 }
317
318 const std::string stress_path = resolve_output_path(args["output"]["data"]["stress_mat"]);
319 const std::string mises_path = resolve_output_path(args["output"]["data"]["mises"]);
320 if ((!stress_path.empty() || !mises_path.empty()) && primary_assembler_)
321 {
322 Eigen::MatrixXd stress;
323 Eigen::VectorXd mises;
327 stress, mises);
328 if (!stress_path.empty())
329 io::write_matrix(stress_path, stress);
330 if (!mises_path.empty())
331 io::write_matrix(mises_path, mises);
332 }
333 }
334
335 void FluidVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
336 {
339 pure_mass_assembler_->set_size(mass_assembler_->size());
340 problem->init(mesh);
341
343 mixed_assembler_->set_size(mesh.dimension());
346 }
347
349 {
350 return mesh_ ? space_.n_bases * mesh_->dimension() : 0;
351 }
352
354 {
356 }
357
359 {
361 }
362
364 {
365 json rhs_solver_params = args["solver"]["linear"];
366 if (!rhs_solver_params.contains("Pardiso"))
367 rhs_solver_params["Pardiso"] = {};
368 rhs_solver_params["Pardiso"]["mtype"] = -2;
369
370 rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
371 *primary_assembler_, *mesh_, nullptr, // no obstacle for the rhs assembler
375 args["space"]["advanced"]["bc_method"],
376 rhs_solver_params,
378 }
379
380 void FluidVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
381 {
382 assert(problem);
383 assert(primary_assembler_);
384 assert(mass_assembler_);
385 assert(pure_mass_assembler_);
386
387 Eigen::VectorXi space_disc_orders, space_disc_ordersq;
388 assign_discr_orders(args["space"], velocity_space_id_, mesh, space_disc_orders, space_disc_ordersq);
389
390 if (args["space"]["use_p_ref"])
391 {
393 mesh,
394 args["space"]["advanced"]["B"],
395 args["space"]["advanced"]["h1_formula"],
396 args["space"]["discr_order"],
397 args["space"]["advanced"]["discr_order_max"],
398 stats,
399 space_disc_orders);
400
401 logger().info("min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
402 }
403
405 mesh,
406 iso_parametric,
407 space_disc_orders,
408 space_disc_ordersq,
409 args["space"]["basis_type"],
410 args["space"]["poly_basis_type"],
412 mesh.dimension(),
413 args["space"]["advanced"]["quadrature_order"],
414 args["space"]["advanced"]["mass_quadrature_order"],
415 args["space"]["advanced"]["use_corner_quadrature"],
416 args["space"]["advanced"]["n_harmonic_samples"],
417 args["space"]["advanced"]["integral_constraints"],
418 space_,
419 boundary_);
420
421 problem->update_nodes(space_.space_in_node_to_node);
423
424 const auto &current_bases = space_.geometry_basis_list();
425 if (args["space"]["advanced"]["count_flipped_els"])
426 stats.count_flipped_elements(mesh, current_bases);
427
428 const int n_samples = 10;
429 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
430
431 logger().info("flipped elements {}", stats.n_flipped);
432 logger().info("h: {}", stats.mesh_size);
433
434 if (space_.disc_orders.maxCoeff() != space_.disc_orders.minCoeff())
435 log_and_throw_error("p refinement not supported in mixed formulation!");
436 if (!space_.poly_edge_to_data.empty())
437 log_and_throw_error("Polygonal bases are not supported in mixed formulations!");
438
439 if (space_.n_bases <= args["solver"]["advanced"]["cache_size"])
440 {
441 igl::Timer cache_timer;
442 cache_timer.start();
443 logger().info("Building cache...");
444 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
445 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
446 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
447 logger().info(" took {}s", cache_timer.getElapsedTime());
448 }
449 else
450 {
454 }
455
456 const auto &all_boundary = boundary_.total_local_boundary;
457 const int prev_bases = space_.n_bases;
458 const int prev_b_size = int(all_boundary.size());
459 const bool use_corner_quadrature = args["space"]["advanced"]["use_corner_quadrature"];
460 const int quadrature_order = args["space"]["advanced"]["quadrature_order"].get<int>();
461 const int mass_quadrature_order = args["space"]["advanced"]["mass_quadrature_order"].get<int>();
462 Eigen::VectorXi pressure_disc_orders, pressure_disc_ordersq;
463 assign_discr_orders(args["space"], pressure_space_id_, mesh, pressure_disc_orders, pressure_disc_ordersq);
464 // to avoid serendipity
465 const std::string pressure_basis_type = args["space"]["basis_type"].get<std::string>() == "Bernstein" ? "Bernstein" : "Lagrange";
467 mesh,
468 /*iso_parametric=*/true,
469 pressure_disc_orders,
470 pressure_disc_ordersq,
471 pressure_basis_type,
472 args["space"]["poly_basis_type"],
474 /*value_dim=*/1,
475 quadrature_order,
476 mass_quadrature_order,
477 use_corner_quadrature,
478 args["space"]["advanced"]["n_harmonic_samples"],
479 args["space"]["advanced"]["integral_constraints"],
483
484 assert(space_.basis_list().size() == pressure_space_.basis_list().size());
485 for (int i = 0; i < int(pressure_space_.basis_list().size()); ++i)
486 {
488 space_.basis_list()[i].compute_quadrature(b_quad);
489 (*pressure_space_.bases)[i].set_quadrature([b_quad](quadrature::Quadrature &quad) { quad = b_quad; });
490 }
491
493 problem->setup_bc(
497 problem->setup_bc(
500
501 problem->setup_nodal_bc(
503 problem->setup_nodal_bc(
505
506 for (const int n_id : boundary_.dirichlet_nodes)
507 {
508 const int tag = mesh.get_node_id(n_id);
509 for (int d = 0; d < mesh.dimension(); ++d)
510 if (problem->is_nodal_dimension_dirichlet(n_id, tag, d, velocity_space_id_))
511 boundary_.boundary_nodes.push_back(n_id * mesh.dimension() + d);
512 }
513
516
517 const bool has_neumann = !boundary_.local_neumann_boundary.empty() || int(boundary_.local_boundary.size()) < prev_b_size;
518 use_avg_pressure = !has_neumann;
519
520 for (int i = prev_bases; i < space_.n_bases; ++i)
521 for (int d = 0; d < mesh.dimension(); ++d)
522 boundary_.boundary_nodes.push_back(i * mesh.dimension() + d);
523
525
526 if (space_.n_bases <= args["solver"]["advanced"]["cache_size"])
528 else
530
532
533 logger().info("n pressure bases: {}", pressure_space_.n_bases);
534 }
535
537 {
539
540 igl::Timer timer;
541 json p_params = {};
542 p_params["formulation"] = primary_assembler_->name();
543 {
544 RowVectorNd min, max, delta;
545 mesh.bounding_box(min, max);
546 delta = (max - min) / 2. + min;
547 if (mesh.is_volume())
548 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
549 else
550 p_params["bbox_center"] = {delta(0), delta(1)};
551 }
552 problem->set_parameters(p_params, resources_);
553
554 rhs_.resize(0, 0);
555
556 timer.start();
557 logger().info("Assigning rhs...");
558
559 assert(rhs_assembler_ != nullptr);
560 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
561 rhs_ *= -1;
562
563 timings.assigning_rhs_time = timer.getElapsedTime();
564 logger().info(" took {}s", timings.assigning_rhs_time);
565
566 const int prev_size = rhs_.rows();
567 rhs_.conservativeResize(prev_size + pressure_block_size(), rhs_.cols());
568 rhs_.bottomRows(pressure_block_size()).setZero();
569 }
570
571 void FluidVarForm::assemble_mass_mat(const mesh::Mesh &mesh, const json &args)
572 {
573 if (!problem->is_time_dependent())
574 {
575 avg_mass_ = 1;
577 if (!primary_assembler_->is_linear())
579 return;
580 }
581
582 mass_.resize(0, 0);
583 igl::Timer timer;
584 timer.start();
585 logger().info("Assembling mass mat...");
586
587 StiffnessMatrix velocity_mass;
589 if (!primary_assembler_->is_linear())
591
592 std::vector<Eigen::Triplet<double>> blocks;
593 blocks.reserve(velocity_mass.nonZeros());
594 for (int k = 0; k < velocity_mass.outerSize(); ++k)
595 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
596 blocks.emplace_back(it.row(), it.col(), it.value());
597
598 mass_.resize(primary_ndof(), primary_ndof());
599 mass_.setFromTriplets(blocks.begin(), blocks.end());
600 mass_.makeCompressed();
601
602 avg_mass_ = 0;
603 for (int k = 0; k < velocity_mass.outerSize(); ++k)
604 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
605 {
606 assert(it.col() == k);
607 avg_mass_ += it.value();
608 }
609 avg_mass_ /= std::max(1, int(velocity_mass.rows()));
610 logger().info("average mass {}", avg_mass_);
611
612 if (args["solver"]["advanced"]["lump_mass_matrix"])
614
615 timer.stop();
616 timings.assembling_mass_mat_time = timer.getElapsedTime();
617 logger().info(" took {}s", timings.assembling_mass_mat_time);
618
619 stats.nn_zero = mass_.nonZeros();
620 stats.num_dofs = mass_.rows();
621 stats.mat_size = (long long)mass_.rows() * (long long)mass_.cols();
622 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
623 }
624
625 void FluidVarForm::prepare_initial_solution(Eigen::MatrixXd &sol) const
626 {
627 if (sol.size() <= 0)
628 {
629 assert(rhs_assembler_ != nullptr);
630 if (problem->is_time_dependent())
631 rhs_assembler_->initial_solution(sol);
632 else
633 {
634 sol.resize(rhs_.size(), 1);
635 sol.setZero();
636 }
637 }
638 if (sol.cols() > 1)
639 sol.conservativeResize(Eigen::NoChange, 1);
640 sol.conservativeResize(stacked_ndof(), sol.cols());
641 sol.bottomRows(pressure_block_size()).setZero();
642 }
643
644 void FluidVarForm::split_solution(const Eigen::MatrixXd &stacked, Eigen::MatrixXd &primary, Eigen::MatrixXd &pressure) const
645 {
646 const int cols = std::max(1, int(stacked.cols()));
647 primary.setZero(primary_ndof(), cols);
648 pressure.setZero(pressure_space_.n_bases, cols);
649
650 const int primary_rows = std::min(primary_ndof(), int(stacked.rows()));
651 if (primary_rows > 0)
652 primary.topRows(primary_rows) = stacked.topRows(primary_rows);
653
654 if (stacked.rows() > primary_ndof())
655 {
656 const int pressure_rows = std::min(pressure_space_.n_bases, int(stacked.rows()) - primary_ndof());
657 if (pressure_rows > 0)
658 pressure.topRows(pressure_rows) = stacked.middleRows(primary_ndof(), pressure_rows);
659 }
660 }
661
663 {
664 igl::Timer timer;
665 timer.start();
666 logger().info("Assembling stiffness mat...");
667
668 StiffnessMatrix velocity_stiffness, mixed_stiffness, pressure_stiffness;
669 primary_assembler_->assemble(mesh_->is_volume(), space_.n_bases, space_.basis_list(), space_.geometry_basis_list(), ass_vals_cache_, 0, velocity_stiffness);
672
675 velocity_stiffness, mixed_stiffness, pressure_stiffness, stiffness);
676
677 timer.stop();
678 timings.assembling_stiffness_mat_time = timer.getElapsedTime();
679 logger().info(" took {}s", timings.assembling_stiffness_mat_time);
680
681 stats.nn_zero = stiffness.nonZeros();
682 stats.num_dofs = stiffness.rows();
683 stats.mat_size = (long long)stiffness.rows() * (long long)stiffness.cols();
684 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
685
686 write_matrix_market(args, stiffness);
687 }
688
690 const std::unique_ptr<polysolve::linear::Solver> &solver,
692 Eigen::VectorXd &b,
693 const bool compute_spectrum,
694 Eigen::MatrixXd &sol)
695 {
696 Eigen::VectorXd x;
697 stats.spectrum = dirichlet_solve(
698 *solver,
699 A,
700 b,
702 x,
703 primary_ndof(),
704 args["output"]["data"]["stiffness_mat"],
705 compute_spectrum,
706 /*is_fluid=*/true,
708
709 sol = x;
710 solver->get_info(stats.solver_info);
711
712 const double error = (A * x - b).norm();
713 if (error > 1e-4)
714 logger().error("Solver error: {}", error);
715 else
716 logger().debug("Solver error: {}", error);
717 }
718
719 std::vector<io::OutputField> FluidVarForm::output_fields(
720 const io::OutputSample &sample,
721 const Eigen::MatrixXd &solution,
722 const io::OutputFieldOptions &options) const
723 {
724 std::vector<io::OutputField> fields;
725 if (!mesh_ || !problem || solution.size() <= 0)
726 return fields;
727
728 Eigen::MatrixXd velocity, pressure;
729 split_solution(solution, velocity, pressure);
730
731 const int field_dim = mesh_->dimension();
732 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
733 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
734 const bool export_solution_gradient =
735 !options.fields.empty() && options.export_field("solution_gradient");
736 const bool export_pressure_gradient =
737 !options.fields.empty() && options.export_field("pressure_gradient");
738
739 const auto resize_to_output_rows = [&](Eigen::MatrixXd &values) {
740 if (output_rows <= values.rows())
741 return;
742
743 const int previous_rows = values.rows();
744 values.conservativeResize(output_rows, values.cols());
745 values.bottomRows(output_rows - previous_rows).setZero();
746 };
747
748 const auto sample_vector_field = [&](const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients = nullptr) -> bool {
749 if (dof_values.size() <= 0 || field_dim <= 0)
750 return false;
751
752 if (has_element_samples)
753 {
754 values.resize(sample.local_points.rows(), field_dim);
755 if (gradients)
756 gradients->resize(sample.local_points.rows(), field_dim * mesh_->dimension());
757 for (int i = 0; i < sample.local_points.rows(); ++i)
758 {
759 const int element_id = sample.element_ids(i);
760 if (element_id < 0)
761 {
762 values.row(i).setZero();
763 if (gradients)
764 gradients->row(i).setZero();
765 continue;
766 }
767
768 Eigen::MatrixXd local_sol, local_grad;
771 element_id, sample.local_points.row(i), dof_values, local_sol, local_grad);
772
773 for (int d = 0; d < field_dim; ++d)
774 values(i, d) = local_sol(d);
775 if (gradients)
776 gradients->row(i) = local_grad;
777 }
778
779 resize_to_output_rows(values);
780 if (gradients)
781 resize_to_output_rows(*gradients);
782 return true;
783 }
784
785 if (sample.node_ids.size() > 0)
786 {
787 values.resize(sample.node_ids.size(), field_dim);
788 for (int i = 0; i < sample.node_ids.size(); ++i)
789 {
790 const int node_id = sample.node_ids(i);
791 for (int d = 0; d < field_dim; ++d)
792 {
793 const int dof = node_id * field_dim + d;
794 if (dof < 0 || dof >= dof_values.rows())
795 return false;
796 values(i, d) = dof_values(dof);
797 }
798 }
799 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
800 }
801
802 return false;
803 };
804
805 Eigen::MatrixXd velocity_values, velocity_gradients;
806 const bool sampled_velocity = sample_vector_field(
807 velocity, velocity_values,
808 export_solution_gradient ? &velocity_gradients : nullptr);
809 if (sampled_velocity && options.export_field("velocity"))
810 fields.push_back({"velocity", velocity_values, io::OutputField::Association::Point});
811 // if (sampled_velocity && options.export_field("solution"))
812 // fields.push_back({"solution", velocity_values, io::OutputField::Association::Point});
813 if (sampled_velocity && export_solution_gradient)
814 fields.push_back({"solution_gradient", velocity_gradients, io::OutputField::Association::Point});
815
816 if (mesh_ && (options.export_field("pressure") || export_pressure_gradient))
817 {
818 Eigen::MatrixXd values, gradients;
820 *mesh_, pressure_space_.basis_list(), space_.geometry_basis_list(), sample, pressure, values,
821 export_pressure_gradient ? &gradients : nullptr))
822 {
823 if (options.export_field("pressure"))
824 fields.push_back({"pressure", values, io::OutputField::Association::Point});
825 if (export_pressure_gradient)
826 fields.push_back({"pressure_gradient", gradients, io::OutputField::Association::Point});
827 }
828 }
829
830 const auto &paraview_options = args["output"]["paraview"]["options"];
831 if (paraview_options["material"] && has_element_samples)
832 {
833 const auto &params = primary_assembler_->parameters();
834 std::map<std::string, Eigen::MatrixXd> param_values;
835 for (const auto &[p, _] : params)
836 param_values[p].setZero(output_rows, 1);
837
838 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
839 const auto &density = mass_assembler_->density();
840 for (int i = 0; i < sample.local_points.rows(); ++i)
841 {
842 const int element_id = sample.element_ids(i);
843 if (element_id < 0)
844 continue;
845
846 for (const auto &[p, func] : params)
847 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
848 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
849 }
850
851 for (const auto &[name, values] : param_values)
852 if (options.export_field(name))
853 fields.push_back({name, values, io::OutputField::Association::Point});
854 if (options.export_field("rho"))
855 fields.push_back({"rho", rhos, io::OutputField::Association::Point});
856 }
857
858 if (paraview_options["body_ids"] && options.export_field("body_ids") && has_element_samples)
859 {
860 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
861 for (int i = 0; i < sample.element_ids.size(); ++i)
862 {
863 const int element_id = sample.element_ids(i);
864 if (element_id >= 0)
865 ids(i) = mesh_->get_body_id(element_id);
866 }
867 fields.push_back({"body_ids", ids, io::OutputField::Association::Point});
868 }
869
870 return fields;
871 }
872
873 void StokesVarForm::solve_static_linear(Eigen::MatrixXd &sol)
874 {
875 auto solver = polysolve::linear::Solver::create(args["solver"]["linear"], logger());
876 logger().info("{}...", solver->name());
877
878 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
879 const QuadratureOrders boundary_samples = n_boundary_samples(space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gdiscr_order);
880 rhs_assembler_->set_bc(
881 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples,
882 boundary_.local_neumann_boundary, rhs_);
883
885 build_stiffness_mat(A);
886 Eigen::VectorXd b = rhs_;
887 solve_linear_system(solver, A, b, args["output"]["advanced"]["spectrum"], sol);
888 }
889
890 void StokesVarForm::solve_transient_linear(Eigen::MatrixXd &sol)
891 {
892 auto solver = polysolve::linear::Solver::create(args["solver"]["linear"], logger());
893 logger().info("{}...", solver->name());
894
895 Eigen::MatrixXd velocity, pressure;
896 split_solution(sol, velocity, pressure);
897
899 args["time"]["integrator"]);
900 bdf->init(
901 velocity,
902 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
903 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
904 dt);
905 time_integrator = bdf;
906 restore_checkpoint_integrator(
907 time_integrator, "/checkpoint/state/primary_integrator", dt,
908 "primary", space_, mesh_->dimension());
909
910 save_timestep(t0, 0, t0, dt, sol);
911
912 Eigen::MatrixXd current_rhs = rhs_;
913 StiffnessMatrix stiffness, expanded_mass;
914 build_stiffness_mat(stiffness);
915 expand_primary_matrix(stacked_ndof(), mass_, expanded_mass);
916 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
917 const QuadratureOrders boundary_samples = n_boundary_samples(space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gdiscr_order);
918
919 for (int t = 1; t <= time_steps; ++t)
920 {
921 const double time = t0 + t * dt;
922 rhs_assembler_->compute_energy_grad(
923 boundary_.local_boundary, boundary_.boundary_nodes, mass_assembler_->density(), boundary_samples, boundary_.local_neumann_boundary, rhs_, time,
924 current_rhs);
925 rhs_assembler_->set_bc(
926 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples, boundary_.local_neumann_boundary, current_rhs, velocity, time);
927
928 if (current_rhs.rows() != stacked_ndof())
929 {
930 const int old_rows = current_rhs.rows();
931 current_rhs.conservativeResize(stacked_ndof(), current_rhs.cols());
932 if (stacked_ndof() > old_rows)
933 current_rhs.bottomRows(stacked_ndof() - old_rows).setZero();
934 }
935 current_rhs.bottomRows(pressure_block_size()).setZero();
936
937 StiffnessMatrix A = expanded_mass / bdf->beta_dt() + stiffness;
938 Eigen::VectorXd b = Eigen::VectorXd::Zero(stacked_ndof());
939 b.head(primary_ndof()) = (mass_ * bdf->weighted_sum_x_prevs()) / bdf->beta_dt();
940 for (int i : boundary_.boundary_nodes)
941 b[i] = 0;
942 b += current_rhs;
943
944 solve_linear_system(solver, A, b, args["output"]["advanced"]["spectrum"].get<bool>() && t == time_steps, sol);
945 split_solution(sol, velocity, pressure);
946 bdf->update_quantities(velocity.col(0));
947
948 save_timestep(time, t, t0, dt, sol);
949 save_step_state(t0, dt, t, sol, time_integrator.get());
950 logger().info("{}/{} t={}", t, time_steps, time);
951 notify_time_step(t, time_steps, t0, dt);
952 }
953 }
954
955 void StokesVarForm::solve_problem(
956 Eigen::MatrixXd &sol,
957 const InitialConditionOverride *initial_condition_override,
958 const ForwardStepCallback &post_step)
959 {
960 assert(!initial_condition_override && "Stokes does not support initial-condition overrides");
961 assert(!post_step && "Stokes does not support post-step callbacks");
962
963 stats.spectrum.setZero();
964 igl::Timer timer;
965 timer.start();
966 logger().info("Solving {}", primary_assembler_->name());
967
968 prepare_initial_solution(sol);
969 if (problem->is_time_dependent())
970 solve_transient_linear(sol);
971 else
972 {
973 time_integrator = nullptr;
974 solve_static_linear(sol);
975 }
976
977 timer.stop();
978 timings.solving_time = timer.getElapsedTime();
979 logger().info(" took {}s", timings.solving_time);
980 }
981
982 namespace
983 {
984 json residual_solver_params(const json &input)
985 {
986 json params = input;
987 params["solver"] = "Newton";
988 params["line_search"]["method"] = "ResidualBacktracking";
989
990 if (!params.contains("Newton") || params["Newton"].is_null())
991 params["Newton"] = json::object();
992 params["Newton"]["force_psd_projection"] = false;
993 params["Newton"]["use_psd_projection"] = true;
994 return params;
995 }
996
997 StiffnessMatrix append_identity_mass(const StiffnessMatrix &velocity_mass, const int extra_size)
998 {
999 std::vector<Eigen::Triplet<double>> entries;
1000 entries.reserve(velocity_mass.nonZeros() + extra_size);
1001 for (int k = 0; k < velocity_mass.outerSize(); ++k)
1002 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
1003 entries.emplace_back(it.row(), it.col(), it.value());
1004 for (int i = 0; i < extra_size; ++i)
1005 entries.emplace_back(velocity_mass.rows() + i, velocity_mass.cols() + i, 1.0);
1006
1007 StiffnessMatrix result(
1008 velocity_mass.rows() + extra_size,
1009 velocity_mass.cols() + extra_size);
1010 result.setFromTriplets(entries.begin(), entries.end());
1011 result.makeCompressed();
1012 return result;
1013 }
1014 } // namespace
1015
1016 void NavierStokesVarForm::build_forms(Eigen::MatrixXd &sol, const double t)
1017 {
1018 assert(sol.rows() == stacked_ndof());
1019 assert(sol.cols() == 1);
1020
1021 auto stokes_assembler = std::make_shared<assembler::StokesVelocity>();
1022 set_materials(*stokes_assembler, mesh_->dimension());
1023 auto *navier_stokes_assembler = dynamic_cast<assembler::NavierStokesVelocity *>(primary_assembler_.get());
1024 assert(navier_stokes_assembler);
1025
1026 stacked_form_ = std::make_shared<solver::StackedForm>();
1027 const auto velocity_block = stacked_form_->add_block(primary_ndof());
1028 const auto pressure_block = stacked_form_->add_block(pressure_space_.n_bases);
1029
1030 navier_stokes_form_ = std::make_shared<solver::NavierStokesForm>(
1031 space_.n_bases, space_.basis_list(), space_.geometry_basis_list(),
1032 stokes_assembler, *navier_stokes_assembler, ass_vals_cache_, t, mesh_->is_volume());
1033 stacked_form_->add(velocity_block, navier_stokes_form_);
1034
1035 velocity_rhs_ = rhs_.topRows(primary_ndof());
1036 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
1037 const QuadratureOrders boundary_samples = n_boundary_samples(space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gdiscr_order);
1038 body_form_ = std::make_shared<solver::BodyForm>(
1039 primary_ndof(), /*n_pressure_bases=*/0,
1040 boundary_.boundary_nodes, boundary_.local_boundary,
1041 boundary_.local_neumann_boundary, boundary_samples,
1042 velocity_rhs_, *rhs_assembler_, mass_assembler_->density(),
1043 /*is_formulation_mixed=*/false, problem->is_time_dependent());
1044 body_form_->update_quantities(t, sol.topRows(primary_ndof()));
1045 stacked_form_->add(velocity_block, body_form_);
1046
1047 mixed_form_ = std::make_shared<solver::MixedLinearForm>(
1048 space_.n_bases, pressure_space_.n_bases,
1049 space_.basis_list(), pressure_space_.basis_list(), space_.geometry_basis_list(),
1050 *mixed_assembler_, ass_vals_cache_, pressure_ass_vals_cache_, t, mesh_->is_volume());
1051 stacked_form_->add(velocity_block, pressure_block, mixed_form_);
1052
1053 if (use_avg_pressure)
1054 {
1055 const auto average_block = stacked_form_->add_block(1);
1056 average_pressure_form_ = std::make_shared<solver::AveragePressureForm>(pressure_space_.n_bases);
1057 stacked_form_->add(pressure_block, average_block, average_pressure_form_);
1058 }
1059 else
1060 average_pressure_form_ = nullptr;
1061
1062 inertia_form_ = nullptr;
1063 if (problem->is_time_dependent())
1064 {
1065 assert(time_integrator);
1066 inertia_form_ = std::make_shared<solver::InertiaForm>(mass_, *time_integrator);
1067 if (!boundary_.boundary_nodes.empty())
1068 {
1069 inertia_form_->set_x_tilde_updater(
1070 [this, boundary_samples](
1071 const double time,
1072 const Eigen::VectorXd &,
1073 Eigen::VectorXd &target) {
1074 Eigen::MatrixXd projected_target = target;
1075 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
1076 rhs_assembler_->set_bc(
1077 boundary_.local_boundary, boundary_.boundary_nodes,
1078 boundary_samples, empty_neumann_boundary,
1079 projected_target, Eigen::MatrixXd(), time);
1080 assert(projected_target.cols() == 1);
1081 target = projected_target.col(0);
1082 });
1083 }
1084 stacked_form_->add(velocity_block, inertia_form_);
1085 update_transient_form_weights();
1086 }
1087
1088 forms_ = {stacked_form_};
1089 for (const auto &form : forms_)
1090 form->set_output_dir(output_path);
1091
1092 al_forms_.clear();
1093 if (!boundary_.boundary_nodes.empty())
1094 {
1095 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
1096 const auto velocity_al_block = stacked_al->add_block(primary_ndof());
1097 stacked_al->add_block(pressure_space_.n_bases);
1098 if (use_avg_pressure)
1099 stacked_al->add_block(1);
1100 stacked_al->add(
1101 velocity_al_block,
1102 std::make_shared<solver::BCLagrangianForm>(
1103 primary_ndof(), boundary_.boundary_nodes,
1104 boundary_.local_boundary, boundary_.local_neumann_boundary,
1105 boundary_samples, pure_mass_, *rhs_assembler_,
1106 /*obstacle_ndof=*/0, problem->is_time_dependent(), t));
1107 al_forms_.push_back(stacked_al);
1108 }
1109
1110 const StiffnessMatrix residual_mass = append_identity_mass(pure_mass_, pressure_block_size());
1111 nl_problem_ = std::make_shared<solver::NLProblem>(
1112 stacked_ndof(), t, forms_, al_forms_,
1113 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
1114 units.characteristic_length(), /*characteristic_force=*/1,
1115 residual_mass, mesh_->dimension(), /*is_residual=*/true);
1116 nl_problem_->init(sol);
1117 nl_problem_->update_quantities(t, sol);
1118 stats.solver_info = json::array();
1119 }
1120
1121 void NavierStokesVarForm::update_transient_form_weights()
1122 {
1123 assert(time_integrator);
1124 const double scaling = time_integrator->acceleration_scaling();
1125 navier_stokes_form_->set_weight(scaling);
1126 body_form_->set_weight(scaling);
1127 mixed_form_->set_row_weights(scaling, scaling);
1128 if (average_pressure_form_)
1129 average_pressure_form_->set_weight(scaling);
1130 }
1131
1132 void NavierStokesVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &sol)
1133 {
1134 assert(nl_problem_);
1135 const json nonlinear_params = residual_solver_params(args["solver"]["nonlinear"]);
1136 const json al_nonlinear_params = residual_solver_params(args["solver"]["augmented_lagrangian"]["nonlinear"]);
1137 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
1138 polysolve::nonlinear::Solver::create(
1139 nonlinear_params, args["solver"]["linear"], units.characteristic_length(), logger());
1140
1141 solver::ALSolver al_solver(
1142 al_forms_, args["solver"]["augmented_lagrangian"]["initial_weight"],
1143 args["solver"]["augmented_lagrangian"]["scaling"],
1144 args["solver"]["augmented_lagrangian"]["max_weight"],
1145 args["solver"]["augmented_lagrangian"]["eta"],
1146 [](const Eigen::VectorXd &) {});
1147
1148 al_solver.post_subsolve = [&](const double al_weight) {
1149 stats.solver_info.push_back(
1150 {{"type", al_weight > 0 ? "al" : "rc"},
1151 {"t", step},
1152 {"info", nl_solver->info()}});
1153 if (al_weight > 0)
1154 stats.solver_info.back()["weight"] = al_weight;
1155 save_subsolve(stats.solver_info.size(), step, sol);
1156 };
1157
1158 if (!al_forms_.empty())
1159 al_solver.solve_al(
1160 *nl_problem_, sol, al_nonlinear_params,
1161 args["solver"]["linear"], units.characteristic_length(), nl_solver);
1162 al_solver.solve_reduced(
1163 *nl_problem_, sol, nonlinear_params,
1164 args["solver"]["linear"], units.characteristic_length(), nl_solver);
1165 }
1166
1167 void NavierStokesVarForm::solve_problem(
1168 Eigen::MatrixXd &sol,
1169 const InitialConditionOverride *initial_condition_override,
1170 const ForwardStepCallback &post_step)
1171 {
1172 assert(!initial_condition_override && "Navier-Stokes does not support initial-condition overrides");
1173 assert(!post_step && "Navier-Stokes does not support post-step callbacks");
1174
1175 stats.spectrum.setZero();
1176 igl::Timer timer;
1177 timer.start();
1178 logger().info("Solving {}", primary_assembler_->name());
1179
1180 prepare_initial_solution(sol);
1181 if (!problem->is_time_dependent())
1182 {
1183 time_integrator = nullptr;
1184 build_forms(sol, 1.0);
1185 solve_nonlinear_step(0, sol);
1186 }
1187 else
1188 {
1189 Eigen::MatrixXd velocity, pressure;
1190 split_solution(sol, velocity, pressure);
1192 args["time"]["integrator"],
1194 bdf->init(
1195 velocity,
1196 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()),
1197 Eigen::MatrixXd::Zero(velocity.rows(), velocity.cols()), dt);
1198 time_integrator = bdf;
1199 restore_checkpoint_integrator(
1200 time_integrator, "/checkpoint/state/primary_integrator", dt,
1201 "primary", space_, mesh_->dimension());
1202
1203 build_forms(sol, t0 + dt);
1204 save_timestep(t0, 0, t0, dt, sol);
1205 for (int step = 1; step <= time_steps; ++step)
1206 {
1207 const double time = t0 + step * dt;
1208 logger().info("{}/{} steps, dt={}s t={}s", step, time_steps, dt, time);
1209 solve_nonlinear_step(step, sol);
1210
1211 split_solution(sol, velocity, pressure);
1212 time_integrator->update_quantities(velocity.col(0));
1213 update_transient_form_weights();
1214 nl_problem_->update_quantities(t0 + (step + 1) * dt, sol);
1215
1216 save_timestep(time, step, t0, dt, sol);
1217 save_step_state(t0, dt, step, sol, time_integrator.get());
1218 notify_time_step(step, time_steps, t0, dt);
1219 }
1220 }
1221
1222 timer.stop();
1223 timings.solving_time = timer.getElapsedTime();
1224 logger().info(" took {}s", timings.solving_time);
1225 }
1226} // namespace polyfem::varform
std::vector< Eigen::Triplet< double > > entries
int x
std::array< Matrix< int, 3, 3 >, 3 > space_
static std::shared_ptr< MixedAssembler > make_mixed_assembler(const std::string &formulation)
static std::string other_assembler_name(const std::string &formulation)
static void merge_mixed_matrices(const int n_bases, const int n_pressure_bases, const int problem_dim, const bool add_average, const StiffnessMatrix &velocity_stiffness, const StiffnessMatrix &mixed_stiffness, const StiffnessMatrix &pressure_stiffness, StiffnessMatrix &stiffness)
utility to merge 3 blocks of mixed matrices, A=velocity_stiffness, B=mixed_stiffness,...
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
void init(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const bool is_mass=false)
computes the basis evaluation and geometric mapping for each of the given ElementBases in bases initi...
void init_empty(const bool is_mass=false)
initialize an empty cache.
Represents one basis function and its gradient.
Definition Basis.hpp:44
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
static void interpolate_at_local_vals(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const int el_index, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, Eigen::MatrixXd &result_grad)
interpolate solution and gradient at element (calls interpolate_at_local_vals with sol)
static void compute_stress_at_quadrature_points(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Assembler &assembler, const Eigen::MatrixXd &fun, const double t, Eigen::MatrixXd &result, Eigen::VectorXd &von_mises)
compute von mises stress at quadrature points for the function fun, also compute the interpolated fun...
void export_data(const OutputSpace &space, const OutputFieldFunction &output_fields, const bool is_time_dependent, const double tend_in, const double dt, const ExportOptions &opts, const std::string &vis_mesh_path) const
exports everytihng, txt, vtu, etc
Definition OutData.cpp:2103
double assembling_stiffness_mat_time
time to assembly
double assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
all stats from polyfem
int n_flipped
number of flipped elements, compute only when using count_flipped_els (false by default)
json solver_info
information of the solver, eg num iteration, time, errors, etc the informations varies depending on t...
Eigen::Vector4d spectrum
spectrum of the stiffness matrix, enable only if POLYSOLVE_WITH_SPECTRA is ON (off by default)
void count_flipped_elements(const polyfem::mesh::Mesh &mesh, const std::vector< polyfem::basis::ElementBases > &gbases)
counts the number of flipped elements
Definition OutData.cpp:2834
void compute_errors(const int n_bases, const std::vector< polyfem::basis::ElementBases > &bases, const std::vector< polyfem::basis::ElementBases > &gbases, const polyfem::mesh::Mesh &mesh, const assembler::Problem &problem, const double tend, const Eigen::MatrixXd &sol)
compute errors
Definition OutData.cpp:2879
void compute_mesh_size(const polyfem::mesh::Mesh &mesh_in, const std::vector< polyfem::basis::ElementBases > &bases_in, const int n_samples, const bool use_curved_mesh_size)
computes the mesh size, it samples every edges n_samples times uses curved_mesh_size (false by defaul...
Definition OutData.cpp:2751
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
double mesh_size
max edge lenght
void save_json(const nlohmann::json &args, const int n_bases, const int n_pressure_bases, const Eigen::MatrixXd &sol, const mesh::Mesh &mesh, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Problem &problem, const OutRuntimeData &runtime, const std::string &formulation, const bool isoparametric, const int sol_at_node_id, nlohmann::json &j) const
saves the output statistic to a json object
Definition OutData.cpp:3122
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
virtual bool is_volume() const =0
checks if mesh is volume
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
Definition Mesh.cpp:346
int dimension() const
utily for dimension
Definition Mesh.hpp:138
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:475
static const ProblemFactory & factory()
std::shared_ptr< assembler::Problem > get_problem(const std::string &problem) const
static void p_refine(const mesh::Mesh &mesh, const double B, const bool h1_formula, const int base_p, const int discr_order_max, io::OutStatsData &stats, Eigen::VectorXi &disc_orders)
compute a priori prefinement
Definition APriori.cpp:242
std::function< void(const double)> post_subsolve
Definition ALSolver.hpp:53
static std::shared_ptr< BDF > construct_bdf_integrator(const json &params, DynamicOrder dynamic_order=DynamicOrder::Second)
Construct a BDF integrator for algorithms using BDF-specific operations.
const std::vector< basis::ElementBases > & geometry_basis_list() const
Definition FESpace.hpp:115
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
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
const std::vector< basis::ElementBases > & basis_list() const
Definition FESpace.hpp:109
bool is_iso_parametric() const
Definition FESpace.hpp:104
void assemble_rhs(const mesh::Mesh &mesh) override
assembler::AssemblyValsCache pressure_ass_vals_cache_
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args) override
void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const override
std::shared_ptr< assembler::HRZMass > pure_mass_assembler_
io::OutputSpace output_space() const override
Get the output space of the variational formulation, for output purposes.
VarFormBoundaryState pressure_boundary_
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
assembler::AssemblyValsCache ass_vals_cache_
void split_solution(const Eigen::MatrixXd &stacked, Eigen::MatrixXd &primary, Eigen::MatrixXd &pressure) const
std::shared_ptr< assembler::Assembler > primary_assembler_
void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution) override
assembler::AssemblyValsCache pure_mass_ass_vals_cache_
std::shared_ptr< assembler::RhsAssembler > rhs_assembler_
void export_data(const Eigen::MatrixXd &solution) const override
void prepare_initial_solution(Eigen::MatrixXd &sol) const
VarFormBoundaryState boundary_
void save_json(const Eigen::MatrixXd &solution, std::ostream &out) const override
Save the solution to a JSON file, for output purposes.
io::OutStatsData compute_errors(const Eigen::MatrixXd &solution) override
Get the error statistics of the variational formulation, for output purposes.
std::shared_ptr< assembler::Mass > mass_assembler_
void solve_linear_system(const std::unique_ptr< polysolve::linear::Solver > &solver, StiffnessMatrix &A, Eigen::VectorXd &b, const bool compute_spectrum, Eigen::MatrixXd &sol)
void build_stiffness_mat(StiffnessMatrix &stiffness)
std::vector< io::OutputField > output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options) const override
Get the output fields of the variational formulation, for output purposes.
std::shared_ptr< assembler::Assembler > pressure_assembler_
assembler::AssemblyValsCache mass_ass_vals_cache_
std::shared_ptr< assembler::MixedAssembler > mixed_assembler_
void load_mesh(const mesh::Mesh &mesh, const json &args) override
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
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1125
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:263
virtual std::string name() const =0
Get the name of the variational formulation.
void write_checkpoint_ordering(io::CheckpointWriter &writer, const std::string &name, const FESpace &space) const
Definition VarForm.cpp:938
virtual void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution)
Definition VarForm.cpp:905
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:275
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:684
io::OutStatsData stats
Definition VarForm.hpp:267
io::OutGeometryData::ExportOptions export_options(const io::OutputSpace &space) const
Definition VarForm.cpp:835
io::OutGeometryData output_geometry_
Definition VarForm.hpp:279
io::OutputFieldFunction output_field_function(const Eigen::MatrixXd &solution, const io::OutGeometryData::ExportOptions &opts) const
Definition VarForm.cpp:844
virtual void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const
Definition VarForm.cpp:897
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 ensure_output_sampler() const
Definition VarForm.cpp:821
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
void validate_checkpoint_solution(const Eigen::MatrixXd &solution, int expected_rows) const
Definition VarForm.cpp:1037
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:270
const io::ResourceIO & resources_
Definition VarForm.hpp:285
void set_materials(assembler::Assembler &assembler, const int size) const
Definition VarForm.cpp:806
virtual void reset()=0
Definition VarForm.cpp:194
str func
Definition p_bases.py:417
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
Definition MatrixIO.cpp:43
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
bool write_matrix_market(const json &args, const StiffnessMatrix &stiffness)
bool sample_scalar_field(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &field_bases, const std::vector< basis::ElementBases > &gbases, const io::OutputSample &sample, const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients=nullptr)
void expand_primary_matrix(const int full_size, const StiffnessMatrix &primary, StiffnessMatrix &expanded)
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:55
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
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24
bool export_field(const std::string &field) const
Definition OutData.cpp:56
std::vector< std::string > fields
Eigen::VectorXi node_ids
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points
const mesh::Mesh * mesh
std::vector< RowVectorNd > neumann_nodes_position
Definition FESpace.hpp:163
std::vector< mesh::LocalBoundary > local_boundary
Definition FESpace.hpp:155
std::vector< mesh::LocalBoundary > local_neumann_boundary
Definition FESpace.hpp:156
std::vector< mesh::LocalBoundary > total_local_boundary
Definition FESpace.hpp:154
std::vector< RowVectorNd > dirichlet_nodes_position
Definition FESpace.hpp:161