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{
41 const Eigen::MatrixXd &solution,
42 const io::CheckpointMetadata &metadata) const
43 {
44 VarForm::serialize_checkpoint(writer, solution, metadata);
45 write_checkpoint_ordering(writer, "primary", space_);
46 write_checkpoint_ordering(writer, "pressure", pressure_space_);
49 log_and_throw_error("Cannot checkpoint NavierStokesFSI without its mesh-motion integrator.");
50 mesh_displacement_time_integrator_->serialize_checkpoint(writer, "/checkpoint/state/mesh_motion_integrator");
51 if (has_solid_)
52 {
53 if (!solid_varform_ || !solid_varform_->embedding_time_integrator())
54 log_and_throw_error("Cannot checkpoint two-mesh NavierStokesFSI without its solid integrator.");
55 const io::OutputSpace solid_output = solid_varform_->output_space();
56 if (solid_output.mesh == nullptr)
57 log_and_throw_error("Cannot checkpoint NavierStokesFSI without its solid mesh.");
58 writer.write_mesh("/checkpoint/meshes/solid", *solid_output.mesh);
59 write_checkpoint_ordering(writer, "solid", solid_varform_->space_);
60 solid_varform_->embedding_time_integrator()->serialize_checkpoint(writer, "/checkpoint/state/solid_integrator");
61 }
62 }
63
65 const io::CheckpointReader &reader,
66 Eigen::MatrixXd &solution)
67 {
68 VarForm::deserialize_checkpoint(reader, solution);
70 const int dim = mesh_->dimension();
71 reorder_checkpoint_block(reader, "primary", space_, dim, 0, solution);
73 reader, "pressure", pressure_space_,
74 1, pressure_offset(), solution);
76 reader, "mesh_motion", mesh_displacement_space_,
77 dim, mesh_displacement_offset(), solution);
78 if (has_solid_)
80 reader, "solid", solid_varform_->space_,
81 dim, solid_displacement_offset(), solution);
82 }
83
84 namespace
85 {
86 json first_material(const json &materials)
87 {
88 return materials.is_array() ? materials.at(0) : materials;
89 }
90
91 json mesh_material(const json &material)
92 {
93 json result = material.at("mesh_material");
94 if (material.contains("id"))
95 result["id"] = material["id"];
96 return result;
97 }
98
99 json filter_fe_space_entries(const json &entries, const int fe_space_id)
100 {
101 if (!entries.is_array())
102 return entries;
103
104 json result = json::array();
105 for (const json &entry : entries)
106 {
107 if (!entry.is_object())
108 {
109 result.push_back(entry);
110 continue;
111 }
112 if (entry.contains("fe_space") && entry["fe_space"].get<int>() != fe_space_id)
113 continue;
114 json filtered = entry;
115 filtered.erase("fe_space");
116 result.push_back(std::move(filtered));
117 }
118 return result;
119 }
120
121 json residual_solver_params(const json &input)
122 {
123 json params = 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;
130 return params;
131 }
132
133 StiffnessMatrix residual_mass(
134 const StiffnessMatrix &velocity_mass,
135 const int pressure_size,
136 const StiffnessMatrix &mesh_mass,
137 const StiffnessMatrix &solid_mass,
138 const StiffnessMatrix &fluid_interface_mass,
139 const StiffnessMatrix &mesh_interface_mass,
140 const bool add_average)
141 {
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());
168 if (add_average)
169 entries.emplace_back(total - 1, total - 1, 1);
170 StiffnessMatrix result(total, total);
171 result.setFromTriplets(entries.begin(), entries.end());
172 result.makeCompressed();
173 return result;
174 }
175
176 bool same_point(const RowVectorNd &a, const RowVectorNd &b)
177 {
178 return a.size() == b.size() && (a - b).norm() <= 1e-10;
179 }
180
181 int local_edge(const mesh::Mesh2D &mesh, const mesh::Navigation::Index &index)
182 {
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)
185 return le;
186 log_and_throw_error("Unable to locate interface edge {} in element {}.", index.edge, index.face);
187 }
188
189 std::map<int, Eigen::VectorXd> global_basis_values(
190 const basis::ElementBases &bases, const Eigen::MatrixXd &points)
191 {
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())
197 {
198 auto it = result.try_emplace(
199 global.index, Eigen::VectorXd::Zero(points.rows()))
200 .first;
201 it->second += global.val * values[i].val.col(0);
202 }
203 return result;
204 }
205
206 struct ScalarTraceOperators
207 {
210 std::vector<int> multiplier_source_ids;
211 };
212
213 struct VectorTraceOperators
214 {
217 };
218
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)
228 {
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)
233 {
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.");
241
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);
252
253 Eigen::MatrixXd uv, fluid_points;
254 Eigen::VectorXd weights;
255 if (fluid_vertices == 3)
257 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points, weights);
258 else
260 fluid_edge, quadrature_order, fluid_index.edge, fluid_mesh, uv, fluid_points, weights);
261
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)
267 {
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);
271 else
272 solid_points.row(q) = (1 - t) * solid_endpoints.row(1) + t * solid_endpoints.row(0);
273 }
274
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)
278 {
279 if (multiplier_values.cwiseAbs().maxCoeff() < 1e-12)
280 continue;
281 const auto [it, inserted] = multiplier_index.try_emplace(source_id, multiplier_index.size());
282 const int row = it->second;
283 if (inserted)
284 result.multiplier_source_ids.push_back(source_id);
285 for (const auto &[trial_id, trial_values] : source_values)
286 {
287 const double value = (weights.array() * multiplier_values.array() * trial_values.array()).sum();
288 if (std::abs(value) > 1e-14)
289 source_entries.emplace_back(row, trial_id, value);
290 }
291 for (const auto &[trial_id, trial_values] : solid_values)
292 {
293 const double value = (weights.array() * multiplier_values.array() * trial_values.array()).sum();
294 if (std::abs(value) > 1e-14)
295 solid_entries.emplace_back(row, trial_id, value);
296 }
297 }
298 }
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());
303 return result;
304 }
305
306 VectorTraceOperators vector_trace(
307 const ScalarTraceOperators &scalar,
308 const int dim,
309 const std::vector<int> &source_dirichlet_dofs)
310 {
311 assert(scalar.source.rows() == scalar.solid.rows());
312 assert(scalar.source.rows() == int(scalar.multiplier_source_ids.size()));
313
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;
318
319 std::vector<int> vector_rows(scalar.source.rows() * dim, -1);
320 int n_rows = 0;
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++;
325
326 const auto expand = [&](const StiffnessMatrix &matrix) {
327 std::vector<Eigen::Triplet<double>> entries;
328 entries.reserve(matrix.nonZeros() * dim);
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)
332 {
333 const int row = vector_rows[it.row() * dim + d];
334 if (row >= 0)
335 entries.emplace_back(row, it.col() * dim + d, it.value());
336 }
337 StiffnessMatrix result(n_rows, matrix.cols() * dim);
338 result.setFromTriplets(entries.begin(), entries.end());
339 return result;
340 };
341
342 VectorTraceOperators result;
343 result.source = expand(scalar.source);
344 result.solid = expand(scalar.solid);
345 return result;
346 }
347 } // namespace
348
350 {
356 has_solid_ = false;
357 mesh_elastic_formulation_ = "NeoHookean";
358 solid_elastic_formulation_ = "NeoHookean";
359 solid_args_ = json();
360 solid_varform_ = nullptr;
361 interface_2d_.clear();
362 interface_3d_.clear();
369 mesh_elastic_assembler_ = nullptr;
370 mesh_mass_assembler_ = nullptr;
372 mesh_rhs_assembler_ = nullptr;
373 mesh_rhs_.resize(0, 0);
374 fluid_zero_rhs_.resize(0, 0);
375 mesh_pure_mass_.resize(0, 0);
376 interface_velocity_trace_.resize(0, 0);
378 interface_mesh_trace_.resize(0, 0);
379 interface_solid_mesh_trace_.resize(0, 0);
380 ale_assemblers_.clear();
382 fsi_forms_.clear();
383 fsi_al_forms_.clear();
384 fsi_problem_ = nullptr;
385 ale_form_ = nullptr;
386 interface_form_ = nullptr;
387 auxiliary_form_ = nullptr;
388 mesh_elastic_form_ = nullptr;
389 fluid_neumann_form_ = nullptr;
390 mesh_body_form_ = nullptr;
391 average_pressure_form_ = nullptr;
392 }
393
395 const std::string &formulation,
396 const Units &units,
397 const json &args,
398 const std::string &out_path)
399 {
400 if (!args.contains("time") || args["time"].is_null())
401 log_and_throw_error("NavierStokesFSI is only available for time-dependent problems.");
402 FluidVarForm::init(formulation, units, args, out_path);
403
404 const json &materials = args.at("materials");
405 const json material = first_material(materials);
406 mesh_displacement_space_id_ = material.at("mesh_displacement_space_id").get<int>();
409 log_and_throw_error("NavierStokesFSI requires distinct velocity, pressure, and mesh-displacement FE spaces.");
410 mesh_elastic_formulation_ = material.at("mesh_material").at("type").get<std::string>();
412 log_and_throw_error("NavierStokesFSI mesh_material must be an elastic material, got {}.", mesh_elastic_formulation_);
413
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());
421 if (has_solid_)
422 {
423 fluid_geometry_id_ = material.at("fluid_geometry_id").get<int>();
424 solid_geometry_id_ = material.at("solid_geometry_id").get<int>();
425 displacement_space_id_ = material.at("displacement_space_id").get<int>();
426 solid_elastic_formulation_ = material.at("solid_material").at("type").get<std::string>();
428 log_and_throw_error("NavierStokesFSI solid_material must be an elastic material, got {}.", solid_elastic_formulation_);
429 const std::set<int> ids{
431 if (ids.size() != 4)
432 log_and_throw_error("Two-mesh NavierStokesFSI requires four distinct FE-space IDs.");
434 log_and_throw_error("Two-mesh NavierStokesFSI requires distinct fluid and solid geometry IDs.");
435 }
436
437 if (materials.is_array())
438 for (const json &entry : materials)
439 {
440 if (entry.at("mesh_displacement_space_id").get<int>() != mesh_displacement_space_id_)
441 log_and_throw_error("All NavierStokesFSI materials must use the same mesh-displacement FE space.");
442 if (entry.at("mesh_material").at("type").get<std::string>() != mesh_elastic_formulation_)
443 log_and_throw_error("All NavierStokesFSI regions must use the same mesh elastic formulation.");
444 for (const std::string &field : solid_fields)
445 if (entry.contains(field) != has_solid_)
446 log_and_throw_error("All NavierStokesFSI materials must consistently enable the two-mesh solid fields.");
447 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_))
448 log_and_throw_error("All NavierStokesFSI materials must use the same geometry IDs, solid FE space, and solid formulation.");
449 }
450
451 if (args.at("space").at("discr_order").is_array())
452 {
453 bool found = false;
454 for (const json &entry : args.at("space").at("discr_order"))
455 found |= entry.at("fe_space").get<int>() == mesh_displacement_space_id_;
456 if (!found)
457 log_and_throw_error("NavierStokesFSI discretization orders must name the mesh-displacement FE space.");
458 }
459
461 mesh_mass_assembler_ = std::make_shared<assembler::Mass>();
462 mesh_pure_mass_assembler_ = std::make_shared<assembler::HRZMass>();
464 std::make_shared<assembler::NavierStokesFSIVelocity>(),
465 std::make_shared<assembler::NavierStokesFSIMixed>(),
466 std::make_shared<assembler::NavierStokesFSIPressure>(),
467 std::make_shared<assembler::NavierStokesFSIInertia>()};
468
469 mesh_displacement_problem_ = std::make_shared<assembler::GenericTensorProblem>("NavierStokesFSIMeshDisplacement");
471 mesh_displacement_problem_->set_parameters({{"is_time_dependent", true}}, resources_);
472 auto boundary_conditions = args["boundary_conditions"];
473 mesh_displacement_problem_->set_parameters(boundary_conditions, resources_);
474 mesh_displacement_problem_->set_parameters(args["initial_conditions"], resources_);
475 mesh_displacement_problem_->set_parameters(args["output"], resources_);
477
478 if (has_solid_)
479 {
481 solid_varform_ = std::make_shared<NonlinearElasticTransientVarForm>(resources_);
483 }
484 }
485
487 {
488 if (args["materials"].is_array())
489 {
490 json result = json::array();
491 for (const json &material : args["materials"])
492 result.push_back(mesh_material(material));
493 return result;
494 }
495 return mesh_material(args["materials"]);
496 }
497
499 {
500 json result = args;
501 const json material = first_material(args.at("materials"));
502 result["materials"] = material.at("solid_material");
503 result.erase("preset_problem");
504
505 if (result["space"]["discr_order"].is_array())
506 result["space"]["discr_order"] = filter_fe_space_entries(
507 result["space"]["discr_order"], displacement_space_id_);
508
509 for (const char *key : {
510 "rhs", "dirichlet_boundary", "neumann_boundary",
511 "nodal_neumann_boundary", "normal_aligned_neumann_boundary"})
512 {
513 if (result["boundary_conditions"].contains(key))
514 result["boundary_conditions"][key] = filter_fe_space_entries(
515 result["boundary_conditions"][key], displacement_space_id_);
516 }
517 result["boundary_conditions"]["pressure_boundary"] = json::array();
518 result["boundary_conditions"]["pressure_cavity"] = json::array();
519
520 for (const char *key : {"solution", "velocity", "acceleration"})
521 if (result["initial_conditions"].contains(key))
522 result["initial_conditions"][key] = filter_fe_space_entries(
523 result["initial_conditions"][key], displacement_space_id_);
524
525 result["time"]["integrator"] = time_integrator_args(displacement_space_id_);
526 result["constraints"]["hard"] = json::array();
527 result["constraints"]["soft"] = json::array();
528 result["space"]["remesh"]["enabled"] = false;
529
530 result["output"]["advanced"]["timestep_prefix"] =
531 "solid_" + result["output"]["advanced"]["timestep_prefix"].get<std::string>();
532 return result;
533 }
534
536 {
537 const json &integrators = args["time"]["integrator"];
538 if (!integrators.is_array())
539 return integrators;
540 for (const json &integrator : integrators)
541 if (integrator.value("fe_space", -1) == fe_space_id)
542 {
543 json result = integrator;
544 result.erase("fe_space");
545 return result;
546 }
547 log_and_throw_error("Missing time integrator for FE space {}.", fe_space_id);
548 }
549
550 void NavierStokesFSIVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
551 {
552 if (has_solid_)
553 {
554 std::unique_ptr<mesh::Mesh> fluid_mesh, solid_mesh;
556 {
557 if (!checkpoint_reader_->get().exists("/checkpoint/meshes/solid"))
558 log_and_throw_error("Two-mesh NavierStokesFSI checkpoint is missing its solid mesh.");
559 fluid_mesh = mesh.copy();
560 solid_mesh = checkpoint_reader_->get().read_mesh(
561 "/checkpoint/meshes/solid", !mesh.is_conforming());
562 }
563 else
564 {
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)
569 {
570 if (piece.id == fluid_geometry_id_)
571 fluid_mesh = std::move(piece.mesh);
572 else if (piece.id == solid_geometry_id_)
573 solid_mesh = std::move(piece.mesh);
574 else
575 log_and_throw_error("Unexpected geometry ID {} in two-mesh NavierStokesFSI.", piece.id);
576 }
577 }
578 if (!fluid_mesh || !solid_mesh)
580 "Unable to find configured fluid/solid geometry IDs {}/{}.",
582
583 if (fluid_mesh->dimension() == 2)
584 {
586 dynamic_cast<const mesh::Mesh2D &>(*fluid_mesh),
587 dynamic_cast<const mesh::Mesh2D &>(*solid_mesh));
588 if (interface_2d_.empty())
589 log_and_throw_error("Configured 2D fluid and solid geometries do not share an interface.");
590 }
591 else
592 {
594 dynamic_cast<const mesh::Mesh3D &>(*fluid_mesh),
595 dynamic_cast<const mesh::Mesh3D &>(*solid_mesh));
596 if (interface_3d_.empty())
597 log_and_throw_error("Configured 3D fluid and solid geometries do not share an interface.");
598 }
599
600 mesh_ = std::move(fluid_mesh);
601 solid_varform_->set_mesh(std::move(solid_mesh));
602 }
603
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);
608 for (const auto &assembler : ale_assemblers_)
609 {
610 assembler->set_size(mesh_->dimension());
611 assembler->set_materials(body_ids, this->args["materials"], units, resources_);
612 }
613 const json mesh_materials = mesh_material_args();
614 mesh_elastic_assembler_->set_size(mesh_->dimension());
615 mesh_elastic_assembler_->set_materials(body_ids, mesh_materials, units, resources_);
616 mesh_mass_assembler_->set_size(mesh_->dimension());
617 mesh_mass_assembler_->set_materials(body_ids, mesh_materials, units, resources_);
618 mesh_pure_mass_assembler_->set_size(mesh_->dimension());
620 }
621
622 void NavierStokesFSIVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
623 {
624 FluidVarForm::build_basis(mesh, iso_parametric, args);
625 // The interface traction multiplier can exchange a constant normal
626 // traction with the fluid pressure. Keep an explicit physical-domain
627 // pressure reference in the coupled problem even when an outer boundary
628 // is marked Neumann.
629 if (has_solid_)
630 use_avg_pressure = true;
631 Eigen::VectorXi orders, ordersq;
632 assign_discr_orders(args["space"], mesh_displacement_space_id_, mesh, 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"],
644
646 <= args["solver"]["advanced"]["cache_size"])
647 {
651 }
652 else
653 {
657 }
659 logger().info("n mesh displacement bases: {}", mesh_displacement_space_.n_bases);
660 if (has_solid_)
661 {
662 solid_varform_->prepare_for_embedding();
664 logger().info("n solid displacement dofs: {}", solid_varform_->embedding_ndof());
665 logger().info(
666 "n FSI interface multiplier dofs: physical={}, mesh={}",
668 }
669 }
670
672 {
673 assert(has_solid_ && solid_varform_ && mesh_);
674 if (mesh_->dimension() != 2)
675 log_and_throw_error("Two-mesh FSI interface coupling currently supports 2D meshes.");
676 const io::OutputSpace solid_output = solid_varform_->output_space();
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);
680 const FESpace &solid_space = solid_varform_->embedding_space();
681 const int order = 2 * std::max({space_.disc_orders.maxCoeff(), mesh_displacement_space_.disc_orders.maxCoeff(), solid_space.disc_orders.maxCoeff()}) + 2;
682
683 const ScalarTraceOperators physical = assemble_2d_trace_operators(
684 fluid_mesh, solid_mesh, interface_2d_,
686 solid_space.basis_list(), solid_space.n_bases, order);
687 const ScalarTraceOperators computational = assemble_2d_trace_operators(
688 fluid_mesh, solid_mesh, interface_2d_,
690 solid_space.basis_list(), solid_space.n_bases, order);
691 const VectorTraceOperators physical_vector =
692 vector_trace(physical, mesh_->dimension(), boundary_.boundary_nodes);
693 const VectorTraceOperators computational_vector =
694 vector_trace(computational, mesh_->dimension(), mesh_displacement_boundary_.boundary_nodes);
695 interface_velocity_trace_ = physical_vector.source;
696 interface_solid_velocity_trace_ = physical_vector.solid;
697 interface_mesh_trace_ = computational_vector.source;
698 interface_solid_mesh_trace_ = computational_vector.solid;
699 if (interface_velocity_trace_.rows() == 0 || interface_mesh_trace_.rows() == 0)
700 log_and_throw_error("The fluid-solid interface has no active FE trace degrees of freedom.");
701 }
702
704 {
711 mesh.dimension());
712 std::vector<int> unused;
719 for (const int node : mesh_displacement_boundary_.dirichlet_nodes)
720 {
721 const int tag = mesh.get_node_id(node);
722 for (int d = 0; d < mesh.dimension(); ++d)
723 if (mesh_displacement_problem_->is_nodal_dimension_dirichlet(node, tag, d, mesh_displacement_space_id_))
724 mesh_displacement_boundary_.boundary_nodes.push_back(node * mesh.dimension() + d);
725 }
729 }
730
732 {
735 return;
736 json solver_params = args["solver"]["linear"];
737 if (!solver_params.contains("Pardiso"))
738 solver_params["Pardiso"] = {};
739 solver_params["Pardiso"]["mtype"] = -2;
740 mesh_rhs_assembler_ = std::make_shared<assembler::RhsAssembler>(
741 *mesh_elastic_assembler_, *mesh_, nullptr,
747 args["space"]["advanced"]["bc_method"], solver_params,
749 }
750
752 {
754 assert(mesh_rhs_assembler_);
756 mesh_rhs_ *= -1;
757 const Eigen::MatrixXd velocity_rhs = rhs_.topRows(primary_ndof());
758 rhs_.setZero(total_ndof(), 1);
759 rhs_.topRows(primary_ndof()) = velocity_rhs;
761 }
762
771
773 {
774 return mesh_ ? mesh_displacement_space_.n_bases * mesh_->dimension() : 0;
775 }
776
778 {
779 return has_solid_ && solid_varform_ ? solid_varform_->embedding_ndof() : 0;
780 }
781
786
791
798
800 {
801 if (sol.size() == 0)
802 {
803 Eigen::MatrixXd velocity, mesh_displacement, solid_displacement;
804 rhs_assembler_->initial_solution(velocity);
805 mesh_rhs_assembler_->initial_solution(mesh_displacement);
806 if (has_solid_)
807 solid_varform_->initial_solution_for_embedding(solid_displacement, "solid_");
808 sol.setZero(total_ndof(), 1);
809 sol.topRows(primary_ndof()) = velocity.topRows(primary_ndof()).leftCols(1);
811 mesh_displacement.topRows(mesh_displacement_ndof()).leftCols(1);
812 if (has_solid_)
814 solid_displacement.topRows(solid_displacement_ndof()).leftCols(1);
815 }
816 else
817 {
818 if (sol.cols() > 1)
819 sol.conservativeResize(Eigen::NoChange, 1);
820 if (sol.rows() != total_ndof())
821 {
822 const Eigen::MatrixXd input = sol;
823 sol.setZero(total_ndof(), 1);
824 const int rows = std::min<int>(input.rows(), total_ndof());
825 if (rows > 0)
826 sol.topRows(rows) = input.topRows(rows);
827 }
828 }
829 }
830
831 void NavierStokesFSIVarForm::build_forms(Eigen::MatrixXd &sol, const double t)
832 {
833 const int dim = mesh_->dimension();
834 const Eigen::VectorXd velocity = sol.topRows(primary_ndof());
835 const Eigen::VectorXd mesh_displacement = sol.middleRows(mesh_displacement_offset(), mesh_displacement_ndof());
836 Eigen::MatrixXd solid_displacement;
837 if (has_solid_)
838 {
839 solid_displacement = sol.middleRows(solid_displacement_offset(), solid_displacement_ndof());
840 solid_varform_->init_forms_for_embedding(solid_displacement, t, "solid_");
841 }
842
847 Eigen::MatrixXd velocity_initial_velocity, mesh_initial_velocity;
848 rhs_assembler_->initial_velocity(velocity_initial_velocity);
849 mesh_rhs_assembler_->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;
855 Eigen::MatrixXd mesh_history_acceleration = Eigen::MatrixXd::Zero(mesh_displacement_ndof(), 1);
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);
858 time_integrator = velocity_bdf;
861 time_integrator, "/checkpoint/state/primary_integrator", dt,
862 "primary", space_, dim);
864 mesh_displacement_time_integrator_, "/checkpoint/state/mesh_motion_integrator", dt,
865 "mesh_motion", mesh_displacement_space_, dim);
866 if (has_solid_ && solid_varform_->embedding_time_integrator())
868 solid_varform_->embedding_time_integrator(), "/checkpoint/state/solid_integrator", dt,
869 "solid", solid_varform_->space_, dim);
870
871 ale_form_ = std::make_shared<solver::NavierStokesFSIForm>(
876 t, dt, mesh_->is_volume(),
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_);
879 });
880 const int gorder = mesh_->orders().size() == 0 ? 1 : mesh_->orders().maxCoeff();
881 const QuadratureOrders velocity_samples = n_boundary_samples(
882 space_.disc_orders.maxCoeff(), space_.disc_ordersq.maxCoeff(), gorder);
883 ale_form_->set_velocity_tilde_updater(
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;
887 rhs_assembler_->set_bc(boundary_.local_boundary, boundary_.boundary_nodes, velocity_samples, empty_neumann, projected, Eigen::MatrixXd(), time);
888 target = projected.col(0);
889 });
890
891 auxiliary_form_ = std::make_shared<solver::StackedForm>();
892 const auto velocity_block = auxiliary_form_->add_block(primary_ndof());
894 const auto mesh_block = auxiliary_form_->add_block(mesh_displacement_ndof());
895 std::optional<solver::StackedForm::Block> solid_block;
896 if (has_solid_)
897 {
898 solid_block = auxiliary_form_->add_block(solid_displacement_ndof());
899 for (const auto &form : solid_varform_->embedding_forms())
900 auxiliary_form_->add(*solid_block, form);
903 }
904
905 const solver::ElementInversionCheck check = args["solver"]["advanced"]["check_inversion"];
906 mesh_elastic_form_ = std::make_shared<solver::ElasticForm>(
909 args["solver"]["advanced"]["jacobian_threshold"], check);
910 auxiliary_form_->add(mesh_block, mesh_elastic_form_);
911
912 fluid_zero_rhs_ = Eigen::MatrixXd::Zero(primary_ndof(), 1);
913 fluid_neumann_form_ = std::make_shared<solver::BodyForm>(
916 mass_assembler_->density(), false, true);
917 fluid_neumann_form_->update_quantities(t, velocity);
918 auxiliary_form_->add(velocity_block, fluid_neumann_form_);
919
920 const QuadratureOrders mesh_samples = n_boundary_samples(
922 mesh_displacement_space_.disc_ordersq.maxCoeff(), gorder);
923 mesh_body_form_ = std::make_shared<solver::BodyForm>(
927 mesh_rhs_, *mesh_rhs_assembler_, mesh_mass_assembler_->density(), false, true);
928 mesh_body_form_->update_quantities(t, mesh_displacement);
929 auxiliary_form_->add(mesh_block, mesh_body_form_);
930
932 {
933 auxiliary_form_->add_block(1);
934 average_pressure_form_ = std::make_shared<solver::NavierStokesFSIAveragePressureForm>(
940 }
941 else
942 average_pressure_form_ = nullptr;
943
944 if (has_solid_)
945 {
946 interface_form_ = std::make_shared<solver::FSIInterfaceForm>(
951 *time_integrator, *solid_varform_->embedding_time_integrator());
952 }
953 else
954 interface_form_ = nullptr;
955
957 if (interface_form_)
958 fsi_forms_.push_back(interface_form_);
961 for (const auto &form : fsi_forms_)
962 form->set_output_dir(output_path);
963 fsi_al_forms_.clear();
965 || (has_solid_ && !solid_varform_->embedding_al_forms().empty()))
966 {
967 auto stacked_al = std::make_shared<solver::StackedAugmentedLagrangianForm>();
968 const auto velocity_al = stacked_al->add_block(primary_ndof());
969 stacked_al->add_block(pressure_space_.n_bases);
970 const auto mesh_al = stacked_al->add_block(mesh_displacement_ndof());
971 std::optional<solver::StackedAugmentedLagrangianForm::Block> solid_al;
972 if (has_solid_)
973 {
974 solid_al = stacked_al->add_block(solid_displacement_ndof());
975 stacked_al->add_block(fluid_interface_multiplier_ndof());
976 stacked_al->add_block(mesh_interface_multiplier_ndof());
977 }
979 stacked_al->add_block(1);
980 if (!boundary_.boundary_nodes.empty())
981 stacked_al->add(velocity_al, std::make_shared<solver::BCLagrangianForm>(
983 velocity_samples, pure_mass_, *rhs_assembler_, 0, true, t));
985 stacked_al->add(mesh_al, std::make_shared<solver::BCLagrangianForm>(
988 mesh_samples, mesh_pure_mass_, *mesh_rhs_assembler_, 0, true, t));
989 if (has_solid_)
990 for (const auto &form : solid_varform_->embedding_al_forms())
991 stacked_al->add(*solid_al, form);
992 fsi_al_forms_.push_back(stacked_al);
993 }
994
995 fsi_problem_ = std::make_shared<solver::NLProblem>(
997 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
999 residual_mass(
1001 has_solid_ ? solid_varform_->embedding_norm_matrix() : StiffnessMatrix(),
1002 has_solid_ ? interface_form_->fluid_multiplier_mass() : StiffnessMatrix(),
1003 has_solid_ ? interface_form_->mesh_multiplier_mass() : StiffnessMatrix(),
1005 dim, true);
1006 fsi_problem_->init(sol);
1007 fsi_problem_->update_quantities(t, sol);
1009 stats.solver_info = json::array();
1010 }
1011
1013 {
1014 const double scale = time_integrator->acceleration_scaling();
1016 fluid_neumann_form_->set_weight(scale);
1018 average_pressure_form_->set_weight(scale);
1019 }
1020
1021 void NavierStokesFSIVarForm::solve_nonlinear_step(const int step, Eigen::MatrixXd &sol)
1022 {
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(
1026 nonlinear_params, args["solver"]["linear"], units.characteristic_length(), logger());
1027 solver::ALSolver al_solver(
1028 fsi_al_forms_, args["solver"]["augmented_lagrangian"]["initial_weight"],
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) {
1033 if (has_solid_)
1034 solid_varform_->update_barrier_stiffness_for_embedding(
1036 });
1037 al_solver.post_subsolve = [&](const double weight) {
1038 stats.solver_info.push_back({{"type", weight > 0 ? "al" : "rc"}, {"t", step}, {"info", nonlinear_solver->info()}});
1039 if (weight > 0)
1040 stats.solver_info.back()["weight"] = weight;
1041 save_subsolve(stats.solver_info.size(), step, sol);
1042 };
1043 if (!fsi_al_forms_.empty())
1044 al_solver.solve_al(*fsi_problem_, sol, al_params, args["solver"]["linear"], units.characteristic_length(), nonlinear_solver);
1045 al_solver.solve_reduced(*fsi_problem_, sol, nonlinear_params, args["solver"]["linear"], units.characteristic_length(), nonlinear_solver);
1046 }
1047
1049 Eigen::MatrixXd &sol,
1050 const InitialConditionOverride *initial_condition_override,
1051 const ForwardStepCallback &post_step)
1052 {
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");
1055
1056 igl::Timer timer;
1057 timer.start();
1059 build_forms(sol, t0 + dt);
1060 save_fsi_timestep(t0, 0, sol);
1061 for (int step = 1; step <= time_steps; ++step)
1062 {
1063 const double time = t0 + step * dt;
1064 logger().info("{}/{} steps, dt={}s t={}s", step, time_steps, dt, time);
1065 solve_nonlinear_step(step, sol);
1066 time_integrator->update_quantities(sol.topRows(primary_ndof()));
1067 mesh_displacement_time_integrator_->update_quantities(
1069 if (has_solid_)
1070 solid_varform_->advance_for_embedding(
1073 fsi_problem_->update_quantities(t0 + (step + 1) * dt, sol);
1074 save_fsi_timestep(time, step, sol);
1075 save_step_state(t0, dt, step, sol, time_integrator.get());
1077 }
1078 timer.stop();
1079 timings.solving_time = timer.getElapsedTime();
1080 }
1081
1083 const double time, const int step, const Eigen::MatrixXd &solution) const
1084 {
1085 if (!has_solid_)
1086 {
1087 save_timestep(time, step, t0, dt, solution);
1088 return;
1089 }
1090
1091 paraviewo::VTMWriter vtm(time);
1092 const bool fluid_saved = save_timestep_to_vtm(time, step, dt, solution, vtm, "Fluid");
1093 const bool solid_saved = solid_varform_->save_timestep_for_embedding(
1094 time, step, dt,
1095 solution.middleRows(solid_displacement_offset(), solid_displacement_ndof()),
1096 vtm, "Solid");
1097 if (!fluid_saved && !solid_saved)
1098 return;
1099
1100 const int global_t = output_file_index(step);
1101 const std::string step_name = args["output"]["advanced"]["timestep_prefix"];
1102 vtm.save(resolve_output_path(fmt::format(step_name + "{:d}.vtm", global_t)));
1104 resolve_output_path(args["output"]["paraview"]["file_name"]),
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>());
1107 }
1108
1109 std::vector<io::OutputField> NavierStokesFSIVarForm::output_fields(
1110 const io::OutputSample &sample,
1111 const Eigen::MatrixXd &solution,
1112 const io::OutputFieldOptions &options) const
1113 {
1114 std::vector<io::OutputField> fields = FluidVarForm::output_fields(sample, solution, options);
1115 if (!mesh_ || solution.rows() < mesh_displacement_offset() + mesh_displacement_ndof()
1116 || !options.export_field("mesh_displacement"))
1117 return fields;
1118
1119 const int dim = mesh_->dimension();
1120 const Eigen::MatrixXd mesh_displacement =
1121 solution.middleRows(mesh_displacement_offset(), mesh_displacement_ndof());
1122 const bool has_element_samples =
1123 sample.local_points.rows() > 0 && sample.local_points.rows() == sample.element_ids.size();
1124 const int output_rows = sample.points.rows() > 0
1125 ? sample.points.rows()
1126 : std::max<int>(sample.local_points.rows(), sample.node_ids.size());
1127 Eigen::MatrixXd values;
1128
1129 if (has_element_samples)
1130 {
1131 values.setZero(output_rows, dim);
1132 for (int i = 0; i < sample.local_points.rows(); ++i)
1133 {
1134 const int element = sample.element_ids(i);
1135 if (element < 0)
1136 continue;
1137 Eigen::MatrixXd local_value, local_gradient;
1139 *mesh_, dim,
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);
1145 }
1146 }
1147 else if (sample.node_ids.size() > 0)
1148 {
1149 values.resize(sample.node_ids.size(), dim);
1150 for (int i = 0; i < sample.node_ids.size(); ++i)
1151 {
1152 const int node = sample.node_ids(i);
1153 if (node < 0 || node * dim + dim > mesh_displacement.rows())
1154 return fields;
1155 values.row(i) = mesh_displacement.block(node * dim, 0, dim, 1).transpose();
1156 }
1157 }
1158 else
1159 {
1160 return fields;
1161 }
1162
1163 fields.push_back({"mesh_displacement", values, io::OutputField::Association::Point});
1164 return fields;
1165 }
1166} // 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.
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
Definition OutData.cpp:2621
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:50
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.
Definition Mesh.cpp:26
int dimension() const
utily for dimension
Definition Mesh.hpp:138
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:475
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::shared_ptr< solver::NLProblem > fsi_problem_
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_
void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const override
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 solve_problem(Eigen::MatrixXd &sol, const InitialConditionOverride *initial_condition_override, const ForwardStepCallback &post_step) override
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_
void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution) override
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.
void reorder_checkpoint_block(const io::CheckpointReader &reader, const std::string &name, const FESpace &space, int block_size, int row_offset, Eigen::MatrixXd &value) const
Definition VarForm.cpp:981
int output_file_index(const int t) const
Definition VarForm.cpp:1111
static void rebuild_node_positions(const std::vector< basis::ElementBases > &bases, const std::vector< int > &node_ids, std::vector< RowVectorNd > &positions)
Definition VarForm.cpp:1125
void save_step_state(const double t0, const double dt, const int t, const Eigen::MatrixXd &solution, const time_integrator::ImplicitTimeIntegrator *time_integrator) const
Definition VarForm.cpp:863
void write_checkpoint_ordering(io::CheckpointWriter &writer, const std::string &name, const FESpace &space) const
Definition VarForm.cpp:938
virtual void deserialize_checkpoint(const io::CheckpointReader &reader, Eigen::MatrixXd &solution)
Definition VarForm.cpp:905
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:273
void assign_discr_orders(const json &space_args, const mesh::Mesh &mesh, Eigen::VectorXi &disc_orders, Eigen::VectorXi &disc_ordersq)
Definition VarForm.cpp:684
io::OutStatsData stats
Definition VarForm.hpp:265
void notify_time_step(const int t, const int time_steps, const double t0, const double dt) const
Definition VarForm.cpp:1105
io::OutGeometryData output_geometry_
Definition VarForm.hpp:277
void init_child_varform(VarForm &child, const std::string &formulation, const Units &units, const json &args, const std::string &out_path) const
Definition VarForm.cpp:249
void save_subsolve(const int i, const int t, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:1086
void restore_checkpoint_integrator(const std::shared_ptr< time_integrator::ImplicitTimeIntegrator > &integrator, const std::string &group, double dt, const std::string &ordering, const FESpace &space, int block_size) const
Definition VarForm.cpp:917
virtual void serialize_checkpoint(io::CheckpointWriter &writer, const Eigen::MatrixXd &solution, const io::CheckpointMetadata &metadata) const
Definition VarForm.cpp:897
QuadratureOrders n_boundary_samples(const int discr_order, const int discr_orderq, const int gdiscr_order) const
Definition VarForm.cpp:185
std::optional< std::reference_wrapper< const io::CheckpointReader > > checkpoint_reader_
Definition VarForm.hpp:271
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:274
std::string resolve_output_path(const std::string &path) const
Definition VarForm.cpp:1116
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:1063
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &solution) const
Definition VarForm.cpp:1047
void validate_checkpoint_solution(const Eigen::MatrixXd &solution, int expected_rows) const
Definition VarForm.cpp:1037
io::OutRuntimeData timings
runtime statistics
Definition VarForm.hpp:268
const io::ResourceIO & resources_
Definition VarForm.hpp:283
int norm
Definition p_bases.py:265
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.
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:53
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