168 * We start by including all the necessary deal.II header files.
174 * #include <deal.II/base/
point.h>
175 * #include <deal.II/base/function.h>
180 * <a name=
"equation_data.h-"></a>
181 * @sect{Equation data}
185 * In the next
namespace, we declare and implement suitable
functions that may be used
for the
initial and boundary conditions
191 *
namespace EquationData {
194 *
static const unsigned int degree_p = 1;
200 * We declare
class that describes the boundary conditions and
initial one for velocity:
207 * class Velocity:
public Function<dim> {
209 * Velocity(
const double initial_time = 0.0);
212 *
const unsigned int component = 0)
const override;
214 *
virtual void vector_value(
const Point<dim>& p,
220 * Velocity<dim>::Velocity(
const double initial_time):
Function<dim>(dim, initial_time) {}
224 *
double Velocity<dim>::value(
const Point<dim>& p,
const unsigned int component)
const {
226 *
if(component == 0) {
227 *
const double Um = 1.5;
228 *
const double H = 4.1;
230 *
return 4.0*Um*p(1)*(H - p(1))/(H*H);
239 *
Assert(values.size() == dim, ExcDimensionMismatch(values.size(), dim));
241 *
for(
unsigned int i = 0; i < dim; ++i)
242 * values[i] =
value(p, i);
248 * We
do the same
for the pressure
255 *
class Pressure:
public Function<dim> {
257 * Pressure(
const double initial_time = 0.0);
260 *
const unsigned int component = 0)
const override;
265 * Pressure<dim>::Pressure(
const double initial_time):
Function<dim>(1, initial_time) {}
269 *
double Pressure<dim>::value(
const Point<dim>& p,
const unsigned int component)
const {
273 *
return 22.0 - p(0);
280<a name=
"ann-navier_stokes_TRBDF2_DG.cc"></a>
281<h1>Annotated version of navier_stokes_TRBDF2_DG.cc</h1>
301 * We start by including all the necessary deal.II header files and some
C++
308 * #include <deal.II/base/quadrature_lib.h>
309 * #include <deal.II/base/multithread_info.h>
310 * #include <deal.II/base/thread_management.h>
311 * #include <deal.II/base/work_stream.h>
312 * #include <deal.II/base/
parallel.h>
313 * #include <deal.II/base/utilities.h>
314 * #include <deal.II/base/conditional_ostream.h>
316 * #include <deal.II/lac/vector.h>
317 * #include <deal.II/lac/solver_cg.h>
318 * #include <deal.II/lac/precondition.h>
319 * #include <deal.II/lac/solver_gmres.h>
320 * #include <deal.II/lac/affine_constraints.h>
322 * #include <deal.II/grid/tria.h>
323 * #include <deal.II/grid/grid_generator.h>
324 * #include <deal.II/grid/grid_tools.h>
325 * #include <deal.II/grid/grid_refinement.h>
326 * #include <deal.II/grid/tria_accessor.h>
327 * #include <deal.II/grid/tria_iterator.h>
328 * #include <deal.II/distributed/grid_refinement.h>
330 * #include <deal.II/dofs/dof_handler.h>
331 * #include <deal.II/dofs/dof_accessor.h>
332 * #include <deal.II/dofs/dof_tools.h>
334 * #include <deal.II/fe/fe_q.h>
335 * #include <deal.II/fe/fe_dgq.h>
336 * #include <deal.II/fe/fe_values.h>
337 * #include <deal.II/fe/fe_tools.h>
338 * #include <deal.II/fe/fe_system.h>
340 * #include <deal.II/numerics/matrix_tools.h>
341 * #include <deal.II/numerics/vector_tools.h>
342 * #include <deal.II/numerics/data_out.h>
346 * #include <iostream>
348 * #include <deal.II/matrix_free/matrix_free.h>
349 * #include <deal.II/matrix_free/operators.h>
350 * #include <deal.II/matrix_free/fe_evaluation.h>
351 * #include <deal.II/fe/component_mask.h>
353 * #include <deal.II/base/timer.h>
354 * #include <deal.II/distributed/solution_transfer.h>
355 * #include <deal.II/numerics/error_estimator.h>
357 * #include <deal.II/multigrid/multigrid.h>
358 * #include <deal.II/multigrid/mg_transfer_matrix_free.h>
359 * #include <deal.II/multigrid/mg_tools.h>
360 * #include <deal.II/multigrid/mg_coarse.h>
361 * #include <deal.II/multigrid/mg_smoother.h>
362 * #include <deal.II/multigrid/mg_matrix.h>
364 * #include <deal.II/meshworker/
mesh_loop.h>
366 * #include
"runtime_parameters.h"
367 * #include
"equation_data.h"
372 *
template<
int dim,
typename Number,
typename VectorizedArrayType>
377 *
const unsigned int&,
378 *
const std::pair<unsigned int, unsigned int>&)>& cell_operation,
381 *
const unsigned int&,
382 *
const std::pair<unsigned int, unsigned int>&)>& face_operation,
385 *
const unsigned int&,
386 *
const std::pair<unsigned int, unsigned int>&)>& boundary_operation,
387 *
const unsigned int dof_no = 0) {
395 *
const unsigned int dummy = 0;
397 * matrix_free.
loop(cell_operation, face_operation, boundary_operation,
398 * diagonal_global, dummy,
false,
406 * We include the code in a suitable
namespace:
412 *
namespace NS_TRBDF2 {
417 * The following
class is an auxiliary one for post-processing of the vorticity
427 * std::vector<
Vector<double>>& computed_quantities)
const override;
429 *
virtual std::vector<std::string> get_names()
const override;
431 *
virtual std::vector<DataComponentInterpretation::DataComponentInterpretation>
432 * get_data_component_interpretation()
const override;
434 *
virtual UpdateFlags get_needed_update_flags()
const override;
439 * This function evaluates the vorticty in both 2D and 3D cases
448 *
const unsigned int n_quadrature_points = inputs.
solution_values.size();
452 *
Assert(computed_quantities.size() == n_quadrature_points, ExcInternalError());
457 *
Assert(computed_quantities[0].size() == 1, ExcInternalError());
460 *
Assert(computed_quantities[0].size() == dim, ExcInternalError());
465 *
for(
unsigned int q = 0; q < n_quadrature_points; ++q)
469 *
for(
unsigned int q = 0; q < n_quadrature_points; ++q) {
479 * This auxiliary function is required by the base
class DataProcessor and simply
480 * sets the name
for the output file
487 * std::vector<std::string> PostprocessorVorticity<dim>::get_names()
const {
488 * std::vector<std::string> names;
489 * names.emplace_back(
"vorticity");
491 * names.emplace_back(
"vorticity");
492 * names.emplace_back(
"vorticity");
500 * This auxiliary function is required by the base
class DataProcessor and simply
501 * specifies
if the vorticity is a
scalar (2D) or a vector (3D)
508 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
509 * PostprocessorVorticity<dim>::get_data_component_interpretation()
const {
510 * std::vector<DataComponentInterpretation::DataComponentInterpretation> interpretation;
519 *
return interpretation;
524 * This auxiliary function is required by the base
class DataProcessor and simply
525 * sets which variables have to updated (only the gradients)
532 *
UpdateFlags PostprocessorVorticity<dim>::get_needed_update_flags()
const {
539 * The following structs are auxiliary objects
for mesh refinement. ScratchData simply sets
547 *
struct ScratchData {
549 *
const unsigned int quadrature_degree,
550 *
const UpdateFlags update_flags): fe_values(fe,
QGauss<dim>(quadrature_degree), update_flags) {}
552 * ScratchData(
const ScratchData<dim>& scratch_data): fe_values(scratch_data.fe_values.get_fe(),
553 * scratch_data.fe_values.get_quadrature(),
554 * scratch_data.fe_values.get_update_flags()) {}
561 * CopyData simply sets the cell
index
570 * CopyData(
const CopyData &) =
default;
580 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
581 * @sect{ <code>NavierStokesProjectionOperator::NavierStokesProjectionOperator</code> }
585 * The following
class sets effectively the weak formulation of the problems for the different stages
586 * and
for both velocity and pressure.
587 * The
template parameters are the dimnesion of the problem, the polynomial degree
for the pressure,
588 * the polynomial degree
for the velocity, the number of quadrature points
for integrals
for the pressure step,
589 * the number of quadrature points
for integrals
for the velocity step, the type of vector
for storage and the type
590 * of floating
point data (in general
double or
float for preconditioners structures
if desired).
596 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
599 *
using Number =
typename Vec::value_type;
601 * NavierStokesProjectionOperator();
603 * NavierStokesProjectionOperator(RunTimeParameters::Data_Storage& data);
605 *
void set_dt(
const double time_step);
607 *
void set_TR_BDF2_stage(
const unsigned int stage);
609 *
void set_NS_stage(
const unsigned int stage);
611 *
void set_u_extr(
const Vec& src);
613 *
void vmult_rhs_velocity(Vec& dst,
const std::vector<Vec>& src)
const;
615 *
void vmult_rhs_pressure(Vec& dst,
const std::vector<Vec>& src)
const;
617 *
void vmult_grad_p_projection(Vec& dst,
const Vec& src)
const;
631 *
unsigned int TR_BDF2_stage;
632 *
unsigned int NS_stage;
635 *
virtual void apply_add(Vec& dst,
const Vec& src)
const override;
640 *
const double a21 = 0.5;
641 *
const double a22 = 0.5;
644 *
const double theta_v = 1.0;
645 *
const double theta_p = 1.0;
646 *
const double C_p = 1.0*(fe_degree_p + 1)*(fe_degree_p + 1);
647 *
const double C_u = 1.0*(fe_degree_v + 1)*(fe_degree_v + 1);
651 * EquationData::Velocity<dim> vel_boundary_inflow;
657 *
const std::vector<Vec>& src,
658 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
661 *
const std::vector<Vec>& src,
662 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
665 *
const std::vector<Vec>& src,
666 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
670 *
const std::vector<Vec>& src,
671 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
674 *
const std::vector<Vec>& src,
675 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
678 *
const std::vector<Vec>& src,
679 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
684 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
688 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
692 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
697 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
701 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
705 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
710 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
714 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
718 *
const unsigned int& src,
719 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
722 *
const unsigned int& src,
723 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
726 *
const unsigned int& src,
727 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
731 *
const unsigned int& src,
732 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
735 *
const unsigned int& src,
736 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
739 *
const unsigned int& src,
740 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
746 * We start with the
default constructor. It is important
for MultiGrid, so it is fundamental
747 * to properly
set the parameters of the time scheme.
753 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
754 * NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
755 * NavierStokesProjectionOperator():
757 * a32(a31), a33(1.0/(2.0 -
gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr() {}
762 * We focus now on the constructor with runtime parameters storage
768 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
769 * NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
770 * NavierStokesProjectionOperator(RunTimeParameters::Data_Storage& data):
773 * a32(a31), a33(1.0/(2.0 -
gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr(),
774 * vel_boundary_inflow(data.initial_time) {}
779 * Setter of time-step (called by
Multigrid and in
case a smaller time-step towards the end is needed)
785 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
786 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
787 * set_dt(
const double time_step) {
794 * Setter of TR-BDF2 stage (
this can be known only during the effective execution
795 * and so it has to be demanded to the
class that really solves the problem)
801 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
802 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
803 * set_TR_BDF2_stage(
const unsigned int stage) {
805 *
Assert(stage > 0, ExcInternalError());
807 * TR_BDF2_stage = stage;
813 * Setter of NS stage (
this can be known only during the effective execution
814 * and so it has to be demanded to the
class that really solves the problem)
820 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
821 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
822 * set_NS_stage(
const unsigned int stage) {
824 *
Assert(stage > 0, ExcInternalError());
832 * Setter of extrapolated velocity
for different stages
838 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
839 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
840 * set_u_extr(
const Vec& src) {
842 * u_extr.update_ghost_values();
848 * We are in a DG-
MatrixFree framework, so it is convenient to compute separately cell contribution,
849 *
internal faces contributions and boundary faces contributions. We start by
850 * assembling the rhs cell term
for the velocity.
856 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
857 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
860 *
const std::vector<Vec>& src,
861 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
862 *
if(TR_BDF2_stage == 1) {
869 * phi_old_extr(data, 0);
873 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
877 * phi_old.reinit(cell);
882 * phi_old_extr.reinit(cell);
884 * phi_old_press.reinit(cell);
889 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
890 *
const auto& u_n = phi_old.get_value(q);
891 *
const auto& grad_u_n = phi_old.get_gradient(q);
892 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
893 *
const auto& tensor_product_u_n =
outer_product(u_n, u_n_gamma_ov_2);
894 *
const auto& p_n = phi_old_press.get_value(q);
895 *
auto p_n_times_identity = tensor_product_u_n;
896 * p_n_times_identity = 0;
897 *
for(
unsigned int d = 0;
d < dim; ++
d)
898 * p_n_times_identity[d][d] = p_n;
900 * phi.submit_value(1.0/(gamma*dt)*u_n, q);
902 * phi.submit_gradient(-a21/Re*grad_u_n + a21*tensor_product_u_n + p_n_times_identity, q);
918 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
919 * phi_old.reinit(cell);
921 * phi_int.reinit(cell);
923 * phi_old_press.reinit(cell);
928 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
929 *
const auto& u_n = phi_old.get_value(q);
930 *
const auto& grad_u_n = phi_old.get_gradient(q);
931 *
const auto& u_n_gamma = phi_int.get_value(q);
932 *
const auto& grad_u_n_gamma = phi_int.get_gradient(q);
934 *
const auto& tensor_product_u_n_gamma =
outer_product(u_n_gamma, u_n_gamma);
935 *
const auto& p_n = phi_old_press.get_value(q);
936 *
auto p_n_times_identity = tensor_product_u_n;
937 * p_n_times_identity = 0;
938 *
for(
unsigned int d = 0;
d < dim; ++
d)
939 * p_n_times_identity[d][d] = p_n;
941 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_n_gamma, q);
942 * phi.submit_gradient(a32*tensor_product_u_n_gamma + a31*tensor_product_u_n -
943 * a32/Re*grad_u_n_gamma - a31/Re*grad_u_n + p_n_times_identity, q);
953 * The followinf function assembles rhs face term
for the velocity
959 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
960 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
963 *
const std::vector<Vec>& src,
964 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
965 *
if(TR_BDF2_stage == 1) {
970 * phi_m(data,
false, 0),
971 * phi_old_p(data,
true, 0),
972 * phi_old_m(data,
false, 0),
973 * phi_old_extr_p(data,
true, 0),
974 * phi_old_extr_m(data,
false, 0);
976 * phi_old_press_m(data,
false, 1);
979 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
980 * phi_old_p.reinit(face);
982 * phi_old_m.reinit(face);
984 * phi_old_extr_p.reinit(face);
986 * phi_old_extr_m.reinit(face);
988 * phi_old_press_p.reinit(face);
990 * phi_old_press_m.reinit(face);
992 * phi_p.reinit(face);
993 * phi_m.reinit(face);
996 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
997 *
const auto& n_plus = phi_p.get_normal_vector(q);
1001 *
const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1002 *
const auto& avg_tensor_product_u_n = 0.5*(
outer_product(phi_old_p.get_value(q), phi_old_extr_p.get_value(q)) +
1003 *
outer_product(phi_old_m.get_value(q), phi_old_extr_m.get_value(q)));
1004 *
const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1006 * phi_p.submit_value((a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus - avg_p_old*n_plus, q);
1007 * phi_m.submit_value(-(a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus + avg_p_old*n_plus, q);
1016 * phi_m(data,
false, 0),
1017 * phi_old_p(data,
true, 0),
1018 * phi_old_m(data,
false, 0),
1019 * phi_int_p(data,
true, 0),
1020 * phi_int_m(data,
false, 0);
1022 * phi_old_press_m(data,
false, 1);
1025 *
for(
unsigned int face = face_range.first; face < face_range.second; ++ face) {
1026 * phi_old_p.reinit(face);
1028 * phi_old_m.reinit(face);
1030 * phi_int_p.reinit(face);
1032 * phi_int_m.reinit(face);
1034 * phi_old_press_p.reinit(face);
1036 * phi_old_press_m.reinit(face);
1038 * phi_p.reinit(face);
1039 * phi_m.reinit(face);
1042 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1043 *
const auto& n_plus = phi_p.get_normal_vector(q);
1045 *
const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1046 *
const auto& avg_grad_u_int = 0.5*(phi_int_p.get_gradient(q) + phi_int_m.get_gradient(q));
1047 *
const auto& avg_tensor_product_u_n = 0.5*(
outer_product(phi_old_p.get_value(q), phi_old_p.get_value(q)) +
1048 *
outer_product(phi_old_m.get_value(q), phi_old_m.get_value(q)));
1049 *
const auto& avg_tensor_product_u_n_gamma = 0.5*(
outer_product(phi_int_p.get_value(q), phi_int_p.get_value(q)) +
1050 *
outer_product(phi_int_m.get_value(q), phi_int_m.get_value(q)));
1051 *
const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1053 * phi_p.submit_value((a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1054 * a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus - avg_p_old*n_plus, q);
1055 * phi_m.submit_value(-(a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1056 * a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus + avg_p_old*n_plus, q);
1067 * The followinf function assembles rhs boundary term
for the velocity
1073 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1074 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1077 *
const std::vector<Vec>& src,
1078 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1079 *
if(TR_BDF2_stage == 1) {
1083 * phi_old(data,
true, 0),
1084 * phi_old_extr(data,
true, 0);
1088 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1089 * phi_old.reinit(face);
1091 * phi_old_extr.reinit(face);
1093 * phi_old_press.reinit(face);
1098 *
const auto coef_jump = (
boundary_id == 1) ? 0.0 : C_u*
std::
abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1099 *
const double aux_coeff = (
boundary_id == 1) ? 0.0 : 1.0;
1102 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1103 *
const auto& n_plus = phi.get_normal_vector(q);
1105 *
const auto& grad_u_old = phi_old.get_gradient(q);
1106 *
const auto& tensor_product_u_n =
outer_product(phi_old.get_value(q), phi_old_extr.get_value(q));
1107 *
const auto& p_old = phi_old_press.get_value(q);
1108 *
const auto& point_vectorized = phi.quadrature_point(q);
1110 *
if(boundary_id == 0) {
1111 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1115 *
for(
unsigned int d = 0;
d < dim; ++
d)
1116 * point[d] = point_vectorized[d][v];
1117 *
for(
unsigned int d = 0;
d < dim; ++
d)
1118 * u_int_m[d][v] = vel_boundary_inflow.value(point, d);
1121 *
const auto tensor_product_u_int_m =
outer_product(u_int_m, phi_old_extr.get_value(q));
1124 * phi.submit_value((a21/Re*grad_u_old - a21*tensor_product_u_n)*n_plus - p_old*n_plus +
1125 * a22/Re*2.0*coef_jump*u_int_m -
1126 * aux_coeff*a22*tensor_product_u_int_m*n_plus + a22*lambda*u_int_m, q);
1127 * phi.submit_normal_derivative(-aux_coeff*theta_v*a22/Re*u_int_m, q);
1136 * phi_old(data,
true, 0),
1137 * phi_int(data,
true, 0),
1138 * phi_int_extr(data,
true, 0);
1142 *
for(
unsigned int face = face_range.first; face < face_range.second; ++ face) {
1143 * phi_old.reinit(face);
1145 * phi_int.reinit(face);
1147 * phi_old_press.reinit(face);
1149 * phi_int_extr.reinit(face);
1154 *
const auto coef_jump = (
boundary_id == 1) ? 0.0 : C_u*
std::
abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1155 *
const double aux_coeff = (
boundary_id == 1) ? 0.0 : 1.0;
1158 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1159 *
const auto& n_plus = phi.get_normal_vector(q);
1161 *
const auto& grad_u_old = phi_old.get_gradient(q);
1162 *
const auto& grad_u_int = phi_int.get_gradient(q);
1163 *
const auto& tensor_product_u_n =
outer_product(phi_old.get_value(q), phi_old.get_value(q));
1164 *
const auto& tensor_product_u_n_gamma =
outer_product(phi_int.get_value(q), phi_int.get_value(q));
1165 *
const auto& p_old = phi_old_press.get_value(q);
1166 *
const auto& point_vectorized = phi.quadrature_point(q);
1168 *
if(boundary_id == 0) {
1169 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1171 *
for(
unsigned int d = 0;
d < dim; ++
d)
1172 * point[d] = point_vectorized[d][v];
1173 *
for(
unsigned int d = 0;
d < dim; ++
d)
1174 * u_m[d][v] = vel_boundary_inflow.value(point, d);
1177 *
const auto tensor_product_u_m =
outer_product(u_m, phi_int_extr.get_value(q));
1180 * phi.submit_value((a31/Re*grad_u_old + a32/Re*grad_u_int -
1181 * a31*tensor_product_u_n - a32*tensor_product_u_n_gamma)*n_plus - p_old*n_plus +
1182 * a33/Re*2.0*coef_jump*u_m -
1183 * aux_coeff*a33*tensor_product_u_m*n_plus + a33*lambda*u_m, q);
1184 * phi.submit_normal_derivative(-aux_coeff*theta_v*a33/Re*u_m, q);
1194 * Put together all the previous steps
for velocity. This is done automatically by the
loop function of
'MatrixFree' class
1200 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1201 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1202 * vmult_rhs_velocity(Vec& dst,
const std::vector<Vec>& src)
const {
1203 *
for(
auto& vec : src)
1204 * vec.update_ghost_values();
1206 * this->data->
loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_velocity,
1207 * &NavierStokesProjectionOperator::assemble_rhs_face_term_velocity,
1208 * &NavierStokesProjectionOperator::assemble_rhs_boundary_term_velocity,
1209 *
this, dst, src,
true,
1217 * Now we focus on computing the rhs
for the projection step
for the pressure with the same ratio.
1218 * The following function assembles rhs cell term
for the pressure
1224 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1225 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1228 *
const std::vector<Vec>& src,
1229 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1233 * phi_old(data, 1, 1);
1236 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
1238 *
const double coeff_2 = (TR_BDF2_stage == 1) ? gamma*dt : (1.0 -
gamma)*dt;
1241 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1242 * phi_proj.reinit(cell);
1244 * phi_old.reinit(cell);
1249 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1250 *
const auto& u_star_star = phi_proj.get_value(q);
1251 *
const auto& p_old = phi_old.get_value(q);
1253 * phi.submit_value(1.0/coeff*p_old, q);
1254 * phi.submit_gradient(1.0/coeff_2*u_star_star, q);
1263 * The following function assembles rhs face term
for the pressure
1269 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1270 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1273 *
const std::vector<Vec>& src,
1274 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1277 * phi_m(data,
false, 1, 1);
1279 * phi_proj_m(data,
false, 0, 1);
1281 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 -
gamma)*dt);
1284 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1285 * phi_proj_p.reinit(face);
1287 * phi_proj_m.reinit(face);
1289 * phi_p.reinit(face);
1290 * phi_m.reinit(face);
1293 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1294 *
const auto& n_plus = phi_p.get_normal_vector(q);
1295 *
const auto& avg_u_star_star = 0.5*(phi_proj_p.get_value(q) + phi_proj_m.get_value(q));
1297 * phi_p.submit_value(-coeff*scalar_product(avg_u_star_star, n_plus), q);
1298 * phi_m.submit_value(coeff*scalar_product(avg_u_star_star, n_plus), q);
1308 * The following function assembles rhs boundary term
for the pressure
1314 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1315 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1318 *
const std::vector<Vec>& src,
1319 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1324 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 -
gamma)*dt);
1327 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1328 * phi_proj.reinit(face);
1333 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1334 *
const auto& n_plus = phi.get_normal_vector(q);
1336 * phi.submit_value(-coeff*scalar_product(phi_proj.get_value(q), n_plus), q);
1345 * Put together all the previous steps
for pressure
1351 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1352 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1353 * vmult_rhs_pressure(Vec& dst,
const std::vector<Vec>& src)
const {
1354 *
for(
auto& vec : src)
1355 * vec.update_ghost_values();
1357 * this->data->
loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_pressure,
1358 * &NavierStokesProjectionOperator::assemble_rhs_face_term_pressure,
1359 * &NavierStokesProjectionOperator::assemble_rhs_boundary_term_pressure,
1360 *
this, dst, src,
true,
1368 * Now we need to build the
'matrices', i.e. the bilinear forms. We start by
1369 * assembling the cell term
for the velocity
1375 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1376 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1380 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1381 *
if(TR_BDF2_stage == 1) {
1385 * phi_old_extr(data, 0);
1388 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1391 * phi_old_extr.reinit(cell);
1395 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1396 *
const auto& u_int = phi.get_value(q);
1397 *
const auto& grad_u_int = phi.get_gradient(q);
1398 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1399 *
const auto& tensor_product_u_int =
outer_product(u_int, u_n_gamma_ov_2);
1401 * phi.submit_value(1.0/(gamma*dt)*u_int, q);
1402 * phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1410 * phi_int_extr(data, 0);
1413 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1416 * phi_int_extr.reinit(cell);
1420 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1421 *
const auto& u_curr = phi.get_value(q);
1422 *
const auto& grad_u_curr = phi.get_gradient(q);
1423 *
const auto& u_n1_int = phi_int_extr.get_value(q);
1424 *
const auto& tensor_product_u_curr =
outer_product(u_curr, u_n1_int);
1426 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
1427 * phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
1437 * The following function assembles face term
for the velocity
1443 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1444 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1448 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1449 *
if(TR_BDF2_stage == 1) {
1452 * phi_m(data,
false, 0),
1453 * phi_old_extr_p(data,
true, 0),
1454 * phi_old_extr_m(data,
false, 0);
1457 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1458 * phi_p.reinit(face);
1460 * phi_m.reinit(face);
1462 * phi_old_extr_p.reinit(face);
1464 * phi_old_extr_m.reinit(face);
1467 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1468 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1471 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1472 *
const auto& n_plus = phi_p.get_normal_vector(q);
1474 *
const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1475 *
const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
1476 *
const auto& avg_tensor_product_u_int = 0.5*(
outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
1477 *
outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
1479 *
std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
1481 * phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
1482 * a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int, q);
1483 * phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
1484 * a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
1485 * phi_p.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1486 * phi_m.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1495 * phi_m(data,
false, 0),
1496 * phi_extr_p(data,
true, 0),
1497 * phi_extr_m(data,
false, 0);
1500 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1501 * phi_p.reinit(face);
1503 * phi_m.reinit(face);
1505 * phi_extr_p.reinit(face);
1507 * phi_extr_m.reinit(face);
1510 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1511 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1514 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1515 *
const auto& n_plus = phi_p.get_normal_vector(q);
1517 *
const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1518 *
const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
1519 *
const auto& avg_tensor_product_u = 0.5*(
outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
1520 *
outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
1522 *
std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
1524 * phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
1525 * a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
1526 * phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
1527 * a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
1528 * phi_p.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1529 * phi_m.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1540 * The following function assembles boundary term
for the velocity
1546 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1547 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1551 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1552 *
if(TR_BDF2_stage == 1) {
1555 * phi_old_extr(data,
true, 0);
1558 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1561 * phi_old_extr.reinit(face);
1565 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1569 *
if(boundary_id != 1) {
1570 *
const double coef_trasp = 0.0;
1573 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1574 *
const auto& n_plus = phi.get_normal_vector(q);
1575 *
const auto& grad_u_int = phi.get_gradient(q);
1576 *
const auto& u_int = phi.get_value(q);
1577 *
const auto& tensor_product_u_int =
outer_product(phi.get_value(q), phi_old_extr.get_value(q));
1578 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1580 * phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
1581 * a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
1582 * phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
1588 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1589 *
const auto& n_plus = phi.get_normal_vector(q);
1590 *
const auto& grad_u_int = phi.get_gradient(q);
1591 *
const auto& u_int = phi.get_value(q);
1592 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1594 *
const auto& point_vectorized = phi.quadrature_point(q);
1595 *
auto u_int_m = u_int;
1596 *
auto grad_u_int_m = grad_u_int;
1597 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1599 *
for(
unsigned int d = 0;
d < dim; ++
d)
1600 * point[d] = point_vectorized[d][v];
1602 * u_int_m[1][v] = -u_int_m[1][v];
1604 * grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
1605 * grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
1608 * phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
1609 * a22*
outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
1610 * a22*0.5*
lambda*(u_int - u_int_m), q);
1611 * phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
1620 * phi_extr(data,
true, 0);
1623 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1626 * phi_extr.reinit(face);
1630 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1632 *
if(boundary_id != 1) {
1633 *
const double coef_trasp = 0.0;
1636 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1637 *
const auto& n_plus = phi.get_normal_vector(q);
1638 *
const auto& grad_u = phi.get_gradient(q);
1639 *
const auto& u = phi.get_value(q);
1640 *
const auto& tensor_product_u =
outer_product(phi.get_value(q), phi_extr.get_value(q));
1641 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1643 * phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
1644 * a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
1645 * phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
1651 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1652 *
const auto& n_plus = phi.get_normal_vector(q);
1653 *
const auto& grad_u = phi.get_gradient(q);
1654 *
const auto& u = phi.get_value(q);
1655 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1657 *
const auto& point_vectorized = phi.quadrature_point(q);
1659 *
auto grad_u_m = grad_u;
1660 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1662 *
for(
unsigned int d = 0;
d < dim; ++
d)
1663 * point[d] = point_vectorized[d][v];
1665 * u_m[1][v] = -u_m[1][v];
1667 * grad_u_m[0][0][v] = -grad_u_m[0][0][v];
1668 * grad_u_m[0][1][v] = -grad_u_m[0][1][v];
1671 * phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
1672 * a33*
outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus + a33*0.5*
lambda*(u - u_m), q);
1673 * phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
1684 * Next, we focus on
'matrices' to compute the pressure. We
first assemble cell term
for the pressure
1690 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1691 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1695 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1699 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
1702 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1707 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1708 * phi.submit_gradient(phi.get_gradient(q), q);
1709 * phi.submit_value(1.0/coeff*phi.get_value(q), q);
1719 * The following function assembles face term
for the pressure
1725 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1726 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1730 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1733 * phi_m(data,
false, 1, 1);
1736 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1737 * phi_p.reinit(face);
1739 * phi_m.reinit(face);
1742 *
const auto coef_jump = C_p*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1743 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1746 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1747 *
const auto& n_plus = phi_p.get_normal_vector(q);
1749 *
const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1750 *
const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
1752 * phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
1753 * phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
1754 * phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1755 * phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1765 * The following function assembles boundary term
for the pressure
1771 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1772 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1776 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1779 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1782 *
if(boundary_id == 1) {
1786 *
const auto coef_jump = C_p*
std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
1788 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1789 *
const auto& n_plus = phi.get_normal_vector(q);
1791 *
const auto& grad_pres = phi.get_gradient(q);
1792 *
const auto& pres = phi.get_value(q);
1794 * phi.submit_value(-scalar_product(grad_pres, n_plus) + coef_jump*pres , q);
1795 * phi.submit_normal_derivative(-theta_p*pres, q);
1805 * Before coding the
'apply_add' function, which is the one that will perform the
loop, we focus on
1806 * the linear system that arises to
project the
gradient of the pressure into the velocity space.
1807 * The following function assembles rhs cell term
for the projection of
gradient of pressure. Since no
1808 * integration by parts is performed, only a cell term contribution is present.
1814 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1815 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1819 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1825 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1826 * phi_pres.reinit(cell);
1831 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q)
1832 * phi.submit_value(phi_pres.get_gradient(q), q);
1841 * Put together all the previous steps
for projection of pressure
gradient. Here we
loop only over cells
1847 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1848 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1849 * vmult_grad_p_projection(Vec& dst,
const Vec& src)
const {
1850 * this->data->
cell_loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_projection_grad_p,
1851 *
this, dst, src,
true);
1857 * Assemble now cell term
for the projection of
gradient of pressure. This is
nothing but a mass
matrix
1863 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1864 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1868 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1872 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1877 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q)
1878 * phi.submit_value(phi.get_value(q), q);
1887 * Put together all previous steps. This is the overridden function that effectively performs the
1888 *
matrix-vector multiplication.
1894 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1895 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1896 * apply_add(Vec& dst,
const Vec& src)
const {
1897 *
if(NS_stage == 1) {
1898 * this->data->
loop(&NavierStokesProjectionOperator::assemble_cell_term_velocity,
1899 * &NavierStokesProjectionOperator::assemble_face_term_velocity,
1900 * &NavierStokesProjectionOperator::assemble_boundary_term_velocity,
1901 *
this, dst, src,
false,
1905 *
else if(NS_stage == 2) {
1906 * this->data->
loop(&NavierStokesProjectionOperator::assemble_cell_term_pressure,
1907 * &NavierStokesProjectionOperator::assemble_face_term_pressure,
1908 * &NavierStokesProjectionOperator::assemble_boundary_term_pressure,
1909 *
this, dst, src,
false,
1913 *
else if(NS_stage == 3) {
1914 * this->data->
cell_loop(&NavierStokesProjectionOperator::assemble_cell_term_projection_grad_p,
1915 *
this, dst, src,
false);
1918 *
Assert(
false, ExcNotImplemented());
1924 * Finally, we focus on computing the
diagonal for preconditioners and we start by assembling
1925 * the
diagonal cell term
for the velocity. Since we
do not have access to the entries of the
matrix,
1926 * 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.
1927 * This is why
'src' will result as unused.
1933 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
1934 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1937 *
const unsigned int& ,
1938 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1939 *
if(TR_BDF2_stage == 1) {
1941 * phi_old_extr(data, 0);
1947 *
for(
unsigned int d = 0;
d < dim; ++
d)
1951 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1952 * phi_old_extr.reinit(cell);
1957 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1958 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
1960 * phi.submit_dof_value(tmp, i);
1964 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1965 *
const auto& u_int = phi.get_value(q);
1966 *
const auto& grad_u_int = phi.get_gradient(q);
1967 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1968 *
const auto& tensor_product_u_int =
outer_product(u_int, u_n_gamma_ov_2);
1970 * phi.submit_value(1.0/(gamma*dt)*u_int, q);
1971 * phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1974 *
diagonal[i] = phi.get_dof_value(i);
1976 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
1977 * phi.submit_dof_value(diagonal[i], i);
1978 * phi.distribute_local_to_global(dst);
1983 * phi_int_extr(data, 0);
1987 *
for(
unsigned int d = 0;
d < dim; ++
d)
1991 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1992 * phi_int_extr.reinit(cell);
1997 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1998 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2000 * phi.submit_dof_value(tmp, i);
2004 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2005 *
const auto& u_curr = phi.get_value(q);
2006 *
const auto& grad_u_curr = phi.get_gradient(q);
2007 *
const auto& u_n1_int = phi_int_extr.get_value(q);
2008 *
const auto& tensor_product_u_curr =
outer_product(u_curr, u_n1_int);
2010 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
2011 * phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
2014 *
diagonal[i] = phi.get_dof_value(i);
2016 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2017 * phi.submit_dof_value(diagonal[i], i);
2018 * phi.distribute_local_to_global(dst);
2026 * The following function assembles
diagonal face term
for the velocity
2032 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2033 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2036 *
const unsigned int& ,
2037 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2038 *
if(TR_BDF2_stage == 1) {
2040 * phi_m(data,
false, 0),
2041 * phi_old_extr_p(data,
true, 0),
2042 * phi_old_extr_m(data,
false, 0);
2044 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2048 * diagonal_m(phi_m.dofs_per_component);
2051 *
for(
unsigned int d = 0;
d < dim; ++
d)
2055 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2056 * phi_old_extr_p.reinit(face);
2058 * phi_old_extr_m.reinit(face);
2060 * phi_p.reinit(face);
2061 * phi_m.reinit(face);
2063 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2064 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2067 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2068 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2072 * phi_p.submit_dof_value(tmp, i);
2074 * phi_m.submit_dof_value(tmp, i);
2078 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2079 *
const auto& n_plus = phi_p.get_normal_vector(q);
2080 *
const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2081 *
const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
2082 *
const auto& avg_tensor_product_u_int = 0.5*(
outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
2083 *
outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
2085 *
std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
2087 * phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
2088 * a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int , q);
2089 * phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
2090 * a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
2091 * phi_p.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2092 * phi_m.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2095 * diagonal_p[i] = phi_p.get_dof_value(i);
2097 * diagonal_m[i] = phi_m.get_dof_value(i);
2099 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2100 * phi_p.submit_dof_value(diagonal_p[i], i);
2101 * phi_m.submit_dof_value(diagonal_m[i], i);
2103 * phi_p.distribute_local_to_global(dst);
2104 * phi_m.distribute_local_to_global(dst);
2109 * phi_m(data,
false, 0),
2110 * phi_extr_p(data,
true, 0),
2111 * phi_extr_m(data,
false, 0);
2113 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2115 * diagonal_m(phi_m.dofs_per_component);
2117 *
for(
unsigned int d = 0;
d < dim; ++
d)
2121 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2122 * phi_extr_p.reinit(face);
2124 * phi_extr_m.reinit(face);
2126 * phi_p.reinit(face);
2127 * phi_m.reinit(face);
2129 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2130 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2133 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2134 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2138 * phi_p.submit_dof_value(tmp, i);
2140 * phi_m.submit_dof_value(tmp, i);
2144 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2145 *
const auto& n_plus = phi_p.get_normal_vector(q);
2146 *
const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2147 *
const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
2148 *
const auto& avg_tensor_product_u = 0.5*(
outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
2149 *
outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
2151 *
std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
2153 * phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
2154 * a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
2155 * phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
2156 * a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
2157 * phi_p.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2158 * phi_m.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2161 * diagonal_p[i] = phi_p.get_dof_value(i);
2163 * diagonal_m[i] = phi_m.get_dof_value(i);
2165 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2166 * phi_p.submit_dof_value(diagonal_p[i], i);
2167 * phi_m.submit_dof_value(diagonal_m[i], i);
2169 * phi_p.distribute_local_to_global(dst);
2170 * phi_m.distribute_local_to_global(dst);
2178 * The following function assembles boundary term
for the velocity
2184 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2185 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2188 *
const unsigned int& ,
2189 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2190 *
if(TR_BDF2_stage == 1) {
2192 * phi_old_extr(data,
true, 0);
2196 *
for(
unsigned int d = 0;
d < dim; ++
d)
2200 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2201 * phi_old_extr.reinit(face);
2206 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2208 *
if(boundary_id != 1) {
2209 *
const double coef_trasp = 0.0;
2212 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2213 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2215 * phi.submit_dof_value(tmp, i);
2219 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2220 *
const auto& n_plus = phi.get_normal_vector(q);
2221 *
const auto& grad_u_int = phi.get_gradient(q);
2222 *
const auto& u_int = phi.get_value(q);
2223 *
const auto& tensor_product_u_int =
outer_product(phi.get_value(q), phi_old_extr.get_value(q));
2224 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2226 * phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
2227 * a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
2228 * phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
2231 *
diagonal[i] = phi.get_dof_value(i);
2233 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2234 * phi.submit_dof_value(diagonal[i], i);
2235 * phi.distribute_local_to_global(dst);
2239 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2240 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2242 * phi.submit_dof_value(tmp, i);
2246 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2247 *
const auto& n_plus = phi.get_normal_vector(q);
2248 *
const auto& grad_u_int = phi.get_gradient(q);
2249 *
const auto& u_int = phi.get_value(q);
2250 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2252 *
const auto& point_vectorized = phi.quadrature_point(q);
2253 *
auto u_int_m = u_int;
2254 *
auto grad_u_int_m = grad_u_int;
2255 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2257 *
for(
unsigned int d = 0;
d < dim; ++
d)
2258 * point[d] = point_vectorized[d][v];
2260 * u_int_m[1][v] = -u_int_m[1][v];
2262 * grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
2263 * grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
2266 * phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
2267 * a22*
outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
2268 * a22*0.5*
lambda*(u_int - u_int_m), q);
2269 * phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
2272 *
diagonal[i] = phi.get_dof_value(i);
2274 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2275 * phi.submit_dof_value(diagonal[i], i);
2276 * phi.distribute_local_to_global(dst);
2282 * phi_extr(data,
true, 0);
2286 *
for(
unsigned int d = 0;
d < dim; ++
d)
2290 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2291 * phi_extr.reinit(face);
2296 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2298 *
if(boundary_id != 1) {
2299 *
const double coef_trasp = 0.0;
2302 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2303 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2305 * phi.submit_dof_value(tmp, i);
2309 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2310 *
const auto& n_plus = phi.get_normal_vector(q);
2311 *
const auto& grad_u = phi.get_gradient(q);
2312 *
const auto& u = phi.get_value(q);
2313 *
const auto& tensor_product_u =
outer_product(phi.get_value(q), phi_extr.get_value(q));
2314 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2316 * phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
2317 * a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
2318 * phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
2321 *
diagonal[i] = phi.get_dof_value(i);
2323 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2324 * phi.submit_dof_value(diagonal[i], i);
2325 * phi.distribute_local_to_global(dst);
2329 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2330 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2332 * phi.submit_dof_value(tmp, i);
2336 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2337 *
const auto& n_plus = phi.get_normal_vector(q);
2338 *
const auto& grad_u = phi.get_gradient(q);
2339 *
const auto& u = phi.get_value(q);
2340 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2342 *
const auto& point_vectorized = phi.quadrature_point(q);
2344 *
auto grad_u_m = grad_u;
2345 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2347 *
for(
unsigned int d = 0;
d < dim; ++
d)
2348 * point[d] = point_vectorized[d][v];
2350 * u_m[1][v] = -u_m[1][v];
2352 * grad_u_m[0][0][v] = -grad_u_m[0][0][v];
2353 * grad_u_m[0][1][v] = -grad_u_m[0][1][v];
2356 * phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
2357 * a33*
outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus +
2358 * a33*0.5*
lambda*(u - u_m), q);
2359 * phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
2362 *
diagonal[i] = phi.get_dof_value(i);
2364 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2365 * phi.submit_dof_value(diagonal[i], i);
2366 * phi.distribute_local_to_global(dst);
2375 * Now we consider the pressure related bilinear forms. We
first assemble diagonal cell term
for the pressure
2381 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2382 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2385 *
const unsigned int& ,
2386 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
2393 *
const double coeff = (TR_BDF2_stage == 1) ? 1e6*gamma*dt*gamma*dt : 1e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
2396 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
2400 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2401 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2409 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2410 * phi.submit_value(1.0/coeff*phi.get_value(q), q);
2411 * phi.submit_gradient(phi.get_gradient(q), q);
2414 *
diagonal[i] = phi.get_dof_value(i);
2416 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2417 * phi.submit_dof_value(diagonal[i], i);
2419 * phi.distribute_local_to_global(dst);
2426 * The following function assembles
diagonal face term
for the pressure
2432 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2433 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2436 *
const unsigned int& ,
2437 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2439 * phi_m(data,
false, 1, 1);
2441 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2443 * diagonal_m(phi_m.dofs_per_component);
2448 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2449 * phi_p.reinit(face);
2450 * phi_m.reinit(face);
2452 *
const auto coef_jump = C_p*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2453 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2456 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2457 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2467 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2468 *
const auto& n_plus = phi_p.get_normal_vector(q);
2470 *
const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2471 *
const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
2473 * phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
2474 * phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
2475 * phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2476 * phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2479 * diagonal_p[i] = phi_p.get_dof_value(i);
2481 * diagonal_m[i] = phi_m.get_dof_value(i);
2483 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2484 * phi_p.submit_dof_value(diagonal_p[i], i);
2485 * phi_m.submit_dof_value(diagonal_m[i], i);
2487 * phi_p.distribute_local_to_global(dst);
2488 * phi_m.distribute_local_to_global(dst);
2501 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2502 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2505 *
const unsigned int& ,
2506 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2511 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2514 *
if(boundary_id == 1) {
2517 *
const auto coef_jump = C_p*
std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
2519 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2520 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2525 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2526 *
const auto& n_plus = phi.get_normal_vector(q);
2528 *
const auto& grad_pres = phi.get_gradient(q);
2529 *
const auto& pres = phi.get_value(q);
2531 * phi.submit_value(-scalar_product(grad_pres, n_plus) + 2.0*coef_jump*pres , q);
2532 * phi.submit_normal_derivative(-theta_p*pres, q);
2535 *
diagonal[i] = phi.get_dof_value(i);
2537 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2538 * phi.submit_dof_value(diagonal[i], i);
2539 * phi.distribute_local_to_global(dst);
2547 * Put together all previous steps. We create a dummy auxliary vector that serves
for the src input argument in
2548 * the previous
functions that as we have seen before is unused. Then everything is done by the
'loop' function
2549 * and it is saved in the field
'inverse_diagonal_entries' already present in the base
class. Anyway since there is
2550 * only one field, we need to resize properly depending on whether we are considering the velocity or the pressure.
2556 *
template<
int dim,
int fe_degree_p,
int fe_degree_v,
int n_q_po
ints_1d_p,
int n_q_po
ints_1d_v,
typename Vec>
2557 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2559 *
Assert(NS_stage == 1 || NS_stage == 2, ExcInternalError());
2562 *
auto& inverse_diagonal = this->inverse_diagonal_entries->get_vector();
2564 *
if(NS_stage == 1) {
2565 * ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2568 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& cell_range) {
2569 * (this->assemble_diagonal_cell_term_velocity)(data, dst, src, cell_range);
2571 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& face_range) {
2572 * (this->assemble_diagonal_face_term_velocity)(data, dst, src, face_range);
2574 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& boundary_range) {
2575 * (this->assemble_diagonal_boundary_term_velocity)(data, dst, src, boundary_range);
2579 *
else if(NS_stage == 2) {
2580 * ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2583 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& cell_range) {
2584 * (this->assemble_diagonal_cell_term_pressure)(data, dst, src, cell_range);
2586 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& face_range) {
2587 * (this->assemble_diagonal_face_term_pressure)(data, dst, src, face_range);
2589 * [&](
const auto& data,
auto& dst,
const auto& src,
const auto& boundary_range) {
2590 * (this->assemble_diagonal_boundary_term_pressure)(data, dst, src, boundary_range);
2595 *
for(
unsigned int i = 0; i < inverse_diagonal.locally_owned_size(); ++i) {
2596 *
Assert(inverse_diagonal.local_element(i) != 0.0,
2597 * ExcMessage(
"No diagonal entry in a definite operator should be zero"));
2598 * inverse_diagonal.local_element(i) = 1.0/inverse_diagonal.local_element(i);
2606 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
2607 * @sect{The <code>NavierStokesProjection</code>
class}
2611 * Now we are ready
for the main
class of the program. It
implements the calls to the various steps
2612 * of the projection method for Navier-Stokes equations.
2619 * class NavierStokesProjection {
2621 * NavierStokesProjection(RunTimeParameters::Data_Storage& data);
2623 *
void run(
const bool verbose =
false,
const unsigned int output_interval = 10);
2628 *
const double gamma;
2629 *
unsigned int TR_BDF2_stage;
2633 * EquationData::Velocity<dim> vel_init;
2634 * EquationData::Pressure<dim> pres_init;
2672 * <<
" The time step " << arg1 <<
" is out of range."
2674 * <<
" The permitted range is (0," << arg2 <<
"]");
2678 *
void setup_dofs();
2680 *
void initialize();
2682 *
void interpolate_velocity();
2684 *
void diffusion_step();
2686 *
void projection_step();
2688 *
void project_grad(
const unsigned int flag);
2690 *
double get_maximal_velocity();
2692 *
double get_maximal_difference_velocity();
2694 *
void output_results(
const unsigned int step);
2696 *
void refine_mesh();
2698 *
void interpolate_max_res(
const unsigned int level);
2700 *
void save_max_res();
2703 *
void compute_lift_and_drag();
2706 * std::shared_ptr<MatrixFree<dim, double>> matrix_free_storage;
2709 * NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2710 * EquationData::degree_p + 1, EquationData::degree_p + 2,
2714 *
MGLevelObject<NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2715 * EquationData::degree_p + 1, EquationData::degree_p + 2,
2724 * constraints_pressure;
2727 *
unsigned int max_its;
2730 *
unsigned int max_loc_refinements;
2731 *
unsigned int min_loc_refinements;
2732 *
unsigned int refinement_iterations;
2734 * std::string saving_dir;
2739 * std::ofstream time_out;
2743 * std::ofstream output_n_dofs_velocity;
2744 * std::ofstream output_n_dofs_pressure;
2746 * std::ofstream output_lift;
2747 * std::ofstream output_drag;
2753 * In the constructor, we just read all the data from the
2754 * <code>Data_Storage</code>
object that is passed as an argument, verify that
2755 * the data we read are reasonable and,
finally, create the
triangulation and
2763 * NavierStokesProjection<dim>::NavierStokesProjection(RunTimeParameters::Data_Storage& data):
2764 * t_0(data.initial_time),
2765 * T(data.final_time),
2768 * Re(data.Reynolds),
2770 * vel_init(data.initial_time),
2771 * pres_init(data.initial_time),
2774 * fe_velocity(
FE_DGQ<dim>(EquationData::degree_p + 1), dim),
2775 * fe_pressure(
FE_DGQ<dim>(EquationData::degree_p), 1),
2778 * quadrature_pressure(EquationData::degree_p + 1),
2779 * quadrature_velocity(EquationData::degree_p + 2),
2780 * navier_stokes_matrix(data),
2781 * max_its(data.max_iterations),
2783 * max_loc_refinements(data.max_loc_refinements),
2784 * min_loc_refinements(data.min_loc_refinements),
2785 * refinement_iterations(data.refinement_iterations),
2786 * saving_dir(data.dir),
2788 * time_out(
"./" + data.dir +
"/time_analysis_" +
2792 * output_n_dofs_velocity(
"./" + data.dir +
"/n_dofs_velocity.dat",
std::ofstream::out),
2793 * output_n_dofs_pressure(
"./" + data.dir +
"/n_dofs_pressure.dat",
std::ofstream::out),
2794 * output_lift(
"./" + data.dir +
"/lift.dat",
std::ofstream::out),
2795 * output_drag(
"./" + data.dir +
"/drag.dat",
std::ofstream::out) {
2796 *
if(EquationData::degree_p < 1) {
2798 * <<
" WARNING: The chosen pair of finite element spaces is not stable."
2800 * <<
" The obtained results will be nonsense" << std::endl;
2803 *
AssertThrow(!((dt <= 0.0) || (dt > 0.5*T)), ExcInvalidTimeStep(dt, 0.5*T));
2805 * matrix_free_storage = std::make_shared<MatrixFree<dim, double>>();
2815 * The method that creates the
triangulation and refines it the needed number
2823 *
void NavierStokesProjection<dim>::create_triangulation(
const unsigned int n_refines) {
2826 *
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);
2829 * pcout <<
"Number of refines = " << n_refines << std::endl;
2836 * After creating the
triangulation, it creates the mesh dependent
2837 * data, i.e. it distributes degrees of freedom, and
2838 * initializes the vectors that we will use.
2845 *
void NavierStokesProjection<dim>::setup_dofs() {
2850 * dof_handler_velocity.distribute_dofs(fe_velocity);
2851 * dof_handler_pressure.distribute_dofs(fe_pressure);
2853 * pcout <<
"dim (X_h) = " << dof_handler_velocity.n_dofs()
2855 * <<
"dim (M_h) = " << dof_handler_pressure.n_dofs()
2857 * <<
"Re = " << Re << std::endl
2861 * output_n_dofs_velocity << dof_handler_velocity.n_dofs() << std::endl;
2862 * output_n_dofs_pressure << dof_handler_pressure.n_dofs() << std::endl;
2874 * std::vector<const DoFHandler<dim>*> dof_handlers;
2877 * dof_handlers.push_back(&dof_handler_velocity);
2878 * dof_handlers.push_back(&dof_handler_pressure);
2880 * constraints_velocity.
clear();
2881 * constraints_velocity.close();
2882 * constraints_pressure.clear();
2883 * constraints_pressure.close();
2884 * std::vector<const AffineConstraints<double>*> constraints;
2885 * constraints.push_back(&constraints_velocity);
2886 * constraints.push_back(&constraints_pressure);
2888 * std::vector<QGauss<1>> quadratures;
2892 * quadratures.push_back(
QGauss<1>(EquationData::degree_p + 2));
2893 * quadratures.push_back(
QGauss<1>(EquationData::degree_p + 1));
2897 * matrix_free_storage->reinit(
MappingQ1<dim>(),dof_handlers, constraints, quadratures, additional_data);
2898 * matrix_free_storage->initialize_dof_vector(u_star, 0);
2899 * matrix_free_storage->initialize_dof_vector(rhs_u, 0);
2900 * matrix_free_storage->initialize_dof_vector(u_n, 0);
2901 * matrix_free_storage->initialize_dof_vector(u_extr, 0);
2902 * matrix_free_storage->initialize_dof_vector(u_n_minus_1, 0);
2903 * matrix_free_storage->initialize_dof_vector(u_n_gamma, 0);
2904 * matrix_free_storage->initialize_dof_vector(u_tmp, 0);
2905 * matrix_free_storage->initialize_dof_vector(grad_pres_int, 0);
2907 * matrix_free_storage->initialize_dof_vector(pres_int, 1);
2908 * matrix_free_storage->initialize_dof_vector(pres_n, 1);
2909 * matrix_free_storage->initialize_dof_vector(rhs_p, 1);
2915 * mg_matrices.clear_elements();
2916 * dof_handler_velocity.distribute_mg_dofs();
2917 * dof_handler_pressure.distribute_mg_dofs();
2920 * mg_matrices.resize(0, nlevels - 1);
2929 * std::vector<const DoFHandler<dim>*> dof_handlers_mg;
2930 * dof_handlers_mg.push_back(&dof_handler_velocity);
2931 * dof_handlers_mg.push_back(&dof_handler_pressure);
2932 * std::vector<const AffineConstraints<float>*> constraints_mg;
2934 * constraints_velocity_mg.
clear();
2935 * constraints_velocity_mg.close();
2936 * constraints_mg.push_back(&constraints_velocity_mg);
2938 * constraints_pressure_mg.
clear();
2939 * constraints_pressure_mg.close();
2940 * constraints_mg.push_back(&constraints_pressure_mg);
2943 * mg_mf_storage_level->reinit(
MappingQ1<dim>(),dof_handlers_mg, constraints_mg, quadratures, additional_data_mg);
2944 *
const std::vector<unsigned int> tmp = {1};
2945 * mg_matrices[
level].initialize(mg_mf_storage_level, tmp, tmp);
2946 * mg_matrices[
level].set_dt(dt);
2947 * mg_matrices[
level].set_NS_stage(2);
2956 * This method loads the
initial data. It simply uses the class <code>Pressure</code> instance
for the pressure
2957 * and the class <code>Velocity</code> instance
for the velocity.
2964 *
void NavierStokesProjection<dim>::initialize() {
2976 * This function computes the extrapolated velocity to be used in the momentum predictor
2983 *
void NavierStokesProjection<dim>::interpolate_velocity() {
2988 * --- TR-BDF2
first step
2991 *
if(TR_BDF2_stage == 1) {
2992 * u_extr.equ(1.0 + gamma/(2.0*(1.0 - gamma)), u_n);
2993 * u_tmp.equ(gamma/(2.0*(1.0 - gamma)), u_n_minus_1);
2998 * --- TR-BDF2
second step
3002 * u_extr.equ(1.0 + (1.0 - gamma)/gamma, u_n_gamma);
3003 * u_tmp.equ((1.0 - gamma)/gamma, u_n);
3011 * We are
finally ready to solve the diffusion step.
3018 *
void NavierStokesProjection<dim>::diffusion_step() {
3023 *
const std::vector<unsigned int> tmp = {0};
3024 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3027 * navier_stokes_matrix.set_NS_stage(1);
3032 *
if(TR_BDF2_stage == 1) {
3033 * navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_extr, pres_n});
3034 * navier_stokes_matrix.set_u_extr(u_extr);
3038 * navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_n_gamma, pres_int, u_extr});
3039 * navier_stokes_matrix.set_u_extr(u_extr);
3044 *
SolverControl solver_control(max_its, eps*rhs_u.l2_norm());
3049 * EquationData::degree_p,
3050 * EquationData::degree_p + 1,
3051 * EquationData::degree_p + 1,
3052 * EquationData::degree_p + 2,
3054 * navier_stokes_matrix.compute_diagonal();
3055 * preconditioner.initialize(navier_stokes_matrix);
3057 * gmres.solve(navier_stokes_matrix, u_star, rhs_u, preconditioner);
3063 * Next, we solve the projection step.
3070 *
void NavierStokesProjection<dim>::projection_step() {
3075 *
const std::vector<unsigned int> tmp = {1};
3076 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3078 * navier_stokes_matrix.set_NS_stage(2);
3080 *
if(TR_BDF2_stage == 1)
3081 * navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_n});
3083 * navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_int});
3086 *
SolverControl solver_control(max_its, eps*rhs_p.l2_norm());
3091 * mg_transfer.
build(dof_handler_pressure);
3094 * EquationData::degree_p,
3095 * EquationData::degree_p + 1,
3096 * EquationData::degree_p + 1,
3097 * EquationData::degree_p + 2,
3105 * smoother_data[
level].smoothing_range = 15.0;
3106 * smoother_data[
level].degree = 3;
3107 * smoother_data[
level].eig_cg_n_iterations = 10;
3110 * smoother_data[0].smoothing_range = 2
e-2;
3112 * smoother_data[0].eig_cg_n_iterations = mg_matrices[0].m();
3114 * mg_matrices[
level].compute_diagonal();
3115 * smoother_data[
level].preconditioner = mg_matrices[
level].get_matrix_diagonal_inverse();
3117 * mg_smoother.initialize(mg_matrices, smoother_data);
3123 * NavierStokesProjectionOperator<dim,
3124 * EquationData::degree_p,
3125 * EquationData::degree_p + 1,
3126 * EquationData::degree_p + 1,
3127 * EquationData::degree_p + 2,
3140 *
if(TR_BDF2_stage == 1) {
3141 * pres_int = pres_n;
3142 * cg.solve(navier_stokes_matrix, pres_int, rhs_p, preconditioner);
3145 * pres_n = pres_int;
3146 * cg.solve(navier_stokes_matrix, pres_n, rhs_p, preconditioner);
3153 * This implements the projection step
for the
gradient of pressure
3160 *
void NavierStokesProjection<dim>::project_grad(
const unsigned int flag) {
3165 *
Assert(flag > 0, ExcInternalError());
3168 *
const std::vector<unsigned int> tmp = {0};
3169 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3172 * navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_n);
3173 *
else if(flag == 2)
3174 * navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_int);
3177 * navier_stokes_matrix.set_NS_stage(3);
3180 *
SolverControl solver_control(max_its, 1e-12*rhs_u.l2_norm());
3188 * The following function is used in determining the maximal velocity
3189 * in order to compute the Courant number.
3196 *
double NavierStokesProjection<dim>::get_maximal_velocity() {
3197 *
return u_n.linfty_norm();
3203 * The following function is used in determining the maximal nodal difference
3204 * between old and current velocity
value in order to see
if we have reached steady-state.
3211 *
double NavierStokesProjection<dim>::get_maximal_difference_velocity() {
3213 * u_tmp -= u_n_minus_1;
3215 *
return u_tmp.linfty_norm();
3221 * This method plots the current solution. The main difficulty is that we want
3222 * to create a single output file that contains the data
for all velocity
3223 * components and the pressure. On the other hand, velocities and the pressure
3224 * live on separate
DoFHandler objects, so we need to pay attention when we use
3225 *
'add_data_vector' to select the proper space.
3232 *
void NavierStokesProjection<dim>::output_results(
const unsigned int step) {
3237 * std::vector<std::string> velocity_names(dim,
"v");
3238 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
3240 * u_n.update_ghost_values();
3241 * data_out.add_data_vector(dof_handler_velocity, u_n, velocity_names, component_interpretation_velocity);
3242 * pres_n.update_ghost_values();
3245 * std::vector<std::string> velocity_names_old(dim,
"v_old");
3246 * u_n_minus_1.update_ghost_values();
3247 * data_out.add_data_vector(dof_handler_velocity, u_n_minus_1, velocity_names_old, component_interpretation_velocity);
3250 * PostprocessorVorticity<dim> postprocessor;
3251 * data_out.add_data_vector(dof_handler_velocity, u_n, postprocessor);
3256 * data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3263 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3264 * @sect{<code>NavierStokesProjection::compute_lift_and_drag</code>}
3268 * This routine computes the lift and the drag forces in a non-dimensional framework
3269 * (so basically
for the classical coefficients, it is necessary to multiply by a factor 2).
3276 *
void NavierStokesProjection<dim>::compute_lift_and_drag() {
3277 *
QGauss<dim - 1> face_quadrature_formula(EquationData::degree_p + 2);
3278 *
const int n_q_points = face_quadrature_formula.size();
3280 * std::vector<double> pressure_values(n_q_points);
3281 * std::vector<std::vector<Tensor<1, dim>>> velocity_gradients(n_q_points, std::vector<
Tensor<1, dim>>(dim));
3290 *
FEFaceValues<dim> fe_face_values_velocity(fe_velocity, face_quadrature_formula,
3295 *
double local_drag = 0.0;
3296 *
double local_lift = 0.0;
3302 *
auto tmp_cell = dof_handler_pressure.begin_active();
3303 *
for(
const auto& cell : dof_handler_velocity.active_cell_iterators()) {
3304 *
if(cell->is_locally_owned()) {
3305 *
for(
unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face) {
3306 *
if(cell->face(face)->at_boundary() && cell->face(face)->boundary_id() == 4) {
3307 * fe_face_values_velocity.reinit(cell, face);
3308 * fe_face_values_pressure.reinit(tmp_cell, face);
3310 * fe_face_values_velocity.get_function_gradients(u_n, velocity_gradients);
3311 * fe_face_values_pressure.get_function_values(pres_n, pressure_values);
3313 *
for(
int q = 0; q < n_q_points; q++) {
3314 * normal_vector = -fe_face_values_velocity.normal_vector(q);
3316 *
for(
unsigned int d = 0;
d < dim; ++
d) {
3317 * fluid_pressure[
d][
d] = pressure_values[q];
3318 *
for(
unsigned int k = 0; k < dim; ++k)
3319 * fluid_stress[d][k] = 1.0/Re*velocity_gradients[q][d][k];
3321 * fluid_stress = fluid_stress - fluid_pressure;
3323 * forces = fluid_stress*normal_vector*fe_face_values_velocity.JxW(q);
3325 * local_drag += forces[0];
3326 * local_lift += forces[1];
3339 * output_lift << lift << std::endl;
3340 * output_drag << drag << std::endl;
3348 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3349 * @sect{ <code>NavierStokesProjection::refine_mesh</code>}
3353 * After finding a good
initial guess on the coarse mesh, we hope to
3354 * decrease the error through refining the mesh. We also need to transfer the current solution to the
3361 *
template <
int dim>
3362 *
void NavierStokesProjection<dim>::refine_mesh() {
3368 * tmp_velocity.
reinit(dof_handler_velocity.locally_owned_dofs(), locally_relevant_dofs, MPI_COMM_WORLD);
3369 * tmp_velocity = u_n;
3370 * tmp_velocity.update_ghost_values();
3377 *
const auto cell_worker = [&](
const Iterator& cell,
3378 * ScratchData<dim>& scratch_data,
3379 * CopyData& copy_data) {
3381 * fe_values.
reinit(cell);
3384 * std::vector<std::vector<Tensor<1, dim>>>
gradients(fe_values.n_quadrature_points, std::vector<
Tensor<1, dim>>(dim));
3385 * fe_values.get_function_gradients(tmp_velocity, gradients);
3386 * copy_data.cell_index = cell->active_cell_index();
3387 *
double vorticity_norm_square = 0.0;
3390 *
for(
unsigned k = 0; k < fe_values.n_quadrature_points; ++k) {
3392 * vorticity_norm_square += vorticity*vorticity*fe_values.JxW(k);
3394 * copy_data.value = cell->diameter()*cell->diameter()*vorticity_norm_square;
3399 *
const auto copier = [&](
const CopyData ©_data) {
3401 * estimated_error_per_cell[copy_data.cell_index] += copy_data.value;
3405 * ScratchData<dim> scratch_data(fe_velocity, EquationData::degree_p + 2, cell_flags);
3406 * CopyData copy_data;
3408 * dof_handler_velocity.end(),
3418 *
for(
const auto& cell:
triangulation.active_cell_iterators()) {
3419 *
if(cell->refine_flag_set() &&
static_cast<unsigned int>(cell->level()) == max_loc_refinements)
3420 * cell->clear_refine_flag();
3421 *
if(cell->coarsen_flag_set() &&
static_cast<unsigned int>(cell->level()) == min_loc_refinements)
3422 * cell->clear_coarsen_flag();
3429 * std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3430 * velocities.push_back(&u_n);
3431 * velocities.push_back(&u_n_minus_1);
3433 * solution_transfer_velocity(dof_handler_velocity);
3434 * solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3436 * solution_transfer_pressure(dof_handler_pressure);
3437 * solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3448 * transfer_velocity_minus_1,
3449 * transfer_pressure;
3450 * transfer_velocity.
reinit(u_n);
3451 * transfer_velocity.zero_out_ghost_values();
3452 * transfer_velocity_minus_1.reinit(u_n_minus_1);
3453 * transfer_velocity_minus_1.zero_out_ghost_values();
3454 * transfer_pressure.reinit(pres_n);
3455 * transfer_pressure.zero_out_ghost_values();
3457 * std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3458 * transfer_velocities.push_back(&transfer_velocity);
3459 * transfer_velocities.push_back(&transfer_velocity_minus_1);
3460 * solution_transfer_velocity.interpolate(transfer_velocities);
3461 * transfer_velocity.update_ghost_values();
3462 * transfer_velocity_minus_1.update_ghost_values();
3463 * solution_transfer_pressure.interpolate(transfer_pressure);
3464 * transfer_pressure.update_ghost_values();
3466 * u_n = transfer_velocity;
3467 * u_n_minus_1 = transfer_velocity_minus_1;
3468 * pres_n = transfer_pressure;
3474 * Interpolate the locally refined solution to a mesh with maximal resolution
3475 * and transfer velocity and pressure.
3482 *
void NavierStokesProjection<dim>::interpolate_max_res(
const unsigned int level) {
3484 * solution_transfer_velocity(dof_handler_velocity);
3485 * std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3486 * velocities.push_back(&u_n);
3487 * velocities.push_back(&u_n_minus_1);
3488 * solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3491 * solution_transfer_pressure(dof_handler_pressure);
3492 * solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3495 *
if(cell->is_locally_owned())
3496 * cell->set_refine_flag();
3503 * transfer_pressure;
3505 * transfer_velocity.
reinit(u_n);
3506 * transfer_velocity.zero_out_ghost_values();
3507 * transfer_velocity_minus_1.reinit(u_n_minus_1);
3508 * transfer_velocity_minus_1.zero_out_ghost_values();
3510 * transfer_pressure.reinit(pres_n);
3511 * transfer_pressure.zero_out_ghost_values();
3513 * std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3515 * transfer_velocities.push_back(&transfer_velocity);
3516 * transfer_velocities.push_back(&transfer_velocity_minus_1);
3517 * solution_transfer_velocity.interpolate(transfer_velocities);
3518 * transfer_velocity.update_ghost_values();
3519 * transfer_velocity_minus_1.update_ghost_values();
3521 * solution_transfer_pressure.interpolate(transfer_pressure);
3522 * transfer_pressure.update_ghost_values();
3524 * u_n = transfer_velocity;
3525 * u_n_minus_1 = transfer_velocity_minus_1;
3526 * pres_n = transfer_pressure;
3532 * Save maximum resolution to a mesh adapted.
3539 *
void NavierStokesProjection<dim>::save_max_res() {
3541 *
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);
3546 * dof_handler_velocity_tmp.distribute_dofs(fe_velocity);
3547 * dof_handler_pressure_tmp.distribute_dofs(fe_pressure);
3551 * u_n_tmp.
reinit(dof_handler_velocity_tmp.n_dofs());
3552 * pres_n_tmp.reinit(dof_handler_pressure_tmp.n_dofs());
3555 * std::vector<std::string> velocity_names(dim,
"v");
3556 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
3559 * u_n_tmp.update_ghost_values();
3560 * data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, velocity_names, component_interpretation_velocity);
3562 * pres_n_tmp.update_ghost_values();
3564 * PostprocessorVorticity<dim> postprocessor;
3565 * data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, postprocessor);
3568 *
const std::string output =
"./" + saving_dir +
"/solution_max_res_end.vtu";
3569 * data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3576 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3577 * @sect{ <code>NavierStokesProjection::run</code> }
3581 * This is the time marching function, which starting at <code>t_0</code>
3582 * advances in time
using the projection method with time step <code>dt</code>
3583 * until <code>T</code>.
3587 * Its
second parameter, <code>verbose</code> indicates whether the function
3588 * should output information what it is doing at any given moment:
3596 *
void NavierStokesProjection<dim>::run(
const bool verbose,
const unsigned int output_interval) {
3599 * output_results(1);
3600 *
double time = t_0 + dt;
3601 *
unsigned int n = 1;
3602 *
while(
std::abs(T - time) > 1e-10) {
3605 * pcout <<
"Step = " << n <<
" Time = " << time << std::endl;
3608 * TR_BDF2_stage = 1;
3609 * navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3611 * mg_matrices[
level].set_TR_BDF2_stage(TR_BDF2_stage);
3613 * verbose_cout <<
" Interpolating the velocity stage 1" << std::endl;
3614 * interpolate_velocity();
3616 * verbose_cout <<
" Diffusion Step stage 1 " << std::endl;
3619 * verbose_cout <<
" Projection Step stage 1" << std::endl;
3621 * u_tmp.equ(gamma*dt, u_tmp);
3623 * projection_step();
3625 * verbose_cout <<
" Updating the Velocity stage 1" << std::endl;
3626 * u_n_gamma.equ(1.0, u_star);
3628 * grad_pres_int.equ(1.0, u_tmp);
3629 * u_tmp.equ(-gamma*dt, u_tmp);
3630 * u_n_gamma += u_tmp;
3631 * u_n_minus_1 = u_n;
3634 * TR_BDF2_stage = 2;
3636 * mg_matrices[
level].set_TR_BDF2_stage(TR_BDF2_stage);
3637 * navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3639 * verbose_cout <<
" Interpolating the velocity stage 2" << std::endl;
3640 * interpolate_velocity();
3642 * verbose_cout <<
" Diffusion Step stage 2 " << std::endl;
3645 * verbose_cout <<
" Projection Step stage 2" << std::endl;
3646 * u_tmp.equ((1.0 - gamma)*dt, grad_pres_int);
3648 * projection_step();
3650 * verbose_cout <<
" Updating the Velocity stage 2" << std::endl;
3651 * u_n.equ(1.0, u_star);
3653 * u_tmp.equ((gamma - 1.0)*dt, u_tmp);
3656 *
const double max_vel = get_maximal_velocity();
3657 * pcout<<
"Maximal velocity = " << max_vel << std::endl;
3659 * pcout <<
"CFL = " << dt*max_vel*(EquationData::degree_p + 1)*
3661 * compute_lift_and_drag();
3662 *
if(n % output_interval == 0) {
3663 * verbose_cout <<
"Plotting Solution final" << std::endl;
3664 * output_results(n);
3667 *
if(T - time < dt && T - time > 1e-10) {
3669 * navier_stokes_matrix.set_dt(dt);
3671 * mg_matrices[
level].set_dt(dt);
3674 *
if(refinement_iterations > 0 && n % refinement_iterations == 0) {
3675 * verbose_cout <<
"Refining mesh" << std::endl;
3679 *
if(n % output_interval != 0) {
3680 * verbose_cout <<
"Plotting Solution final" << std::endl;
3681 * output_results(n);
3683 *
if(refinement_iterations > 0) {
3685 * interpolate_max_res(lev);
3696 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3697 * @sect{ The main function }
3701 * The main function looks very much like in all the other tutorial programs. We
first initialize
MPI,
3702 * we initialize the
class 'NavierStokesProjection' with the dimension as template parameter and then
3703 * let the method
'run' do the job.
3709 *
int main(
int argc,
char *argv[]) {
3711 *
using namespace NS_TRBDF2;
3713 * RunTimeParameters::Data_Storage data;
3714 * data.read_data(
"parameter-file.prm");
3721 * NavierStokesProjection<2> test(data);
3722 * test.run(data.verbose, data.output_interval);
3724 *
if(curr_rank == 0)
3725 * std::cout <<
"----------------------------------------------------"
3727 * <<
"Apparently everything went fine!" << std::endl
3728 * <<
"Don't forget to brush your teeth :-)" << std::endl
3733 *
catch(std::exception &exc) {
3734 * std::cerr << std::endl
3736 * <<
"----------------------------------------------------"
3738 * std::cerr <<
"Exception on processing: " << std::endl
3739 * << exc.what() << std::endl
3740 * <<
"Aborting!" << std::endl
3741 * <<
"----------------------------------------------------"
3746 * std::cerr << std::endl
3748 * <<
"----------------------------------------------------"
3750 * std::cerr <<
"Unknown exception!" << std::endl
3751 * <<
"Aborting!" << std::endl
3752 * <<
"----------------------------------------------------"
3761<a name=
"ann-runtime_parameters.h"></a>
3762<h1>Annotated version of runtime_parameters.h</h1>
3780 * We start by including all the necessary deal.II header files
3786 * #include <deal.II/base/parameter_handler.h>
3791 * <a name=
"runtime_parameters.h-"></a>
3792 * @sect{Run time parameters}
3796 * Since our method has several parameters that can be fine-tuned we put them
3797 * into an external file, so that they can be determined at
run-time.
3803 *
namespace RunTimeParameters {
3804 *
using namespace dealii;
3806 *
class Data_Storage {
3810 *
void read_data(
const std::string& filename);
3812 *
double initial_time;
3813 *
double final_time;
3818 *
unsigned int n_refines;
3819 *
unsigned int max_loc_refinements;
3820 *
unsigned int min_loc_refinements;
3824 *
unsigned int max_iterations;
3828 *
unsigned int output_interval;
3832 *
unsigned int refinement_iterations;
3840 * In the constructor of
this class we declare all the parameters in suitable (but arbitrary) subsections.
3846 * Data_Storage::Data_Storage(): initial_time(0.0),
3851 * max_loc_refinements(0),
3852 * min_loc_refinements(0),
3853 * max_iterations(1000),
3856 * output_interval(15),
3857 * refinement_iterations(0) {
3858 * prm.enter_subsection(
"Physical data");
3860 * prm.declare_entry(
"initial_time",
3863 *
" The initial time of the simulation. ");
3864 * prm.declare_entry(
"final_time",
3867 *
" The final time of the simulation. ");
3868 * prm.declare_entry(
"Reynolds",
3871 *
" The Reynolds number. ");
3873 * prm.leave_subsection();
3875 * prm.enter_subsection(
"Time step data");
3877 * prm.declare_entry(
"dt",
3880 *
" The time step size. ");
3882 * prm.leave_subsection();
3884 * prm.enter_subsection(
"Space discretization");
3886 * prm.declare_entry(
"n_of_refines",
3889 *
" The number of cells we want on each direction of the mesh. ");
3890 * prm.declare_entry(
"max_loc_refinements",
3893 *
" The number of maximum local refinements. ");
3894 * prm.declare_entry(
"min_loc_refinements",
3897 *
" The number of minimum local refinements. ");
3899 * prm.leave_subsection();
3901 * prm.enter_subsection(
"Data solve");
3903 * prm.declare_entry(
"max_iterations",
3906 *
" The maximal number of iterations linear solvers must make. ");
3907 * prm.declare_entry(
"eps",
3910 *
" The stopping criterion. ");
3912 * prm.leave_subsection();
3914 * prm.declare_entry(
"refinement_iterations",
3917 *
" This number indicates how often we need to "
3918 *
"refine the mesh");
3920 * prm.declare_entry(
"saving directory",
"SimTest");
3922 * prm.declare_entry(
"verbose",
3925 *
" This indicates whether the output of the solution "
3926 *
"process should be verbose. ");
3928 * prm.declare_entry(
"output_interval",
3931 *
" This indicates between how many time steps we print "
3932 *
"the solution. ");
3937 * We need now a routine to read all declared parameters in the constructor
3943 *
void Data_Storage::read_data(
const std::string& filename) {
3944 * std::ifstream file(filename);
3947 * prm.parse_input(file);
3949 * prm.enter_subsection(
"Physical data");
3951 * initial_time = prm.get_double(
"initial_time");
3952 * final_time = prm.get_double(
"final_time");
3953 * Reynolds = prm.get_double(
"Reynolds");
3955 * prm.leave_subsection();
3957 * prm.enter_subsection(
"Time step data");
3959 * dt = prm.get_double(
"dt");
3961 * prm.leave_subsection();
3963 * prm.enter_subsection(
"Space discretization");
3965 * n_refines = prm.get_integer(
"n_of_refines");
3966 * max_loc_refinements = prm.get_integer(
"max_loc_refinements");
3967 * min_loc_refinements = prm.get_integer(
"min_loc_refinements");
3969 * prm.leave_subsection();
3971 * prm.enter_subsection(
"Data solve");
3973 * max_iterations = prm.get_integer(
"max_iterations");
3974 *
eps = prm.get_double(
"eps");
3976 * prm.leave_subsection();
3978 * dir = prm.get(
"saving directory");
3980 * refinement_iterations = prm.get_integer(
"refinement_iterations");
3982 * verbose = prm.get_bool(
"verbose");
3984 * output_interval = prm.get_integer(
"output_interval");
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)
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 > >())
types::boundary_id get_boundary_id(const unsigned int face_batch_index) const
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
unsigned int n_active_cells() const
void refine_global(const unsigned int times=1)
virtual types::global_cell_index n_global_active_cells() const override
virtual unsigned int n_global_levels() const override
virtual void execute_coarsening_and_refinement() override
virtual bool prepare_coarsening_and_refinement() override
__global__ void set(Number *val, const Number s, const size_type N)
#define Assert(cond, exc)
#define DeclException2(Exception2, type1, type2, outsequence)
#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)
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())
@ 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.
@ component_is_part_of_vector
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 > ¢er=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.
@ matrix
Contents is actually a matrix.
@ diagonal
Matrix is diagonal.
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
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)
@ construct_multigrid_hierarchy
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
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)
static const unsigned int invalid_unsigned_int
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())
::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 > &)
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
UpdateFlags mapping_update_flags
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
VectorizedArray< Number, width > make_vectorized_array(const Number &u)