PolyFEM
Loading...
Searching...
No Matches
ThermoElasticVarForm.cpp
Go to the documentation of this file.
2
7
10
12
14
25
27
33
34#include <igl/Timer.h>
35
36#include <polysolve/linear/Solver.hpp>
37#include <polysolve/nonlinear/Solver.hpp>
38
39#include <algorithm>
40#include <cassert>
41#include <limits>
42#include <map>
43#include <vector>
44
45namespace polyfem::varform
46{
47 namespace
48 {
49 json first_material(const json &materials)
50 {
51 return materials.is_array() ? materials.front() : materials;
52 }
53
54 void disable_newton_psd_projection(json &solver_params)
55 {
56 const auto disable_for_newton = [](json &params) {
57 if (!params.contains("Newton") || params["Newton"].is_null())
58 params["Newton"] = json::object();
59 params["Newton"]["use_psd_projection"] = false;
60 };
61
62 if (solver_params.contains("solver") && solver_params["solver"].is_array())
63 {
64 for (json &strategy : solver_params["solver"])
65 {
66 const std::string type = strategy.value("type", "");
67 if (type == "Newton" || type == "SparseNewton" || type == "sparse_newton"
68 || type == "DenseNewton" || type == "dense_newton")
69 disable_for_newton(strategy);
70 }
71 }
72 else
73 {
74 disable_for_newton(solver_params);
75 }
76 }
77
78 void assert_same_space_ids(
79 const json &materials,
80 const int displacement_space_id,
81 const int temperature_space_id)
82 {
83 for (const json &material : utils::json_as_array(materials))
84 {
85 if (material.at("displacement_space_id").get<int>() != displacement_space_id
86 || material.at("temperature_space_id").get<int>() != temperature_space_id)
87 {
88 log_and_throw_error("All ThermoElasticity materials must use the same FE space ids.");
89 }
90 }
91 }
92
93 json elastic_material_from_thermo_material(const json &material)
94 {
95 if (!material.contains("elastic_material") || !material["elastic_material"].is_object())
96 log_and_throw_error("ThermoElasticity requires elastic_material to be an elastic material object.");
97
98 json elastic_material = material["elastic_material"];
99 const std::string type = elastic_material.value("type", "");
101 log_and_throw_error("ThermoElasticity elastic_material must be an elastic material, got '{}'.", type);
102
103 if (material.contains("id"))
104 elastic_material["id"] = material["id"];
105 if (material.contains("rho"))
106 elastic_material["rho"] = material["rho"];
107
108 return elastic_material;
109 }
110
111 json solver_params_for_residual_mode(const json &solver_params, const bool is_residual)
112 {
113 json params = solver_params;
114 if (is_residual)
115 disable_newton_psd_projection(params);
116 return params;
117 }
118
119 std::string elastic_formulation_from_thermo_materials(const json &materials)
120 {
121 std::string formulation;
122 for (const json &material : utils::json_as_array(materials))
123 {
124 const json elastic_material = elastic_material_from_thermo_material(material);
125 const std::string type = elastic_material["type"];
126 if (formulation.empty())
127 formulation = type;
128 else if (formulation != type)
129 formulation = "MultiModels";
130 }
131
132 return formulation;
133 }
134
135 StiffnessMatrix block_diag(const StiffnessMatrix &a, const StiffnessMatrix &b)
136 {
137 std::vector<Eigen::Triplet<double>> entries;
138 entries.reserve(a.nonZeros() + b.nonZeros());
139
140 for (int k = 0; k < a.outerSize(); ++k)
141 for (StiffnessMatrix::InnerIterator it(a, k); it; ++it)
142 entries.emplace_back(it.row(), it.col(), it.value());
143
144 for (int k = 0; k < b.outerSize(); ++k)
145 for (StiffnessMatrix::InnerIterator it(b, k); it; ++it)
146 entries.emplace_back(a.rows() + it.row(), a.cols() + it.col(), it.value());
147
148 StiffnessMatrix out(a.rows() + b.rows(), a.cols() + b.cols());
149 out.setFromTriplets(entries.begin(), entries.end());
150 out.makeCompressed();
151 return out;
152 }
153
154 StiffnessMatrix identity_mass(const int size)
155 {
156 return utils::sparse_identity(size, size);
157 }
158
159 } // namespace
160
190
192 const std::string &formulation,
193 const Units &units,
194 const json &args,
195 const std::string &out_path)
196 {
197 VarForm::init(formulation, units, args, out_path);
199
200 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
201
203 if (args["solver"]["advanced"]["check_inversion"] == "Conservative")
204 {
205 if (auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(primary_assembler_))
206 elastic_assembler->set_use_robust_jacobian();
207 }
208
209 temperature_assembler_ = std::make_shared<assembler::Laplacian>("conductivity");
211 mass_assembler_ = std::make_shared<assembler::Mass>();
212 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
213 temperature_mass_assembler_ = std::make_shared<assembler::Mass>(std::make_shared<assembler::ThermalMassDensity>());
214 temperature_pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
215
216 problem = std::make_shared<assembler::GenericTensorProblem>("ThermoElasticDisplacement");
217 problem->clear();
218 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>("ThermoElasticTemperature");
219 temperature_problem_->clear();
220
221 json tmp;
222 tmp["is_time_dependent"] = is_time_dependent;
223 problem->set_parameters(tmp, root_path);
224 temperature_problem_->set_parameters(tmp, root_path);
225
226 auto bc = args["boundary_conditions"];
227 bc["root_path"] = root_path;
228 problem->set_parameters(bc, root_path);
229 temperature_problem_->set_parameters(bc, root_path);
230 problem->set_parameters(args["initial_conditions"], root_path);
231 temperature_problem_->set_parameters(args["initial_conditions"], root_path);
232 problem->set_parameters(args["output"], root_path);
233 temperature_problem_->set_parameters(args["output"], root_path);
234
235 problem->set_units(*primary_assembler_, units);
237
238 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
239 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
240 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
241 contact_dhat_was_explicit_ = args["contact"].value("_dhat_was_explicit", false);
242 this->args["contact"].erase("_dhat_was_explicit");
243 }
244
246 {
247 const json material = first_material(args.at("materials"));
248 displacement_space_id_ = material.at("displacement_space_id").get<int>();
249 temperature_space_id_ = material.at("temperature_space_id").get<int>();
251 log_and_throw_error("ThermoElasticity requires distinct displacement and temperature FE spaces.");
252
253 elastic_formulation_ = elastic_formulation_from_thermo_materials(args.at("materials"));
254 assert_same_space_ids(args.at("materials"), displacement_space_id_, temperature_space_id_);
255 }
256
258 {
259 if (args["materials"].is_array())
260 {
261 json materials = json::array();
262 for (const json &material : args["materials"])
263 materials.push_back(elastic_material_from_thermo_material(material));
264 return materials;
265 }
266
267 return elastic_material_from_thermo_material(args["materials"]);
268 }
269
271 {
272 const json &integrators = args["time"]["integrator"];
273 if (!integrators.is_array())
274 return integrators;
275
276 for (const json &integrator : integrators)
277 {
278 if (integrator.value("fe_space", -1) == fe_space_id)
279 {
280 json copy = integrator;
281 copy.erase("fe_space");
282 return copy;
283 }
284 }
285
286 log_and_throw_error("Missing time integrator for FE space {}.", fe_space_id);
287 }
288
289 void ThermoElasticVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
290 {
291 assert(mesh_);
292 std::vector<int> body_ids(mesh.n_elements());
293 for (int i = 0; i < mesh.n_elements(); ++i)
294 body_ids[i] = mesh.get_body_id(i);
295
296 const json elastic_materials = elastic_material_args();
297
298 primary_assembler_->set_size(mesh.dimension());
299 primary_assembler_->set_materials(body_ids, elastic_materials, units, root_path);
300 thermoelastic_assembler_->set_size(mesh.dimension());
301 thermoelastic_assembler_->set_materials(body_ids, args["materials"], units, root_path);
302 mass_assembler_->set_size(mesh.dimension());
303 mass_assembler_->set_materials(body_ids, elastic_materials, units, root_path);
304 pure_mass_assembler_->set_size(mass_assembler_->size());
305
306 temperature_assembler_->set_size(1);
307 temperature_assembler_->set_materials(body_ids, args["materials"], units, root_path);
308 temperature_mass_assembler_->set_size(1);
309 temperature_mass_assembler_->set_materials(body_ids, args["materials"], units, root_path);
311
312 problem->init(mesh);
313 temperature_problem_->init(mesh);
314
315 logger().info("Loading obstacles...");
317 units,
318 args["geometry"],
319 utils::json_as_array(args["boundary_conditions"]["obstacle_displacements"]),
320 utils::json_as_array(args["boundary_conditions"]["dirichlet_boundary"]),
321 root_path, mesh.dimension());
322 }
323
324 void ThermoElasticVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
325 {
326 assert(problem);
327 assert(temperature_problem_);
328 assert(primary_assembler_);
330
331 Eigen::VectorXi displacement_orders, displacement_ordersq;
332 assign_discr_orders(args["space"], displacement_space_id_, mesh, displacement_orders, displacement_ordersq);
333
334 if (args["space"]["use_p_ref"])
335 {
337 mesh,
338 args["space"]["advanced"]["B"],
339 args["space"]["advanced"]["h1_formula"],
340 args["space"]["discr_order"],
341 args["space"]["advanced"]["discr_order_max"],
342 stats,
343 displacement_orders);
344
345 logger().info("min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
346 }
347
349 mesh,
350 iso_parametric,
351 displacement_orders,
352 displacement_ordersq,
353 args["space"]["basis_type"],
354 args["space"]["poly_basis_type"],
356 mesh.dimension(),
357 args["space"]["advanced"]["quadrature_order"],
358 args["space"]["advanced"]["mass_quadrature_order"],
359 args["space"]["advanced"]["use_corner_quadrature"],
360 args["space"]["advanced"]["n_harmonic_samples"],
361 args["space"]["advanced"]["integral_constraints"],
362 space_,
363 boundary_);
364
365 problem->update_nodes(space_.space_in_node_to_node);
368
369 const int n_fe_bases = space_.n_bases;
371
372 logger().info("Building collision mesh...");
375 logger().info("Done!");
376
377 for (int i = n_fe_bases; i < space_.n_bases; ++i)
378 {
379 for (int d = 0; d < mesh.dimension(); ++d)
380 boundary_.boundary_nodes.push_back(i * mesh.dimension() + d);
381 }
383
384 build_temperature_basis(mesh, iso_parametric, args);
386
387 const auto &current_bases = space_.geometry_basis_list();
388 if (args["space"]["advanced"]["count_flipped_els"])
389 stats.count_flipped_elements(mesh, current_bases);
390
391 const int n_samples = 10;
392 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
393 logger().info("flipped elements {}", stats.n_flipped);
394 logger().info("h: {}", stats.mesh_size);
395
396 if (std::max(space_.n_bases, temperature_space_.n_bases) <= args["solver"]["advanced"]["cache_size"])
397 {
398 igl::Timer timer;
399 timer.start();
400 logger().info("Building cache...");
401 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
402 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
403 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
407 logger().info(" took {}s", timer.getElapsedTime());
408 }
409 else
410 {
417 }
418 }
419
421 {
423
424 problem->setup_bc(
425 mesh,
432 mesh.dimension());
433 std::vector<int> unused_neumann_boundary_nodes;
434 problem->setup_bc(
435 mesh,
441 unused_neumann_boundary_nodes,
442 mesh.dimension());
443
444 problem->setup_nodal_bc(
445 mesh,
450 problem->setup_nodal_bc(
451 mesh,
456
457 for (const int n_id : boundary_.dirichlet_nodes)
458 {
459 const int tag = mesh.get_node_id(n_id);
460 for (int d = 0; d < mesh.dimension(); ++d)
461 if (problem->is_nodal_dimension_dirichlet(n_id, tag, d, displacement_space_id_))
462 boundary_.boundary_nodes.push_back(n_id * mesh.dimension() + d);
463 }
464
468 }
469
470 void ThermoElasticVarForm::build_temperature_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
471 {
472 Eigen::VectorXi temperature_orders, temperature_ordersq;
473 assign_discr_orders(args["space"], temperature_space_id_, mesh, temperature_orders, temperature_ordersq);
474
476 mesh,
477 iso_parametric,
478 temperature_orders,
479 temperature_ordersq,
480 args["space"]["basis_type"],
481 args["space"]["poly_basis_type"],
483 /*value_dim=*/1,
484 args["space"]["advanced"]["quadrature_order"],
485 args["space"]["advanced"]["mass_quadrature_order"],
486 args["space"]["advanced"]["use_corner_quadrature"],
487 args["space"]["advanced"]["n_harmonic_samples"],
488 args["space"]["advanced"]["integral_constraints"],
492
493 logger().info("n temperature bases: {}", temperature_space_.n_bases);
494 }
495
497 {
499
501
502 temperature_problem_->setup_bc(
503 mesh,
510 /*value_dim=*/1);
511 std::vector<int> unused_neumann_boundary_nodes;
512 temperature_problem_->setup_bc(
513 mesh,
519 unused_neumann_boundary_nodes,
520 /*value_dim=*/1);
521
522 temperature_problem_->setup_nodal_bc(
523 mesh,
528 temperature_problem_->setup_nodal_bc(
529 mesh,
534
535 for (const int n_id : temperature_boundary_.dirichlet_nodes)
537
541 }
542
544 {
545 json rhs_solver_params = args["solver"]["linear"];
546 if (!rhs_solver_params.contains("Pardiso"))
547 rhs_solver_params["Pardiso"] = {};
548 rhs_solver_params["Pardiso"]["mtype"] = -2;
549
550 solve_data_.rhs_assembler = std::make_shared<assembler::RhsAssembler>(
556 args["space"]["advanced"]["bc_method"],
557 rhs_solver_params,
560
561 temperature_rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
562 *temperature_assembler_, *mesh_, nullptr,
565 temperature_space_.n_bases, /*size=*/1,
568 args["space"]["advanced"]["bc_method"],
569 rhs_solver_params,
571 }
572
574 {
575 igl::Timer timer;
576 json p_params = {};
577 p_params["formulation"] = primary_assembler_->name();
578 p_params["root_path"] = root_path;
579 {
580 RowVectorNd min, max, delta;
581 mesh.bounding_box(min, max);
582 delta = (max - min) / 2. + min;
583 if (mesh.is_volume())
584 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
585 else
586 p_params["bbox_center"] = {delta(0), delta(1)};
587 }
588 problem->set_parameters(p_params, root_path);
589 temperature_problem_->set_parameters(p_params, root_path);
590
591 rhs_.resize(0, 0);
592 temperature_rhs_.resize(0, 0);
593
594 timer.start();
595 logger().info("Assigning rhs...");
596
598 assert(rhs_assembler_ != nullptr);
599 assert(temperature_rhs_assembler_ != nullptr);
600 assert(temperature_rhs_density_ != nullptr);
601 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
602 rhs_ *= -1;
604 temperature_rhs_ *= -1;
605
606 timings.assigning_rhs_time = timer.getElapsedTime();
607 logger().info(" took {}s", timings.assigning_rhs_time);
608 }
609
611 {
612 mass_.resize(0, 0);
613 pure_mass_.resize(0, 0);
614 temperature_mass_.resize(0, 0);
615 temperature_pure_mass_.resize(0, 0);
616
617 igl::Timer timer;
618 timer.start();
619 logger().info("Assembling mass mat...");
620
625
626 assert(mass_.size() > 0);
627 avg_mass_ = 0;
628 for (int k = 0; k < mass_.outerSize(); ++k)
629 for (StiffnessMatrix::InnerIterator it(mass_, k); it; ++it)
630 avg_mass_ += it.value();
631 avg_mass_ /= mass_.rows();
632 logger().info("average mass {}", avg_mass_);
633
634 if (args["solver"]["advanced"]["lump_mass_matrix"])
635 {
638 }
639
640 stacked_lumped_mass_ = block_diag(
641 pure_mass_.size() > 0 ? pure_mass_ : identity_mass(displacement_ndof()),
642 temperature_pure_mass_.size() > 0 ? temperature_pure_mass_ : identity_mass(temperature_ndof()));
643
644 timer.stop();
645 timings.assembling_mass_mat_time = timer.getElapsedTime();
646 logger().info(" took {}s", timings.assembling_mass_mat_time);
647
650 stats.mat_size = (long long)stacked_lumped_mass_.rows() * (long long)stacked_lumped_mass_.cols();
651 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
652 }
653
654 void ThermoElasticVarForm::initial_temperature_solution(Eigen::MatrixXd &solution) const
655 {
656 assert(temperature_rhs_assembler_ != nullptr);
657
658 const bool was_solution_loaded = read_initial_x_from_file(
659 resolve_input_path(args["input"]["data"]["state"]), "temperature",
660 args["input"]["data"]["reorder"], temperature_space_.space_in_node_to_node,
661 /*dim=*/1, solution);
662
663 if (!was_solution_loaded)
664 temperature_rhs_assembler_->initial_solution(solution);
665 }
666
668 const Eigen::MatrixXd &displacement,
669 const Eigen::MatrixXd &temperature) const
670 {
671 assert(displacement.rows() == displacement_ndof());
672 assert(temperature.rows() == temperature_ndof());
673 assert(displacement.cols() == temperature.cols());
674
675 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
676 solution << displacement, temperature;
677 return solution;
678 }
679
681 const Eigen::MatrixXd &solution,
682 Eigen::MatrixXd &displacement,
683 Eigen::MatrixXd &temperature) const
684 {
685 assert(solution.rows() == total_ndof());
686 displacement = solution.topRows(displacement_ndof());
687 temperature = solution.bottomRows(temperature_ndof());
688 }
689
690 void ThermoElasticVarForm::build_forms(Eigen::MatrixXd &solution, const double t)
691 {
692 assert(solution.cols() == 1);
693 assert(solution.rows() == total_ndof());
694
695 const bool is_time_dependent = problem->is_time_dependent();
696 const double form_dt = is_time_dependent ? dt : 0.0;
697
698 stacked_form_ = std::make_shared<solver::StackedForm>();
699 const auto displacement_block = stacked_form_->add_block(displacement_ndof());
700 const auto temperature_block = stacked_form_->add_block(temperature_ndof());
701
702 Eigen::MatrixXd displacement, temperature;
703 split_solution(solution, displacement, temperature);
704
705 if (is_time_dependent)
706 {
710
711 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
712 initial_solution(displacement_solution);
713 displacement_solution.col(0) = displacement;
714 initial_velocity(displacement_velocity);
715 initial_acceleration(displacement_acceleration);
716 solve_data_.time_integrator->init(displacement_solution, displacement_velocity, displacement_acceleration, dt);
717 }
718 else
719 {
721 }
722
723 init_forms(args, mesh_->dimension(), displacement, t);
724 for (const auto &form : forms)
725 {
726 assert(form);
727 stacked_form_->add(displacement_block, form);
728 }
729 solve_data_.al_form.clear();
730
731 temperature_form_ = std::make_shared<solver::ElasticForm>(
733 *temperature_assembler_, temperature_ass_vals_cache_, t, form_dt, mesh_->is_volume(),
734 /*jacobian_threshold=*/0.0, solver::ElementInversionCheck::Discrete);
735 stacked_form_->add(temperature_block, temperature_form_);
736
738 thermoelastic_form_ = std::make_shared<solver::MixedAssemblerForm>(
742 t, form_dt, mesh_->is_volume());
743 stacked_form_->add(displacement_block, temperature_block, thermoelastic_form_);
744
745 assert(temperature_space_.disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized FE orders");
746 assert(temperature_space_.geometry && "Thermal boundary quadrature requires an initialized geometry mapping");
747 assert(temperature_space_.geometry->disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized geometry orders");
748 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
749 const QuadratureOrders temperature_boundary_samples =
751 temperature_body_form_ = std::make_shared<solver::BodyForm>(
752 temperature_ndof(), 0,
754 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
757 /*is_formulation_mixed=*/false, is_time_dependent);
758 temperature_body_form_->update_quantities(t, temperature);
759 stacked_form_->add(temperature_block, temperature_body_form_);
760
761 if (is_time_dependent)
762 {
766
767 Eigen::MatrixXd temperature_solution;
768 initial_temperature_solution(temperature_solution);
769 temperature_solution.col(0) = temperature;
770 Eigen::MatrixXd temperature_velocity = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
771 Eigen::MatrixXd temperature_acceleration = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
772 temperature_time_integrator_->init(temperature_solution, temperature_velocity, temperature_acceleration, dt);
773
774 temperature_inertia_form_ = std::make_shared<solver::InertiaForm>(temperature_mass_, *temperature_time_integrator_);
776 {
777 temperature_inertia_form_->set_x_tilde_updater(
778 [this, temperature_boundary_samples](
779 const double t,
780 const Eigen::VectorXd &,
781 Eigen::VectorXd &target) {
782 Eigen::MatrixXd projected_target = target;
783 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
787 temperature_boundary_samples,
788 empty_neumann_boundary,
789 projected_target,
790 Eigen::MatrixXd(),
791 t);
792 assert(projected_target.cols() == 1);
793 target = projected_target.col(0);
794 });
795 }
796 stacked_form_->add(temperature_block, temperature_inertia_form_);
797
799 }
800 else
801 {
804 }
805
806 forms.clear();
807 forms.push_back(stacked_form_);
808 for (const auto &form : forms)
809 form->set_output_dir(output_path);
810
811 solve_data_.al_form.clear();
813 {
814 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
815 const auto displacement_al_block = stacked_al->add_block(displacement_block.size());
816 const auto temperature_al_block = stacked_al->add_block(temperature_block.size());
817
818 if (!boundary_.boundary_nodes.empty())
819 {
820 stacked_al->add(
821 displacement_al_block,
822 std::make_shared<solver::BCLagrangianForm>(
823 displacement_block.size(),
826 obstacle.n_vertices() * mesh_->dimension(), is_time_dependent, t));
827 }
828
830 {
831 stacked_al->add(
832 temperature_al_block,
833 std::make_shared<solver::BCLagrangianForm>(
834 temperature_block.size(),
836 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
838 /*obstacle_ndof=*/0, is_time_dependent, t));
839 }
840
841 solve_data_.al_form.push_back(stacked_al);
842 }
843 }
844
846 {
847 assert(problem->is_time_dependent());
848 assert(solve_data_.time_integrator && "Transient thermoelasticity requires a displacement time integrator");
850
852
853 const double displacement_scaling = solve_data_.time_integrator->acceleration_scaling();
854 const double temperature_scaling = temperature_time_integrator_->acceleration_scaling();
855
857 temperature_form_->set_weight(temperature_scaling);
859 temperature_body_form_->set_weight(temperature_scaling);
861 thermoelastic_form_->set_row_weights(displacement_scaling, temperature_scaling);
862 }
863
864 void ThermoElasticVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &solution)
865 {
866 assert(solve_data_.nl_problem != nullptr && "Thermoelastic forms must initialize the nonlinear problem before solving");
868
869 const json nonlinear_params = solver_params_for_residual_mode(args["solver"]["nonlinear"], nl_problem.is_residual());
870 const json al_nonlinear_params = solver_params_for_residual_mode(args["solver"]["augmented_lagrangian"]["nonlinear"], nl_problem.is_residual());
871
872 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
873 polysolve::nonlinear::Solver::create(
874 nonlinear_params, args["solver"]["linear"],
876
877 if (nl_problem.uses_lagging())
878 nl_problem.init_lagging(solution);
879
880 const auto update_displacement_barrier_stiffness = [&](const Eigen::VectorXd &x) {
881 const Eigen::VectorXd displacement = x.head(displacement_ndof());
883 };
884
885 if (!solve_data_.al_form.empty())
886 {
887 solver::ALSolver al_solver(
889 args["solver"]["augmented_lagrangian"]["initial_weight"],
890 args["solver"]["augmented_lagrangian"]["scaling"],
891 args["solver"]["augmented_lagrangian"]["max_weight"],
892 args["solver"]["augmented_lagrangian"]["eta"],
893 update_displacement_barrier_stiffness);
894
895 al_solver.post_subsolve = [&](const double al_weight) {
896 stats.solver_info.push_back(
897 {{"type", al_weight > 0 ? "al" : "rc"},
898 {"t", step},
899 {"info", nl_solver->info()}});
900 if (al_weight > 0)
901 stats.solver_info.back()["weight"] = al_weight;
902 save_subsolve(stats.solver_info.size(), step, solution);
903 };
904
905 al_solver.solve_al(
906 nl_problem, solution, al_nonlinear_params,
907 args["solver"]["linear"], units.characteristic_length(), nl_solver);
908 al_solver.solve_reduced(
909 nl_problem, solution, nonlinear_params,
910 args["solver"]["linear"], units.characteristic_length(), nl_solver);
911 return;
912 }
913
914 Eigen::VectorXd x = solution;
915 nl_problem.init(x);
916 update_displacement_barrier_stiffness(x);
917 nl_problem.normalize_forms();
918 nl_solver->minimize(nl_problem, x);
919 nl_problem.finish();
920 solution = x;
921 stats.solver_info.push_back({{"type", "rc"}, {"t", step}, {"info", nl_solver->info()}});
922 save_subsolve(stats.solver_info.size(), step, solution);
923 }
924
926 Eigen::MatrixXd &sol,
927 const InitialConditionOverride *initial_condition_override,
928 const ForwardStepCallback &post_step)
929 {
930 assert(!initial_condition_override && "Thermoelasticity does not support initial-condition overrides");
931 assert(!post_step && "Thermoelasticity does not support post-step callbacks");
932
933 stats.spectrum.setZero();
934
935 igl::Timer timer;
936 timer.start();
937 logger().info("Solving ThermoElasticity");
938
939 {
940 POLYFEM_SCOPED_TIMER("Setup RHS");
941
942 if (sol.size() <= 0)
943 {
944 Eigen::MatrixXd displacement, temperature;
945 initial_solution(displacement);
946 initial_temperature_solution(temperature);
947 const int cols = std::max(displacement.cols(), temperature.cols());
948 if (displacement.cols() != cols)
949 displacement.conservativeResize(Eigen::NoChange, cols);
950 if (temperature.cols() != cols)
951 temperature.conservativeResize(Eigen::NoChange, cols);
952 sol = stacked_solution(displacement, temperature);
953 }
954
955 if (sol.cols() > 1)
956 sol.conservativeResize(Eigen::NoChange, 1);
957 }
958
959 build_forms(sol, problem->is_time_dependent() ? t0 + dt : 1.0);
960
961 double characteristic_length = 0;
962 if (args["solver"]["advanced"]["characteristic_length"] > 0)
963 characteristic_length = args["solver"]["advanced"]["characteristic_length"];
964 else
965 {
966 RowVectorNd min, max;
967 mesh_->bounding_box(min, max);
968 characteristic_length = (max - min).norm();
969 }
970
971 double characteristic_force_density = 0;
972 if (args["solver"]["advanced"]["characteristic_force_density"] <= 0)
973 {
974 logger().warn("No user-specified force density was provided, defaulting to 10000.");
975 characteristic_force_density = 10000;
976 }
977 else
978 characteristic_force_density = args["solver"]["advanced"]["characteristic_force_density"];
979
980 solve_data_.nl_problem = std::make_shared<solver::NLProblem>(
981 total_ndof(), problem->is_time_dependent() ? t0 + dt : 1.0,
983 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
984 characteristic_length, characteristic_force_density,
985 stacked_lumped_mass_.size() > 0 ? stacked_lumped_mass_ : identity_mass(total_ndof()),
986 mesh_->dimension(),
987 problem->is_time_dependent());
988 solve_data_.nl_problem->init(sol);
989 solve_data_.nl_problem->update_quantities(problem->is_time_dependent() ? t0 + dt : 1.0, sol);
990 stats.solver_info = json::array();
991
992 if (!problem->is_time_dependent())
993 {
994 solve_nonlinear_step(0, sol);
995 }
996 else
997 {
998 save_timestep(t0, 0, t0, dt, sol);
999 for (int t = 1; t <= time_steps; ++t)
1000 {
1001 const double time = t0 + dt * t;
1002 solve_nonlinear_step(t, sol);
1003
1004 save_timestep(time, t, t0, dt, sol);
1005
1006 Eigen::MatrixXd displacement, temperature;
1007 split_solution(sol, displacement, temperature);
1008 solve_data_.time_integrator->update_quantities(displacement);
1009 temperature_time_integrator_->update_quantities(temperature);
1012 solve_data_.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
1013
1014 logger().info("{}/{} t={}", t, time_steps, time);
1016 save_step_state(t0, dt, t, nullptr);
1017 }
1018 }
1019
1020 timer.stop();
1021 timings.solving_time = timer.getElapsedTime();
1022 logger().info(" took {}s", timings.solving_time);
1023 }
1024
1026 {
1027 if (!args["output"]["advanced"]["compute_error"])
1028 return stats;
1029
1030 Eigen::MatrixXd displacement, temperature;
1031 split_solution(solution, displacement, temperature);
1032
1033 double tend = 0;
1034 if (!args["time"].is_null())
1035 tend = args["time"]["tend"];
1036
1038 return stats;
1039 }
1040
1041 std::vector<io::OutputField> ThermoElasticVarForm::output_fields(
1042 const io::OutputSample &sample,
1043 const Eigen::MatrixXd &solution,
1044 const io::OutputFieldOptions &options) const
1045 {
1046 Eigen::MatrixXd displacement, temperature;
1047 split_solution(solution, displacement, temperature);
1048
1049 std::vector<io::OutputField> fields =
1050 NonlinearElasticVarForm::output_fields(sample, displacement, options);
1051 fields.erase(
1052 std::remove_if(
1053 fields.begin(), fields.end(),
1054 [](const io::OutputField &field) {
1055 return field.name == "solution" || field.name == "solution_gradient";
1056 }),
1057 fields.end());
1058
1059 if (!mesh_ || temperature.size() <= 0)
1060 return fields;
1062 return fields;
1063
1064 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
1065 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
1066
1067 const auto has_field = [&](const std::string &name) {
1068 return std::any_of(fields.begin(), fields.end(), [&](const io::OutputField &field) {
1069 return field.name == name;
1070 });
1071 };
1072
1073 const auto append_material_fields = [&](const assembler::Assembler &assembler) {
1074 const auto &paraview_options = args["output"]["paraview"]["options"];
1075 if (!paraview_options["material"] || !has_element_samples)
1076 return;
1077
1078 const auto params = assembler.parameters();
1079 std::map<std::string, Eigen::MatrixXd> param_values;
1080 for (const auto &[p, _] : params)
1081 param_values[p].setZero(output_rows, 1);
1082
1083 for (int i = 0; i < sample.local_points.rows(); ++i)
1084 {
1085 const int element_id = sample.element_ids(i);
1086 if (element_id < 0)
1087 continue;
1088
1089 for (const auto &[p, func] : params)
1090 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
1091 }
1092
1093 for (const auto &[name, values] : param_values)
1094 if (options.export_field(name) && !has_field(name))
1095 fields.push_back({name, values, io::OutputField::Association::Point});
1096 };
1097
1098 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients = nullptr) -> bool {
1099 if (has_element_samples)
1100 {
1101 values.resize(sample.local_points.rows(), 1);
1102 if (gradients)
1103 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1104 for (int i = 0; i < sample.local_points.rows(); ++i)
1105 {
1106 const int element_id = sample.element_ids(i);
1107 if (element_id < 0)
1108 {
1109 values(i) = 0;
1110 if (gradients)
1111 gradients->row(i).setZero();
1112 continue;
1113 }
1114
1115 Eigen::MatrixXd local_sol, local_grad;
1117 *mesh_, 1, temperature_space_.basis_list(), temperature_space_.geometry_basis_list(),
1118 element_id, sample.local_points.row(i), temperature, local_sol, local_grad);
1119 values(i) = local_sol(0);
1120 if (gradients)
1121 gradients->row(i) = local_grad;
1122 }
1123
1124 if (output_rows > values.rows())
1125 {
1126 const int previous_rows = values.rows();
1127 values.conservativeResize(output_rows, Eigen::NoChange);
1128 values.bottomRows(output_rows - previous_rows).setZero();
1129 if (gradients)
1130 {
1131 gradients->conservativeResize(output_rows, Eigen::NoChange);
1132 gradients->bottomRows(output_rows - previous_rows).setZero();
1133 }
1134 }
1135 return true;
1136 }
1137
1138 if (sample.node_ids.size() > 0)
1139 {
1140 values.resize(sample.node_ids.size(), 1);
1141 for (int i = 0; i < sample.node_ids.size(); ++i)
1142 {
1143 const int node_id = sample.node_ids(i);
1144 if (node_id < 0 || node_id >= temperature.rows())
1145 return false;
1146 values(i) = temperature(node_id);
1147 }
1148 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1149 }
1150
1151 return false;
1152 };
1153
1154 const bool export_temperature_gradient =
1155 !options.fields.empty() && options.export_field("temperature_gradient");
1156 if (options.export_field("temperature") || export_temperature_gradient)
1157 {
1158 Eigen::MatrixXd values, gradients;
1159 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1160 {
1161 if (options.export_field("temperature"))
1162 fields.push_back({"temperature", values, io::OutputField::Association::Point});
1163 if (export_temperature_gradient)
1164 fields.push_back({"temperature_gradient", gradients, io::OutputField::Association::Point});
1165 }
1166 }
1167
1168 if (thermoelastic_assembler_)
1169 append_material_fields(*thermoelastic_assembler_);
1170 if (temperature_assembler_)
1171 append_material_fields(*temperature_assembler_);
1172 if (temperature_mass_assembler_)
1173 append_material_fields(*temperature_mass_assembler_);
1174
1175 return fields;
1176 }
1177} // namespace polyfem::varform
std::vector< Eigen::Triplet< double > > entries
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
double characteristic_length() const
Definition Units.hpp:22
static std::shared_ptr< MixedNLAssembler > make_mixed_nl_assembler(const std::string &formulation)
static bool is_elastic_material(const std::string &material)
utility to check if material is one of the elastic materials
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.
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)
double assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
double solving_time
time to solve
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:2815
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:2860
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:2732
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
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 get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:525
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:456
int dimension() const
utily for dimension
Definition Mesh.hpp:164
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:508
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
void solve_reduced(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
Definition ALSolver.hpp:41
std::function< void(const double)> post_subsolve
Definition ALSolver.hpp:53
void solve_al(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
Definition ALSolver.hpp:29
bool is_residual() const override
virtual void init(const TVector &x0) override
double normalize_forms() override
void init_lagging(const TVector &x) override
void update_dt()
updates the dt inside the different forms
std::shared_ptr< solver::NLProblem > nl_problem
std::vector< std::shared_ptr< solver::AugmentedLagrangianForm > > al_form
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
void update_barrier_stiffness(const Eigen::VectorXd &x)
update the barrier stiffness for the forms
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
static std::shared_ptr< ImplicitTimeIntegrator > construct_time_integrator(const json &params, DynamicOrder dynamic_order=DynamicOrder::Second)
Factory method for constructing an implicit time integrator.
assembler::AssemblyValsCache pure_mass_ass_vals_cache_
std::shared_ptr< assembler::Assembler > primary_assembler_
QuadratureOrders elastic_boundary_samples() const
assembler::AssemblyValsCache ass_vals_cache_
std::shared_ptr< assembler::HRZMass > pure_mass_assembler_
std::shared_ptr< assembler::Mass > mass_assembler_
std::shared_ptr< assembler::RhsAssembler > rhs_assembler_
assembler::AssemblyValsCache mass_ass_vals_cache_
void initial_velocity(Eigen::MatrixXd &velocity, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
void initial_acceleration(Eigen::MatrixXd &acceleration, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
void initial_solution(Eigen::MatrixXd &solution, const InitialConditionOverride *override=nullptr, const std::string &state_prefix="") const
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
const std::vector< basis::ElementBases > & basis_list() const
Definition FESpace.hpp:109
virtual void init_forms(const json &args, int dim, Eigen::MatrixXd &sol, double t)
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.
static void build_collision_mesh(const mesh::Mesh &mesh, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const std::vector< mesh::LocalBoundary > &total_local_boundary, const mesh::Obstacle &obstacle, const json &args, const std::function< std::string(const std::string &)> &resolve_input_path, const Eigen::VectorXi &in_node_to_node, ipc::CollisionMesh &collision_mesh)
std::vector< std::shared_ptr< solver::Form > > forms
void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args) 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.
std::shared_ptr< solver::BodyForm > temperature_body_form_
std::shared_ptr< assembler::HRZMass > temperature_pure_mass_assembler_
std::shared_ptr< assembler::MixedNLAssembler > thermoelastic_assembler_
assembler::AssemblyValsCache temperature_mass_ass_vals_cache_
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > temperature_time_integrator_
std::shared_ptr< solver::MixedAssemblerForm > thermoelastic_form_
json time_integrator_args(const int fe_space_id) const
void build_forms(Eigen::MatrixXd &solution, const double t)
void load_mesh(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< solver::ElasticForm > temperature_form_
void solve_nonlinear_step(const int step, Eigen::MatrixXd &solution)
io::OutStatsData compute_errors(const Eigen::MatrixXd &solution) override
Get the error statistics of the variational formulation, for output purposes.
void assemble_rhs(const mesh::Mesh &mesh) override
assembler::AssemblyValsCache temperature_ass_vals_cache_
assembler::AssemblyValsCache temperature_pure_mass_ass_vals_cache_
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< assembler::Problem > temperature_problem_
std::string name() const override
Get the name of the variational formulation.
void split_solution(const Eigen::MatrixXd &solution, Eigen::MatrixXd &displacement, Eigen::MatrixXd &temperature) const
std::shared_ptr< assembler::Assembler > temperature_assembler_
Eigen::MatrixXd stacked_solution(const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &temperature) const
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 build_temperature_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
void initial_temperature_solution(Eigen::MatrixXd &solution) const
std::shared_ptr< assembler::Density > temperature_rhs_density_
std::shared_ptr< solver::InertiaForm > temperature_inertia_form_
std::shared_ptr< assembler::RhsAssembler > temperature_rhs_assembler_
void solve_problem(Eigen::MatrixXd &sol, const InitialConditionOverride *initial_condition_override, const ForwardStepCallback &post_step) override
std::shared_ptr< solver::StackedForm > stacked_form_
std::shared_ptr< assembler::Mass > temperature_mass_assembler_
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Definition VarForm.cpp:1105
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1119
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:214
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:226
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:745
io::OutStatsData stats
Definition VarForm.hpp:218
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:999
static bool read_initial_x_from_file(const std::string &state_path, const std::string &x_name, const bool reorder, const Eigen::VectorXi &in_node_to_node, const int dim, Eigen::MatrixXd &x)
Definition VarForm.cpp:38
void save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:980
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:255
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:319
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:941
virtual void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
Initialize the variational formulation with the given parameters.
Definition VarForm.cpp:276
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:221
void save_step_state(const double t0, const double dt, const int t, const time_integrator::ImplicitTimeIntegrator *time_integrator, const bool rest_mesh_written=false) const
Definition VarForm.cpp:926
Obstacle read_obstacle_geometry(const Units &units, const json &geometry, const std::vector< json > &displacements, const std::vector< json > &dirichlets, const std::string &root_path, const int dim, const std::vector< std::string > &_names, const std::vector< Eigen::MatrixXd > &_vertices, const std::vector< Eigen::MatrixXi > &_cells, const bool non_conforming)
read a FEM mesh from a geometry JSON
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
Eigen::SparseMatrix< double > sparse_identity(int rows, int cols)
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
Definition JSONUtils.hpp:41
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:49
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
std::array< int, 2 > QuadratureOrders
Definition Types.hpp:19
nlohmann::json json
Definition Common.hpp:9
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
Eigen::VectorXi node_ids
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points
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