deal.II version GIT relicensing-2173-gae8fc9d14b 2024-11-24 06:40:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
NavierStokes_TRBDF2_DG.h
Go to the documentation of this file.
1
164 *  
165 * @endcode
166 *
167 * We start by including all the necessary deal.II header files.
168 *
169
170 *
171 *
172 * @code
173 *   #include <deal.II/base/point.h>
174 *   #include <deal.II/base/function.h>
175 *  
176 * @endcode
177 *
178 *
179 * <a name="equation_data.h-"></a>
180 * @sect{Equation data}
181 *
182
183 *
184 * In the next namespace, we declare and implement suitable functions that may be used for the initial and boundary conditions
185 *
186
187 *
188 *
189 * @code
190 *   namespace EquationData {
191 *   using namespace dealii;
192 *  
193 *   static const unsigned int degree_p = 1; /*--- Polynomial degree for the pressure. The one for the velocity
194 *   will be equal to degree_p + 1, but its value can be easily changed
195 *   in the template parameter list of the classes with weak form ---*/
196 *  
197 * @endcode
198 *
199 * We declare class that describes the boundary conditions and initial one for velocity:
200 *
201
202 *
203 *
204 * @code
205 *   template<int dim>
206 *   class Velocity: public Function<dim> {
207 *   public:
208 *   Velocity(const double initial_time = 0.0);
209 *  
210 *   virtual double value(const Point<dim>& p,
211 *   const unsigned int component = 0) const override;
212 *  
213 *   virtual void vector_value(const Point<dim>& p,
214 *   Vector<double>& values) const override;
215 *   };
216 *  
217 *  
218 *   template<int dim>
219 *   Velocity<dim>::Velocity(const double initial_time): Function<dim>(dim, initial_time) {}
220 *  
221 *  
222 *   template<int dim>
223 *   double Velocity<dim>::value(const Point<dim>& p, const unsigned int component) const {
224 *   AssertIndexRange(component, 3);
225 *   if(component == 0) {
226 *   const double Um = 1.5;
227 *   const double H = 4.1;
228 *  
229 *   return 4.0*Um*p(1)*(H - p(1))/(H*H);
230 *   }
231 *   else
232 *   return 0.0;
233 *   }
234 *  
235 *  
236 *   template<int dim>
237 *   void Velocity<dim>::vector_value(const Point<dim>& p, Vector<double>& values) const {
238 *   Assert(values.size() == dim, ExcDimensionMismatch(values.size(), dim));
239 *  
240 *   for(unsigned int i = 0; i < dim; ++i)
241 *   values[i] = value(p, i);
242 *   }
243 *  
244 *  
245 * @endcode
246 *
247 * We do the same for the pressure
248 *
249
250 *
251 *
252 * @code
253 *   template<int dim>
254 *   class Pressure: public Function<dim> {
255 *   public:
256 *   Pressure(const double initial_time = 0.0);
257 *  
258 *   virtual double value(const Point<dim>& p,
259 *   const unsigned int component = 0) const override;
260 *   };
261 *  
262 *  
263 *   template<int dim>
264 *   Pressure<dim>::Pressure(const double initial_time): Function<dim>(1, initial_time) {}
265 *  
266 *  
267 *   template<int dim>
268 *   double Pressure<dim>::value(const Point<dim>& p, const unsigned int component) const {
269 *   (void)component;
270 *   AssertIndexRange(component, 1);
271 *  
272 *   return 22.0 - p(0);
273 *   }
274 *  
275 *   } // namespace EquationData
276 * @endcode
277
278
279<a name="ann-navier_stokes_TRBDF2_DG.cc"></a>
280<h1>Annotated version of navier_stokes_TRBDF2_DG.cc</h1>
281 *
282 *
283 *
284 *
285 * @code
286 *   /* -----------------------------------------------------------------------------
287 *   *
288 *   * SPDX-License-Identifier: LGPL-2.1-or-later
289 *   * Copyright (C) 2022 by Giuseppe Orlando
290 *   *
291 *   * This file is part of the deal.II code gallery.
292 *   *
293 *   * -----------------------------------------------------------------------------
294 *   *
295 *   * Author: Giuseppe Orlando, 2022.
296 *   */
297 *  
298 * @endcode
299 *
300 * We start by including all the necessary deal.II header files and some C++
301 * related ones.
302 *
303
304 *
305 *
306 * @code
307 *   #include <deal.II/base/quadrature_lib.h>
308 *   #include <deal.II/base/multithread_info.h>
309 *   #include <deal.II/base/thread_management.h>
310 *   #include <deal.II/base/work_stream.h>
311 *   #include <deal.II/base/parallel.h>
312 *   #include <deal.II/base/utilities.h>
313 *   #include <deal.II/base/conditional_ostream.h>
314 *  
315 *   #include <deal.II/lac/vector.h>
316 *   #include <deal.II/lac/solver_cg.h>
317 *   #include <deal.II/lac/precondition.h>
318 *   #include <deal.II/lac/solver_gmres.h>
319 *   #include <deal.II/lac/affine_constraints.h>
320 *  
321 *   #include <deal.II/grid/tria.h>
322 *   #include <deal.II/grid/grid_generator.h>
323 *   #include <deal.II/grid/grid_tools.h>
324 *   #include <deal.II/grid/grid_refinement.h>
325 *   #include <deal.II/grid/tria_accessor.h>
326 *   #include <deal.II/grid/tria_iterator.h>
327 *   #include <deal.II/distributed/grid_refinement.h>
328 *  
329 *   #include <deal.II/dofs/dof_handler.h>
330 *   #include <deal.II/dofs/dof_accessor.h>
331 *   #include <deal.II/dofs/dof_tools.h>
332 *  
333 *   #include <deal.II/fe/fe_q.h>
334 *   #include <deal.II/fe/fe_dgq.h>
335 *   #include <deal.II/fe/fe_values.h>
336 *   #include <deal.II/fe/fe_tools.h>
337 *   #include <deal.II/fe/fe_system.h>
338 *  
339 *   #include <deal.II/numerics/matrix_tools.h>
340 *   #include <deal.II/numerics/vector_tools.h>
341 *   #include <deal.II/numerics/data_out.h>
342 *  
343 *   #include <fstream>
344 *   #include <cmath>
345 *   #include <iostream>
346 *  
347 *   #include <deal.II/matrix_free/matrix_free.h>
348 *   #include <deal.II/matrix_free/operators.h>
349 *   #include <deal.II/matrix_free/fe_evaluation.h>
350 *   #include <deal.II/fe/component_mask.h>
351 *  
352 *   #include <deal.II/base/timer.h>
353 *   #include <deal.II/distributed/solution_transfer.h>
354 *   #include <deal.II/numerics/error_estimator.h>
355 *  
356 *   #include <deal.II/multigrid/multigrid.h>
357 *   #include <deal.II/multigrid/mg_transfer_matrix_free.h>
358 *   #include <deal.II/multigrid/mg_tools.h>
359 *   #include <deal.II/multigrid/mg_coarse.h>
360 *   #include <deal.II/multigrid/mg_smoother.h>
361 *   #include <deal.II/multigrid/mg_matrix.h>
362 *  
363 *   #include <deal.II/meshworker/mesh_loop.h>
364 *  
365 *   #include "runtime_parameters.h"
366 *   #include "equation_data.h"
367 *  
368 *   namespace MatrixFreeTools {
369 *   using namespace dealii;
370 *  
371 *   template<int dim, typename Number, typename VectorizedArrayType>
373 *   LinearAlgebra::distributed::Vector<Number>& diagonal_global,
374 *   const std::function<void(const MatrixFree<dim, Number, VectorizedArrayType>&,
376 *   const unsigned int&,
377 *   const std::pair<unsigned int, unsigned int>&)>& cell_operation,
378 *   const std::function<void(const MatrixFree<dim, Number, VectorizedArrayType>&,
380 *   const unsigned int&,
381 *   const std::pair<unsigned int, unsigned int>&)>& face_operation,
382 *   const std::function<void(const MatrixFree<dim, Number, VectorizedArrayType>&,
384 *   const unsigned int&,
385 *   const std::pair<unsigned int, unsigned int>&)>& boundary_operation,
386 *   const unsigned int dof_no = 0) {
387 * @endcode
388 *
389 * initialize vector
390 *
391 * @code
392 *   matrix_free.initialize_dof_vector(diagonal_global, dof_no);
393 *  
394 *   const unsigned int dummy = 0;
395 *  
396 *   matrix_free.loop(cell_operation, face_operation, boundary_operation,
397 *   diagonal_global, dummy, false,
400 *   }
401 *   }
402 *  
403 * @endcode
404 *
405 * We include the code in a suitable namespace:
406 *
407
408 *
409 *
410 * @code
411 *   namespace NS_TRBDF2 {
412 *   using namespace dealii;
413 *  
414 * @endcode
415 *
416 * The following class is an auxiliary one for post-processing of the vorticity
417 *
418
419 *
420 *
421 * @code
422 *   template<int dim>
423 *   class PostprocessorVorticity: public DataPostprocessor<dim> {
424 *   public:
425 *   virtual void evaluate_vector_field(const DataPostprocessorInputs::Vector<dim>& inputs,
426 *   std::vector<Vector<double>>& computed_quantities) const override;
427 *  
428 *   virtual std::vector<std::string> get_names() const override;
429 *  
430 *   virtual std::vector<DataComponentInterpretation::DataComponentInterpretation>
431 *   get_data_component_interpretation() const override;
432 *  
433 *   virtual UpdateFlags get_needed_update_flags() const override;
434 *   };
435 *  
436 * @endcode
437 *
438 * This function evaluates the vorticty in both 2D and 3D cases
439 *
440
441 *
442 *
443 * @code
444 *   template <int dim>
445 *   void PostprocessorVorticity<dim>::evaluate_vector_field(const DataPostprocessorInputs::Vector<dim>& inputs,
446 *   std::vector<Vector<double>>& computed_quantities) const {
447 *   const unsigned int n_quadrature_points = inputs.solution_values.size();
448 *  
449 *   /*--- Check the correctness of all data structures ---*/
450 *   Assert(inputs.solution_gradients.size() == n_quadrature_points, ExcInternalError());
451 *   Assert(computed_quantities.size() == n_quadrature_points, ExcInternalError());
452 *  
453 *   Assert(inputs.solution_values[0].size() == dim, ExcInternalError());
454 *  
455 *   if(dim == 2) {
456 *   Assert(computed_quantities[0].size() == 1, ExcInternalError());
457 *   }
458 *   else {
459 *   Assert(computed_quantities[0].size() == dim, ExcInternalError());
460 *   }
461 *  
462 *   /*--- Compute the vorticty ---*/
463 *   if(dim == 2) {
464 *   for(unsigned int q = 0; q < n_quadrature_points; ++q)
465 *   computed_quantities[q](0) = inputs.solution_gradients[q][1][0] - inputs.solution_gradients[q][0][1];
466 *   }
467 *   else {
468 *   for(unsigned int q = 0; q < n_quadrature_points; ++q) {
469 *   computed_quantities[q](0) = inputs.solution_gradients[q][2][1] - inputs.solution_gradients[q][1][2];
470 *   computed_quantities[q](1) = inputs.solution_gradients[q][0][2] - inputs.solution_gradients[q][2][0];
471 *   computed_quantities[q](2) = inputs.solution_gradients[q][1][0] - inputs.solution_gradients[q][0][1];
472 *   }
473 *   }
474 *   }
475 *  
476 * @endcode
477 *
478 * This auxiliary function is required by the base class DataProcessor and simply
479 * sets the name for the output file
480 *
481
482 *
483 *
484 * @code
485 *   template<int dim>
486 *   std::vector<std::string> PostprocessorVorticity<dim>::get_names() const {
487 *   std::vector<std::string> names;
488 *   names.emplace_back("vorticity");
489 *   if(dim == 3) {
490 *   names.emplace_back("vorticity");
491 *   names.emplace_back("vorticity");
492 *   }
493 *  
494 *   return names;
495 *   }
496 *  
497 * @endcode
498 *
499 * This auxiliary function is required by the base class DataProcessor and simply
500 * specifies if the vorticity is a scalar (2D) or a vector (3D)
501 *
502
503 *
504 *
505 * @code
506 *   template<int dim>
507 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
508 *   PostprocessorVorticity<dim>::get_data_component_interpretation() const {
509 *   std::vector<DataComponentInterpretation::DataComponentInterpretation> interpretation;
510 *   if(dim == 2)
511 *   interpretation.push_back(DataComponentInterpretation::component_is_scalar);
512 *   else {
516 *   }
517 *  
518 *   return interpretation;
519 *   }
520 *  
521 * @endcode
522 *
523 * This auxiliary function is required by the base class DataProcessor and simply
524 * sets which variables have to updated (only the gradients)
525 *
526
527 *
528 *
529 * @code
530 *   template<int dim>
531 *   UpdateFlags PostprocessorVorticity<dim>::get_needed_update_flags() const {
532 *   return update_gradients;
533 *   }
534 *  
535 *  
536 * @endcode
537 *
538 * The following structs are auxiliary objects for mesh refinement. ScratchData simply sets
539 * the FEValues object
540 *
541
542 *
543 *
544 * @code
545 *   template <int dim>
546 *   struct ScratchData {
547 *   ScratchData(const FiniteElement<dim>& fe,
548 *   const unsigned int quadrature_degree,
549 *   const UpdateFlags update_flags): fe_values(fe, QGauss<dim>(quadrature_degree), update_flags) {}
550 *  
551 *   ScratchData(const ScratchData<dim>& scratch_data): fe_values(scratch_data.fe_values.get_fe(),
552 *   scratch_data.fe_values.get_quadrature(),
553 *   scratch_data.fe_values.get_update_flags()) {}
554 *   FEValues<dim> fe_values;
555 *   };
556 *  
557 *  
558 * @endcode
559 *
560 * CopyData simply sets the cell index
561 *
562
563 *
564 *
565 * @code
566 *   struct CopyData {
567 *   CopyData() : cell_index(numbers::invalid_unsigned_int), value(0.0) {}
568 *  
569 *   CopyData(const CopyData &) = default;
570 *  
571 *   unsigned int cell_index;
572 *   double value;
573 *   };
574 *  
575 *  
576 * @endcode
577 *
578 *
579 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
580 * @sect{ <code>NavierStokesProjectionOperator::NavierStokesProjectionOperator</code> }
581 *
582
583 *
584 * The following class sets effectively the weak formulation of the problems for the different stages
585 * and for both velocity and pressure.
586 * The template parameters are the dimnesion of the problem, the polynomial degree for the pressure,
587 * the polynomial degree for the velocity, the number of quadrature points for integrals for the pressure step,
588 * the number of quadrature points for integrals for the velocity step, the type of vector for storage and the type
589 * of floating point data (in general double or float for preconditioners structures if desired).
590 *
591
592 *
593 *
594 * @code
595 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
596 *   class NavierStokesProjectionOperator: public MatrixFreeOperators::Base<dim, Vec> {
597 *   public:
598 *   using Number = typename Vec::value_type;
599 *  
600 *   NavierStokesProjectionOperator();
601 *  
602 *   NavierStokesProjectionOperator(RunTimeParameters::Data_Storage& data);
603 *  
604 *   void set_dt(const double time_step);
605 *  
606 *   void set_TR_BDF2_stage(const unsigned int stage);
607 *  
608 *   void set_NS_stage(const unsigned int stage);
609 *  
610 *   void set_u_extr(const Vec& src);
611 *  
612 *   void vmult_rhs_velocity(Vec& dst, const std::vector<Vec>& src) const;
613 *  
614 *   void vmult_rhs_pressure(Vec& dst, const std::vector<Vec>& src) const;
615 *  
616 *   void vmult_grad_p_projection(Vec& dst, const Vec& src) const;
617 *  
618 *   virtual void compute_diagonal() override;
619 *  
620 *   protected:
621 *   double Re;
622 *   double dt;
623 *  
624 *   /*--- Parameters of time-marching scheme ---*/
625 *   double gamma;
626 *   double a31;
627 *   double a32;
628 *   double a33;
629 *  
630 *   unsigned int TR_BDF2_stage; /*--- Flag to denote at which stage of the TR-BDF2 are ---*/
631 *   unsigned int NS_stage; /*--- Flag to denote at which stage of NS solution inside each TR-BDF2 stage we are
632 *   (solution of the velocity or of the pressure)---*/
633 *  
634 *   virtual void apply_add(Vec& dst, const Vec& src) const override;
635 *  
636 *   private:
637 *   /*--- Auxiliary variable for the TR stage
638 *   (just to avoid to report a lot of 0.5 and for my personal choice to be coherent with the article) ---*/
639 *   const double a21 = 0.5;
640 *   const double a22 = 0.5;
641 *  
642 *   /*--- Penalty method parameters, theta = 1 means SIP, while C_p and C_u are the penalization coefficients ---*/
643 *   const double theta_v = 1.0;
644 *   const double theta_p = 1.0;
645 *   const double C_p = 1.0*(fe_degree_p + 1)*(fe_degree_p + 1);
646 *   const double C_u = 1.0*(fe_degree_v + 1)*(fe_degree_v + 1);
647 *  
648 *   Vec u_extr; /*--- Auxiliary variable to update the extrapolated velocity ---*/
649 *  
650 *   EquationData::Velocity<dim> vel_boundary_inflow; /*--- Auxiliary variable to impose velocity boundary conditions ---*/
651 *  
652 *   /*--- The following functions basically assemble the linear and bilinear forms. Their syntax is due to
653 *   the base class MatrixFreeOperators::Base ---*/
654 *   void assemble_rhs_cell_term_velocity(const MatrixFree<dim, Number>& data,
655 *   Vec& dst,
656 *   const std::vector<Vec>& src,
657 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
658 *   void assemble_rhs_face_term_velocity(const MatrixFree<dim, Number>& data,
659 *   Vec& dst,
660 *   const std::vector<Vec>& src,
661 *   const std::pair<unsigned int, unsigned int>& face_range) const;
662 *   void assemble_rhs_boundary_term_velocity(const MatrixFree<dim, Number>& data,
663 *   Vec& dst,
664 *   const std::vector<Vec>& src,
665 *   const std::pair<unsigned int, unsigned int>& face_range) const;
666 *  
667 *   void assemble_rhs_cell_term_pressure(const MatrixFree<dim, Number>& data,
668 *   Vec& dst,
669 *   const std::vector<Vec>& src,
670 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
671 *   void assemble_rhs_face_term_pressure(const MatrixFree<dim, Number>& data,
672 *   Vec& dst,
673 *   const std::vector<Vec>& src,
674 *   const std::pair<unsigned int, unsigned int>& face_range) const;
675 *   void assemble_rhs_boundary_term_pressure(const MatrixFree<dim, Number>& data,
676 *   Vec& dst,
677 *   const std::vector<Vec>& src,
678 *   const std::pair<unsigned int, unsigned int>& face_range) const;
679 *  
680 *   void assemble_cell_term_velocity(const MatrixFree<dim, Number>& data,
681 *   Vec& dst,
682 *   const Vec& src,
683 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
684 *   void assemble_face_term_velocity(const MatrixFree<dim, Number>& data,
685 *   Vec& dst,
686 *   const Vec& src,
687 *   const std::pair<unsigned int, unsigned int>& face_range) const;
688 *   void assemble_boundary_term_velocity(const MatrixFree<dim, Number>& data,
689 *   Vec& dst,
690 *   const Vec& src,
691 *   const std::pair<unsigned int, unsigned int>& face_range) const;
692 *  
693 *   void assemble_cell_term_pressure(const MatrixFree<dim, Number>& data,
694 *   Vec& dst,
695 *   const Vec& src,
696 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
697 *   void assemble_face_term_pressure(const MatrixFree<dim, Number>& data,
698 *   Vec& dst,
699 *   const Vec& src,
700 *   const std::pair<unsigned int, unsigned int>& face_range) const;
701 *   void assemble_boundary_term_pressure(const MatrixFree<dim, Number>& data,
702 *   Vec& dst,
703 *   const Vec& src,
704 *   const std::pair<unsigned int, unsigned int>& face_range) const;
705 *  
706 *   void assemble_cell_term_projection_grad_p(const MatrixFree<dim, Number>& data,
707 *   Vec& dst,
708 *   const Vec& src,
709 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
710 *   void assemble_rhs_cell_term_projection_grad_p(const MatrixFree<dim, Number>& data,
711 *   Vec& dst,
712 *   const Vec& src,
713 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
714 *  
715 *   void assemble_diagonal_cell_term_velocity(const MatrixFree<dim, Number>& data,
716 *   Vec& dst,
717 *   const unsigned int& src,
718 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
719 *   void assemble_diagonal_face_term_velocity(const MatrixFree<dim, Number>& data,
720 *   Vec& dst,
721 *   const unsigned int& src,
722 *   const std::pair<unsigned int, unsigned int>& face_range) const;
723 *   void assemble_diagonal_boundary_term_velocity(const MatrixFree<dim, Number>& data,
724 *   Vec& dst,
725 *   const unsigned int& src,
726 *   const std::pair<unsigned int, unsigned int>& face_range) const;
727 *  
728 *   void assemble_diagonal_cell_term_pressure(const MatrixFree<dim, Number>& data,
729 *   Vec& dst,
730 *   const unsigned int& src,
731 *   const std::pair<unsigned int, unsigned int>& cell_range) const;
732 *   void assemble_diagonal_face_term_pressure(const MatrixFree<dim, Number>& data,
733 *   Vec& dst,
734 *   const unsigned int& src,
735 *   const std::pair<unsigned int, unsigned int>& face_range) const;
736 *   void assemble_diagonal_boundary_term_pressure(const MatrixFree<dim, Number>& data,
737 *   Vec& dst,
738 *   const unsigned int& src,
739 *   const std::pair<unsigned int, unsigned int>& face_range) const;
740 *   };
741 *  
742 *  
743 * @endcode
744 *
745 * We start with the default constructor. It is important for MultiGrid, so it is fundamental
746 * to properly set the parameters of the time scheme.
747 *
748
749 *
750 *
751 * @code
752 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
753 *   NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
754 *   NavierStokesProjectionOperator():
755 *   MatrixFreeOperators::Base<dim, Vec>(), Re(), dt(), gamma(2.0 - std::sqrt(2.0)), a31((1.0 - gamma)/(2.0*(2.0 - gamma))),
756 *   a32(a31), a33(1.0/(2.0 - gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr() {}
757 *  
758 *  
759 * @endcode
760 *
761 * We focus now on the constructor with runtime parameters storage
762 *
763
764 *
765 *
766 * @code
767 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
768 *   NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
769 *   NavierStokesProjectionOperator(RunTimeParameters::Data_Storage& data):
770 *   MatrixFreeOperators::Base<dim, Vec>(), Re(data.Reynolds), dt(data.dt),
771 *   gamma(2.0 - std::sqrt(2.0)), a31((1.0 - gamma)/(2.0*(2.0 - gamma))),
772 *   a32(a31), a33(1.0/(2.0 - gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr(),
773 *   vel_boundary_inflow(data.initial_time) {}
774 *  
775 *  
776 * @endcode
777 *
778 * Setter of time-step (called by Multigrid and in case a smaller time-step towards the end is needed)
779 *
780
781 *
782 *
783 * @code
784 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
785 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
786 *   set_dt(const double time_step) {
787 *   dt = time_step;
788 *   }
789 *  
790 *  
791 * @endcode
792 *
793 * Setter of TR-BDF2 stage (this can be known only during the effective execution
794 * and so it has to be demanded to the class that really solves the problem)
795 *
796
797 *
798 *
799 * @code
800 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
801 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
802 *   set_TR_BDF2_stage(const unsigned int stage) {
803 *   AssertIndexRange(stage, 3);
804 *   Assert(stage > 0, ExcInternalError());
805 *  
806 *   TR_BDF2_stage = stage;
807 *   }
808 *  
809 *  
810 * @endcode
811 *
812 * Setter of NS stage (this can be known only during the effective execution
813 * and so it has to be demanded to the class that really solves the problem)
814 *
815
816 *
817 *
818 * @code
819 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
820 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
821 *   set_NS_stage(const unsigned int stage) {
822 *   AssertIndexRange(stage, 4);
823 *   Assert(stage > 0, ExcInternalError());
824 *  
825 *   NS_stage = stage;
826 *   }
827 *  
828 *  
829 * @endcode
830 *
831 * Setter of extrapolated velocity for different stages
832 *
833
834 *
835 *
836 * @code
837 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
838 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
839 *   set_u_extr(const Vec& src) {
840 *   u_extr = src;
841 *   u_extr.update_ghost_values();
842 *   }
843 *  
844 *  
845 * @endcode
846 *
847 * We are in a DG-MatrixFree framework, so it is convenient to compute separately cell contribution,
848 * internal faces contributions and boundary faces contributions. We start by
849 * assembling the rhs cell term for the velocity.
850 *
851
852 *
853 *
854 * @code
855 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
856 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
857 *   assemble_rhs_cell_term_velocity(const MatrixFree<dim, Number>& data,
858 *   Vec& dst,
859 *   const std::vector<Vec>& src,
860 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
861 *   if(TR_BDF2_stage == 1) {
862 *   /*--- We first start by declaring the suitable instances to read the old velocity, the
863 *   extrapolated velocity and the old pressure. 'phi' will be used only to submit the result.
864 *   The second argument specifies which dof handler has to be used (in this implementation 0 stands for
865 *   velocity and 1 for pressure). ---*/
867 *   phi_old(data, 0),
868 *   phi_old_extr(data, 0);
870 *  
871 *   /*--- We loop over the cells in the range ---*/
872 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
873 *   /*--- Now we need to assign the current cell to each FEEvaluation object and then to specify which src vector
874 *   it has to read (the proper order is clearly delegated to the user, which has to pay attention in the function
875 *   call to be coherent). ---*/
876 *   phi_old.reinit(cell);
877 *   phi_old.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
878 *   /*--- The 'gather_evaluate' function reads data from the vector.
879 *   The second and third parameter specifies if you want to read
880 *   values and/or derivative related quantities ---*/
881 *   phi_old_extr.reinit(cell);
882 *   phi_old_extr.gather_evaluate(src[1], EvaluationFlags::values);
883 *   phi_old_press.reinit(cell);
884 *   phi_old_press.gather_evaluate(src[2], EvaluationFlags::values);
885 *   phi.reinit(cell);
886 *  
887 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
888 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
889 *   const auto& u_n = phi_old.get_value(q);
890 *   const auto& grad_u_n = phi_old.get_gradient(q);
891 *   const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
892 *   const auto& tensor_product_u_n = outer_product(u_n, u_n_gamma_ov_2);
893 *   const auto& p_n = phi_old_press.get_value(q);
894 *   auto p_n_times_identity = tensor_product_u_n;
895 *   p_n_times_identity = 0;
896 *   for(unsigned int d = 0; d < dim; ++d)
897 *   p_n_times_identity[d][d] = p_n;
898 *  
899 *   phi.submit_value(1.0/(gamma*dt)*u_n, q); /*--- 'submit_value' contains quantites that we want to test against the
900 *   test function ---*/
901 *   phi.submit_gradient(-a21/Re*grad_u_n + a21*tensor_product_u_n + p_n_times_identity, q);
902 *   /*--- 'submit_gradient' contains quantites that we want to test against the gradient of test function ---*/
903 *   }
904 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
905 *   /*--- 'integrate_scatter' is the responsible of distributing into dst.
906 *   The flag parameter specifies if we are testing against the test function and/or its gradient ---*/
907 *   }
908 *   }
909 *   else {
910 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
912 *   phi_old(data, 0),
913 *   phi_int(data, 0);
915 *  
916 *   /*--- We loop over the cells in the range ---*/
917 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
918 *   phi_old.reinit(cell);
919 *   phi_old.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
920 *   phi_int.reinit(cell);
921 *   phi_int.gather_evaluate(src[1], EvaluationFlags::values | EvaluationFlags::gradients);
922 *   phi_old_press.reinit(cell);
923 *   phi_old_press.gather_evaluate(src[2], EvaluationFlags::values);
924 *   phi.reinit(cell);
925 *  
926 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
927 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
928 *   const auto& u_n = phi_old.get_value(q);
929 *   const auto& grad_u_n = phi_old.get_gradient(q);
930 *   const auto& u_n_gamma = phi_int.get_value(q);
931 *   const auto& grad_u_n_gamma = phi_int.get_gradient(q);
932 *   const auto& tensor_product_u_n = outer_product(u_n, u_n);
933 *   const auto& tensor_product_u_n_gamma = outer_product(u_n_gamma, u_n_gamma);
934 *   const auto& p_n = phi_old_press.get_value(q);
935 *   auto p_n_times_identity = tensor_product_u_n;
936 *   p_n_times_identity = 0;
937 *   for(unsigned int d = 0; d < dim; ++d)
938 *   p_n_times_identity[d][d] = p_n;
939 *  
940 *   phi.submit_value(1.0/((1.0 - gamma)*dt)*u_n_gamma, q);
941 *   phi.submit_gradient(a32*tensor_product_u_n_gamma + a31*tensor_product_u_n -
942 *   a32/Re*grad_u_n_gamma - a31/Re*grad_u_n + p_n_times_identity, q);
943 *   }
944 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
945 *   }
946 *   }
947 *   }
948 *  
949 *  
950 * @endcode
951 *
952 * The followinf function assembles rhs face term for the velocity
953 *
954
955 *
956 *
957 * @code
958 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
959 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
960 *   assemble_rhs_face_term_velocity(const MatrixFree<dim, Number>& data,
961 *   Vec& dst,
962 *   const std::vector<Vec>& src,
963 *   const std::pair<unsigned int, unsigned int>& face_range) const {
964 *   if(TR_BDF2_stage == 1) {
965 *   /*--- We first start by declaring the suitable instances to read already available quantities. In this case
966 *   we are at the face between two elements and this is the reason of 'FEFaceEvaluation'. It contains an extra
967 *   input argument, the second one, that specifies if it is from 'interior' or not---*/
969 *   phi_m(data, false, 0),
970 *   phi_old_p(data, true, 0),
971 *   phi_old_m(data, false, 0),
972 *   phi_old_extr_p(data, true, 0),
973 *   phi_old_extr_m(data, false, 0);
975 *   phi_old_press_m(data, false, 1);
976 *  
977 *   /*--- We loop over the faces in the range ---*/
978 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
979 *   phi_old_p.reinit(face);
980 *   phi_old_p.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
981 *   phi_old_m.reinit(face);
982 *   phi_old_m.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
983 *   phi_old_extr_p.reinit(face);
984 *   phi_old_extr_p.gather_evaluate(src[1], EvaluationFlags::values);
985 *   phi_old_extr_m.reinit(face);
986 *   phi_old_extr_m.gather_evaluate(src[1], EvaluationFlags::values);
987 *   phi_old_press_p.reinit(face);
988 *   phi_old_press_p.gather_evaluate(src[2], EvaluationFlags::values);
989 *   phi_old_press_m.reinit(face);
990 *   phi_old_press_m.gather_evaluate(src[2], EvaluationFlags::values);
991 *   phi_p.reinit(face);
992 *   phi_m.reinit(face);
993 *  
994 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
995 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
996 *   const auto& n_plus = phi_p.get_normal_vector(q); /*--- The normal vector is the same
997 *   for both phi_p and phi_m. If the face is interior,
998 *   it correspond to the outer normal ---*/
999 *  
1000 *   const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1001 *   const auto& avg_tensor_product_u_n = 0.5*(outer_product(phi_old_p.get_value(q), phi_old_extr_p.get_value(q)) +
1002 *   outer_product(phi_old_m.get_value(q), phi_old_extr_m.get_value(q)));
1003 *   const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1004 *  
1005 *   phi_p.submit_value((a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus - avg_p_old*n_plus, q);
1006 *   phi_m.submit_value(-(a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus + avg_p_old*n_plus, q);
1007 *   }
1008 *   phi_p.integrate_scatter(EvaluationFlags::values, dst);
1009 *   phi_m.integrate_scatter(EvaluationFlags::values, dst);
1010 *   }
1011 *   }
1012 *   else {
1013 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1015 *   phi_m(data, false, 0),
1016 *   phi_old_p(data, true, 0),
1017 *   phi_old_m(data, false, 0),
1018 *   phi_int_p(data, true, 0),
1019 *   phi_int_m(data, false, 0);
1021 *   phi_old_press_m(data, false, 1);
1022 *  
1023 *   /*--- We loop over the faces in the range ---*/
1024 *   for(unsigned int face = face_range.first; face < face_range.second; ++ face) {
1025 *   phi_old_p.reinit(face);
1026 *   phi_old_p.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
1027 *   phi_old_m.reinit(face);
1028 *   phi_old_m.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
1029 *   phi_int_p.reinit(face);
1030 *   phi_int_p.gather_evaluate(src[1], EvaluationFlags::values | EvaluationFlags::gradients);
1031 *   phi_int_m.reinit(face);
1032 *   phi_int_m.gather_evaluate(src[1], EvaluationFlags::values | EvaluationFlags::gradients);
1033 *   phi_old_press_p.reinit(face);
1034 *   phi_old_press_p.gather_evaluate(src[2], EvaluationFlags::values);
1035 *   phi_old_press_m.reinit(face);
1036 *   phi_old_press_m.gather_evaluate(src[2], EvaluationFlags::values);
1037 *   phi_p.reinit(face);
1038 *   phi_m.reinit(face);
1039 *  
1040 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1041 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1042 *   const auto& n_plus = phi_p.get_normal_vector(q);
1043 *  
1044 *   const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1045 *   const auto& avg_grad_u_int = 0.5*(phi_int_p.get_gradient(q) + phi_int_m.get_gradient(q));
1046 *   const auto& avg_tensor_product_u_n = 0.5*(outer_product(phi_old_p.get_value(q), phi_old_p.get_value(q)) +
1047 *   outer_product(phi_old_m.get_value(q), phi_old_m.get_value(q)));
1048 *   const auto& avg_tensor_product_u_n_gamma = 0.5*(outer_product(phi_int_p.get_value(q), phi_int_p.get_value(q)) +
1049 *   outer_product(phi_int_m.get_value(q), phi_int_m.get_value(q)));
1050 *   const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1051 *  
1052 *   phi_p.submit_value((a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1053 *   a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus - avg_p_old*n_plus, q);
1054 *   phi_m.submit_value(-(a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1055 *   a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus + avg_p_old*n_plus, q);
1056 *   }
1057 *   phi_p.integrate_scatter(EvaluationFlags::values, dst);
1058 *   phi_m.integrate_scatter(EvaluationFlags::values, dst);
1059 *   }
1060 *   }
1061 *   }
1062 *  
1063 *  
1064 * @endcode
1065 *
1066 * The followinf function assembles rhs boundary term for the velocity
1067 *
1068
1069 *
1070 *
1071 * @code
1072 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1073 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1074 *   assemble_rhs_boundary_term_velocity(const MatrixFree<dim, Number>& data,
1075 *   Vec& dst,
1076 *   const std::vector<Vec>& src,
1077 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1078 *   if(TR_BDF2_stage == 1) {
1079 *   /*--- We first start by declaring the suitable instances to read already available quantities. Clearly on the boundary
1080 *   the second argument has to be true. ---*/
1082 *   phi_old(data, true, 0),
1083 *   phi_old_extr(data, true, 0);
1085 *  
1086 *   /*--- We loop over the faces in the range ---*/
1087 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1088 *   phi_old.reinit(face);
1089 *   phi_old.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
1090 *   phi_old_extr.reinit(face);
1091 *   phi_old_extr.gather_evaluate(src[1], EvaluationFlags::values);
1092 *   phi_old_press.reinit(face);
1093 *   phi_old_press.gather_evaluate(src[2], EvaluationFlags::values);
1094 *   phi.reinit(face);
1095 *  
1096 *   const auto boundary_id = data.get_boundary_id(face); /*--- Get the id in order to impose the proper boundary condition ---*/
1097 *   const auto coef_jump = (boundary_id == 1) ? 0.0 : C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1098 *   const double aux_coeff = (boundary_id == 1) ? 0.0 : 1.0;
1099 *  
1100 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1101 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1102 *   const auto& n_plus = phi.get_normal_vector(q);
1103 *  
1104 *   const auto& grad_u_old = phi_old.get_gradient(q);
1105 *   const auto& tensor_product_u_n = outer_product(phi_old.get_value(q), phi_old_extr.get_value(q));
1106 *   const auto& p_old = phi_old_press.get_value(q);
1107 *   const auto& point_vectorized = phi.quadrature_point(q);
1108 *   auto u_int_m = Tensor<1, dim, VectorizedArray<Number>>();
1109 *   if(boundary_id == 0) {
1110 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1111 *   Point<dim> point; /*--- The point returned by the 'quadrature_point' function is not an instance of Point
1112 *   and so it is not ready to be directly used. We need to pay attention to the
1113 *   vectorization ---*/
1114 *   for(unsigned int d = 0; d < dim; ++d)
1115 *   point[d] = point_vectorized[d][v];
1116 *   for(unsigned int d = 0; d < dim; ++d)
1117 *   u_int_m[d][v] = vel_boundary_inflow.value(point, d);
1118 *   }
1119 *   }
1120 *   const auto tensor_product_u_int_m = outer_product(u_int_m, phi_old_extr.get_value(q));
1121 *   const auto lambda = (boundary_id == 1) ? 0.0 : std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1122 *  
1123 *   phi.submit_value((a21/Re*grad_u_old - a21*tensor_product_u_n)*n_plus - p_old*n_plus +
1124 *   a22/Re*2.0*coef_jump*u_int_m -
1125 *   aux_coeff*a22*tensor_product_u_int_m*n_plus + a22*lambda*u_int_m, q);
1126 *   phi.submit_normal_derivative(-aux_coeff*theta_v*a22/Re*u_int_m, q); /*--- This is equivalent to multiply to the gradient
1127 *   with outer product and use 'submit_gradient' ---*/
1128 *   }
1129 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1130 *   }
1131 *   }
1132 *   else {
1133 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1135 *   phi_old(data, true, 0),
1136 *   phi_int(data, true, 0),
1137 *   phi_int_extr(data, true, 0);
1139 *  
1140 *   /*--- We loop over the faces in the range ---*/
1141 *   for(unsigned int face = face_range.first; face < face_range.second; ++ face) {
1142 *   phi_old.reinit(face);
1143 *   phi_old.gather_evaluate(src[0], EvaluationFlags::values | EvaluationFlags::gradients);
1144 *   phi_int.reinit(face);
1145 *   phi_int.gather_evaluate(src[1], EvaluationFlags::values | EvaluationFlags::gradients);
1146 *   phi_old_press.reinit(face);
1147 *   phi_old_press.gather_evaluate(src[2], EvaluationFlags::values);
1148 *   phi_int_extr.reinit(face);
1149 *   phi_int_extr.gather_evaluate(src[3], EvaluationFlags::values);
1150 *   phi.reinit(face);
1151 *  
1152 *   const auto boundary_id = data.get_boundary_id(face);
1153 *   const auto coef_jump = (boundary_id == 1) ? 0.0 : C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1154 *   const double aux_coeff = (boundary_id == 1) ? 0.0 : 1.0;
1155 *  
1156 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1157 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1158 *   const auto& n_plus = phi.get_normal_vector(q);
1159 *  
1160 *   const auto& grad_u_old = phi_old.get_gradient(q);
1161 *   const auto& grad_u_int = phi_int.get_gradient(q);
1162 *   const auto& tensor_product_u_n = outer_product(phi_old.get_value(q), phi_old.get_value(q));
1163 *   const auto& tensor_product_u_n_gamma = outer_product(phi_int.get_value(q), phi_int.get_value(q));
1164 *   const auto& p_old = phi_old_press.get_value(q);
1165 *   const auto& point_vectorized = phi.quadrature_point(q);
1166 *   auto u_m = Tensor<1, dim, VectorizedArray<Number>>();
1167 *   if(boundary_id == 0) {
1168 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1169 *   Point<dim> point;
1170 *   for(unsigned int d = 0; d < dim; ++d)
1171 *   point[d] = point_vectorized[d][v];
1172 *   for(unsigned int d = 0; d < dim; ++d)
1173 *   u_m[d][v] = vel_boundary_inflow.value(point, d);
1174 *   }
1175 *   }
1176 *   const auto tensor_product_u_m = outer_product(u_m, phi_int_extr.get_value(q));
1177 *   const auto lambda = (boundary_id == 1) ? 0.0 : std::abs(scalar_product(phi_int_extr.get_value(q), n_plus));
1178 *  
1179 *   phi.submit_value((a31/Re*grad_u_old + a32/Re*grad_u_int -
1180 *   a31*tensor_product_u_n - a32*tensor_product_u_n_gamma)*n_plus - p_old*n_plus +
1181 *   a33/Re*2.0*coef_jump*u_m -
1182 *   aux_coeff*a33*tensor_product_u_m*n_plus + a33*lambda*u_m, q);
1183 *   phi.submit_normal_derivative(-aux_coeff*theta_v*a33/Re*u_m, q);
1184 *   }
1185 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1186 *   }
1187 *   }
1188 *   }
1189 *  
1190 *  
1191 * @endcode
1192 *
1193 * Put together all the previous steps for velocity. This is done automatically by the loop function of 'MatrixFree' class
1194 *
1195
1196 *
1197 *
1198 * @code
1199 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1200 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1201 *   vmult_rhs_velocity(Vec& dst, const std::vector<Vec>& src) const {
1202 *   for(auto& vec : src)
1203 *   vec.update_ghost_values();
1204 *  
1205 *   this->data->loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_velocity,
1206 *   &NavierStokesProjectionOperator::assemble_rhs_face_term_velocity,
1207 *   &NavierStokesProjectionOperator::assemble_rhs_boundary_term_velocity,
1208 *   this, dst, src, true,
1211 *   }
1212 *  
1213 *  
1214 * @endcode
1215 *
1216 * Now we focus on computing the rhs for the projection step for the pressure with the same ratio.
1217 * The following function assembles rhs cell term for the pressure
1218 *
1219
1220 *
1221 *
1222 * @code
1223 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1224 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1225 *   assemble_rhs_cell_term_pressure(const MatrixFree<dim, Number>& data,
1226 *   Vec& dst,
1227 *   const std::vector<Vec>& src,
1228 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1229 *   /*--- We first start by declaring the suitable instances to read already available quantities.
1230 *   The third parameter specifies that we want to use the second quadrature formula stored. ---*/
1232 *   phi_old(data, 1, 1);
1234 *  
1235 *   const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 - gamma)*dt*(1.0 - gamma)*dt;
1236 *  
1237 *   const double coeff_2 = (TR_BDF2_stage == 1) ? gamma*dt : (1.0 - gamma)*dt;
1238 *  
1239 *   /*--- We loop over cells in the range ---*/
1240 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1241 *   phi_proj.reinit(cell);
1242 *   phi_proj.gather_evaluate(src[0], EvaluationFlags::values);
1243 *   phi_old.reinit(cell);
1244 *   phi_old.gather_evaluate(src[1], EvaluationFlags::values);
1245 *   phi.reinit(cell);
1246 *  
1247 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1248 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1249 *   const auto& u_star_star = phi_proj.get_value(q);
1250 *   const auto& p_old = phi_old.get_value(q);
1251 *  
1252 *   phi.submit_value(1.0/coeff*p_old, q);
1253 *   phi.submit_gradient(1.0/coeff_2*u_star_star, q);
1254 *   }
1255 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1256 *   }
1257 *   }
1258 *  
1259 *  
1260 * @endcode
1261 *
1262 * The following function assembles rhs face term for the pressure
1263 *
1264
1265 *
1266 *
1267 * @code
1268 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1269 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1270 *   assemble_rhs_face_term_pressure(const MatrixFree<dim, Number>& data,
1271 *   Vec& dst,
1272 *   const std::vector<Vec>& src,
1273 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1274 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1276 *   phi_m(data, false, 1, 1);
1278 *   phi_proj_m(data, false, 0, 1);
1279 *  
1280 *   const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 - gamma)*dt);
1281 *  
1282 *   /*--- We loop over faces in the range ---*/
1283 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1284 *   phi_proj_p.reinit(face);
1285 *   phi_proj_p.gather_evaluate(src[0], EvaluationFlags::values);
1286 *   phi_proj_m.reinit(face);
1287 *   phi_proj_m.gather_evaluate(src[0], EvaluationFlags::values);
1288 *   phi_p.reinit(face);
1289 *   phi_m.reinit(face);
1290 *  
1291 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1292 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1293 *   const auto& n_plus = phi_p.get_normal_vector(q);
1294 *   const auto& avg_u_star_star = 0.5*(phi_proj_p.get_value(q) + phi_proj_m.get_value(q));
1295 *  
1296 *   phi_p.submit_value(-coeff*scalar_product(avg_u_star_star, n_plus), q);
1297 *   phi_m.submit_value(coeff*scalar_product(avg_u_star_star, n_plus), q);
1298 *   }
1299 *   phi_p.integrate_scatter(EvaluationFlags::values, dst);
1300 *   phi_m.integrate_scatter(EvaluationFlags::values, dst);
1301 *   }
1302 *   }
1303 *  
1304 *  
1305 * @endcode
1306 *
1307 * The following function assembles rhs boundary term for the pressure
1308 *
1309
1310 *
1311 *
1312 * @code
1313 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1314 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1315 *   assemble_rhs_boundary_term_pressure(const MatrixFree<dim, Number>& data,
1316 *   Vec& dst,
1317 *   const std::vector<Vec>& src,
1318 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1319 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1322 *  
1323 *   const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 - gamma)*dt);
1324 *  
1325 *   /*--- We loop over faces in the range ---*/
1326 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1327 *   phi_proj.reinit(face);
1328 *   phi_proj.gather_evaluate(src[0], EvaluationFlags::values);
1329 *   phi.reinit(face);
1330 *  
1331 *   /*--- Now we loop over all the quadrature points to compute the integrals ---*/
1332 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1333 *   const auto& n_plus = phi.get_normal_vector(q);
1334 *  
1335 *   phi.submit_value(-coeff*scalar_product(phi_proj.get_value(q), n_plus), q);
1336 *   }
1337 *   phi.integrate_scatter(EvaluationFlags::values, dst);
1338 *   }
1339 *   }
1340 *  
1341 *  
1342 * @endcode
1343 *
1344 * Put together all the previous steps for pressure
1345 *
1346
1347 *
1348 *
1349 * @code
1350 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1351 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1352 *   vmult_rhs_pressure(Vec& dst, const std::vector<Vec>& src) const {
1353 *   for(auto& vec : src)
1354 *   vec.update_ghost_values();
1355 *  
1356 *   this->data->loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_pressure,
1357 *   &NavierStokesProjectionOperator::assemble_rhs_face_term_pressure,
1358 *   &NavierStokesProjectionOperator::assemble_rhs_boundary_term_pressure,
1359 *   this, dst, src, true,
1362 *   }
1363 *  
1364 *  
1365 * @endcode
1366 *
1367 * Now we need to build the 'matrices', i.e. the bilinear forms. We start by
1368 * assembling the cell term for the velocity
1369 *
1370
1371 *
1372 *
1373 * @code
1374 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1375 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1376 *   assemble_cell_term_velocity(const MatrixFree<dim, Number>& data,
1377 *   Vec& dst,
1378 *   const Vec& src,
1379 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1380 *   if(TR_BDF2_stage == 1) {
1381 *   /*--- We first start by declaring the suitable instances to read already available quantities. Moreover 'phi' in
1382 *   this case serves for a bilinear form and so it will not used only to submit but also to read the src ---*/
1384 *   phi_old_extr(data, 0);
1385 *  
1386 *   /*--- We loop over all cells in the range ---*/
1387 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1388 *   phi.reinit(cell);
1389 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1390 *   phi_old_extr.reinit(cell);
1391 *   phi_old_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1392 *  
1393 *   /*--- Now we loop over all quadrature points ---*/
1394 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1395 *   const auto& u_int = phi.get_value(q);
1396 *   const auto& grad_u_int = phi.get_gradient(q);
1397 *   const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1398 *   const auto& tensor_product_u_int = outer_product(u_int, u_n_gamma_ov_2);
1399 *  
1400 *   phi.submit_value(1.0/(gamma*dt)*u_int, q);
1401 *   phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1402 *   }
1403 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1404 *   }
1405 *   }
1406 *   else {
1407 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1409 *   phi_int_extr(data, 0);
1410 *  
1411 *   /*--- We loop over all cells in the range ---*/
1412 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1413 *   phi.reinit(cell);
1414 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1415 *   phi_int_extr.reinit(cell);
1416 *   phi_int_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1417 *  
1418 *   /*--- Now we loop over all quadrature points ---*/
1419 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1420 *   const auto& u_curr = phi.get_value(q);
1421 *   const auto& grad_u_curr = phi.get_gradient(q);
1422 *   const auto& u_n1_int = phi_int_extr.get_value(q);
1423 *   const auto& tensor_product_u_curr = outer_product(u_curr, u_n1_int);
1424 *  
1425 *   phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
1426 *   phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
1427 *   }
1428 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1429 *   }
1430 *   }
1431 *   }
1432 *  
1433 *  
1434 * @endcode
1435 *
1436 * The following function assembles face term for the velocity
1437 *
1438
1439 *
1440 *
1441 * @code
1442 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1443 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1444 *   assemble_face_term_velocity(const MatrixFree<dim, Number>& data,
1445 *   Vec& dst,
1446 *   const Vec& src,
1447 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1448 *   if(TR_BDF2_stage == 1) {
1449 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1451 *   phi_m(data, false, 0),
1452 *   phi_old_extr_p(data, true, 0),
1453 *   phi_old_extr_m(data, false, 0);
1454 *  
1455 *   /*--- We loop over all faces in the range ---*/
1456 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1457 *   phi_p.reinit(face);
1458 *   phi_p.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1459 *   phi_m.reinit(face);
1460 *   phi_m.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1461 *   phi_old_extr_p.reinit(face);
1462 *   phi_old_extr_p.gather_evaluate(u_extr, EvaluationFlags::values);
1463 *   phi_old_extr_m.reinit(face);
1464 *   phi_old_extr_m.gather_evaluate(u_extr, EvaluationFlags::values);
1465 *  
1466 *   const auto coef_jump = C_u*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1467 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1468 *  
1469 *   /*--- Now we loop over all quadrature points ---*/
1470 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1471 *   const auto& n_plus = phi_p.get_normal_vector(q);
1472 *  
1473 *   const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1474 *   const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
1475 *   const auto& avg_tensor_product_u_int = 0.5*(outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
1476 *   outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
1477 *   const auto lambda = std::max(std::abs(scalar_product(phi_old_extr_p.get_value(q), n_plus)),
1478 *   std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
1479 *  
1480 *   phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
1481 *   a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int, q);
1482 *   phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
1483 *   a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
1484 *   phi_p.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1485 *   phi_m.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1486 *   }
1487 *   phi_p.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1488 *   phi_m.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1489 *   }
1490 *   }
1491 *   else {
1492 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1494 *   phi_m(data, false, 0),
1495 *   phi_extr_p(data, true, 0),
1496 *   phi_extr_m(data, false, 0);
1497 *  
1498 *   /*--- We loop over all faces in the range ---*/
1499 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1500 *   phi_p.reinit(face);
1501 *   phi_p.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1502 *   phi_m.reinit(face);
1503 *   phi_m.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1504 *   phi_extr_p.reinit(face);
1505 *   phi_extr_p.gather_evaluate(u_extr, EvaluationFlags::values);
1506 *   phi_extr_m.reinit(face);
1507 *   phi_extr_m.gather_evaluate(u_extr, EvaluationFlags::values);
1508 *  
1509 *   const auto coef_jump = C_u*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1510 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1511 *  
1512 *   /*--- Now we loop over all quadrature points ---*/
1513 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1514 *   const auto& n_plus = phi_p.get_normal_vector(q);
1515 *  
1516 *   const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1517 *   const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
1518 *   const auto& avg_tensor_product_u = 0.5*(outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
1519 *   outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
1520 *   const auto lambda = std::max(std::abs(scalar_product(phi_extr_p.get_value(q), n_plus)),
1521 *   std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
1522 *  
1523 *   phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
1524 *   a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
1525 *   phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
1526 *   a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
1527 *   phi_p.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1528 *   phi_m.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1529 *   }
1530 *   phi_p.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1531 *   phi_m.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1532 *   }
1533 *   }
1534 *   }
1535 *  
1536 *  
1537 * @endcode
1538 *
1539 * The following function assembles boundary term for the velocity
1540 *
1541
1542 *
1543 *
1544 * @code
1545 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1546 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1547 *   assemble_boundary_term_velocity(const MatrixFree<dim, Number>& data,
1548 *   Vec& dst,
1549 *   const Vec& src,
1550 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1551 *   if(TR_BDF2_stage == 1) {
1552 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1554 *   phi_old_extr(data, true, 0);
1555 *  
1556 *   /*--- We loop over all faces in the range ---*/
1557 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1558 *   phi.reinit(face);
1559 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1560 *   phi_old_extr.reinit(face);
1561 *   phi_old_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1562 *  
1563 *   const auto boundary_id = data.get_boundary_id(face);
1564 *   const auto coef_jump = C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1565 *  
1566 *   /*--- The application of the mirror principle is not so trivial because we have a Dirichlet condition
1567 *   on a single component for the outflow; so we distinguish the two cases ---*/
1568 *   if(boundary_id != 1) {
1569 *   const double coef_trasp = 0.0;
1570 *  
1571 *   /*--- Now we loop over all quadrature points ---*/
1572 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1573 *   const auto& n_plus = phi.get_normal_vector(q);
1574 *   const auto& grad_u_int = phi.get_gradient(q);
1575 *   const auto& u_int = phi.get_value(q);
1576 *   const auto& tensor_product_u_int = outer_product(phi.get_value(q), phi_old_extr.get_value(q));
1577 *   const auto& lambda = std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1578 *  
1579 *   phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
1580 *   a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
1581 *   phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
1582 *   }
1583 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1584 *   }
1585 *   else {
1586 *   /*--- Now we loop over all quadrature points ---*/
1587 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1588 *   const auto& n_plus = phi.get_normal_vector(q);
1589 *   const auto& grad_u_int = phi.get_gradient(q);
1590 *   const auto& u_int = phi.get_value(q);
1591 *   const auto& lambda = std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1592 *  
1593 *   const auto& point_vectorized = phi.quadrature_point(q);
1594 *   auto u_int_m = u_int;
1595 *   auto grad_u_int_m = grad_u_int;
1596 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1597 *   Point<dim> point;
1598 *   for(unsigned int d = 0; d < dim; ++d)
1599 *   point[d] = point_vectorized[d][v];
1600 *  
1601 *   u_int_m[1][v] = -u_int_m[1][v];
1602 *  
1603 *   grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
1604 *   grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
1605 *   }
1606 *  
1607 *   phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
1608 *   a22*outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
1609 *   a22*0.5*lambda*(u_int - u_int_m), q);
1610 *   phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
1611 *   }
1612 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1613 *   }
1614 *   }
1615 *   }
1616 *   else {
1617 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1619 *   phi_extr(data, true, 0);
1620 *  
1621 *   /*--- We loop over all faces in the range ---*/
1622 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1623 *   phi.reinit(face);
1624 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1625 *   phi_extr.reinit(face);
1626 *   phi_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1627 *  
1628 *   const auto boundary_id = data.get_boundary_id(face);
1629 *   const auto coef_jump = C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1630 *  
1631 *   if(boundary_id != 1) {
1632 *   const double coef_trasp = 0.0;
1633 *  
1634 *   /*--- Now we loop over all quadrature points ---*/
1635 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1636 *   const auto& n_plus = phi.get_normal_vector(q);
1637 *   const auto& grad_u = phi.get_gradient(q);
1638 *   const auto& u = phi.get_value(q);
1639 *   const auto& tensor_product_u = outer_product(phi.get_value(q), phi_extr.get_value(q));
1640 *   const auto& lambda = std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1641 *  
1642 *   phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
1643 *   a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
1644 *   phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
1645 *   }
1646 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1647 *   }
1648 *   else {
1649 *   /*--- Now we loop over all quadrature points ---*/
1650 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1651 *   const auto& n_plus = phi.get_normal_vector(q);
1652 *   const auto& grad_u = phi.get_gradient(q);
1653 *   const auto& u = phi.get_value(q);
1654 *   const auto& lambda = std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1655 *  
1656 *   const auto& point_vectorized = phi.quadrature_point(q);
1657 *   auto u_m = u;
1658 *   auto grad_u_m = grad_u;
1659 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1660 *   Point<dim> point;
1661 *   for(unsigned int d = 0; d < dim; ++d)
1662 *   point[d] = point_vectorized[d][v];
1663 *  
1664 *   u_m[1][v] = -u_m[1][v];
1665 *  
1666 *   grad_u_m[0][0][v] = -grad_u_m[0][0][v];
1667 *   grad_u_m[0][1][v] = -grad_u_m[0][1][v];
1668 *   }
1669 *  
1670 *   phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
1671 *   a33*outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus + a33*0.5*lambda*(u - u_m), q);
1672 *   phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
1673 *   }
1674 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1675 *   }
1676 *   }
1677 *   }
1678 *   }
1679 *  
1680 *  
1681 * @endcode
1682 *
1683 * Next, we focus on 'matrices' to compute the pressure. We first assemble cell term for the pressure
1684 *
1685
1686 *
1687 *
1688 * @code
1689 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1690 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1691 *   assemble_cell_term_pressure(const MatrixFree<dim, Number>& data,
1692 *   Vec& dst,
1693 *   const Vec& src,
1694 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1695 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1697 *  
1698 *   const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 - gamma)*dt*(1.0 - gamma)*dt;
1699 *  
1700 *   /*--- Loop over all cells in the range ---*/
1701 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1702 *   phi.reinit(cell);
1703 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1704 *  
1705 *   /*--- Now we loop over all quadrature points ---*/
1706 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1707 *   phi.submit_gradient(phi.get_gradient(q), q);
1708 *   phi.submit_value(1.0/coeff*phi.get_value(q), q);
1709 *   }
1710 *  
1711 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1712 *   }
1713 *   }
1714 *  
1715 *  
1716 * @endcode
1717 *
1718 * The following function assembles face term for the pressure
1719 *
1720
1721 *
1722 *
1723 * @code
1724 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1725 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1726 *   assemble_face_term_pressure(const MatrixFree<dim, Number>& data,
1727 *   Vec& dst,
1728 *   const Vec& src,
1729 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1730 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1732 *   phi_m(data, false, 1, 1);
1733 *  
1734 *   /*--- Loop over all faces in the range ---*/
1735 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1736 *   phi_p.reinit(face);
1737 *   phi_p.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1738 *   phi_m.reinit(face);
1739 *   phi_m.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1740 *  
1741 *   const auto coef_jump = C_p*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1742 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1743 *  
1744 *   /*--- Loop over quadrature points ---*/
1745 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1746 *   const auto& n_plus = phi_p.get_normal_vector(q);
1747 *  
1748 *   const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1749 *   const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
1750 *  
1751 *   phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
1752 *   phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
1753 *   phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1754 *   phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1755 *   }
1756 *   phi_p.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1757 *   phi_m.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1758 *   }
1759 *   }
1760 *  
1761 *  
1762 * @endcode
1763 *
1764 * The following function assembles boundary term for the pressure
1765 *
1766
1767 *
1768 *
1769 * @code
1770 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1771 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1772 *   assemble_boundary_term_pressure(const MatrixFree<dim, Number>& data,
1773 *   Vec& dst,
1774 *   const Vec& src,
1775 *   const std::pair<unsigned int, unsigned int>& face_range) const {
1777 *  
1778 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
1779 *   const auto boundary_id = data.get_boundary_id(face);
1780 *  
1781 *   if(boundary_id == 1) {
1782 *   phi.reinit(face);
1783 *   phi.gather_evaluate(src, EvaluationFlags::values | EvaluationFlags::gradients);
1784 *  
1785 *   const auto coef_jump = C_p*std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
1786 *  
1787 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1788 *   const auto& n_plus = phi.get_normal_vector(q);
1789 *  
1790 *   const auto& grad_pres = phi.get_gradient(q);
1791 *   const auto& pres = phi.get_value(q);
1792 *  
1793 *   phi.submit_value(-scalar_product(grad_pres, n_plus) + coef_jump*pres , q);
1794 *   phi.submit_normal_derivative(-theta_p*pres, q);
1795 *   }
1796 *   phi.integrate_scatter(EvaluationFlags::values | EvaluationFlags::gradients, dst);
1797 *   }
1798 *   }
1799 *   }
1800 *  
1801 *  
1802 * @endcode
1803 *
1804 * Before coding the 'apply_add' function, which is the one that will perform the loop, we focus on
1805 * the linear system that arises to project the gradient of the pressure into the velocity space.
1806 * The following function assembles rhs cell term for the projection of gradient of pressure. Since no
1807 * integration by parts is performed, only a cell term contribution is present.
1808 *
1809
1810 *
1811 *
1812 * @code
1813 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1814 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1815 *   assemble_rhs_cell_term_projection_grad_p(const MatrixFree<dim, Number>& data,
1816 *   Vec& dst,
1817 *   const Vec& src,
1818 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1819 *   /*--- We first start by declaring the suitable instances to read already available quantities. ---*/
1822 *  
1823 *   /*--- Loop over all cells in the range ---*/
1824 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1825 *   phi_pres.reinit(cell);
1826 *   phi_pres.gather_evaluate(src, EvaluationFlags::gradients);
1827 *   phi.reinit(cell);
1828 *  
1829 *   /*--- Loop over quadrature points ---*/
1830 *   for(unsigned int q = 0; q < phi.n_q_points; ++q)
1831 *   phi.submit_value(phi_pres.get_gradient(q), q);
1832 *  
1833 *   phi.integrate_scatter(EvaluationFlags::values, dst);
1834 *   }
1835 *   }
1836 *  
1837 *  
1838 * @endcode
1839 *
1840 * Put together all the previous steps for projection of pressure gradient. Here we loop only over cells
1841 *
1842
1843 *
1844 *
1845 * @code
1846 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1847 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1848 *   vmult_grad_p_projection(Vec& dst, const Vec& src) const {
1849 *   this->data->cell_loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_projection_grad_p,
1850 *   this, dst, src, true);
1851 *   }
1852 *  
1853 *  
1854 * @endcode
1855 *
1856 * Assemble now cell term for the projection of gradient of pressure. This is nothing but a mass matrix
1857 *
1858
1859 *
1860 *
1861 * @code
1862 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1863 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1864 *   assemble_cell_term_projection_grad_p(const MatrixFree<dim, Number>& data,
1865 *   Vec& dst,
1866 *   const Vec& src,
1867 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1869 *  
1870 *   /*--- Loop over all cells in the range ---*/
1871 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1872 *   phi.reinit(cell);
1873 *   phi.gather_evaluate(src, EvaluationFlags::values);
1874 *  
1875 *   /*--- Loop over quadrature points ---*/
1876 *   for(unsigned int q = 0; q < phi.n_q_points; ++q)
1877 *   phi.submit_value(phi.get_value(q), q);
1878 *  
1879 *   phi.integrate_scatter(EvaluationFlags::values, dst);
1880 *   }
1881 *   }
1882 *  
1883 *  
1884 * @endcode
1885 *
1886 * Put together all previous steps. This is the overridden function that effectively performs the
1887 * matrix-vector multiplication.
1888 *
1889
1890 *
1891 *
1892 * @code
1893 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1894 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1895 *   apply_add(Vec& dst, const Vec& src) const {
1896 *   if(NS_stage == 1) {
1897 *   this->data->loop(&NavierStokesProjectionOperator::assemble_cell_term_velocity,
1898 *   &NavierStokesProjectionOperator::assemble_face_term_velocity,
1899 *   &NavierStokesProjectionOperator::assemble_boundary_term_velocity,
1900 *   this, dst, src, false,
1903 *   }
1904 *   else if(NS_stage == 2) {
1905 *   this->data->loop(&NavierStokesProjectionOperator::assemble_cell_term_pressure,
1906 *   &NavierStokesProjectionOperator::assemble_face_term_pressure,
1907 *   &NavierStokesProjectionOperator::assemble_boundary_term_pressure,
1908 *   this, dst, src, false,
1911 *   }
1912 *   else if(NS_stage == 3) {
1913 *   this->data->cell_loop(&NavierStokesProjectionOperator::assemble_cell_term_projection_grad_p,
1914 *   this, dst, src, false); /*--- Since we have only a cell term contribution, we use cell_loop ---*/
1915 *   }
1916 *   else
1917 *   Assert(false, ExcNotImplemented());
1918 *   }
1919 *  
1920 *  
1921 * @endcode
1922 *
1923 * Finally, we focus on computing the diagonal for preconditioners and we start by assembling
1924 * the diagonal cell term for the velocity. Since we do not have access to the entries of the matrix,
1925 * in order to compute the element i, we test the matrix against a vector which is equal to 1 in position i and 0 elsewhere.
1926 * This is why 'src' will result as unused.
1927 *
1928
1929 *
1930 *
1931 * @code
1932 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
1933 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1934 *   assemble_diagonal_cell_term_velocity(const MatrixFree<dim, Number>& data,
1935 *   Vec& dst,
1936 *   const unsigned int& ,
1937 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
1938 *   if(TR_BDF2_stage == 1) {
1940 *   phi_old_extr(data, 0);
1941 *  
1943 *   /*--- Build a vector of ones to be tested (here we will see the velocity as a whole vector, since
1944 *   dof_handler_velocity is vectorial and so the dof values are vectors). ---*/
1946 *   for(unsigned int d = 0; d < dim; ++d)
1947 *   tmp[d] = make_vectorized_array<Number>(1.0);
1948 *  
1949 *   /*--- Loop over cells in the range ---*/
1950 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1951 *   phi_old_extr.reinit(cell);
1952 *   phi_old_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1953 *   phi.reinit(cell);
1954 *  
1955 *   /*--- Loop over dofs ---*/
1956 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1957 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
1958 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j); /*--- Set all dofs to zero ---*/
1959 *   phi.submit_dof_value(tmp, i); /*--- Set dof i equal to one ---*/
1961 *  
1962 *   /*--- Loop over quadrature points ---*/
1963 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
1964 *   const auto& u_int = phi.get_value(q);
1965 *   const auto& grad_u_int = phi.get_gradient(q);
1966 *   const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1967 *   const auto& tensor_product_u_int = outer_product(u_int, u_n_gamma_ov_2);
1968 *  
1969 *   phi.submit_value(1.0/(gamma*dt)*u_int, q);
1970 *   phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1971 *   }
1973 *   diagonal[i] = phi.get_dof_value(i);
1974 *   }
1975 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
1976 *   phi.submit_dof_value(diagonal[i], i);
1977 *   phi.distribute_local_to_global(dst);
1978 *   }
1979 *   }
1980 *   else {
1982 *   phi_int_extr(data, 0);
1983 *  
1986 *   for(unsigned int d = 0; d < dim; ++d)
1987 *   tmp[d] = make_vectorized_array<Number>(1.0);
1988 *  
1989 *   /*--- Loop over cells in the range ---*/
1990 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1991 *   phi_int_extr.reinit(cell);
1992 *   phi_int_extr.gather_evaluate(u_extr, EvaluationFlags::values);
1993 *   phi.reinit(cell);
1994 *  
1995 *   /*--- Loop over dofs ---*/
1996 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1997 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
1998 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
1999 *   phi.submit_dof_value(tmp, i);
2001 *  
2002 *   /*--- Loop over quadrature points ---*/
2003 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2004 *   const auto& u_curr = phi.get_value(q);
2005 *   const auto& grad_u_curr = phi.get_gradient(q);
2006 *   const auto& u_n1_int = phi_int_extr.get_value(q);
2007 *   const auto& tensor_product_u_curr = outer_product(u_curr, u_n1_int);
2008 *  
2009 *   phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
2010 *   phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
2011 *   }
2013 *   diagonal[i] = phi.get_dof_value(i);
2014 *   }
2015 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2016 *   phi.submit_dof_value(diagonal[i], i);
2017 *   phi.distribute_local_to_global(dst);
2018 *   }
2019 *   }
2020 *   }
2021 *  
2022 *  
2023 * @endcode
2024 *
2025 * The following function assembles diagonal face term for the velocity
2026 *
2027
2028 *
2029 *
2030 * @code
2031 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2032 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2033 *   assemble_diagonal_face_term_velocity(const MatrixFree<dim, Number>& data,
2034 *   Vec& dst,
2035 *   const unsigned int& ,
2036 *   const std::pair<unsigned int, unsigned int>& face_range) const {
2037 *   if(TR_BDF2_stage == 1) {
2039 *   phi_m(data, false, 0),
2040 *   phi_old_extr_p(data, true, 0),
2041 *   phi_old_extr_m(data, false, 0);
2042 *  
2043 *   AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component); /*--- We just assert for safety that dimension match,
2044 *   in the sense that we have selected the proper
2045 *   space ---*/
2046 *   AlignedVector<Tensor<1, dim, VectorizedArray<Number>>> diagonal_p(phi_p.dofs_per_component),
2047 *   diagonal_m(phi_m.dofs_per_component);
2048 *  
2050 *   for(unsigned int d = 0; d < dim; ++d)
2051 *   tmp[d] = make_vectorized_array<Number>(1.0); /*--- We build the usual vector of ones that we will use as dof value ---*/
2052 *  
2053 *   /*--- Now we loop over faces ---*/
2054 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2055 *   phi_old_extr_p.reinit(face);
2056 *   phi_old_extr_p.gather_evaluate(u_extr, EvaluationFlags::values);
2057 *   phi_old_extr_m.reinit(face);
2058 *   phi_old_extr_m.gather_evaluate(u_extr, EvaluationFlags::values);
2059 *   phi_p.reinit(face);
2060 *   phi_m.reinit(face);
2061 *  
2062 *   const auto coef_jump = C_u*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2063 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2064 *  
2065 *   /*--- Loop over dofs. We will set all equal to zero apart from the current one ---*/
2066 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2067 *   for(unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2068 *   phi_p.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2069 *   phi_m.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2070 *   }
2071 *   phi_p.submit_dof_value(tmp, i);
2073 *   phi_m.submit_dof_value(tmp, i);
2075 *  
2076 *   /*--- Loop over quadrature points to compute the integral ---*/
2077 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2078 *   const auto& n_plus = phi_p.get_normal_vector(q);
2079 *   const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2080 *   const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
2081 *   const auto& avg_tensor_product_u_int = 0.5*(outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
2082 *   outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
2083 *   const auto lambda = std::max(std::abs(scalar_product(phi_old_extr_p.get_value(q), n_plus)),
2084 *   std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
2085 *  
2086 *   phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
2087 *   a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int , q);
2088 *   phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
2089 *   a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
2090 *   phi_p.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2091 *   phi_m.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2092 *   }
2094 *   diagonal_p[i] = phi_p.get_dof_value(i);
2096 *   diagonal_m[i] = phi_m.get_dof_value(i);
2097 *   }
2098 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2099 *   phi_p.submit_dof_value(diagonal_p[i], i);
2100 *   phi_m.submit_dof_value(diagonal_m[i], i);
2101 *   }
2102 *   phi_p.distribute_local_to_global(dst);
2103 *   phi_m.distribute_local_to_global(dst);
2104 *   }
2105 *   }
2106 *   else {
2108 *   phi_m(data, false, 0),
2109 *   phi_extr_p(data, true, 0),
2110 *   phi_extr_m(data, false, 0);
2111 *  
2112 *   AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2113 *   AlignedVector<Tensor<1, dim, VectorizedArray<Number>>> diagonal_p(phi_p.dofs_per_component),
2114 *   diagonal_m(phi_m.dofs_per_component);
2116 *   for(unsigned int d = 0; d < dim; ++d)
2117 *   tmp[d] = make_vectorized_array<Number>(1.0);
2118 *  
2119 *   /*--- Now we loop over faces ---*/
2120 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2121 *   phi_extr_p.reinit(face);
2122 *   phi_extr_p.gather_evaluate(u_extr, EvaluationFlags::values);
2123 *   phi_extr_m.reinit(face);
2124 *   phi_extr_m.gather_evaluate(u_extr, EvaluationFlags::values);
2125 *   phi_p.reinit(face);
2126 *   phi_m.reinit(face);
2127 *  
2128 *   const auto coef_jump = C_u*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2129 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2130 *  
2131 *   /*--- Loop over dofs. We will set all equal to zero apart from the current one ---*/
2132 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2133 *   for(unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2134 *   phi_p.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2135 *   phi_m.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2136 *   }
2137 *   phi_p.submit_dof_value(tmp, i);
2139 *   phi_m.submit_dof_value(tmp, i);
2141 *  
2142 *   /*--- Loop over quadrature points to compute the integral ---*/
2143 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2144 *   const auto& n_plus = phi_p.get_normal_vector(q);
2145 *   const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2146 *   const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
2147 *   const auto& avg_tensor_product_u = 0.5*(outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
2148 *   outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
2149 *   const auto lambda = std::max(std::abs(scalar_product(phi_extr_p.get_value(q), n_plus)),
2150 *   std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
2151 *  
2152 *   phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
2153 *   a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
2154 *   phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
2155 *   a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
2156 *   phi_p.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2157 *   phi_m.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2158 *   }
2160 *   diagonal_p[i] = phi_p.get_dof_value(i);
2162 *   diagonal_m[i] = phi_m.get_dof_value(i);
2163 *   }
2164 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2165 *   phi_p.submit_dof_value(diagonal_p[i], i);
2166 *   phi_m.submit_dof_value(diagonal_m[i], i);
2167 *   }
2168 *   phi_p.distribute_local_to_global(dst);
2169 *   phi_m.distribute_local_to_global(dst);
2170 *   }
2171 *   }
2172 *   }
2173 *  
2174 *  
2175 * @endcode
2176 *
2177 * The following function assembles boundary term for the velocity
2178 *
2179
2180 *
2181 *
2182 * @code
2183 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2184 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2185 *   assemble_diagonal_boundary_term_velocity(const MatrixFree<dim, Number>& data,
2186 *   Vec& dst,
2187 *   const unsigned int& ,
2188 *   const std::pair<unsigned int, unsigned int>& face_range) const {
2189 *   if(TR_BDF2_stage == 1) {
2191 *   phi_old_extr(data, true, 0);
2192 *  
2195 *   for(unsigned int d = 0; d < dim; ++d)
2196 *   tmp[d] = make_vectorized_array<Number>(1.0);
2197 *  
2198 *   /*--- Loop over all faces in the range ---*/
2199 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2200 *   phi_old_extr.reinit(face);
2201 *   phi_old_extr.gather_evaluate(u_extr, EvaluationFlags::values);
2202 *   phi.reinit(face);
2203 *  
2204 *   const auto boundary_id = data.get_boundary_id(face);
2205 *   const auto coef_jump = C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2206 *  
2207 *   if(boundary_id != 1) {
2208 *   const double coef_trasp = 0.0;
2209 *  
2210 *   /*--- Loop over all dofs ---*/
2211 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2212 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2213 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2214 *   phi.submit_dof_value(tmp, i);
2216 *  
2217 *   /*--- Loop over quadrature points to compute the integral ---*/
2218 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2219 *   const auto& n_plus = phi.get_normal_vector(q);
2220 *   const auto& grad_u_int = phi.get_gradient(q);
2221 *   const auto& u_int = phi.get_value(q);
2222 *   const auto& tensor_product_u_int = outer_product(phi.get_value(q), phi_old_extr.get_value(q));
2223 *   const auto& lambda = std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2224 *  
2225 *   phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
2226 *   a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
2227 *   phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
2228 *   }
2230 *   diagonal[i] = phi.get_dof_value(i);
2231 *   }
2232 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2233 *   phi.submit_dof_value(diagonal[i], i);
2234 *   phi.distribute_local_to_global(dst);
2235 *   }
2236 *   else {
2237 *   /*--- Loop over all dofs ---*/
2238 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2239 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2240 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2241 *   phi.submit_dof_value(tmp, i);
2243 *  
2244 *   /*--- Loop over quadrature points to compute the integral ---*/
2245 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2246 *   const auto& n_plus = phi.get_normal_vector(q);
2247 *   const auto& grad_u_int = phi.get_gradient(q);
2248 *   const auto& u_int = phi.get_value(q);
2249 *   const auto& lambda = std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2250 *  
2251 *   const auto& point_vectorized = phi.quadrature_point(q);
2252 *   auto u_int_m = u_int;
2253 *   auto grad_u_int_m = grad_u_int;
2254 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2255 *   Point<dim> point;
2256 *   for(unsigned int d = 0; d < dim; ++d)
2257 *   point[d] = point_vectorized[d][v];
2258 *  
2259 *   u_int_m[1][v] = -u_int_m[1][v];
2260 *  
2261 *   grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
2262 *   grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
2263 *   }
2264 *  
2265 *   phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
2266 *   a22*outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
2267 *   a22*0.5*lambda*(u_int - u_int_m), q);
2268 *   phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
2269 *   }
2271 *   diagonal[i] = phi.get_dof_value(i);
2272 *   }
2273 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2274 *   phi.submit_dof_value(diagonal[i], i);
2275 *   phi.distribute_local_to_global(dst);
2276 *   }
2277 *   }
2278 *   }
2279 *   else {
2281 *   phi_extr(data, true, 0);
2282 *  
2285 *   for(unsigned int d = 0; d < dim; ++d)
2286 *   tmp[d] = make_vectorized_array<Number>(1.0);
2287 *  
2288 *   /*--- Loop over all faces in the range ---*/
2289 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2290 *   phi_extr.reinit(face);
2291 *   phi_extr.gather_evaluate(u_extr, EvaluationFlags::values);
2292 *   phi.reinit(face);
2293 *  
2294 *   const auto boundary_id = data.get_boundary_id(face);
2295 *   const auto coef_jump = C_u*std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2296 *  
2297 *   if(boundary_id != 1) {
2298 *   const double coef_trasp = 0.0;
2299 *  
2300 *   /*--- Loop over all dofs ---*/
2301 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2302 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2303 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2304 *   phi.submit_dof_value(tmp, i);
2306 *  
2307 *   /*--- Loop over quadrature points to compute the integral ---*/
2308 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2309 *   const auto& n_plus = phi.get_normal_vector(q);
2310 *   const auto& grad_u = phi.get_gradient(q);
2311 *   const auto& u = phi.get_value(q);
2312 *   const auto& tensor_product_u = outer_product(phi.get_value(q), phi_extr.get_value(q));
2313 *   const auto& lambda = std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2314 *  
2315 *   phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
2316 *   a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
2317 *   phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
2318 *   }
2320 *   diagonal[i] = phi.get_dof_value(i);
2321 *   }
2322 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2323 *   phi.submit_dof_value(diagonal[i], i);
2324 *   phi.distribute_local_to_global(dst);
2325 *   }
2326 *   else {
2327 *   /*--- Loop over all dofs ---*/
2328 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2329 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2330 *   phi.submit_dof_value(Tensor<1, dim, VectorizedArray<Number>>(), j);
2331 *   phi.submit_dof_value(tmp, i);
2333 *  
2334 *   /*--- Loop over quadrature points to compute the integral ---*/
2335 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2336 *   const auto& n_plus = phi.get_normal_vector(q);
2337 *   const auto& grad_u = phi.get_gradient(q);
2338 *   const auto& u = phi.get_value(q);
2339 *   const auto& lambda = std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2340 *  
2341 *   const auto& point_vectorized = phi.quadrature_point(q);
2342 *   auto u_m = u;
2343 *   auto grad_u_m = grad_u;
2344 *   for(unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2345 *   Point<dim> point;
2346 *   for(unsigned int d = 0; d < dim; ++d)
2347 *   point[d] = point_vectorized[d][v];
2348 *  
2349 *   u_m[1][v] = -u_m[1][v];
2350 *  
2351 *   grad_u_m[0][0][v] = -grad_u_m[0][0][v];
2352 *   grad_u_m[0][1][v] = -grad_u_m[0][1][v];
2353 *   }
2354 *  
2355 *   phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
2356 *   a33*outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus +
2357 *   a33*0.5*lambda*(u - u_m), q);
2358 *   phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
2359 *   }
2361 *   diagonal[i] = phi.get_dof_value(i);
2362 *   }
2363 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2364 *   phi.submit_dof_value(diagonal[i], i);
2365 *   phi.distribute_local_to_global(dst);
2366 *   }
2367 *   }
2368 *   }
2369 *   }
2370 *  
2371 *  
2372 * @endcode
2373 *
2374 * Now we consider the pressure related bilinear forms. We first assemble diagonal cell term for the pressure
2375 *
2376
2377 *
2378 *
2379 * @code
2380 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2381 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2382 *   assemble_diagonal_cell_term_pressure(const MatrixFree<dim, Number>& data,
2383 *   Vec& dst,
2384 *   const unsigned int& ,
2385 *   const std::pair<unsigned int, unsigned int>& cell_range) const {
2387 *  
2388 *   AlignedVector<VectorizedArray<Number>> diagonal(phi.dofs_per_component); /*--- Here we are using dofs_per_component but
2389 *   it coincides with dofs_per_cell since it is
2390 *   scalar finite element space ---*/
2391 *  
2392 *   const double coeff = (TR_BDF2_stage == 1) ? 1e6*gamma*dt*gamma*dt : 1e6*(1.0 - gamma)*dt*(1.0 - gamma)*dt;
2393 *  
2394 *   /*--- Loop over all cells in the range ---*/
2395 *   for(unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
2396 *   phi.reinit(cell);
2397 *  
2398 *   /*--- Loop over all dofs ---*/
2399 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2400 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2401 *   phi.submit_dof_value(VectorizedArray<Number>(), j); /*--- We set all dofs to zero ---*/
2402 *   phi.submit_dof_value(make_vectorized_array<Number>(1.0), i); /*--- Now we set the current one to 1; since it is scalar,
2403 *   we can directly use 'make_vectorized_array' without
2404 *   relying on 'Tensor' ---*/
2406 *  
2407 *   /*--- Loop over quadrature points ---*/
2408 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2409 *   phi.submit_value(1.0/coeff*phi.get_value(q), q);
2410 *   phi.submit_gradient(phi.get_gradient(q), q);
2411 *   }
2413 *   diagonal[i] = phi.get_dof_value(i);
2414 *   }
2415 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2416 *   phi.submit_dof_value(diagonal[i], i);
2417 *  
2418 *   phi.distribute_local_to_global(dst);
2419 *   }
2420 *   }
2421 *  
2422 *  
2423 * @endcode
2424 *
2425 * The following function assembles diagonal face term for the pressure
2426 *
2427
2428 *
2429 *
2430 * @code
2431 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2432 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2433 *   assemble_diagonal_face_term_pressure(const MatrixFree<dim, Number>& data,
2434 *   Vec& dst,
2435 *   const unsigned int& ,
2436 *   const std::pair<unsigned int, unsigned int>& face_range) const {
2438 *   phi_m(data, false, 1, 1);
2439 *  
2440 *   AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2441 *   AlignedVector<VectorizedArray<Number>> diagonal_p(phi_p.dofs_per_component),
2442 *   diagonal_m(phi_m.dofs_per_component); /*--- Again, we just assert for safety that dimension
2443 *   match, in the sense that we have selected
2444 *   the proper space ---*/
2445 *  
2446 *   /*--- Loop over all faces ---*/
2447 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2448 *   phi_p.reinit(face);
2449 *   phi_m.reinit(face);
2450 *  
2451 *   const auto coef_jump = C_p*0.5*(std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2452 *   std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2453 *  
2454 *   /*--- Loop over all dofs ---*/
2455 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2456 *   for(unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2457 *   phi_p.submit_dof_value(VectorizedArray<Number>(), j);
2458 *   phi_m.submit_dof_value(VectorizedArray<Number>(), j);
2459 *   }
2460 *   phi_p.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2461 *   phi_m.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2464 *  
2465 *   /*--- Loop over all quadrature points to compute the integral ---*/
2466 *   for(unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2467 *   const auto& n_plus = phi_p.get_normal_vector(q);
2468 *  
2469 *   const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2470 *   const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
2471 *  
2472 *   phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
2473 *   phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
2474 *   phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2475 *   phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2476 *   }
2478 *   diagonal_p[i] = phi_p.get_dof_value(i);
2480 *   diagonal_m[i] = phi_m.get_dof_value(i);
2481 *   }
2482 *   for(unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2483 *   phi_p.submit_dof_value(diagonal_p[i], i);
2484 *   phi_m.submit_dof_value(diagonal_m[i], i);
2485 *   }
2486 *   phi_p.distribute_local_to_global(dst);
2487 *   phi_m.distribute_local_to_global(dst);
2488 *   }
2489 *   }
2490 *  
2491 *  
2492 * @endcode
2493 *
2494 * Eventually, we assemble diagonal boundary term for the pressure
2495 *
2496
2497 *
2498 *
2499 * @code
2500 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2501 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2502 *   assemble_diagonal_boundary_term_pressure(const MatrixFree<dim, Number>& data,
2503 *   Vec& dst,
2504 *   const unsigned int& ,
2505 *   const std::pair<unsigned int, unsigned int>& face_range) const {
2507 *  
2508 *   AlignedVector<VectorizedArray<Number>> diagonal(phi.dofs_per_component);
2509 *  
2510 *   for(unsigned int face = face_range.first; face < face_range.second; ++face) {
2511 *   const auto boundary_id = data.get_boundary_id(face);
2512 *  
2513 *   if(boundary_id == 1) {
2514 *   phi.reinit(face);
2515 *  
2516 *   const auto coef_jump = C_p*std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
2517 *  
2518 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2519 *   for(unsigned int j = 0; j < phi.dofs_per_component; ++j)
2520 *   phi.submit_dof_value(VectorizedArray<Number>(), j);
2521 *   phi.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2523 *  
2524 *   for(unsigned int q = 0; q < phi.n_q_points; ++q) {
2525 *   const auto& n_plus = phi.get_normal_vector(q);
2526 *  
2527 *   const auto& grad_pres = phi.get_gradient(q);
2528 *   const auto& pres = phi.get_value(q);
2529 *  
2530 *   phi.submit_value(-scalar_product(grad_pres, n_plus) + 2.0*coef_jump*pres , q);
2531 *   phi.submit_normal_derivative(-theta_p*pres, q);
2532 *   }
2534 *   diagonal[i] = phi.get_dof_value(i);
2535 *   }
2536 *   for(unsigned int i = 0; i < phi.dofs_per_component; ++i)
2537 *   phi.submit_dof_value(diagonal[i], i);
2538 *   phi.distribute_local_to_global(dst);
2539 *   }
2540 *   }
2541 *   }
2542 *  
2543 *  
2544 * @endcode
2545 *
2546 * Put together all previous steps. We create a dummy auxliary vector that serves for the src input argument in
2547 * the previous functions that as we have seen before is unused. Then everything is done by the 'loop' function
2548 * and it is saved in the field 'inverse_diagonal_entries' already present in the base class. Anyway since there is
2549 * only one field, we need to resize properly depending on whether we are considering the velocity or the pressure.
2550 *
2551
2552 *
2553 *
2554 * @code
2555 *   template<int dim, int fe_degree_p, int fe_degree_v, int n_q_points_1d_p, int n_q_points_1d_v, typename Vec>
2556 *   void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2557 *   compute_diagonal() {
2558 *   Assert(NS_stage == 1 || NS_stage == 2, ExcInternalError());
2559 *  
2560 *   this->inverse_diagonal_entries.reset(new DiagonalMatrix<Vec>());
2561 *   auto& inverse_diagonal = this->inverse_diagonal_entries->get_vector();
2562 *  
2563 *   if(NS_stage == 1) {
2564 *   ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2565 *   (*(this->data),
2566 *   inverse_diagonal,
2567 *   [&](const auto& data, auto& dst, const auto& src, const auto& cell_range) {
2568 *   (this->assemble_diagonal_cell_term_velocity)(data, dst, src, cell_range);
2569 *   },
2570 *   [&](const auto& data, auto& dst, const auto& src, const auto& face_range) {
2571 *   (this->assemble_diagonal_face_term_velocity)(data, dst, src, face_range);
2572 *   },
2573 *   [&](const auto& data, auto& dst, const auto& src, const auto& boundary_range) {
2574 *   (this->assemble_diagonal_boundary_term_velocity)(data, dst, src, boundary_range);
2575 *   },
2576 *   0);
2577 *   }
2578 *   else if(NS_stage == 2) {
2579 *   ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2580 *   (*(this->data),
2581 *   inverse_diagonal,
2582 *   [&](const auto& data, auto& dst, const auto& src, const auto& cell_range) {
2583 *   (this->assemble_diagonal_cell_term_pressure)(data, dst, src, cell_range);
2584 *   },
2585 *   [&](const auto& data, auto& dst, const auto& src, const auto& face_range) {
2586 *   (this->assemble_diagonal_face_term_pressure)(data, dst, src, face_range);
2587 *   },
2588 *   [&](const auto& data, auto& dst, const auto& src, const auto& boundary_range) {
2589 *   (this->assemble_diagonal_boundary_term_pressure)(data, dst, src, boundary_range);
2590 *   },
2591 *   1);
2592 *   }
2593 *  
2594 *   for(unsigned int i = 0; i < inverse_diagonal.locally_owned_size(); ++i) {
2595 *   Assert(inverse_diagonal.local_element(i) != 0.0,
2596 *   ExcMessage("No diagonal entry in a definite operator should be zero"));
2597 *   inverse_diagonal.local_element(i) = 1.0/inverse_diagonal.local_element(i);
2598 *   }
2599 *   }
2600 *  
2601 *  
2602 * @endcode
2603 *
2604 *
2605 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
2606 * @sect{The <code>NavierStokesProjection</code> class}
2607 *
2608
2609 *
2610 * Now we are ready for the main class of the program. It implements the calls to the various steps
2611 * of the projection method for Navier-Stokes equations.
2612 *
2613
2614 *
2615 *
2616 * @code
2617 *   template<int dim>
2618 *   class NavierStokesProjection {
2619 *   public:
2620 *   NavierStokesProjection(RunTimeParameters::Data_Storage& data);
2621 *  
2622 *   void run(const bool verbose = false, const unsigned int output_interval = 10);
2623 *  
2624 *   protected:
2625 *   const double t_0;
2626 *   const double T;
2627 *   const double gamma; //--- TR-BDF2 parameter
2628 *   unsigned int TR_BDF2_stage; //--- Flag to check at which current stage of TR-BDF2 are
2629 *   const double Re;
2630 *   double dt;
2631 *  
2632 *   EquationData::Velocity<dim> vel_init;
2633 *   EquationData::Pressure<dim> pres_init; /*--- Instance of 'Velocity' and 'Pressure' classes to initialize. ---*/
2634 *  
2636 *  
2637 *   /*--- Finite Element spaces ---*/
2638 *   FESystem<dim> fe_velocity;
2639 *   FESystem<dim> fe_pressure;
2640 *  
2641 *   /*--- Handler for dofs ---*/
2642 *   DoFHandler<dim> dof_handler_velocity;
2643 *   DoFHandler<dim> dof_handler_pressure;
2644 *  
2645 *   /*--- Quadrature formulas for velocity and pressure, respectively ---*/
2646 *   QGauss<dim> quadrature_pressure;
2647 *   QGauss<dim> quadrature_velocity;
2648 *  
2649 *   /*--- Now we define all the vectors for the solution. We start from the pressure
2650 *   with p^n, p^(n+gamma) and a vector for rhs ---*/
2654 *  
2655 *   /*--- Next, we move to the velocity, with u^n, u^(n-1), u^(n+gamma/2),
2656 *   u^(n+gamma) and other two auxiliary vectors as well as the rhs ---*/
2665 *  
2666 *   Vector<double> Linfty_error_per_cell_vel;
2667 *  
2668 *   DeclException2(ExcInvalidTimeStep,
2669 *   double,
2670 *   double,
2671 *   << " The time step " << arg1 << " is out of range."
2672 *   << std::endl
2673 *   << " The permitted range is (0," << arg2 << "]");
2674 *  
2675 *   void create_triangulation(const unsigned int n_refines);
2676 *  
2677 *   void setup_dofs();
2678 *  
2679 *   void initialize();
2680 *  
2681 *   void interpolate_velocity();
2682 *  
2683 *   void diffusion_step();
2684 *  
2685 *   void projection_step();
2686 *  
2687 *   void project_grad(const unsigned int flag);
2688 *  
2689 *   double get_maximal_velocity();
2690 *  
2691 *   double get_maximal_difference_velocity();
2692 *  
2693 *   void output_results(const unsigned int step);
2694 *  
2695 *   void refine_mesh();
2696 *  
2697 *   void interpolate_max_res(const unsigned int level);
2698 *  
2699 *   void save_max_res();
2700 *  
2701 *   private:
2702 *   void compute_lift_and_drag();
2703 *  
2704 *   /*--- Technical member to handle the various steps ---*/
2705 *   std::shared_ptr<MatrixFree<dim, double>> matrix_free_storage;
2706 *  
2707 *   /*--- Now we need an instance of the class implemented before with the weak form ---*/
2708 *   NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2709 *   EquationData::degree_p + 1, EquationData::degree_p + 2,
2710 *   LinearAlgebra::distributed::Vector<double>> navier_stokes_matrix;
2711 *  
2712 *   /*--- This is an instance for geometric multigrid preconditioner ---*/
2713 *   MGLevelObject<NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2714 *   EquationData::degree_p + 1, EquationData::degree_p + 2,
2716 *  
2717 *   /*--- Here we define two 'AffineConstraints' instance, one for each finite element space.
2718 *   This is just a technical issue, due to MatrixFree requirements. In general
2719 *   this class is used to impose boundary conditions (or any kind of constraints), but in this case, since
2720 *   we are using a weak imposition of bcs, everything is already in the weak forms and so these instances
2721 *   will be default constructed ---*/
2722 *   AffineConstraints<double> constraints_velocity,
2723 *   constraints_pressure;
2724 *  
2725 *   /*--- Now a bunch of variables handled by 'ParamHandler' introduced at the beginning of the code ---*/
2726 *   unsigned int max_its;
2727 *   double eps;
2728 *  
2729 *   unsigned int max_loc_refinements;
2730 *   unsigned int min_loc_refinements;
2731 *   unsigned int refinement_iterations;
2732 *  
2733 *   std::string saving_dir;
2734 *  
2735 *   /*--- Finally, some output related streams ---*/
2736 *   ConditionalOStream pcout;
2737 *  
2738 *   std::ofstream time_out;
2739 *   ConditionalOStream ptime_out;
2740 *   TimerOutput time_table;
2741 *  
2742 *   std::ofstream output_n_dofs_velocity;
2743 *   std::ofstream output_n_dofs_pressure;
2744 *  
2745 *   std::ofstream output_lift;
2746 *   std::ofstream output_drag;
2747 *   };
2748 *  
2749 *  
2750 * @endcode
2751 *
2752 * In the constructor, we just read all the data from the
2753 * <code>Data_Storage</code> object that is passed as an argument, verify that
2754 * the data we read are reasonable and, finally, create the triangulation and
2755 * load the initial data.
2756 *
2757
2758 *
2759 *
2760 * @code
2761 *   template<int dim>
2762 *   NavierStokesProjection<dim>::NavierStokesProjection(RunTimeParameters::Data_Storage& data):
2763 *   t_0(data.initial_time),
2764 *   T(data.final_time),
2765 *   gamma(2.0 - std::sqrt(2.0)), //--- Save also in the NavierStokes class the TR-BDF2 parameter value
2766 *   TR_BDF2_stage(1), //--- Initialize the flag for the TR_BDF2 stage
2767 *   Re(data.Reynolds),
2768 *   dt(data.dt),
2769 *   vel_init(data.initial_time),
2770 *   pres_init(data.initial_time),
2771 *   triangulation(MPI_COMM_WORLD, parallel::distributed::Triangulation<dim>::limit_level_difference_at_vertices,
2772 *   parallel::distributed::Triangulation<dim>::construct_multigrid_hierarchy),
2773 *   fe_velocity(FE_DGQ<dim>(EquationData::degree_p + 1), dim),
2774 *   fe_pressure(FE_DGQ<dim>(EquationData::degree_p), 1),
2775 *   dof_handler_velocity(triangulation),
2776 *   dof_handler_pressure(triangulation),
2777 *   quadrature_pressure(EquationData::degree_p + 1),
2778 *   quadrature_velocity(EquationData::degree_p + 2),
2779 *   navier_stokes_matrix(data),
2780 *   max_its(data.max_iterations),
2781 *   eps(data.eps),
2782 *   max_loc_refinements(data.max_loc_refinements),
2783 *   min_loc_refinements(data.min_loc_refinements),
2784 *   refinement_iterations(data.refinement_iterations),
2785 *   saving_dir(data.dir),
2786 *   pcout(std::cout, Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0),
2787 *   time_out("./" + data.dir + "/time_analysis_" +
2788 *   Utilities::int_to_string(Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD)) + "proc.dat"),
2789 *   ptime_out(time_out, Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0),
2790 *   time_table(ptime_out, TimerOutput::summary, TimerOutput::cpu_and_wall_times),
2791 *   output_n_dofs_velocity("./" + data.dir + "/n_dofs_velocity.dat", std::ofstream::out),
2792 *   output_n_dofs_pressure("./" + data.dir + "/n_dofs_pressure.dat", std::ofstream::out),
2793 *   output_lift("./" + data.dir + "/lift.dat", std::ofstream::out),
2794 *   output_drag("./" + data.dir + "/drag.dat", std::ofstream::out) {
2795 *   if(EquationData::degree_p < 1) {
2796 *   pcout
2797 *   << " WARNING: The chosen pair of finite element spaces is not stable."
2798 *   << std::endl
2799 *   << " The obtained results will be nonsense" << std::endl;
2800 *   }
2801 *  
2802 *   AssertThrow(!((dt <= 0.0) || (dt > 0.5*T)), ExcInvalidTimeStep(dt, 0.5*T));
2803 *  
2804 *   matrix_free_storage = std::make_shared<MatrixFree<dim, double>>();
2805 *  
2806 *   create_triangulation(data.n_refines);
2807 *   setup_dofs();
2808 *   initialize();
2809 *   }
2810 *  
2811 *  
2812 * @endcode
2813 *
2814 * The method that creates the triangulation and refines it the needed number
2815 * of times.
2816 *
2817
2818 *
2819 *
2820 * @code
2821 *   template<int dim>
2822 *   void NavierStokesProjection<dim>::create_triangulation(const unsigned int n_refines) {
2823 *   TimerOutput::Scope t(time_table, "Create triangulation");
2824 *  
2825 *   GridGenerator::plate_with_a_hole(triangulation, 0.5, 1.0, 1.0, 1.1, 1.0, 19.0, Point<2>(2.0, 2.0), 0, 1, 1.0, 2, true);
2826 *   /*--- We strongly advice to check the documentation to verify the meaning of all input parameters. ---*/
2827 *  
2828 *   pcout << "Number of refines = " << n_refines << std::endl;
2829 *   triangulation.refine_global(n_refines);
2830 *   }
2831 *  
2832 *  
2833 * @endcode
2834 *
2835 * After creating the triangulation, it creates the mesh dependent
2836 * data, i.e. it distributes degrees of freedom, and
2837 * initializes the vectors that we will use.
2838 *
2839
2840 *
2841 *
2842 * @code
2843 *   template<int dim>
2844 *   void NavierStokesProjection<dim>::setup_dofs() {
2845 *   pcout << "Number of active cells: " << triangulation.n_global_active_cells() << std::endl;
2846 *   pcout << "Number of levels: " << triangulation.n_global_levels() << std::endl;
2847 *  
2848 *   /*--- Distribute dofs and prepare for multigrid ---*/
2849 *   dof_handler_velocity.distribute_dofs(fe_velocity);
2850 *   dof_handler_pressure.distribute_dofs(fe_pressure);
2851 *  
2852 *   pcout << "dim (X_h) = " << dof_handler_velocity.n_dofs()
2853 *   << std::endl
2854 *   << "dim (M_h) = " << dof_handler_pressure.n_dofs()
2855 *   << std::endl
2856 *   << "Re = " << Re << std::endl
2857 *   << std::endl;
2858 *  
2859 *   if(Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) {
2860 *   output_n_dofs_velocity << dof_handler_velocity.n_dofs() << std::endl;
2861 *   output_n_dofs_pressure << dof_handler_pressure.n_dofs() << std::endl;
2862 *   }
2863 *  
2864 *   typename MatrixFree<dim, double>::AdditionalData additional_data;
2865 *   additional_data.mapping_update_flags = (update_gradients | update_JxW_values |
2867 *   additional_data.mapping_update_flags_inner_faces = (update_gradients | update_JxW_values | update_quadrature_points |
2869 *   additional_data.mapping_update_flags_boundary_faces = (update_gradients | update_JxW_values | update_quadrature_points |
2871 *   additional_data.tasks_parallel_scheme = MatrixFree<dim, double>::AdditionalData::none;
2872 *  
2873 *   std::vector<const DoFHandler<dim>*> dof_handlers; /*--- Vector of dof_handlers to feed the 'MatrixFree'. Here the order
2874 *   counts and enters into the game as parameter of FEEvaluation and
2875 *   FEFaceEvaluation in the previous class ---*/
2876 *   dof_handlers.push_back(&dof_handler_velocity);
2877 *   dof_handlers.push_back(&dof_handler_pressure);
2878 *  
2879 *   constraints_velocity.clear();
2880 *   constraints_velocity.close();
2881 *   constraints_pressure.clear();
2882 *   constraints_pressure.close();
2883 *   std::vector<const AffineConstraints<double>*> constraints;
2884 *   constraints.push_back(&constraints_velocity);
2885 *   constraints.push_back(&constraints_pressure);
2886 *  
2887 *   std::vector<QGauss<1>> quadratures; /*--- We cannot directly use 'quadrature_velocity' and 'quadrature_pressure',
2888 *   because the 'MatrixFree' structure wants a quadrature formula for 1D
2889 *   (this is way the template parameter of the previous class was called 'n_q_points_1d_p'
2890 *   and 'n_q_points_1d_v' and the reason of '1' as QGauss template parameter). ---*/
2891 *   quadratures.push_back(QGauss<1>(EquationData::degree_p + 2));
2892 *   quadratures.push_back(QGauss<1>(EquationData::degree_p + 1));
2893 *  
2894 *   /*--- Initialize the matrix-free structure and size properly the vectors. Here again the
2895 *   second input argument of the 'initialize_dof_vector' method depends on the order of 'dof_handlers' ---*/
2896 *   matrix_free_storage->reinit(MappingQ1<dim>(),dof_handlers, constraints, quadratures, additional_data);
2897 *   matrix_free_storage->initialize_dof_vector(u_star, 0);
2898 *   matrix_free_storage->initialize_dof_vector(rhs_u, 0);
2899 *   matrix_free_storage->initialize_dof_vector(u_n, 0);
2900 *   matrix_free_storage->initialize_dof_vector(u_extr, 0);
2901 *   matrix_free_storage->initialize_dof_vector(u_n_minus_1, 0);
2902 *   matrix_free_storage->initialize_dof_vector(u_n_gamma, 0);
2903 *   matrix_free_storage->initialize_dof_vector(u_tmp, 0);
2904 *   matrix_free_storage->initialize_dof_vector(grad_pres_int, 0);
2905 *  
2906 *   matrix_free_storage->initialize_dof_vector(pres_int, 1);
2907 *   matrix_free_storage->initialize_dof_vector(pres_n, 1);
2908 *   matrix_free_storage->initialize_dof_vector(rhs_p, 1);
2909 *  
2910 *   /*--- Initialize the multigrid structure. We dedicate ad hoc 'dof_handlers_mg' and 'constraints_mg' because
2911 *   we use float as type. Moreover we can initialize already with the index of the finite element of the pressure;
2912 *   anyway we need by requirement to declare also structures for the velocity for coherence (basically because
2913 *   the index of finite element space has to be the same, so the pressure has to be the second).---*/
2914 *   mg_matrices.clear_elements();
2915 *   dof_handler_velocity.distribute_mg_dofs();
2916 *   dof_handler_pressure.distribute_mg_dofs();
2917 *  
2918 *   const unsigned int nlevels = triangulation.n_global_levels();
2919 *   mg_matrices.resize(0, nlevels - 1);
2920 *   for(unsigned int level = 0; level < nlevels; ++level) {
2921 *   typename MatrixFree<dim, float>::AdditionalData additional_data_mg;
2923 *   additional_data_mg.mapping_update_flags = (update_gradients | update_JxW_values);
2924 *   additional_data_mg.mapping_update_flags_inner_faces = (update_gradients | update_JxW_values);
2925 *   additional_data_mg.mapping_update_flags_boundary_faces = (update_gradients | update_JxW_values);
2926 *   additional_data_mg.mg_level = level;
2927 *  
2928 *   std::vector<const DoFHandler<dim>*> dof_handlers_mg;
2929 *   dof_handlers_mg.push_back(&dof_handler_velocity);
2930 *   dof_handlers_mg.push_back(&dof_handler_pressure);
2931 *   std::vector<const AffineConstraints<float>*> constraints_mg;
2932 *   AffineConstraints<float> constraints_velocity_mg;
2933 *   constraints_velocity_mg.clear();
2934 *   constraints_velocity_mg.close();
2935 *   constraints_mg.push_back(&constraints_velocity_mg);
2936 *   AffineConstraints<float> constraints_pressure_mg;
2937 *   constraints_pressure_mg.clear();
2938 *   constraints_pressure_mg.close();
2939 *   constraints_mg.push_back(&constraints_pressure_mg);
2940 *  
2941 *   std::shared_ptr<MatrixFree<dim, float>> mg_mf_storage_level(new MatrixFree<dim, float>());
2942 *   mg_mf_storage_level->reinit(MappingQ1<dim>(),dof_handlers_mg, constraints_mg, quadratures, additional_data_mg);
2943 *   const std::vector<unsigned int> tmp = {1};
2944 *   mg_matrices[level].initialize(mg_mf_storage_level, tmp, tmp);
2945 *   mg_matrices[level].set_dt(dt);
2946 *   mg_matrices[level].set_NS_stage(2);
2947 *   }
2948 *  
2949 *   Linfty_error_per_cell_vel.reinit(triangulation.n_active_cells());
2950 *   }
2951 *  
2952 *  
2953 * @endcode
2954 *
2955 * This method loads the initial data. It simply uses the class <code>Pressure</code> instance for the pressure
2956 * and the class <code>Velocity</code> instance for the velocity.
2957 *
2958
2959 *
2960 *
2961 * @code
2962 *   template<int dim>
2963 *   void NavierStokesProjection<dim>::initialize() {
2964 *   TimerOutput::Scope t(time_table, "Initialize pressure and velocity");
2965 *  
2966 *   VectorTools::interpolate(dof_handler_pressure, pres_init, pres_n);
2967 *  
2968 *   VectorTools::interpolate(dof_handler_velocity, vel_init, u_n_minus_1);
2969 *   VectorTools::interpolate(dof_handler_velocity, vel_init, u_n);
2970 *   }
2971 *  
2972 *  
2973 * @endcode
2974 *
2975 * This function computes the extrapolated velocity to be used in the momentum predictor
2976 *
2977
2978 *
2979 *
2980 * @code
2981 *   template<int dim>
2982 *   void NavierStokesProjection<dim>::interpolate_velocity() {
2983 *   TimerOutput::Scope t(time_table, "Interpolate velocity");
2984 *  
2985 * @endcode
2986 *
2987 * --- TR-BDF2 first step
2988 *
2989 * @code
2990 *   if(TR_BDF2_stage == 1) {
2991 *   u_extr.equ(1.0 + gamma/(2.0*(1.0 - gamma)), u_n);
2992 *   u_tmp.equ(gamma/(2.0*(1.0 - gamma)), u_n_minus_1);
2993 *   u_extr -= u_tmp;
2994 *   }
2995 * @endcode
2996 *
2997 * --- TR-BDF2 second step
2998 *
2999 * @code
3000 *   else {
3001 *   u_extr.equ(1.0 + (1.0 - gamma)/gamma, u_n_gamma);
3002 *   u_tmp.equ((1.0 - gamma)/gamma, u_n);
3003 *   u_extr -= u_tmp;
3004 *   }
3005 *   }
3006 *  
3007 *  
3008 * @endcode
3009 *
3010 * We are finally ready to solve the diffusion step.
3011 *
3012
3013 *
3014 *
3015 * @code
3016 *   template<int dim>
3017 *   void NavierStokesProjection<dim>::diffusion_step() {
3018 *   TimerOutput::Scope t(time_table, "Diffusion step");
3019 *  
3020 *   /*--- We first specify that we want to deal with velocity dof_handler (index 0, since it is the first one
3021 *   in the 'dof_handlers' vector) ---*/
3022 *   const std::vector<unsigned int> tmp = {0};
3023 *   navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3024 *  
3025 *   /*--- Next, we specify at we are at stage 1, namely the diffusion step ---*/
3026 *   navier_stokes_matrix.set_NS_stage(1);
3027 *  
3028 *   /*--- Now, we compute the right-hand side and we set the convective velocity. The necessity of 'set_u_extr' is
3029 *   that this quantity is required in the bilinear forms and we can't use a vector of src like on the right-hand side,
3030 *   so it has to be available ---*/
3031 *   if(TR_BDF2_stage == 1) {
3032 *   navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_extr, pres_n});
3033 *   navier_stokes_matrix.set_u_extr(u_extr);
3034 *   u_star = u_extr;
3035 *   }
3036 *   else {
3037 *   navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_n_gamma, pres_int, u_extr});
3038 *   navier_stokes_matrix.set_u_extr(u_extr);
3039 *   u_star = u_extr;
3040 *   }
3041 *  
3042 *   /*--- Build the linear solver; in this case we specify the maximum number of iterations and residual ---*/
3043 *   SolverControl solver_control(max_its, eps*rhs_u.l2_norm());
3045 *  
3046 *   /*--- Build a Jacobi preconditioner and solve ---*/
3047 *   PreconditionJacobi<NavierStokesProjectionOperator<dim,
3048 *   EquationData::degree_p,
3049 *   EquationData::degree_p + 1,
3050 *   EquationData::degree_p + 1,
3051 *   EquationData::degree_p + 2,
3052 *   LinearAlgebra::distributed::Vector<double>>> preconditioner;
3053 *   navier_stokes_matrix.compute_diagonal();
3054 *   preconditioner.initialize(navier_stokes_matrix);
3055 *  
3056 *   gmres.solve(navier_stokes_matrix, u_star, rhs_u, preconditioner);
3057 *   }
3058 *  
3059 *  
3060 * @endcode
3061 *
3062 * Next, we solve the projection step.
3063 *
3064
3065 *
3066 *
3067 * @code
3068 *   template<int dim>
3069 *   void NavierStokesProjection<dim>::projection_step() {
3070 *   TimerOutput::Scope t(time_table, "Projection step pressure");
3071 *  
3072 *   /*--- We start in the same way of 'diffusion_step': we first reinitialize with the index of FE space,
3073 *   we specify that this is the second stage and we compute the right-hand side ---*/
3074 *   const std::vector<unsigned int> tmp = {1};
3075 *   navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3076 *  
3077 *   navier_stokes_matrix.set_NS_stage(2);
3078 *  
3079 *   if(TR_BDF2_stage == 1)
3080 *   navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_n});
3081 *   else
3082 *   navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_int});
3083 *  
3084 *   /*--- Build the linear solver (Conjugate Gradient in this case) ---*/
3085 *   SolverControl solver_control(max_its, eps*rhs_p.l2_norm());
3087 *  
3088 *   /*--- Build the preconditioner (as in @ref step_37 "step-37") ---*/
3089 *   MGTransferMatrixFree<dim, float> mg_transfer;
3090 *   mg_transfer.build(dof_handler_pressure);
3091 *  
3092 *   using SmootherType = PreconditionChebyshev<NavierStokesProjectionOperator<dim,
3093 *   EquationData::degree_p,
3094 *   EquationData::degree_p + 1,
3095 *   EquationData::degree_p + 1,
3096 *   EquationData::degree_p + 2,
3101 *   smoother_data.resize(0, triangulation.n_global_levels() - 1);
3102 *   for(unsigned int level = 0; level < triangulation.n_global_levels(); ++level) {
3103 *   if(level > 0) {
3104 *   smoother_data[level].smoothing_range = 15.0;
3105 *   smoother_data[level].degree = 3;
3106 *   smoother_data[level].eig_cg_n_iterations = 10;
3107 *   }
3108 *   else {
3109 *   smoother_data[0].smoothing_range = 2e-2;
3110 *   smoother_data[0].degree = numbers::invalid_unsigned_int;
3111 *   smoother_data[0].eig_cg_n_iterations = mg_matrices[0].m();
3112 *   }
3113 *   mg_matrices[level].compute_diagonal();
3114 *   smoother_data[level].preconditioner = mg_matrices[level].get_matrix_diagonal_inverse();
3115 *   }
3116 *   mg_smoother.initialize(mg_matrices, smoother_data);
3117 *  
3119 *   SolverCG<LinearAlgebra::distributed::Vector<float>> cg_mg(solver_control);
3122 *   NavierStokesProjectionOperator<dim,
3123 *   EquationData::degree_p,
3124 *   EquationData::degree_p + 1,
3125 *   EquationData::degree_p + 1,
3126 *   EquationData::degree_p + 2,
3128 *   PreconditionIdentity> mg_coarse(cg_mg, mg_matrices[0], identity);
3129 *  
3130 *   mg::Matrix<LinearAlgebra::distributed::Vector<float>> mg_matrix(mg_matrices);
3131 *  
3132 *   Multigrid<LinearAlgebra::distributed::Vector<float>> mg(mg_matrix, mg_coarse, mg_transfer, mg_smoother, mg_smoother);
3133 *  
3134 *   PreconditionMG<dim,
3136 *   MGTransferMatrixFree<dim, float>> preconditioner(dof_handler_pressure, mg, mg_transfer);
3137 *  
3138 *   /*--- Solve the linear system ---*/
3139 *   if(TR_BDF2_stage == 1) {
3140 *   pres_int = pres_n;
3141 *   cg.solve(navier_stokes_matrix, pres_int, rhs_p, preconditioner);
3142 *   }
3143 *   else {
3144 *   pres_n = pres_int;
3145 *   cg.solve(navier_stokes_matrix, pres_n, rhs_p, preconditioner);
3146 *   }
3147 *   }
3148 *  
3149 *  
3150 * @endcode
3151 *
3152 * This implements the projection step for the gradient of pressure
3153 *
3154
3155 *
3156 *
3157 * @code
3158 *   template<int dim>
3159 *   void NavierStokesProjection<dim>::project_grad(const unsigned int flag) {
3160 *   TimerOutput::Scope t(time_table, "Gradient of pressure projection");
3161 *  
3162 *   /*--- The input parameter flag is used just to specify where we want to save the result ---*/
3163 *   AssertIndexRange(flag, 3);
3164 *   Assert(flag > 0, ExcInternalError());
3165 *  
3166 *   /*--- We need to select the dof handler related to the velocity since the result lives there ---*/
3167 *   const std::vector<unsigned int> tmp = {0};
3168 *   navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3169 *  
3170 *   if(flag == 1)
3171 *   navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_n);
3172 *   else if(flag == 2)
3173 *   navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_int);
3174 *  
3175 *   /*--- We conventionally decide that the this corresponds to third stage ---*/
3176 *   navier_stokes_matrix.set_NS_stage(3);
3177 *  
3178 *   /*--- Solve the system ---*/
3179 *   SolverControl solver_control(max_its, 1e-12*rhs_u.l2_norm());
3181 *   cg.solve(navier_stokes_matrix, u_tmp, rhs_u, PreconditionIdentity());
3182 *   }
3183 *  
3184 *  
3185 * @endcode
3186 *
3187 * The following function is used in determining the maximal velocity
3188 * in order to compute the Courant number.
3189 *
3190
3191 *
3192 *
3193 * @code
3194 *   template<int dim>
3195 *   double NavierStokesProjection<dim>::get_maximal_velocity() {
3196 *   return u_n.linfty_norm();
3197 *   }
3198 *  
3199 *  
3200 * @endcode
3201 *
3202 * The following function is used in determining the maximal nodal difference
3203 * between old and current velocity value in order to see if we have reached steady-state.
3204 *
3205
3206 *
3207 *
3208 * @code
3209 *   template<int dim>
3210 *   double NavierStokesProjection<dim>::get_maximal_difference_velocity() {
3211 *   u_tmp = u_n;
3212 *   u_tmp -= u_n_minus_1;
3213 *  
3214 *   return u_tmp.linfty_norm();
3215 *   }
3216 *  
3217 *  
3218 * @endcode
3219 *
3220 * This method plots the current solution. The main difficulty is that we want
3221 * to create a single output file that contains the data for all velocity
3222 * components and the pressure. On the other hand, velocities and the pressure
3223 * live on separate DoFHandler objects, so we need to pay attention when we use
3224 * 'add_data_vector' to select the proper space.
3225 *
3226
3227 *
3228 *
3229 * @code
3230 *   template<int dim>
3231 *   void NavierStokesProjection<dim>::output_results(const unsigned int step) {
3232 *   TimerOutput::Scope t(time_table, "Output results");
3233 *  
3234 *   DataOut<dim> data_out;
3235 *  
3236 *   std::vector<std::string> velocity_names(dim, "v");
3237 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
3238 *   component_interpretation_velocity(dim, DataComponentInterpretation::component_is_part_of_vector);
3239 *   u_n.update_ghost_values();
3240 *   data_out.add_data_vector(dof_handler_velocity, u_n, velocity_names, component_interpretation_velocity);
3241 *   pres_n.update_ghost_values();
3242 *   data_out.add_data_vector(dof_handler_pressure, pres_n, "p", {DataComponentInterpretation::component_is_scalar});
3243 *  
3244 *   std::vector<std::string> velocity_names_old(dim, "v_old");
3245 *   u_n_minus_1.update_ghost_values();
3246 *   data_out.add_data_vector(dof_handler_velocity, u_n_minus_1, velocity_names_old, component_interpretation_velocity);
3247 *  
3248 *   /*--- Here we rely on the postprocessor we have built ---*/
3249 *   PostprocessorVorticity<dim> postprocessor;
3250 *   data_out.add_data_vector(dof_handler_velocity, u_n, postprocessor);
3251 *  
3252 *   data_out.build_patches(MappingQ1<dim>(), 1, DataOut<dim>::curved_inner_cells);
3253 *  
3254 *   const std::string output = "./" + saving_dir + "/solution-" + Utilities::int_to_string(step, 5) + ".vtu";
3255 *   data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3256 *   }
3257 *  
3258 *  
3259 * @endcode
3260 *
3261 *
3262 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
3263 * @sect{<code>NavierStokesProjection::compute_lift_and_drag</code>}
3264 *
3265
3266 *
3267 * This routine computes the lift and the drag forces in a non-dimensional framework
3268 * (so basically for the classical coefficients, it is necessary to multiply by a factor 2).
3269 *
3270
3271 *
3272 *
3273 * @code
3274 *   template<int dim>
3275 *   void NavierStokesProjection<dim>::compute_lift_and_drag() {
3276 *   QGauss<dim - 1> face_quadrature_formula(EquationData::degree_p + 2);
3277 *   const int n_q_points = face_quadrature_formula.size();
3278 *  
3279 *   std::vector<double> pressure_values(n_q_points);
3280 *   std::vector<std::vector<Tensor<1, dim>>> velocity_gradients(n_q_points, std::vector<Tensor<1, dim>>(dim));
3281 *  
3282 *   Tensor<1, dim> normal_vector;
3283 *   Tensor<2, dim> fluid_stress;
3284 *   Tensor<2, dim> fluid_pressure;
3285 *   Tensor<1, dim> forces;
3286 *  
3287 *   /*--- We need to compute the integral over the cylinder boundary, so we need to use 'FEFaceValues' instances.
3288 *   For the velocity we need the gradients, for the pressure the values. ---*/
3289 *   FEFaceValues<dim> fe_face_values_velocity(fe_velocity, face_quadrature_formula,
3292 *   FEFaceValues<dim> fe_face_values_pressure(fe_pressure, face_quadrature_formula, update_values);
3293 *  
3294 *   double local_drag = 0.0;
3295 *   double local_lift = 0.0;
3296 *  
3297 *   /*--- We need to perform a unique loop because the whole stress tensor takes into account contributions of
3298 *   velocity and pressure obviously. However, the two dof_handlers are different, so we need to create an ad-hoc
3299 *   iterator for the pressure that we update manually. It is guaranteed that the cells are visited in the same order
3300 *   (see the documentation) ---*/
3301 *   auto tmp_cell = dof_handler_pressure.begin_active();
3302 *   for(const auto& cell : dof_handler_velocity.active_cell_iterators()) {
3303 *   if(cell->is_locally_owned()) {
3304 *   for(unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face) {
3305 *   if(cell->face(face)->at_boundary() && cell->face(face)->boundary_id() == 4) {
3306 *   fe_face_values_velocity.reinit(cell, face);
3307 *   fe_face_values_pressure.reinit(tmp_cell, face);
3308 *  
3309 *   fe_face_values_velocity.get_function_gradients(u_n, velocity_gradients); /*--- velocity gradients ---*/
3310 *   fe_face_values_pressure.get_function_values(pres_n, pressure_values); /*--- pressure values ---*/
3311 *  
3312 *   for(int q = 0; q < n_q_points; q++) {
3313 *   normal_vector = -fe_face_values_velocity.normal_vector(q);
3314 *  
3315 *   for(unsigned int d = 0; d < dim; ++ d) {
3316 *   fluid_pressure[d][d] = pressure_values[q];
3317 *   for(unsigned int k = 0; k < dim; ++k)
3318 *   fluid_stress[d][k] = 1.0/Re*velocity_gradients[q][d][k];
3319 *   }
3320 *   fluid_stress = fluid_stress - fluid_pressure;
3321 *  
3322 *   forces = fluid_stress*normal_vector*fe_face_values_velocity.JxW(q);
3323 *  
3324 *   local_drag += forces[0];
3325 *   local_lift += forces[1];
3326 *   }
3327 *   }
3328 *   }
3329 *   }
3330 *   ++tmp_cell;
3331 *   }
3332 *  
3333 *   /*--- At the end, each processor has computed the contribution to the boundary cells it owns and, therefore,
3334 *   we need to sum up all the contributions. ---*/
3335 *   const double lift = Utilities::MPI::sum(local_lift, MPI_COMM_WORLD);
3336 *   const double drag = Utilities::MPI::sum(local_drag, MPI_COMM_WORLD);
3337 *   if(Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) {
3338 *   output_lift << lift << std::endl;
3339 *   output_drag << drag << std::endl;
3340 *   }
3341 *   }
3342 *  
3343 *  
3344 * @endcode
3345 *
3346 *
3347 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
3348 * @sect{ <code>NavierStokesProjection::refine_mesh</code>}
3349 *
3350
3351 *
3352 * After finding a good initial guess on the coarse mesh, we hope to
3353 * decrease the error through refining the mesh. We also need to transfer the current solution to the
3354 * next mesh using the SolutionTransfer class.
3355 *
3356
3357 *
3358 *
3359 * @code
3360 *   template <int dim>
3361 *   void NavierStokesProjection<dim>::refine_mesh() {
3362 *   TimerOutput::Scope t(time_table, "Refine mesh");
3363 *  
3364 *   /*--- We first create a proper vector for computing estimator ---*/
3365 *   const IndexSet locally_relevant_dofs = DoFTools::extract_locally_relevant_dofs(dof_handler_velocity);
3367 *   tmp_velocity.reinit(dof_handler_velocity.locally_owned_dofs(), locally_relevant_dofs, MPI_COMM_WORLD);
3368 *   tmp_velocity = u_n;
3369 *   tmp_velocity.update_ghost_values();
3370 *  
3371 *   using Iterator = typename DoFHandler<dim>::active_cell_iterator;
3372 *   Vector<float> estimated_error_per_cell(triangulation.n_active_cells());
3373 *  
3374 *   /*--- This is basically the indicator per cell computation (see @ref step_50 "step-50"). Since it is not so complciated
3375 *   we implement it through a lambda expression ---*/
3376 *   const auto cell_worker = [&](const Iterator& cell,
3377 *   ScratchData<dim>& scratch_data,
3378 *   CopyData& copy_data) {
3379 *   FEValues<dim>& fe_values = scratch_data.fe_values; /*--- Here we finally use the 'FEValues' inside ScratchData ---*/
3380 *   fe_values.reinit(cell);
3381 *  
3382 *   /*--- Compute the gradients for all quadrature points ---*/
3383 *   std::vector<std::vector<Tensor<1, dim>>> gradients(fe_values.n_quadrature_points, std::vector<Tensor<1, dim>>(dim));
3384 *   fe_values.get_function_gradients(tmp_velocity, gradients);
3385 *   copy_data.cell_index = cell->active_cell_index();
3386 *   double vorticity_norm_square = 0.0;
3387 *   /*--- Loop over quadrature points and evaluate the integral multiplying the vorticty
3388 *   by the weights and the determinant of the Jacobian (which are included in 'JxW') ---*/
3389 *   for(unsigned k = 0; k < fe_values.n_quadrature_points; ++k) {
3390 *   const double vorticity = gradients[k][1][0] - gradients[k][0][1];
3391 *   vorticity_norm_square += vorticity*vorticity*fe_values.JxW(k);
3392 *   }
3393 *   copy_data.value = cell->diameter()*cell->diameter()*vorticity_norm_square;
3394 *   };
3395 *  
3397 *  
3398 *   const auto copier = [&](const CopyData &copy_data) {
3399 *   if(copy_data.cell_index != numbers::invalid_unsigned_int)
3400 *   estimated_error_per_cell[copy_data.cell_index] += copy_data.value;
3401 *   };
3402 *  
3403 *   /*--- Now everything is 'automagically' handled by 'mesh_loop' ---*/
3404 *   ScratchData<dim> scratch_data(fe_velocity, EquationData::degree_p + 2, cell_flags);
3405 *   CopyData copy_data;
3406 *   MeshWorker::mesh_loop(dof_handler_velocity.begin_active(),
3407 *   dof_handler_velocity.end(),
3408 *   cell_worker,
3409 *   copier,
3410 *   scratch_data,
3411 *   copy_data,
3413 *  
3414 *   /*--- Refine grid. In case the refinement level is above a certain value (or the coarsening level is below)
3415 *   we clear the flags. ---*/
3417 *   for(const auto& cell: triangulation.active_cell_iterators()) {
3418 *   if(cell->refine_flag_set() && static_cast<unsigned int>(cell->level()) == max_loc_refinements)
3419 *   cell->clear_refine_flag();
3420 *   if(cell->coarsen_flag_set() && static_cast<unsigned int>(cell->level()) == min_loc_refinements)
3421 *   cell->clear_coarsen_flag();
3422 *   }
3423 *   triangulation.prepare_coarsening_and_refinement();
3424 *  
3425 *   /*--- Now we prepare the object for transferring, basically saving the old quantities using SolutionTransfer.
3426 *   Since the 'prepare_for_coarsening_and_refinement' method can be called only once, but we have two vectors
3427 *   for dof_handler_velocity, we need to put them in an auxiliary vector. ---*/
3428 *   std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3429 *   velocities.push_back(&u_n);
3430 *   velocities.push_back(&u_n_minus_1);
3432 *   solution_transfer_velocity(dof_handler_velocity);
3433 *   solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3435 *   solution_transfer_pressure(dof_handler_pressure);
3436 *   solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3437 *  
3438 *   triangulation.execute_coarsening_and_refinement(); /*--- Effectively perform the remeshing ---*/
3439 *  
3440 *   /*--- First DoFHandler objects are set up within the new grid ----*/
3441 *   setup_dofs();
3442 *  
3443 *   /*--- Interpolate current solutions to new mesh. This is done using auxliary vectors just for safety,
3444 *   but the new u_n or pres_n could be used. Again, the only point is that the function 'interpolate'
3445 *   can be called once and so the vectors related to 'dof_handler_velocity' have to collected in an auxiliary vector. ---*/
3446 *   LinearAlgebra::distributed::Vector<double> transfer_velocity,
3447 *   transfer_velocity_minus_1,
3448 *   transfer_pressure;
3449 *   transfer_velocity.reinit(u_n);
3450 *   transfer_velocity.zero_out_ghost_values();
3451 *   transfer_velocity_minus_1.reinit(u_n_minus_1);
3452 *   transfer_velocity_minus_1.zero_out_ghost_values();
3453 *   transfer_pressure.reinit(pres_n);
3454 *   transfer_pressure.zero_out_ghost_values();
3455 *  
3456 *   std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3457 *   transfer_velocities.push_back(&transfer_velocity);
3458 *   transfer_velocities.push_back(&transfer_velocity_minus_1);
3459 *   solution_transfer_velocity.interpolate(transfer_velocities);
3460 *   transfer_velocity.update_ghost_values();
3461 *   transfer_velocity_minus_1.update_ghost_values();
3462 *   solution_transfer_pressure.interpolate(transfer_pressure);
3463 *   transfer_pressure.update_ghost_values();
3464 *  
3465 *   u_n = transfer_velocity;
3466 *   u_n_minus_1 = transfer_velocity_minus_1;
3467 *   pres_n = transfer_pressure;
3468 *   }
3469 *  
3470 *  
3471 * @endcode
3472 *
3473 * Interpolate the locally refined solution to a mesh with maximal resolution
3474 * and transfer velocity and pressure.
3475 *
3476
3477 *
3478 *
3479 * @code
3480 *   template<int dim>
3481 *   void NavierStokesProjection<dim>::interpolate_max_res(const unsigned int level) {
3483 *   solution_transfer_velocity(dof_handler_velocity);
3484 *   std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3485 *   velocities.push_back(&u_n);
3486 *   velocities.push_back(&u_n_minus_1);
3487 *   solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3488 *  
3490 *   solution_transfer_pressure(dof_handler_pressure);
3491 *   solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3492 *  
3493 *   for(const auto& cell: triangulation.active_cell_iterators_on_level(level)) {
3494 *   if(cell->is_locally_owned())
3495 *   cell->set_refine_flag();
3496 *   }
3497 *   triangulation.execute_coarsening_and_refinement();
3498 *  
3499 *   setup_dofs();
3500 *  
3501 *   LinearAlgebra::distributed::Vector<double> transfer_velocity, transfer_velocity_minus_1,
3502 *   transfer_pressure;
3503 *  
3504 *   transfer_velocity.reinit(u_n);
3505 *   transfer_velocity.zero_out_ghost_values();
3506 *   transfer_velocity_minus_1.reinit(u_n_minus_1);
3507 *   transfer_velocity_minus_1.zero_out_ghost_values();
3508 *  
3509 *   transfer_pressure.reinit(pres_n);
3510 *   transfer_pressure.zero_out_ghost_values();
3511 *  
3512 *   std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3513 *  
3514 *   transfer_velocities.push_back(&transfer_velocity);
3515 *   transfer_velocities.push_back(&transfer_velocity_minus_1);
3516 *   solution_transfer_velocity.interpolate(transfer_velocities);
3517 *   transfer_velocity.update_ghost_values();
3518 *   transfer_velocity_minus_1.update_ghost_values();
3519 *  
3520 *   solution_transfer_pressure.interpolate(transfer_pressure);
3521 *   transfer_pressure.update_ghost_values();
3522 *  
3523 *   u_n = transfer_velocity;
3524 *   u_n_minus_1 = transfer_velocity_minus_1;
3525 *   pres_n = transfer_pressure;
3526 *   }
3527 *  
3528 *  
3529 * @endcode
3530 *
3531 * Save maximum resolution to a mesh adapted.
3532 *
3533
3534 *
3535 *
3536 * @code
3537 *   template<int dim>
3538 *   void NavierStokesProjection<dim>::save_max_res() {
3539 *   parallel::distributed::Triangulation<dim> triangulation_tmp(MPI_COMM_WORLD);
3540 *   GridGenerator::plate_with_a_hole(triangulation_tmp, 0.5, 1.0, 1.0, 1.1, 1.0, 19.0, Point<2>(2.0, 2.0), 0, 1, 1.0, 2, true);
3541 *   triangulation_tmp.refine_global(triangulation.n_global_levels() - 1);
3542 *  
3543 *   DoFHandler<dim> dof_handler_velocity_tmp(triangulation_tmp);
3544 *   DoFHandler<dim> dof_handler_pressure_tmp(triangulation_tmp);
3545 *   dof_handler_velocity_tmp.distribute_dofs(fe_velocity);
3546 *   dof_handler_pressure_tmp.distribute_dofs(fe_pressure);
3547 *  
3549 *   pres_n_tmp;
3550 *   u_n_tmp.reinit(dof_handler_velocity_tmp.n_dofs());
3551 *   pres_n_tmp.reinit(dof_handler_pressure_tmp.n_dofs());
3552 *  
3553 *   DataOut<dim> data_out;
3554 *   std::vector<std::string> velocity_names(dim, "v");
3555 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
3556 *   component_interpretation_velocity(dim, DataComponentInterpretation::component_is_part_of_vector);
3557 *   VectorTools::interpolate_to_different_mesh(dof_handler_velocity, u_n, dof_handler_velocity_tmp, u_n_tmp);
3558 *   u_n_tmp.update_ghost_values();
3559 *   data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, velocity_names, component_interpretation_velocity);
3560 *   VectorTools::interpolate_to_different_mesh(dof_handler_pressure, pres_n, dof_handler_pressure_tmp, pres_n_tmp);
3561 *   pres_n_tmp.update_ghost_values();
3562 *   data_out.add_data_vector(dof_handler_pressure_tmp, pres_n_tmp, "p", {DataComponentInterpretation::component_is_scalar});
3563 *   PostprocessorVorticity<dim> postprocessor;
3564 *   data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, postprocessor);
3565 *  
3566 *   data_out.build_patches(MappingQ1<dim>(), 1, DataOut<dim>::curved_inner_cells);
3567 *   const std::string output = "./" + saving_dir + "/solution_max_res_end.vtu";
3568 *   data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3569 *   }
3570 *  
3571 *  
3572 * @endcode
3573 *
3574 *
3575 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
3576 * @sect{ <code>NavierStokesProjection::run</code> }
3577 *
3578
3579 *
3580 * This is the time marching function, which starting at <code>t_0</code>
3581 * advances in time using the projection method with time step <code>dt</code>
3582 * until <code>T</code>.
3583 *
3584
3585 *
3586 * Its second parameter, <code>verbose</code> indicates whether the function
3587 * should output information what it is doing at any given moment:
3588 * we use the ConditionalOStream class to do that for us.
3589 *
3590
3591 *
3592 *
3593 * @code
3594 *   template<int dim>
3595 *   void NavierStokesProjection<dim>::run(const bool verbose, const unsigned int output_interval) {
3596 *   ConditionalOStream verbose_cout(std::cout, verbose && Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0);
3597 *  
3598 *   output_results(1);
3599 *   double time = t_0 + dt;
3600 *   unsigned int n = 1;
3601 *   while(std::abs(T - time) > 1e-10) {
3602 *   time += dt;
3603 *   n++;
3604 *   pcout << "Step = " << n << " Time = " << time << std::endl;
3605 *  
3606 *   /*--- First stage of TR-BDF2 and we start by setting the proper flag ---*/
3607 *   TR_BDF2_stage = 1;
3608 *   navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3609 *   for(unsigned int level = 0; level < triangulation.n_global_levels(); ++level)
3610 *   mg_matrices[level].set_TR_BDF2_stage(TR_BDF2_stage);
3611 *  
3612 *   verbose_cout << " Interpolating the velocity stage 1" << std::endl;
3613 *   interpolate_velocity();
3614 *  
3615 *   verbose_cout << " Diffusion Step stage 1 " << std::endl;
3616 *   diffusion_step();
3617 *  
3618 *   verbose_cout << " Projection Step stage 1" << std::endl;
3619 *   project_grad(1);
3620 *   u_tmp.equ(gamma*dt, u_tmp);
3621 *   u_star += u_tmp; /*--- In the rhs of the projection step we need u_star + gamma*dt*grad(pres_n) and we save it into u_star ---*/
3622 *   projection_step();
3623 *  
3624 *   verbose_cout << " Updating the Velocity stage 1" << std::endl;
3625 *   u_n_gamma.equ(1.0, u_star);
3626 *   project_grad(2);
3627 *   grad_pres_int.equ(1.0, u_tmp); /*--- We save grad(pres_int), because we will need it soon ---*/
3628 *   u_tmp.equ(-gamma*dt, u_tmp);
3629 *   u_n_gamma += u_tmp; /*--- u_n_gamma = u_star - gamma*dt*grad(pres_int) ---*/
3630 *   u_n_minus_1 = u_n;
3631 *  
3632 *   /*--- Second stage of TR-BDF2 ---*/
3633 *   TR_BDF2_stage = 2;
3634 *   for(unsigned int level = 0; level < triangulation.n_global_levels(); ++level)
3635 *   mg_matrices[level].set_TR_BDF2_stage(TR_BDF2_stage);
3636 *   navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3637 *  
3638 *   verbose_cout << " Interpolating the velocity stage 2" << std::endl;
3639 *   interpolate_velocity();
3640 *  
3641 *   verbose_cout << " Diffusion Step stage 2 " << std::endl;
3642 *   diffusion_step();
3643 *  
3644 *   verbose_cout << " Projection Step stage 2" << std::endl;
3645 *   u_tmp.equ((1.0 - gamma)*dt, grad_pres_int);
3646 *   u_star += u_tmp; /*--- In the rhs of the projection step we need u_star + (1 - gamma)*dt*grad(pres_int) ---*/
3647 *   projection_step();
3648 *  
3649 *   verbose_cout << " Updating the Velocity stage 2" << std::endl;
3650 *   u_n.equ(1.0, u_star);
3651 *   project_grad(1);
3652 *   u_tmp.equ((gamma - 1.0)*dt, u_tmp);
3653 *   u_n += u_tmp; /*--- u_n = u_star - (1 - gamma)*dt*grad(pres_n) ---*/
3654 *  
3655 *   const double max_vel = get_maximal_velocity();
3656 *   pcout<< "Maximal velocity = " << max_vel << std::endl;
3657 *   /*--- The Courant number is computed taking into account the polynomial degree for the velocity ---*/
3658 *   pcout << "CFL = " << dt*max_vel*(EquationData::degree_p + 1)*
3660 *   compute_lift_and_drag();
3661 *   if(n % output_interval == 0) {
3662 *   verbose_cout << "Plotting Solution final" << std::endl;
3663 *   output_results(n);
3664 *   }
3665 *   /*--- In case dt is not a multiple of T, we reduce dt in order to end up at T ---*/
3666 *   if(T - time < dt && T - time > 1e-10) {
3667 *   dt = T - time;
3668 *   navier_stokes_matrix.set_dt(dt);
3669 *   for(unsigned int level = 0; level < triangulation.n_global_levels(); ++level)
3670 *   mg_matrices[level].set_dt(dt);
3671 *   }
3672 *   /*--- Perform the refinement if desired ---*/
3673 *   if(refinement_iterations > 0 && n % refinement_iterations == 0) {
3674 *   verbose_cout << "Refining mesh" << std::endl;
3675 *   refine_mesh();
3676 *   }
3677 *   }
3678 *   if(n % output_interval != 0) {
3679 *   verbose_cout << "Plotting Solution final" << std::endl;
3680 *   output_results(n);
3681 *   }
3682 *   if(refinement_iterations > 0) {
3683 *   for(unsigned int lev = 0; lev < triangulation.n_global_levels() - 1; ++ lev)
3684 *   interpolate_max_res(lev);
3685 *   save_max_res();
3686 *   }
3687 *   }
3688 *  
3689 *   } // namespace NS_TRBDF2
3690 *  
3691 *  
3692 * @endcode
3693 *
3694 *
3695 * <a name="navier_stokes_TRBDF2_DG.cc-"></a>
3696 * @sect{ The main function }
3697 *
3698
3699 *
3700 * The main function looks very much like in all the other tutorial programs. We first initialize MPI,
3701 * we initialize the class 'NavierStokesProjection' with the dimension as template parameter and then
3702 * let the method 'run' do the job.
3703 *
3704
3705 *
3706 *
3707 * @code
3708 *   int main(int argc, char *argv[]) {
3709 *   try {
3710 *   using namespace NS_TRBDF2;
3711 *  
3712 *   RunTimeParameters::Data_Storage data;
3713 *   data.read_data("parameter-file.prm");
3714 *  
3715 *   Utilities::MPI::MPI_InitFinalize mpi_init(argc, argv, -1);
3716 *  
3717 *   const auto& curr_rank = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
3718 *   deallog.depth_console(data.verbose && curr_rank == 0 ? 2 : 0);
3719 *  
3720 *   NavierStokesProjection<2> test(data);
3721 *   test.run(data.verbose, data.output_interval);
3722 *  
3723 *   if(curr_rank == 0)
3724 *   std::cout << "----------------------------------------------------"
3725 *   << std::endl
3726 *   << "Apparently everything went fine!" << std::endl
3727 *   << "Don't forget to brush your teeth :-)" << std::endl
3728 *   << std::endl;
3729 *  
3730 *   return 0;
3731 *   }
3732 *   catch(std::exception &exc) {
3733 *   std::cerr << std::endl
3734 *   << std::endl
3735 *   << "----------------------------------------------------"
3736 *   << std::endl;
3737 *   std::cerr << "Exception on processing: " << std::endl
3738 *   << exc.what() << std::endl
3739 *   << "Aborting!" << std::endl
3740 *   << "----------------------------------------------------"
3741 *   << std::endl;
3742 *   return 1;
3743 *   }
3744 *   catch(...) {
3745 *   std::cerr << std::endl
3746 *   << std::endl
3747 *   << "----------------------------------------------------"
3748 *   << std::endl;
3749 *   std::cerr << "Unknown exception!" << std::endl
3750 *   << "Aborting!" << std::endl
3751 *   << "----------------------------------------------------"
3752 *   << std::endl;
3753 *   return 1;
3754 *   }
3755 *  
3756 *   }
3757 * @endcode
3758
3759
3760<a name="ann-runtime_parameters.h"></a>
3761<h1>Annotated version of runtime_parameters.h</h1>
3762 *
3763 *
3764 *
3765 *
3766 * @code
3767 *   /* -----------------------------------------------------------------------------
3768 *   *
3769 *   * SPDX-License-Identifier: LGPL-2.1-or-later
3770 *   * Copyright (C) 2022 by Giuseppe Orlando
3771 *   *
3772 *   * This file is part of the deal.II code gallery.
3773 *   *
3774 *   * -----------------------------------------------------------------------------
3775 *   */
3776 *  
3777 * @endcode
3778 *
3779 * We start by including all the necessary deal.II header files
3780 *
3781
3782 *
3783 *
3784 * @code
3785 *   #include <deal.II/base/parameter_handler.h>
3786 *  
3787 * @endcode
3788 *
3789 *
3790 * <a name="runtime_parameters.h-"></a>
3791 * @sect{Run time parameters}
3792 *
3793
3794 *
3795 * Since our method has several parameters that can be fine-tuned we put them
3796 * into an external file, so that they can be determined at run-time.
3797 *
3798
3799 *
3800 *
3801 * @code
3802 *   namespace RunTimeParameters {
3803 *   using namespace dealii;
3804 *  
3805 *   class Data_Storage {
3806 *   public:
3807 *   Data_Storage();
3808 *  
3809 *   void read_data(const std::string& filename);
3810 *  
3811 *   double initial_time;
3812 *   double final_time;
3813 *  
3814 *   double Reynolds;
3815 *   double dt;
3816 *  
3817 *   unsigned int n_refines; /*--- Number of refinements ---*/
3818 *   unsigned int max_loc_refinements; /*--- Number of maximum local refinements allowed ---*/
3819 *   unsigned int min_loc_refinements; /*--- Number of minimum local refinements allowed
3820 *   once reached that level ---*/
3821 *  
3822 *   /*--- Parameters related to the linear solver ---*/
3823 *   unsigned int max_iterations;
3824 *   double eps;
3825 *  
3826 *   bool verbose;
3827 *   unsigned int output_interval;
3828 *  
3829 *   std::string dir; /*--- Auxiliary string variable for output storage ---*/
3830 *  
3831 *   unsigned int refinement_iterations; /*--- Auxiliary variable about how many steps perform remeshing ---*/
3832 *  
3833 *   protected:
3834 *   ParameterHandler prm;
3835 *   };
3836 *  
3837 * @endcode
3838 *
3839 * In the constructor of this class we declare all the parameters in suitable (but arbitrary) subsections.
3840 *
3841
3842 *
3843 *
3844 * @code
3845 *   Data_Storage::Data_Storage(): initial_time(0.0),
3846 *   final_time(1.0),
3847 *   Reynolds(1.0),
3848 *   dt(5e-4),
3849 *   n_refines(0),
3850 *   max_loc_refinements(0),
3851 *   min_loc_refinements(0),
3852 *   max_iterations(1000),
3853 *   eps(1e-12),
3854 *   verbose(true),
3855 *   output_interval(15),
3856 *   refinement_iterations(0) {
3857 *   prm.enter_subsection("Physical data");
3858 *   {
3859 *   prm.declare_entry("initial_time",
3860 *   "0.0",
3861 *   Patterns::Double(0.0),
3862 *   " The initial time of the simulation. ");
3863 *   prm.declare_entry("final_time",
3864 *   "1.0",
3865 *   Patterns::Double(0.0),
3866 *   " The final time of the simulation. ");
3867 *   prm.declare_entry("Reynolds",
3868 *   "1.0",
3869 *   Patterns::Double(0.0),
3870 *   " The Reynolds number. ");
3871 *   }
3872 *   prm.leave_subsection();
3873 *  
3874 *   prm.enter_subsection("Time step data");
3875 *   {
3876 *   prm.declare_entry("dt",
3877 *   "5e-4",
3878 *   Patterns::Double(0.0),
3879 *   " The time step size. ");
3880 *   }
3881 *   prm.leave_subsection();
3882 *  
3883 *   prm.enter_subsection("Space discretization");
3884 *   {
3885 *   prm.declare_entry("n_of_refines",
3886 *   "100",
3887 *   Patterns::Integer(0, 1500),
3888 *   " The number of cells we want on each direction of the mesh. ");
3889 *   prm.declare_entry("max_loc_refinements",
3890 *   "4",
3891 *   Patterns::Integer(0, 10),
3892 *   " The number of maximum local refinements. ");
3893 *   prm.declare_entry("min_loc_refinements",
3894 *   "2",
3895 *   Patterns::Integer(0, 10),
3896 *   " The number of minimum local refinements. ");
3897 *   }
3898 *   prm.leave_subsection();
3899 *  
3900 *   prm.enter_subsection("Data solve");
3901 *   {
3902 *   prm.declare_entry("max_iterations",
3903 *   "1000",
3904 *   Patterns::Integer(1, 30000),
3905 *   " The maximal number of iterations linear solvers must make. ");
3906 *   prm.declare_entry("eps",
3907 *   "1e-12",
3908 *   Patterns::Double(0.0),
3909 *   " The stopping criterion. ");
3910 *   }
3911 *   prm.leave_subsection();
3912 *  
3913 *   prm.declare_entry("refinement_iterations",
3914 *   "0",
3915 *   Patterns::Integer(0),
3916 *   " This number indicates how often we need to "
3917 *   "refine the mesh");
3918 *  
3919 *   prm.declare_entry("saving directory", "SimTest");
3920 *  
3921 *   prm.declare_entry("verbose",
3922 *   "true",
3923 *   Patterns::Bool(),
3924 *   " This indicates whether the output of the solution "
3925 *   "process should be verbose. ");
3926 *  
3927 *   prm.declare_entry("output_interval",
3928 *   "1",
3929 *   Patterns::Integer(1),
3930 *   " This indicates between how many time steps we print "
3931 *   "the solution. ");
3932 *   }
3933 *  
3934 * @endcode
3935 *
3936 * We need now a routine to read all declared parameters in the constructor
3937 *
3938
3939 *
3940 *
3941 * @code
3942 *   void Data_Storage::read_data(const std::string& filename) {
3943 *   std::ifstream file(filename);
3944 *   AssertThrow(file, ExcFileNotOpen(filename));
3945 *  
3946 *   prm.parse_input(file);
3947 *  
3948 *   prm.enter_subsection("Physical data");
3949 *   {
3950 *   initial_time = prm.get_double("initial_time");
3951 *   final_time = prm.get_double("final_time");
3952 *   Reynolds = prm.get_double("Reynolds");
3953 *   }
3954 *   prm.leave_subsection();
3955 *  
3956 *   prm.enter_subsection("Time step data");
3957 *   {
3958 *   dt = prm.get_double("dt");
3959 *   }
3960 *   prm.leave_subsection();
3961 *  
3962 *   prm.enter_subsection("Space discretization");
3963 *   {
3964 *   n_refines = prm.get_integer("n_of_refines");
3965 *   max_loc_refinements = prm.get_integer("max_loc_refinements");
3966 *   min_loc_refinements = prm.get_integer("min_loc_refinements");
3967 *   }
3968 *   prm.leave_subsection();
3969 *  
3970 *   prm.enter_subsection("Data solve");
3971 *   {
3972 *   max_iterations = prm.get_integer("max_iterations");
3973 *   eps = prm.get_double("eps");
3974 *   }
3975 *   prm.leave_subsection();
3976 *  
3977 *   dir = prm.get("saving directory");
3978 *  
3979 *   refinement_iterations = prm.get_integer("refinement_iterations");
3980 *  
3981 *   verbose = prm.get_bool("verbose");
3982 *  
3983 *   output_interval = prm.get_integer("output_interval");
3984 *   }
3985 *  
3986 *   } // namespace RunTimeParameters
3987 * @endcode
3988
3989
3990*/
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
virtual RangeNumberType value(const Point< dim > &p, const unsigned int component=0) const
void reinit(const size_type size, const bool omit_zeroing_entries=false)
unsigned int depth_console(const unsigned int n)
Definition logstream.cc:349
void resize(const unsigned int new_minlevel, const unsigned int new_maxlevel, Args &&...args)
void build(const DoFHandler< dim > &dof_handler, const std::vector< std::shared_ptr< const Utilities::MPI::Partitioner > > &external_partitioners=std::vector< std::shared_ptr< const Utilities::MPI::Partitioner > >())
void clear()
unsigned int mg_level
void loop(const std::function< void(const MatrixFree< dim, Number, VectorizedArrayType > &, OutVector &, const InVector &, const std::pair< unsigned int, unsigned int > &)> &cell_operation, const std::function< void(const MatrixFree< dim, Number, VectorizedArrayType > &, OutVector &, const InVector &, const std::pair< unsigned int, unsigned int > &)> &inner_face_operation, const std::function< void(const MatrixFree< dim, Number, VectorizedArrayType > &, OutVector &, const InVector &, const std::pair< unsigned int, unsigned int > &)> &boundary_face_operation, OutVector &dst, const InVector &src, const bool zero_dst_vector=false, const DataAccessOnFaces dst_vector_face_access=DataAccessOnFaces::unspecified, const DataAccessOnFaces src_vector_face_access=DataAccessOnFaces::unspecified) const
void initialize_dof_vector(VectorType &vec, const unsigned int dof_handler_index=0) const
void cell_loop(const std::function< void(const MatrixFree< dim, Number, VectorizedArrayType > &, OutVector &, const InVector &, const std::pair< unsigned int, unsigned int > &)> &cell_operation, OutVector &dst, const InVector &src, const bool zero_dst_vector=false) const
Definition point.h:111
Point< 2 > second
Definition grid_out.cc:4624
Point< 2 > first
Definition grid_out.cc:4623
unsigned int level
Definition grid_out.cc:4626
unsigned int cell_index
#define Assert(cond, exc)
#define DeclException2(Exception2, type1, type2, outsequence)
Definition exceptions.h:534
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
#define AssertThrow(cond, exc)
typename ActiveSelector::active_cell_iterator active_cell_iterator
void mesh_loop(const CellIteratorType &begin, const CellIteratorType &end, const CellWorkerFunctionType &cell_worker, const CopierType &copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const AssembleFlags flags=assemble_own_cells, const BoundaryWorkerFunctionType &boundary_worker=BoundaryWorkerFunctionType(), const FaceWorkerFunctionType &face_worker=FaceWorkerFunctionType(), const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
Definition mesh_loop.h:281
void loop(IteratorType begin, std_cxx20::type_identity_t< IteratorType > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, AssemblerType &assembler, const LoopControl &lctrl=LoopControl())
Definition loop.h:564
UpdateFlags
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
LogStream deallog
Definition logstream.cc:36
std::vector< index_type > data
Definition mpi.cc:735
std::size_t size
Definition mpi.cc:734
const Event initial
Definition event.cc:64
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
void create_triangulation(Triangulation< dim, dim > &tria, const AdditionalData &additional_data=AdditionalData())
void plate_with_a_hole(Triangulation< dim > &tria, const double inner_radius=0.4, const double outer_radius=1., const double pad_bottom=2., const double pad_top=2., const double pad_left=1., const double pad_right=1., const Point< dim > &center=Point< dim >(), const types::manifold_id polar_manifold_id=0, const types::manifold_id tfi_manifold_id=1, const double L=1., const unsigned int n_slices=2, const bool colorize=false)
Rectangular plate with an (offset) cylindrical hole.
double minimal_cell_diameter(const Triangulation< dim, spacedim > &triangulation, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< dim, spacedim >()))
@ matrix
Contents is actually a matrix.
@ diagonal
Matrix is diagonal.
void compute_diagonal(const MatrixFree< dim, Number, VectorizedArrayType > &matrix_free, VectorType &diagonal_global, const std::function< void(FEEvaluation< dim, fe_degree, n_q_points_1d, n_components, Number, VectorizedArrayType > &)> &cell_operation, const unsigned int dof_no=0, const unsigned int quad_no=0, const unsigned int first_selected_component=0, const unsigned int first_vector_component=0)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:191
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:92
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:107
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:470
void interpolate(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Function< spacedim, typename VectorType::value_type > &function, VectorType &vec, const ComponentMask &component_mask={}, const unsigned int level=numbers::invalid_unsigned_int)
void project(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const AffineConstraints< typename VectorType::value_type > &constraints, const Quadrature< dim > &quadrature, const Function< spacedim, typename VectorType::value_type > &function, VectorType &vec, const bool enforce_zero_boundary=false, const Quadrature< dim - 1 > &q_boundary=(dim > 1 ? QGauss< dim - 1 >(2) :Quadrature< dim - 1 >()), const bool project_to_boundary_first=false)
void interpolate_to_different_mesh(const DoFHandler< dim, spacedim > &dof_handler_1, const VectorType &u1, const DoFHandler< dim, spacedim > &dof_handler_2, VectorType &u2)
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
long double gamma(const unsigned int n)
int(&) functions(const void *v1, const void *v2)
void assemble(const MeshWorker::DoFInfoBox< dim, DOFINFO > &dinfo, A *assembler)
Definition loop.h:70
Definition mg.h:81
static const unsigned int invalid_unsigned_int
Definition types.h:220
void refine_and_coarsen_fixed_number(::Triangulation< dim, spacedim > &tria, const ::Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const types::global_cell_index max_n_cells=std::numeric_limits< types::global_cell_index >::max())
::SolutionTransfer< dim, VectorType, spacedim > SolutionTransfer
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int boundary_id
Definition types.h:144
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
std::vector<::Vector< double > > solution_values
std::vector< std::vector< Tensor< 1, spacedim > > > solution_gradients
TasksParallelScheme tasks_parallel_scheme
DEAL_II_HOST constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)
std_cxx20::type_identity< T > identity