28#include <polysolve/linear/FEMSolver.hpp>
29#include <polysolve/nonlinear/Solver.hpp>
30#include <paraviewo/VTMWriter.hpp>
31#include <spdlog/fmt/fmt.h>
41 json first_material(
const json &materials)
43 return materials.is_array() ? materials.at(0) : materials;
46 json mesh_material(
const json &material)
48 json result = material.at(
"mesh_material");
49 if (material.contains(
"id"))
50 result[
"id"] = material[
"id"];
54 json filter_fe_space_entries(
const json &
entries,
const int fe_space_id)
59 json result = json::array();
62 if (!entry.is_object())
64 result.push_back(entry);
67 if (entry.contains(
"fe_space") && entry[
"fe_space"].get<
int>() != fe_space_id)
69 json filtered = entry;
70 filtered.erase(
"fe_space");
71 result.push_back(std::move(filtered));
76 json residual_solver_params(
const json &input)
79 params[
"solver"] =
"Newton";
80 params[
"line_search"][
"method"] =
"ResidualBacktracking";
81 if (!params.contains(
"Newton") || params[
"Newton"].is_null())
82 params[
"Newton"] = json::object();
83 params[
"Newton"][
"force_psd_projection"] =
false;
84 params[
"Newton"][
"use_psd_projection"] =
true;
90 const int pressure_size,
95 const bool add_average)
97 const int mesh_offset = velocity_mass.rows() + pressure_size;
98 const int solid_offset = mesh_offset + mesh_mass.rows();
99 const int fluid_interface_offset = solid_offset + solid_mass.rows();
100 const int mesh_interface_offset = fluid_interface_offset + fluid_interface_mass.rows();
101 const int total = mesh_interface_offset + mesh_interface_mass.rows() + (add_average ? 1 : 0);
102 std::vector<Eigen::Triplet<double>>
entries;
103 entries.reserve(velocity_mass.nonZeros() + pressure_size + mesh_mass.nonZeros()
104 + solid_mass.nonZeros() + fluid_interface_mass.nonZeros()
105 + mesh_interface_mass.nonZeros() + (add_average ? 1 : 0));
106 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
107 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
108 entries.emplace_back(it.row(), it.col(), it.value());
109 for (
int i = 0; i < pressure_size; ++i)
110 entries.emplace_back(velocity_mass.rows() + i, velocity_mass.rows() + i, 1);
111 for (
int k = 0; k < mesh_mass.outerSize(); ++k)
112 for (StiffnessMatrix::InnerIterator it(mesh_mass, k); it; ++it)
113 entries.emplace_back(mesh_offset + it.row(), mesh_offset + it.col(), it.value());
114 for (
int k = 0; k < solid_mass.outerSize(); ++k)
115 for (StiffnessMatrix::InnerIterator it(solid_mass, k); it; ++it)
116 entries.emplace_back(solid_offset + it.row(), solid_offset + it.col(), it.value());
117 for (
int k = 0; k < fluid_interface_mass.outerSize(); ++k)
118 for (StiffnessMatrix::InnerIterator it(fluid_interface_mass, k); it; ++it)
119 entries.emplace_back(fluid_interface_offset + it.row(), fluid_interface_offset + it.col(), it.value());
120 for (
int k = 0; k < mesh_interface_mass.outerSize(); ++k)
121 for (StiffnessMatrix::InnerIterator it(mesh_interface_mass, k); it; ++it)
122 entries.emplace_back(mesh_interface_offset + it.row(), mesh_interface_offset + it.col(), it.value());
124 entries.emplace_back(total - 1, total - 1, 1);
127 result.makeCompressed();
133 return a.size() ==
b.size() && (a -
b).
norm() <= 1
e-10;
136 int local_edge(
const mesh::Mesh2D &mesh,
const mesh::Navigation::Index &index)
138 for (
int le = 0; le < mesh.n_face_vertices(index.face); ++le)
139 if (mesh.get_index_from_face(index.face, le).edge == index.edge)
141 log_and_throw_error(
"Unable to locate interface edge {} in element {}.", index.edge, index.face);
144 std::map<int, Eigen::VectorXd> global_basis_values(
145 const basis::ElementBases &bases,
const Eigen::MatrixXd &points)
147 std::vector<assembler::AssemblyValues> values;
148 bases.evaluate_bases(points, values);
149 std::map<int, Eigen::VectorXd> result;
150 for (
int i = 0; i < int(values.size()); ++i)
151 for (
const auto &global : bases.bases[i].global())
153 auto it = result.try_emplace(
154 global.index, Eigen::VectorXd::Zero(
points.rows()))
156 it->second += global.val * values[i].val.col(0);
161 struct ScalarTraceOperators
168 struct VectorTraceOperators
174 ScalarTraceOperators assemble_2d_trace_operators(
175 const mesh::Mesh2D &fluid_mesh,
176 const mesh::Mesh2D &solid_mesh,
177 const std::vector<std::pair<mesh::Navigation::Index, mesh::Navigation::Index>> &interface_pairs,
178 const std::vector<basis::ElementBases> &source_bases,
179 const int source_n_bases,
180 const std::vector<basis::ElementBases> &solid_bases,
181 const int solid_n_bases,
182 const int quadrature_order)
184 ScalarTraceOperators result;
185 std::map<int, int> multiplier_index;
186 std::vector<Eigen::Triplet<double>> source_entries, solid_entries;
187 for (
const auto &[fluid_index, solid_index] : interface_pairs)
189 const int fluid_edge = local_edge(fluid_mesh, fluid_index);
190 const int solid_edge = local_edge(solid_mesh, solid_index);
191 const int fluid_vertices = fluid_mesh.n_face_vertices(fluid_index.face);
192 const int solid_vertices = solid_mesh.n_face_vertices(solid_index.face);
193 if ((fluid_vertices != 3 && fluid_vertices != 4)
194 || (solid_vertices != 3 && solid_vertices != 4))
195 log_and_throw_error(
"FSI interface coupling currently supports triangular and quadrilateral 2D elements.");
197 const RowVectorNd fluid_from = fluid_mesh.point(fluid_mesh.face_vertex(fluid_index.face, fluid_edge));
198 const RowVectorNd fluid_to = fluid_mesh.point(fluid_mesh.face_vertex(fluid_index.face, (fluid_edge + 1) % fluid_vertices));
199 const RowVectorNd solid_from = solid_mesh.point(solid_mesh.face_vertex(solid_index.face, solid_edge));
200 const RowVectorNd solid_to = solid_mesh.point(solid_mesh.face_vertex(solid_index.face, (solid_edge + 1) % solid_vertices));
201 const bool same_orientation = same_point(fluid_from, solid_from) && same_point(fluid_to, solid_to);
202 const bool opposite_orientation = same_point(fluid_from, solid_to) && same_point(fluid_to, solid_from);
203 if (!same_orientation && !opposite_orientation)
205 "FSI interface edge pair ({}, {}) is only partially overlapping; conforming facets are required.",
206 fluid_index.edge, solid_index.edge);
208 Eigen::MatrixXd uv, fluid_points;
210 if (fluid_vertices == 3)
212 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points,
weights);
215 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points,
weights);
217 const Eigen::Matrix2d solid_endpoints = solid_vertices == 3
219 : utils::BoundarySampler::quad_local_node_coordinates_from_edge(solid_edge);
220 Eigen::MatrixXd solid_points(fluid_points.rows(), 2);
221 for (
int q = 0; q < solid_points.rows(); ++q)
223 const double t = uv(q, 1);
224 if (same_orientation)
225 solid_points.row(q) = (1 - t) * solid_endpoints.row(0) + t * solid_endpoints.row(1);
227 solid_points.row(q) = (1 - t) * solid_endpoints.row(1) + t * solid_endpoints.row(0);
230 const auto source_values = global_basis_values(source_bases.at(fluid_index.face), fluid_points);
231 const auto solid_values = global_basis_values(solid_bases.at(solid_index.face), solid_points);
232 for (
const auto &[source_id, multiplier_values] : source_values)
234 if (multiplier_values.cwiseAbs().maxCoeff() < 1e-12)
236 const auto [it, inserted] = multiplier_index.try_emplace(source_id, multiplier_index.size());
237 const int row = it->second;
239 result.multiplier_source_ids.push_back(source_id);
240 for (
const auto &[trial_id, trial_values] : source_values)
242 const double value = (
weights.array() * multiplier_values.array() * trial_values.array()).sum();
243 if (std::abs(value) > 1
e-14)
244 source_entries.emplace_back(row, trial_id, value);
246 for (
const auto &[trial_id, trial_values] : solid_values)
248 const double value = (
weights.array() * multiplier_values.array() * trial_values.array()).sum();
249 if (std::abs(value) > 1
e-14)
250 solid_entries.emplace_back(row, trial_id, value);
254 result.source.resize(multiplier_index.size(), source_n_bases);
255 result.source.setFromTriplets(source_entries.begin(), source_entries.end());
256 result.solid.resize(multiplier_index.size(), solid_n_bases);
257 result.solid.setFromTriplets(solid_entries.begin(), solid_entries.end());
261 VectorTraceOperators vector_trace(
262 const ScalarTraceOperators &scalar,
264 const std::vector<int> &source_dirichlet_dofs)
266 assert(scalar.source.rows() == scalar.solid.rows());
267 assert(scalar.source.rows() ==
int(scalar.multiplier_source_ids.size()));
269 std::vector<bool> is_dirichlet(scalar.source.cols() * dim,
false);
270 for (
const int dof : source_dirichlet_dofs)
271 if (dof >= 0 && dof < int(is_dirichlet.size()))
272 is_dirichlet[dof] = true;
274 std::vector<int> vector_rows(scalar.source.rows() * dim, -1);
276 for (
int row = 0; row < scalar.source.rows(); ++row)
277 for (
int d = 0; d <
dim; ++d)
278 if (!is_dirichlet[scalar.multiplier_source_ids[row] * dim + d])
279 vector_rows[row *
dim + d] = n_rows++;
282 std::vector<Eigen::Triplet<double>>
entries;
284 for (
int k = 0; k < matrix.outerSize(); ++k)
285 for (StiffnessMatrix::InnerIterator it(matrix, k); it; ++it)
286 for (
int d = 0; d <
dim; ++d)
288 const int row = vector_rows[it.row() *
dim + d];
290 entries.emplace_back(row, it.col() *
dim + d, it.value());
297 VectorTraceOperators result;
298 result.source = expand(scalar.source);
299 result.solid = expand(scalar.solid);
350 const std::string &formulation,
353 const std::string &out_path)
355 if (!
args.contains(
"time") ||
args[
"time"].is_null())
359 const json &materials =
args.at(
"materials");
360 const json material = first_material(materials);
364 log_and_throw_error(
"NavierStokesFSI requires distinct velocity, pressure, and mesh-displacement FE spaces.");
369 const std::array<std::string, 4> solid_fields{{
"fluid_geometry_id",
"solid_geometry_id",
"displacement_space_id",
"solid_material"}};
370 int present_solid_fields = 0;
371 for (
const std::string &field : solid_fields)
372 present_solid_fields += material.contains(field);
373 if (present_solid_fields != 0 && present_solid_fields !=
int(solid_fields.size()))
374 log_and_throw_error(
"Two-mesh NavierStokesFSI requires fluid_geometry_id, solid_geometry_id, displacement_space_id, and solid_material together.");
375 has_solid_ = present_solid_fields == int(solid_fields.size());
384 const std::set<int> ids{
389 log_and_throw_error(
"Two-mesh NavierStokesFSI requires distinct fluid and solid geometry IDs.");
392 if (materials.is_array())
393 for (
const json &entry : materials)
396 log_and_throw_error(
"All NavierStokesFSI materials must use the same mesh-displacement FE space.");
398 log_and_throw_error(
"All NavierStokesFSI regions must use the same mesh elastic formulation.");
399 for (
const std::string &field : solid_fields)
401 log_and_throw_error(
"All NavierStokesFSI materials must consistently enable the two-mesh solid fields.");
403 log_and_throw_error(
"All NavierStokesFSI materials must use the same geometry IDs, solid FE space, and solid formulation.");
406 if (
args.at(
"space").at(
"discr_order").is_array())
409 for (
const json &entry :
args.at(
"space").at(
"discr_order"))
412 log_and_throw_error(
"NavierStokesFSI discretization orders must name the mesh-displacement FE space.");
419 std::make_shared<assembler::NavierStokesFSIVelocity>(),
420 std::make_shared<assembler::NavierStokesFSIMixed>(),
421 std::make_shared<assembler::NavierStokesFSIPressure>(),
422 std::make_shared<assembler::NavierStokesFSIInertia>()};
427 auto boundary_conditions =
args[
"boundary_conditions"];
428 boundary_conditions[
"root_path"] =
root_path;
437 solid_varform_ = std::make_shared<NonlinearElasticTransientVarForm>();
444 if (
args[
"materials"].is_array())
446 json result = json::array();
447 for (
const json &material :
args[
"materials"])
448 result.push_back(mesh_material(material));
451 return mesh_material(
args[
"materials"]);
457 const json material = first_material(
args.at(
"materials"));
458 result[
"materials"] = material.at(
"solid_material");
459 result.erase(
"preset_problem");
461 if (result[
"space"][
"discr_order"].is_array())
462 result[
"space"][
"discr_order"] = filter_fe_space_entries(
465 for (
const char *key : {
466 "rhs",
"dirichlet_boundary",
"neumann_boundary",
467 "nodal_neumann_boundary",
"normal_aligned_neumann_boundary"})
469 if (result[
"boundary_conditions"].contains(key))
470 result[
"boundary_conditions"][key] = filter_fe_space_entries(
473 result[
"boundary_conditions"][
"pressure_boundary"] = json::array();
474 result[
"boundary_conditions"][
"pressure_cavity"] = json::array();
476 for (
const char *key : {
"solution",
"velocity",
"acceleration"})
477 if (result[
"initial_conditions"].contains(key))
478 result[
"initial_conditions"][key] = filter_fe_space_entries(
482 result[
"constraints"][
"hard"] = json::array();
483 result[
"constraints"][
"soft"] = json::array();
484 result[
"space"][
"remesh"][
"enabled"] =
false;
486 result[
"output"][
"advanced"][
"timestep_prefix"] =
487 "solid_" + result[
"output"][
"advanced"][
"timestep_prefix"].get<std::string>();
493 const json &integrators =
args[
"time"][
"integrator"];
494 if (!integrators.is_array())
496 for (
const json &integrator : integrators)
497 if (integrator.value(
"fe_space", -1) == fe_space_id)
499 json result = integrator;
500 result.erase(
"fe_space");
510 auto pieces = mesh.
split();
511 if (pieces.size() != 2)
512 log_and_throw_error(
"Two-mesh NavierStokesFSI expected exactly two geometry partitions, got {}.", pieces.size());
514 std::unique_ptr<mesh::Mesh> fluid_mesh, solid_mesh;
515 for (
auto &piece : pieces)
518 fluid_mesh = std::move(piece.mesh);
520 solid_mesh = std::move(piece.mesh);
524 if (!fluid_mesh || !solid_mesh)
526 "Unable to find configured fluid/solid geometry IDs {}/{}.",
529 if (fluid_mesh->dimension() == 2)
535 log_and_throw_error(
"Configured 2D fluid and solid geometries do not share an interface.");
543 log_and_throw_error(
"Configured 3D fluid and solid geometries do not share an interface.");
546 mesh_ = std::move(fluid_mesh);
551 std::vector<int> body_ids(
mesh_->n_elements());
552 for (
int e = 0; e <
mesh_->n_elements(); ++e)
553 body_ids[e] =
mesh_->get_body_id(e);
556 assembler->set_size(
mesh_->dimension());
557 assembler->set_materials(body_ids, this->args[
"materials"],
units,
root_path);
577 Eigen::VectorXi orders, ordersq;
580 mesh, iso_parametric, orders, ordersq,
581 args[
"space"][
"basis_type"],
args[
"space"][
"poly_basis_type"],
583 args[
"space"][
"advanced"][
"quadrature_order"],
584 args[
"space"][
"advanced"][
"mass_quadrature_order"],
585 args[
"space"][
"advanced"][
"use_corner_quadrature"],
586 args[
"space"][
"advanced"][
"n_harmonic_samples"],
587 args[
"space"][
"advanced"][
"integral_constraints"],
592 <=
args[
"solver"][
"advanced"][
"cache_size"])
612 "n FSI interface multiplier dofs: physical={}, mesh={}",
620 if (
mesh_->dimension() != 2)
623 assert(solid_output.
mesh);
624 const auto &fluid_mesh =
dynamic_cast<const mesh::Mesh2D &
>(*mesh_);
625 const auto &solid_mesh =
dynamic_cast<const mesh::Mesh2D &
>(*solid_output.
mesh);
629 const ScalarTraceOperators physical = assemble_2d_trace_operators(
633 const ScalarTraceOperators computational = assemble_2d_trace_operators(
637 const VectorTraceOperators physical_vector =
639 const VectorTraceOperators computational_vector =
646 log_and_throw_error(
"The fluid-solid interface has no active FE trace degrees of freedom.");
658 std::vector<int> unused;
668 for (
int d = 0; d < mesh.
dimension(); ++d)
682 json solver_params =
args[
"solver"][
"linear"];
683 if (!solver_params.contains(
"Pardiso"))
684 solver_params[
"Pardiso"] = {};
685 solver_params[
"Pardiso"][
"mtype"] = -2;
693 args[
"space"][
"advanced"][
"bc_method"], solver_params,
749 Eigen::MatrixXd velocity, mesh_displacement, solid_displacement;
752 state_path,
"u",
args[
"input"][
"data"][
"reorder"],
755 state_path,
"mesh_u",
args[
"input"][
"data"][
"reorder"],
757 if (!loaded_velocity)
759 if (!loaded_mesh_displacement)
762 solid_varform_->initial_solution_for_embedding(solid_displacement,
"solid_");
774 sol.conservativeResize(Eigen::NoChange, 1);
777 const Eigen::MatrixXd input = sol;
779 const int rows = std::min<int>(input.rows(),
total_ndof());
781 sol.topRows(rows) = input.topRows(rows);
788 const int dim =
mesh_->dimension();
789 const Eigen::VectorXd velocity = sol.topRows(
primary_ndof());
791 Eigen::MatrixXd solid_displacement;
795 solid_varform_->init_forms_for_embedding(solid_displacement, t,
"solid_");
802 Eigen::MatrixXd velocity_initial_velocity, mesh_initial_velocity;
805 Eigen::MatrixXd velocity_history = velocity;
806 Eigen::MatrixXd velocity_history_velocity = velocity_initial_velocity;
807 Eigen::MatrixXd velocity_history_acceleration = Eigen::MatrixXd::Zero(
primary_ndof(), 1);
808 Eigen::MatrixXd mesh_history = mesh_displacement;
809 Eigen::MatrixXd mesh_history_velocity = mesh_initial_velocity;
813 state_path,
"u",
args[
"input"][
"data"][
"reorder"],
817 state_path,
"v",
args[
"input"][
"data"][
"reorder"],
819 velocity_history_velocity.setZero(velocity_history.rows(), velocity_history.cols());
821 state_path,
"a",
args[
"input"][
"data"][
"reorder"],
823 velocity_history_acceleration.setZero(velocity_history.rows(), velocity_history.cols());
826 state_path,
"mesh_u",
args[
"input"][
"data"][
"reorder"],
830 state_path,
"mesh_v",
args[
"input"][
"data"][
"reorder"],
832 mesh_history_velocity.setZero(mesh_history.rows(), mesh_history.cols());
834 state_path,
"mesh_a",
args[
"input"][
"data"][
"reorder"],
836 mesh_history_acceleration.setZero(mesh_history.rows(), mesh_history.cols());
838 velocity_bdf->init(velocity_history, velocity_history_velocity, velocity_history_acceleration,
dt);
839 mesh_bdf->init(mesh_history, mesh_history_velocity, mesh_history_acceleration,
dt);
843 ale_form_ = std::make_shared<solver::NavierStokesFSIForm>(
849 [
this](
const int element,
const Eigen::MatrixXd &points,
const double time, Eigen::MatrixXd &value) {
850 problem->rhs(*primary_assembler_, *mesh_, element, points, time, value, velocity_space_id_);
852 const int gorder =
mesh_->orders().size() == 0 ? 1 :
mesh_->orders().maxCoeff();
856 [
this, velocity_samples](
const double time,
const Eigen::VectorXd &, Eigen::VectorXd &target) {
857 Eigen::MatrixXd projected = target;
858 const std::vector<mesh::LocalBoundary> empty_neumann;
860 target = projected.col(0);
867 std::optional<solver::StackedForm::Block> solid_block;
881 args[
"solver"][
"advanced"][
"jacobian_threshold"], check);
939 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
940 const auto velocity_al = stacked_al->add_block(
primary_ndof());
943 std::optional<solver::StackedAugmentedLagrangianForm::Block> solid_al;
951 stacked_al->add_block(1);
953 stacked_al->add(velocity_al, std::make_shared<solver::BCLagrangianForm>(
957 stacked_al->add(mesh_al, std::make_shared<solver::BCLagrangianForm>(
963 stacked_al->add(*solid_al, form);
969 polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger()),
995 const json nonlinear_params = residual_solver_params(
args[
"solver"][
"nonlinear"]);
996 const json al_params = residual_solver_params(
args[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
997 std::shared_ptr<polysolve::nonlinear::Solver> nonlinear_solver = polysolve::nonlinear::Solver::create(
1001 args[
"solver"][
"augmented_lagrangian"][
"scaling"],
1002 args[
"solver"][
"augmented_lagrangian"][
"max_weight"],
1003 args[
"solver"][
"augmented_lagrangian"][
"eta"],
1004 [
this](
const Eigen::VectorXd &
x) {
1010 stats.
solver_info.push_back({{
"type", weight > 0 ?
"al" :
"rc"}, {
"t", step}, {
"info", nonlinear_solver->info()}});
1027 for (
int step = 1; step <=
time_steps; ++step)
1029 const double time =
t0 + step *
dt;
1052 const double time,
const int step,
const Eigen::MatrixXd &solution)
const
1060 paraviewo::VTMWriter vtm(time);
1062 const bool solid_saved =
solid_varform_->save_timestep_for_embedding(
1066 if (!fluid_saved && !solid_saved)
1070 const std::string step_name =
args[
"output"][
"advanced"][
"timestep_prefix"];
1074 [step_name](
int i) {
return fmt::format(step_name +
"{:d}.vtm", i); },
1075 global_t,
t0,
dt,
args[
"output"][
"paraview"][
"skip_frame"].get<
int>());
1083 if (state_path.empty())
1086 const auto save_history = [&](
const std::string &
name,
const std::deque<Eigen::VectorXd> &history) {
1087 Eigen::MatrixXd values(history.front().size(), history.size());
1088 for (
int i = 0; i < int(history.size()); ++i)
1089 values.col(i) = history[i];
1102 if (state_path.empty())
1105 const auto save_history = [&](
const std::string &
name,
const std::deque<Eigen::VectorXd> &history) {
1106 Eigen::MatrixXd values(history.front().size(), history.size());
1107 for (
int i = 0; i < int(history.size()); ++i)
1108 values.col(i) = history[i];
1111 const auto &integrator =
solid_varform_->embedding_time_integrator();
1112 save_history(
"solid_u", integrator->x_prevs());
1113 save_history(
"solid_v", integrator->v_prevs());
1114 save_history(
"solid_a", integrator->a_prevs());
1119 const Eigen::MatrixXd &solution,
1127 const int dim =
mesh_->dimension();
1128 const Eigen::MatrixXd mesh_displacement =
1130 const bool has_element_samples =
1132 const int output_rows = sample.
points.rows() > 0
1135 Eigen::MatrixXd values;
1137 if (has_element_samples)
1139 values.setZero(output_rows, dim);
1145 Eigen::MatrixXd local_value, local_gradient;
1149 element, sample.
local_points.row(i), mesh_displacement,
1150 local_value, local_gradient);
1151 for (
int d = 0; d < dim; ++d)
1152 values(i, d) = local_value(d);
1155 else if (sample.
node_ids.size() > 0)
1157 values.resize(sample.
node_ids.size(), dim);
1158 for (
int i = 0; i < sample.
node_ids.size(); ++i)
1160 const int node = sample.
node_ids(i);
1161 if (node < 0 || node * dim + dim > mesh_displacement.rows())
1163 values.row(i) = mesh_displacement.block(node * dim, 0, dim, 1).transpose();
std::vector< Eigen::Triplet< double > > entries
double characteristic_length() const
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)
void save_pvd(const std::string &name, const std::function< std::string(int)> &vtu_names, int time_steps, double t0, double dt, int skip_frame=1) const
save a PVD of a time dependent simulation
double solving_time
time to solve
json solver_info
information of the solver, eg num iteration, time, errors, etc the informations varies depending on t...
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
virtual bool is_volume() const =0
checks if mesh is volume
std::vector< MeshWithID > split() const
Split the mesh according to its per-element geometry IDs.
int dimension() const
utily for dimension
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
std::function< void(const double)> post_subsolve
static std::shared_ptr< BDF > construct_bdf_integrator(const json ¶ms, DynamicOrder dynamic_order=DynamicOrder::Second)
Construct a BDF integrator for algorithms using BDF-specific operations.
static void quadrature_for_quad_edge(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static void quadrature_for_tri_edge(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static Eigen::Matrix2d tri_local_node_coordinates_from_edge(int le)
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
std::vector< std::pair< Navigation::Index, Navigation::Index > > compute_mesh_interface(const Mesh2D &first, const Mesh2D &second)
Pair coincident boundary edges, including nonconforming leader/follower edges.
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