33#include <polysolve/linear/Solver.hpp>
34#include <polysolve/nonlinear/Solver.hpp>
46 const Eigen::MatrixXd &solution,
53 log_and_throw_error(
"Cannot checkpoint thermoelastic state without its temperature integrator.");
59 Eigen::MatrixXd &solution)
65 mesh_->dimension(), 0, solution);
73 json first_material(
const json &materials)
75 return materials.is_array() ? materials.front() : materials;
78 void disable_newton_psd_projection(
json &solver_params)
80 const auto disable_for_newton = [](
json ¶ms) {
81 if (!params.contains(
"Newton") || params[
"Newton"].is_null())
82 params[
"Newton"] = json::object();
83 params[
"Newton"][
"use_psd_projection"] =
false;
86 if (solver_params.contains(
"solver") && solver_params[
"solver"].is_array())
88 for (
json &strategy : solver_params[
"solver"])
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);
98 disable_for_newton(solver_params);
102 void assert_same_space_ids(
103 const json &materials,
104 const int displacement_space_id,
105 const int temperature_space_id)
109 if (material.at(
"displacement_space_id").get<
int>() != displacement_space_id
110 || material.at(
"temperature_space_id").get<
int>() != temperature_space_id)
117 json elastic_material_from_thermo_material(
const json &material)
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.");
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);
127 if (material.contains(
"id"))
128 elastic_material[
"id"] = material[
"id"];
129 if (material.contains(
"rho"))
130 elastic_material[
"rho"] = material[
"rho"];
132 return elastic_material;
135 json solver_params_for_residual_mode(
const json &solver_params,
const bool is_residual)
137 json params = solver_params;
139 disable_newton_psd_projection(params);
143 std::string elastic_formulation_from_thermo_materials(
const json &materials)
145 std::string formulation;
148 const json elastic_material = elastic_material_from_thermo_material(material);
149 const std::string type = elastic_material[
"type"];
150 if (formulation.empty())
152 else if (formulation != type)
153 formulation =
"MultiModels";
161 std::vector<Eigen::Triplet<double>>
entries;
162 entries.reserve(a.nonZeros() +
b.nonZeros());
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());
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());
174 out.makeCompressed();
216 const std::string &formulation,
219 const std::string &out_path)
221 for (
const std::string field : {
"pressure_boundary",
"pressure_cavity"})
223 const json::json_pointer path(
"/boundary_conditions/" + field);
224 if (
args.contains(path) && !
args.at(path).empty())
231 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
234 if (
args[
"solver"][
"advanced"][
"check_inversion"] ==
"Conservative")
236 if (
auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(
primary_assembler_))
237 elastic_assembler->set_use_robust_jacobian();
247 problem = std::make_shared<assembler::GenericTensorProblem>(
"ThermoElasticDisplacement");
249 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>(
"ThermoElasticTemperature");
253 tmp[
"is_time_dependent"] = is_time_dependent;
257 auto bc =
args[
"boundary_conditions"];
268 t0 = is_time_dependent ?
args[
"time"][
"t0"].get<
double>() : 0.0;
269 time_steps = is_time_dependent ?
args[
"time"][
"time_steps"].get<
int>() : 0;
270 dt = is_time_dependent ?
args[
"time"][
"dt"].get<
double>() : 0.0;
272 this->args[
"contact"].erase(
"_dhat_was_explicit");
277 const json material = first_material(
args.at(
"materials"));
281 log_and_throw_error(
"ThermoElasticity requires distinct displacement and temperature FE spaces.");
289 if (
args[
"materials"].is_array())
291 json materials = json::array();
292 for (
const json &material :
args[
"materials"])
293 materials.push_back(elastic_material_from_thermo_material(material));
297 return elastic_material_from_thermo_material(
args[
"materials"]);
302 const json &integrators =
args[
"time"][
"integrator"];
303 if (!integrators.is_array())
306 for (
const json &integrator : integrators)
308 if (integrator.value(
"fe_space", -1) == fe_space_id)
310 json copy = integrator;
311 copy.erase(
"fe_space");
353 Eigen::VectorXi displacement_orders, displacement_ordersq;
356 if (
args[
"space"][
"use_p_ref"])
360 args[
"space"][
"advanced"][
"B"],
361 args[
"space"][
"advanced"][
"h1_formula"],
362 args[
"space"][
"discr_order"],
363 args[
"space"][
"advanced"][
"discr_order_max"],
365 displacement_orders);
367 logger().info(
"min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
374 displacement_ordersq,
375 args[
"space"][
"basis_type"],
376 args[
"space"][
"poly_basis_type"],
379 args[
"space"][
"advanced"][
"quadrature_order"],
380 args[
"space"][
"advanced"][
"mass_quadrature_order"],
381 args[
"space"][
"advanced"][
"use_corner_quadrature"],
382 args[
"space"][
"advanced"][
"n_harmonic_samples"],
383 args[
"space"][
"advanced"][
"integral_constraints"],
394 logger().info(
"Building collision mesh...");
401 for (
int d = 0; d < mesh.
dimension(); ++d)
410 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
413 const int n_samples = 10;
422 logger().info(
"Building cache...");
429 logger().info(
" took {}s", timer.getElapsedTime());
455 std::vector<int> unused_neumann_boundary_nodes;
463 unused_neumann_boundary_nodes,
482 for (
int d = 0; d < mesh.
dimension(); ++d)
494 Eigen::VectorXi temperature_orders, temperature_ordersq;
502 args[
"space"][
"basis_type"],
503 args[
"space"][
"poly_basis_type"],
506 args[
"space"][
"advanced"][
"quadrature_order"],
507 args[
"space"][
"advanced"][
"mass_quadrature_order"],
508 args[
"space"][
"advanced"][
"use_corner_quadrature"],
509 args[
"space"][
"advanced"][
"n_harmonic_samples"],
510 args[
"space"][
"advanced"][
"integral_constraints"],
533 std::vector<int> unused_neumann_boundary_nodes;
541 unused_neumann_boundary_nodes,
567 json rhs_solver_params =
args[
"solver"][
"linear"];
568 if (!rhs_solver_params.contains(
"Pardiso"))
569 rhs_solver_params[
"Pardiso"] = {};
570 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
578 args[
"space"][
"advanced"][
"bc_method"],
590 args[
"space"][
"advanced"][
"bc_method"],
603 delta = (max - min) / 2. + min;
605 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
607 p_params[
"bbox_center"] = {delta(0), delta(1)};
616 logger().info(
"Assigning rhs...");
640 logger().info(
"Assembling mass mat...");
647 assert(
mass_.size() > 0);
649 for (
int k = 0; k <
mass_.outerSize(); ++k)
650 for (StiffnessMatrix::InnerIterator it(
mass_, k); it; ++it)
655 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
682 const Eigen::MatrixXd &displacement,
683 const Eigen::MatrixXd &temperature)
const
687 assert(displacement.cols() == temperature.cols());
689 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
690 solution << displacement, temperature;
695 const Eigen::MatrixXd &solution,
696 Eigen::MatrixXd &displacement,
697 Eigen::MatrixXd &temperature)
const
706 assert(solution.cols() == 1);
709 const bool is_time_dependent =
problem->is_time_dependent();
710 const double form_dt = is_time_dependent ?
dt : 0.0;
716 Eigen::MatrixXd displacement, temperature;
719 if (is_time_dependent)
725 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
727 displacement_solution.col(0) = displacement;
741 for (
const auto &form :
forms)
759 t, form_dt,
mesh_->is_volume());
764 assert(
temperature_space_.
geometry->disc_orders.size() > 0 &&
"Thermal boundary quadrature requires initialized geometry orders");
765 const int gdiscr_order =
mesh_->orders().size() <= 0 ? 1 :
mesh_->orders().maxCoeff();
774 false, is_time_dependent);
778 if (is_time_dependent)
784 Eigen::MatrixXd temperature_solution;
786 temperature_solution.col(0) = temperature;
787 Eigen::MatrixXd temperature_velocity = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
788 Eigen::MatrixXd temperature_acceleration = Eigen::MatrixXd::Zero(temperature_solution.rows(), temperature_solution.cols());
798 [
this, temperature_boundary_samples](
800 const Eigen::VectorXd &,
801 Eigen::VectorXd &target) {
802 Eigen::MatrixXd projected_target = target;
803 const std::vector<mesh::LocalBoundary> empty_neumann_boundary;
807 temperature_boundary_samples,
808 empty_neumann_boundary,
812 assert(projected_target.cols() == 1);
813 target = projected_target.col(0);
828 for (
const auto &form :
forms)
834 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
835 const auto displacement_al_block = stacked_al->add_block(displacement_block.size());
836 const auto temperature_al_block = stacked_al->add_block(temperature_block.size());
841 displacement_al_block,
842 std::make_shared<solver::BCLagrangianForm>(
843 displacement_block.size(),
852 temperature_al_block,
853 std::make_shared<solver::BCLagrangianForm>(
854 temperature_block.size(),
858 0, is_time_dependent, t));
867 assert(
problem->is_time_dependent());
886 assert(
solve_data_.
nl_problem !=
nullptr &&
"Thermoelastic forms must initialize the nonlinear problem before solving");
889 const json nonlinear_params = solver_params_for_residual_mode(
args[
"solver"][
"nonlinear"], nl_problem.
is_residual());
890 const json al_nonlinear_params = solver_params_for_residual_mode(
args[
"solver"][
"augmented_lagrangian"][
"nonlinear"], nl_problem.
is_residual());
892 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
893 polysolve::nonlinear::Solver::create(
894 nonlinear_params,
args[
"solver"][
"linear"],
900 const auto update_displacement_barrier_stiffness = [&](
const Eigen::VectorXd &
x) {
909 args[
"solver"][
"augmented_lagrangian"][
"initial_weight"],
910 args[
"solver"][
"augmented_lagrangian"][
"scaling"],
911 args[
"solver"][
"augmented_lagrangian"][
"max_weight"],
912 args[
"solver"][
"augmented_lagrangian"][
"eta"],
913 update_displacement_barrier_stiffness);
917 {{
"type", al_weight > 0 ?
"al" :
"rc"},
919 {
"info", nl_solver->info()}});
926 nl_problem, solution, al_nonlinear_params,
929 nl_problem, solution, nonlinear_params,
934 Eigen::VectorXd
x = solution;
936 update_displacement_barrier_stiffness(
x);
938 nl_solver->minimize(nl_problem,
x);
941 stats.
solver_info.push_back({{
"type",
"rc"}, {
"t", step}, {
"info", nl_solver->info()}});
946 Eigen::MatrixXd &sol,
950 assert(!initial_condition_override &&
"Thermoelasticity does not support initial-condition overrides");
951 assert(!post_step &&
"Thermoelasticity does not support post-step callbacks");
957 logger().info(
"Solving ThermoElasticity");
964 Eigen::MatrixXd displacement, temperature;
967 const int cols = std::max(displacement.cols(), temperature.cols());
968 if (displacement.cols() != cols)
969 displacement.conservativeResize(Eigen::NoChange, cols);
970 if (temperature.cols() != cols)
971 temperature.conservativeResize(Eigen::NoChange, cols);
976 sol.conservativeResize(Eigen::NoChange, 1);
981 double characteristic_length = 0;
982 if (
args[
"solver"][
"advanced"][
"characteristic_length"] > 0)
983 characteristic_length =
args[
"solver"][
"advanced"][
"characteristic_length"];
987 mesh_->bounding_box(min, max);
988 characteristic_length = (max - min).norm();
991 double characteristic_force_density = 0;
992 if (
args[
"solver"][
"advanced"][
"characteristic_force_density"] <= 0)
994 logger().warn(
"No user-specified force density was provided, defaulting to 10000.");
995 characteristic_force_density = 10000;
998 characteristic_force_density =
args[
"solver"][
"advanced"][
"characteristic_force_density"];
1003 polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger()),
1004 characteristic_length, characteristic_force_density,
1007 problem->is_time_dependent());
1012 if (!
problem->is_time_dependent())
1021 const double time =
t0 +
dt * t;
1026 Eigen::MatrixXd displacement, temperature;
1047 if (!
args[
"output"][
"advanced"][
"compute_error"])
1050 Eigen::MatrixXd displacement, temperature;
1054 if (!
args[
"time"].is_null())
1055 tend =
args[
"time"][
"tend"];
1063 const Eigen::MatrixXd &solution,
1066 Eigen::MatrixXd displacement, temperature;
1069 std::vector<io::OutputField> fields =
1073 fields.begin(), fields.end(),
1075 return field.name ==
"solution" || field.name ==
"solution_gradient";
1079 if (!
mesh_ || temperature.size() <= 0)
1087 const auto has_field = [&](
const std::string &
name) {
1088 return std::any_of(fields.begin(), fields.end(), [&](
const io::OutputField &field) {
1089 return field.name == name;
1094 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
1095 if (!paraview_options[
"material"] || !has_element_samples)
1098 const auto params = assembler.parameters();
1099 std::map<std::string, Eigen::MatrixXd> param_values;
1100 for (
const auto &[p, _] : params)
1101 param_values[p].setZero(output_rows, 1);
1109 for (
const auto &[p, func] : params)
1113 for (
const auto &[
name, values] : param_values)
1118 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
1119 if (has_element_samples)
1121 values.resize(sample.local_points.rows(), 1);
1123 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1124 for (
int i = 0; i < sample.local_points.rows(); ++i)
1126 const int element_id = sample.element_ids(i);
1131 gradients->row(i).setZero();
1135 Eigen::MatrixXd local_sol, local_grad;
1137 *mesh_, 1, temperature_space_.basis_list(), temperature_space_.geometry_basis_list(),
1138 element_id, sample.local_points.row(i), temperature, local_sol, local_grad);
1139 values(i) = local_sol(0);
1141 gradients->row(i) = local_grad;
1144 if (output_rows > values.rows())
1146 const int previous_rows = values.rows();
1147 values.conservativeResize(output_rows, Eigen::NoChange);
1148 values.bottomRows(output_rows - previous_rows).setZero();
1151 gradients->conservativeResize(output_rows, Eigen::NoChange);
1152 gradients->bottomRows(output_rows - previous_rows).setZero();
1158 if (sample.node_ids.size() > 0)
1160 values.resize(sample.node_ids.size(), 1);
1161 for (
int i = 0; i < sample.node_ids.size(); ++i)
1163 const int node_id = sample.node_ids(i);
1164 if (node_id < 0 || node_id >= temperature.rows())
1166 values(i) = temperature(node_id);
1168 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1174 const bool export_temperature_gradient =
1175 !options.fields.empty() && options.export_field(
"temperature_gradient");
1176 if (options.export_field(
"temperature") || export_temperature_gradient)
1178 Eigen::MatrixXd values, gradients;
1179 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1181 if (options.export_field(
"temperature"))
1183 if (export_temperature_gradient)
1188 if (thermoelastic_assembler_)
1189 append_material_fields(*thermoelastic_assembler_);
1190 if (temperature_assembler_)
1191 append_material_fields(*temperature_assembler_);
1192 if (temperature_mass_assembler_)
1193 append_material_fields(*temperature_mass_assembler_);
std::vector< Eigen::Triplet< double > > entries
#define POLYFEM_SCOPED_TIMER(...)
double characteristic_length() const
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
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
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
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...
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.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
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.
int dimension() const
utily for dimension
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
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
void solve_reduced(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
std::function< void(const double)> post_subsolve
void solve_al(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
bool uses_lagging() const
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 ¶ms, DynamicOrder dynamic_order=DynamicOrder::Second)
Factory method for constructing an implicit time integrator.
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.
spdlog::logger & logger()
Retrieves the current logger.
std::array< int, 2 > QuadratureOrders
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
bool export_field(const std::string &field) const
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points