28#include <polysolve/linear/FEMSolver.hpp>
29#include <polysolve/nonlinear/Solver.hpp>
30#include <paraviewo/VTMWriter.hpp>
31#include <spdlog/fmt/fmt.h>
41 const Eigen::MatrixXd &solution,
49 log_and_throw_error(
"Cannot checkpoint NavierStokesFSI without its mesh-motion integrator.");
54 log_and_throw_error(
"Cannot checkpoint two-mesh NavierStokesFSI without its solid integrator.");
56 if (solid_output.
mesh ==
nullptr)
60 solid_varform_->embedding_time_integrator()->serialize_checkpoint(writer,
"/checkpoint/state/solid_integrator");
66 Eigen::MatrixXd &solution)
70 const int dim =
mesh_->dimension();
86 json first_material(
const json &materials)
88 return materials.is_array() ? materials.at(0) : materials;
91 json mesh_material(
const json &material)
93 json result = material.at(
"mesh_material");
94 if (material.contains(
"id"))
95 result[
"id"] = material[
"id"];
99 json filter_fe_space_entries(
const json &
entries,
const int fe_space_id)
104 json result = json::array();
107 if (!entry.is_object())
109 result.push_back(entry);
112 if (entry.contains(
"fe_space") && entry[
"fe_space"].get<
int>() != fe_space_id)
114 json filtered = entry;
115 filtered.erase(
"fe_space");
116 result.push_back(std::move(filtered));
121 json residual_solver_params(
const json &input)
124 params[
"solver"] =
"Newton";
125 params[
"line_search"][
"method"] =
"ResidualBacktracking";
126 if (!params.contains(
"Newton") || params[
"Newton"].is_null())
127 params[
"Newton"] = json::object();
128 params[
"Newton"][
"force_psd_projection"] =
false;
129 params[
"Newton"][
"use_psd_projection"] =
true;
135 const int pressure_size,
140 const bool add_average)
142 const int mesh_offset = velocity_mass.rows() + pressure_size;
143 const int solid_offset = mesh_offset + mesh_mass.rows();
144 const int fluid_interface_offset = solid_offset + solid_mass.rows();
145 const int mesh_interface_offset = fluid_interface_offset + fluid_interface_mass.rows();
146 const int total = mesh_interface_offset + mesh_interface_mass.rows() + (add_average ? 1 : 0);
147 std::vector<Eigen::Triplet<double>>
entries;
148 entries.reserve(velocity_mass.nonZeros() + pressure_size + mesh_mass.nonZeros()
149 + solid_mass.nonZeros() + fluid_interface_mass.nonZeros()
150 + mesh_interface_mass.nonZeros() + (add_average ? 1 : 0));
151 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
152 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
153 entries.emplace_back(it.row(), it.col(), it.value());
154 for (
int i = 0; i < pressure_size; ++i)
155 entries.emplace_back(velocity_mass.rows() + i, velocity_mass.rows() + i, 1);
156 for (
int k = 0; k < mesh_mass.outerSize(); ++k)
157 for (StiffnessMatrix::InnerIterator it(mesh_mass, k); it; ++it)
158 entries.emplace_back(mesh_offset + it.row(), mesh_offset + it.col(), it.value());
159 for (
int k = 0; k < solid_mass.outerSize(); ++k)
160 for (StiffnessMatrix::InnerIterator it(solid_mass, k); it; ++it)
161 entries.emplace_back(solid_offset + it.row(), solid_offset + it.col(), it.value());
162 for (
int k = 0; k < fluid_interface_mass.outerSize(); ++k)
163 for (StiffnessMatrix::InnerIterator it(fluid_interface_mass, k); it; ++it)
164 entries.emplace_back(fluid_interface_offset + it.row(), fluid_interface_offset + it.col(), it.value());
165 for (
int k = 0; k < mesh_interface_mass.outerSize(); ++k)
166 for (StiffnessMatrix::InnerIterator it(mesh_interface_mass, k); it; ++it)
167 entries.emplace_back(mesh_interface_offset + it.row(), mesh_interface_offset + it.col(), it.value());
169 entries.emplace_back(total - 1, total - 1, 1);
172 result.makeCompressed();
178 return a.size() ==
b.size() && (a -
b).
norm() <= 1
e-10;
181 int local_edge(
const mesh::Mesh2D &mesh,
const mesh::Navigation::Index &index)
183 for (
int le = 0; le < mesh.n_face_vertices(index.face); ++le)
184 if (mesh.get_index_from_face(index.face, le).edge == index.edge)
186 log_and_throw_error(
"Unable to locate interface edge {} in element {}.", index.edge, index.face);
189 std::map<int, Eigen::VectorXd> global_basis_values(
190 const basis::ElementBases &bases,
const Eigen::MatrixXd &points)
192 std::vector<assembler::AssemblyValues> values;
193 bases.evaluate_bases(points, values);
194 std::map<int, Eigen::VectorXd> result;
195 for (
int i = 0; i < int(values.size()); ++i)
196 for (
const auto &global : bases.bases[i].global())
198 auto it = result.try_emplace(
199 global.index, Eigen::VectorXd::Zero(
points.rows()))
201 it->second += global.val * values[i].val.col(0);
206 struct ScalarTraceOperators
213 struct VectorTraceOperators
219 ScalarTraceOperators assemble_2d_trace_operators(
220 const mesh::Mesh2D &fluid_mesh,
221 const mesh::Mesh2D &solid_mesh,
222 const std::vector<std::pair<mesh::Navigation::Index, mesh::Navigation::Index>> &interface_pairs,
223 const std::vector<basis::ElementBases> &source_bases,
224 const int source_n_bases,
225 const std::vector<basis::ElementBases> &solid_bases,
226 const int solid_n_bases,
227 const int quadrature_order)
229 ScalarTraceOperators result;
230 std::map<int, int> multiplier_index;
231 std::vector<Eigen::Triplet<double>> source_entries, solid_entries;
232 for (
const auto &[fluid_index, solid_index] : interface_pairs)
234 const int fluid_edge = local_edge(fluid_mesh, fluid_index);
235 const int solid_edge = local_edge(solid_mesh, solid_index);
236 const int fluid_vertices = fluid_mesh.n_face_vertices(fluid_index.face);
237 const int solid_vertices = solid_mesh.n_face_vertices(solid_index.face);
238 if ((fluid_vertices != 3 && fluid_vertices != 4)
239 || (solid_vertices != 3 && solid_vertices != 4))
240 log_and_throw_error(
"FSI interface coupling currently supports triangular and quadrilateral 2D elements.");
242 const RowVectorNd fluid_from = fluid_mesh.point(fluid_mesh.face_vertex(fluid_index.face, fluid_edge));
243 const RowVectorNd fluid_to = fluid_mesh.point(fluid_mesh.face_vertex(fluid_index.face, (fluid_edge + 1) % fluid_vertices));
244 const RowVectorNd solid_from = solid_mesh.point(solid_mesh.face_vertex(solid_index.face, solid_edge));
245 const RowVectorNd solid_to = solid_mesh.point(solid_mesh.face_vertex(solid_index.face, (solid_edge + 1) % solid_vertices));
246 const bool same_orientation = same_point(fluid_from, solid_from) && same_point(fluid_to, solid_to);
247 const bool opposite_orientation = same_point(fluid_from, solid_to) && same_point(fluid_to, solid_from);
248 if (!same_orientation && !opposite_orientation)
250 "FSI interface edge pair ({}, {}) is only partially overlapping; conforming facets are required.",
251 fluid_index.edge, solid_index.edge);
253 Eigen::MatrixXd uv, fluid_points;
255 if (fluid_vertices == 3)
257 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points,
weights);
260 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points,
weights);
262 const Eigen::Matrix2d solid_endpoints = solid_vertices == 3
264 : utils::BoundarySampler::quad_local_node_coordinates_from_edge(solid_edge);
265 Eigen::MatrixXd solid_points(fluid_points.rows(), 2);
266 for (
int q = 0; q < solid_points.rows(); ++q)
268 const double t = uv(q, 1);
269 if (same_orientation)
270 solid_points.row(q) = (1 - t) * solid_endpoints.row(0) + t * solid_endpoints.row(1);
272 solid_points.row(q) = (1 - t) * solid_endpoints.row(1) + t * solid_endpoints.row(0);
275 const auto source_values = global_basis_values(source_bases.at(fluid_index.face), fluid_points);
276 const auto solid_values = global_basis_values(solid_bases.at(solid_index.face), solid_points);
277 for (
const auto &[source_id, multiplier_values] : source_values)
279 if (multiplier_values.cwiseAbs().maxCoeff() < 1e-12)
281 const auto [it, inserted] = multiplier_index.try_emplace(source_id, multiplier_index.size());
282 const int row = it->second;
284 result.multiplier_source_ids.push_back(source_id);
285 for (
const auto &[trial_id, trial_values] : source_values)
287 const double value = (
weights.array() * multiplier_values.array() * trial_values.array()).sum();
288 if (std::abs(value) > 1
e-14)
289 source_entries.emplace_back(row, trial_id, value);
291 for (
const auto &[trial_id, trial_values] : solid_values)
293 const double value = (
weights.array() * multiplier_values.array() * trial_values.array()).sum();
294 if (std::abs(value) > 1
e-14)
295 solid_entries.emplace_back(row, trial_id, value);
299 result.source.resize(multiplier_index.size(), source_n_bases);
300 result.source.setFromTriplets(source_entries.begin(), source_entries.end());
301 result.solid.resize(multiplier_index.size(), solid_n_bases);
302 result.solid.setFromTriplets(solid_entries.begin(), solid_entries.end());
306 VectorTraceOperators vector_trace(
307 const ScalarTraceOperators &scalar,
309 const std::vector<int> &source_dirichlet_dofs)
311 assert(scalar.source.rows() == scalar.solid.rows());
312 assert(scalar.source.rows() ==
int(scalar.multiplier_source_ids.size()));
314 std::vector<bool> is_dirichlet(scalar.source.cols() * dim,
false);
315 for (
const int dof : source_dirichlet_dofs)
316 if (dof >= 0 && dof < int(is_dirichlet.size()))
317 is_dirichlet[dof] = true;
319 std::vector<int> vector_rows(scalar.source.rows() * dim, -1);
321 for (
int row = 0; row < scalar.source.rows(); ++row)
322 for (
int d = 0; d <
dim; ++d)
323 if (!is_dirichlet[scalar.multiplier_source_ids[row] * dim + d])
324 vector_rows[row *
dim + d] = n_rows++;
327 std::vector<Eigen::Triplet<double>>
entries;
329 for (
int k = 0; k < matrix.outerSize(); ++k)
330 for (StiffnessMatrix::InnerIterator it(matrix, k); it; ++it)
331 for (
int d = 0; d <
dim; ++d)
333 const int row = vector_rows[it.row() *
dim + d];
335 entries.emplace_back(row, it.col() *
dim + d, it.value());
342 VectorTraceOperators result;
343 result.source = expand(scalar.source);
344 result.solid = expand(scalar.solid);
395 const std::string &formulation,
398 const std::string &out_path)
400 if (!
args.contains(
"time") ||
args[
"time"].is_null())
404 const json &materials =
args.at(
"materials");
405 const json material = first_material(materials);
409 log_and_throw_error(
"NavierStokesFSI requires distinct velocity, pressure, and mesh-displacement FE spaces.");
414 const std::array<std::string, 4> solid_fields{{
"fluid_geometry_id",
"solid_geometry_id",
"displacement_space_id",
"solid_material"}};
415 int present_solid_fields = 0;
416 for (
const std::string &field : solid_fields)
417 present_solid_fields += material.contains(field);
418 if (present_solid_fields != 0 && present_solid_fields !=
int(solid_fields.size()))
419 log_and_throw_error(
"Two-mesh NavierStokesFSI requires fluid_geometry_id, solid_geometry_id, displacement_space_id, and solid_material together.");
420 has_solid_ = present_solid_fields == int(solid_fields.size());
429 const std::set<int> ids{
434 log_and_throw_error(
"Two-mesh NavierStokesFSI requires distinct fluid and solid geometry IDs.");
437 if (materials.is_array())
438 for (
const json &entry : materials)
441 log_and_throw_error(
"All NavierStokesFSI materials must use the same mesh-displacement FE space.");
443 log_and_throw_error(
"All NavierStokesFSI regions must use the same mesh elastic formulation.");
444 for (
const std::string &field : solid_fields)
446 log_and_throw_error(
"All NavierStokesFSI materials must consistently enable the two-mesh solid fields.");
448 log_and_throw_error(
"All NavierStokesFSI materials must use the same geometry IDs, solid FE space, and solid formulation.");
451 if (
args.at(
"space").at(
"discr_order").is_array())
454 for (
const json &entry :
args.at(
"space").at(
"discr_order"))
457 log_and_throw_error(
"NavierStokesFSI discretization orders must name the mesh-displacement FE space.");
464 std::make_shared<assembler::NavierStokesFSIVelocity>(),
465 std::make_shared<assembler::NavierStokesFSIMixed>(),
466 std::make_shared<assembler::NavierStokesFSIPressure>(),
467 std::make_shared<assembler::NavierStokesFSIInertia>()};
472 auto boundary_conditions =
args[
"boundary_conditions"];
488 if (
args[
"materials"].is_array())
490 json result = json::array();
491 for (
const json &material :
args[
"materials"])
492 result.push_back(mesh_material(material));
495 return mesh_material(
args[
"materials"]);
501 const json material = first_material(
args.at(
"materials"));
502 result[
"materials"] = material.at(
"solid_material");
503 result.erase(
"preset_problem");
505 if (result[
"space"][
"discr_order"].is_array())
506 result[
"space"][
"discr_order"] = filter_fe_space_entries(
509 for (
const char *key : {
510 "rhs",
"dirichlet_boundary",
"neumann_boundary",
511 "nodal_neumann_boundary",
"normal_aligned_neumann_boundary"})
513 if (result[
"boundary_conditions"].contains(key))
514 result[
"boundary_conditions"][key] = filter_fe_space_entries(
517 result[
"boundary_conditions"][
"pressure_boundary"] = json::array();
518 result[
"boundary_conditions"][
"pressure_cavity"] = json::array();
520 for (
const char *key : {
"solution",
"velocity",
"acceleration"})
521 if (result[
"initial_conditions"].contains(key))
522 result[
"initial_conditions"][key] = filter_fe_space_entries(
526 result[
"constraints"][
"hard"] = json::array();
527 result[
"constraints"][
"soft"] = json::array();
528 result[
"space"][
"remesh"][
"enabled"] =
false;
530 result[
"output"][
"advanced"][
"timestep_prefix"] =
531 "solid_" + result[
"output"][
"advanced"][
"timestep_prefix"].get<std::string>();
537 const json &integrators =
args[
"time"][
"integrator"];
538 if (!integrators.is_array())
540 for (
const json &integrator : integrators)
541 if (integrator.value(
"fe_space", -1) == fe_space_id)
543 json result = integrator;
544 result.erase(
"fe_space");
554 std::unique_ptr<mesh::Mesh> fluid_mesh, solid_mesh;
559 fluid_mesh = mesh.
copy();
565 auto pieces = mesh.
split();
566 if (pieces.size() != 2)
567 log_and_throw_error(
"Two-mesh NavierStokesFSI expected exactly two geometry partitions, got {}.", pieces.size());
568 for (
auto &piece : pieces)
571 fluid_mesh = std::move(piece.mesh);
573 solid_mesh = std::move(piece.mesh);
578 if (!fluid_mesh || !solid_mesh)
580 "Unable to find configured fluid/solid geometry IDs {}/{}.",
583 if (fluid_mesh->dimension() == 2)
589 log_and_throw_error(
"Configured 2D fluid and solid geometries do not share an interface.");
597 log_and_throw_error(
"Configured 3D fluid and solid geometries do not share an interface.");
600 mesh_ = std::move(fluid_mesh);
605 std::vector<int> body_ids(
mesh_->n_elements());
606 for (
int e = 0; e <
mesh_->n_elements(); ++e)
607 body_ids[e] =
mesh_->get_body_id(e);
610 assembler->set_size(
mesh_->dimension());
611 assembler->set_materials(body_ids, this->args[
"materials"],
units,
resources_);
631 Eigen::VectorXi orders, ordersq;
634 mesh, iso_parametric, orders, ordersq,
635 args[
"space"][
"basis_type"],
args[
"space"][
"poly_basis_type"],
637 args[
"space"][
"advanced"][
"quadrature_order"],
638 args[
"space"][
"advanced"][
"mass_quadrature_order"],
639 args[
"space"][
"advanced"][
"use_corner_quadrature"],
640 args[
"space"][
"advanced"][
"n_harmonic_samples"],
641 args[
"space"][
"advanced"][
"integral_constraints"],
646 <=
args[
"solver"][
"advanced"][
"cache_size"])
666 "n FSI interface multiplier dofs: physical={}, mesh={}",
674 if (
mesh_->dimension() != 2)
677 assert(solid_output.
mesh);
678 const auto &fluid_mesh =
dynamic_cast<const mesh::Mesh2D &
>(*mesh_);
679 const auto &solid_mesh =
dynamic_cast<const mesh::Mesh2D &
>(*solid_output.
mesh);
683 const ScalarTraceOperators physical = assemble_2d_trace_operators(
687 const ScalarTraceOperators computational = assemble_2d_trace_operators(
691 const VectorTraceOperators physical_vector =
693 const VectorTraceOperators computational_vector =
700 log_and_throw_error(
"The fluid-solid interface has no active FE trace degrees of freedom.");
712 std::vector<int> unused;
722 for (
int d = 0; d < mesh.
dimension(); ++d)
736 json solver_params =
args[
"solver"][
"linear"];
737 if (!solver_params.contains(
"Pardiso"))
738 solver_params[
"Pardiso"] = {};
739 solver_params[
"Pardiso"][
"mtype"] = -2;
747 args[
"space"][
"advanced"][
"bc_method"], solver_params,
803 Eigen::MatrixXd velocity, mesh_displacement, solid_displacement;
807 solid_varform_->initial_solution_for_embedding(solid_displacement,
"solid_");
819 sol.conservativeResize(Eigen::NoChange, 1);
822 const Eigen::MatrixXd input = sol;
824 const int rows = std::min<int>(input.rows(),
total_ndof());
826 sol.topRows(rows) = input.topRows(rows);
833 const int dim =
mesh_->dimension();
834 const Eigen::VectorXd velocity = sol.topRows(
primary_ndof());
836 Eigen::MatrixXd solid_displacement;
840 solid_varform_->init_forms_for_embedding(solid_displacement, t,
"solid_");
847 Eigen::MatrixXd velocity_initial_velocity, mesh_initial_velocity;
850 Eigen::MatrixXd velocity_history = velocity;
851 Eigen::MatrixXd velocity_history_velocity = velocity_initial_velocity;
852 Eigen::MatrixXd velocity_history_acceleration = Eigen::MatrixXd::Zero(
primary_ndof(), 1);
853 Eigen::MatrixXd mesh_history = mesh_displacement;
854 Eigen::MatrixXd mesh_history_velocity = mesh_initial_velocity;
856 velocity_bdf->init(velocity_history, velocity_history_velocity, velocity_history_acceleration,
dt);
857 mesh_bdf->init(mesh_history, mesh_history_velocity, mesh_history_acceleration,
dt);
868 solid_varform_->embedding_time_integrator(),
"/checkpoint/state/solid_integrator",
dt,
871 ale_form_ = std::make_shared<solver::NavierStokesFSIForm>(
877 [
this](
const int element,
const Eigen::MatrixXd &points,
const double time, Eigen::MatrixXd &value) {
878 problem->rhs(*primary_assembler_, *mesh_, element, points, time, value, velocity_space_id_);
880 const int gorder =
mesh_->orders().size() == 0 ? 1 :
mesh_->orders().maxCoeff();
884 [
this, velocity_samples](
const double time,
const Eigen::VectorXd &, Eigen::VectorXd &target) {
885 Eigen::MatrixXd projected = target;
886 const std::vector<mesh::LocalBoundary> empty_neumann;
888 target = projected.col(0);
895 std::optional<solver::StackedForm::Block> solid_block;
909 args[
"solver"][
"advanced"][
"jacobian_threshold"], check);
967 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
968 const auto velocity_al = stacked_al->add_block(
primary_ndof());
971 std::optional<solver::StackedAugmentedLagrangianForm::Block> solid_al;
979 stacked_al->add_block(1);
981 stacked_al->add(velocity_al, std::make_shared<solver::BCLagrangianForm>(
985 stacked_al->add(mesh_al, std::make_shared<solver::BCLagrangianForm>(
991 stacked_al->add(*solid_al, form);
997 polysolve::linear::Solver::create(
args[
"solver"][
"linear"],
logger()),
1023 const json nonlinear_params = residual_solver_params(
args[
"solver"][
"nonlinear"]);
1024 const json al_params = residual_solver_params(
args[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
1025 std::shared_ptr<polysolve::nonlinear::Solver> nonlinear_solver = polysolve::nonlinear::Solver::create(
1029 args[
"solver"][
"augmented_lagrangian"][
"scaling"],
1030 args[
"solver"][
"augmented_lagrangian"][
"max_weight"],
1031 args[
"solver"][
"augmented_lagrangian"][
"eta"],
1032 [
this](
const Eigen::VectorXd &
x) {
1038 stats.
solver_info.push_back({{
"type", weight > 0 ?
"al" :
"rc"}, {
"t", step}, {
"info", nonlinear_solver->info()}});
1049 Eigen::MatrixXd &sol,
1053 assert(!initial_condition_override &&
"Navier-Stokes FSI does not support initial-condition overrides");
1054 assert(!post_step &&
"Navier-Stokes FSI does not support post-step callbacks");
1061 for (
int step = 1; step <=
time_steps; ++step)
1063 const double time =
t0 + step *
dt;
1083 const double time,
const int step,
const Eigen::MatrixXd &solution)
const
1091 paraviewo::VTMWriter vtm(time);
1093 const bool solid_saved =
solid_varform_->save_timestep_for_embedding(
1097 if (!fluid_saved && !solid_saved)
1101 const std::string step_name =
args[
"output"][
"advanced"][
"timestep_prefix"];
1105 [step_name](
int i) {
return fmt::format(step_name +
"{:d}.vtm", i); },
1106 global_t,
t0,
dt,
args[
"output"][
"paraview"][
"skip_frame"].get<
int>());
1111 const Eigen::MatrixXd &solution,
1119 const int dim =
mesh_->dimension();
1120 const Eigen::MatrixXd mesh_displacement =
1122 const bool has_element_samples =
1124 const int output_rows = sample.
points.rows() > 0
1127 Eigen::MatrixXd values;
1129 if (has_element_samples)
1131 values.setZero(output_rows, dim);
1137 Eigen::MatrixXd local_value, local_gradient;
1141 element, sample.
local_points.row(i), mesh_displacement,
1142 local_value, local_gradient);
1143 for (
int d = 0; d < dim; ++d)
1144 values(i, d) = local_value(d);
1147 else if (sample.
node_ids.size() > 0)
1149 values.resize(sample.
node_ids.size(), dim);
1150 for (
int i = 0; i < sample.
node_ids.size(); ++i)
1152 const int node = sample.
node_ids(i);
1153 if (node < 0 || node * dim + dim > mesh_displacement.rows())
1155 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.
void write_mesh(const std::string &group, const mesh::Mesh &mesh)
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_conforming() const =0
if the mesh is conforming
virtual bool is_volume() const =0
checks if mesh is volume
virtual std::unique_ptr< Mesh > copy() const =0
Create a copy of the mesh.
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)
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