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