Fluid structure interaction suite
stokes.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2022 by Luca Heltai
4 //
5 // This file is part of the FSI-suite platform, based on the deal.II library.
6 //
7 // The FSI-suite platform is free software; you can use it, redistribute it,
8 // and/or modify it under the terms of the GNU Lesser General Public License as
9 // published by the Free Software Foundation; either version 3.0 of the License,
10 // or (at your option) any later version. The full text of the license can be
11 // found in the file LICENSE at the top level of the FSI-suite platform
12 // distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include "pdes/serial/stokes.h"
17 
19 
22 
24 
25 using namespace dealii;
26 namespace PDEs
27 {
28  namespace Serial
29  {
30  template <int dim>
32  : ParameterAcceptor("/Stokes/")
33  , component_names(
34  ParsedTools::Components::blocks_to_names({"u", "p"}, {dim, 1}))
35  , grid_generator("/Stokes/Grid")
36  , grid_refinement("/Stokes/Grid/Refinement")
37  , finite_element("/Stokes/",
38  component_names,
39  "FESystem[FE_Q(2)^d-FE_Q(1)]")
40  , dof_handler(triangulation)
41  , inverse_operator("/Stokes/Solver")
42  , velocity_preconditioner("/Stokes/Solver/AMG Velocity preconditioner")
43  , schur_preconditioner("/Stokes/Solver/AMG Schur preconditioner")
44  , constants("/Stokes/Constants", {"eta"}, {1.0}, {"Viscosity"})
45  , forcing_term(
46  "/Stokes/Functions",
47  ParsedTools::Components::join(std::vector<std::string>(dim + 1, "0"),
48  ";"),
49  "Forcing term")
50  , exact_solution(
51  "/Stokes/Functions",
52  ParsedTools::Components::join(std::vector<std::string>(dim + 1, "0"),
53  ";"),
54  "Exact solution")
55  , boundary_conditions(
56  "/Stokes/Boundary conditions",
57  component_names,
59  {"u"},
61  {ParsedTools::Components::join(std::vector<std::string>(dim + 1, "0"),
62  ";")})
63  , error_table(Utilities::split_string_list(component_names),
66  , data_out("/Stokes/Output")
67  , velocity(0)
68  , pressure(dim)
69  {
70  enter_subsection("Error table (" + component_names + ")");
71  enter_my_subsection(this->prm);
72  error_table.add_parameters(this->prm);
73  leave_my_subsection(this->prm);
74  leave_subsection();
75  add_parameter("Console level", this->console_level);
76  }
77 
78 
79 
80  template <int dim>
81  void
83  {
84  deallog << "System setup" << std::endl;
85  // No mixed grids
86  const auto ref_cells = triangulation.get_reference_cells();
88  ref_cells.size() == 1,
89  ExcMessage(
90  "This program does nots support mixed simplx/hex grid types."));
91 
92  // Compatible FE space and grid.
93  AssertThrow(finite_element().reference_cell() == ref_cells[0],
94  ExcMessage("The finite element must be defined on the same "
95  "cell type as the grid."));
96 
97  boundary_conditions.update_user_substitution_map(constants);
98  exact_solution.update_constants(constants);
99  forcing_term.update_constants(constants);
100 
101  dof_handler.distribute_dofs(finite_element);
102  auto blocks = ParsedTools::Components::block_indices(component_names,
103  component_names);
104 
105  DoFRenumbering::component_wise(this->dof_handler, blocks);
106 
107  dofs_per_block =
108  DoFTools::count_dofs_per_fe_block(this->dof_handler, blocks);
109 
110  mapping = get_default_linear_mapping(triangulation).clone();
111 
112  deallog << "Number of dofs " << dof_handler.n_dofs() << "("
113  << dofs_per_block[0] << " + " << dofs_per_block[1] << ")"
114  << std::endl;
115 
116  constraints.clear();
117  DoFTools::make_hanging_node_constraints(dof_handler, constraints);
118  boundary_conditions.check_consistency(triangulation);
119  boundary_conditions.apply_essential_boundary_conditions(*mapping,
120  dof_handler,
121  constraints);
122  constraints.close();
123 
124 
125  BlockDynamicSparsityPattern dsp(dofs_per_block, dofs_per_block);
126  DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
127  sparsity_pattern.copy_from(dsp);
128  system_matrix.reinit(sparsity_pattern);
129  solution.reinit(dofs_per_block);
130  system_rhs.reinit(dofs_per_block);
131  boundary_conditions.apply_natural_boundary_conditions(
132  *mapping, dof_handler, constraints, system_matrix, system_rhs);
133  }
134 
135 
136 
137  template <int dim>
138  void
140  {
141  deallog << "Assemble system" << std::endl;
142  const ReferenceCell cell_type = finite_element().reference_cell();
143 
144  const Quadrature<dim> quadrature_formula =
145  cell_type.get_gauss_type_quadrature<dim>(
146  finite_element().tensor_degree() + 1);
147 
148  Quadrature<dim - 1> face_quadrature_formula;
149  if constexpr (dim > 1)
150  {
151  // TODO: make sure we work also for wedges and pyramids
152  const ReferenceCell face_type = cell_type.face_reference_cell(0);
153  face_quadrature_formula =
154  face_type.get_gauss_type_quadrature<dim - 1>(
155  finite_element().tensor_degree() + 1);
156  }
157  else
158  {
159  face_quadrature_formula =
160  QGauss<dim - 1>(finite_element().tensor_degree() + 1);
161  }
162 
163  FEValues<dim, dim> fe_values(*mapping,
164  finite_element,
165  quadrature_formula,
169 
170  const unsigned int dofs_per_cell = finite_element().n_dofs_per_cell();
171  FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
172  Vector<double> cell_rhs(dofs_per_cell);
173  std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
174  for (const auto &cell : dof_handler.active_cell_iterators())
175  {
176  fe_values.reinit(cell);
177  cell_matrix = 0;
178  cell_rhs = 0;
179  for (const unsigned int q_index :
180  fe_values.quadrature_point_indices())
181  {
182  for (const unsigned int i : fe_values.dof_indices())
183  {
184  const auto &eps_v = fe_values[velocity].gradient(i, q_index);
185  const auto &div_v =
186  fe_values[velocity].divergence(i, q_index);
187  const auto &q = fe_values[pressure].value(i, q_index);
188 
189  for (const unsigned int j : fe_values.dof_indices())
190  {
191  const auto &eps_u =
192  fe_values[velocity].gradient(j, q_index);
193  const auto &div_u =
194  fe_values[velocity].divergence(j, q_index);
195  const auto &p = fe_values[pressure].value(j, q_index);
196  cell_matrix(i, j) +=
197  (constants["eta"] * scalar_product(eps_v, eps_u) -
198  p * div_v - q * div_u + q * p / constants["eta"]) *
199  fe_values.JxW(q_index); // dx
200  }
201 
202  cell_rhs(i) +=
203  (fe_values.shape_value(i, q_index) * // phi_i(x_q)
204  forcing_term.value(fe_values.quadrature_point(q_index),
205  finite_element()
206  .system_to_component_index(i)
207  .first) * // f(x_q)
208  fe_values.JxW(q_index)); // dx
209  }
210  }
211  cell->get_dof_indices(local_dof_indices);
212  constraints.distribute_local_to_global(cell_matrix,
213  cell_rhs,
214  local_dof_indices,
215  system_matrix,
216  system_rhs);
217  }
218  }
219 
220 
221 
222  template <int dim>
223  void
225  {
226  deallog << "Solve system" << std::endl;
227 
228  // SparseDirectUMFPACK A_direct;
229  // A_direct.initialize(system_matrix);
230  // A_direct.vmult(solution, system_rhs);
231  // constraints.distribute(solution);
232 
233  velocity_preconditioner.initialize(system_matrix.block(0, 0));
234  schur_preconditioner.initialize(system_matrix.block(1, 1));
235  deallog << "Preconditioners initialized" << std::endl;
236 
237  auto precA =
238  linear_operator(system_matrix.block(0, 0), velocity_preconditioner);
239 
240  auto precS =
241  -1 * linear_operator(system_matrix.block(1, 1), schur_preconditioner);
242 
243  // Force compiler to understand what's going on...
244  std::array<decltype(precA), 2> tmp_prec = {{precA, precS}};
245  const auto diagprecAA = block_diagonal_operator<2>(tmp_prec);
246 
247  const auto A = linear_operator(system_matrix.block(0, 0));
248  const auto B = linear_operator(system_matrix.block(1, 0));
249  const auto Bt = linear_operator(system_matrix.block(0, 1));
250  const auto M = linear_operator(system_matrix.block(1, 1));
251  const auto Zero = 0 * M;
252 
253  const auto AA = block_operator<2, 2>({{{{A, Bt}}, {{B, Zero}}}});
254 
255  // If we use gmres or another non symmetric solver, use a non-symmetric
256  // preconditioner
257  if (inverse_operator.get_solver_name() != "cg")
258  {
259  const auto precAA = block_back_substitution(AA, diagprecAA);
260  const auto inv = inverse_operator(AA, precAA);
261  solution = inv * system_rhs;
262  }
263  else
264  {
265  const auto inv = inverse_operator(AA, diagprecAA);
266  solution = inv * system_rhs;
267  }
268 
269  constraints.distribute(solution);
270 
271  // const auto A = linear_operator<Vector<double>>(system_matrix.block(0,
272  // 0)); const auto invA = inner_inverse_operator(A,
273  // velocity_preconditioner);
274 
275  // // Build the schur complement
276  // const auto Bt =
277  // linear_operator<Vector<double>>(system_matrix.block(0, 1));
278  // const auto B = linear_operator<Vector<double>>(system_matrix.block(1,
279  // 0)); const auto Mp =
280  // linear_operator<Vector<double>>(system_matrix.block(1, 1));
281 
282  // const auto S = B * invA * Bt;
283  // const auto invS = outer_inverse_operator(S, schur_preconditioner);
284 
285  // auto & u = solution.block(0);
286  // auto & p = solution.block(1);
287  // const auto &f = system_rhs.block(0);
288  // const auto &g = system_rhs.block(1);
289 
290  // deallog << "Solving schur complement" << std::endl;
291 
292  // p = invS * (B * invA * f - g);
293  // deallog << "Computed p (norm = " << p.l2_norm() << ")" << std::endl;
294 
295  // deallog << "Solving for velocity" << std::endl;
296  // u = invA * (f - Bt * p);
297  // deallog << "Computed u (norm = " << u.l2_norm() << ")"
298  // << ")" << std::endl;
299 
300  // constraints.distribute(solution);
301  }
302 
303 
304 
305  template <int dim>
306  void
307  Stokes<dim>::output_results(const unsigned cycle) const
308  {
309  deallog << "Output results" << std::endl;
310  // Save each cycle in its own file
311  const auto suffix =
314  grid_refinement.get_n_refinement_cycles()));
315  data_out.attach_dof_handler(dof_handler, suffix);
316  data_out.add_data_vector(solution, component_names);
317  data_out.write_data_and_clear(*mapping);
318  }
319 
320 
321 
322  template <int dim>
323  void
325  {
326  deallog.pop();
327  deallog.depth_console(console_level);
328  grid_generator.generate(triangulation);
329  for (unsigned int cycle = 0;
330  cycle < grid_refinement.get_n_refinement_cycles();
331  ++cycle)
332  {
333  deallog.push("Cycle " + Utilities::int_to_string(cycle));
334  setup_system();
335  assemble_system();
336  solve();
337  error_table.error_from_exact(dof_handler, solution, exact_solution);
338  output_results(cycle);
339  if (cycle < grid_refinement.get_n_refinement_cycles() - 1)
340  grid_refinement.estimate_mark_refine(*mapping,
341  dof_handler,
342  solution,
343  triangulation);
344  deallog.pop();
345  }
346  error_table.output_table(std::cout);
347  }
348 
349  template class Stokes<2>;
350  template class Stokes<3>;
351  } // namespace Serial
352 } // namespace PDEs
const Point< spacedim > & quadrature_point(const unsigned int q_point) const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > dof_indices() const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > quadrature_point_indices() const
double JxW(const unsigned int q_point) const
const double & shape_value(const unsigned int i, const unsigned int q_point) const
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
void push(const std::string &text)
unsigned int depth_console(const unsigned int n)
void pop()
Quadrature< dim > get_gauss_type_quadrature(const unsigned n_points_1d) const
ReferenceCell face_reference_cell(const unsigned int face_no) const
void output_results(const unsigned cycle) const
Definition: stokes.cc:307
void assemble_system()
Definition: stokes.cc:139
void setup_system()
Definition: stokes.cc:82
Point< 2 > first
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
LinearOperator< Domain, Range, Payload > inverse_operator(const LinearOperator< Range, Domain, Payload > &op, Solver &solver, const Preconditioner &preconditioner)
LinearOperator< Domain, Range, typename BlockPayload::BlockType > block_back_substitution(const BlockLinearOperator< Range, Domain, BlockPayload > &block_operator, const BlockLinearOperator< Domain, Range, BlockPayload > &diagonal_inverse)
LinearOperator< Range, Domain, TrilinosWrappers::internal::LinearOperatorImplementation::TrilinosPayload > linear_operator(const TrilinosWrappers::SparseMatrix &operator_exemplar, const Matrix &matrix)
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
update_values
update_JxW_values
update_gradients
update_quadrature_points
LogStream deallog
const Mapping< dim, spacedim > & get_default_linear_mapping(const Triangulation< dim, spacedim > &triangulation)
void component_wise(DoFHandler< dim, spacedim > &dof_handler, const std::vector< unsigned int > &target_component=std::vector< unsigned int >())
std::vector< types::global_dof_index > count_dofs_per_fe_block(const DoFHandler< dim, spacedim > &dof, const std::vector< unsigned int > &target_block=std::vector< unsigned int >())
void reference_cell(Triangulation< dim, spacedim > &tria, const ReferenceCell &reference_cell)
std::vector< std::string > split_string_list(const std::string &s, const std::string &delimiter=",")
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
unsigned int needed_digits(const unsigned int max_number)
PDEs::Stokes< dim, LAC::LAPETSc > Stokes
Definition: stokes.h:80
We collect in this namespace all PDEs that are relevant to Fluid Structure Interaction Problems.
std::string blocks_to_names(const std::vector< std::string > &components, const std::vector< unsigned int > &multiplicities)
Build component names from block names and multiplicities.
Definition: components.cc:40
std::vector< unsigned int > block_indices(const std::string &component_names, const std::string &selected_components)
Return the indices within the block corresponding to the given selected components,...
Definition: components.cc:134
std::string join(const Container &strings, const std::string &separator)
Join strings in a container together using a given separator.
We collect in this namespace some wrappers around commonly used deal.II classes, derived from the Par...
const types::boundary_id internal_face_boundary_id
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation