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