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)
224 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
227 if (
args[
"solver"][
"advanced"][
"check_inversion"] ==
"Conservative")
229 if (
auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(
primary_assembler_))
230 elastic_assembler->set_use_robust_jacobian();
240 problem = std::make_shared<assembler::GenericTensorProblem>(
"ThermoElasticDisplacement");
242 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>(
"ThermoElasticTemperature");
246 tmp[
"is_time_dependent"] = is_time_dependent;
250 auto bc =
args[
"boundary_conditions"];
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;
265 this->args[
"contact"].erase(
"_dhat_was_explicit");
270 const json material = first_material(
args.at(
"materials"));
274 log_and_throw_error(
"ThermoElasticity requires distinct displacement and temperature FE spaces.");
282 if (
args[
"materials"].is_array())
284 json materials = json::array();
285 for (
const json &material :
args[
"materials"])
286 materials.push_back(elastic_material_from_thermo_material(material));
290 return elastic_material_from_thermo_material(
args[
"materials"]);
295 const json &integrators =
args[
"time"][
"integrator"];
296 if (!integrators.is_array())
299 for (
const json &integrator : integrators)
301 if (integrator.value(
"fe_space", -1) == fe_space_id)
303 json copy = integrator;
304 copy.erase(
"fe_space");
346 Eigen::VectorXi displacement_orders, displacement_ordersq;
349 if (
args[
"space"][
"use_p_ref"])
353 args[
"space"][
"advanced"][
"B"],
354 args[
"space"][
"advanced"][
"h1_formula"],
355 args[
"space"][
"discr_order"],
356 args[
"space"][
"advanced"][
"discr_order_max"],
358 displacement_orders);
360 logger().info(
"min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
367 displacement_ordersq,
368 args[
"space"][
"basis_type"],
369 args[
"space"][
"poly_basis_type"],
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"],
387 logger().info(
"Building collision mesh...");
394 for (
int d = 0; d < mesh.
dimension(); ++d)
403 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
406 const int n_samples = 10;
415 logger().info(
"Building cache...");
422 logger().info(
" took {}s", timer.getElapsedTime());
448 std::vector<int> unused_neumann_boundary_nodes;
456 unused_neumann_boundary_nodes,
475 for (
int d = 0; d < mesh.
dimension(); ++d)
487 Eigen::VectorXi temperature_orders, temperature_ordersq;
495 args[
"space"][
"basis_type"],
496 args[
"space"][
"poly_basis_type"],
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"],
526 std::vector<int> unused_neumann_boundary_nodes;
534 unused_neumann_boundary_nodes,
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;
571 args[
"space"][
"advanced"][
"bc_method"],
583 args[
"space"][
"advanced"][
"bc_method"],
596 delta = (max - min) / 2. + min;
598 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
600 p_params[
"bbox_center"] = {delta(0), delta(1)};
609 logger().info(
"Assigning rhs...");
633 logger().info(
"Assembling mass mat...");
640 assert(
mass_.size() > 0);
642 for (
int k = 0; k <
mass_.outerSize(); ++k)
643 for (StiffnessMatrix::InnerIterator it(
mass_, k); it; ++it)
648 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
675 const Eigen::MatrixXd &displacement,
676 const Eigen::MatrixXd &temperature)
const
680 assert(displacement.cols() == temperature.cols());
682 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
683 solution << displacement, temperature;
688 const Eigen::MatrixXd &solution,
689 Eigen::MatrixXd &displacement,
690 Eigen::MatrixXd &temperature)
const
699 assert(solution.cols() == 1);
702 const bool is_time_dependent =
problem->is_time_dependent();
703 const double form_dt = is_time_dependent ?
dt : 0.0;
709 Eigen::MatrixXd displacement, temperature;
712 if (is_time_dependent)
718 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
720 displacement_solution.col(0) = displacement;
734 for (
const auto &form :
forms)
752 t, form_dt,
mesh_->is_volume());
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();
767 false, is_time_dependent);
771 if (is_time_dependent)
777 Eigen::MatrixXd 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());
791 [
this, temperature_boundary_samples](
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,
805 assert(projected_target.cols() == 1);
806 target = projected_target.col(0);
821 for (
const auto &form :
forms)
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());
834 displacement_al_block,
835 std::make_shared<solver::BCLagrangianForm>(
836 displacement_block.size(),
845 temperature_al_block,
846 std::make_shared<solver::BCLagrangianForm>(
847 temperature_block.size(),
851 0, is_time_dependent, t));
860 assert(
problem->is_time_dependent());
879 assert(
solve_data_.
nl_problem !=
nullptr &&
"Thermoelastic forms must initialize the nonlinear problem before solving");
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());
885 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
886 polysolve::nonlinear::Solver::create(
887 nonlinear_params,
args[
"solver"][
"linear"],
893 const auto update_displacement_barrier_stiffness = [&](
const Eigen::VectorXd &
x) {
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);
910 {{
"type", al_weight > 0 ?
"al" :
"rc"},
912 {
"info", nl_solver->info()}});
919 nl_problem, solution, al_nonlinear_params,
922 nl_problem, solution, nonlinear_params,
927 Eigen::VectorXd
x = solution;
929 update_displacement_barrier_stiffness(
x);
931 nl_solver->minimize(nl_problem,
x);
934 stats.
solver_info.push_back({{
"type",
"rc"}, {
"t", step}, {
"info", nl_solver->info()}});
939 Eigen::MatrixXd &sol,
943 assert(!initial_condition_override &&
"Thermoelasticity does not support initial-condition overrides");
944 assert(!post_step &&
"Thermoelasticity does not support post-step callbacks");
950 logger().info(
"Solving ThermoElasticity");
957 Eigen::MatrixXd displacement, 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);
969 sol.conservativeResize(Eigen::NoChange, 1);
974 double characteristic_length = 0;
975 if (
args[
"solver"][
"advanced"][
"characteristic_length"] > 0)
976 characteristic_length =
args[
"solver"][
"advanced"][
"characteristic_length"];
980 mesh_->bounding_box(min, max);
981 characteristic_length = (max - min).norm();
984 double characteristic_force_density = 0;
985 if (
args[
"solver"][
"advanced"][
"characteristic_force_density"] <= 0)
987 logger().warn(
"No user-specified force density was provided, defaulting to 10000.");
988 characteristic_force_density = 10000;
991 characteristic_force_density =
args[
"solver"][
"advanced"][
"characteristic_force_density"];
996 polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger()),
997 characteristic_length, characteristic_force_density,
1000 problem->is_time_dependent());
1005 if (!
problem->is_time_dependent())
1014 const double time =
t0 +
dt * t;
1019 Eigen::MatrixXd displacement, temperature;
1040 if (!
args[
"output"][
"advanced"][
"compute_error"])
1043 Eigen::MatrixXd displacement, temperature;
1047 if (!
args[
"time"].is_null())
1048 tend =
args[
"time"][
"tend"];
1056 const Eigen::MatrixXd &solution,
1059 Eigen::MatrixXd displacement, temperature;
1062 std::vector<io::OutputField> fields =
1066 fields.begin(), fields.end(),
1068 return field.name ==
"solution" || field.name ==
"solution_gradient";
1072 if (!
mesh_ || temperature.size() <= 0)
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;
1087 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
1088 if (!paraview_options[
"material"] || !has_element_samples)
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);
1102 for (
const auto &[p, func] : params)
1106 for (
const auto &[
name, values] : param_values)
1111 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
1112 if (has_element_samples)
1114 values.resize(sample.local_points.rows(), 1);
1116 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1117 for (
int i = 0; i < sample.local_points.rows(); ++i)
1119 const int element_id = sample.element_ids(i);
1124 gradients->row(i).setZero();
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);
1134 gradients->row(i) = local_grad;
1137 if (output_rows > values.rows())
1139 const int previous_rows = values.rows();
1140 values.conservativeResize(output_rows, Eigen::NoChange);
1141 values.bottomRows(output_rows - previous_rows).setZero();
1144 gradients->conservativeResize(output_rows, Eigen::NoChange);
1145 gradients->bottomRows(output_rows - previous_rows).setZero();
1151 if (sample.node_ids.size() > 0)
1153 values.resize(sample.node_ids.size(), 1);
1154 for (
int i = 0; i < sample.node_ids.size(); ++i)
1156 const int node_id = sample.node_ids(i);
1157 if (node_id < 0 || node_id >= temperature.rows())
1159 values(i) = temperature(node_id);
1161 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1167 const bool export_temperature_gradient =
1168 !options.fields.empty() && options.export_field(
"temperature_gradient");
1169 if (options.export_field(
"temperature") || export_temperature_gradient)
1171 Eigen::MatrixXd values, gradients;
1172 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1174 if (options.export_field(
"temperature"))
1176 if (export_temperature_gradient)
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_);
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