167 * We start by including all the necessary deal.II header files.
173 * #include <deal.II/base/
point.h>
174 * #include <deal.II/base/function.h>
179 * <a name=
"equation_data.h-"></a>
180 * @sect{Equation
data}
184 * In the next
namespace, we declare and implement suitable
functions that may be used
for the
initial and boundary conditions
190 *
namespace EquationData {
193 *
static const unsigned int degree_p = 1;
199 * We declare
class that describes the boundary conditions and
initial one for velocity:
206 * class Velocity:
public Function<dim> {
208 * Velocity(
const double initial_time = 0.0);
211 *
const unsigned int component = 0)
const override;
213 *
virtual void vector_value(
const Point<dim>& p,
219 * Velocity<dim>::Velocity(
const double initial_time):
Function<dim>(dim, initial_time) {}
223 *
double Velocity<dim>::value(
const Point<dim>& p,
const unsigned int component)
const {
225 *
if(component == 0) {
226 *
const double Um = 1.5;
227 *
const double H = 4.1;
229 *
return 4.0*Um*p(1)*(H - p(1))/(H*H);
240 *
for(
unsigned int i = 0; i < dim; ++i)
241 * values[i] =
value(p, i);
247 * We
do the same
for the pressure
254 *
class Pressure:
public Function<dim> {
256 * Pressure(
const double initial_time = 0.0);
259 *
const unsigned int component = 0)
const override;
264 * Pressure<dim>::Pressure(
const double initial_time):
Function<dim>(1, initial_time) {}
268 *
double Pressure<dim>::value(
const Point<dim>& p,
const unsigned int component)
const {
272 *
return 22.0 - p(0);
279<a name=
"ann-navier_stokes_TRBDF2_DG.cc"></a>
280<h1>Annotated version of navier_stokes_TRBDF2_DG.cc</h1>
300 * We start by including all the necessary deal.II header files and some
C++
307 * #include <deal.II/base/quadrature_lib.h>
308 * #include <deal.II/base/multithread_info.h>
309 * #include <deal.II/base/thread_management.h>
310 * #include <deal.II/base/work_stream.h>
311 * #include <deal.II/base/
parallel.h>
312 * #include <deal.II/base/utilities.h>
313 * #include <deal.II/base/conditional_ostream.h>
315 * #include <deal.II/lac/vector.h>
316 * #include <deal.II/lac/solver_cg.h>
317 * #include <deal.II/lac/precondition.h>
318 * #include <deal.II/lac/solver_gmres.h>
319 * #include <deal.II/lac/affine_constraints.h>
321 * #include <deal.II/grid/tria.h>
322 * #include <deal.II/grid/grid_generator.h>
323 * #include <deal.II/grid/grid_tools.h>
324 * #include <deal.II/grid/grid_refinement.h>
325 * #include <deal.II/grid/tria_accessor.h>
326 * #include <deal.II/grid/tria_iterator.h>
327 * #include <deal.II/distributed/grid_refinement.h>
329 * #include <deal.II/dofs/dof_handler.h>
330 * #include <deal.II/dofs/dof_accessor.h>
331 * #include <deal.II/dofs/dof_tools.h>
333 * #include <deal.II/fe/fe_q.h>
334 * #include <deal.II/fe/fe_dgq.h>
335 * #include <deal.II/fe/fe_values.h>
336 * #include <deal.II/fe/fe_tools.h>
337 * #include <deal.II/fe/fe_system.h>
339 * #include <deal.II/numerics/matrix_tools.h>
340 * #include <deal.II/numerics/vector_tools.h>
341 * #include <deal.II/numerics/data_out.h>
345 * #include <iostream>
347 * #include <deal.II/matrix_free/matrix_free.h>
348 * #include <deal.II/matrix_free/operators.h>
349 * #include <deal.II/matrix_free/fe_evaluation.h>
350 * #include <deal.II/fe/component_mask.h>
352 * #include <deal.II/base/timer.h>
353 * #include <deal.II/distributed/solution_transfer.h>
354 * #include <deal.II/numerics/error_estimator.h>
356 * #include <deal.II/multigrid/multigrid.h>
357 * #include <deal.II/multigrid/mg_transfer_matrix_free.h>
358 * #include <deal.II/multigrid/mg_tools.h>
359 * #include <deal.II/multigrid/mg_coarse.h>
360 * #include <deal.II/multigrid/mg_smoother.h>
361 * #include <deal.II/multigrid/mg_matrix.h>
363 * #include <deal.II/meshworker/
mesh_loop.h>
365 * #include
"runtime_parameters.h"
366 * #include
"equation_data.h"
371 *
template<
int dim,
typename Number,
typename VectorizedArrayType>
376 *
const unsigned int&,
377 *
const std::pair<unsigned int, unsigned int>&)>& cell_operation,
380 *
const unsigned int&,
381 *
const std::pair<unsigned int, unsigned int>&)>& face_operation,
384 *
const unsigned int&,
385 *
const std::pair<unsigned int, unsigned int>&)>& boundary_operation,
386 *
const unsigned int dof_no = 0) {
394 *
const unsigned int dummy = 0;
396 * matrix_free.
loop(cell_operation, face_operation, boundary_operation,
397 * diagonal_global, dummy,
false,
405 * We include the code in a suitable
namespace:
411 *
namespace NS_TRBDF2 {
416 * The following
class is an auxiliary one for post-processing of the vorticity
426 * std::vector<
Vector<double>>& computed_quantities)
const override;
428 *
virtual std::vector<std::string> get_names()
const override;
430 *
virtual std::vector<DataComponentInterpretation::DataComponentInterpretation>
431 * get_data_component_interpretation()
const override;
433 *
virtual UpdateFlags get_needed_update_flags()
const override;
438 * This function evaluates the vorticty in both 2D and 3D cases
447 *
const unsigned int n_quadrature_points = inputs.
solution_values.size();
451 *
Assert(computed_quantities.size() == n_quadrature_points, ExcInternalError());
456 *
Assert(computed_quantities[0].
size() == 1, ExcInternalError());
459 *
Assert(computed_quantities[0].
size() == dim, ExcInternalError());
464 *
for(
unsigned int q = 0; q < n_quadrature_points; ++q)
468 *
for(
unsigned int q = 0; q < n_quadrature_points; ++q) {
478 * This auxiliary function is required by the base
class DataProcessor and simply
479 * sets the name
for the output file
486 * std::vector<std::string> PostprocessorVorticity<dim>::get_names()
const {
487 * std::vector<std::string> names;
488 * names.emplace_back(
"vorticity");
490 * names.emplace_back(
"vorticity");
491 * names.emplace_back(
"vorticity");
499 * This auxiliary function is required by the base
class DataProcessor and simply
500 * specifies
if the vorticity is a
scalar (2D) or a vector (3D)
507 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
508 * PostprocessorVorticity<dim>::get_data_component_interpretation()
const {
509 * std::vector<DataComponentInterpretation::DataComponentInterpretation> interpretation;
518 *
return interpretation;
523 * This auxiliary function is required by the base
class DataProcessor and simply
524 * sets which variables have to updated (only the gradients)
531 *
UpdateFlags PostprocessorVorticity<dim>::get_needed_update_flags()
const {
538 * The following structs are auxiliary objects
for mesh refinement. ScratchData simply sets
546 *
struct ScratchData {
548 *
const unsigned int quadrature_degree,
549 *
const UpdateFlags update_flags): fe_values(fe,
QGauss<dim>(quadrature_degree), update_flags) {}
551 * ScratchData(
const ScratchData<dim>& scratch_data): fe_values(scratch_data.fe_values.get_fe(),
552 * scratch_data.fe_values.get_quadrature(),
553 * scratch_data.fe_values.get_update_flags()) {}
560 * CopyData simply sets the cell
index
569 * CopyData(
const CopyData &) =
default;
579 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
580 * @sect{ <code>NavierStokesProjectionOperator::NavierStokesProjectionOperator</code> }
584 * The following
class sets effectively the weak formulation of the problems for the different stages
585 * and
for both velocity and pressure.
586 * The
template parameters are the dimnesion of the problem, the polynomial degree
for the pressure,
587 * the polynomial degree
for the velocity, the number of quadrature points
for integrals
for the pressure step,
588 * the number of quadrature points
for integrals
for the velocity step, the type of vector
for storage and the type
589 * of floating
point data (in general
double or
float for preconditioners structures
if desired).
595 *
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>
598 *
using Number =
typename Vec::value_type;
600 * NavierStokesProjectionOperator();
602 * NavierStokesProjectionOperator(RunTimeParameters::Data_Storage&
data);
604 *
void set_dt(
const double time_step);
606 *
void set_TR_BDF2_stage(
const unsigned int stage);
608 *
void set_NS_stage(
const unsigned int stage);
610 *
void set_u_extr(
const Vec& src);
612 *
void vmult_rhs_velocity(Vec& dst,
const std::vector<Vec>& src)
const;
614 *
void vmult_rhs_pressure(Vec& dst,
const std::vector<Vec>& src)
const;
616 *
void vmult_grad_p_projection(Vec& dst,
const Vec& src)
const;
630 *
unsigned int TR_BDF2_stage;
631 *
unsigned int NS_stage;
634 *
virtual void apply_add(Vec& dst,
const Vec& src)
const override;
639 *
const double a21 = 0.5;
640 *
const double a22 = 0.5;
643 *
const double theta_v = 1.0;
644 *
const double theta_p = 1.0;
645 *
const double C_p = 1.0*(fe_degree_p + 1)*(fe_degree_p + 1);
646 *
const double C_u = 1.0*(fe_degree_v + 1)*(fe_degree_v + 1);
650 * EquationData::Velocity<dim> vel_boundary_inflow;
656 *
const std::vector<Vec>& src,
657 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
660 *
const std::vector<Vec>& src,
661 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
664 *
const std::vector<Vec>& src,
665 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
669 *
const std::vector<Vec>& src,
670 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
673 *
const std::vector<Vec>& src,
674 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
677 *
const std::vector<Vec>& src,
678 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
683 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
687 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
691 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
696 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
700 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
704 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
709 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
713 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
717 *
const unsigned int& src,
718 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
721 *
const unsigned int& src,
722 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
725 *
const unsigned int& src,
726 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
730 *
const unsigned int& src,
731 *
const std::pair<unsigned int, unsigned int>& cell_range)
const;
734 *
const unsigned int& src,
735 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
738 *
const unsigned int& src,
739 *
const std::pair<unsigned int, unsigned int>& face_range)
const;
745 * We start with the
default constructor. It is important
for MultiGrid, so it is fundamental
746 * to properly set the parameters of the time scheme.
752 *
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>
753 * NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
754 * NavierStokesProjectionOperator():
756 * a32(a31), a33(1.0/(2.0 -
gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr() {}
761 * We focus now on the constructor with runtime parameters storage
767 *
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>
768 * NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
769 * NavierStokesProjectionOperator(RunTimeParameters::Data_Storage&
data):
772 * a32(a31), a33(1.0/(2.0 -
gamma)), TR_BDF2_stage(1), NS_stage(1), u_extr(),
773 * vel_boundary_inflow(
data.initial_time) {}
778 * Setter of time-step (called by
Multigrid and in
case a smaller time-step towards the end is needed)
784 *
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>
785 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
786 * set_dt(
const double time_step) {
793 * Setter of TR-BDF2 stage (
this can be known only during the effective execution
794 * and so it has to be demanded to the
class that really solves the problem)
800 *
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>
801 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
802 * set_TR_BDF2_stage(
const unsigned int stage) {
804 *
Assert(stage > 0, ExcInternalError());
806 * TR_BDF2_stage = stage;
812 * Setter of NS stage (
this can be known only during the effective execution
813 * and so it has to be demanded to the
class that really solves the problem)
819 *
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>
820 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
821 * set_NS_stage(
const unsigned int stage) {
823 *
Assert(stage > 0, ExcInternalError());
831 * Setter of extrapolated velocity
for different stages
837 *
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>
838 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
839 * set_u_extr(
const Vec& src) {
841 * u_extr.update_ghost_values();
847 * We are in a DG-
MatrixFree framework, so it is convenient to compute separately cell contribution,
848 *
internal faces contributions and boundary faces contributions. We start by
849 * assembling the rhs cell term
for the velocity.
855 *
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>
856 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
859 *
const std::vector<Vec>& src,
860 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
861 *
if(TR_BDF2_stage == 1) {
868 * phi_old_extr(
data, 0);
872 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
876 * phi_old.reinit(cell);
881 * phi_old_extr.reinit(cell);
883 * phi_old_press.reinit(cell);
888 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
889 *
const auto& u_n = phi_old.get_value(q);
890 *
const auto& grad_u_n = phi_old.get_gradient(q);
891 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
892 *
const auto& tensor_product_u_n =
outer_product(u_n, u_n_gamma_ov_2);
893 *
const auto& p_n = phi_old_press.get_value(q);
894 *
auto p_n_times_identity = tensor_product_u_n;
895 * p_n_times_identity = 0;
896 *
for(
unsigned int d = 0;
d < dim; ++
d)
897 * p_n_times_identity[d][d] = p_n;
899 * phi.submit_value(1.0/(gamma*dt)*u_n, q);
901 * phi.submit_gradient(-a21/Re*grad_u_n + a21*tensor_product_u_n + p_n_times_identity, q);
917 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
918 * phi_old.reinit(cell);
920 * phi_int.reinit(cell);
922 * phi_old_press.reinit(cell);
927 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
928 *
const auto& u_n = phi_old.get_value(q);
929 *
const auto& grad_u_n = phi_old.get_gradient(q);
930 *
const auto& u_n_gamma = phi_int.get_value(q);
931 *
const auto& grad_u_n_gamma = phi_int.get_gradient(q);
933 *
const auto& tensor_product_u_n_gamma =
outer_product(u_n_gamma, u_n_gamma);
934 *
const auto& p_n = phi_old_press.get_value(q);
935 *
auto p_n_times_identity = tensor_product_u_n;
936 * p_n_times_identity = 0;
937 *
for(
unsigned int d = 0;
d < dim; ++
d)
938 * p_n_times_identity[d][d] = p_n;
940 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_n_gamma, q);
941 * phi.submit_gradient(a32*tensor_product_u_n_gamma + a31*tensor_product_u_n -
942 * a32/Re*grad_u_n_gamma - a31/Re*grad_u_n + p_n_times_identity, q);
952 * The followinf function assembles rhs face term
for the velocity
958 *
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>
959 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
962 *
const std::vector<Vec>& src,
963 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
964 *
if(TR_BDF2_stage == 1) {
969 * phi_m(
data,
false, 0),
970 * phi_old_p(
data,
true, 0),
971 * phi_old_m(
data,
false, 0),
972 * phi_old_extr_p(
data,
true, 0),
973 * phi_old_extr_m(
data,
false, 0);
975 * phi_old_press_m(
data,
false, 1);
978 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
979 * phi_old_p.reinit(face);
981 * phi_old_m.reinit(face);
983 * phi_old_extr_p.reinit(face);
985 * phi_old_extr_m.reinit(face);
987 * phi_old_press_p.reinit(face);
989 * phi_old_press_m.reinit(face);
991 * phi_p.reinit(face);
992 * phi_m.reinit(face);
995 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
996 *
const auto& n_plus = phi_p.get_normal_vector(q);
1000 *
const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1001 *
const auto& avg_tensor_product_u_n = 0.5*(
outer_product(phi_old_p.get_value(q), phi_old_extr_p.get_value(q)) +
1002 *
outer_product(phi_old_m.get_value(q), phi_old_extr_m.get_value(q)));
1003 *
const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1005 * phi_p.submit_value((a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus - avg_p_old*n_plus, q);
1006 * phi_m.submit_value(-(a21/Re*avg_grad_u_old - a21*avg_tensor_product_u_n)*n_plus + avg_p_old*n_plus, q);
1015 * phi_m(
data,
false, 0),
1016 * phi_old_p(
data,
true, 0),
1017 * phi_old_m(
data,
false, 0),
1018 * phi_int_p(
data,
true, 0),
1019 * phi_int_m(
data,
false, 0);
1021 * phi_old_press_m(
data,
false, 1);
1024 *
for(
unsigned int face = face_range.first; face < face_range.second; ++ face) {
1025 * phi_old_p.reinit(face);
1027 * phi_old_m.reinit(face);
1029 * phi_int_p.reinit(face);
1031 * phi_int_m.reinit(face);
1033 * phi_old_press_p.reinit(face);
1035 * phi_old_press_m.reinit(face);
1037 * phi_p.reinit(face);
1038 * phi_m.reinit(face);
1041 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1042 *
const auto& n_plus = phi_p.get_normal_vector(q);
1044 *
const auto& avg_grad_u_old = 0.5*(phi_old_p.get_gradient(q) + phi_old_m.get_gradient(q));
1045 *
const auto& avg_grad_u_int = 0.5*(phi_int_p.get_gradient(q) + phi_int_m.get_gradient(q));
1046 *
const auto& avg_tensor_product_u_n = 0.5*(
outer_product(phi_old_p.get_value(q), phi_old_p.get_value(q)) +
1047 *
outer_product(phi_old_m.get_value(q), phi_old_m.get_value(q)));
1048 *
const auto& avg_tensor_product_u_n_gamma = 0.5*(
outer_product(phi_int_p.get_value(q), phi_int_p.get_value(q)) +
1049 *
outer_product(phi_int_m.get_value(q), phi_int_m.get_value(q)));
1050 *
const auto& avg_p_old = 0.5*(phi_old_press_p.get_value(q) + phi_old_press_m.get_value(q));
1052 * phi_p.submit_value((a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1053 * a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus - avg_p_old*n_plus, q);
1054 * phi_m.submit_value(-(a31/Re*avg_grad_u_old + a32/Re*avg_grad_u_int -
1055 * a31*avg_tensor_product_u_n - a32*avg_tensor_product_u_n_gamma)*n_plus + avg_p_old*n_plus, q);
1066 * The followinf function assembles rhs boundary term
for the velocity
1072 *
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>
1073 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1076 *
const std::vector<Vec>& src,
1077 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1078 *
if(TR_BDF2_stage == 1) {
1082 * phi_old(
data,
true, 0),
1083 * phi_old_extr(
data,
true, 0);
1087 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1088 * phi_old.reinit(face);
1090 * phi_old_extr.reinit(face);
1092 * phi_old_press.reinit(face);
1097 *
const auto coef_jump = (
boundary_id == 1) ? 0.0 : C_u*
std::
abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1098 *
const double aux_coeff = (
boundary_id == 1) ? 0.0 : 1.0;
1101 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1102 *
const auto& n_plus = phi.get_normal_vector(q);
1104 *
const auto& grad_u_old = phi_old.get_gradient(q);
1105 *
const auto& tensor_product_u_n =
outer_product(phi_old.get_value(q), phi_old_extr.get_value(q));
1106 *
const auto& p_old = phi_old_press.get_value(q);
1107 *
const auto& point_vectorized = phi.quadrature_point(q);
1109 *
if(boundary_id == 0) {
1110 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1114 *
for(
unsigned int d = 0;
d < dim; ++
d)
1115 * point[d] = point_vectorized[d][v];
1116 *
for(
unsigned int d = 0;
d < dim; ++
d)
1117 * u_int_m[d][v] = vel_boundary_inflow.value(point, d);
1120 *
const auto tensor_product_u_int_m =
outer_product(u_int_m, phi_old_extr.get_value(q));
1123 * phi.submit_value((a21/Re*grad_u_old - a21*tensor_product_u_n)*n_plus - p_old*n_plus +
1124 * a22/Re*2.0*coef_jump*u_int_m -
1125 * aux_coeff*a22*tensor_product_u_int_m*n_plus + a22*lambda*u_int_m, q);
1126 * phi.submit_normal_derivative(-aux_coeff*theta_v*a22/Re*u_int_m, q);
1135 * phi_old(
data,
true, 0),
1136 * phi_int(
data,
true, 0),
1137 * phi_int_extr(
data,
true, 0);
1141 *
for(
unsigned int face = face_range.first; face < face_range.second; ++ face) {
1142 * phi_old.reinit(face);
1144 * phi_int.reinit(face);
1146 * phi_old_press.reinit(face);
1148 * phi_int_extr.reinit(face);
1153 *
const auto coef_jump = (
boundary_id == 1) ? 0.0 : C_u*
std::
abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1154 *
const double aux_coeff = (
boundary_id == 1) ? 0.0 : 1.0;
1157 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1158 *
const auto& n_plus = phi.get_normal_vector(q);
1160 *
const auto& grad_u_old = phi_old.get_gradient(q);
1161 *
const auto& grad_u_int = phi_int.get_gradient(q);
1162 *
const auto& tensor_product_u_n =
outer_product(phi_old.get_value(q), phi_old.get_value(q));
1163 *
const auto& tensor_product_u_n_gamma =
outer_product(phi_int.get_value(q), phi_int.get_value(q));
1164 *
const auto& p_old = phi_old_press.get_value(q);
1165 *
const auto& point_vectorized = phi.quadrature_point(q);
1167 *
if(boundary_id == 0) {
1168 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1170 *
for(
unsigned int d = 0;
d < dim; ++
d)
1171 * point[d] = point_vectorized[d][v];
1172 *
for(
unsigned int d = 0;
d < dim; ++
d)
1173 * u_m[d][v] = vel_boundary_inflow.value(point, d);
1176 *
const auto tensor_product_u_m =
outer_product(u_m, phi_int_extr.get_value(q));
1179 * phi.submit_value((a31/Re*grad_u_old + a32/Re*grad_u_int -
1180 * a31*tensor_product_u_n - a32*tensor_product_u_n_gamma)*n_plus - p_old*n_plus +
1181 * a33/Re*2.0*coef_jump*u_m -
1182 * aux_coeff*a33*tensor_product_u_m*n_plus + a33*lambda*u_m, q);
1183 * phi.submit_normal_derivative(-aux_coeff*theta_v*a33/Re*u_m, q);
1193 * Put together all the previous steps
for velocity. This is done automatically by the
loop function of
'MatrixFree' class
1199 *
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>
1200 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1201 * vmult_rhs_velocity(Vec& dst,
const std::vector<Vec>& src)
const {
1202 *
for(
auto& vec : src)
1203 * vec.update_ghost_values();
1205 * this->data->
loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_velocity,
1206 * &NavierStokesProjectionOperator::assemble_rhs_face_term_velocity,
1207 * &NavierStokesProjectionOperator::assemble_rhs_boundary_term_velocity,
1208 *
this, dst, src,
true,
1216 * Now we focus on computing the rhs
for the projection step
for the pressure with the same ratio.
1217 * The following function assembles rhs cell term
for the pressure
1223 *
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>
1224 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1227 *
const std::vector<Vec>& src,
1228 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1232 * phi_old(
data, 1, 1);
1235 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
1237 *
const double coeff_2 = (TR_BDF2_stage == 1) ? gamma*dt : (1.0 -
gamma)*dt;
1240 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1241 * phi_proj.reinit(cell);
1243 * phi_old.reinit(cell);
1248 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1249 *
const auto& u_star_star = phi_proj.get_value(q);
1250 *
const auto& p_old = phi_old.get_value(q);
1252 * phi.submit_value(1.0/coeff*p_old, q);
1253 * phi.submit_gradient(1.0/coeff_2*u_star_star, q);
1262 * The following function assembles rhs face term
for the pressure
1268 *
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>
1269 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1272 *
const std::vector<Vec>& src,
1273 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1276 * phi_m(
data,
false, 1, 1);
1278 * phi_proj_m(
data,
false, 0, 1);
1280 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 -
gamma)*dt);
1283 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1284 * phi_proj_p.reinit(face);
1286 * phi_proj_m.reinit(face);
1288 * phi_p.reinit(face);
1289 * phi_m.reinit(face);
1292 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1293 *
const auto& n_plus = phi_p.get_normal_vector(q);
1294 *
const auto& avg_u_star_star = 0.5*(phi_proj_p.get_value(q) + phi_proj_m.get_value(q));
1296 * phi_p.submit_value(-coeff*scalar_product(avg_u_star_star, n_plus), q);
1297 * phi_m.submit_value(coeff*scalar_product(avg_u_star_star, n_plus), q);
1307 * The following function assembles rhs boundary term
for the pressure
1313 *
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>
1314 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1317 *
const std::vector<Vec>& src,
1318 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1323 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0/(gamma*dt) : 1.0/((1.0 -
gamma)*dt);
1326 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1327 * phi_proj.reinit(face);
1332 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1333 *
const auto& n_plus = phi.get_normal_vector(q);
1335 * phi.submit_value(-coeff*scalar_product(phi_proj.get_value(q), n_plus), q);
1344 * Put together all the previous steps
for pressure
1350 *
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>
1351 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1352 * vmult_rhs_pressure(Vec& dst,
const std::vector<Vec>& src)
const {
1353 *
for(
auto& vec : src)
1354 * vec.update_ghost_values();
1356 * this->data->
loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_pressure,
1357 * &NavierStokesProjectionOperator::assemble_rhs_face_term_pressure,
1358 * &NavierStokesProjectionOperator::assemble_rhs_boundary_term_pressure,
1359 *
this, dst, src,
true,
1367 * Now we need to build the
'matrices', i.e. the bilinear forms. We start by
1368 * assembling the cell term
for the velocity
1374 *
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>
1375 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1379 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1380 *
if(TR_BDF2_stage == 1) {
1384 * phi_old_extr(
data, 0);
1387 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1390 * phi_old_extr.reinit(cell);
1394 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1395 *
const auto& u_int = phi.get_value(q);
1396 *
const auto& grad_u_int = phi.get_gradient(q);
1397 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1398 *
const auto& tensor_product_u_int =
outer_product(u_int, u_n_gamma_ov_2);
1400 * phi.submit_value(1.0/(gamma*dt)*u_int, q);
1401 * phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1409 * phi_int_extr(
data, 0);
1412 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1415 * phi_int_extr.reinit(cell);
1419 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1420 *
const auto& u_curr = phi.get_value(q);
1421 *
const auto& grad_u_curr = phi.get_gradient(q);
1422 *
const auto& u_n1_int = phi_int_extr.get_value(q);
1423 *
const auto& tensor_product_u_curr =
outer_product(u_curr, u_n1_int);
1425 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
1426 * phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
1436 * The following function assembles face term
for the velocity
1442 *
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>
1443 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1447 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1448 *
if(TR_BDF2_stage == 1) {
1451 * phi_m(
data,
false, 0),
1452 * phi_old_extr_p(
data,
true, 0),
1453 * phi_old_extr_m(
data,
false, 0);
1456 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1457 * phi_p.reinit(face);
1459 * phi_m.reinit(face);
1461 * phi_old_extr_p.reinit(face);
1463 * phi_old_extr_m.reinit(face);
1466 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1467 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1470 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1471 *
const auto& n_plus = phi_p.get_normal_vector(q);
1473 *
const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1474 *
const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
1475 *
const auto& avg_tensor_product_u_int = 0.5*(
outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
1476 *
outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
1478 *
std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
1480 * phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
1481 * a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int, q);
1482 * phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
1483 * a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
1484 * phi_p.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1485 * phi_m.submit_normal_derivative(-theta_v*a22/Re*0.5*jump_u_int, q);
1494 * phi_m(
data,
false, 0),
1495 * phi_extr_p(
data,
true, 0),
1496 * phi_extr_m(
data,
false, 0);
1499 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1500 * phi_p.reinit(face);
1502 * phi_m.reinit(face);
1504 * phi_extr_p.reinit(face);
1506 * phi_extr_m.reinit(face);
1509 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1510 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1513 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1514 *
const auto& n_plus = phi_p.get_normal_vector(q);
1516 *
const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1517 *
const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
1518 *
const auto& avg_tensor_product_u = 0.5*(
outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
1519 *
outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
1521 *
std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
1523 * phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
1524 * a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
1525 * phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
1526 * a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
1527 * phi_p.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1528 * phi_m.submit_normal_derivative(-theta_v*a33/Re*0.5*jump_u, q);
1539 * The following function assembles boundary term
for the velocity
1545 *
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>
1546 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1550 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1551 *
if(TR_BDF2_stage == 1) {
1554 * phi_old_extr(
data,
true, 0);
1557 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1560 * phi_old_extr.reinit(face);
1564 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1568 *
if(boundary_id != 1) {
1569 *
const double coef_trasp = 0.0;
1572 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1573 *
const auto& n_plus = phi.get_normal_vector(q);
1574 *
const auto& grad_u_int = phi.get_gradient(q);
1575 *
const auto& u_int = phi.get_value(q);
1576 *
const auto& tensor_product_u_int =
outer_product(phi.get_value(q), phi_old_extr.get_value(q));
1577 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1579 * phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
1580 * a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
1581 * phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
1587 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1588 *
const auto& n_plus = phi.get_normal_vector(q);
1589 *
const auto& grad_u_int = phi.get_gradient(q);
1590 *
const auto& u_int = phi.get_value(q);
1591 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
1593 *
const auto& point_vectorized = phi.quadrature_point(q);
1594 *
auto u_int_m = u_int;
1595 *
auto grad_u_int_m = grad_u_int;
1596 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1598 *
for(
unsigned int d = 0;
d < dim; ++
d)
1599 * point[d] = point_vectorized[d][v];
1601 * u_int_m[1][v] = -u_int_m[1][v];
1603 * grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
1604 * grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
1607 * phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
1608 * a22*
outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
1609 * a22*0.5*
lambda*(u_int - u_int_m), q);
1610 * phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
1619 * phi_extr(
data,
true, 0);
1622 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1625 * phi_extr.reinit(face);
1629 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
1631 *
if(boundary_id != 1) {
1632 *
const double coef_trasp = 0.0;
1635 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1636 *
const auto& n_plus = phi.get_normal_vector(q);
1637 *
const auto& grad_u = phi.get_gradient(q);
1638 *
const auto& u = phi.get_value(q);
1639 *
const auto& tensor_product_u =
outer_product(phi.get_value(q), phi_extr.get_value(q));
1640 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1642 * phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
1643 * a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
1644 * phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
1650 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1651 *
const auto& n_plus = phi.get_normal_vector(q);
1652 *
const auto& grad_u = phi.get_gradient(q);
1653 *
const auto& u = phi.get_value(q);
1654 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
1656 *
const auto& point_vectorized = phi.quadrature_point(q);
1658 *
auto grad_u_m = grad_u;
1659 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
1661 *
for(
unsigned int d = 0;
d < dim; ++
d)
1662 * point[d] = point_vectorized[d][v];
1664 * u_m[1][v] = -u_m[1][v];
1666 * grad_u_m[0][0][v] = -grad_u_m[0][0][v];
1667 * grad_u_m[0][1][v] = -grad_u_m[0][1][v];
1670 * phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
1671 * a33*
outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus + a33*0.5*
lambda*(u - u_m), q);
1672 * phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
1683 * Next, we focus on
'matrices' to compute the pressure. We
first assemble cell term
for the pressure
1689 *
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>
1690 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1694 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1698 *
const double coeff = (TR_BDF2_stage == 1) ? 1.0e6*gamma*dt*gamma*dt : 1.0e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
1701 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1706 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1707 * phi.submit_gradient(phi.get_gradient(q), q);
1708 * phi.submit_value(1.0/coeff*phi.get_value(q), q);
1718 * The following function assembles face term
for the pressure
1724 *
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>
1725 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1729 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1732 * phi_m(
data,
false, 1, 1);
1735 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1736 * phi_p.reinit(face);
1738 * phi_m.reinit(face);
1741 *
const auto coef_jump = C_p*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
1742 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
1745 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
1746 *
const auto& n_plus = phi_p.get_normal_vector(q);
1748 *
const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
1749 *
const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
1751 * phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
1752 * phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
1753 * phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1754 * phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
1764 * The following function assembles boundary term
for the pressure
1770 *
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>
1771 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1775 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
1778 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
1781 *
if(boundary_id == 1) {
1785 *
const auto coef_jump = C_p*
std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
1787 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1788 *
const auto& n_plus = phi.get_normal_vector(q);
1790 *
const auto& grad_pres = phi.get_gradient(q);
1791 *
const auto& pres = phi.get_value(q);
1793 * phi.submit_value(-scalar_product(grad_pres, n_plus) + coef_jump*pres , q);
1794 * phi.submit_normal_derivative(-theta_p*pres, q);
1804 * Before coding the
'apply_add' function, which is the one that will perform the
loop, we focus on
1805 * the linear system that arises to
project the
gradient of the pressure into the velocity space.
1806 * The following function assembles rhs cell term
for the projection of
gradient of pressure. Since no
1807 * integration by parts is performed, only a cell term contribution is present.
1813 *
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>
1814 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1818 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1824 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1825 * phi_pres.reinit(cell);
1830 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q)
1831 * phi.submit_value(phi_pres.get_gradient(q), q);
1840 * Put together all the previous steps
for projection of pressure
gradient. Here we
loop only over cells
1846 *
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>
1847 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1848 * vmult_grad_p_projection(Vec& dst,
const Vec& src)
const {
1849 * this->data->
cell_loop(&NavierStokesProjectionOperator::assemble_rhs_cell_term_projection_grad_p,
1850 *
this, dst, src,
true);
1856 * Assemble now cell term
for the projection of
gradient of pressure. This is
nothing but a mass
matrix
1862 *
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>
1863 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1867 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1871 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1876 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q)
1877 * phi.submit_value(phi.get_value(q), q);
1886 * Put together all previous steps. This is the overridden function that effectively performs the
1887 *
matrix-vector multiplication.
1893 *
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>
1894 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1895 * apply_add(Vec& dst,
const Vec& src)
const {
1896 *
if(NS_stage == 1) {
1897 * this->data->
loop(&NavierStokesProjectionOperator::assemble_cell_term_velocity,
1898 * &NavierStokesProjectionOperator::assemble_face_term_velocity,
1899 * &NavierStokesProjectionOperator::assemble_boundary_term_velocity,
1900 *
this, dst, src,
false,
1904 *
else if(NS_stage == 2) {
1905 * this->data->
loop(&NavierStokesProjectionOperator::assemble_cell_term_pressure,
1906 * &NavierStokesProjectionOperator::assemble_face_term_pressure,
1907 * &NavierStokesProjectionOperator::assemble_boundary_term_pressure,
1908 *
this, dst, src,
false,
1912 *
else if(NS_stage == 3) {
1913 * this->data->
cell_loop(&NavierStokesProjectionOperator::assemble_cell_term_projection_grad_p,
1914 *
this, dst, src,
false);
1917 *
Assert(
false, ExcNotImplemented());
1923 * Finally, we focus on computing the
diagonal for preconditioners and we start by assembling
1924 * the
diagonal cell term
for the velocity. Since we
do not have access to the entries of the
matrix,
1925 * in order to compute the element i, we test the
matrix against a vector which is
equal to 1 in position i and 0 elsewhere.
1926 * This is why
'src' will result as unused.
1932 *
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>
1933 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
1936 *
const unsigned int& ,
1937 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
1938 *
if(TR_BDF2_stage == 1) {
1940 * phi_old_extr(
data, 0);
1946 *
for(
unsigned int d = 0;
d < dim; ++
d)
1947 * tmp[d] = make_vectorized_array<Number>(1.0);
1950 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1951 * phi_old_extr.reinit(cell);
1956 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1957 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
1959 * phi.submit_dof_value(tmp, i);
1963 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
1964 *
const auto& u_int = phi.get_value(q);
1965 *
const auto& grad_u_int = phi.get_gradient(q);
1966 *
const auto& u_n_gamma_ov_2 = phi_old_extr.get_value(q);
1967 *
const auto& tensor_product_u_int =
outer_product(u_int, u_n_gamma_ov_2);
1969 * phi.submit_value(1.0/(gamma*dt)*u_int, q);
1970 * phi.submit_gradient(-a22*tensor_product_u_int + a22/Re*grad_u_int, q);
1973 *
diagonal[i] = phi.get_dof_value(i);
1975 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
1976 * phi.submit_dof_value(diagonal[i], i);
1977 * phi.distribute_local_to_global(dst);
1982 * phi_int_extr(
data, 0);
1986 *
for(
unsigned int d = 0;
d < dim; ++
d)
1987 * tmp[d] = make_vectorized_array<Number>(1.0);
1990 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
1991 * phi_int_extr.reinit(cell);
1996 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
1997 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
1999 * phi.submit_dof_value(tmp, i);
2003 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2004 *
const auto& u_curr = phi.get_value(q);
2005 *
const auto& grad_u_curr = phi.get_gradient(q);
2006 *
const auto& u_n1_int = phi_int_extr.get_value(q);
2007 *
const auto& tensor_product_u_curr =
outer_product(u_curr, u_n1_int);
2009 * phi.submit_value(1.0/((1.0 - gamma)*dt)*u_curr, q);
2010 * phi.submit_gradient(-a33*tensor_product_u_curr + a33/Re*grad_u_curr, q);
2013 *
diagonal[i] = phi.get_dof_value(i);
2015 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2016 * phi.submit_dof_value(diagonal[i], i);
2017 * phi.distribute_local_to_global(dst);
2025 * The following function assembles
diagonal face term
for the velocity
2031 *
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>
2032 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2035 *
const unsigned int& ,
2036 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2037 *
if(TR_BDF2_stage == 1) {
2039 * phi_m(
data,
false, 0),
2040 * phi_old_extr_p(
data,
true, 0),
2041 * phi_old_extr_m(
data,
false, 0);
2043 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2047 * diagonal_m(phi_m.dofs_per_component);
2050 *
for(
unsigned int d = 0;
d < dim; ++
d)
2051 * tmp[d] = make_vectorized_array<Number>(1.0);
2054 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2055 * phi_old_extr_p.reinit(face);
2057 * phi_old_extr_m.reinit(face);
2059 * phi_p.reinit(face);
2060 * phi_m.reinit(face);
2062 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2063 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2066 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2067 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2071 * phi_p.submit_dof_value(tmp, i);
2073 * phi_m.submit_dof_value(tmp, i);
2077 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2078 *
const auto& n_plus = phi_p.get_normal_vector(q);
2079 *
const auto& avg_grad_u_int = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2080 *
const auto& jump_u_int = phi_p.get_value(q) - phi_m.get_value(q);
2081 *
const auto& avg_tensor_product_u_int = 0.5*(
outer_product(phi_p.get_value(q), phi_old_extr_p.get_value(q)) +
2082 *
outer_product(phi_m.get_value(q), phi_old_extr_m.get_value(q)));
2084 *
std::abs(scalar_product(phi_old_extr_m.get_value(q), n_plus)));
2086 * phi_p.submit_value(a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) +
2087 * a22*avg_tensor_product_u_int*n_plus + 0.5*a22*lambda*jump_u_int , q);
2088 * phi_m.submit_value(-a22/Re*(-avg_grad_u_int*n_plus + coef_jump*jump_u_int) -
2089 * a22*avg_tensor_product_u_int*n_plus - 0.5*a22*lambda*jump_u_int, q);
2090 * phi_p.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2091 * phi_m.submit_normal_derivative(-theta_v*0.5*a22/Re*jump_u_int, q);
2094 * diagonal_p[i] = phi_p.get_dof_value(i);
2096 * diagonal_m[i] = phi_m.get_dof_value(i);
2098 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2099 * phi_p.submit_dof_value(diagonal_p[i], i);
2100 * phi_m.submit_dof_value(diagonal_m[i], i);
2102 * phi_p.distribute_local_to_global(dst);
2103 * phi_m.distribute_local_to_global(dst);
2108 * phi_m(
data,
false, 0),
2109 * phi_extr_p(
data,
true, 0),
2110 * phi_extr_m(
data,
false, 0);
2112 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2114 * diagonal_m(phi_m.dofs_per_component);
2116 *
for(
unsigned int d = 0;
d < dim; ++
d)
2117 * tmp[d] = make_vectorized_array<Number>(1.0);
2120 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2121 * phi_extr_p.reinit(face);
2123 * phi_extr_m.reinit(face);
2125 * phi_p.reinit(face);
2126 * phi_m.reinit(face);
2128 *
const auto coef_jump = C_u*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2129 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2132 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2133 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2137 * phi_p.submit_dof_value(tmp, i);
2139 * phi_m.submit_dof_value(tmp, i);
2143 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2144 *
const auto& n_plus = phi_p.get_normal_vector(q);
2145 *
const auto& avg_grad_u = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2146 *
const auto& jump_u = phi_p.get_value(q) - phi_m.get_value(q);
2147 *
const auto& avg_tensor_product_u = 0.5*(
outer_product(phi_p.get_value(q), phi_extr_p.get_value(q)) +
2148 *
outer_product(phi_m.get_value(q), phi_extr_m.get_value(q)));
2150 *
std::abs(scalar_product(phi_extr_m.get_value(q), n_plus)));
2152 * phi_p.submit_value(a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) +
2153 * a33*avg_tensor_product_u*n_plus + 0.5*a33*lambda*jump_u, q);
2154 * phi_m.submit_value(-a33/Re*(-avg_grad_u*n_plus + coef_jump*jump_u) -
2155 * a33*avg_tensor_product_u*n_plus - 0.5*a33*lambda*jump_u, q);
2156 * phi_p.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2157 * phi_m.submit_normal_derivative(-theta_v*0.5*a33/Re*jump_u, q);
2160 * diagonal_p[i] = phi_p.get_dof_value(i);
2162 * diagonal_m[i] = phi_m.get_dof_value(i);
2164 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2165 * phi_p.submit_dof_value(diagonal_p[i], i);
2166 * phi_m.submit_dof_value(diagonal_m[i], i);
2168 * phi_p.distribute_local_to_global(dst);
2169 * phi_m.distribute_local_to_global(dst);
2177 * The following function assembles boundary term
for the velocity
2183 *
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>
2184 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2187 *
const unsigned int& ,
2188 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2189 *
if(TR_BDF2_stage == 1) {
2191 * phi_old_extr(
data,
true, 0);
2195 *
for(
unsigned int d = 0;
d < dim; ++
d)
2196 * tmp[d] = make_vectorized_array<Number>(1.0);
2199 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2200 * phi_old_extr.reinit(face);
2205 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2207 *
if(boundary_id != 1) {
2208 *
const double coef_trasp = 0.0;
2211 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2212 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2214 * phi.submit_dof_value(tmp, i);
2218 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2219 *
const auto& n_plus = phi.get_normal_vector(q);
2220 *
const auto& grad_u_int = phi.get_gradient(q);
2221 *
const auto& u_int = phi.get_value(q);
2222 *
const auto& tensor_product_u_int =
outer_product(phi.get_value(q), phi_old_extr.get_value(q));
2223 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2225 * phi.submit_value(a22/Re*(-grad_u_int*n_plus + 2.0*coef_jump*u_int) +
2226 * a22*coef_trasp*tensor_product_u_int*n_plus + a22*lambda*u_int, q);
2227 * phi.submit_normal_derivative(-theta_v*a22/Re*u_int, q);
2230 *
diagonal[i] = phi.get_dof_value(i);
2232 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2233 * phi.submit_dof_value(diagonal[i], i);
2234 * phi.distribute_local_to_global(dst);
2238 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2239 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2241 * phi.submit_dof_value(tmp, i);
2245 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2246 *
const auto& n_plus = phi.get_normal_vector(q);
2247 *
const auto& grad_u_int = phi.get_gradient(q);
2248 *
const auto& u_int = phi.get_value(q);
2249 *
const auto&
lambda =
std::abs(scalar_product(phi_old_extr.get_value(q), n_plus));
2251 *
const auto& point_vectorized = phi.quadrature_point(q);
2252 *
auto u_int_m = u_int;
2253 *
auto grad_u_int_m = grad_u_int;
2254 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2256 *
for(
unsigned int d = 0;
d < dim; ++
d)
2257 * point[d] = point_vectorized[d][v];
2259 * u_int_m[1][v] = -u_int_m[1][v];
2261 * grad_u_int_m[0][0][v] = -grad_u_int_m[0][0][v];
2262 * grad_u_int_m[0][1][v] = -grad_u_int_m[0][1][v];
2265 * phi.submit_value(a22/Re*(-(0.5*(grad_u_int + grad_u_int_m))*n_plus + coef_jump*(u_int - u_int_m)) +
2266 * a22*
outer_product(0.5*(u_int + u_int_m), phi_old_extr.get_value(q))*n_plus +
2267 * a22*0.5*
lambda*(u_int - u_int_m), q);
2268 * phi.submit_normal_derivative(-theta_v*a22/Re*(u_int - u_int_m), q);
2271 *
diagonal[i] = phi.get_dof_value(i);
2273 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2274 * phi.submit_dof_value(diagonal[i], i);
2275 * phi.distribute_local_to_global(dst);
2281 * phi_extr(
data,
true, 0);
2285 *
for(
unsigned int d = 0;
d < dim; ++
d)
2286 * tmp[d] = make_vectorized_array<Number>(1.0);
2289 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2290 * phi_extr.reinit(face);
2295 *
const auto coef_jump = C_u*
std::abs((phi.get_normal_vector(0) * phi.inverse_jacobian(0))[dim - 1]);
2297 *
if(boundary_id != 1) {
2298 *
const double coef_trasp = 0.0;
2301 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2302 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2304 * phi.submit_dof_value(tmp, i);
2308 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2309 *
const auto& n_plus = phi.get_normal_vector(q);
2310 *
const auto& grad_u = phi.get_gradient(q);
2311 *
const auto& u = phi.get_value(q);
2312 *
const auto& tensor_product_u =
outer_product(phi.get_value(q), phi_extr.get_value(q));
2313 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2315 * phi.submit_value(a33/Re*(-grad_u*n_plus + 2.0*coef_jump*u) +
2316 * a33*coef_trasp*tensor_product_u*n_plus + a33*lambda*u, q);
2317 * phi.submit_normal_derivative(-theta_v*a33/Re*u, q);
2320 *
diagonal[i] = phi.get_dof_value(i);
2322 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2323 * phi.submit_dof_value(diagonal[i], i);
2324 * phi.distribute_local_to_global(dst);
2328 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2329 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2331 * phi.submit_dof_value(tmp, i);
2335 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2336 *
const auto& n_plus = phi.get_normal_vector(q);
2337 *
const auto& grad_u = phi.get_gradient(q);
2338 *
const auto& u = phi.get_value(q);
2339 *
const auto&
lambda =
std::abs(scalar_product(phi_extr.get_value(q), n_plus));
2341 *
const auto& point_vectorized = phi.quadrature_point(q);
2343 *
auto grad_u_m = grad_u;
2344 *
for(
unsigned int v = 0; v < VectorizedArray<Number>::size(); ++v) {
2346 *
for(
unsigned int d = 0;
d < dim; ++
d)
2347 * point[d] = point_vectorized[d][v];
2349 * u_m[1][v] = -u_m[1][v];
2351 * grad_u_m[0][0][v] = -grad_u_m[0][0][v];
2352 * grad_u_m[0][1][v] = -grad_u_m[0][1][v];
2355 * phi.submit_value(a33/Re*(-(0.5*(grad_u + grad_u_m))*n_plus + coef_jump*(u - u_m)) +
2356 * a33*
outer_product(0.5*(u + u_m), phi_extr.get_value(q))*n_plus +
2357 * a33*0.5*
lambda*(u - u_m), q);
2358 * phi.submit_normal_derivative(-theta_v*a33/Re*(u - u_m), q);
2361 *
diagonal[i] = phi.get_dof_value(i);
2363 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2364 * phi.submit_dof_value(diagonal[i], i);
2365 * phi.distribute_local_to_global(dst);
2374 * Now we consider the pressure related bilinear forms. We
first assemble diagonal cell term
for the pressure
2380 *
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>
2381 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2384 *
const unsigned int& ,
2385 *
const std::pair<unsigned int, unsigned int>& cell_range)
const {
2392 *
const double coeff = (TR_BDF2_stage == 1) ? 1e6*gamma*dt*gamma*dt : 1e6*(1.0 -
gamma)*dt*(1.0 -
gamma)*dt;
2395 *
for(
unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) {
2399 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2400 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2402 * phi.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2408 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2409 * phi.submit_value(1.0/coeff*phi.get_value(q), q);
2410 * phi.submit_gradient(phi.get_gradient(q), q);
2413 *
diagonal[i] = phi.get_dof_value(i);
2415 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2416 * phi.submit_dof_value(diagonal[i], i);
2418 * phi.distribute_local_to_global(dst);
2425 * The following function assembles
diagonal face term
for the pressure
2431 *
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>
2432 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2435 *
const unsigned int& ,
2436 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2438 * phi_m(
data,
false, 1, 1);
2440 *
AssertDimension(phi_p.dofs_per_component, phi_m.dofs_per_component);
2442 * diagonal_m(phi_m.dofs_per_component);
2447 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2448 * phi_p.reinit(face);
2449 * phi_m.reinit(face);
2451 *
const auto coef_jump = C_p*0.5*(
std::abs((phi_p.get_normal_vector(0)*phi_p.inverse_jacobian(0))[dim - 1]) +
2452 *
std::abs((phi_m.get_normal_vector(0)*phi_m.inverse_jacobian(0))[dim - 1]));
2455 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2456 *
for(
unsigned int j = 0; j < phi_p.dofs_per_component; ++j) {
2460 * phi_p.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2461 * phi_m.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2466 *
for(
unsigned int q = 0; q < phi_p.n_q_points; ++q) {
2467 *
const auto& n_plus = phi_p.get_normal_vector(q);
2469 *
const auto& avg_grad_pres = 0.5*(phi_p.get_gradient(q) + phi_m.get_gradient(q));
2470 *
const auto& jump_pres = phi_p.get_value(q) - phi_m.get_value(q);
2472 * phi_p.submit_value(-scalar_product(avg_grad_pres, n_plus) + coef_jump*jump_pres, q);
2473 * phi_m.submit_value(scalar_product(avg_grad_pres, n_plus) - coef_jump*jump_pres, q);
2474 * phi_p.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2475 * phi_m.submit_gradient(-theta_p*0.5*jump_pres*n_plus, q);
2478 * diagonal_p[i] = phi_p.get_dof_value(i);
2480 * diagonal_m[i] = phi_m.get_dof_value(i);
2482 *
for(
unsigned int i = 0; i < phi_p.dofs_per_component; ++i) {
2483 * phi_p.submit_dof_value(diagonal_p[i], i);
2484 * phi_m.submit_dof_value(diagonal_m[i], i);
2486 * phi_p.distribute_local_to_global(dst);
2487 * phi_m.distribute_local_to_global(dst);
2500 *
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>
2501 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2504 *
const unsigned int& ,
2505 *
const std::pair<unsigned int, unsigned int>& face_range)
const {
2510 *
for(
unsigned int face = face_range.first; face < face_range.second; ++face) {
2513 *
if(boundary_id == 1) {
2516 *
const auto coef_jump = C_p*
std::abs((phi.get_normal_vector(0)*phi.inverse_jacobian(0))[dim - 1]);
2518 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i) {
2519 *
for(
unsigned int j = 0; j < phi.dofs_per_component; ++j)
2521 * phi.submit_dof_value(make_vectorized_array<Number>(1.0), i);
2524 *
for(
unsigned int q = 0; q < phi.n_q_points; ++q) {
2525 *
const auto& n_plus = phi.get_normal_vector(q);
2527 *
const auto& grad_pres = phi.get_gradient(q);
2528 *
const auto& pres = phi.get_value(q);
2530 * phi.submit_value(-scalar_product(grad_pres, n_plus) + 2.0*coef_jump*pres , q);
2531 * phi.submit_normal_derivative(-theta_p*pres, q);
2534 *
diagonal[i] = phi.get_dof_value(i);
2536 *
for(
unsigned int i = 0; i < phi.dofs_per_component; ++i)
2537 * phi.submit_dof_value(diagonal[i], i);
2538 * phi.distribute_local_to_global(dst);
2546 * Put together all previous steps. We create a dummy auxliary vector that serves
for the src input argument in
2547 * the previous
functions that as we have seen before is unused. Then everything is done by the
'loop' function
2548 * and it is saved in the field
'inverse_diagonal_entries' already present in the base
class. Anyway since there is
2549 * only one field, we need to resize properly depending on whether we are considering the velocity or the pressure.
2555 *
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>
2556 *
void NavierStokesProjectionOperator<dim, fe_degree_p, fe_degree_v, n_q_points_1d_p, n_q_points_1d_v, Vec>::
2558 *
Assert(NS_stage == 1 || NS_stage == 2, ExcInternalError());
2561 *
auto& inverse_diagonal = this->inverse_diagonal_entries->get_vector();
2563 *
if(NS_stage == 1) {
2564 * ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2567 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& cell_range) {
2568 * (this->assemble_diagonal_cell_term_velocity)(
data, dst, src, cell_range);
2570 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& face_range) {
2571 * (this->assemble_diagonal_face_term_velocity)(
data, dst, src, face_range);
2573 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& boundary_range) {
2574 * (this->assemble_diagonal_boundary_term_velocity)(
data, dst, src, boundary_range);
2578 *
else if(NS_stage == 2) {
2579 * ::MatrixFreeTools::compute_diagonal<dim, Number, VectorizedArray<Number>>
2582 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& cell_range) {
2583 * (this->assemble_diagonal_cell_term_pressure)(
data, dst, src, cell_range);
2585 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& face_range) {
2586 * (this->assemble_diagonal_face_term_pressure)(
data, dst, src, face_range);
2588 * [&](
const auto&
data,
auto& dst,
const auto& src,
const auto& boundary_range) {
2589 * (this->assemble_diagonal_boundary_term_pressure)(
data, dst, src, boundary_range);
2594 *
for(
unsigned int i = 0; i < inverse_diagonal.locally_owned_size(); ++i) {
2595 *
Assert(inverse_diagonal.local_element(i) != 0.0,
2596 * ExcMessage(
"No diagonal entry in a definite operator should be zero"));
2597 * inverse_diagonal.local_element(i) = 1.0/inverse_diagonal.local_element(i);
2605 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
2606 * @sect{The <code>NavierStokesProjection</code>
class}
2610 * Now we are ready
for the main
class of the program. It
implements the calls to the various steps
2611 * of the projection method for Navier-Stokes equations.
2618 * class NavierStokesProjection {
2620 * NavierStokesProjection(RunTimeParameters::Data_Storage&
data);
2622 *
void run(
const bool verbose =
false,
const unsigned int output_interval = 10);
2627 *
const double gamma;
2628 *
unsigned int TR_BDF2_stage;
2632 * EquationData::Velocity<dim> vel_init;
2633 * EquationData::Pressure<dim> pres_init;
2671 * <<
" The time step " << arg1 <<
" is out of range."
2673 * <<
" The permitted range is (0," << arg2 <<
"]");
2677 *
void setup_dofs();
2679 *
void initialize();
2681 *
void interpolate_velocity();
2683 *
void diffusion_step();
2685 *
void projection_step();
2687 *
void project_grad(
const unsigned int flag);
2689 *
double get_maximal_velocity();
2691 *
double get_maximal_difference_velocity();
2693 *
void output_results(
const unsigned int step);
2695 *
void refine_mesh();
2697 *
void interpolate_max_res(
const unsigned int level);
2699 *
void save_max_res();
2702 *
void compute_lift_and_drag();
2705 * std::shared_ptr<MatrixFree<dim, double>> matrix_free_storage;
2708 * NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2709 * EquationData::degree_p + 1, EquationData::degree_p + 2,
2713 *
MGLevelObject<NavierStokesProjectionOperator<dim, EquationData::degree_p, EquationData::degree_p + 1,
2714 * EquationData::degree_p + 1, EquationData::degree_p + 2,
2723 * constraints_pressure;
2726 *
unsigned int max_its;
2729 *
unsigned int max_loc_refinements;
2730 *
unsigned int min_loc_refinements;
2731 *
unsigned int refinement_iterations;
2733 * std::string saving_dir;
2738 * std::ofstream time_out;
2742 * std::ofstream output_n_dofs_velocity;
2743 * std::ofstream output_n_dofs_pressure;
2745 * std::ofstream output_lift;
2746 * std::ofstream output_drag;
2752 * In the constructor, we just read all the
data from the
2753 * <code>Data_Storage</code>
object that is passed as an argument, verify that
2762 * NavierStokesProjection<dim>::NavierStokesProjection(RunTimeParameters::Data_Storage&
data):
2763 * t_0(
data.initial_time),
2764 * T(
data.final_time),
2767 * Re(
data.Reynolds),
2769 * vel_init(
data.initial_time),
2770 * pres_init(
data.initial_time),
2773 * fe_velocity(
FE_DGQ<dim>(EquationData::degree_p + 1), dim),
2774 * fe_pressure(
FE_DGQ<dim>(EquationData::degree_p), 1),
2777 * quadrature_pressure(EquationData::degree_p + 1),
2778 * quadrature_velocity(EquationData::degree_p + 2),
2779 * navier_stokes_matrix(
data),
2780 * max_its(
data.max_iterations),
2782 * max_loc_refinements(
data.max_loc_refinements),
2783 * min_loc_refinements(
data.min_loc_refinements),
2784 * refinement_iterations(
data.refinement_iterations),
2785 * saving_dir(
data.dir),
2787 * time_out(
"./" +
data.dir +
"/time_analysis_" +
2791 * output_n_dofs_velocity(
"./" +
data.dir +
"/n_dofs_velocity.dat",
std::ofstream::out),
2792 * output_n_dofs_pressure(
"./" +
data.dir +
"/n_dofs_pressure.dat",
std::ofstream::out),
2793 * output_lift(
"./" +
data.dir +
"/lift.dat",
std::ofstream::out),
2794 * output_drag(
"./" +
data.dir +
"/drag.dat",
std::ofstream::out) {
2795 *
if(EquationData::degree_p < 1) {
2797 * <<
" WARNING: The chosen pair of finite element spaces is not stable."
2799 * <<
" The obtained results will be nonsense" << std::endl;
2802 *
AssertThrow(!((dt <= 0.0) || (dt > 0.5*T)), ExcInvalidTimeStep(dt, 0.5*T));
2804 * matrix_free_storage = std::make_shared<MatrixFree<dim, double>>();
2814 * The method that creates the
triangulation and refines it the needed number
2822 *
void NavierStokesProjection<dim>::create_triangulation(
const unsigned int n_refines) {
2825 *
GridGenerator::plate_with_a_hole(
triangulation, 0.5, 1.0, 1.0, 1.1, 1.0, 19.0,
Point<2>(2.0, 2.0), 0, 1, 1.0, 2,
true);
2828 * pcout <<
"Number of refines = " << n_refines << std::endl;
2835 * After creating the
triangulation, it creates the mesh dependent
2836 *
data, i.e. it distributes degrees of freedom, and
2837 * initializes the vectors that we will use.
2844 *
void NavierStokesProjection<dim>::setup_dofs() {
2845 * pcout <<
"Number of active cells: " <<
triangulation.n_global_active_cells() << std::endl;
2846 * pcout <<
"Number of levels: " <<
triangulation.n_global_levels() << std::endl;
2849 * dof_handler_velocity.distribute_dofs(fe_velocity);
2850 * dof_handler_pressure.distribute_dofs(fe_pressure);
2852 * pcout <<
"dim (X_h) = " << dof_handler_velocity.n_dofs()
2854 * <<
"dim (M_h) = " << dof_handler_pressure.n_dofs()
2856 * <<
"Re = " << Re << std::endl
2860 * output_n_dofs_velocity << dof_handler_velocity.n_dofs() << std::endl;
2861 * output_n_dofs_pressure << dof_handler_pressure.n_dofs() << std::endl;
2873 * std::vector<const DoFHandler<dim>*> dof_handlers;
2876 * dof_handlers.push_back(&dof_handler_velocity);
2877 * dof_handlers.push_back(&dof_handler_pressure);
2879 * constraints_velocity.
clear();
2880 * constraints_velocity.close();
2881 * constraints_pressure.clear();
2882 * constraints_pressure.close();
2883 * std::vector<const AffineConstraints<double>*> constraints;
2884 * constraints.push_back(&constraints_velocity);
2885 * constraints.push_back(&constraints_pressure);
2887 * std::vector<QGauss<1>> quadratures;
2891 * quadratures.push_back(
QGauss<1>(EquationData::degree_p + 2));
2892 * quadratures.push_back(
QGauss<1>(EquationData::degree_p + 1));
2896 * matrix_free_storage->reinit(
MappingQ1<dim>(),dof_handlers, constraints, quadratures, additional_data);
2897 * matrix_free_storage->initialize_dof_vector(u_star, 0);
2898 * matrix_free_storage->initialize_dof_vector(rhs_u, 0);
2899 * matrix_free_storage->initialize_dof_vector(u_n, 0);
2900 * matrix_free_storage->initialize_dof_vector(u_extr, 0);
2901 * matrix_free_storage->initialize_dof_vector(u_n_minus_1, 0);
2902 * matrix_free_storage->initialize_dof_vector(u_n_gamma, 0);
2903 * matrix_free_storage->initialize_dof_vector(u_tmp, 0);
2904 * matrix_free_storage->initialize_dof_vector(grad_pres_int, 0);
2906 * matrix_free_storage->initialize_dof_vector(pres_int, 1);
2907 * matrix_free_storage->initialize_dof_vector(pres_n, 1);
2908 * matrix_free_storage->initialize_dof_vector(rhs_p, 1);
2914 * mg_matrices.clear_elements();
2915 * dof_handler_velocity.distribute_mg_dofs();
2916 * dof_handler_pressure.distribute_mg_dofs();
2918 *
const unsigned int nlevels =
triangulation.n_global_levels();
2919 * mg_matrices.resize(0, nlevels - 1);
2928 * std::vector<const DoFHandler<dim>*> dof_handlers_mg;
2929 * dof_handlers_mg.push_back(&dof_handler_velocity);
2930 * dof_handlers_mg.push_back(&dof_handler_pressure);
2931 * std::vector<const AffineConstraints<float>*> constraints_mg;
2933 * constraints_velocity_mg.
clear();
2934 * constraints_velocity_mg.close();
2935 * constraints_mg.push_back(&constraints_velocity_mg);
2937 * constraints_pressure_mg.
clear();
2938 * constraints_pressure_mg.close();
2939 * constraints_mg.push_back(&constraints_pressure_mg);
2942 * mg_mf_storage_level->reinit(
MappingQ1<dim>(),dof_handlers_mg, constraints_mg, quadratures, additional_data_mg);
2943 *
const std::vector<unsigned int> tmp = {1};
2944 * mg_matrices[
level].initialize(mg_mf_storage_level, tmp, tmp);
2945 * mg_matrices[
level].set_dt(dt);
2946 * mg_matrices[
level].set_NS_stage(2);
2949 * Linfty_error_per_cell_vel.reinit(
triangulation.n_active_cells());
2955 * This method loads the
initial data. It simply uses the class <code>Pressure</code> instance
for the pressure
2956 * and the class <code>Velocity</code> instance
for the velocity.
2963 *
void NavierStokesProjection<dim>::initialize() {
2975 * This function computes the extrapolated velocity to be used in the momentum predictor
2982 *
void NavierStokesProjection<dim>::interpolate_velocity() {
2987 * --- TR-BDF2
first step
2990 *
if(TR_BDF2_stage == 1) {
2991 * u_extr.equ(1.0 + gamma/(2.0*(1.0 - gamma)), u_n);
2992 * u_tmp.equ(gamma/(2.0*(1.0 - gamma)), u_n_minus_1);
2997 * --- TR-BDF2
second step
3001 * u_extr.equ(1.0 + (1.0 - gamma)/gamma, u_n_gamma);
3002 * u_tmp.equ((1.0 - gamma)/gamma, u_n);
3010 * We are
finally ready to solve the diffusion step.
3017 *
void NavierStokesProjection<dim>::diffusion_step() {
3022 *
const std::vector<unsigned int> tmp = {0};
3023 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3026 * navier_stokes_matrix.set_NS_stage(1);
3031 *
if(TR_BDF2_stage == 1) {
3032 * navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_extr, pres_n});
3033 * navier_stokes_matrix.set_u_extr(u_extr);
3037 * navier_stokes_matrix.vmult_rhs_velocity(rhs_u, {u_n, u_n_gamma, pres_int, u_extr});
3038 * navier_stokes_matrix.set_u_extr(u_extr);
3043 *
SolverControl solver_control(max_its, eps*rhs_u.l2_norm());
3048 * EquationData::degree_p,
3049 * EquationData::degree_p + 1,
3050 * EquationData::degree_p + 1,
3051 * EquationData::degree_p + 2,
3053 * navier_stokes_matrix.compute_diagonal();
3054 * preconditioner.initialize(navier_stokes_matrix);
3056 * gmres.solve(navier_stokes_matrix, u_star, rhs_u, preconditioner);
3062 * Next, we solve the projection step.
3069 *
void NavierStokesProjection<dim>::projection_step() {
3074 *
const std::vector<unsigned int> tmp = {1};
3075 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3077 * navier_stokes_matrix.set_NS_stage(2);
3079 *
if(TR_BDF2_stage == 1)
3080 * navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_n});
3082 * navier_stokes_matrix.vmult_rhs_pressure(rhs_p, {u_star, pres_int});
3085 *
SolverControl solver_control(max_its, eps*rhs_p.l2_norm());
3090 * mg_transfer.
build(dof_handler_pressure);
3093 * EquationData::degree_p,
3094 * EquationData::degree_p + 1,
3095 * EquationData::degree_p + 1,
3096 * EquationData::degree_p + 2,
3104 * smoother_data[
level].smoothing_range = 15.0;
3105 * smoother_data[
level].degree = 3;
3106 * smoother_data[
level].eig_cg_n_iterations = 10;
3109 * smoother_data[0].smoothing_range = 2
e-2;
3111 * smoother_data[0].eig_cg_n_iterations = mg_matrices[0].m();
3113 * mg_matrices[
level].compute_diagonal();
3114 * smoother_data[
level].preconditioner = mg_matrices[
level].get_matrix_diagonal_inverse();
3116 * mg_smoother.initialize(mg_matrices, smoother_data);
3122 * NavierStokesProjectionOperator<dim,
3123 * EquationData::degree_p,
3124 * EquationData::degree_p + 1,
3125 * EquationData::degree_p + 1,
3126 * EquationData::degree_p + 2,
3139 *
if(TR_BDF2_stage == 1) {
3140 * pres_int = pres_n;
3141 * cg.solve(navier_stokes_matrix, pres_int, rhs_p, preconditioner);
3144 * pres_n = pres_int;
3145 * cg.solve(navier_stokes_matrix, pres_n, rhs_p, preconditioner);
3152 * This implements the projection step
for the
gradient of pressure
3159 *
void NavierStokesProjection<dim>::project_grad(
const unsigned int flag) {
3164 *
Assert(flag > 0, ExcInternalError());
3167 *
const std::vector<unsigned int> tmp = {0};
3168 * navier_stokes_matrix.initialize(matrix_free_storage, tmp, tmp);
3171 * navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_n);
3172 *
else if(flag == 2)
3173 * navier_stokes_matrix.vmult_grad_p_projection(rhs_u, pres_int);
3176 * navier_stokes_matrix.set_NS_stage(3);
3179 *
SolverControl solver_control(max_its, 1e-12*rhs_u.l2_norm());
3187 * The following function is used in determining the maximal velocity
3188 * in order to compute the Courant number.
3195 *
double NavierStokesProjection<dim>::get_maximal_velocity() {
3196 *
return u_n.linfty_norm();
3202 * The following function is used in determining the maximal nodal difference
3203 * between old and current velocity
value in order to see
if we have reached steady-state.
3210 *
double NavierStokesProjection<dim>::get_maximal_difference_velocity() {
3212 * u_tmp -= u_n_minus_1;
3214 *
return u_tmp.linfty_norm();
3220 * This method plots the current solution. The main difficulty is that we want
3221 * to create a single output file that contains the
data for all velocity
3222 * components and the pressure. On the other hand, velocities and the pressure
3223 * live on separate
DoFHandler objects, so we need to pay attention when we use
3224 *
'add_data_vector' to select the proper space.
3231 *
void NavierStokesProjection<dim>::output_results(
const unsigned int step) {
3236 * std::vector<std::string> velocity_names(dim,
"v");
3237 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
3239 * u_n.update_ghost_values();
3240 * data_out.add_data_vector(dof_handler_velocity, u_n, velocity_names, component_interpretation_velocity);
3241 * pres_n.update_ghost_values();
3244 * std::vector<std::string> velocity_names_old(dim,
"v_old");
3245 * u_n_minus_1.update_ghost_values();
3246 * data_out.add_data_vector(dof_handler_velocity, u_n_minus_1, velocity_names_old, component_interpretation_velocity);
3249 * PostprocessorVorticity<dim> postprocessor;
3250 * data_out.add_data_vector(dof_handler_velocity, u_n, postprocessor);
3255 * data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3262 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3263 * @sect{<code>NavierStokesProjection::compute_lift_and_drag</code>}
3267 * This routine computes the lift and the drag forces in a non-dimensional framework
3268 * (so basically
for the classical coefficients, it is necessary to multiply by a factor 2).
3275 *
void NavierStokesProjection<dim>::compute_lift_and_drag() {
3276 *
QGauss<dim - 1> face_quadrature_formula(EquationData::degree_p + 2);
3277 *
const int n_q_points = face_quadrature_formula.size();
3279 * std::vector<double> pressure_values(n_q_points);
3280 * std::vector<std::vector<Tensor<1, dim>>> velocity_gradients(n_q_points, std::vector<
Tensor<1, dim>>(dim));
3289 *
FEFaceValues<dim> fe_face_values_velocity(fe_velocity, face_quadrature_formula,
3294 *
double local_drag = 0.0;
3295 *
double local_lift = 0.0;
3301 *
auto tmp_cell = dof_handler_pressure.begin_active();
3302 *
for(
const auto& cell : dof_handler_velocity.active_cell_iterators()) {
3303 *
if(cell->is_locally_owned()) {
3304 *
for(
unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face) {
3305 *
if(cell->face(face)->at_boundary() && cell->face(face)->boundary_id() == 4) {
3306 * fe_face_values_velocity.reinit(cell, face);
3307 * fe_face_values_pressure.reinit(tmp_cell, face);
3309 * fe_face_values_velocity.get_function_gradients(u_n, velocity_gradients);
3310 * fe_face_values_pressure.get_function_values(pres_n, pressure_values);
3312 *
for(
int q = 0; q < n_q_points; q++) {
3313 * normal_vector = -fe_face_values_velocity.normal_vector(q);
3315 *
for(
unsigned int d = 0;
d < dim; ++
d) {
3316 * fluid_pressure[
d][
d] = pressure_values[q];
3317 *
for(
unsigned int k = 0; k < dim; ++k)
3318 * fluid_stress[d][k] = 1.0/Re*velocity_gradients[q][d][k];
3320 * fluid_stress = fluid_stress - fluid_pressure;
3322 * forces = fluid_stress*normal_vector*fe_face_values_velocity.JxW(q);
3324 * local_drag += forces[0];
3325 * local_lift += forces[1];
3338 * output_lift << lift << std::endl;
3339 * output_drag << drag << std::endl;
3347 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3348 * @sect{ <code>NavierStokesProjection::refine_mesh</code>}
3352 * After finding a good
initial guess on the coarse mesh, we hope to
3353 * decrease the error through refining the mesh. We also need to transfer the current solution to the
3360 *
template <
int dim>
3361 *
void NavierStokesProjection<dim>::refine_mesh() {
3367 * tmp_velocity.
reinit(dof_handler_velocity.locally_owned_dofs(), locally_relevant_dofs, MPI_COMM_WORLD);
3368 * tmp_velocity = u_n;
3369 * tmp_velocity.update_ghost_values();
3376 *
const auto cell_worker = [&](
const Iterator& cell,
3377 * ScratchData<dim>& scratch_data,
3378 * CopyData& copy_data) {
3380 * fe_values.
reinit(cell);
3383 * std::vector<std::vector<Tensor<1, dim>>>
gradients(fe_values.n_quadrature_points, std::vector<
Tensor<1, dim>>(dim));
3384 * fe_values.get_function_gradients(tmp_velocity, gradients);
3385 * copy_data.cell_index = cell->active_cell_index();
3386 *
double vorticity_norm_square = 0.0;
3389 *
for(
unsigned k = 0; k < fe_values.n_quadrature_points; ++k) {
3391 * vorticity_norm_square += vorticity*vorticity*fe_values.JxW(k);
3393 * copy_data.value = cell->diameter()*cell->diameter()*vorticity_norm_square;
3398 *
const auto copier = [&](
const CopyData ©_data) {
3400 * estimated_error_per_cell[copy_data.cell_index] += copy_data.value;
3404 * ScratchData<dim> scratch_data(fe_velocity, EquationData::degree_p + 2, cell_flags);
3405 * CopyData copy_data;
3407 * dof_handler_velocity.end(),
3417 *
for(
const auto& cell:
triangulation.active_cell_iterators()) {
3418 *
if(cell->refine_flag_set() &&
static_cast<unsigned int>(cell->level()) == max_loc_refinements)
3419 * cell->clear_refine_flag();
3420 *
if(cell->coarsen_flag_set() &&
static_cast<unsigned int>(cell->level()) == min_loc_refinements)
3421 * cell->clear_coarsen_flag();
3428 * std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3429 * velocities.push_back(&u_n);
3430 * velocities.push_back(&u_n_minus_1);
3432 * solution_transfer_velocity(dof_handler_velocity);
3433 * solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3435 * solution_transfer_pressure(dof_handler_pressure);
3436 * solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3447 * transfer_velocity_minus_1,
3448 * transfer_pressure;
3449 * transfer_velocity.
reinit(u_n);
3450 * transfer_velocity.zero_out_ghost_values();
3451 * transfer_velocity_minus_1.reinit(u_n_minus_1);
3452 * transfer_velocity_minus_1.zero_out_ghost_values();
3453 * transfer_pressure.reinit(pres_n);
3454 * transfer_pressure.zero_out_ghost_values();
3456 * std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3457 * transfer_velocities.push_back(&transfer_velocity);
3458 * transfer_velocities.push_back(&transfer_velocity_minus_1);
3459 * solution_transfer_velocity.interpolate(transfer_velocities);
3460 * transfer_velocity.update_ghost_values();
3461 * transfer_velocity_minus_1.update_ghost_values();
3462 * solution_transfer_pressure.interpolate(transfer_pressure);
3463 * transfer_pressure.update_ghost_values();
3465 * u_n = transfer_velocity;
3466 * u_n_minus_1 = transfer_velocity_minus_1;
3467 * pres_n = transfer_pressure;
3473 * Interpolate the locally refined solution to a mesh with maximal resolution
3474 * and transfer velocity and pressure.
3481 *
void NavierStokesProjection<dim>::interpolate_max_res(
const unsigned int level) {
3483 * solution_transfer_velocity(dof_handler_velocity);
3484 * std::vector<const LinearAlgebra::distributed::Vector<double>*> velocities;
3485 * velocities.push_back(&u_n);
3486 * velocities.push_back(&u_n_minus_1);
3487 * solution_transfer_velocity.prepare_for_coarsening_and_refinement(velocities);
3490 * solution_transfer_pressure(dof_handler_pressure);
3491 * solution_transfer_pressure.prepare_for_coarsening_and_refinement(pres_n);
3494 *
if(cell->is_locally_owned())
3495 * cell->set_refine_flag();
3502 * transfer_pressure;
3504 * transfer_velocity.
reinit(u_n);
3505 * transfer_velocity.zero_out_ghost_values();
3506 * transfer_velocity_minus_1.reinit(u_n_minus_1);
3507 * transfer_velocity_minus_1.zero_out_ghost_values();
3509 * transfer_pressure.reinit(pres_n);
3510 * transfer_pressure.zero_out_ghost_values();
3512 * std::vector<LinearAlgebra::distributed::Vector<double>*> transfer_velocities;
3514 * transfer_velocities.push_back(&transfer_velocity);
3515 * transfer_velocities.push_back(&transfer_velocity_minus_1);
3516 * solution_transfer_velocity.interpolate(transfer_velocities);
3517 * transfer_velocity.update_ghost_values();
3518 * transfer_velocity_minus_1.update_ghost_values();
3520 * solution_transfer_pressure.interpolate(transfer_pressure);
3521 * transfer_pressure.update_ghost_values();
3523 * u_n = transfer_velocity;
3524 * u_n_minus_1 = transfer_velocity_minus_1;
3525 * pres_n = transfer_pressure;
3531 * Save maximum resolution to a mesh adapted.
3538 *
void NavierStokesProjection<dim>::save_max_res() {
3540 *
GridGenerator::plate_with_a_hole(triangulation_tmp, 0.5, 1.0, 1.0, 1.1, 1.0, 19.0,
Point<2>(2.0, 2.0), 0, 1, 1.0, 2,
true);
3541 * triangulation_tmp.refine_global(
triangulation.n_global_levels() - 1);
3545 * dof_handler_velocity_tmp.distribute_dofs(fe_velocity);
3546 * dof_handler_pressure_tmp.distribute_dofs(fe_pressure);
3550 * u_n_tmp.
reinit(dof_handler_velocity_tmp.n_dofs());
3551 * pres_n_tmp.reinit(dof_handler_pressure_tmp.n_dofs());
3554 * std::vector<std::string> velocity_names(dim,
"v");
3555 * std::vector<DataComponentInterpretation::DataComponentInterpretation>
3558 * u_n_tmp.update_ghost_values();
3559 * data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, velocity_names, component_interpretation_velocity);
3561 * pres_n_tmp.update_ghost_values();
3563 * PostprocessorVorticity<dim> postprocessor;
3564 * data_out.add_data_vector(dof_handler_velocity_tmp, u_n_tmp, postprocessor);
3567 *
const std::string output =
"./" + saving_dir +
"/solution_max_res_end.vtu";
3568 * data_out.write_vtu_in_parallel(output, MPI_COMM_WORLD);
3575 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3576 * @sect{ <code>NavierStokesProjection::run</code> }
3580 * This is the time marching function, which starting at <code>t_0</code>
3581 * advances in time
using the projection method with time step <code>dt</code>
3582 * until <code>T</code>.
3586 * Its
second parameter, <code>verbose</code> indicates whether the function
3587 * should output information what it is doing at any given moment:
3595 *
void NavierStokesProjection<dim>::run(
const bool verbose,
const unsigned int output_interval) {
3598 * output_results(1);
3599 *
double time = t_0 + dt;
3600 *
unsigned int n = 1;
3601 *
while(
std::abs(T - time) > 1e-10) {
3604 * pcout <<
"Step = " << n <<
" Time = " << time << std::endl;
3607 * TR_BDF2_stage = 1;
3608 * navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3610 * mg_matrices[
level].set_TR_BDF2_stage(TR_BDF2_stage);
3612 * verbose_cout <<
" Interpolating the velocity stage 1" << std::endl;
3613 * interpolate_velocity();
3615 * verbose_cout <<
" Diffusion Step stage 1 " << std::endl;
3618 * verbose_cout <<
" Projection Step stage 1" << std::endl;
3620 * u_tmp.equ(gamma*dt, u_tmp);
3622 * projection_step();
3624 * verbose_cout <<
" Updating the Velocity stage 1" << std::endl;
3625 * u_n_gamma.equ(1.0, u_star);
3627 * grad_pres_int.equ(1.0, u_tmp);
3628 * u_tmp.equ(-gamma*dt, u_tmp);
3629 * u_n_gamma += u_tmp;
3630 * u_n_minus_1 = u_n;
3633 * TR_BDF2_stage = 2;
3635 * mg_matrices[
level].set_TR_BDF2_stage(TR_BDF2_stage);
3636 * navier_stokes_matrix.set_TR_BDF2_stage(TR_BDF2_stage);
3638 * verbose_cout <<
" Interpolating the velocity stage 2" << std::endl;
3639 * interpolate_velocity();
3641 * verbose_cout <<
" Diffusion Step stage 2 " << std::endl;
3644 * verbose_cout <<
" Projection Step stage 2" << std::endl;
3645 * u_tmp.equ((1.0 - gamma)*dt, grad_pres_int);
3647 * projection_step();
3649 * verbose_cout <<
" Updating the Velocity stage 2" << std::endl;
3650 * u_n.equ(1.0, u_star);
3652 * u_tmp.equ((gamma - 1.0)*dt, u_tmp);
3655 *
const double max_vel = get_maximal_velocity();
3656 * pcout<<
"Maximal velocity = " << max_vel << std::endl;
3658 * pcout <<
"CFL = " << dt*max_vel*(EquationData::degree_p + 1)*
3660 * compute_lift_and_drag();
3661 *
if(n % output_interval == 0) {
3662 * verbose_cout <<
"Plotting Solution final" << std::endl;
3663 * output_results(n);
3666 *
if(T - time < dt && T - time > 1e-10) {
3668 * navier_stokes_matrix.set_dt(dt);
3670 * mg_matrices[
level].set_dt(dt);
3673 *
if(refinement_iterations > 0 && n % refinement_iterations == 0) {
3674 * verbose_cout <<
"Refining mesh" << std::endl;
3678 *
if(n % output_interval != 0) {
3679 * verbose_cout <<
"Plotting Solution final" << std::endl;
3680 * output_results(n);
3682 *
if(refinement_iterations > 0) {
3683 *
for(
unsigned int lev = 0; lev <
triangulation.n_global_levels() - 1; ++ lev)
3684 * interpolate_max_res(lev);
3695 * <a name=
"navier_stokes_TRBDF2_DG.cc-"></a>
3696 * @sect{ The main function }
3700 * The main function looks very much like in all the other tutorial programs. We
first initialize
MPI,
3701 * we initialize the
class 'NavierStokesProjection' with the dimension as template parameter and then
3702 * let the method
'run' do the job.
3708 *
int main(
int argc,
char *argv[]) {
3710 *
using namespace NS_TRBDF2;
3712 * RunTimeParameters::Data_Storage
data;
3713 *
data.read_data(
"parameter-file.prm");
3720 * NavierStokesProjection<2> test(
data);
3721 * test.run(
data.verbose,
data.output_interval);
3723 *
if(curr_rank == 0)
3724 * std::cout <<
"----------------------------------------------------"
3726 * <<
"Apparently everything went fine!" << std::endl
3727 * <<
"Don't forget to brush your teeth :-)" << std::endl
3732 *
catch(std::exception &exc) {
3733 * std::cerr << std::endl
3735 * <<
"----------------------------------------------------"
3737 * std::cerr <<
"Exception on processing: " << std::endl
3738 * << exc.what() << std::endl
3739 * <<
"Aborting!" << std::endl
3740 * <<
"----------------------------------------------------"
3745 * std::cerr << std::endl
3747 * <<
"----------------------------------------------------"
3749 * std::cerr <<
"Unknown exception!" << std::endl
3750 * <<
"Aborting!" << std::endl
3751 * <<
"----------------------------------------------------"
3760<a name=
"ann-runtime_parameters.h"></a>
3761<h1>Annotated version of runtime_parameters.h</h1>
3779 * We start by including all the necessary deal.II header files
3785 * #include <deal.II/base/parameter_handler.h>
3790 * <a name=
"runtime_parameters.h-"></a>
3791 * @sect{Run time parameters}
3795 * Since our method has several parameters that can be fine-tuned we put them
3796 * into an external file, so that they can be determined at
run-time.
3802 *
namespace RunTimeParameters {
3803 *
using namespace dealii;
3805 *
class Data_Storage {
3809 *
void read_data(
const std::string& filename);
3811 *
double initial_time;
3812 *
double final_time;
3817 *
unsigned int n_refines;
3818 *
unsigned int max_loc_refinements;
3819 *
unsigned int min_loc_refinements;
3823 *
unsigned int max_iterations;
3827 *
unsigned int output_interval;
3831 *
unsigned int refinement_iterations;
3839 * In the constructor of
this class we declare all the parameters in suitable (but arbitrary) subsections.
3845 * Data_Storage::Data_Storage(): initial_time(0.0),
3850 * max_loc_refinements(0),
3851 * min_loc_refinements(0),
3852 * max_iterations(1000),
3855 * output_interval(15),
3856 * refinement_iterations(0) {
3857 * prm.enter_subsection(
"Physical data");
3859 * prm.declare_entry(
"initial_time",
3862 *
" The initial time of the simulation. ");
3863 * prm.declare_entry(
"final_time",
3866 *
" The final time of the simulation. ");
3867 * prm.declare_entry(
"Reynolds",
3870 *
" The Reynolds number. ");
3872 * prm.leave_subsection();
3874 * prm.enter_subsection(
"Time step data");
3876 * prm.declare_entry(
"dt",
3879 *
" The time step size. ");
3881 * prm.leave_subsection();
3883 * prm.enter_subsection(
"Space discretization");
3885 * prm.declare_entry(
"n_of_refines",
3888 *
" The number of cells we want on each direction of the mesh. ");
3889 * prm.declare_entry(
"max_loc_refinements",
3892 *
" The number of maximum local refinements. ");
3893 * prm.declare_entry(
"min_loc_refinements",
3896 *
" The number of minimum local refinements. ");
3898 * prm.leave_subsection();
3900 * prm.enter_subsection(
"Data solve");
3902 * prm.declare_entry(
"max_iterations",
3905 *
" The maximal number of iterations linear solvers must make. ");
3906 * prm.declare_entry(
"eps",
3909 *
" The stopping criterion. ");
3911 * prm.leave_subsection();
3913 * prm.declare_entry(
"refinement_iterations",
3916 *
" This number indicates how often we need to "
3917 *
"refine the mesh");
3919 * prm.declare_entry(
"saving directory",
"SimTest");
3921 * prm.declare_entry(
"verbose",
3924 *
" This indicates whether the output of the solution "
3925 *
"process should be verbose. ");
3927 * prm.declare_entry(
"output_interval",
3930 *
" This indicates between how many time steps we print "
3931 *
"the solution. ");
3936 * We need now a routine to read all declared parameters in the constructor
3942 *
void Data_Storage::read_data(
const std::string& filename) {
3943 * std::ifstream file(filename);
3946 * prm.parse_input(file);
3948 * prm.enter_subsection(
"Physical data");
3950 * initial_time = prm.get_double(
"initial_time");
3951 * final_time = prm.get_double(
"final_time");
3952 * Reynolds = prm.get_double(
"Reynolds");
3954 * prm.leave_subsection();
3956 * prm.enter_subsection(
"Time step data");
3958 * dt = prm.get_double(
"dt");
3960 * prm.leave_subsection();
3962 * prm.enter_subsection(
"Space discretization");
3964 * n_refines = prm.get_integer(
"n_of_refines");
3965 * max_loc_refinements = prm.get_integer(
"max_loc_refinements");
3966 * min_loc_refinements = prm.get_integer(
"min_loc_refinements");
3968 * prm.leave_subsection();
3970 * prm.enter_subsection(
"Data solve");
3972 * max_iterations = prm.get_integer(
"max_iterations");
3973 *
eps = prm.get_double(
"eps");
3975 * prm.leave_subsection();
3977 * dir = prm.get(
"saving directory");
3979 * refinement_iterations = prm.get_integer(
"refinement_iterations");
3981 * verbose = prm.get_bool(
"verbose");
3983 * 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 > >())
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
#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.
std::vector< index_type > data
@ 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())
::SolutionTransfer< dim, VectorType, spacedim > SolutionTransfer
::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