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 VarForm::init(formulation, units, args, out_path);
223
224 const bool is_time_dependent = args.contains("time") && !args["time"].is_null();
225
227 if (args["solver"]["advanced"]["check_inversion"] == "Conservative")
228 {
229 if (auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(primary_assembler_))
230 elastic_assembler->set_use_robust_jacobian();
231 }
232
233 temperature_assembler_ = std::make_shared<assembler::Laplacian>("conductivity");
235 mass_assembler_ = std::make_shared<assembler::Mass>();
236 pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
237 temperature_mass_assembler_ = std::make_shared<assembler::Mass>(std::make_shared<assembler::ThermalMassDensity>());
238 temperature_pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
239
240 problem = std::make_shared<assembler::GenericTensorProblem>("ThermoElasticDisplacement");
241 problem->clear();
242 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>("ThermoElasticTemperature");
243 temperature_problem_->clear();
244
245 json tmp;
246 tmp["is_time_dependent"] = is_time_dependent;
247 problem->set_parameters(tmp, resources_);
248 temperature_problem_->set_parameters(tmp, resources_);
249
250 auto bc = args["boundary_conditions"];
251 problem->set_parameters(bc, resources_);
252 temperature_problem_->set_parameters(bc, resources_);
253 problem->set_parameters(args["initial_conditions"], resources_);
254 temperature_problem_->set_parameters(args["initial_conditions"], resources_);
255 problem->set_parameters(args["output"], resources_);
256 temperature_problem_->set_parameters(args["output"], resources_);
257
258 problem->set_units(*primary_assembler_, units);
260
261 t0 = is_time_dependent ? args["time"]["t0"].get<double>() : 0.0;
262 time_steps = is_time_dependent ? args["time"]["time_steps"].get<int>() : 0;
263 dt = is_time_dependent ? args["time"]["dt"].get<double>() : 0.0;
264 contact_dhat_was_explicit_ = args["contact"].value("_dhat_was_explicit", false);
265 this->args["contact"].erase("_dhat_was_explicit");
266 }
267
269 {
270 const json material = first_material(args.at("materials"));
271 displacement_space_id_ = material.at("displacement_space_id").get<int>();
272 temperature_space_id_ = material.at("temperature_space_id").get<int>();
274 log_and_throw_error("ThermoElasticity requires distinct displacement and temperature FE spaces.");
275
276 elastic_formulation_ = elastic_formulation_from_thermo_materials(args.at("materials"));
277 assert_same_space_ids(args.at("materials"), displacement_space_id_, temperature_space_id_);
278 }
279
281 {
282 if (args["materials"].is_array())
283 {
284 json materials = json::array();
285 for (const json &material : args["materials"])
286 materials.push_back(elastic_material_from_thermo_material(material));
287 return materials;
288 }
289
290 return elastic_material_from_thermo_material(args["materials"]);
291 }
292
294 {
295 const json &integrators = args["time"]["integrator"];
296 if (!integrators.is_array())
297 return integrators;
298
299 for (const json &integrator : integrators)
300 {
301 if (integrator.value("fe_space", -1) == fe_space_id)
302 {
303 json copy = integrator;
304 copy.erase("fe_space");
305 return copy;
306 }
307 }
308
309 log_and_throw_error("Missing time integrator for FE space {}.", fe_space_id);
310 }
311
312 void ThermoElasticVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
313 {
314 assert(mesh_);
315 std::vector<int> body_ids(mesh.n_elements());
316 for (int i = 0; i < mesh.n_elements(); ++i)
317 body_ids[i] = mesh.get_body_id(i);
318
319 const json elastic_materials = elastic_material_args();
320
321 primary_assembler_->set_size(mesh.dimension());
322 primary_assembler_->set_materials(body_ids, elastic_materials, units, resources_);
323 thermoelastic_assembler_->set_size(mesh.dimension());
324 thermoelastic_assembler_->set_materials(body_ids, args["materials"], units, resources_);
325 mass_assembler_->set_size(mesh.dimension());
326 mass_assembler_->set_materials(body_ids, elastic_materials, units, resources_);
327 pure_mass_assembler_->set_size(mass_assembler_->size());
328
329 temperature_assembler_->set_size(1);
330 temperature_assembler_->set_materials(body_ids, args["materials"], units, resources_);
331 temperature_mass_assembler_->set_size(1);
332 temperature_mass_assembler_->set_materials(body_ids, args["materials"], units, resources_);
334
335 problem->init(mesh);
336 temperature_problem_->init(mesh);
337 }
338
339 void ThermoElasticVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
340 {
341 assert(problem);
342 assert(temperature_problem_);
343 assert(primary_assembler_);
345
346 Eigen::VectorXi displacement_orders, displacement_ordersq;
347 assign_discr_orders(args["space"], displacement_space_id_, mesh, displacement_orders, displacement_ordersq);
348
349 if (args["space"]["use_p_ref"])
350 {
352 mesh,
353 args["space"]["advanced"]["B"],
354 args["space"]["advanced"]["h1_formula"],
355 args["space"]["discr_order"],
356 args["space"]["advanced"]["discr_order_max"],
357 stats,
358 displacement_orders);
359
360 logger().info("min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
361 }
362
364 mesh,
365 iso_parametric,
366 displacement_orders,
367 displacement_ordersq,
368 args["space"]["basis_type"],
369 args["space"]["poly_basis_type"],
371 mesh.dimension(),
372 args["space"]["advanced"]["quadrature_order"],
373 args["space"]["advanced"]["mass_quadrature_order"],
374 args["space"]["advanced"]["use_corner_quadrature"],
375 args["space"]["advanced"]["n_harmonic_samples"],
376 args["space"]["advanced"]["integral_constraints"],
377 space_,
378 boundary_);
379
380 problem->update_nodes(space_.space_in_node_to_node);
383
384 const int n_fe_bases = space_.n_bases;
386
387 logger().info("Building collision mesh...");
390 logger().info("Done!");
391
392 for (int i = n_fe_bases; i < space_.n_bases; ++i)
393 {
394 for (int d = 0; d < mesh.dimension(); ++d)
395 boundary_.boundary_nodes.push_back(i * mesh.dimension() + d);
396 }
398
399 build_temperature_basis(mesh, iso_parametric, args);
401
402 const auto &current_bases = space_.geometry_basis_list();
403 if (args["space"]["advanced"]["count_flipped_els"])
404 stats.count_flipped_elements(mesh, current_bases);
405
406 const int n_samples = 10;
407 stats.compute_mesh_size(mesh, current_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
408 logger().info("flipped elements {}", stats.n_flipped);
409 logger().info("h: {}", stats.mesh_size);
410
411 if (std::max(space_.n_bases, temperature_space_.n_bases) <= args["solver"]["advanced"]["cache_size"])
412 {
413 igl::Timer timer;
414 timer.start();
415 logger().info("Building cache...");
416 ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases);
417 mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
418 pure_mass_ass_vals_cache_.init(mesh.is_volume(), space_.basis_list(), current_bases, true);
422 logger().info(" took {}s", timer.getElapsedTime());
423 }
424 else
425 {
432 }
433 }
434
436 {
438
439 problem->setup_bc(
440 mesh,
447 mesh.dimension());
448 std::vector<int> unused_neumann_boundary_nodes;
449 problem->setup_bc(
450 mesh,
456 unused_neumann_boundary_nodes,
457 mesh.dimension());
458
459 problem->setup_nodal_bc(
460 mesh,
465 problem->setup_nodal_bc(
466 mesh,
471
472 for (const int n_id : boundary_.dirichlet_nodes)
473 {
474 const int tag = mesh.get_node_id(n_id);
475 for (int d = 0; d < mesh.dimension(); ++d)
476 if (problem->is_nodal_dimension_dirichlet(n_id, tag, d, displacement_space_id_))
477 boundary_.boundary_nodes.push_back(n_id * mesh.dimension() + d);
478 }
479
483 }
484
485 void ThermoElasticVarForm::build_temperature_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
486 {
487 Eigen::VectorXi temperature_orders, temperature_ordersq;
488 assign_discr_orders(args["space"], temperature_space_id_, mesh, temperature_orders, temperature_ordersq);
489
491 mesh,
492 iso_parametric,
493 temperature_orders,
494 temperature_ordersq,
495 args["space"]["basis_type"],
496 args["space"]["poly_basis_type"],
498 /*value_dim=*/1,
499 args["space"]["advanced"]["quadrature_order"],
500 args["space"]["advanced"]["mass_quadrature_order"],
501 args["space"]["advanced"]["use_corner_quadrature"],
502 args["space"]["advanced"]["n_harmonic_samples"],
503 args["space"]["advanced"]["integral_constraints"],
507
508 logger().info("n temperature bases: {}", temperature_space_.n_bases);
509 }
510
512 {
514
516
517 temperature_problem_->setup_bc(
518 mesh,
525 /*value_dim=*/1);
526 std::vector<int> unused_neumann_boundary_nodes;
527 temperature_problem_->setup_bc(
528 mesh,
534 unused_neumann_boundary_nodes,
535 /*value_dim=*/1);
536
537 temperature_problem_->setup_nodal_bc(
538 mesh,
543 temperature_problem_->setup_nodal_bc(
544 mesh,
549
550 for (const int n_id : temperature_boundary_.dirichlet_nodes)
552
556 }
557
559 {
560 json rhs_solver_params = args["solver"]["linear"];
561 if (!rhs_solver_params.contains("Pardiso"))
562 rhs_solver_params["Pardiso"] = {};
563 rhs_solver_params["Pardiso"]["mtype"] = -2;
564
565 solve_data_.rhs_assembler = std::make_shared<assembler::RhsAssembler>(
571 args["space"]["advanced"]["bc_method"],
572 rhs_solver_params,
575
576 temperature_rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
577 *temperature_assembler_, *mesh_, nullptr,
580 temperature_space_.n_bases, /*size=*/1,
583 args["space"]["advanced"]["bc_method"],
584 rhs_solver_params,
586 }
587
589 {
590 igl::Timer timer;
591 json p_params = {};
592 p_params["formulation"] = primary_assembler_->name();
593 {
594 RowVectorNd min, max, delta;
595 mesh.bounding_box(min, max);
596 delta = (max - min) / 2. + min;
597 if (mesh.is_volume())
598 p_params["bbox_center"] = {delta(0), delta(1), delta(2)};
599 else
600 p_params["bbox_center"] = {delta(0), delta(1)};
601 }
602 problem->set_parameters(p_params, resources_);
603 temperature_problem_->set_parameters(p_params, resources_);
604
605 rhs_.resize(0, 0);
606 temperature_rhs_.resize(0, 0);
607
608 timer.start();
609 logger().info("Assigning rhs...");
610
612 assert(rhs_assembler_ != nullptr);
613 assert(temperature_rhs_assembler_ != nullptr);
614 assert(temperature_rhs_density_ != nullptr);
615 rhs_assembler_->assemble(mass_assembler_->density(), rhs_);
616 rhs_ *= -1;
618 temperature_rhs_ *= -1;
619
620 timings.assigning_rhs_time = timer.getElapsedTime();
621 logger().info(" took {}s", timings.assigning_rhs_time);
622 }
623
625 {
626 mass_.resize(0, 0);
627 pure_mass_.resize(0, 0);
628 temperature_mass_.resize(0, 0);
629 temperature_pure_mass_.resize(0, 0);
630
631 igl::Timer timer;
632 timer.start();
633 logger().info("Assembling mass mat...");
634
639
640 assert(mass_.size() > 0);
641 avg_mass_ = 0;
642 for (int k = 0; k < mass_.outerSize(); ++k)
643 for (StiffnessMatrix::InnerIterator it(mass_, k); it; ++it)
644 avg_mass_ += it.value();
645 avg_mass_ /= mass_.rows();
646 logger().info("average mass {}", avg_mass_);
647
648 if (args["solver"]["advanced"]["lump_mass_matrix"])
649 {
652 }
653
654 stacked_lumped_mass_ = block_diag(
655 pure_mass_.size() > 0 ? pure_mass_ : identity_mass(displacement_ndof()),
656 temperature_pure_mass_.size() > 0 ? temperature_pure_mass_ : identity_mass(temperature_ndof()));
657
658 timer.stop();
659 timings.assembling_mass_mat_time = timer.getElapsedTime();
660 logger().info(" took {}s", timings.assembling_mass_mat_time);
661
664 stats.mat_size = (long long)stacked_lumped_mass_.rows() * (long long)stacked_lumped_mass_.cols();
665 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
666 }
667
668 void ThermoElasticVarForm::initial_temperature_solution(Eigen::MatrixXd &solution) const
669 {
670 assert(temperature_rhs_assembler_ != nullptr);
671 temperature_rhs_assembler_->initial_solution(solution);
672 }
673
675 const Eigen::MatrixXd &displacement,
676 const Eigen::MatrixXd &temperature) const
677 {
678 assert(displacement.rows() == displacement_ndof());
679 assert(temperature.rows() == temperature_ndof());
680 assert(displacement.cols() == temperature.cols());
681
682 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
683 solution << displacement, temperature;
684 return solution;
685 }
686
688 const Eigen::MatrixXd &solution,
689 Eigen::MatrixXd &displacement,
690 Eigen::MatrixXd &temperature) const
691 {
692 assert(solution.rows() == total_ndof());
693 displacement = solution.topRows(displacement_ndof());
694 temperature = solution.bottomRows(temperature_ndof());
695 }
696
697 void ThermoElasticVarForm::build_forms(Eigen::MatrixXd &solution, const double t)
698 {
699 assert(solution.cols() == 1);
700 assert(solution.rows() == total_ndof());
701
702 const bool is_time_dependent = problem->is_time_dependent();
703 const double form_dt = is_time_dependent ? dt : 0.0;
704
705 stacked_form_ = std::make_shared<solver::StackedForm>();
706 const auto displacement_block = stacked_form_->add_block(displacement_ndof());
707 const auto temperature_block = stacked_form_->add_block(temperature_ndof());
708
709 Eigen::MatrixXd displacement, temperature;
710 split_solution(solution, displacement, temperature);
711
712 if (is_time_dependent)
713 {
717
718 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
719 initial_solution(displacement_solution);
720 displacement_solution.col(0) = displacement;
721 initial_velocity(displacement_velocity);
722 initial_acceleration(displacement_acceleration);
723 solve_data_.time_integrator->init(displacement_solution, displacement_velocity, displacement_acceleration, dt);
725 solve_data_.time_integrator, "/checkpoint/state/primary_integrator", dt,
726 "primary", space_, mesh_->dimension());
727 }
728 else
729 {
731 }
732
733 init_forms(args, mesh_->dimension(), displacement, t);
734 for (const auto &form : forms)
735 {
736 assert(form);
737 stacked_form_->add(displacement_block, form);
738 }
739 solve_data_.al_form.clear();
740
741 temperature_form_ = std::make_shared<solver::ElasticForm>(
743 *temperature_assembler_, temperature_ass_vals_cache_, t, form_dt, mesh_->is_volume(),
744 /*jacobian_threshold=*/0.0, solver::ElementInversionCheck::Discrete);
745 stacked_form_->add(temperature_block, temperature_form_);
746
748 thermoelastic_form_ = std::make_shared<solver::MixedAssemblerForm>(
752 t, form_dt, mesh_->is_volume());
753 stacked_form_->add(displacement_block, temperature_block, thermoelastic_form_);
754
755 assert(temperature_space_.disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized FE orders");
756 assert(temperature_space_.geometry && "Thermal boundary quadrature requires an initialized geometry mapping");
757 assert(temperature_space_.geometry->disc_orders.size() > 0 && "Thermal boundary quadrature requires initialized geometry orders");
758 const int gdiscr_order = mesh_->orders().size() <= 0 ? 1 : mesh_->orders().maxCoeff();
759 const QuadratureOrders temperature_boundary_samples =
761 temperature_body_form_ = std::make_shared<solver::BodyForm>(
762 temperature_ndof(), 0,
764 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
767 /*is_formulation_mixed=*/false, is_time_dependent);
768 temperature_body_form_->update_quantities(t, temperature);
769 stacked_form_->add(temperature_block, temperature_body_form_);
770
771 if (is_time_dependent)
772 {
776
777 Eigen::MatrixXd temperature_solution;
778 initial_temperature_solution(temperature_solution);
779 temperature_solution.col(0) = temperature;
780 Eigen::MatrixXd temperature_velocity = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
781 Eigen::MatrixXd temperature_acceleration = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
782 temperature_time_integrator_->init(temperature_solution, temperature_velocity, temperature_acceleration, dt);
784 temperature_time_integrator_, "/checkpoint/state/temperature_integrator", dt,
785 "temperature", temperature_space_, 1);
786
787 temperature_inertia_form_ = std::make_shared<solver::InertiaForm>(temperature_mass_, *temperature_time_integrator_);
789 {
790 temperature_inertia_form_->set_x_tilde_updater(
791 [this, temperature_boundary_samples](
792 const double t,
793 const Eigen::VectorXd &,
794 Eigen::VectorXd &target) {
795 Eigen::MatrixXd projected_target = target;
796 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
800 temperature_boundary_samples,
801 empty_neumann_boundary,
802 projected_target,
803 Eigen::MatrixXd(),
804 t);
805 assert(projected_target.cols() == 1);
806 target = projected_target.col(0);
807 });
808 }
809 stacked_form_->add(temperature_block, temperature_inertia_form_);
810
812 }
813 else
814 {
817 }
818
819 forms.clear();
820 forms.push_back(stacked_form_);
821 for (const auto &form : forms)
822 form->set_output_dir(output_path);
823
824 solve_data_.al_form.clear();
826 {
827 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
828 const auto displacement_al_block = stacked_al->add_block(displacement_block.size());
829 const auto temperature_al_block = stacked_al->add_block(temperature_block.size());
830
831 if (!boundary_.boundary_nodes.empty())
832 {
833 stacked_al->add(
834 displacement_al_block,
835 std::make_shared<solver::BCLagrangianForm>(
836 displacement_block.size(),
839 obstacle.n_vertices() * mesh_->dimension(), is_time_dependent, t));
840 }
841
843 {
844 stacked_al->add(
845 temperature_al_block,
846 std::make_shared<solver::BCLagrangianForm>(
847 temperature_block.size(),
849 temperature_boundary_.local_neumann_boundary, temperature_boundary_samples,
851 /*obstacle_ndof=*/0, is_time_dependent, t));
852 }
853
854 solve_data_.al_form.push_back(stacked_al);
855 }
856 }
857
859 {
860 assert(problem->is_time_dependent());
861 assert(solve_data_.time_integrator && "Transient thermoelasticity requires a displacement time integrator");
863
865
866 const double displacement_scaling = solve_data_.time_integrator->acceleration_scaling();
867 const double temperature_scaling = temperature_time_integrator_->acceleration_scaling();
868
870 temperature_form_->set_weight(temperature_scaling);
872 temperature_body_form_->set_weight(temperature_scaling);
874 thermoelastic_form_->set_row_weights(displacement_scaling, temperature_scaling);
875 }
876
877 void ThermoElasticVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &solution)
878 {
879 assert(solve_data_.nl_problem != nullptr && "Thermoelastic forms must initialize the nonlinear problem before solving");
881
882 const json nonlinear_params = solver_params_for_residual_mode(args["solver"]["nonlinear"], nl_problem.is_residual());
883 const json al_nonlinear_params = solver_params_for_residual_mode(args["solver"]["augmented_lagrangian"]["nonlinear"], nl_problem.is_residual());
884
885 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
886 polysolve::nonlinear::Solver::create(
887 nonlinear_params, args["solver"]["linear"],
889
890 if (nl_problem.uses_lagging())
891 nl_problem.init_lagging(solution);
892
893 const auto update_displacement_barrier_stiffness = [&](const Eigen::VectorXd &x) {
894 const Eigen::VectorXd displacement = x.head(displacement_ndof());
896 };
897
898 if (!solve_data_.al_form.empty())
899 {
900 solver::ALSolver al_solver(
902 args["solver"]["augmented_lagrangian"]["initial_weight"],
903 args["solver"]["augmented_lagrangian"]["scaling"],
904 args["solver"]["augmented_lagrangian"]["max_weight"],
905 args["solver"]["augmented_lagrangian"]["eta"],
906 update_displacement_barrier_stiffness);
907
908 al_solver.post_subsolve = [&](const double al_weight) {
909 stats.solver_info.push_back(
910 {{"type", al_weight > 0 ? "al" : "rc"},
911 {"t", step},
912 {"info", nl_solver->info()}});
913 if (al_weight > 0)
914 stats.solver_info.back()["weight"] = al_weight;
915 save_subsolve(stats.solver_info.size(), step, solution);
916 };
917
918 al_solver.solve_al(
919 nl_problem, solution, al_nonlinear_params,
920 args["solver"]["linear"], units.characteristic_length(), nl_solver);
921 al_solver.solve_reduced(
922 nl_problem, solution, nonlinear_params,
923 args["solver"]["linear"], units.characteristic_length(), nl_solver);
924 return;
925 }
926
927 Eigen::VectorXd x = solution;
928 nl_problem.init(x);
929 update_displacement_barrier_stiffness(x);
930 nl_problem.normalize_forms();
931 nl_solver->minimize(nl_problem, x);
932 nl_problem.finish();
933 solution = x;
934 stats.solver_info.push_back({{"type", "rc"}, {"t", step}, {"info", nl_solver->info()}});
935 save_subsolve(stats.solver_info.size(), step, solution);
936 }
937
939 Eigen::MatrixXd &sol,
940 const InitialConditionOverride *initial_condition_override,
941 const ForwardStepCallback &post_step)
942 {
943 assert(!initial_condition_override && "Thermoelasticity does not support initial-condition overrides");
944 assert(!post_step && "Thermoelasticity does not support post-step callbacks");
945
946 stats.spectrum.setZero();
947
948 igl::Timer timer;
949 timer.start();
950 logger().info("Solving ThermoElasticity");
951
952 {
953 POLYFEM_SCOPED_TIMER("Setup RHS");
954
955 if (sol.size() <= 0)
956 {
957 Eigen::MatrixXd displacement, temperature;
958 initial_solution(displacement);
959 initial_temperature_solution(temperature);
960 const int cols = std::max(displacement.cols(), temperature.cols());
961 if (displacement.cols() != cols)
962 displacement.conservativeResize(Eigen::NoChange, cols);
963 if (temperature.cols() != cols)
964 temperature.conservativeResize(Eigen::NoChange, cols);
965 sol = stacked_solution(displacement, temperature);
966 }
967
968 if (sol.cols() > 1)
969 sol.conservativeResize(Eigen::NoChange, 1);
970 }
971
972 build_forms(sol, problem->is_time_dependent() ? t0 + dt : 1.0);
973
974 double characteristic_length = 0;
975 if (args["solver"]["advanced"]["characteristic_length"] > 0)
976 characteristic_length = args["solver"]["advanced"]["characteristic_length"];
977 else
978 {
979 RowVectorNd min, max;
980 mesh_->bounding_box(min, max);
981 characteristic_length = (max - min).norm();
982 }
983
984 double characteristic_force_density = 0;
985 if (args["solver"]["advanced"]["characteristic_force_density"] <= 0)
986 {
987 logger().warn("No user-specified force density was provided, defaulting to 10000.");
988 characteristic_force_density = 10000;
989 }
990 else
991 characteristic_force_density = args["solver"]["advanced"]["characteristic_force_density"];
992
993 solve_data_.nl_problem = std::make_shared<solver::NLProblem>(
994 total_ndof(), problem->is_time_dependent() ? t0 + dt : 1.0,
996 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
997 characteristic_length, characteristic_force_density,
998 stacked_lumped_mass_.size() > 0 ? stacked_lumped_mass_ : identity_mass(total_ndof()),
999 mesh_->dimension(),
1000 problem->is_time_dependent());
1001 solve_data_.nl_problem->init(sol);
1002 solve_data_.nl_problem->update_quantities(problem->is_time_dependent() ? t0 + dt : 1.0, sol);
1003 stats.solver_info = json::array();
1004
1005 if (!problem->is_time_dependent())
1006 {
1007 solve_nonlinear_step(0, sol);
1008 }
1009 else
1010 {
1011 save_timestep(t0, 0, t0, dt, sol);
1012 for (int t = 1; t <= time_steps; ++t)
1013 {
1014 const double time = t0 + dt * t;
1015 solve_nonlinear_step(t, sol);
1016
1017 save_timestep(time, t, t0, dt, sol);
1018
1019 Eigen::MatrixXd displacement, temperature;
1020 split_solution(sol, displacement, temperature);
1021 solve_data_.time_integrator->update_quantities(displacement);
1022 temperature_time_integrator_->update_quantities(temperature);
1025 solve_data_.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
1026
1027 logger().info("{}/{} t={}", t, time_steps, time);
1030 }
1031 }
1032
1033 timer.stop();
1034 timings.solving_time = timer.getElapsedTime();
1035 logger().info(" took {}s", timings.solving_time);
1036 }
1037
1039 {
1040 if (!args["output"]["advanced"]["compute_error"])
1041 return stats;
1042
1043 Eigen::MatrixXd displacement, temperature;
1044 split_solution(solution, displacement, temperature);
1045
1046 double tend = 0;
1047 if (!args["time"].is_null())
1048 tend = args["time"]["tend"];
1049
1051 return stats;
1052 }
1053
1054 std::vector<io::OutputField> ThermoElasticVarForm::output_fields(
1055 const io::OutputSample &sample,
1056 const Eigen::MatrixXd &solution,
1057 const io::OutputFieldOptions &options) const
1058 {
1059 Eigen::MatrixXd displacement, temperature;
1060 split_solution(solution, displacement, temperature);
1061
1062 std::vector<io::OutputField> fields =
1063 NonlinearElasticVarForm::output_fields(sample, displacement, options);
1064 fields.erase(
1065 std::remove_if(
1066 fields.begin(), fields.end(),
1067 [](const io::OutputField &field) {
1068 return field.name == "solution" || field.name == "solution_gradient";
1069 }),
1070 fields.end());
1071
1072 if (!mesh_ || temperature.size() <= 0)
1073 return fields;
1075 return fields;
1076
1077 const bool has_element_samples = sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
1078 const int output_rows = sample.points.rows() > 0 ? sample.points.rows() : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
1079
1080 const auto has_field = [&](const std::string &name) {
1081 return std::any_of(fields.begin(), fields.end(), [&](const io::OutputField &field) {
1082 return field.name == name;
1083 });
1084 };
1085
1086 const auto append_material_fields = [&](const assembler::Assembler &assembler) {
1087 const auto &paraview_options = args["output"]["paraview"]["options"];
1088 if (!paraview_options["material"] || !has_element_samples)
1089 return;
1090
1091 const auto params = assembler.parameters();
1092 std::map<std::string, Eigen::MatrixXd> param_values;
1093 for (const auto &[p, _] : params)
1094 param_values[p].setZero(output_rows, 1);
1095
1096 for (int i = 0; i < sample.local_points.rows(); ++i)
1097 {
1098 const int element_id = sample.element_ids(i);
1099 if (element_id < 0)
1100 continue;
1101
1102 for (const auto &[p, func] : params)
1103 param_values.at(p)(i) = func(sample.local_points.row(i), sample.points.row(i), sample.time, element_id);
1104 }
1105
1106 for (const auto &[name, values] : param_values)
1107 if (options.export_field(name) && !has_field(name))
1108 fields.push_back({name, values, io::OutputField::Association::Point});
1109 };
1110
1111 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients = nullptr) -> bool {
1112 if (has_element_samples)
1113 {
1114 values.resize(sample.local_points.rows(), 1);
1115 if (gradients)
1116 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1117 for (int i = 0; i < sample.local_points.rows(); ++i)
1118 {
1119 const int element_id = sample.element_ids(i);
1120 if (element_id < 0)
1121 {
1122 values(i) = 0;
1123 if (gradients)
1124 gradients->row(i).setZero();
1125 continue;
1126 }
1127
1128 Eigen::MatrixXd local_sol, local_grad;
1130 *mesh_, 1, temperature_space_.basis_list(), temperature_space_.geometry_basis_list(),
1131 element_id, sample.local_points.row(i), temperature, local_sol, local_grad);
1132 values(i) = local_sol(0);
1133 if (gradients)
1134 gradients->row(i) = local_grad;
1135 }
1136
1137 if (output_rows > values.rows())
1138 {
1139 const int previous_rows = values.rows();
1140 values.conservativeResize(output_rows, Eigen::NoChange);
1141 values.bottomRows(output_rows - previous_rows).setZero();
1142 if (gradients)
1143 {
1144 gradients->conservativeResize(output_rows, Eigen::NoChange);
1145 gradients->bottomRows(output_rows - previous_rows).setZero();
1146 }
1147 }
1148 return true;
1149 }
1150
1151 if (sample.node_ids.size() > 0)
1152 {
1153 values.resize(sample.node_ids.size(), 1);
1154 for (int i = 0; i < sample.node_ids.size(); ++i)
1155 {
1156 const int node_id = sample.node_ids(i);
1157 if (node_id < 0 || node_id >= temperature.rows())
1158 return false;
1159 values(i) = temperature(node_id);
1160 }
1161 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1162 }
1163
1164 return false;
1165 };
1166
1167 const bool export_temperature_gradient =
1168 !options.fields.empty() && options.export_field("temperature_gradient");
1169 if (options.export_field("temperature") || export_temperature_gradient)
1170 {
1171 Eigen::MatrixXd values, gradients;
1172 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1173 {
1174 if (options.export_field("temperature"))
1175 fields.push_back({"temperature", values, io::OutputField::Association::Point});
1176 if (export_temperature_gradient)
1177 fields.push_back({"temperature_gradient", gradients, io::OutputField::Association::Point});
1178 }
1179 }
1180
1181 if (thermoelastic_assembler_)
1182 append_material_fields(*thermoelastic_assembler_);
1183 if (temperature_assembler_)
1184 append_material_fields(*temperature_assembler_);
1185 if (temperature_mass_assembler_)
1186 append_material_fields(*temperature_mass_assembler_);
1187
1188 return fields;
1189 }
1190} // 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: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:261
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:273
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:265
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:268
const io::ResourceIO & resources_
Definition VarForm.hpp:283
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:53
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