PolyFEM
Loading...
Searching...
No Matches
NavierStokesFSIVarForm.cpp
Go to the documentation of this file.
2
26
27#include <igl/Timer.h>
28#include <polysolve/linear/FEMSolver.hpp>
29#include <polysolve/nonlinear/Solver.hpp>
30#include <paraviewo/VTMWriter.hpp>
31#include <spdlog/fmt/fmt.h>
32
33#include <optional>
34#include <map>
35#include <set>
36
37namespace polyfem::varform
38{
39 namespace
40 {
41 json first_material(const json &materials)
42 {
43 return materials.is_array() ? materials.at(0) : materials;
44 }
45
46 json mesh_material(const json &material)
47 {
48 json result = material.at("mesh_material");
49 if (material.contains("id"))
50 result["id"] = material["id"];
51 return result;
52 }
53
54 json filter_fe_space_entries(const json &entries, const int fe_space_id)
55 {
56 if (!entries.is_array())
57 return entries;
58
59 json result = json::array();
60 for (const json &entry : entries)
61 {
62 if (!entry.is_object())
63 {
64 result.push_back(entry);
65 continue;
66 }
67 if (entry.contains("fe_space") && entry["fe_space"].get<int>() != fe_space_id)
68 continue;
69 json filtered = entry;
70 filtered.erase("fe_space");
71 result.push_back(std::move(filtered));
72 }
73 return result;
74 }
75
76 json residual_solver_params(const json &input)
77 {
78 json params = 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;
85 return params;
86 }
87
88 StiffnessMatrix residual_mass(
89 const StiffnessMatrix &velocity_mass,
90 const int pressure_size,
91 const StiffnessMatrix &mesh_mass,
92 const StiffnessMatrix &solid_mass,
93 const StiffnessMatrix &fluid_interface_mass,
94 const StiffnessMatrix &mesh_interface_mass,
95 const bool add_average)
96 {
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());
123 if (add_average)
124 entries.emplace_back(total - 1, total - 1, 1);
125 StiffnessMatrix result(total, total);
126 result.setFromTriplets(entries.begin(), entries.end());
127 result.makeCompressed();
128 return result;
129 }
130
131 bool same_point(const RowVectorNd &a, const RowVectorNd &b)
132 {
133 return a.size() == b.size() && (a - b).norm() <= 1e-10;
134 }
135
136 int local_edge(const mesh::Mesh2D &mesh, const mesh::Navigation::Index &index)
137 {
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)
140 return le;
141 log_and_throw_error("Unable to locate interface edge {} in element {}.", index.edge, index.face);
142 }
143
144 std::map<int, Eigen::VectorXd> global_basis_values(
145 const basis::ElementBases &bases, const Eigen::MatrixXd &points)
146 {
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())
152 {
153 auto it = result.try_emplace(
154 global.index, Eigen::VectorXd::Zero(points.rows()))
155 .first;
156 it->second += global.val * values[i].val.col(0);
157 }
158 return result;
159 }
160
161 struct ScalarTraceOperators
162 {
165 std::vector<int> multiplier_source_ids;
166 };
167
168 struct VectorTraceOperators
169 {
172 };
173
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)
183 {
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)
188 {
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.");
196
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);
207
208 Eigen::MatrixXd uv, fluid_points;
209 Eigen::VectorXd weights;
210 if (fluid_vertices == 3)
212 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points, weights);
213 else
215 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points, weights);
216
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)
222 {
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);
226 else
227 solid_points.row(q) = (1 - t) * solid_endpoints.row(1) + t * solid_endpoints.row(0);
228 }
229
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)
233 {
234 if (multiplier_values.cwiseAbs().maxCoeff() < 1e-12)
235 continue;
236 const auto [it, inserted] = multiplier_index.try_emplace(source_id, multiplier_index.size());
237 const int row = it->second;
238 if (inserted)
239 result.multiplier_source_ids.push_back(source_id);
240 for (const auto &[trial_id, trial_values] : source_values)
241 {
242 const double value = (weights.array() * multiplier_values.array() * trial_values.array()).sum();
243 if (std::abs(value) > 1e-14)
244 source_entries.emplace_back(row, trial_id, value);
245 }
246 for (const auto &[trial_id, trial_values] : solid_values)
247 {
248 const double value = (weights.array() * multiplier_values.array() * trial_values.array()).sum();
249 if (std::abs(value) > 1e-14)
250 solid_entries.emplace_back(row, trial_id, value);
251 }
252 }
253 }
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());
258 return result;
259 }
260
261 VectorTraceOperators vector_trace(
262 const ScalarTraceOperators &scalar,
263 const int dim,
264 const std::vector<int> &source_dirichlet_dofs)
265 {
266 assert(scalar.source.rows() == scalar.solid.rows());
267 assert(scalar.source.rows() == int(scalar.multiplier_source_ids.size()));
268
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;
273
274 std::vector<int> vector_rows(scalar.source.rows() * dim, -1);
275 int n_rows = 0;
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++;
280
281 const auto expand = [&](const StiffnessMatrix &matrix) {
282 std::vector<Eigen::Triplet<double>> entries;
283 entries.reserve(matrix.nonZeros() * dim);
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)
287 {
288 const int row = vector_rows[it.row() * dim + d];
289 if (row >= 0)
290 entries.emplace_back(row, it.col() * dim + d, it.value());
291 }
292 StiffnessMatrix result(n_rows, matrix.cols() * dim);
293 result.setFromTriplets(entries.begin(), entries.end());
294 return result;
295 };
296
297 VectorTraceOperators result;
298 result.source = expand(scalar.source);
299 result.solid = expand(scalar.solid);
300 return result;
301 }
302 } // namespace
303
305 {
311 has_solid_ = false;
312 mesh_elastic_formulation_ = "NeoHookean";
313 solid_elastic_formulation_ = "NeoHookean";
314 solid_args_ = json();
315 solid_varform_ = nullptr;
316 interface_2d_.clear();
317 interface_3d_.clear();
324 mesh_elastic_assembler_ = nullptr;
325 mesh_mass_assembler_ = nullptr;
327 mesh_rhs_assembler_ = nullptr;
328 mesh_rhs_.resize(0, 0);
329 fluid_zero_rhs_.resize(0, 0);
330 mesh_pure_mass_.resize(0, 0);
331 interface_velocity_trace_.resize(0, 0);
333 interface_mesh_trace_.resize(0, 0);
334 interface_solid_mesh_trace_.resize(0, 0);
335 ale_assemblers_.clear();
337 fsi_forms_.clear();
338 fsi_al_forms_.clear();
339 fsi_problem_ = nullptr;
340 ale_form_ = nullptr;
341 interface_form_ = nullptr;
342 auxiliary_form_ = nullptr;
343 mesh_elastic_form_ = nullptr;
344 fluid_neumann_form_ = nullptr;
345 mesh_body_form_ = nullptr;
346 average_pressure_form_ = nullptr;
347 }
348
350 const std::string &formulation,
351 const Units &units,
352 const json &args,
353 const std::string &out_path)
354 {
355 if (!args.contains("time") || args["time"].is_null())
356 log_and_throw_error("NavierStokesFSI is only available for time-dependent problems.");
357 FluidVarForm::init(formulation, units, args, out_path);
358
359 const json &materials = args.at("materials");
360 const json material = first_material(materials);
361 mesh_displacement_space_id_ = material.at("mesh_displacement_space_id").get<int>();
364 log_and_throw_error("NavierStokesFSI requires distinct velocity, pressure, and mesh-displacement FE spaces.");
365 mesh_elastic_formulation_ = material.at("mesh_material").at("type").get<std::string>();
367 log_and_throw_error("NavierStokesFSI mesh_material must be an elastic material, got {}.", mesh_elastic_formulation_);
368
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());
376 if (has_solid_)
377 {
378 fluid_geometry_id_ = material.at("fluid_geometry_id").get<int>();
379 solid_geometry_id_ = material.at("solid_geometry_id").get<int>();
380 displacement_space_id_ = material.at("displacement_space_id").get<int>();
381 solid_elastic_formulation_ = material.at("solid_material").at("type").get<std::string>();
383 log_and_throw_error("NavierStokesFSI solid_material must be an elastic material, got {}.", solid_elastic_formulation_);
384 const std::set<int> ids{
386 if (ids.size() != 4)
387 log_and_throw_error("Two-mesh NavierStokesFSI requires four distinct FE-space IDs.");
389 log_and_throw_error("Two-mesh NavierStokesFSI requires distinct fluid and solid geometry IDs.");
390 }
391
392 if (materials.is_array())
393 for (const json &entry : materials)
394 {
395 if (entry.at("mesh_displacement_space_id").get<int>() != mesh_displacement_space_id_)
396 log_and_throw_error("All NavierStokesFSI materials must use the same mesh-displacement FE space.");
397 if (entry.at("mesh_material").at("type").get<std::string>() != mesh_elastic_formulation_)
398 log_and_throw_error("All NavierStokesFSI regions must use the same mesh elastic formulation.");
399 for (const std::string &field : solid_fields)
400 if (entry.contains(field) != has_solid_)
401 log_and_throw_error("All NavierStokesFSI materials must consistently enable the two-mesh solid fields.");
402 if (has_solid_ && (entry.at("fluid_geometry_id") != fluid_geometry_id_ || entry.at("solid_geometry_id") != solid_geometry_id_ || entry.at("displacement_space_id") != displacement_space_id_ || entry.at("solid_material").at("type") != solid_elastic_formulation_))
403 log_and_throw_error("All NavierStokesFSI materials must use the same geometry IDs, solid FE space, and solid formulation.");
404 }
405
406 if (args.at("space").at("discr_order").is_array())
407 {
408 bool found = false;
409 for (const json &entry : args.at("space").at("discr_order"))
410 found |= entry.at("fe_space").get<int>() == mesh_displacement_space_id_;
411 if (!found)
412 log_and_throw_error("NavierStokesFSI discretization orders must name the mesh-displacement FE space.");
413 }
414
416 mesh_mass_assembler_ = std::make_shared<assembler::Mass>();
417 mesh_pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
419 std::make_shared<assembler::NavierStokesFSIVelocity>(),
420 std::make_shared<assembler::NavierStokesFSIMixed>(),
421 std::make_shared<assembler::NavierStokesFSIPressure>(),
422 std::make_shared<assembler::NavierStokesFSIInertia>()};
423
424 mesh_displacement_problem_ = std::make_shared<assembler::GenericTensorProblem>("NavierStokesFSIMeshDisplacement");
426 mesh_displacement_problem_->set_parameters({{"is_time_dependent", true}}, root_path);
427 auto boundary_conditions = args["boundary_conditions"];
428 boundary_conditions["root_path"] = root_path;
429 mesh_displacement_problem_->set_parameters(boundary_conditions, root_path);
430 mesh_displacement_problem_->set_parameters(args["initial_conditions"], root_path);
431 mesh_displacement_problem_->set_parameters(args["output"], root_path);
433
434 if (has_solid_)
435 {
437 solid_varform_ = std::make_shared<NonlinearElasticTransientVarForm>();
439 }
440 }
441
443 {
444 if (args["materials"].is_array())
445 {
446 json result = json::array();
447 for (const json &material : args["materials"])
448 result.push_back(mesh_material(material));
449 return result;
450 }
451 return mesh_material(args["materials"]);
452 }
453
455 {
456 json result = args;
457 const json material = first_material(args.at("materials"));
458 result["materials"] = material.at("solid_material");
459 result.erase("preset_problem");
460
461 if (result["space"]["discr_order"].is_array())
462 result["space"]["discr_order"] = filter_fe_space_entries(
463 result["space"]["discr_order"], displacement_space_id_);
464
465 for (const char *key : {
466 "rhs", "dirichlet_boundary", "neumann_boundary",
467 "nodal_neumann_boundary", "normal_aligned_neumann_boundary"})
468 {
469 if (result["boundary_conditions"].contains(key))
470 result["boundary_conditions"][key] = filter_fe_space_entries(
471 result["boundary_conditions"][key], displacement_space_id_);
472 }
473 result["boundary_conditions"]["pressure_boundary"] = json::array();
474 result["boundary_conditions"]["pressure_cavity"] = json::array();
475
476 for (const char *key : {"solution", "velocity", "acceleration"})
477 if (result["initial_conditions"].contains(key))
478 result["initial_conditions"][key] = filter_fe_space_entries(
479 result["initial_conditions"][key], displacement_space_id_);
480
481 result["time"]["integrator"] = time_integrator_args(displacement_space_id_);
482 result["constraints"]["hard"] = json::array();
483 result["constraints"]["soft"] = json::array();
484 result["space"]["remesh"]["enabled"] = false;
485
486 result["output"]["advanced"]["timestep_prefix"] =
487 "solid_" + result["output"]["advanced"]["timestep_prefix"].get<std::string>();
488 return result;
489 }
490
492 {
493 const json &integrators = args["time"]["integrator"];
494 if (!integrators.is_array())
495 return integrators;
496 for (const json &integrator : integrators)
497 if (integrator.value("fe_space", -1) == fe_space_id)
498 {
499 json result = integrator;
500 result.erase("fe_space");
501 return result;
502 }
503 log_and_throw_error("Missing time integrator for FE space {}.", fe_space_id);
504 }
505
506 void NavierStokesFSIVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
507 {
508 if (has_solid_)
509 {
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());
513
514 std::unique_ptr<mesh::Mesh> fluid_mesh, solid_mesh;
515 for (auto &piece : pieces)
516 {
517 if (piece.id == fluid_geometry_id_)
518 fluid_mesh = std::move(piece.mesh);
519 else if (piece.id == solid_geometry_id_)
520 solid_mesh = std::move(piece.mesh);
521 else
522 log_and_throw_error("Unexpected geometry ID {} in two-mesh NavierStokesFSI.", piece.id);
523 }
524 if (!fluid_mesh || !solid_mesh)
526 "Unable to find configured fluid/solid geometry IDs {}/{}.",
528
529 if (fluid_mesh->dimension() == 2)
530 {
532 dynamic_cast<const mesh::Mesh2D &>(*fluid_mesh),
533 dynamic_cast<const mesh::Mesh2D &>(*solid_mesh));
534 if (interface_2d_.empty())
535 log_and_throw_error("Configured 2D fluid and solid geometries do not share an interface.");
536 }
537 else
538 {
540 dynamic_cast<const mesh::Mesh3D &>(*fluid_mesh),
541 dynamic_cast<const mesh::Mesh3D &>(*solid_mesh));
542 if (interface_3d_.empty())
543 log_and_throw_error("Configured 3D fluid and solid geometries do not share an interface.");
544 }
545
546 mesh_ = std::move(fluid_mesh);
547 solid_varform_->set_mesh(std::move(solid_mesh));
548 }
549
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);
554 for (const auto &assembler : ale_assemblers_)
555 {
556 assembler->set_size(mesh_->dimension());
557 assembler->set_materials(body_ids, this->args["materials"], units, root_path);
558 }
559 const json mesh_materials = mesh_material_args();
560 mesh_elastic_assembler_->set_size(mesh_->dimension());
561 mesh_elastic_assembler_->set_materials(body_ids, mesh_materials, units, root_path);
562 mesh_mass_assembler_->set_size(mesh_->dimension());
563 mesh_mass_assembler_->set_materials(body_ids, mesh_materials, units, root_path);
564 mesh_pure_mass_assembler_->set_size(mesh_->dimension());
566 }
567
568 void NavierStokesFSIVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
569 {
570 FluidVarForm::build_basis(mesh, iso_parametric, args);
571 // The interface traction multiplier can exchange a constant normal
572 // traction with the fluid pressure. Keep an explicit physical-domain
573 // pressure reference in the coupled problem even when an outer boundary
574 // is marked Neumann.
575 if (has_solid_)
576 use_avg_pressure = true;
577 Eigen::VectorXi orders, ordersq;
578 assign_discr_orders(args["space"], mesh_displacement_space_id_, mesh, 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"],
590
592 <= args["solver"]["advanced"]["cache_size"])
593 {
597 }
598 else
599 {
603 }
605 logger().info("n mesh displacement bases: {}", mesh_displacement_space_.n_bases);
606 if (has_solid_)
607 {
608 solid_varform_->prepare_for_embedding();
610 logger().info("n solid displacement dofs: {}", solid_varform_->embedding_ndof());
611 logger().info(
612 "n FSI interface multiplier dofs: physical={}, mesh={}",
614 }
615 }
616
618 {
619 assert(has_solid_ && solid_varform_ && mesh_);
620 if (mesh_->dimension() != 2)
621 log_and_throw_error("Two-mesh FSI interface coupling currently supports 2D meshes.");
622 const io::OutputSpace solid_output = solid_varform_->output_space();
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);
626 const FESpace &solid_space = solid_varform_->embedding_space();
627 const int order = 2 * std::max({space_.disc_orders.maxCoeff(), mesh_displacement_space_.disc_orders.maxCoeff(), solid_space.disc_orders.maxCoeff()}) + 2;
628
629 const ScalarTraceOperators physical = assemble_2d_trace_operators(
630 fluid_mesh, solid_mesh, interface_2d_,
632 solid_space.basis_list(), solid_space.n_bases, order);
633 const ScalarTraceOperators computational = assemble_2d_trace_operators(
634 fluid_mesh, solid_mesh, interface_2d_,
636 solid_space.basis_list(), solid_space.n_bases, order);
637 const VectorTraceOperators physical_vector =
638 vector_trace(physical, mesh_->dimension(), boundary_.boundary_nodes);
639 const VectorTraceOperators computational_vector =
640 vector_trace(computational, mesh_->dimension(), mesh_displacement_boundary_.boundary_nodes);
641 interface_velocity_trace_ = physical_vector.source;
642 interface_solid_velocity_trace_ = physical_vector.solid;
643 interface_mesh_trace_ = computational_vector.source;
644 interface_solid_mesh_trace_ = computational_vector.solid;
645 if (interface_velocity_trace_.rows() == 0 || interface_mesh_trace_.rows() == 0)
646 log_and_throw_error("The fluid-solid interface has no active FE trace degrees of freedom.");
647 }
648
650 {
657 mesh.dimension());
658 std::vector<int> unused;
665 for (const int node : mesh_displacement_boundary_.dirichlet_nodes)
666 {
667 const int tag = mesh.get_node_id(node);
668 for (int d = 0; d < mesh.dimension(); ++d)
669 if (mesh_displacement_problem_->is_nodal_dimension_dirichlet(node, tag, d, mesh_displacement_space_id_))
670 mesh_displacement_boundary_.boundary_nodes.push_back(node * mesh.dimension() + d);
671 }
675 }
676
678 {
681 return;
682 json solver_params = args["solver"]["linear"];
683 if (!solver_params.contains("Pardiso"))
684 solver_params["Pardiso"] = {};
685 solver_params["Pardiso"]["mtype"] = -2;
686 mesh_rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
687 *mesh_elastic_assembler_, *mesh_, nullptr,
693 args["space"]["advanced"]["bc_method"], solver_params,
695 }
696
698 {
700 assert(mesh_rhs_assembler_);
702 mesh_rhs_ *= -1;
703 const Eigen::MatrixXd velocity_rhs = rhs_.topRows(primary_ndof());
704 rhs_.setZero(total_ndof(), 1);
705 rhs_.topRows(primary_ndof()) = velocity_rhs;
707 }
708
717
719 {
720 return mesh_ ? mesh_displacement_space_.n_bases * mesh_->dimension() : 0;
721 }
722
724 {
725 return has_solid_ && solid_varform_ ? solid_varform_->embedding_ndof() : 0;
726 }
727
732
737
744
746 {
747 if (sol.size() == 0)
748 {
749 Eigen::MatrixXd velocity, mesh_displacement, solid_displacement;
750 const std::string state_path = resolve_input_path(args["input"]["data"]["state"]);
751 const bool loaded_velocity = read_initial_x_from_file(
752 state_path, "u", args["input"]["data"]["reorder"],
753 space_.space_in_node_to_node, mesh_->dimension(), velocity);
754 const bool loaded_mesh_displacement = read_initial_x_from_file(
755 state_path, "mesh_u", args["input"]["data"]["reorder"],
756 mesh_displacement_space_.space_in_node_to_node, mesh_->dimension(), mesh_displacement);
757 if (!loaded_velocity)
758 rhs_assembler_->initial_solution(velocity);
759 if (!loaded_mesh_displacement)
760 mesh_rhs_assembler_->initial_solution(mesh_displacement);
761 if (has_solid_)
762 solid_varform_->initial_solution_for_embedding(solid_displacement, "solid_");
763 sol.setZero(total_ndof(), 1);
764 sol.topRows(primary_ndof()) = velocity.topRows(primary_ndof()).leftCols(1);
766 mesh_displacement.topRows(mesh_displacement_ndof()).leftCols(1);
767 if (has_solid_)
769 solid_displacement.topRows(solid_displacement_ndof()).leftCols(1);
770 }
771 else
772 {
773 if (sol.cols() > 1)
774 sol.conservativeResize(Eigen::NoChange, 1);
775 if (sol.rows() != total_ndof())
776 {
777 const Eigen::MatrixXd input = sol;
778 sol.setZero(total_ndof(), 1);
779 const int rows = std::min<int>(input.rows(), total_ndof());
780 if (rows > 0)
781 sol.topRows(rows) = input.topRows(rows);
782 }
783 }
784 }
785
786 void NavierStokesFSIVarForm::build_forms(Eigen::MatrixXd &sol, const double t)
787 {
788 const int dim = mesh_->dimension();
789 const Eigen::VectorXd velocity = sol.topRows(primary_ndof());
790 const Eigen::VectorXd mesh_displacement = sol.middleRows(mesh_displacement_offset(), mesh_displacement_ndof());
791 Eigen::MatrixXd solid_displacement;
792 if (has_solid_)
793 {
794 solid_displacement = sol.middleRows(solid_displacement_offset(), solid_displacement_ndof());
795 solid_varform_->init_forms_for_embedding(solid_displacement, t, "solid_");
796 }
797
802 Eigen::MatrixXd velocity_initial_velocity, mesh_initial_velocity;
803 rhs_assembler_->initial_velocity(velocity_initial_velocity);
804 mesh_rhs_assembler_->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;
810 Eigen::MatrixXd mesh_history_acceleration = Eigen::MatrixXd::Zero(mesh_displacement_ndof(), 1);
811 const std::string state_path = resolve_input_path(args["input"]["data"]["state"]);
813 state_path, "u", args["input"]["data"]["reorder"],
814 space_.space_in_node_to_node, dim, velocity_history))
815 {
817 state_path, "v", args["input"]["data"]["reorder"],
818 space_.space_in_node_to_node, dim, velocity_history_velocity))
819 velocity_history_velocity.setZero(velocity_history.rows(), velocity_history.cols());
821 state_path, "a", args["input"]["data"]["reorder"],
822 space_.space_in_node_to_node, dim, velocity_history_acceleration))
823 velocity_history_acceleration.setZero(velocity_history.rows(), velocity_history.cols());
824 }
826 state_path, "mesh_u", args["input"]["data"]["reorder"],
828 {
830 state_path, "mesh_v", args["input"]["data"]["reorder"],
831 mesh_displacement_space_.space_in_node_to_node, dim, mesh_history_velocity))
832 mesh_history_velocity.setZero(mesh_history.rows(), mesh_history.cols());
834 state_path, "mesh_a", args["input"]["data"]["reorder"],
835 mesh_displacement_space_.space_in_node_to_node, dim, mesh_history_acceleration))
836 mesh_history_acceleration.setZero(mesh_history.rows(), mesh_history.cols());
837 }
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);
840 time_integrator = velocity_bdf;
842
843 ale_form_ = std::make_shared<solver::NavierStokesFSIForm>(
848 t, dt, mesh_->is_volume(),
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_);
851 });
852 const int gorder = mesh_->orders().size() == 0 ? 1 : mesh_->orders().maxCoeff();
853 const QuadratureOrders velocity_samples = n_boundary_samples(
854 space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gorder);
855 ale_form_->set_velocity_tilde_updater(
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;
859 rhs_assembler_->set_bc(boundary_.local_boundary, boundary_.boundary_nodes, velocity_samples, empty_neumann, projected, Eigen::MatrixXd(), time);
860 target = projected.col(0);
861 });
862
863 auxiliary_form_ = std::make_shared<solver::StackedForm>();
864 const auto velocity_block = auxiliary_form_->add_block(primary_ndof());
866 const auto mesh_block = auxiliary_form_->add_block(mesh_displacement_ndof());
867 std::optional<solver::StackedForm::Block> solid_block;
868 if (has_solid_)
869 {
870 solid_block = auxiliary_form_->add_block(solid_displacement_ndof());
871 for (const auto &form : solid_varform_->embedding_forms())
872 auxiliary_form_->add(*solid_block, form);
875 }
876
877 const solver::ElementInversionCheck check = args["solver"]["advanced"]["check_inversion"];
878 mesh_elastic_form_ = std::make_shared<solver::ElasticForm>(
881 args["solver"]["advanced"]["jacobian_threshold"], check);
882 auxiliary_form_->add(mesh_block, mesh_elastic_form_);
883
884 fluid_zero_rhs_ = Eigen::MatrixXd::Zero(primary_ndof(), 1);
885 fluid_neumann_form_ = std::make_shared<solver::BodyForm>(
888 mass_assembler_->density(), false, true);
889 fluid_neumann_form_->update_quantities(t, velocity);
890 auxiliary_form_->add(velocity_block, fluid_neumann_form_);
891
892 const QuadratureOrders mesh_samples = n_boundary_samples(
894 mesh_displacement_space_.disc_ordersq.maxCoeff(), gorder);
895 mesh_body_form_ = std::make_shared<solver::BodyForm>(
899 mesh_rhs_, *mesh_rhs_assembler_, mesh_mass_assembler_->density(), false, true);
900 mesh_body_form_->update_quantities(t, mesh_displacement);
901 auxiliary_form_->add(mesh_block, mesh_body_form_);
902
904 {
905 auxiliary_form_->add_block(1);
906 average_pressure_form_ = std::make_shared<solver::NavierStokesFSIAveragePressureForm>(
912 }
913 else
914 average_pressure_form_ = nullptr;
915
916 if (has_solid_)
917 {
918 interface_form_ = std::make_shared<solver::FSIInterfaceForm>(
923 *time_integrator, *solid_varform_->embedding_time_integrator());
924 }
925 else
926 interface_form_ = nullptr;
927
929 if (interface_form_)
930 fsi_forms_.push_back(interface_form_);
933 for (const auto &form : fsi_forms_)
934 form->set_output_dir(output_path);
935 fsi_al_forms_.clear();
937 || (has_solid_ && !solid_varform_->embedding_al_forms().empty()))
938 {
939 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
940 const auto velocity_al = stacked_al->add_block(primary_ndof());
941 stacked_al->add_block(pressure_space_.n_bases);
942 const auto mesh_al = stacked_al->add_block(mesh_displacement_ndof());
943 std::optional<solver::StackedAugmentedLagrangianForm::Block> solid_al;
944 if (has_solid_)
945 {
946 solid_al = stacked_al->add_block(solid_displacement_ndof());
947 stacked_al->add_block(fluid_interface_multiplier_ndof());
948 stacked_al->add_block(mesh_interface_multiplier_ndof());
949 }
951 stacked_al->add_block(1);
952 if (!boundary_.boundary_nodes.empty())
953 stacked_al->add(velocity_al, std::make_shared<solver::BCLagrangianForm>(
955 velocity_samples, pure_mass_, *rhs_assembler_, 0, true, t));
957 stacked_al->add(mesh_al, std::make_shared<solver::BCLagrangianForm>(
960 mesh_samples, mesh_pure_mass_, *mesh_rhs_assembler_, 0, true, t));
961 if (has_solid_)
962 for (const auto &form : solid_varform_->embedding_al_forms())
963 stacked_al->add(*solid_al, form);
964 fsi_al_forms_.push_back(stacked_al);
965 }
966
967 fsi_problem_ = std::make_shared<solver::NLProblem>(
969 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
971 residual_mass(
973 has_solid_ ? solid_varform_->embedding_norm_matrix() : StiffnessMatrix(),
974 has_solid_ ? interface_form_->fluid_multiplier_mass() : StiffnessMatrix(),
975 has_solid_ ? interface_form_->mesh_multiplier_mass() : StiffnessMatrix(),
977 dim, true);
978 fsi_problem_->init(sol);
979 fsi_problem_->update_quantities(t, sol);
981 stats.solver_info = json::array();
982 }
983
985 {
986 const double scale = time_integrator->acceleration_scaling();
988 fluid_neumann_form_->set_weight(scale);
990 average_pressure_form_->set_weight(scale);
991 }
992
993 void NavierStokesFSIVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &sol)
994 {
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(
998 nonlinear_params, args["solver"]["linear"], units.characteristic_length(), logger());
999 solver::ALSolver al_solver(
1000 fsi_al_forms_, args["solver"]["augmented_lagrangian"]["initial_weight"],
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) {
1005 if (has_solid_)
1006 solid_varform_->update_barrier_stiffness_for_embedding(
1008 });
1009 al_solver.post_subsolve = [&](const double weight) {
1010 stats.solver_info.push_back({{"type", weight > 0 ? "al" : "rc"}, {"t", step}, {"info", nonlinear_solver->info()}});
1011 if (weight > 0)
1012 stats.solver_info.back()["weight"] = weight;
1013 save_subsolve(stats.solver_info.size(), step, sol);
1014 };
1015 if (!fsi_al_forms_.empty())
1016 al_solver.solve_al(*fsi_problem_, sol, al_params, args["solver"]["linear"], units.characteristic_length(), nonlinear_solver);
1017 al_solver.solve_reduced(*fsi_problem_, sol, nonlinear_params, args["solver"]["linear"], units.characteristic_length(), nonlinear_solver);
1018 }
1019
1020 void NavierStokesFSIVarForm::solve_problem(Eigen::MatrixXd &sol)
1021 {
1022 igl::Timer timer;
1023 timer.start();
1025 build_forms(sol, t0 + dt);
1026 save_fsi_timestep(t0, 0, sol);
1027 for (int step = 1; step <= time_steps; ++step)
1028 {
1029 const double time = t0 + step * dt;
1030 logger().info("{}/{} steps, dt={}s t={}s", step, time_steps, dt, time);
1031 solve_nonlinear_step(step, sol);
1032 time_integrator->update_quantities(sol.topRows(primary_ndof()));
1033 mesh_displacement_time_integrator_->update_quantities(
1035 if (has_solid_)
1036 solid_varform_->advance_for_embedding(
1039 fsi_problem_->update_quantities(t0 + (step + 1) * dt, sol);
1040 save_fsi_timestep(time, step, sol);
1041 save_step_state(t0, dt, step, time_integrator.get());
1043 if (has_solid_)
1046 }
1047 timer.stop();
1048 timings.solving_time = timer.getElapsedTime();
1049 }
1050
1052 const double time, const int step, const Eigen::MatrixXd &solution) const
1053 {
1054 if (!has_solid_)
1055 {
1056 save_timestep(time, step, t0, dt, solution);
1057 return;
1058 }
1059
1060 paraviewo::VTMWriter vtm(time);
1061 const bool fluid_saved = save_timestep_to_vtm(time, step, dt, solution, vtm, "Fluid");
1062 const bool solid_saved = solid_varform_->save_timestep_for_embedding(
1063 time, step, dt,
1064 solution.middleRows(solid_displacement_offset(), solid_displacement_ndof()),
1065 vtm, "Solid");
1066 if (!fluid_saved && !solid_saved)
1067 return;
1068
1069 const int global_t = output_file_index(step);
1070 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
1071 vtm.save(resolve_output_path(fmt::format(step_name + "{:d}.vtm", global_t)));
1073 resolve_output_path(args["output"]["paraview"]["file_name"]),
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>());
1076 }
1077
1079 {
1081 const std::string state_path = resolve_output_path(
1082 fmt::format(args["output"]["data"]["state"].get<std::string>(), output_file_index(step)));
1083 if (state_path.empty())
1084 return;
1085
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];
1090 io::write_matrix(state_path, name, values, /*replace=*/false);
1091 };
1092 save_history("mesh_u", mesh_displacement_time_integrator_->x_prevs());
1093 save_history("mesh_v", mesh_displacement_time_integrator_->v_prevs());
1094 save_history("mesh_a", mesh_displacement_time_integrator_->a_prevs());
1095 }
1096
1098 {
1099 assert(has_solid_ && solid_varform_->embedding_time_integrator());
1100 const std::string state_path = resolve_output_path(
1101 fmt::format(args["output"]["data"]["state"].get<std::string>(), output_file_index(step)));
1102 if (state_path.empty())
1103 return;
1104
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];
1109 io::write_matrix(state_path, name, values, /*replace=*/false);
1110 };
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());
1115 }
1116
1117 std::vector<io::OutputField> NavierStokesFSIVarForm::output_fields(
1118 const io::OutputSample &sample,
1119 const Eigen::MatrixXd &solution,
1120 const io::OutputFieldOptions &options) const
1121 {
1122 std::vector<io::OutputField> fields = FluidVarForm::output_fields(sample, solution, options);
1123 if (!mesh_ || solution.rows() < mesh_displacement_offset() + mesh_displacement_ndof()
1124 || !options.export_field("mesh_displacement"))
1125 return fields;
1126
1127 const int dim = mesh_->dimension();
1128 const Eigen::MatrixXd mesh_displacement =
1129 solution.middleRows(mesh_displacement_offset(), mesh_displacement_ndof());
1130 const bool has_element_samples =
1131 sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
1132 const int output_rows = sample.points.rows() > 0
1133 ? sample.points.rows()
1134 : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
1135 Eigen::MatrixXd values;
1136
1137 if (has_element_samples)
1138 {
1139 values.setZero(output_rows, dim);
1140 for (int i = 0; i < sample.local_points.rows(); ++i)
1141 {
1142 const int element = sample.element_ids(i);
1143 if (element < 0)
1144 continue;
1145 Eigen::MatrixXd local_value, local_gradient;
1147 *mesh_, dim,
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);
1153 }
1154 }
1155 else if (sample.node_ids.size() > 0)
1156 {
1157 values.resize(sample.node_ids.size(), dim);
1158 for (int i = 0; i < sample.node_ids.size(); ++i)
1159 {
1160 const int node = sample.node_ids(i);
1161 if (node < 0 || node * dim + dim > mesh_displacement.rows())
1162 return fields;
1163 values.row(i) = mesh_displacement.block(node * dim, 0, dim, 1).transpose();
1164 }
1165 }
1166 else
1167 {
1168 return fields;
1169 }
1170
1171 fields.push_back({"mesh_displacement", values, io::OutputField::Association::Point});
1172 return fields;
1173 }
1174} // namespace polyfem::varform
std::vector< Eigen::Triplet< double > > entries
StiffnessMatrix source
std::vector< int > multiplier_source_ids
StiffnessMatrix solid
std::vector< std::pair< int, double > > weights
int x
double characteristic_length() const
Definition Units.hpp:22
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
Definition OutData.cpp:2591
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.
Definition Mesh.hpp:49
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.
Definition Mesh.cpp:35
int dimension() const
utily for dimension
Definition Mesh.hpp:164
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:508
std::function< void(const double)> post_subsolve
Definition ALSolver.hpp:53
static std::shared_ptr< BDF > construct_bdf_integrator(const json &params, 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)
A finite-element space for one scalar- or vector-valued field.
Definition FESpace.hpp:59
const std::vector< basis::ElementBases > & geometry_basis_list() const
Definition FESpace.hpp:115
std::shared_ptr< std::vector< basis::ElementBases > > bases
Per-element basis data.
Definition FESpace.hpp:68
std::shared_ptr< GeometryMapping > geometry
Geometric mapping used to integrate this FE space.
Definition FESpace.hpp:89
Eigen::VectorXi disc_orders
Primary polynomial degree for each mesh element.
Definition FESpace.hpp:71
Eigen::VectorXi disc_ordersq
Secondary polynomial degree for anisotropic bases, e.g. prisms.
Definition FESpace.hpp:74
int n_bases
Number of globally indexed scalar basis functions in the space.
Definition FESpace.hpp:65
Eigen::VectorXi space_in_node_to_node
Definition FESpace.hpp:91
const std::vector< basis::ElementBases > & basis_list() const
Definition FESpace.hpp:109
void assemble_rhs(const mesh::Mesh &mesh) override
assembler::AssemblyValsCache pressure_ass_vals_cache_
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
void build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args) override
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
assembler::AssemblyValsCache ass_vals_cache_
std::shared_ptr< assembler::RhsAssembler > rhs_assembler_
VarFormBoundaryState boundary_
std::shared_ptr< assembler::Mass > mass_assembler_
std::vector< io::OutputField > output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options) const override
Get the output fields of the variational formulation, for output purposes.
void load_mesh(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< NonlinearElasticTransientVarForm > solid_varform_
void prepare_fsi_initial_solution(Eigen::MatrixXd &sol) const
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > mesh_displacement_time_integrator_
std::vector< io::OutputField > output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options) const override
Get the output fields of the variational formulation, for output purposes.
std::string name() const override
Get the name of the variational formulation.
std::shared_ptr< solver::NLProblem > fsi_problem_
void solve_problem(Eigen::MatrixXd &sol) override
std::shared_ptr< assembler::Assembler > mesh_elastic_assembler_
std::shared_ptr< assembler::Problem > mesh_displacement_problem_
std::shared_ptr< assembler::HRZMass > mesh_pure_mass_assembler_
std::vector< std::shared_ptr< solver::Form > > fsi_forms_
std::vector< std::shared_ptr< assembler::MultiSpacesNLAssembler > > ale_assemblers_
void assemble_mass_mat(const mesh::Mesh &mesh, const json &args) override
void load_mesh(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< solver::NavierStokesFSIForm > ale_form_
std::shared_ptr< solver::FSIInterfaceForm > interface_form_
void build_forms(Eigen::MatrixXd &sol, double t)
std::shared_ptr< solver::BodyForm > mesh_body_form_
std::shared_ptr< assembler::RhsAssembler > mesh_rhs_assembler_
void save_fsi_timestep(double time, int step, const Eigen::MatrixXd &solution) const
std::shared_ptr< solver::ElasticForm > mesh_elastic_form_
std::vector< std::pair< mesh::Navigation::Index, mesh::Navigation::Index > > interface_2d_
std::vector< std::pair< mesh::Navigation3D::Index, mesh::Navigation3D::Index > > interface_3d_
void build_basis(mesh::Mesh &mesh, bool iso_parametric, const json &args) override
std::shared_ptr< solver::BodyForm > fluid_neumann_form_
std::vector< std::shared_ptr< solver::AugmentedLagrangianForm > > fsi_al_forms_
assembler::AssemblyValsCache mesh_displacement_mass_ass_vals_cache_
void solve_nonlinear_step(int step, Eigen::MatrixXd &sol)
std::shared_ptr< assembler::Mass > mesh_mass_assembler_
std::shared_ptr< solver::StackedForm > auxiliary_form_
void assemble_rhs(const mesh::Mesh &mesh) override
assembler::AssemblyValsCache mesh_displacement_pure_mass_ass_vals_cache_
std::shared_ptr< solver::NavierStokesFSIAveragePressureForm > average_pressure_form_
assembler::AssemblyValsCache mesh_displacement_ass_vals_cache_
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
int output_file_index(const int t) const
Definition VarForm.cpp:1098
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Definition VarForm.cpp:1103
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1117
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:207
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:743
io::OutStatsData stats
Definition VarForm.hpp:199
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:997
static bool read_initial_x_from_file(const std::string &state_path, const std::string &x_name, const bool reorder, const Eigen::VectorXi &in_node_to_node, const int dim, Eigen::MatrixXd &x)
Definition VarForm.cpp:39
io::OutGeometryData output_geometry_
Definition VarForm.hpp:211
void save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:978
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:256
void build_fe_space(mesh::Mesh &mesh, const bool iso_parametric, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::string &basis_type, const std::string &poly_basis_type, const assembler::Assembler &space_assembler, const int value_dim, const int quadrature_order, const int mass_quadrature_order, const bool use_corner_quadrature, const int n_harmonic_samples, const int integral_constraints, FESpace &space, VarFormBoundaryState &boundary, std::shared_ptr< GeometryMapping > geometry=nullptr)
Definition VarForm.cpp:320
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1108
bool save_timestep_to_vtm(const double time, const int t, const double dt, const Eigen::MatrixXd &solution, paraviewo::VTMWriter &vtm, const std::string &block_prefix) const
Definition VarForm.cpp:955
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:939
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:202
void save_step_state(const double t0, const double dt, const int t, const time_integrator::ImplicitTimeIntegrator *time_integrator, const bool rest_mesh_written=false) const
Definition VarForm.cpp:924
int norm
Definition p_bases.py:265
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.
Definition MatrixIO.cpp:42
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.
Definition Logger.cpp:44
std::array< int, 2 > QuadratureOrders
Definition Types.hpp:19
nlohmann::json json
Definition Common.hpp:9
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24
bool export_field(const std::string &field) const
Definition OutData.cpp:56
Eigen::VectorXi node_ids
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points
const mesh::Mesh * mesh
std::vector< RowVectorNd > neumann_nodes_position
Definition FESpace.hpp:163
std::vector< mesh::LocalBoundary > local_boundary
Definition FESpace.hpp:155
std::vector< mesh::LocalBoundary > local_neumann_boundary
Definition FESpace.hpp:156
std::vector< mesh::LocalBoundary > total_local_boundary
Definition FESpace.hpp:154
std::vector< RowVectorNd > dirichlet_nodes_position
Definition FESpace.hpp:161