36#include <polysolve/linear/Solver.hpp>
37#include <polysolve/nonlinear/Solver.hpp>
49 json first_material(
const json &materials)
51 return materials.is_array() ? materials.front() : materials;
54 void disable_newton_psd_projection(
json &solver_params)
56 const auto disable_for_newton = [](
json ¶ms) {
57 if (!params.contains(
"Newton") || params[
"Newton"].is_null())
58 params[
"Newton"] = json::object();
59 params[
"Newton"][
"use_psd_projection"] =
false;
62 if (solver_params.contains(
"solver") && solver_params[
"solver"].is_array())
64 for (
json &strategy : solver_params[
"solver"])
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);
74 disable_for_newton(solver_params);
78 void assert_same_space_ids(
79 const json &materials,
80 const int displacement_space_id,
81 const int temperature_space_id)
85 if (material.at(
"displacement_space_id").get<
int>() != displacement_space_id
86 || material.at(
"temperature_space_id").get<
int>() != temperature_space_id)
93 json elastic_material_from_thermo_material(
const json &material)
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.");
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);
103 if (material.contains(
"id"))
104 elastic_material[
"id"] = material[
"id"];
105 if (material.contains(
"rho"))
106 elastic_material[
"rho"] = material[
"rho"];
108 return elastic_material;
111 json solver_params_for_residual_mode(
const json &solver_params,
const bool is_residual)
113 json params = solver_params;
115 disable_newton_psd_projection(params);
119 std::string elastic_formulation_from_thermo_materials(
const json &materials)
121 std::string formulation;
124 const json elastic_material = elastic_material_from_thermo_material(material);
125 const std::string type = elastic_material[
"type"];
126 if (formulation.empty())
128 else if (formulation != type)
129 formulation =
"MultiModels";
137 std::vector<Eigen::Triplet<double>>
entries;
138 entries.reserve(a.nonZeros() +
b.nonZeros());
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());
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());
150 out.makeCompressed();
192 const std::string &formulation,
195 const std::string &out_path)
200 const bool is_time_dependent =
args.contains(
"time") && !
args[
"time"].is_null();
203 if (
args[
"solver"][
"advanced"][
"check_inversion"] ==
"Conservative")
205 if (
auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(
primary_assembler_))
206 elastic_assembler->set_use_robust_jacobian();
216 problem = std::make_shared<assembler::GenericTensorProblem>(
"ThermoElasticDisplacement");
218 temperature_problem_ = std::make_shared<assembler::GenericScalarProblem>(
"ThermoElasticTemperature");
222 tmp[
"is_time_dependent"] = is_time_dependent;
226 auto bc =
args[
"boundary_conditions"];
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;
242 this->args[
"contact"].erase(
"_dhat_was_explicit");
247 const json material = first_material(
args.at(
"materials"));
251 log_and_throw_error(
"ThermoElasticity requires distinct displacement and temperature FE spaces.");
259 if (
args[
"materials"].is_array())
261 json materials = json::array();
262 for (
const json &material :
args[
"materials"])
263 materials.push_back(elastic_material_from_thermo_material(material));
267 return elastic_material_from_thermo_material(
args[
"materials"]);
272 const json &integrators =
args[
"time"][
"integrator"];
273 if (!integrators.is_array())
276 for (
const json &integrator : integrators)
278 if (integrator.value(
"fe_space", -1) == fe_space_id)
280 json copy = integrator;
281 copy.erase(
"fe_space");
315 logger().info(
"Loading obstacles...");
331 Eigen::VectorXi displacement_orders, displacement_ordersq;
334 if (
args[
"space"][
"use_p_ref"])
338 args[
"space"][
"advanced"][
"B"],
339 args[
"space"][
"advanced"][
"h1_formula"],
340 args[
"space"][
"discr_order"],
341 args[
"space"][
"advanced"][
"discr_order_max"],
343 displacement_orders);
345 logger().info(
"min p: {} max p: {}", displacement_orders.minCoeff(), displacement_orders.maxCoeff());
352 displacement_ordersq,
353 args[
"space"][
"basis_type"],
354 args[
"space"][
"poly_basis_type"],
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"],
372 logger().info(
"Building collision mesh...");
379 for (
int d = 0; d < mesh.
dimension(); ++d)
388 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
391 const int n_samples = 10;
400 logger().info(
"Building cache...");
407 logger().info(
" took {}s", timer.getElapsedTime());
433 std::vector<int> unused_neumann_boundary_nodes;
441 unused_neumann_boundary_nodes,
460 for (
int d = 0; d < mesh.
dimension(); ++d)
472 Eigen::VectorXi temperature_orders, temperature_ordersq;
480 args[
"space"][
"basis_type"],
481 args[
"space"][
"poly_basis_type"],
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"],
511 std::vector<int> unused_neumann_boundary_nodes;
519 unused_neumann_boundary_nodes,
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;
556 args[
"space"][
"advanced"][
"bc_method"],
568 args[
"space"][
"advanced"][
"bc_method"],
582 delta = (max - min) / 2. + min;
584 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
586 p_params[
"bbox_center"] = {delta(0), delta(1)};
595 logger().info(
"Assigning rhs...");
619 logger().info(
"Assembling mass mat...");
626 assert(
mass_.size() > 0);
628 for (
int k = 0; k <
mass_.outerSize(); ++k)
629 for (StiffnessMatrix::InnerIterator it(
mass_, k); it; ++it)
634 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
663 if (!was_solution_loaded)
668 const Eigen::MatrixXd &displacement,
669 const Eigen::MatrixXd &temperature)
const
673 assert(displacement.cols() == temperature.cols());
675 Eigen::MatrixXd solution(displacement.rows() + temperature.rows(), displacement.cols());
676 solution << displacement, temperature;
681 const Eigen::MatrixXd &solution,
682 Eigen::MatrixXd &displacement,
683 Eigen::MatrixXd &temperature)
const
692 assert(solution.cols() == 1);
695 const bool is_time_dependent =
problem->is_time_dependent();
696 const double form_dt = is_time_dependent ?
dt : 0.0;
702 Eigen::MatrixXd displacement, temperature;
705 if (is_time_dependent)
711 Eigen::MatrixXd displacement_solution, displacement_velocity, displacement_acceleration;
713 displacement_solution.col(0) = displacement;
724 for (
const auto &form :
forms)
742 t, form_dt,
mesh_->is_volume());
745 const int gdiscr_order =
mesh_->orders().size() <= 0 ? 1 :
mesh_->orders().maxCoeff();
754 false, is_time_dependent);
758 if (is_time_dependent)
764 Eigen::MatrixXd 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());
775 [
this, temperature_boundary_samples](
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,
789 assert(projected_target.cols() == 1);
790 target = projected_target.col(0);
805 for (
const auto &form :
forms)
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());
818 displacement_al_block,
819 std::make_shared<solver::BCLagrangianForm>(
820 displacement_block.size(),
829 temperature_al_block,
830 std::make_shared<solver::BCLagrangianForm>(
831 temperature_block.size(),
835 0, is_time_dependent, t));
844 assert(
problem->is_time_dependent());
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());
869 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
870 polysolve::nonlinear::Solver::create(
871 nonlinear_params,
args[
"solver"][
"linear"],
877 const auto update_displacement_barrier_stiffness = [&](
const Eigen::VectorXd &
x) {
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);
894 {{
"type", al_weight > 0 ?
"al" :
"rc"},
896 {
"info", nl_solver->info()}});
903 nl_problem, solution, al_nonlinear_params,
906 nl_problem, solution, nonlinear_params,
911 Eigen::VectorXd
x = solution;
913 update_displacement_barrier_stiffness(
x);
915 nl_solver->minimize(nl_problem,
x);
918 stats.
solver_info.push_back({{
"type",
"rc"}, {
"t", step}, {
"info", nl_solver->info()}});
928 logger().info(
"Solving ThermoElasticity");
935 Eigen::MatrixXd displacement, 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);
947 sol.conservativeResize(Eigen::NoChange, 1);
952 double characteristic_length = 0;
953 if (
args[
"solver"][
"advanced"][
"characteristic_length"] > 0)
954 characteristic_length =
args[
"solver"][
"advanced"][
"characteristic_length"];
958 mesh_->bounding_box(min, max);
959 characteristic_length = (max - min).norm();
962 double characteristic_force_density = 0;
963 if (
args[
"solver"][
"advanced"][
"characteristic_force_density"] <= 0)
965 logger().warn(
"No user-specified force density was provided, defaulting to 10000.");
966 characteristic_force_density = 10000;
969 characteristic_force_density =
args[
"solver"][
"advanced"][
"characteristic_force_density"];
974 polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger()),
975 characteristic_length, characteristic_force_density,
983 if (!
problem->is_time_dependent())
992 const double time =
t0 +
dt * t;
997 Eigen::MatrixXd displacement, temperature;
1018 if (!
args[
"output"][
"advanced"][
"compute_error"])
1021 Eigen::MatrixXd displacement, temperature;
1025 if (!
args[
"time"].is_null())
1026 tend =
args[
"time"][
"tend"];
1034 const Eigen::MatrixXd &solution,
1037 Eigen::MatrixXd displacement, temperature;
1040 std::vector<io::OutputField> fields =
1044 fields.begin(), fields.end(),
1046 return field.name ==
"solution" || field.name ==
"solution_gradient";
1050 if (!
mesh_ || temperature.size() <= 0)
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;
1065 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
1066 if (!paraview_options[
"material"] || !has_element_samples)
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);
1080 for (
const auto &[p, func] : params)
1084 for (
const auto &[
name, values] : param_values)
1089 const auto sample_temperature = [&](Eigen::MatrixXd &values, Eigen::MatrixXd *gradients =
nullptr) ->
bool {
1090 if (has_element_samples)
1092 values.resize(sample.local_points.rows(), 1);
1094 gradients->resize(sample.local_points.rows(), mesh_->dimension());
1095 for (
int i = 0; i < sample.local_points.rows(); ++i)
1097 const int element_id = sample.element_ids(i);
1102 gradients->row(i).setZero();
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);
1112 gradients->row(i) = local_grad;
1115 if (output_rows > values.rows())
1117 const int previous_rows = values.rows();
1118 values.conservativeResize(output_rows, Eigen::NoChange);
1119 values.bottomRows(output_rows - previous_rows).setZero();
1122 gradients->conservativeResize(output_rows, Eigen::NoChange);
1123 gradients->bottomRows(output_rows - previous_rows).setZero();
1129 if (sample.node_ids.size() > 0)
1131 values.resize(sample.node_ids.size(), 1);
1132 for (
int i = 0; i < sample.node_ids.size(); ++i)
1134 const int node_id = sample.node_ids(i);
1135 if (node_id < 0 || node_id >= temperature.rows())
1137 values(i) = temperature(node_id);
1139 return sample.points.rows() == 0 || sample.points.rows() == values.rows();
1145 const bool export_temperature_gradient =
1146 !options.fields.empty() && options.export_field(
"temperature_gradient");
1147 if (options.export_field(
"temperature") || export_temperature_gradient)
1149 Eigen::MatrixXd values, gradients;
1150 if (sample_temperature(values, export_temperature_gradient ? &gradients : nullptr))
1152 if (options.export_field(
"temperature"))
1154 if (export_temperature_gradient)
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_);
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.
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.
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