PolyFEM
Loading...
Searching...
No Matches
ScalarVarForm.cpp
Go to the documentation of this file.
1#include "ScalarVarForm.hpp"
2
5
8
11
17
18#include <unsupported/Eigen/SparseExtra>
19
20#include <polysolve/linear/FEMSolver.hpp>
21
22#include <algorithm>
23
24namespace polyfem::varform
25{
26 namespace
27 {
28 void write_matrix_market(const json &args, const StiffnessMatrix &stiffness)
29 {
30 const std::string full_mat_path = args["output"]["data"]["full_mat"];
31 if (!full_mat_path.empty())
32 Eigen::saveMarket(stiffness, full_mat_path);
33 }
34 } // namespace
35
37 {
39 space_.reset();
44 rhs_assembler_ = nullptr;
45 mass_.resize(0, 0);
46 pure_mass_.resize(0, 0);
47 avg_mass_ = 0;
48 rhs_.resize(0, 0);
49 primary_assembler_ = nullptr;
50 mass_assembler_ = nullptr;
51 pure_mass_assembler_ = nullptr;
52 t0 = 0;
53 time_steps = 0;
54 dt = 0;
55 time_integrator = nullptr;
56 }
57
58 void ScalarVarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
59 {
60 VarForm::init(formulation, units, args, out_path);
61 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
62
64 assert(primary_assembler_->name() == formulation);
65 assert(primary_assembler_->is_linear());
66 assert(!primary_assembler_->is_tensor());
67 mass_assembler_ = std::make_shared<assembler::Mass>();
68 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
69
70 if (!args.contains("preset_problem"))
71 {
72 problem = std::make_shared<assembler::GenericScalarProblem>("GenericScalar");
73 problem->clear();
74
75 json tmp;
76 tmp["is_time_dependent"] = is_time_dependent;
77 problem->set_parameters(tmp, root_path);
78
79 auto bc = args["boundary_conditions"];
80 bc["root_path"] = root_path;
81 problem->set_parameters(bc, root_path);
82 problem->set_parameters(args["initial_conditions"], root_path);
83 problem->set_parameters(args["output"], root_path);
84 }
85 else
86 {
87 problem = problem::ProblemFactory::factory().get_problem(args["preset_problem"]["type"]);
88 problem->clear();
89 problem->set_parameters(args["preset_problem"], root_path);
90 }
91
92 problem->set_units(*primary_assembler_, units);
93
94 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
95 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
96 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
97 }
98
99 void ScalarVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
100 {
103 pure_mass_assembler_->set_size(mass_assembler_->size());
104
105 problem->init(mesh);
106 }
107
108 void ScalarVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
109 {
110 assert(problem);
111 assert(primary_assembler_);
112 assert(mass_assembler_);
113 assert(pure_mass_assembler_);
114
115 Eigen::VectorXi space_disc_orders;
116 assign_discr_orders(args["space"]["discr_order"], mesh, space_disc_orders);
117
118 if (args["space"]["use_p_ref"])
119 {
121 mesh,
122 args["space"]["advanced"]["B"],
123 args["space"]["advanced"]["h1_formula"],
124 args["space"]["discr_order"],
125 args["space"]["advanced"]["discr_order_max"],
126 stats,
127 space_disc_orders);
128
129 logger().info("min p: {} max p: {}", space_disc_orders.minCoeff(), space_disc_orders.maxCoeff());
130 }
131
133 mesh,
134 iso_parametric,
135 space_disc_orders,
136 args["space"]["basis_type"],
137 args["space"]["poly_basis_type"],
139 /*value_dim=*/1,
140 args["space"]["advanced"]["quadrature_order"],
141 args["space"]["advanced"]["mass_quadrature_order"],
142 args["space"]["advanced"]["use_corner_quadrature"],
143 args["space"]["advanced"]["n_harmonic_samples"],
144 args["space"]["advanced"]["integral_constraints"],
145 space_,
146 boundary_);
147
149
150 problem->update_nodes(space_.space_in_node_to_node);
152
153 problem->setup_bc(
154 mesh,
156 /*fe_space_id=*/-1,
161 /*value_dim=*/1);
162 std::vector<int> unused_neumann_boundary_nodes;
163 problem->setup_bc(
164 mesh,
166 /*fe_space_id=*/-1,
170 unused_neumann_boundary_nodes,
171 /*value_dim=*/1);
172
173 problem->setup_nodal_bc(
174 mesh,
176 /*fe_space_id=*/-1,
179 problem->setup_nodal_bc(
180 mesh,
182 /*fe_space_id=*/-1,
185
186 for (const int n_id : boundary_.dirichlet_nodes)
187 {
188 const int tag = mesh.get_node_id(n_id);
189 if (problem->is_nodal_dimension_dirichlet(n_id, tag, 0))
190 boundary_.boundary_nodes.push_back(n_id);
191 }
192
194
197
198 const auto &current_bases = space_.geometry_basis_list();
199 if (args["space"]["advanced"]["count_flipped_els"])
200 stats.count_flipped_elements(mesh, current_bases);
201
202 const int n_samples = 10;
203 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
204
205 logger().info("flipped elements {}", stats.n_flipped);
206 logger().info("h: {}", stats.mesh_size);
207
208 if (space_.n_bases <= args["solver"]["advanced"]["cache_size"])
209 {
210 igl::Timer timer;
211 timer.start();
212 logger().info("Building cache...");
213 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
214 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
215 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
216 logger().info(" took {}s", timer.getElapsedTime());
217 }
218 else
219 {
223 }
224 }
225
227 {
228 json rhs_solver_params = args["solver"]["linear"];
229 if (!rhs_solver_params.contains("Pardiso"))
230 rhs_solver_params["Pardiso"] = {};
231 rhs_solver_params["Pardiso"]["mtype"] = -2;
232
233 rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
234 *primary_assembler_, *mesh_, nullptr,
238 args["space"]["advanced"]["bc_method"],
239 rhs_solver_params,
240 /*fe_space_id=*/-1);
241 }
242
244 {
245 igl::Timer timer;
246 json p_params = {};
247 p_params["formulation"] = primary_assembler_->name();
248 p_params["root_path"] = root_path;
249 {
250 RowVectorNd min, max, delta;
251 mesh.bounding_box(min, max);
252 delta = (max - min) / 2. + min;
253 if (mesh.is_volume())
254 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
255 else
256 p_params["bbox_center"] = {delta(0), delta(1)};
257 }
258 problem->set_parameters(p_params, root_path);
259
260 rhs_.resize(0, 0);
261
262 timer.start();
263 logger().info("Assigning rhs...");
264
266 assert(rhs_assembler_ != nullptr);
267 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
268 rhs_ *= -1;
269
270 timings.assigning_rhs_time = timer.getElapsedTime();
271 logger().info(" took {}s", timings.assigning_rhs_time);
272 }
273
274 void ScalarVarForm::assemble_mass_mat(const mesh::Mesh &mesh, const json &args)
275 {
276 if (!problem->is_time_dependent())
277 {
278 avg_mass_ = 1;
280 return;
281 }
282
283 mass_.resize(0, 0);
284
285 igl::Timer timer;
286 timer.start();
287 logger().info("Assembling mass mat...");
288
290
291 assert(mass_.size() > 0);
292
293 avg_mass_ = 0;
294 for (int k = 0; k < mass_.outerSize(); ++k)
295 {
296 for (StiffnessMatrix::InnerIterator it(mass_, k); it; ++it)
297 {
298 assert(it.col() == k);
299 avg_mass_ += it.value();
300 }
301 }
302
303 avg_mass_ /= mass_.rows();
304 logger().info("average mass {}", avg_mass_);
305
306 if (args["solver"]["advanced"]["lump_mass_matrix"])
308
309 timer.stop();
310 timings.assembling_mass_mat_time = timer.getElapsedTime();
311 logger().info(" took {}s", timings.assembling_mass_mat_time);
312
313 stats.nn_zero = mass_.nonZeros();
314 stats.num_dofs = mass_.rows();
315 stats.mat_size = (long long)mass_.rows() * (long long)mass_.cols();
316 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
317 }
318
319 void ScalarVarForm::prepare_initial_solution(Eigen::MatrixXd &solution) const
320 {
321 assert(rhs_assembler_ != nullptr);
322
323 const bool was_solution_loaded = read_initial_x_from_file(
324 resolve_input_path(args["input"]["data"]["state"]), "u",
325 args["input"]["data"]["reorder"], space_.space_in_node_to_node,
326 /*dim=*/1, solution);
327
328 if (!was_solution_loaded)
329 {
330 if (problem->is_time_dependent())
331 rhs_assembler_->initial_solution(solution);
332 else
333 {
334 solution.resize(rhs_.size(), 1);
335 solution.setZero();
336 }
337 }
338 }
339
340 void ScalarVarForm::save_json(const Eigen::MatrixXd &solution, std::ostream &out) const
341 {
342 if (!mesh_)
343 {
344 logger().error("Load the mesh first!");
345 return;
346 }
347 if (solution.size() <= 0)
348 {
349 logger().error("Solve the problem first!");
350 return;
351 }
352
353 logger().info("Saving json...");
354 const int primary_size = space_.n_bases;
355 const Eigen::MatrixXd stats_solution =
356 solution.rows() >= primary_size
357 ? solution.topRows(primary_size).eval()
358 : solution;
359
360 nlohmann::json j;
362 args, space_.n_bases, /*n_auxiliary_bases=*/0,
363 stats_solution, *mesh_, space_.disc_orders, space_.disc_ordersq, *problem,
365 args["output"]["advanced"]["sol_at_node"], j);
366 out << j.dump(4) << std::endl;
367 }
368
370 {
371 Eigen::VectorXi output_orders = space_.disc_orders;
372 if (mesh_ && space_.disc_ordersq.size() == space_.disc_orders.size())
373 {
374 for (int e = 0; e < output_orders.size(); ++e)
375 {
376 if (mesh_->is_prism(e))
377 output_orders(e) = std::max(space_.disc_orders(e), space_.disc_ordersq(e));
378 }
379 }
380
381 return {
382 mesh_.get(),
384 output_orders,
385 &space_.polys,
388 nullptr,
389 nullptr,
392 }
393
394 io::OutStatsData ScalarVarForm::compute_errors(const Eigen::MatrixXd &solution)
395 {
396 if (!args["output"]["advanced"]["compute_error"])
397 return stats;
398
399 double tend = 0;
400 if (!args["time"].is_null())
401 tend = args["time"]["tend"];
402
404 return stats;
405 }
406
407 void ScalarVarForm::export_data(const Eigen::MatrixXd &solution) const
408 {
409 const io::OutputSpace space = output_space();
410 if (!space.mesh)
411 {
412 logger().error("Load the mesh first!");
413 return;
414 }
415 if (solution.size() <= 0)
416 {
417 logger().error("Solve the problem first!");
418 return;
419 }
420
422
423 const std::string vis_mesh_path = resolve_output_path(args["output"]["paraview"]["file_name"]);
424 const bool has_time = args.contains("time") && !args["time"].is_null();
425 double tend = has_time ? args["time"]["tend"].get<double>() : 1.0;
426 double dt = 1;
427 if (has_time)
428 dt = args["time"]["dt"];
429
430 const auto opts = export_options(space);
432 space,
433 output_field_function(solution, opts),
434 has_time,
435 tend, dt,
436 opts,
437 vis_mesh_path);
438
439 const std::string solution_path = resolve_output_path(args["output"]["data"]["solution"]);
440 if (!solution_path.empty())
441 {
442 const int primary_ndof = std::min<int>(solution.rows(), space_.n_bases);
443 const Eigen::MatrixXd primary_solution = solution.topRows(primary_ndof);
444 if (opts.reorder_output && space_.space_in_node_to_node.size() > 0)
445 {
446 const Eigen::MatrixXd nodal_solution = utils::unflatten(primary_solution, 1);
447 Eigen::MatrixXd reordered = Eigen::MatrixXd::Zero(nodal_solution.rows(), nodal_solution.cols());
448 for (int input_node = 0; input_node < space_.space_in_node_to_node.size(); ++input_node)
449 {
450 const int node = space_.space_in_node_to_node(input_node);
451 if (node >= 0 && node < nodal_solution.rows() && input_node < reordered.rows())
452 reordered.row(input_node) = nodal_solution.row(node);
453 }
454 io::write_matrix(solution_path, reordered);
455 }
456 else
457 {
458 io::write_matrix(solution_path, primary_solution);
459 }
460 }
461
462 const std::string nodes_path = resolve_output_path(args["output"]["data"]["nodes"]);
463 if (!nodes_path.empty())
464 {
465 Eigen::MatrixXd nodes = Eigen::MatrixXd::Zero(space_.n_bases, mesh_->dimension());
466 for (const basis::ElementBases &element_bases : space_.basis_list())
467 for (const basis::Basis &basis : element_bases.bases)
468 for (const auto &global : basis.global())
469 nodes.row(global.index) = global.node;
470 io::write_matrix(nodes_path, nodes);
471 }
472
473 const std::string stress_path = resolve_output_path(args["output"]["data"]["stress_mat"]);
474 const std::string mises_path = resolve_output_path(args["output"]["data"]["mises"]);
475 if ((!stress_path.empty() || !mises_path.empty()) && primary_assembler_)
476 {
477 Eigen::MatrixXd stress;
478 Eigen::VectorXd mises;
482 stress, mises);
483 if (!stress_path.empty())
484 io::write_matrix(stress_path, stress);
485 if (!mises_path.empty())
486 io::write_matrix(mises_path, mises);
487 }
488 }
489
490 std::vector<io::OutputField> ScalarVarForm::output_fields(
491 const io::OutputSample &sample,
492 const Eigen::MatrixXd &solution,
493 const io::OutputFieldOptions &options) const
494 {
495 std::vector<io::OutputField> fields;
496 if (!mesh_ || !problem || solution.size() <= 0)
497 return fields;
498
499 assert(problem->is_scalar());
500 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
501 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
502 const int primary_ndof = std::min<int>(solution.rows(), space_.n_bases);
503 const Eigen::MatrixXd primary_solution = solution.topRows(primary_ndof);
504
505 const auto sample_dof_field = [&](const Eigen::MatrixXd &dof_values, Eigen::MatrixXd &values, Eigen::MatrixXd *gradients = nullptr) -> bool {
506 if (dof_values.size() <= 0)
507 return false;
508
509 if (has_element_samples)
510 {
511 values.resize(sample.local_points.rows(), 1);
512 if (gradients)
513 gradients->resize(sample.local_points.rows(), mesh_->dimension());
514 for (int i = 0; i < sample.local_points.rows(); ++i)
515 {
516 const int element_id = sample.element_ids(i);
517 if (element_id < 0)
518 {
519 values(i) = 0;
520 if (gradients)
521 gradients->row(i).setZero();
522 continue;
523 }
524
525 Eigen::MatrixXd local_sol, local_grad;
528 element_id, sample.local_points.row(i), dof_values, local_sol, local_grad);
529 values(i) = local_sol(0);
530 if (gradients)
531 gradients->row(i) = local_grad;
532 }
533
534 if (output_rows > values.rows())
535 {
536 const int previous_rows = values.rows();
537 values.conservativeResize(output_rows, Eigen::NoChange);
538 values.bottomRows(output_rows - previous_rows).setZero();
539 if (gradients)
540 {
541 gradients->conservativeResize(output_rows, Eigen::NoChange);
542 gradients->bottomRows(output_rows - previous_rows).setZero();
543 }
544 }
545 return true;
546 }
547
548 if (sample.node_ids.size() > 0)
549 {
550 values.resize(sample.node_ids.size(), 1);
551 for (int i = 0; i < sample.node_ids.size(); ++i)
552 {
553 const int node_id = sample.node_ids(i);
554 if (node_id < 0 || node_id >= dof_values.rows())
555 return false;
556 values(i) = dof_values(node_id);
557 }
558 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
559 }
560
561 return false;
562 };
563
564 const auto &paraview_options = args["output"]["paraview"]["options"];
565 if (has_element_samples && problem->has_exact_sol() && sample.points.rows() == output_rows)
566 {
567 Eigen::MatrixXd exact;
568 problem->exact(sample.points, sample.time, exact);
569 if (exact.rows() == output_rows)
570 {
571 if (options.export_field("exact"))
572 fields.push_back({"exact", exact, io::OutputField::Association::Point});
573 if (options.export_field("error"))
574 {
575 Eigen::MatrixXd values;
576 if (sample_dof_field(primary_solution, values))
577 fields.push_back({"error", (values - exact).rowwise().norm(), io::OutputField::Association::Point});
578 }
579 }
580 }
581
582 if ((paraview_options["nodes"] || (!options.fields.empty() && options.export_field("nodes")))
583 && has_element_samples
584 && sample.primitive_ids.size() == 0)
585 {
586 Eigen::MatrixXd dof_ids(primary_ndof, 1);
587 dof_ids.col(0).setLinSpaced(primary_ndof, 0, primary_ndof - 1);
588 Eigen::MatrixXd values;
589 if (sample_dof_field(dof_ids, values))
590 fields.push_back({"nodes", values, io::OutputField::Association::Point});
591 }
592
593 if ((paraview_options["jacobian_validity"] || (!options.fields.empty() && options.export_field("validity")))
594 && has_element_samples
595 && mesh_->dimension() == 1
596 && sample.primitive_ids.size() == 0)
597 {
598 const auto invalid_elements = utils::count_invalid(mesh_->dimension(), space_.basis_list(), space_.geometry_basis_list(), primary_solution);
599 Eigen::MatrixXd validity = Eigen::MatrixXd::Zero(output_rows, 1);
600 for (int i = 0; i < sample.element_ids.size(); ++i)
601 validity(i) = std::find(invalid_elements.begin(), invalid_elements.end(), sample.element_ids(i)) != invalid_elements.end();
602 fields.push_back({"validity", validity, io::OutputField::Association::Point});
603 }
604
605 const bool export_solution_gradient =
606 !options.fields.empty() && options.export_field("solution_gradient");
607 if (options.export_field("solution") || export_solution_gradient)
608 {
609 Eigen::MatrixXd values, gradients;
610 if (sample_dof_field(
611 solution, values,
612 export_solution_gradient ? &gradients : nullptr))
613 {
614 if (options.export_field("solution"))
615 fields.push_back({"solution", values, io::OutputField::Association::Point});
616 if (export_solution_gradient)
617 fields.push_back({"solution_gradient", gradients, io::OutputField::Association::Point});
618 }
619 }
620
621 if (paraview_options["material"] && has_element_samples)
622 {
623 const auto &params = primary_assembler_->parameters();
624 std::map<std::string, Eigen::MatrixXd> param_values;
625 for (const auto &[p, _] : params)
626 param_values[p].setZero(output_rows, 1);
627
628 Eigen::MatrixXd rhos = Eigen::MatrixXd::Zero(output_rows, 1);
629 const auto &density = mass_assembler_->density();
630 for (int i = 0; i < sample.local_points.rows(); ++i)
631 {
632 const int element_id = sample.element_ids(i);
633 if (element_id < 0)
634 continue;
635
636 for (const auto &[p, func] : params)
637 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
638 rhos(i) = density(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
639 }
640
641 for (const auto &[name, values] : param_values)
642 if (options.export_field(name))
643 fields.push_back({name, values, io::OutputField::Association::Point});
644 if (options.export_field("rho"))
645 fields.push_back({"rho", rhos, io::OutputField::Association::Point});
646 }
647
648 if (paraview_options["body_ids"] && options.export_field("body_ids") && has_element_samples)
649 {
650 Eigen::MatrixXd ids = Eigen::MatrixXd::Zero(output_rows, 1);
651 for (int i = 0; i < sample.element_ids.size(); ++i)
652 {
653 const int element_id = sample.element_ids(i);
654 if (element_id >= 0)
655 ids(i) = mesh_->get_body_id(element_id);
656 }
657 fields.push_back({"body_ids", ids, io::OutputField::Association::Point});
658 }
659
660 return fields;
661 }
662
663 void ScalarVarForm::build_stiffness_mat(StiffnessMatrix &stiffness)
664 {
665 igl::Timer timer;
666 timer.start();
667 logger().info("Assembling stiffness mat...");
668 assert(primary_assembler_->is_linear());
669 assert(problem->is_scalar());
670
671 primary_assembler_->assemble(mesh_->is_volume(), space_.n_bases, space_.basis_list(), space_.geometry_basis_list(), ass_vals_cache_, 0, stiffness);
672
673 timer.stop();
674 timings.assembling_stiffness_mat_time = timer.getElapsedTime();
675 logger().info(" took {}s", timings.assembling_stiffness_mat_time);
676
677 stats.nn_zero = stiffness.nonZeros();
678 stats.num_dofs = stiffness.rows();
679 stats.mat_size = (long long)stiffness.rows() * (long long)stiffness.cols();
680 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
681
682 write_matrix_market(args, stiffness);
683 }
684
685 void ScalarVarForm::solve_linear_system(
686 const std::unique_ptr<polysolve::linear::Solver> &solver,
688 Eigen::VectorXd &b,
689 const bool compute_spectrum,
690 Eigen::MatrixXd &sol)
691 {
692 assert(primary_assembler_->is_linear());
693 assert(problem->is_scalar());
694 assert(rhs_assembler_ != nullptr);
695
696 Eigen::VectorXd x;
697 stats.spectrum = dirichlet_solve(
698 *solver,
699 A,
700 b,
701 boundary_.boundary_nodes,
702 x,
703 space_.n_bases,
704 args["output"]["data"]["stiffness_mat"],
705 compute_spectrum,
706 /*is_problem_mixed=*/false,
707 /*use_avg_pressure=*/false);
708
709 sol = x;
710 solver->get_info(stats.solver_info);
711
712 const auto 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 void ScalarVarForm::solve_static(Eigen::MatrixXd &sol)
720 {
721 auto solver = polysolve::linear::Solver::create(args["solver"]["linear"], logger());
722 logger().info("{}...", solver->name());
723
724 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
725 const QuadratureOrders boundary_samples = n_boundary_samples(space_.disc_orders.maxCoeff(), gdiscr_order);
726
727 rhs_assembler_->set_bc(
728 boundary_.local_boundary, boundary_.boundary_nodes, boundary_samples,
729 (primary_assembler_->name() != "Bilaplacian") ? boundary_.local_neumann_boundary : std::vector<mesh::LocalBoundary>(), rhs_);
730
732 build_stiffness_mat(A);
733
734 Eigen::VectorXd b = rhs_;
735 solve_linear_system(solver, A, b, args["output"]["advanced"]["spectrum"], sol);
736 }
737
738 void ScalarVarForm::solve_transient(Eigen::MatrixXd &sol)
739 {
740 assert(problem->is_time_dependent());
741 assert(rhs_assembler_ != nullptr);
742
743 auto solver = polysolve::linear::Solver::create(args["solver"]["linear"], logger());
744 logger().info("{}...", solver->name());
745
747 args["time"]["integrator"]);
748 bdf->init(sol, Eigen::VectorXd::Zero(sol.size()), Eigen::VectorXd::Zero(sol.size()), dt);
749 time_integrator = bdf;
750
751 save_timestep(t0, 0, t0, dt, sol);
752
753 Eigen::MatrixXd current_rhs = rhs_;
754
755 StiffnessMatrix stiffness;
756 build_stiffness_mat(stiffness);
757
758 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
759 const QuadratureOrders n_b_samples = n_boundary_samples(space_.disc_orders.maxCoeff(), gdiscr_order);
760 for (int t = 1; t <= time_steps; ++t)
761 {
762 const double time = t0 + t * dt;
763
764 rhs_assembler_->compute_energy_grad(
765 boundary_.local_boundary, boundary_.boundary_nodes, mass_assembler_->density(), n_b_samples,
766 boundary_.local_neumann_boundary, rhs_, time, current_rhs);
767
768 rhs_assembler_->set_bc(
769 boundary_.local_boundary, boundary_.boundary_nodes, n_b_samples, boundary_.local_neumann_boundary, current_rhs, sol, time);
770
771 StiffnessMatrix A = mass_ / bdf->beta_dt() + stiffness;
772 Eigen::VectorXd b = (mass_ * bdf->weighted_sum_x_prevs()) / bdf->beta_dt();
773 for (int i : boundary_.boundary_nodes)
774 b[i] = 0;
775 b += current_rhs;
776
777 solve_linear_system(solver, A, b, args["output"]["advanced"]["spectrum"].get<bool>() && t == time_steps, sol);
778
779 bdf->update_quantities(sol);
780 save_timestep(time, t, t0, dt, sol);
781 save_step_state(t0, dt, t, time_integrator.get());
782
783 logger().info("{}/{} t={}", t, time_steps, time);
784 notify_time_step(t, time_steps, t0, dt);
785 }
786 }
787
788 void ScalarVarForm::solve_problem(Eigen::MatrixXd &sol)
789 {
790 stats.spectrum.setZero();
791
792 igl::Timer timer;
793 timer.start();
794 logger().info("Solving {}", primary_assembler_->name());
795
796 {
797 POLYFEM_SCOPED_TIMER("Setup RHS");
798
799 if (sol.size() <= 0)
800 prepare_initial_solution(sol);
801
802 if (sol.cols() > 1)
803 sol.conservativeResize(Eigen::NoChange, 1);
804 }
805
806 time_integrator = nullptr;
807 if (problem->is_time_dependent())
808 solve_transient(sol);
809 else
810 solve_static(sol);
811
812 timer.stop();
813 timings.solving_time = timer.getElapsedTime();
814 logger().info(" took {}s", timings.solving_time);
815 }
816} // namespace polyfem::varform
int x
std::array< Matrix< int, 3, 3 >, 3 > space_
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
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:1124
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)
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:1811
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:1856
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:1728
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:2099
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:41
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:401
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:497
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
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
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
const std::vector< basis::ElementBases > & basis_list() const
Definition FESpace.hpp:109
bool is_iso_parametric() const
Definition FESpace.hpp:104
void save_json(const Eigen::MatrixXd &solution, std::ostream &out) const override
Save the solution to a JSON file, for output purposes.
std::shared_ptr< assembler::HRZMass > pure_mass_assembler_
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
std::shared_ptr< assembler::Mass > mass_assembler_
io::OutStatsData compute_errors(const Eigen::MatrixXd &solution) override
Get the error statistics of the variational formulation, for output purposes.
std::string name() const override
Get the name of the variational formulation.
void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args) override
assembler::AssemblyValsCache pure_mass_ass_vals_cache_
assembler::AssemblyValsCache mass_ass_vals_cache_
assembler::AssemblyValsCache ass_vals_cache_
void export_data(const Eigen::MatrixXd &solution) const override
void assemble_rhs(const mesh::Mesh &mesh) override
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.
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
void prepare_initial_solution(Eigen::MatrixXd &solution) const
std::shared_ptr< assembler::Assembler > primary_assembler_
std::shared_ptr< assembler::RhsAssembler > rhs_assembler_
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.
void load_mesh(const mesh::Mesh &mesh, const json &args) override
io::OutputSpace output_space() const override
Get the output space of the variational formulation, for output purposes.
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Definition VarForm.cpp:1052
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1066
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:190
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:202
io::OutStatsData stats
Definition VarForm.hpp:194
static bool read_initial_x_from_file(const std::string &state_path, const std::string &x_name, const bool reorder, const Eigen::VectorXi &in_node_to_node, const int dim, Eigen::MatrixXd &x)
Definition VarForm.cpp:38
io::OutGeometryData::ExportOptions export_options(const io::OutputSpace &space) const
Definition VarForm.cpp:859
io::OutGeometryData output_geometry_
Definition VarForm.hpp:206
io::OutputFieldFunction output_field_function(const Eigen::MatrixXd &solution, const io::OutGeometryData::ExportOptions &opts) const
Definition VarForm.cpp:868
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1057
void ensure_output_sampler() const
Definition VarForm.cpp:845
virtual void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
Initialize the variational formulation with the given parameters.
Definition VarForm.cpp:279
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:322
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:197
void set_materials(assembler::Assembler &assembler, const int size) const
Definition VarForm.cpp:830
virtual void reset()=0
Definition VarForm.cpp:267
void assign_discr_orders(const json &discr_order, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders)
Definition VarForm.cpp:744
bool 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:42
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.
std::vector< int > count_invalid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const unsigned max_iter)
Definition Jacobian.cpp:122
bool write_matrix_market(const json &args, const StiffnessMatrix &stiffness)
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
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24
bool export_field(const std::string &field) const
Definition OutData.cpp:51
std::vector< std::string > fields
Eigen::VectorXi node_ids
Eigen::VectorXi primitive_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