PolyFEM
Loading...
Searching...
No Matches
ThermoElasticVarForm.cpp
Go to the documentation of this file.
2
7
9
11
22
24
30
31#include <igl/Timer.h>
32
33#include <polysolve/linear/Solver.hpp>
34#include <polysolve/nonlinear/Solver.hpp>
35
36#include <algorithm>
37#include <cassert>
38#include <limits>
39#include <map>
40#include <vector>
41
42namespace polyfem::varform
43{
46 const Eigen::MatrixXd &solution,
47 const io::CheckpointMetadata &metadata) const
48 {
49 VarForm::serialize_checkpoint(writer, solution, metadata);
50 write_checkpoint_ordering(writer, "primary", space_);
51 write_checkpoint_ordering(writer, "temperature", temperature_space_);
53 log_and_throw_error("Cannot checkpoint thermoelastic state without its temperature integrator.");
54 temperature_time_integrator_->serialize_checkpoint(writer, "/checkpoint/state/temperature_integrator");
55 }
56
58 const io::CheckpointReader &reader,
59 Eigen::MatrixXd &solution)
60 {
61 VarForm::deserialize_checkpoint(reader, solution);
64 reader, "primary", space_,
65 mesh_->dimension(), 0, solution);
67 reader, "temperature", temperature_space_,
68 1, displacement_ndof(), solution);
69 }
70
71 namespace
72 {
73 json first_material(const json &materials)
74 {
75 return materials.is_array() ? materials.front() : materials;
76 }
77
78 void disable_newton_psd_projection(json &solver_params)
79 {
80 const auto disable_for_newton = [](json &params) {
81 if (!params.contains("Newton") || params["Newton"].is_null())
82 params["Newton"] = json::object();
83 params["Newton"]["use_psd_projection"] = false;
84 };
85
86 if (solver_params.contains("solver") && solver_params["solver"].is_array())
87 {
88 for (json &strategy : solver_params["solver"])
89 {
90 const std::string type = strategy.value("type", "");
91 if (type == "Newton" || type == "SparseNewton" || type == "sparse_newton"
92 || type == "DenseNewton" || type == "dense_newton")
93 disable_for_newton(strategy);
94 }
95 }
96 else
97 {
98 disable_for_newton(solver_params);
99 }
100 }
101
102 void assert_same_space_ids(
103 const json &materials,
104 const int displacement_space_id,
105 const int temperature_space_id)
106 {
107 for (const json &material : utils::json_as_array(materials))
108 {
109 if (material.at("displacement_space_id").get<int>() != displacement_space_id
110 || material.at("temperature_space_id").get<int>() != temperature_space_id)
111 {
112 log_and_throw_error("All ThermoElasticity materials must use the same FE space ids.");
113 }
114 }
115 }
116
117 json elastic_material_from_thermo_material(const json &material)
118 {
119 if (!material.contains("elastic_material") || !material["elastic_material"].is_object())
120 log_and_throw_error("ThermoElasticity requires elastic_material to be an elastic material object.");
121
122 json elastic_material = material["elastic_material"];
123 const std::string type = elastic_material.value("type", "");
125 log_and_throw_error("ThermoElasticity elastic_material must be an elastic material, got '{}'.", type);
126
127 if (material.contains("id"))
128 elastic_material["id"] = material["id"];
129 if (material.contains("rho"))
130 elastic_material["rho"] = material["rho"];
131
132 return elastic_material;
133 }
134
135 json solver_params_for_residual_mode(const json &solver_params, const bool is_residual)
136 {
137 json params = solver_params;
138 if (is_residual)
139 disable_newton_psd_projection(params);
140 return params;
141 }
142
143 std::string elastic_formulation_from_thermo_materials(const json &materials)
144 {
145 std::string formulation;
146 for (const json &material : utils::json_as_array(materials))
147 {
148 const json elastic_material = elastic_material_from_thermo_material(material);
149 const std::string type = elastic_material["type"];
150 if (formulation.empty())
151 formulation = type;
152 else if (formulation != type)
153 formulation = "MultiModels";
154 }
155
156 return formulation;
157 }
158
159 StiffnessMatrix block_diag(const StiffnessMatrix &a, const StiffnessMatrix &b)
160 {
161 std::vector<Eigen::Triplet<double>> entries;
162 entries.reserve(a.nonZeros() + b.nonZeros());
163
164 for (int k = 0; k < a.outerSize(); ++k)
165 for (StiffnessMatrix::InnerIterator it(a, k); it; ++it)
166 entries.emplace_back(it.row(), it.col(), it.value());
167
168 for (int k = 0; k < b.outerSize(); ++k)
169 for (StiffnessMatrix::InnerIterator it(b, k); it; ++it)
170 entries.emplace_back(a.rows() + it.row(), a.cols() + it.col(), it.value());
171
172 StiffnessMatrix out(a.rows() + b.rows(), a.cols() + b.cols());
173 out.setFromTriplets(entries.begin(), entries.end());
174 out.makeCompressed();
175 return out;
176 }
177
178 StiffnessMatrix identity_mass(const int size)
179 {
180 return utils::sparse_identity(size, size);
181 }
182
183 } // namespace
184
214
216 const std::string &formulation,
217 const Units &units,
218 const json &args,
219 const std::string &out_path)
220 {
221 for (const std::string field : {"pressure_boundary", "pressure_cavity"})
222 {
223 const json::json_pointer path("/boundary_conditions/" + field);
224 if (args.contains(path) && !args.at(path).empty())
225 log_and_throw_error("{} does not support boundary_conditions.{}.", formulation, field);
226 }
227
228 VarForm::init(formulation, units, args, out_path);
230
231 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
232
234 if (args["solver"]["advanced"]["check_inversion"] == "Conservative")
235 {
236 if (auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(primary_assembler_))
237 elastic_assembler->set_use_robust_jacobian();
238 }
239
240 temperature_assembler_ = std::make_shared<assembler::Laplacian>("conductivity");
242 mass_assembler_ = std::make_shared<assembler::Mass>();
243 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
244 temperature_mass_assembler_ = std::make_shared<assembler::Mass>(std::make_shared<assembler::ThermalMassDensity>());
245 temperature_pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
246
247 problem = std::make_shared<assembler::GenericTensorProblem>("ThermoElasticDisplacement");
248 problem->clear();
249 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>("ThermoElasticTemperature");
250 temperature_problem_->clear();
251
252 json tmp;
253 tmp["is_time_dependent"] = is_time_dependent;
254 problem->set_parameters(tmp, resources_);
255 temperature_problem_->set_parameters(tmp, resources_);
256
257 auto bc = args["boundary_conditions"];
258 problem->set_parameters(bc, resources_);
259 temperature_problem_->set_parameters(bc, resources_);
260 problem->set_parameters(args["initial_conditions"], resources_);
261 temperature_problem_->set_parameters(args["initial_conditions"], resources_);
262 problem->set_parameters(args["output"], resources_);
263 temperature_problem_->set_parameters(args["output"], resources_);
264
265 problem->set_units(*primary_assembler_, units);
267
268 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
269 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
270 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
271 contact_dhat_was_explicit_ = args["contact"].value("_dhat_was_explicit", false);
272 this->args["contact"].erase("_dhat_was_explicit");
273 }
274
276 {
277 const json material = first_material(args.at("materials"));
278 displacement_space_id_ = material.at("displacement_space_id").get<int>();
279 temperature_space_id_ = material.at("temperature_space_id").get<int>();
281 log_and_throw_error("ThermoElasticity requires distinct displacement and temperature FE spaces.");
282
283 elastic_formulation_ = elastic_formulation_from_thermo_materials(args.at("materials"));
284 assert_same_space_ids(args.at("materials"), displacement_space_id_, temperature_space_id_);
285 }
286
288 {
289 if (args["materials"].is_array())
290 {
291 json materials = json::array();
292 for (const json &material : args["materials"])
293 materials.push_back(elastic_material_from_thermo_material(material));
294 return materials;
295 }
296
297 return elastic_material_from_thermo_material(args["materials"]);
298 }
299
301 {
302 const json &integrators = args["time"]["integrator"];
303 if (!integrators.is_array())
304 return integrators;
305
306 for (const json &integrator : integrators)
307 {
308 if (integrator.value("fe_space", -1) == fe_space_id)
309 {
310 json copy = integrator;
311 copy.erase("fe_space");
312 return copy;
313 }
314 }
315
316 log_and_throw_error("Missing time integrator for FE space {}.", fe_space_id);
317 }
318
319 void ThermoElasticVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
320 {
321 assert(mesh_);
322 std::vector<int> body_ids(mesh.n_elements());
323 for (int i = 0; i < mesh.n_elements(); ++i)
324 body_ids[i] = mesh.get_body_id(i);
325
326 const json elastic_materials = elastic_material_args();
327
328 primary_assembler_->set_size(mesh.dimension());
329 primary_assembler_->set_materials(body_ids, elastic_materials, units, resources_);
330 thermoelastic_assembler_->set_size(mesh.dimension());
331 thermoelastic_assembler_->set_materials(body_ids, args["materials"], units, resources_);
332 mass_assembler_->set_size(mesh.dimension());
333 mass_assembler_->set_materials(body_ids, elastic_materials, units, resources_);
334 pure_mass_assembler_->set_size(mass_assembler_->size());
335
336 temperature_assembler_->set_size(1);
337 temperature_assembler_->set_materials(body_ids, args["materials"], units, resources_);
338 temperature_mass_assembler_->set_size(1);
339 temperature_mass_assembler_->set_materials(body_ids, args["materials"], units, resources_);
341
342 problem->init(mesh);
343 temperature_problem_->init(mesh);
344 }
345
346 void ThermoElasticVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
347 {
348 assert(problem);
349 assert(temperature_problem_);
350 assert(primary_assembler_);
352
353 Eigen::VectorXi displacement_orders, displacement_ordersq;
354 assign_discr_orders(args["space"], displacement_space_id_, mesh, displacement_orders, displacement_ordersq);
355
356 if (args["space"]["use_p_ref"])
357 {
359 mesh,
360 args["space"]["advanced"]["B"],
361 args["space"]["advanced"]["h1_formula"],
362 args["space"]["discr_order"],
363 args["space"]["advanced"]["discr_order_max"],
364 stats,
365 displacement_orders);
366
367 logger().info("min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
368 }
369
371 mesh,
372 iso_parametric,
373 displacement_orders,
374 displacement_ordersq,
375 args["space"]["basis_type"],
376 args["space"]["poly_basis_type"],
378 mesh.dimension(),
379 args["space"]["advanced"]["quadrature_order"],
380 args["space"]["advanced"]["mass_quadrature_order"],
381 args["space"]["advanced"]["use_corner_quadrature"],
382 args["space"]["advanced"]["n_harmonic_samples"],
383 args["space"]["advanced"]["integral_constraints"],
384 space_,
385 boundary_);
386
387 problem->update_nodes(space_.space_in_node_to_node);
390
391 const int n_fe_bases = space_.n_bases;
393
394 logger().info("Building collision mesh...");
397 logger().info("Done!");
398
399 for (int i = n_fe_bases; i < space_.n_bases; ++i)
400 {
401 for (int d = 0; d < mesh.dimension(); ++d)
402 boundary_.boundary_nodes.push_back(i * mesh.dimension() + d);
403 }
405
406 build_temperature_basis(mesh, iso_parametric, args);
408
409 const auto &current_bases = space_.geometry_basis_list();
410 if (args["space"]["advanced"]["count_flipped_els"])
411 stats.count_flipped_elements(mesh, current_bases);
412
413 const int n_samples = 10;
414 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
415 logger().info("flipped elements {}", stats.n_flipped);
416 logger().info("h: {}", stats.mesh_size);
417
418 if (std::max(space_.n_bases, temperature_space_.n_bases) <= args["solver"]["advanced"]["cache_size"])
419 {
420 igl::Timer timer;
421 timer.start();
422 logger().info("Building cache...");
423 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
424 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
425 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
429 logger().info(" took {}s", timer.getElapsedTime());
430 }
431 else
432 {
439 }
440 }
441
443 {
445
446 problem->setup_bc(
447 mesh,
454 mesh.dimension());
455 std::vector<int> unused_neumann_boundary_nodes;
456 problem->setup_bc(
457 mesh,
463 unused_neumann_boundary_nodes,
464 mesh.dimension());
465
466 problem->setup_nodal_bc(
467 mesh,
472 problem->setup_nodal_bc(
473 mesh,
478
479 for (const int n_id : boundary_.dirichlet_nodes)
480 {
481 const int tag = mesh.get_node_id(n_id);
482 for (int d = 0; d < mesh.dimension(); ++d)
483 if (problem->is_nodal_dimension_dirichlet(n_id, tag, d, displacement_space_id_))
484 boundary_.boundary_nodes.push_back(n_id * mesh.dimension() + d);
485 }
486
490 }
491
492 void ThermoElasticVarForm::build_temperature_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
493 {
494 Eigen::VectorXi temperature_orders, temperature_ordersq;
495 assign_discr_orders(args["space"], temperature_space_id_, mesh, temperature_orders, temperature_ordersq);
496
498 mesh,
499 iso_parametric,
500 temperature_orders,
501 temperature_ordersq,
502 args["space"]["basis_type"],
503 args["space"]["poly_basis_type"],
505 /*value_dim=*/1,
506 args["space"]["advanced"]["quadrature_order"],
507 args["space"]["advanced"]["mass_quadrature_order"],
508 args["space"]["advanced"]["use_corner_quadrature"],
509 args["space"]["advanced"]["n_harmonic_samples"],
510 args["space"]["advanced"]["integral_constraints"],
514
515 logger().info("n temperature bases: {}", temperature_space_.n_bases);
516 }
517
519 {
521
523
524 temperature_problem_->setup_bc(
525 mesh,
532 /*value_dim=*/1);
533 std::vector<int> unused_neumann_boundary_nodes;
534 temperature_problem_->setup_bc(
535 mesh,
541 unused_neumann_boundary_nodes,
542 /*value_dim=*/1);
543
544 temperature_problem_->setup_nodal_bc(
545 mesh,
550 temperature_problem_->setup_nodal_bc(
551 mesh,
556
557 for (const int n_id : temperature_boundary_.dirichlet_nodes)
559
563 }
564
566 {
567 json rhs_solver_params = args["solver"]["linear"];
568 if (!rhs_solver_params.contains("Pardiso"))
569 rhs_solver_params["Pardiso"] = {};
570 rhs_solver_params["Pardiso"]["mtype"] = -2;
571
572 solve_data_.rhs_assembler = std::make_shared<assembler::RhsAssembler>(
578 args["space"]["advanced"]["bc_method"],
579 rhs_solver_params,
582
583 temperature_rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
584 *temperature_assembler_, *mesh_, nullptr,
587 temperature_space_.n_bases, /*size=*/1,
590 args["space"]["advanced"]["bc_method"],
591 rhs_solver_params,
593 }
594
596 {
597 igl::Timer timer;
598 json p_params = {};
599 p_params["formulation"] = primary_assembler_->name();
600 {
601 RowVectorNd min, max, delta;
602 mesh.bounding_box(min, max);
603 delta = (max - min) / 2. + min;
604 if (mesh.is_volume())
605 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
606 else
607 p_params["bbox_center"] = {delta(0), delta(1)};
608 }
609 problem->set_parameters(p_params, resources_);
610 temperature_problem_->set_parameters(p_params, resources_);
611
612 rhs_.resize(0, 0);
613 temperature_rhs_.resize(0, 0);
614
615 timer.start();
616 logger().info("Assigning rhs...");
617
619 assert(rhs_assembler_ != nullptr);
620 assert(temperature_rhs_assembler_ != nullptr);
621 assert(temperature_rhs_density_ != nullptr);
622 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
623 rhs_ *= -1;
625 temperature_rhs_ *= -1;
626
627 timings.assigning_rhs_time = timer.getElapsedTime();
628 logger().info(" took {}s", timings.assigning_rhs_time);
629 }
630
632 {
633 mass_.resize(0, 0);
634 pure_mass_.resize(0, 0);
635 temperature_mass_.resize(0, 0);
636 temperature_pure_mass_.resize(0, 0);
637
638 igl::Timer timer;
639 timer.start();
640 logger().info("Assembling mass mat...");
641
646
647 assert(mass_.size() > 0);
648 avg_mass_ = 0;
649 for (int k = 0; k < mass_.outerSize(); ++k)
650 for (StiffnessMatrix::InnerIterator it(mass_, k); it; ++it)
651 avg_mass_ += it.value();
652 avg_mass_ /= mass_.rows();
653 logger().info("average mass {}", avg_mass_);
654
655 if (args["solver"]["advanced"]["lump_mass_matrix"])
656 {
659 }
660
661 stacked_lumped_mass_ = block_diag(
662 pure_mass_.size() > 0 ? pure_mass_ : identity_mass(displacement_ndof()),
663 temperature_pure_mass_.size() > 0 ? temperature_pure_mass_ : identity_mass(temperature_ndof()));
664
665 timer.stop();
666 timings.assembling_mass_mat_time = timer.getElapsedTime();
667 logger().info(" took {}s", timings.assembling_mass_mat_time);
668
671 stats.mat_size = (long long)stacked_lumped_mass_.rows() * (long long)stacked_lumped_mass_.cols();
672 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
673 }
674
675 void ThermoElasticVarForm::initial_temperature_solution(Eigen::MatrixXd &solution) const
676 {
677 assert(temperature_rhs_assembler_ != nullptr);
678 temperature_rhs_assembler_->initial_solution(solution);
679 }
680
682 const Eigen::MatrixXd &displacement,
683 const Eigen::MatrixXd &temperature) const
684 {
685 assert(displacement.rows() == displacement_ndof());
686 assert(temperature.rows() == temperature_ndof());
687 assert(displacement.cols() == temperature.cols());
688
689 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
690 solution << displacement, temperature;
691 return solution;
692 }
693
695 const Eigen::MatrixXd &solution,
696 Eigen::MatrixXd &displacement,
697 Eigen::MatrixXd &temperature) const
698 {
699 assert(solution.rows() == total_ndof());
700 displacement = solution.topRows(displacement_ndof());
701 temperature = solution.bottomRows(temperature_ndof());
702 }
703
704 void ThermoElasticVarForm::build_forms(Eigen::MatrixXd &solution, const double t)
705 {
706 assert(solution.cols() == 1);
707 assert(solution.rows() == total_ndof());
708
709 const bool is_time_dependent = problem->is_time_dependent();
710 const double form_dt = is_time_dependent ? dt : 0.0;
711
712 stacked_form_ = std::make_shared<solver::StackedForm>();
713 const auto displacement_block = stacked_form_->add_block(displacement_ndof());
714 const auto temperature_block = stacked_form_->add_block(temperature_ndof());
715
716 Eigen::MatrixXd displacement, temperature;
717 split_solution(solution, displacement, temperature);
718
719 if (is_time_dependent)
720 {
724
725 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
726 initial_solution(displacement_solution);
727 displacement_solution.col(0) = displacement;
728 initial_velocity(displacement_velocity);
729 initial_acceleration(displacement_acceleration);
730 solve_data_.time_integrator->init(displacement_solution, displacement_velocity, displacement_acceleration, dt);
732 solve_data_.time_integrator, "/checkpoint/state/primary_integrator", dt,
733 "primary", space_, mesh_->dimension());
734 }
735 else
736 {
738 }
739
740 init_forms(args, mesh_->dimension(), displacement, t);
741 for (const auto &form : forms)
742 {
743 assert(form);
744 stacked_form_->add(displacement_block, form);
745 }
746 solve_data_.al_form.clear();
747
748 temperature_form_ = std::make_shared<solver::ElasticForm>(
750 *temperature_assembler_, temperature_ass_vals_cache_, t, form_dt, mesh_->is_volume(),
751 /*jacobian_threshold=*/0.0, solver::ElementInversionCheck::Discrete);
752 stacked_form_->add(temperature_block, temperature_form_);
753
755 thermoelastic_form_ = std::make_shared<solver::MixedAssemblerForm>(
759 t, form_dt, mesh_->is_volume());
760 stacked_form_->add(displacement_block, temperature_block, thermoelastic_form_);
761
762 assert(temperature_space_.disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized FE orders");
763 assert(temperature_space_.geometry && "Thermal boundary quadrature requires an initialized geometry mapping");
764 assert(temperature_space_.geometry->disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized geometry orders");
765 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
766 const QuadratureOrders temperature_boundary_samples =
768 temperature_body_form_ = std::make_shared<solver::BodyForm>(
769 temperature_ndof(), 0,
771 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
774 /*is_formulation_mixed=*/false, is_time_dependent);
775 temperature_body_form_->update_quantities(t, temperature);
776 stacked_form_->add(temperature_block, temperature_body_form_);
777
778 if (is_time_dependent)
779 {
783
784 Eigen::MatrixXd temperature_solution;
785 initial_temperature_solution(temperature_solution);
786 temperature_solution.col(0) = temperature;
787 Eigen::MatrixXd temperature_velocity = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
788 Eigen::MatrixXd temperature_acceleration = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
789 temperature_time_integrator_->init(temperature_solution, temperature_velocity, temperature_acceleration, dt);
791 temperature_time_integrator_, "/checkpoint/state/temperature_integrator", dt,
792 "temperature", temperature_space_, 1);
793
794 temperature_inertia_form_ = std::make_shared<solver::InertiaForm>(temperature_mass_, *temperature_time_integrator_);
796 {
797 temperature_inertia_form_->set_x_tilde_updater(
798 [this, temperature_boundary_samples](
799 const double t,
800 const Eigen::VectorXd &,
801 Eigen::VectorXd &target) {
802 Eigen::MatrixXd projected_target = target;
803 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
807 temperature_boundary_samples,
808 empty_neumann_boundary,
809 projected_target,
810 Eigen::MatrixXd(),
811 t);
812 assert(projected_target.cols() == 1);
813 target = projected_target.col(0);
814 });
815 }
816 stacked_form_->add(temperature_block, temperature_inertia_form_);
817
819 }
820 else
821 {
824 }
825
826 forms.clear();
827 forms.push_back(stacked_form_);
828 for (const auto &form : forms)
829 form->set_output_dir(output_path);
830
831 solve_data_.al_form.clear();
833 {
834 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
835 const auto displacement_al_block = stacked_al->add_block(displacement_block.size());
836 const auto temperature_al_block = stacked_al->add_block(temperature_block.size());
837
838 if (!boundary_.boundary_nodes.empty())
839 {
840 stacked_al->add(
841 displacement_al_block,
842 std::make_shared<solver::BCLagrangianForm>(
843 displacement_block.size(),
846 obstacle.n_vertices() * mesh_->dimension(), is_time_dependent, t));
847 }
848
850 {
851 stacked_al->add(
852 temperature_al_block,
853 std::make_shared<solver::BCLagrangianForm>(
854 temperature_block.size(),
856 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
858 /*obstacle_ndof=*/0, is_time_dependent, t));
859 }
860
861 solve_data_.al_form.push_back(stacked_al);
862 }
863 }
864
866 {
867 assert(problem->is_time_dependent());
868 assert(solve_data_.time_integrator && "Transient thermoelasticity requires a displacement time integrator");
870
872
873 const double displacement_scaling = solve_data_.time_integrator->acceleration_scaling();
874 const double temperature_scaling = temperature_time_integrator_->acceleration_scaling();
875
877 temperature_form_->set_weight(temperature_scaling);
879 temperature_body_form_->set_weight(temperature_scaling);
881 thermoelastic_form_->set_row_weights(displacement_scaling, temperature_scaling);
882 }
883
884 void ThermoElasticVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &solution)
885 {
886 assert(solve_data_.nl_problem != nullptr && "Thermoelastic forms must initialize the nonlinear problem before solving");
888
889 const json nonlinear_params = solver_params_for_residual_mode(args["solver"]["nonlinear"], nl_problem.is_residual());
890 const json al_nonlinear_params = solver_params_for_residual_mode(args["solver"]["augmented_lagrangian"]["nonlinear"], nl_problem.is_residual());
891
892 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
893 polysolve::nonlinear::Solver::create(
894 nonlinear_params, args["solver"]["linear"],
896
897 if (nl_problem.uses_lagging())
898 nl_problem.init_lagging(solution);
899
900 const auto update_displacement_barrier_stiffness = [&](const Eigen::VectorXd &x) {
901 const Eigen::VectorXd displacement = x.head(displacement_ndof());
903 };
904
905 if (!solve_data_.al_form.empty())
906 {
907 solver::ALSolver al_solver(
909 args["solver"]["augmented_lagrangian"]["initial_weight"],
910 args["solver"]["augmented_lagrangian"]["scaling"],
911 args["solver"]["augmented_lagrangian"]["max_weight"],
912 args["solver"]["augmented_lagrangian"]["eta"],
913 update_displacement_barrier_stiffness);
914
915 al_solver.post_subsolve = [&](const double al_weight) {
916 stats.solver_info.push_back(
917 {{"type", al_weight > 0 ? "al" : "rc"},
918 {"t", step},
919 {"info", nl_solver->info()}});
920 if (al_weight > 0)
921 stats.solver_info.back()["weight"] = al_weight;
922 save_subsolve(stats.solver_info.size(), step, solution);
923 };
924
925 al_solver.solve_al(
926 nl_problem, solution, al_nonlinear_params,
927 args["solver"]["linear"], units.characteristic_length(), nl_solver);
928 al_solver.solve_reduced(
929 nl_problem, solution, nonlinear_params,
930 args["solver"]["linear"], units.characteristic_length(), nl_solver);
931 return;
932 }
933
934 Eigen::VectorXd x = solution;
935 nl_problem.init(x);
936 update_displacement_barrier_stiffness(x);
937 nl_problem.normalize_forms();
938 nl_solver->minimize(nl_problem, x);
939 nl_problem.finish();
940 solution = x;
941 stats.solver_info.push_back({{"type", "rc"}, {"t", step}, {"info", nl_solver->info()}});
942 save_subsolve(stats.solver_info.size(), step, solution);
943 }
944
946 Eigen::MatrixXd &sol,
947 const InitialConditionOverride *initial_condition_override,
948 const ForwardStepCallback &post_step)
949 {
950 assert(!initial_condition_override && "Thermoelasticity does not support initial-condition overrides");
951 assert(!post_step && "Thermoelasticity does not support post-step callbacks");
952
953 stats.spectrum.setZero();
954
955 igl::Timer timer;
956 timer.start();
957 logger().info("Solving ThermoElasticity");
958
959 {
960 POLYFEM_SCOPED_TIMER("Setup RHS");
961
962 if (sol.size() <= 0)
963 {
964 Eigen::MatrixXd displacement, temperature;
965 initial_solution(displacement);
966 initial_temperature_solution(temperature);
967 const int cols = std::max(displacement.cols(), temperature.cols());
968 if (displacement.cols() != cols)
969 displacement.conservativeResize(Eigen::NoChange, cols);
970 if (temperature.cols() != cols)
971 temperature.conservativeResize(Eigen::NoChange, cols);
972 sol = stacked_solution(displacement, temperature);
973 }
974
975 if (sol.cols() > 1)
976 sol.conservativeResize(Eigen::NoChange, 1);
977 }
978
979 build_forms(sol, problem->is_time_dependent() ? t0 + dt : 1.0);
980
981 double characteristic_length = 0;
982 if (args["solver"]["advanced"]["characteristic_length"] > 0)
983 characteristic_length = args["solver"]["advanced"]["characteristic_length"];
984 else
985 {
986 RowVectorNd min, max;
987 mesh_->bounding_box(min, max);
988 characteristic_length = (max - min).norm();
989 }
990
991 double characteristic_force_density = 0;
992 if (args["solver"]["advanced"]["characteristic_force_density"] <= 0)
993 {
994 logger().warn("No user-specified force density was provided, defaulting to 10000.");
995 characteristic_force_density = 10000;
996 }
997 else
998 characteristic_force_density = args["solver"]["advanced"]["characteristic_force_density"];
999
1000 solve_data_.nl_problem = std::make_shared<solver::NLProblem>(
1001 total_ndof(), problem->is_time_dependent() ? t0 + dt : 1.0,
1003 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
1004 characteristic_length, characteristic_force_density,
1005 stacked_lumped_mass_.size() > 0 ? stacked_lumped_mass_ : identity_mass(total_ndof()),
1006 mesh_->dimension(),
1007 problem->is_time_dependent());
1008 solve_data_.nl_problem->init(sol);
1009 solve_data_.nl_problem->update_quantities(problem->is_time_dependent() ? t0 + dt : 1.0, sol);
1010 stats.solver_info = json::array();
1011
1012 if (!problem->is_time_dependent())
1013 {
1014 solve_nonlinear_step(0, sol);
1015 }
1016 else
1017 {
1018 save_timestep(t0, 0, t0, dt, sol);
1019 for (int t = 1; t <= time_steps; ++t)
1020 {
1021 const double time = t0 + dt * t;
1022 solve_nonlinear_step(t, sol);
1023
1024 save_timestep(time, t, t0, dt, sol);
1025
1026 Eigen::MatrixXd displacement, temperature;
1027 split_solution(sol, displacement, temperature);
1028 solve_data_.time_integrator->update_quantities(displacement);
1029 temperature_time_integrator_->update_quantities(temperature);
1032 solve_data_.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
1033
1034 logger().info("{}/{} t={}", t, time_steps, time);
1037 }
1038 }
1039
1040 timer.stop();
1041 timings.solving_time = timer.getElapsedTime();
1042 logger().info(" took {}s", timings.solving_time);
1043 }
1044
1046 {
1047 if (!args["output"]["advanced"]["compute_error"])
1048 return stats;
1049
1050 Eigen::MatrixXd displacement, temperature;
1051 split_solution(solution, displacement, temperature);
1052
1053 double tend = 0;
1054 if (!args["time"].is_null())
1055 tend = args["time"]["tend"];
1056
1058 return stats;
1059 }
1060
1061 std::vector<io::OutputField> ThermoElasticVarForm::output_fields(
1062 const io::OutputSample &sample,
1063 const Eigen::MatrixXd &solution,
1064 const io::OutputFieldOptions &options) const
1065 {
1066 Eigen::MatrixXd displacement, temperature;
1067 split_solution(solution, displacement, temperature);
1068
1069 std::vector<io::OutputField> fields =
1070 NonlinearElasticVarForm::output_fields(sample, displacement, options);
1071 fields.erase(
1072 std::remove_if(
1073 fields.begin(), fields.end(),
1074 [](const io::OutputField &field) {
1075 return field.name == "solution" || field.name == "solution_gradient";
1076 }),
1077 fields.end());
1078
1079 if (!mesh_ || temperature.size() <= 0)
1080 return fields;
1082 return fields;
1083
1084 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
1085 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
1086
1087 const auto has_field = [&](const std::string &name) {
1088 return std::any_of(fields.begin(), fields.end(), [&](const io::OutputField &field) {
1089 return field.name == name;
1090 });
1091 };
1092
1093 const auto append_material_fields = [&](const assembler::Assembler &assembler) {
1094 const auto &paraview_options = args["output"]["paraview"]["options"];
1095 if (!paraview_options["material"] || !has_element_samples)
1096 return;
1097
1098 const auto params = assembler.parameters();
1099 std::map<std::string, Eigen::MatrixXd> param_values;
1100 for (const auto &[p, _] : params)
1101 param_values[p].setZero(output_rows, 1);
1102
1103 for (int i = 0; i < sample.local_points.rows(); ++i)
1104 {
1105 const int element_id = sample.element_ids(i);
1106 if (element_id < 0)
1107 continue;
1108
1109 for (const auto &[p, func] : params)
1110 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
1111 }
1112
1113 for (const auto &[name, values] : param_values)
1114 if (options.export_field(name) && !has_field(name))
1115 fields.push_back({name, values, io::OutputField::Association::Point});
1116 };
1117
1118 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients = nullptr) -> bool {
1119 if (has_element_samples)
1120 {
1121 values.resize(sample.local_points.rows(), 1);
1122 if (gradients)
1123 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1124 for (int i = 0; i < sample.local_points.rows(); ++i)
1125 {
1126 const int element_id = sample.element_ids(i);
1127 if (element_id < 0)
1128 {
1129 values(i) = 0;
1130 if (gradients)
1131 gradients->row(i).setZero();
1132 continue;
1133 }
1134
1135 Eigen::MatrixXd local_sol, local_grad;
1137 *mesh_, 1, temperature_space_.basis_list(), temperature_space_.geometry_basis_list(),
1138 element_id, sample.local_points.row(i), temperature, local_sol, local_grad);
1139 values(i) = local_sol(0);
1140 if (gradients)
1141 gradients->row(i) = local_grad;
1142 }
1143
1144 if (output_rows > values.rows())
1145 {
1146 const int previous_rows = values.rows();
1147 values.conservativeResize(output_rows, Eigen::NoChange);
1148 values.bottomRows(output_rows - previous_rows).setZero();
1149 if (gradients)
1150 {
1151 gradients->conservativeResize(output_rows, Eigen::NoChange);
1152 gradients->bottomRows(output_rows - previous_rows).setZero();
1153 }
1154 }
1155 return true;
1156 }
1157
1158 if (sample.node_ids.size() > 0)
1159 {
1160 values.resize(sample.node_ids.size(), 1);
1161 for (int i = 0; i < sample.node_ids.size(); ++i)
1162 {
1163 const int node_id = sample.node_ids(i);
1164 if (node_id < 0 || node_id >= temperature.rows())
1165 return false;
1166 values(i) = temperature(node_id);
1167 }
1168 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1169 }
1170
1171 return false;
1172 };
1173
1174 const bool export_temperature_gradient =
1175 !options.fields.empty() && options.export_field("temperature_gradient");
1176 if (options.export_field("temperature") || export_temperature_gradient)
1177 {
1178 Eigen::MatrixXd values, gradients;
1179 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1180 {
1181 if (options.export_field("temperature"))
1182 fields.push_back({"temperature", values, io::OutputField::Association::Point});
1183 if (export_temperature_gradient)
1184 fields.push_back({"temperature_gradient", gradients, io::OutputField::Association::Point});
1185 }
1186 }
1187
1188 if (thermoelastic_assembler_)
1189 append_material_fields(*thermoelastic_assembler_);
1190 if (temperature_assembler_)
1191 append_material_fields(*temperature_assembler_);
1192 if (temperature_mass_assembler_)
1193 append_material_fields(*temperature_mass_assembler_);
1194
1195 return fields;
1196 }
1197} // 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: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
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:148
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:492
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 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 io::ResourceIO &resources, 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.
void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution) override
std::shared_ptr< solver::BodyForm > temperature_body_form_
std::shared_ptr< assembler::HRZMass > temperature_pure_mass_assembler_
std::shared_ptr< assembler::MixedNLAssembler > thermoelastic_assembler_
void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const override
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_
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
void save_step_state(const double t0, const double dt, const int t, const Eigen::MatrixXd &solution, const time_integrator::ImplicitTimeIntegrator *time_integrator) const
Definition VarForm.cpp:863
void write_checkpoint_ordering(io::CheckpointWriter &writer, const std::string &name, const FESpace &space) const
Definition VarForm.cpp:938
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
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:1105
void save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:1086
void restore_checkpoint_integrator(const std::shared_ptr< time_integrator::ImplicitTimeIntegrator > &integrator, const std::string &group, double dt, const std::string &ordering, const FESpace &space, int block_size) const
Definition VarForm.cpp:917
virtual void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const
Definition VarForm.cpp:897
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:185
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
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:1047
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
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:49
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
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