Reference documentation for deal.II version 9.5.1
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
step-75.h
Go to the documentation of this file.
1) const override
466 *   {
467 *   const std::array<double, dim> p_sphere =
469 *  
470 *   constexpr const double alpha = 2. / 3.;
471 *   return std::pow(p_sphere[0], alpha) * std::sin(alpha * p_sphere[1]);
472 *   }
473 *   };
474 *  
475 *  
476 *  
477 * @endcode
478 *
479 *
480 * <a name="Parameters"></a>
481 * <h3>Parameters</h3>
482 *
483
484 *
485 * For this tutorial, we will use a simplified set of parameters. It is also
487 * short we decided on using simple structs. The actual intention of all these
489 * location where they are used.
490 *
491
492 *
493 * The following parameter set controls the coarse-grid solver, the smoothers,
494 * and the inter-grid transfer scheme of the multigrid mechanism.
495 * We populate it with default parameters.
496 *
497 * @code
498 *   struct MultigridParameters
499 *   {
500 *   struct
501 *   {
502 *   std::string type = "cg_with_amg";
503 *   unsigned int maxiter = 10000;
504 *   double abstol = 1e-20;
505 *   double reltol = 1e-4;
506 *   unsigned int smoother_sweeps = 1;
507 *   unsigned int n_cycles = 1;
508 *   std::string smoother_type = "ILU";
509 *   } coarse_solver;
510 *  
511 *   struct
512 *   {
513 *   std::string type = "chebyshev";
514 *   double smoothing_range = 20;
515 *   unsigned int degree = 5;
516 *   unsigned int eig_cg_n_iterations = 20;
517 *   } smoother;
518 *  
519 *   struct
520 *   {
523 *   PolynomialCoarseningSequenceType::decrease_by_one;
524 *   bool perform_h_transfer = true;
525 *   } transfer;
526 *   };
527 *  
528 *  
529 *  
530 * @endcode
531 *
532 * This is the general parameter struct for the problem class. You will find
535 * parameters for cell weighting. It also contains an instance of the above
536 * struct for multigrid parameters which will be passed to the multigrid
537 * algorithm.
538 *
539 * @code
540 *   struct Parameters
541 *   {
542 *   unsigned int n_cycles = 8;
543 *   double tolerance_factor = 1e-12;
544 *  
546 *  
547 *   unsigned int min_h_level = 5;
548 *   unsigned int max_h_level = 12;
549 *   unsigned int min_p_degree = 2;
550 *   unsigned int max_p_degree = 6;
551 *   unsigned int max_p_level_difference = 1;
552 *  
553 *   double refine_fraction = 0.3;
554 *   double coarsen_fraction = 0.03;
555 *   double p_refine_fraction = 0.9;
556 *   double p_coarsen_fraction = 0.9;
557 *  
558 *   double weighting_factor = 1.;
559 *   double weighting_exponent = 1.;
560 *   };
561 *  
562 *  
563 *  
564 * @endcode
565 *
566 *
567 * <a name="MatrixfreeLaplaceoperator"></a>
568 * <h3>Matrix-free Laplace operator</h3>
569 *
570
571 *
572 * This is a matrix-free implementation of the Laplace operator that will
576 *
577
578 *
579 * We will use the FEEvaluation class to evaluate the solution vector
580 * at the quadrature points and to perform the integration. In contrast to
581 * other tutorials, the template arguments `degree` is set to @f$-1@f$ and
582 * `number of quadrature in 1d` to @f$0@f$. In this case, FEEvaluation selects
583 * dynamically the correct polynomial degree and number of quadrature
585 * template parameters so that we do not have to worry about them later on.
586 *
587 * @code
588 *   template <int dim, typename number>
589 *   class LaplaceOperator : public Subscriptor
590 *   {
591 *   public:
593 *  
594 *   using FECellIntegrator = FEEvaluation<dim, -1, 0, 1, number>;
595 *  
596 *   LaplaceOperator() = default;
597 *  
598 *   LaplaceOperator(const hp::MappingCollection<dim> &mapping,
599 *   const DoFHandler<dim> & dof_handler,
600 *   const hp::QCollection<dim> & quad,
601 *   const AffineConstraints<number> & constraints,
602 *   VectorType & system_rhs);
603 *  
604 *   void reinit(const hp::MappingCollection<dim> &mapping,
605 *   const DoFHandler<dim> & dof_handler,
606 *   const hp::QCollection<dim> & quad,
607 *   const AffineConstraints<number> & constraints,
608 *   VectorType & system_rhs);
609 *  
610 *   types::global_dof_index m() const;
611 *  
612 *   number el(unsigned int, unsigned int) const;
613 *  
614 *   void initialize_dof_vector(VectorType &vec) const;
615 *  
616 *   void vmult(VectorType &dst, const VectorType &src) const;
617 *  
618 *   void Tvmult(VectorType &dst, const VectorType &src) const;
619 *  
621 *  
622 *   void compute_inverse_diagonal(VectorType &diagonal) const;
623 *  
624 *   private:
626 *  
628 *   VectorType & dst,
629 *   const VectorType &src) const;
630 *  
631 *  
633 *   const MatrixFree<dim, number> & matrix_free,
634 *   VectorType & dst,
635 *   const VectorType & src,
636 *   const std::pair<unsigned int, unsigned int> &range) const;
637 *  
638 *   MatrixFree<dim, number> matrix_free;
639 *  
640 * @endcode
641 *
643 * preconditioner, we need an actual system matrix on the coarsest level.
646 * dedicated SparseMatrix object. In the default case, this matrix stays
648 * allocation). Since this is a `const` function, we need the "mutable"
649 * keyword here. We also need a the constraints object to build the matrix.
650 *
651 * @code
652 *   AffineConstraints<number> constraints;
653 *   mutable TrilinosWrappers::SparseMatrix system_matrix;
654 *   };
655 *  
656 *  
657 *  
658 * @endcode
659 *
661 * the class. In particular, these functions initialize the internal
663 * right-hand-side vector.
664 *
665 * @code
666 *   template <int dim, typename number>
667 *   LaplaceOperator<dim, number>::LaplaceOperator(
668 *   const hp::MappingCollection<dim> &mapping,
669 *   const DoFHandler<dim> & dof_handler,
670 *   const hp::QCollection<dim> & quad,
671 *   const AffineConstraints<number> & constraints,
672 *   VectorType & system_rhs)
673 *   {
674 *   this->reinit(mapping, dof_handler, quad, constraints, system_rhs);
675 *   }
676 *  
677 *  
678 *  
679 *   template <int dim, typename number>
680 *   void LaplaceOperator<dim, number>::reinit(
681 *   const hp::MappingCollection<dim> &mapping,
682 *   const DoFHandler<dim> & dof_handler,
683 *   const hp::QCollection<dim> & quad,
684 *   const AffineConstraints<number> & constraints,
685 *   VectorType & system_rhs)
686 *   {
687 * @endcode
688 *
689 * Clear internal data structures (in the case that the operator is reused).
690 *
691 * @code
692 *   this->system_matrix.clear();
693 *  
694 * @endcode
695 *
696 * Copy the constraints, since they might be needed for computation of the
698 *
699 * @code
700 *   this->constraints.copy_from(constraints);
701 *  
702 * @endcode
703 *
704 * Set up MatrixFree. At the quadrature points, we only need to evaluate
705 * the gradient of the solution and test with the gradient of the shape
707 *
708 * @code
710 *   data.mapping_update_flags = update_gradients;
711 *  
712 *   matrix_free.reinit(mapping, dof_handler, constraints, quad, data);
713 *  
714 * @endcode
715 *
716 * Compute the right-hand side vector. For this purpose, we set up a second
718 * the constraints due to Dirichlet-boundary conditions. This modified
719 * operator is applied to a vector with only the Dirichlet values set. The
720 * result is the negative right-hand-side vector.
721 *
722 * @code
723 *   {
725 *  
729 *  
732 *   constraints_without_dbc.close();
733 *  
734 *   VectorType b, x;
735 *  
736 *   this->initialize_dof_vector(system_rhs);
737 *  
738 *   MatrixFree<dim, number> matrix_free;
739 *   matrix_free.reinit(
740 *   mapping, dof_handler, constraints_without_dbc, quad, data);
741 *  
742 *   matrix_free.initialize_dof_vector(b);
743 *   matrix_free.initialize_dof_vector(x);
744 *  
745 *   constraints.distribute(x);
746 *  
747 *   matrix_free.cell_loop(&LaplaceOperator::do_cell_integral_range,
748 *   this,
749 *   b,
750 *   x);
751 *  
752 *   constraints.set_zero(b);
753 *  
754 *   system_rhs -= b;
755 *   }
756 *   }
757 *  
758 *  
759 *  
760 * @endcode
761 *
763 * including the smoothers.
764 *
765
766 *
767 * Since we do not have a matrix, query the DoFHandler for the number of
768 * degrees of freedom.
769 *
770 * @code
771 *   template <int dim, typename number>
772 *   types::global_dof_index LaplaceOperator<dim, number>::m() const
773 *   {
774 *   return matrix_free.get_dof_handler().n_dofs();
775 *   }
776 *  
777 *  
778 *  
779 * @endcode
780 *
781 * Access a particular element in the matrix. This function is neither
783 *
784 * @code
785 *   template <int dim, typename number>
786 *   number LaplaceOperator<dim, number>::el(unsigned int, unsigned int) const
787 *   {
788 *   Assert(false, ExcNotImplemented());
789 *   return 0;
790 *   }
791 *  
792 *  
793 *  
794 * @endcode
795 *
797 * MatrixFree function with the same name.
798 *
799 * @code
800 *   template <int dim, typename number>
801 *   void
802 *   LaplaceOperator<dim, number>::initialize_dof_vector(VectorType &vec) const
803 *   {
804 *   matrix_free.initialize_dof_vector(vec);
805 *   }
806 *  
807 *  
808 *  
809 * @endcode
810 *
812 * over all cells and evaluating the effect of the cell integrals (see also:
814 *
815 * @code
816 *   template <int dim, typename number>
817 *   void LaplaceOperator<dim, number>::vmult(VectorType & dst,
818 *   const VectorType &src) const
819 *   {
820 *   this->matrix_free.cell_loop(
821 *   &LaplaceOperator::do_cell_integral_range, this, dst, src, true);
822 *   }
823 *  
824 *  
825 *  
826 * @endcode
827 *
829 * symmetric "matrices", this function can simply delegate it task to vmult().
830 *
831 * @code
832 *   template <int dim, typename number>
833 *   void LaplaceOperator<dim, number>::Tvmult(VectorType & dst,
834 *   const VectorType &src) const
835 *   {
836 *   this->vmult(dst, src);
837 *   }
838 *  
839 *  
840 *  
841 * @endcode
842 *
844 * diagonal entries of the matrix. Instead, we compute the diagonal by
845 * performing a sequence of operator evaluations to unit basis vectors.
846 * For this purpose, an optimized function from the MatrixFreeTools
847 * namespace is used. The inversion is performed manually afterwards.
848 *
849 * @code
850 *   template <int dim, typename number>
851 *   void LaplaceOperator<dim, number>::compute_inverse_diagonal(
852 *   VectorType &diagonal) const
853 *   {
854 *   this->matrix_free.initialize_dof_vector(diagonal);
856 *   diagonal,
857 *   &LaplaceOperator::do_cell_integral_local,
858 *   this);
859 *  
860 *   for (auto &i : diagonal)
861 *   i = (std::abs(i) > 1.0e-10) ? (1.0 / i) : 1.0;
862 *   }
863 *  
864 *  
865 *  
866 * @endcode
867 *
871 * this tutorial for linear elements (on the coarse grid), this is
872 * acceptable.
873 * The matrix entries are obtained via sequence of operator evaluations.
874 * For this purpose, the optimized function MatrixFreeTools::compute_matrix()
876 * (lazy allocation).
877 *
878 * @code
881 *   LaplaceOperator<dim, number>::get_system_matrix() const
882 *   {
883 *   if (system_matrix.m() == 0 && system_matrix.n() == 0)
884 *   {
885 *   const auto &dof_handler = this->matrix_free.get_dof_handler();
886 *  
888 *   dof_handler.locally_owned_dofs(),
889 *   dof_handler.get_triangulation().get_communicator());
890 *  
891 *   DoFTools::make_sparsity_pattern(dof_handler, dsp, this->constraints);
892 *  
893 *   dsp.compress();
894 *   system_matrix.reinit(dsp);
895 *  
897 *   matrix_free,
898 *   constraints,
899 *   system_matrix,
900 *   &LaplaceOperator::do_cell_integral_local,
901 *   this);
902 *   }
903 *  
904 *   return this->system_matrix;
905 *   }
906 *  
907 *  
908 *  
909 * @endcode
910 *
914 *
915 * @code
916 *   template <int dim, typename number>
917 *   void LaplaceOperator<dim, number>::do_cell_integral_local(
919 *   {
921 *  
922 *   for (unsigned int q = 0; q < integrator.n_q_points; ++q)
923 *   integrator.submit_gradient(integrator.get_gradient(q), q);
924 *  
926 *   }
927 *  
928 *  
929 *  
930 * @endcode
931 *
932 * Same as above but with access to the global vectors.
933 *
934 * @code
935 *   template <int dim, typename number>
936 *   void LaplaceOperator<dim, number>::do_cell_integral_global(
938 *   VectorType & dst,
939 *   const VectorType &src) const
940 *   {
941 *   integrator.gather_evaluate(src, EvaluationFlags::gradients);
942 *  
943 *   for (unsigned int q = 0; q < integrator.n_q_points; ++q)
944 *   integrator.submit_gradient(integrator.get_gradient(q), q);
945 *  
946 *   integrator.integrate_scatter(EvaluationFlags::gradients, dst);
947 *   }
948 *  
949 *  
950 *  
951 * @endcode
952 *
953 * This function loops over all cell batches within a cell-batch range and
954 * calls the above function.
955 *
956 * @code
957 *   template <int dim, typename number>
958 *   void LaplaceOperator<dim, number>::do_cell_integral_range(
959 *   const MatrixFree<dim, number> & matrix_free,
960 *   VectorType & dst,
961 *   const VectorType & src,
962 *   const std::pair<unsigned int, unsigned int> &range) const
963 *   {
964 *   FECellIntegrator integrator(matrix_free, range);
965 *  
966 *   for (unsigned cell = range.first; cell < range.second; ++cell)
967 *   {
968 *   integrator.reinit(cell);
969 *  
971 *   }
972 *   }
973 *  
974 *  
975 *  
976 * @endcode
977 *
978 *
979 * <a name="Solverandpreconditioner"></a>
980 * <h3>Solver and preconditioner</h3>
981 *
982
983 *
984 *
985 * <a name="Conjugategradientsolverwithmultigridpreconditioner"></a>
986 * <h4>Conjugate-gradient solver with multigrid preconditioner</h4>
987 *
988
989 *
990 * This function solves the equation system with a sequence of provided
991 * multigrid objects. It is meant to be treated as general as possible, hence
992 * the multitude of template parameters.
993 *
994 * @code
995 *   template <typename VectorType,
996 *   int dim,
997 *   typename SystemMatrixType,
998 *   typename LevelMatrixType,
999 *   typename MGTransferType>
1000 *   static void
1001 *   mg_solve(SolverControl & solver_control,
1002 *   VectorType & dst,
1003 *   const VectorType & src,
1005 *   const DoFHandler<dim> & dof,
1007 *   const MGLevelObject<std::unique_ptr<LevelMatrixType>> &mg_matrices,
1008 *   const MGTransferType & mg_transfer)
1009 *   {
1010 *   AssertThrow(mg_data.coarse_solver.type == "cg_with_amg",
1011 *   ExcNotImplemented());
1012 *   AssertThrow(mg_data.smoother.type == "chebyshev", ExcNotImplemented());
1013 *  
1014 *   const unsigned int min_level = mg_matrices.min_level();
1015 *   const unsigned int max_level = mg_matrices.max_level();
1016 *  
1019 *   VectorType,
1021 *   using PreconditionerType = PreconditionMG<dim, VectorType, MGTransferType>;
1022 *  
1023 * @endcode
1024 *
1025 * We initialize level operators and Chebyshev smoothers here.
1026 *
1027 * @code
1029 *  
1031 *   min_level, max_level);
1032 *  
1033 *   for (unsigned int level = min_level; level <= max_level; ++level)
1034 *   {
1035 *   smoother_data[level].preconditioner =
1036 *   std::make_shared<SmootherPreconditionerType>();
1037 *   mg_matrices[level]->compute_inverse_diagonal(
1038 *   smoother_data[level].preconditioner->get_vector());
1039 *   smoother_data[level].smoothing_range = mg_data.smoother.smoothing_range;
1040 *   smoother_data[level].degree = mg_data.smoother.degree;
1041 *   smoother_data[level].eig_cg_n_iterations =
1042 *   mg_data.smoother.eig_cg_n_iterations;
1043 *   }
1044 *  
1046 *   mg_smoother;
1047 *   mg_smoother.initialize(mg_matrices, smoother_data);
1048 *  
1049 * @endcode
1050 *
1051 * Next, we initialize the coarse-grid solver. We use conjugate-gradient
1052 * method with AMG as preconditioner.
1053 *
1054 * @code
1055 *   ReductionControl coarse_grid_solver_control(mg_data.coarse_solver.maxiter,
1056 *   mg_data.coarse_solver.abstol,
1057 *   mg_data.coarse_solver.reltol,
1058 *   false,
1059 *   false);
1061 *  
1062 *   std::unique_ptr<MGCoarseGridBase<VectorType>> mg_coarse;
1063 *  
1066 *   amg_data.smoother_sweeps = mg_data.coarse_solver.smoother_sweeps;
1067 *   amg_data.n_cycles = mg_data.coarse_solver.n_cycles;
1068 *   amg_data.smoother_type = mg_data.coarse_solver.smoother_type.c_str();
1069 *  
1070 *   precondition_amg.initialize(mg_matrices[min_level]->get_system_matrix(),
1071 *   amg_data);
1072 *  
1073 *   mg_coarse =
1074 *   std::make_unique<MGCoarseGridIterativeSolver<VectorType,
1077 *   decltype(precondition_amg)>>(
1079 *  
1080 * @endcode
1081 *
1082 * Finally, we create the Multigrid object, convert it to a preconditioner,
1083 * and use it inside of a conjugate-gradient solver to solve the linear
1085 *
1086 * @code
1089 *  
1090 *   PreconditionerType preconditioner(dof, mg, mg_transfer);
1091 *  
1092 *   SolverCG<VectorType>(solver_control)
1093 *   .solve(fine_matrix, dst, src, preconditioner);
1094 *   }
1095 *  
1096 *  
1097 *  
1098 * @endcode
1099 *
1100 *
1101 * <a name="Hybridpolynomialgeometricglobalcoarseningmultigridpreconditioner"></a>
1102 * <h4>Hybrid polynomial/geometric-global-coarsening multigrid preconditioner</h4>
1103 *
1104
1105 *
1106 * The above function deals with the actual solution for a given sequence of
1107 * multigrid objects. This functions creates the actual multigrid levels, in
1108 * particular the operators, and the transfer operator as a
1110 *
1111 * @code
1112 *   template <typename VectorType, typename OperatorType, int dim>
1113 *   void solve_with_gmg(SolverControl & solver_control,
1114 *   const OperatorType & system_matrix,
1115 *   VectorType & dst,
1116 *   const VectorType & src,
1117 *   const MultigridParameters & mg_data,
1118 *   const hp::MappingCollection<dim> mapping_collection,
1119 *   const DoFHandler<dim> & dof_handler,
1121 *   {
1122 * @endcode
1123 *
1124 * Create a DoFHandler and operator for each multigrid level,
1125 * as well as, create transfer operators. To be able to
1127 * via global coarsening of p or h. For latter, we need also a sequence
1130 *
1131
1132 *
1133 * In case no h-transfer is requested, we provide an empty deleter for the
1136 *
1137 * @code
1138 *   MGLevelObject<DoFHandler<dim>> dof_handlers;
1141 *  
1142 *   std::vector<std::shared_ptr<const Triangulation<dim>>>
1144 *   if (mg_data.transfer.perform_h_transfer)
1147 *   dof_handler.get_triangulation());
1148 *   else
1149 *   coarse_grid_triangulations.emplace_back(
1150 *   &(dof_handler.get_triangulation()), [](auto *) {});
1151 *  
1152 * @endcode
1153 *
1154 * Determine the total number of levels for the multigrid operation and
1155 * allocate sufficient memory for all levels.
1156 *
1157 * @code
1158 *   const unsigned int n_h_levels = coarse_grid_triangulations.size() - 1;
1159 *  
1160 *   const auto get_max_active_fe_degree = [&](const auto &dof_handler) {
1161 *   unsigned int max = 0;
1162 *  
1163 *   for (auto &cell : dof_handler.active_cell_iterators())
1164 *   if (cell->is_locally_owned())
1165 *   max =
1166 *   std::max(max, dof_handler.get_fe(cell->active_fe_index()).degree);
1167 *  
1168 *   return Utilities::MPI::max(max, MPI_COMM_WORLD);
1169 *   };
1170 *  
1171 *   const unsigned int n_p_levels =
1173 *   get_max_active_fe_degree(dof_handler), mg_data.transfer.p_sequence)
1174 *   .size();
1175 *  
1176 *   std::map<unsigned int, unsigned int> fe_index_for_degree;
1177 *   for (unsigned int i = 0; i < dof_handler.get_fe_collection().size(); ++i)
1178 *   {
1179 *   const unsigned int degree = dof_handler.get_fe(i).degree;
1181 *   ExcMessage("FECollection does not contain unique degrees."));
1182 *   fe_index_for_degree[degree] = i;
1183 *   }
1184 *  
1185 *   unsigned int minlevel = 0;
1186 *   unsigned int maxlevel = n_h_levels + n_p_levels - 1;
1187 *  
1188 *   dof_handlers.resize(minlevel, maxlevel);
1189 *   operators.resize(minlevel, maxlevel);
1190 *   transfers.resize(minlevel, maxlevel);
1191 *  
1192 * @endcode
1193 *
1195 * DoFHandler accordingly. We start with the h-levels, where we distribute
1196 * on increasingly finer meshes linear elements.
1197 *
1198 * @code
1199 *   for (unsigned int l = 0; l < n_h_levels; ++l)
1200 *   {
1201 *   dof_handlers[l].reinit(*coarse_grid_triangulations[l]);
1202 *   dof_handlers[l].distribute_dofs(dof_handler.get_fe_collection());
1203 *   }
1204 *  
1205 * @endcode
1206 *
1207 * After we reached the finest mesh, we will adjust the polynomial degrees
1208 * on each level. We reverse iterate over our data structure and start at
1210 * indices. We then lower the polynomial degree of each cell level by level.
1211 *
1212 * @code
1213 *   for (unsigned int i = 0, l = maxlevel; i < n_p_levels; ++i, --l)
1214 *   {
1215 *   dof_handlers[l].reinit(dof_handler.get_triangulation());
1216 *  
1217 *   if (l == maxlevel) // finest level
1218 *   {
1219 *   auto &dof_handler_mg = dof_handlers[l];
1220 *  
1221 *   auto cell_other = dof_handler.begin_active();
1222 *   for (auto &cell : dof_handler_mg.active_cell_iterators())
1223 *   {
1224 *   if (cell->is_locally_owned())
1225 *   cell->set_active_fe_index(cell_other->active_fe_index());
1226 *   cell_other++;
1227 *   }
1228 *   }
1229 *   else // coarse level
1230 *   {
1231 *   auto &dof_handler_fine = dof_handlers[l + 1];
1232 *   auto &dof_handler_coarse = dof_handlers[l + 0];
1233 *  
1234 *   auto cell_other = dof_handler_fine.begin_active();
1235 *   for (auto &cell : dof_handler_coarse.active_cell_iterators())
1236 *   {
1237 *   if (cell->is_locally_owned())
1238 *   {
1239 *   const unsigned int next_degree =
1242 *   cell_other->get_fe().degree,
1243 *   mg_data.transfer.p_sequence);
1246 *   ExcMessage("Next polynomial degree in sequence "
1247 *   "does not exist in FECollection."));
1248 *  
1249 *   cell->set_active_fe_index(fe_index_for_degree[next_degree]);
1250 *   }
1251 *   cell_other++;
1252 *   }
1253 *   }
1254 *  
1255 *   dof_handlers[l].distribute_dofs(dof_handler.get_fe_collection());
1256 *   }
1257 *  
1258 * @endcode
1259 *
1261 * multigrid level. This involves determining constraints with homogeneous
1262 * Dirichlet boundary conditions, and building the operator just like on the
1263 * active level.
1264 *
1265 * @code
1267 *   constraints(minlevel, maxlevel);
1268 *  
1269 *   for (unsigned int level = minlevel; level <= maxlevel; ++level)
1270 *   {
1271 *   const auto &dof_handler = dof_handlers[level];
1272 *   auto & constraint = constraints[level];
1273 *  
1277 *  
1279 *   VectorTools::interpolate_boundary_values(mapping_collection,
1280 *   dof_handler,
1281 *   0,
1283 *   constraint);
1284 *   constraint.close();
1285 *  
1286 *   VectorType dummy;
1287 *  
1288 *   operators[level] = std::make_unique<OperatorType>(mapping_collection,
1289 *   dof_handler,
1291 *   constraint,
1292 *   dummy);
1293 *   }
1294 *  
1295 * @endcode
1296 *
1298 * operator as needed by the Multigrid solver class.
1299 *
1300 * @code
1301 *   for (unsigned int level = minlevel; level < maxlevel; ++level)
1302 *   transfers[level + 1].reinit(dof_handlers[level + 1],
1303 *   dof_handlers[level],
1304 *   constraints[level + 1],
1305 *   constraints[level]);
1306 *  
1308 *   transfers, [&](const auto l, auto &vec) {
1309 *   operators[l]->initialize_dof_vector(vec);
1310 *   });
1311 *  
1312 * @endcode
1313 *
1314 * Finally, proceed to solve the problem with multigrid.
1315 *
1316 * @code
1317 *   mg_solve(solver_control,
1318 *   dst,
1319 *   src,
1320 *   mg_data,
1321 *   dof_handler,
1322 *   system_matrix,
1323 *   operators,
1324 *   transfer);
1325 *   }
1326 *  
1327 *  
1328 *  
1329 * @endcode
1330 *
1331 *
1332 * <a name="ThecodeLaplaceProblemcodeclasstemplate"></a>
1334 *
1335
1336 *
1337 * Now we will finally declare the main class of this program, which solves
1339 * will look familiar as it is similar to the main classes of @ref step_27 "step-27" and
1340 * @ref step_40 "step-40". There are basically just two additions:
1342 * replaced by an object of the LaplaceOperator class for the MatrixFree
1343 * formulation.
1344 * - An object of parallel::CellWeights, which will help us with load
1346 *
1347 * @code
1348 *   template <int dim>
1349 *   class LaplaceProblem
1350 *   {
1351 *   public:
1352 *   LaplaceProblem(const Parameters &parameters);
1353 *  
1354 *   void run();
1355 *  
1356 *   private:
1357 *   void initialize_grid();
1358 *   void setup_system();
1359 *   void print_diagnostics();
1360 *   void solve_system();
1361 *   void compute_indicators();
1362 *   void adapt_resolution();
1363 *   void output_results(const unsigned int cycle);
1364 *  
1365 *   MPI_Comm mpi_communicator;
1366 *  
1367 *   const Parameters prm;
1368 *  
1370 *   DoFHandler<dim> dof_handler;
1371 *  
1372 *   hp::MappingCollection<dim> mapping_collection;
1373 *   hp::FECollection<dim> fe_collection;
1375 *   hp::QCollection<dim - 1> face_quadrature_collection;
1376 *  
1377 *   IndexSet locally_owned_dofs;
1379 *  
1380 *   AffineConstraints<double> constraints;
1381 *  
1385 *  
1386 *   std::unique_ptr<FESeries::Legendre<dim>> legendre;
1387 *   std::unique_ptr<parallel::CellWeights<dim>> cell_weights;
1388 *  
1391 *  
1394 *   };
1395 *  
1396 *  
1397 *  
1398 * @endcode
1399 *
1400 *
1401 * <a name="ThecodeLaplaceProblemcodeclassimplementation"></a>
1403 *
1404
1405 *
1406 *
1407 * <a name="Constructor"></a>
1408 * <h4>Constructor</h4>
1409 *
1410
1411 *
1413 * one of @ref step_40 "step-40". We again prepare the ConditionalOStream object to allow
1414 * only the first process to output anything over the console, and initialize
1415 * the computing timer properly.
1416 *
1417 * @code
1418 *   template <int dim>
1420 *   : mpi_communicator(MPI_COMM_WORLD)
1421 *   , prm(parameters)
1422 *   , triangulation(mpi_communicator)
1423 *   , dof_handler(triangulation)
1424 *   , pcout(std::cout,
1425 *   (Utilities::MPI::this_mpi_process(mpi_communicator) == 0))
1426 *   , computing_timer(mpi_communicator,
1427 *   pcout,
1430 *   {
1431 *   Assert(prm.min_h_level <= prm.max_h_level,
1432 *   ExcMessage(
1433 *   "Triangulation level limits have been incorrectly set up."));
1434 *   Assert(prm.min_p_degree <= prm.max_p_degree,
1435 *   ExcMessage("FECollection degrees have been incorrectly set up."));
1436 *  
1437 * @endcode
1438 *
1440 * actual body of the constructor, and create corresponding objects for
1441 * every degree in the specified range from the parameter struct. As we are
1442 * only dealing with non-distorted rectangular cells, a linear mapping
1443 * object is sufficient in this context.
1444 *
1445
1446 *
1447 * In the Parameters struct, we provide ranges for levels on which the
1448 * function space is operating with a reasonable resolution. The multigrid
1449 * algorithm requires linear elements on the coarsest possible level. So we
1450 * start with the lowest polynomial degree and fill the collection with
1452 * reached.
1453 *
1454 * @code
1455 *   mapping_collection.push_back(MappingQ1<dim>());
1456 *  
1457 *   for (unsigned int degree = 1; degree <= prm.max_p_degree; ++degree)
1458 *   {
1459 *   fe_collection.push_back(FE_Q<dim>(degree));
1460 *   quadrature_collection.push_back(QGauss<dim>(degree + 1));
1461 *   face_quadrature_collection.push_back(QGauss<dim - 1>(degree + 1));
1462 *   }
1463 *  
1464 * @endcode
1465 *
1466 * As our FECollection contains more finite elements than we want to use for
1467 * the finite element approximation of our solution, we would like to limit
1468 * the range on which active FE indices can operate on. For this, the
1469 * FECollection class allows to register a hierarchy that determines the
1470 * succeeding and preceding finite element in case of of p-refinement and
1472 * consult this hierarchy to determine future FE indices. We will register
1473 * such a hierarchy that only works on finite elements with polynomial
1475 *
1476 * @code
1477 *   const unsigned int min_fe_index = prm.min_p_degree - 1;
1478 *   fe_collection.set_hierarchy(
1479 *   /*next_index=*/
1480 *   [](const typename hp::FECollection<dim> &fe_collection,
1481 *   const unsigned int fe_index) -> unsigned int {
1482 *   return ((fe_index + 1) < fe_collection.size()) ? fe_index + 1 :
1483 *   fe_index;
1484 *   },
1485 *   /*previous_index=*/
1486 *   [min_fe_index](const typename hp::FECollection<dim> &,
1487 *   const unsigned int fe_index) -> unsigned int {
1488 *   Assert(fe_index >= min_fe_index,
1489 *   ExcMessage("Finite element is not part of hierarchy!"));
1490 *   return (fe_index > min_fe_index) ? fe_index - 1 : fe_index;
1491 *   });
1492 *  
1493 * @endcode
1494 *
1495 * We initialize the FESeries::Legendre object in the default configuration
1496 * for smoothness estimation.
1497 *
1498 * @code
1499 *   legendre = std::make_unique<FESeries::Legendre<dim>>(
1501 *  
1502 * @endcode
1503 *
1509 * require this functionality for load balancing and to limit the polynomial
1510 * degrees of neighboring cells.
1511 *
1512
1513 *
1514 * For the former, we would like to assign a weight to every cell that is
1515 * proportional to the number of degrees of freedom of its future finite
1517 * easily attach individual weights at the right place during the refinement
1518 * process, i.e., after all refine and coarsen flags have been set correctly
1519 * for hp-adaptation and right before repartitioning for load balancing is
1520 * about to happen. Functions can be registered that will attach weights in
1521 * the form that @f$a (n_\text{dofs})^b@f$ with a provided pair of parameters
1522 * @f$(a,b)@f$. We register such a function in the following.
1523 *
1524
1525 *
1527 * linearly with the number of degrees of freedom owned. We set the
1528 * parameters for cell weighting correspondingly: A weighting factor of @f$1@f$
1531 *
1532 * @code
1533 *   cell_weights = std::make_unique<parallel::CellWeights<dim>>(
1534 *   dof_handler,
1536 *   {prm.weighting_factor, prm.weighting_exponent}));
1537 *  
1538 * @endcode
1539 *
1540 * In h-adaptive applications, we ensure a 2:1 mesh balance by limiting the
1541 * difference of refinement levels of neighboring cells to one. With the
1543 * p-levels on neighboring cells: levels of future finite elements are not
1544 * allowed to differ by more than a specified difference. The function
1548 * future FE indices accordingly. As we ask the p4est oracle to perform
1552 * that for the duration of its life. Thus, we will create an object of this
1553 * class right before limiting the p-level difference, and connect the
1554 * corresponding lambda function to the signal
1557 * Furthermore, we specify that this function will be connected to the front
1559 * other function connected to the same signal.
1560 *
1561 * @code
1562 *   triangulation.signals.post_p4est_refinement.connect(
1563 *   [&, min_fe_index]() {
1567 *   prm.max_p_level_difference,
1568 *   /*contains=*/min_fe_index);
1569 *   },
1570 *   boost::signals2::at_front);
1571 *   }
1572 *  
1573 *  
1574 *  
1575 * @endcode
1576 *
1577 *
1578 * <a name="LaplaceProbleminitialize_grid"></a>
1580 *
1581
1582 *
1584 * as demonstrated in @ref step_50 "step-50". However in the 2d case, that particular
1585 * function removes the first quadrant, while we need the fourth quadrant
1586 * removed in our scenario. Thus, we will use a different function
1588 * the mesh. Furthermore, we formulate that function in a way that it also
1590 * in the positive z-direction.
1591 *
1592
1593 *
1594 * We first pretend to build a GridGenerator::subdivided_hyper_rectangle().
1595 * The parameters that we need to provide are Point objects for the lower left
1596 * and top right corners, as well as the number of repetitions that the base
1597 * mesh will have in each direction. We provide them for the first two
1598 * dimensions and treat the higher third dimension separately.
1599 *
1600
1601 *
1602 * To create a L-shaped domain, we need to remove the excess cells. For this,
1604 * remove one cell in every cell from the negative direction, but remove one
1605 * from the positive x-direction.
1606 *
1607
1608 *
1609 * On the coarse grid, we set the initial active FE indices and distribute the
1610 * degrees of freedom once. We do that in order to assign the hp::FECollection
1614 * parallel::distributed::Triangulation::refine_global().
1615 *
1616 * @code
1617 *   template <int dim>
1618 *   void LaplaceProblem<dim>::initialize_grid()
1619 *   {
1620 *   TimerOutput::Scope t(computing_timer, "initialize grid");
1621 *  
1622 *   std::vector<unsigned int> repetitions(dim);
1624 *   for (unsigned int d = 0; d < dim; ++d)
1625 *   if (d < 2)
1626 *   {
1627 *   repetitions[d] = 2;
1628 *   bottom_left[d] = -1.;
1629 *   top_right[d] = 1.;
1630 *   }
1631 *   else
1632 *   {
1633 *   repetitions[d] = 1;
1634 *   bottom_left[d] = 0.;
1635 *   top_right[d] = 1.;
1636 *   }
1637 *  
1638 *   std::vector<int> cells_to_remove(dim, 1);
1639 *   cells_to_remove[0] = -1;
1640 *  
1643 *  
1644 *   const unsigned int min_fe_index = prm.min_p_degree - 1;
1645 *   for (const auto &cell : dof_handler.active_cell_iterators())
1646 *   if (cell->is_locally_owned())
1647 *   cell->set_active_fe_index(min_fe_index);
1648 *  
1649 *   dof_handler.distribute_dofs(fe_collection);
1650 *  
1651 *   triangulation.refine_global(prm.min_h_level);
1652 *   }
1653 *  
1654 *  
1655 *  
1656 * @endcode
1657 *
1658 *
1659 * <a name="LaplaceProblemsetup_system"></a>
1661 *
1662
1663 *
1664 * This function looks exactly the same to the one of @ref step_40 "step-40", but you will
1669 *
1670 * @code
1671 *   template <int dim>
1673 *   {
1674 *   TimerOutput::Scope t(computing_timer, "setup system");
1675 *  
1676 *   dof_handler.distribute_dofs(fe_collection);
1677 *  
1678 *   locally_owned_dofs = dof_handler.locally_owned_dofs();
1681 *  
1682 *   locally_relevant_solution.reinit(locally_owned_dofs,
1684 *   mpi_communicator);
1685 *   system_rhs.reinit(locally_owned_dofs, mpi_communicator);
1686 *  
1687 *   constraints.clear();
1688 *   constraints.reinit(locally_relevant_dofs);
1689 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints);
1691 *   mapping_collection, dof_handler, 0, Solution<dim>(), constraints);
1692 *   constraints.close();
1693 *  
1694 *   laplace_operator.reinit(mapping_collection,
1695 *   dof_handler,
1697 *   constraints,
1698 *   system_rhs);
1699 *   }
1700 *  
1701 *  
1702 *  
1703 * @endcode
1704 *
1705 *
1706 * <a name="LaplaceProblemprint_diagnostics"></a>
1708 *
1709
1710 *
1712 * system and its partitioning. In addition to the usual global number of
1713 * active cells and degrees of freedom, we also output their local
1714 * equivalents. For a regulated output, we will communicate the local
1715 * quantities with a Utilities::MPI::gather operation to the first process
1716 * which will then output all information. Output of local quantities is
1718 *
1719
1720 *
1721 * Furthermore, we would like to print the frequencies of the polynomial
1723 * stored locally, we will count the finite elements on locally owned cells
1725 *
1726 * @code
1727 *   template <int dim>
1729 *   {
1730 *   const unsigned int first_n_processes =
1731 *   std::min<unsigned int>(8,
1732 *   Utilities::MPI::n_mpi_processes(mpi_communicator));
1733 *   const bool output_cropped =
1735 *  
1736 *   {
1737 *   pcout << " Number of active cells: "
1738 *   << triangulation.n_global_active_cells() << std::endl
1739 *   << " by partition: ";
1740 *  
1741 *   std::vector<unsigned int> n_active_cells_per_subdomain =
1742 *   Utilities::MPI::gather(mpi_communicator,
1743 *   triangulation.n_locally_owned_active_cells());
1744 *   for (unsigned int i = 0; i < first_n_processes; ++i)
1745 *   pcout << ' ' << n_active_cells_per_subdomain[i];
1746 *   if (output_cropped)
1747 *   pcout << " ...";
1748 *   pcout << std::endl;
1749 *   }
1750 *  
1751 *   {
1752 *   pcout << " Number of degrees of freedom: " << dof_handler.n_dofs()
1753 *   << std::endl
1754 *   << " by partition: ";
1755 *  
1756 *   std::vector<types::global_dof_index> n_dofs_per_subdomain =
1757 *   Utilities::MPI::gather(mpi_communicator,
1758 *   dof_handler.n_locally_owned_dofs());
1759 *   for (unsigned int i = 0; i < first_n_processes; ++i)
1760 *   pcout << ' ' << n_dofs_per_subdomain[i];
1761 *   if (output_cropped)
1762 *   pcout << " ...";
1763 *   pcout << std::endl;
1764 *   }
1765 *  
1766 *   {
1767 *   std::vector<types::global_dof_index> n_constraints_per_subdomain =
1768 *   Utilities::MPI::gather(mpi_communicator, constraints.n_constraints());
1769 *  
1770 *   pcout << " Number of constraints: "
1771 *   << std::accumulate(n_constraints_per_subdomain.begin(),
1773 *   0)
1774 *   << std::endl
1775 *   << " by partition: ";
1776 *   for (unsigned int i = 0; i < first_n_processes; ++i)
1777 *   pcout << ' ' << n_constraints_per_subdomain[i];
1778 *   if (output_cropped)
1779 *   pcout << " ...";
1780 *   pcout << std::endl;
1781 *   }
1782 *  
1783 *   {
1784 *   std::vector<unsigned int> n_fe_indices(fe_collection.size(), 0);
1785 *   for (const auto &cell : dof_handler.active_cell_iterators())
1786 *   if (cell->is_locally_owned())
1787 *   n_fe_indices[cell->active_fe_index()]++;
1788 *  
1789 *   Utilities::MPI::sum(n_fe_indices, mpi_communicator, n_fe_indices);
1790 *  
1791 *   pcout << " Frequencies of poly. degrees:";
1792 *   for (unsigned int i = 0; i < fe_collection.size(); ++i)
1793 *   if (n_fe_indices[i] > 0)
1794 *   pcout << ' ' << fe_collection[i].degree << ':' << n_fe_indices[i];
1795 *   pcout << std::endl;
1796 *   }
1797 *   }
1798 *  
1799 *  
1800 *  
1801 * @endcode
1802 *
1803 *
1804 * <a name="LaplaceProblemsolve_system"></a>
1806 *
1807
1808 *
1809 * The scaffold around the solution is similar to the one of @ref step_40 "step-40". We
1812 * solution happens with the function introduced earlier.
1813 *
1814 * @code
1815 *   template <int dim>
1817 *   {
1818 *   TimerOutput::Scope t(computing_timer, "solve system");
1819 *  
1821 *   laplace_operator.initialize_dof_vector(completely_distributed_solution);
1822 *  
1823 *   SolverControl solver_control(system_rhs.size(),
1824 *   prm.tolerance_factor * system_rhs.l2_norm());
1825 *  
1826 *   solve_with_gmg(solver_control,
1829 *   system_rhs,
1830 *   prm.mg_data,
1831 *   mapping_collection,
1832 *   dof_handler,
1834 *  
1835 *   pcout << " Solved in " << solver_control.last_step() << " iterations."
1836 *   << std::endl;
1837 *  
1838 *   constraints.distribute(completely_distributed_solution);
1839 *  
1840 *   locally_relevant_solution.copy_locally_owned_data_from(
1842 *   locally_relevant_solution.update_ghost_values();
1843 *   }
1844 *  
1845 *  
1846 *  
1847 * @endcode
1848 *
1849 *
1850 * <a name="LaplaceProblemcompute_indicators"></a>
1852 *
1853
1854 *
1855 * This function contains only a part of the typical <code>refine_grid</code>
1856 * function from other tutorials and is new in that sense. Here, we will only
1857 * calculate all indicators for adaptation with actually refining the grid. We
1858 * do this for the purpose of writing all indicators to the file system, so we
1859 * store them for later.
1860 *
1861
1862 *
1865 * scaling factor of the underlying face integrals to be dependent on the
1866 * actual polynomial degree of the neighboring elements is favorable in
1867 * hp-adaptive applications @cite davydov2017hp. We can do this by specifying
1868 * the very last parameter from the additional ones you notices. The others
1870 *
1871
1872 *
1876 * we set the minimal polynomial degree to 2 as it seems that the smoothness
1877 * estimation algorithms have trouble with linear elements.
1878 *
1879 * @code
1880 *   template <int dim>
1882 *   {
1883 *   TimerOutput::Scope t(computing_timer, "compute indicators");
1884 *  
1885 *   estimated_error_per_cell.grow_or_shrink(triangulation.n_active_cells());
1887 *   dof_handler,
1888 *   face_quadrature_collection,
1889 *   std::map<types::boundary_id, const Function<dim> *>(),
1892 *   /*component_mask=*/ComponentMask(),
1893 *   /*coefficients=*/nullptr,
1894 *   /*n_threads=*/numbers::invalid_unsigned_int,
1895 *   /*subdomain_id=*/numbers::invalid_subdomain_id,
1896 *   /*material_id=*/numbers::invalid_material_id,
1897 *   /*strategy=*/
1899 *  
1900 *   hp_decision_indicators.grow_or_shrink(triangulation.n_active_cells());
1902 *   dof_handler,
1905 *   }
1906 *  
1907 *  
1908 *  
1909 * @endcode
1910 *
1911 *
1912 * <a name="LaplaceProblemadapt_resolution"></a>
1914 *
1915
1916 *
1917 * With the previously calculated indicators, we will finally flag all cells
1918 * for adaptation and also execute refinement in this function. As in previous
1919 * tutorials, we will use the "fixed number" strategy, but now for
1920 * hp-adaptation.
1921 *
1922 * @code
1923 *   template <int dim>
1925 *   {
1926 *   TimerOutput::Scope t(computing_timer, "adapt resolution");
1927 *  
1928 * @endcode
1929 *
1931 * on each cell. There is nothing new here.
1932 *
1933
1934 *
1936 * elaborated in the other deal.II tutorials: using the fixed number
1937 * strategy, we will flag 30% of all cells for refinement and 3% for
1938 * coarsening, as provided in the Parameters struct.
1939 *
1940 * @code
1942 *   triangulation,
1944 *   prm.refine_fraction,
1945 *   prm.coarsen_fraction);
1946 *  
1947 * @endcode
1948 *
1950 * and coarsen those cells flagged in the previous step, but need to decide
1952 * polynomial degree.
1953 *
1954
1955 *
1956 * The next function call sets future FE indices according to the previously
1958 * indices will only be set on those cells that have refine or coarsen flags
1959 * assigned.
1960 *
1961
1962 *
1965 * the domain, and a smooth solution anywhere else, we would like to
1967 * our choice of a fraction of 90% for both p-refinement and p-coarsening.
1968 *
1969 * @code
1972 *   prm.p_refine_fraction,
1973 *   prm.p_coarsen_fraction);
1974 *  
1975 * @endcode
1976 *
1978 * specified limits of the provided level ranges in the Parameters struct.
1981 * hierarchy for p-adaptation in the constructor. Now, we need to do this
1982 * manually in the h-adaptive context like in @ref step_31 "step-31".
1983 *
1984
1985 *
1986 * We will iterate over all cells on the designated min and max levels and
1987 * remove the corresponding flags. As an alternative, we could also flag
1988 * these cells for p-adaptation by setting future FE indices accordingly
1990 *
1991 * @code
1992 *   Assert(triangulation.n_levels() >= prm.min_h_level + 1 &&
1993 *   triangulation.n_levels() <= prm.max_h_level + 1,
1994 *   ExcInternalError());
1995 *  
1996 *   if (triangulation.n_levels() > prm.max_h_level)
1997 *   for (const auto &cell :
1998 *   triangulation.active_cell_iterators_on_level(prm.max_h_level))
1999 *   cell->clear_refine_flag();
2000 *  
2001 *   for (const auto &cell :
2002 *   triangulation.active_cell_iterators_on_level(prm.min_h_level))
2003 *   cell->clear_coarsen_flag();
2004 *  
2005 * @endcode
2006 *
2007 * At this stage, we have both the future FE indices and the classic refine
2012 *
2013
2014 *
2015 * Now, we would like to only impose one type of adaptation on cells, which
2016 * is what the next function will sort out for us. In short, on cells which
2018 * one and remove the h-adaptation one.
2019 *
2020 * @code
2021 *   hp::Refinement::choose_p_over_h(dof_handler);
2022 *  
2023 * @endcode
2024 *
2026 * only the grid will be updated, but also all previous future FE indices
2027 * will become active.
2028 *
2029
2030 *
2031 * Remember that we have attached functions to triangulation signals in the
2032 * constructor, will be triggered in this function call. So there is even
2034 * balancing, as well as we will limit the difference of p-levels between
2035 * neighboring cells.
2036 *
2037 * @code
2038 *   triangulation.execute_coarsening_and_refinement();
2039 *   }
2040 *  
2041 *  
2042 *  
2043 * @endcode
2044 *
2045 *
2046 * <a name="LaplaceProblemoutput_results"></a>
2048 *
2049
2050 *
2052 * like in @ref step_40 "step-40". In addition to the data containers that we prepared
2053 * throughout the tutorial, we would also like to write out the polynomial
2054 * degree of each finite element on the grid as well as the subdomain each
2056 * this function.
2057 *
2058 * @code
2059 *   template <int dim>
2060 *   void LaplaceProblem<dim>::output_results(const unsigned int cycle)
2061 *   {
2062 *   TimerOutput::Scope t(computing_timer, "output results");
2063 *  
2064 *   Vector<float> fe_degrees(triangulation.n_active_cells());
2065 *   for (const auto &cell : dof_handler.active_cell_iterators())
2066 *   if (cell->is_locally_owned())
2067 *   fe_degrees(cell->active_cell_index()) = cell->get_fe().degree;
2068 *  
2069 *   Vector<float> subdomain(triangulation.n_active_cells());
2070 *   for (auto &subd : subdomain)
2072 *  
2073 *   DataOut<dim> data_out;
2074 *   data_out.attach_dof_handler(dof_handler);
2075 *   data_out.add_data_vector(locally_relevant_solution, "solution");
2076 *   data_out.add_data_vector(fe_degrees, "fe_degree");
2077 *   data_out.add_data_vector(subdomain, "subdomain");
2078 *   data_out.add_data_vector(estimated_error_per_cell, "error");
2079 *   data_out.add_data_vector(hp_decision_indicators, "hp_indicator");
2080 *   data_out.build_patches(mapping_collection);
2081 *  
2082 *   data_out.write_vtu_with_pvtu_record(
2083 *   "./", "solution", cycle, mpi_communicator, 2, 1);
2084 *   }
2085 *  
2086 *  
2087 *  
2088 * @endcode
2089 *
2090 *
2091 * <a name="LaplaceProblemrun"></a>
2092 * <h4>LaplaceProblem::run</h4>
2093 *
2094
2095 *
2096 * The actual run function again looks very familiar to @ref step_40 "step-40". The only
2098 * Here, we will pre-calculate the Legendre transformation matrices. In
2101 * calculate them all at once before the actual time measurement begins. We
2103 *
2104 * @code
2105 *   template <int dim>
2107 *   {
2108 *   pcout << "Running with Trilinos on "
2109 *   << Utilities::MPI::n_mpi_processes(mpi_communicator)
2110 *   << " MPI rank(s)..." << std::endl;
2111 *  
2112 *   {
2113 *   pcout << "Calculating transformation matrices..." << std::endl;
2114 *   TimerOutput::Scope t(computing_timer, "calculate transformation");
2115 *   legendre->precalculate_all_transformation_matrices();
2116 *   }
2117 *  
2118 *   for (unsigned int cycle = 0; cycle < prm.n_cycles; ++cycle)
2119 *   {
2120 *   pcout << "Cycle " << cycle << ':' << std::endl;
2121 *  
2122 *   if (cycle == 0)
2123 *   initialize_grid();
2124 *   else
2125 *   adapt_resolution();
2126 *  
2127 *   setup_system();
2128 *  
2130 *  
2131 *   solve_system();
2132 *  
2134 *  
2135 *   if (Utilities::MPI::n_mpi_processes(mpi_communicator) <= 32)
2136 *   output_results(cycle);
2137 *  
2138 *   computing_timer.print_summary();
2139 *   computing_timer.reset();
2140 *  
2141 *   pcout << std::endl;
2142 *   }
2143 *   }
2144 *   } // namespace Step75
2145 *  
2146 *  
2147 *  
2148 * @endcode
2149 *
2150 *
2151 * <a name="main"></a>
2152 * <h4>main()</h4>
2153 *
2154
2155 *
2156 * The final function is the <code>main</code> function that will ultimately
2157 * create and run a LaplaceOperator instantiation. Its structure is similar to
2159 *
2160 * @code
2161 *   int main(int argc, char *argv[])
2162 *   {
2163 *   try
2164 *   {
2165 *   using namespace dealii;
2166 *   using namespace Step75;
2167 *  
2169 *  
2170 *   Parameters prm;
2172 *   laplace_problem.run();
2173 *   }
2174 *   catch (std::exception &exc)
2175 *   {
2176 *   std::cerr << std::endl
2177 *   << std::endl
2178 *   << "----------------------------------------------------"
2179 *   << std::endl;
2180 *   std::cerr << "Exception on processing: " << std::endl
2181 *   << exc.what() << std::endl
2182 *   << "Aborting!" << std::endl
2183 *   << "----------------------------------------------------"
2184 *   << std::endl;
2185 *  
2186 *   return 1;
2187 *   }
2188 *   catch (...)
2189 *   {
2190 *   std::cerr << std::endl
2191 *   << std::endl
2192 *   << "----------------------------------------------------"
2193 *   << std::endl;
2194 *   std::cerr << "Unknown exception!" << std::endl
2195 *   << "Aborting!" << std::endl
2196 *   << "----------------------------------------------------"
2197 *   << std::endl;
2198 *   return 1;
2199 *   }
2200 *  
2201 *   return 0;
2202 *   }
2203 * @endcode
2204<a name="Results"></a><h1>Results</h1>
2205
2206
2208release mode, your terminal output should look like this:
2209@code
2210Running with Trilinos on 4 MPI rank(s)...
2211Calculating transformation matrices...
2212Cycle 0:
2213 Number of active cells: 3072
2214 by partition: 768 768 768 768
2215 Number of degrees of freedom: 12545
2216 by partition: 3201 3104 3136 3104
2217 Number of constraints: 542
2218 by partition: 165 74 138 165
2219 Frequencies of poly. degrees: 2:3072
2220 Solved in 7 iterations.
2221
2222
2223+---------------------------------------------+------------+------------+
2224| Total wallclock time elapsed since start | 0.172s | |
2225| | | |
2226| Section | no. calls | wall time | % of total |
2227+---------------------------------+-----------+------------+------------+
2228| calculate transformation | 1 | 0.0194s | 11% |
2229| compute indicators | 1 | 0.00676s | 3.9% |
2230| initialize grid | 1 | 0.011s | 6.4% |
2231| output results | 1 | 0.0343s | 20% |
2232| setup system | 1 | 0.00839s | 4.9% |
2233| solve system | 1 | 0.0896s | 52% |
2234+---------------------------------+-----------+------------+------------+
2235
2236
2237Cycle 1:
2238 Number of active cells: 3351
2239 by partition: 875 761 843 872
2240 Number of degrees of freedom: 18228
2241 by partition: 4535 4735 4543 4415
2242 Number of constraints: 1202
2243 by partition: 303 290 326 283
2244 Frequencies of poly. degrees: 2:2522 3:829
2245 Solved in 7 iterations.
2246
2247
2248+---------------------------------------------+------------+------------+
2249| Total wallclock time elapsed since start | 0.165s | |
2250| | | |
2251| Section | no. calls | wall time | % of total |
2252+---------------------------------+-----------+------------+------------+
2253| adapt resolution | 1 | 0.00473s | 2.9% |
2254| compute indicators | 1 | 0.00764s | 4.6% |
2255| output results | 1 | 0.0243s | 15% |
2256| setup system | 1 | 0.00662s | 4% |
2257| solve system | 1 | 0.121s | 74% |
2258+---------------------------------+-----------+------------+------------+
2259
2260
2261...
2262
2263
2264Cycle 7:
2265 Number of active cells: 5610
2266 by partition: 1324 1483 1482 1321
2267 Number of degrees of freedom: 82047
2268 by partition: 21098 19960 20111 20878
2269 Number of constraints: 14357
2270 by partition: 3807 3229 3554 3767
2271 Frequencies of poly. degrees: 2:1126 3:1289 4:2725 5:465 6:5
2272 Solved in 7 iterations.
2273
2274
2275+---------------------------------------------+------------+------------+
2276| Total wallclock time elapsed since start | 1.83s | |
2277| | | |
2278| Section | no. calls | wall time | % of total |
2279+---------------------------------+-----------+------------+------------+
2280| adapt resolution | 1 | 0.00834s | 0.46% |
2281| compute indicators | 1 | 0.0178s | 0.97% |
2282| output results | 1 | 0.0434s | 2.4% |
2283| setup system | 1 | 0.0165s | 0.9% |
2284| solve system | 1 | 1.74s | 95% |
2285+---------------------------------+-----------+------------+------------+
2286@endcode
2287
2289differences in the number of active cells and degrees of freedom. This
2290is due to the fact that solver and preconditioner depend on the
2292the solution in the last digits and ultimately yields to different
2294
2295Furthermore, the number of iterations for the solver stays about the
2299
2300Let us have a look at the graphical output of the program. After all
2303partitioning on twelve processes on the left and the polynomial degrees
2304of finite elements on the right. In the left picture, each color
2306corresponds to the polynomial degree two and the darkest one corresponds
2307to degree six:
2308
2309<div class="twocolumn" style="width: 80%; text-align: center;">
2310 <div>
2311 <img src="https://www.dealii.org/images/steps/developer/step-75.subdomains-07.svg"
2312 alt="Partitioning after seven refinements.">
2313 </div>
2314 <div>
2315 <img src="https://www.dealii.org/images/steps/developer/step-75.fedegrees-07.svg"
2316 alt="Local approximation degrees after seven refinements.">
2317 </div>
2318</div>
2319
2320
2321
2322<a name="extensions"></a>
2323<a name="Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
2324
2325
2327hp-adaptive finite element methods. In the following paragraphs, you
2329extensions are already part of https://github.com/marcfehling/hpbox/,
2331around with.
2332
2333
2334<a name="Differenthpdecisionstrategies"></a><h4>Different hp-decision strategies</h4>
2335
2336
2339change the polynomial degree. We only presented the <i>Legendre
2340coefficient decay</i> strategy in this tutorial, while @ref step_27 "step-27"
2342
2343See the "possibilities for extensions" section of @ref step_27 "step-27" for an
2345for a detailed description.
2346
2347There, another strategy is mentioned that has not been shown in any
2349usage of this method for parallel distributed applications is more
2352flags, and we need to transfer the solution across refined meshes. For
2354function to the Triangulation::Signals::post_p4est_refinement signal in
2355a way that it will be called <i>after</i> the
2356hp::Refinement::limit_p_level_difference() function. At this stage, all
2357refinement flags and future FE indices are terminally set and a reliable
2360parallel::distributed::CellDataTransfer.
2361
2368
2369
2370<a name="Solvewithmatrixbasedmethods"></a><h4>Solve with matrix-based methods</h4>
2371
2372
2373This tutorial focuses solely on matrix-free strategies. All hp-adaptive
2375parallel distributed context.
2376
2377To create a system matrix, you can either use the
2378LaplaceOperator::get_system_matrix() function, or use an
2379<code>assemble_system()</code> function similar to the one of @ref step_27 "step-27".
2380You can then pass the system matrix to the solver as usual.
2381
2382You can time the results of both matrix-based and matrix-free
2384variant is faster.
2385
2386
2387<a name="Multigridvariants"></a><h4>Multigrid variants</h4>
2388
2389
2391coarse-grid solver (CG with AMG), smoother (Chebyshev smoother with
2392point Jacobi preconditioner), and geometric-coarsening scheme (global
2393coarsening) within the multigrid algorithm. Feel free to try out
2395 *
2396 *
2397<a name="PlainProg"></a>
2398<h1> The plain program</h1>
2399@include "step-75.cc"
2400*/
iterator begin() const
Definition array_view.h:594
bool empty() const
Definition array_view.h:585
iterator end() const
Definition array_view.h:603
value_type * data() const noexcept
Definition array_view.h:553
void reinit(value_type *starting_element, const std::size_t n_elements)
Definition array_view.h:413
std::size_t size() const
Definition array_view.h:576
static void estimate(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim - 1 > &quadrature, const std::map< types::boundary_id, const Function< spacedim, typename InputVector::value_type > * > &neumann_bc, const InputVector &solution, Vector< float > &error, const ComponentMask &component_mask=ComponentMask(), const Function< spacedim > *coefficients=nullptr, const unsigned int n_threads=numbers::invalid_unsigned_int, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id, const types::material_id material_id=numbers::invalid_material_id, const Strategy strategy=cell_diameter_over_24)
Definition point.h:112
@ wall_times
Definition timer.h:652
virtual types::subdomain_id locally_owned_subdomain() const
void coarsen_global(const unsigned int times=1)
virtual void execute_coarsening_and_refinement()
unsigned int size() const
Definition collection.h:265
static WeightingFunction ndofs_weighting(const std::pair< float, float > &coefficients)
Point< 2 > second
Definition grid_out.cc:4616
Point< 2 > first
Definition grid_out.cc:4615
unsigned int level
Definition grid_out.cc:4618
IteratorRange< active_cell_iterator > active_cell_iterators() const
__global__ void set(Number *val, const Number s, const size_type N)
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void loop(ITERATOR begin, std_cxx20::type_identity_t< ITERATOR > 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 make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints=AffineConstraints< number >(), const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
@ update_gradients
Shape function gradients.
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
std::array< double, dim > to_spherical(const Point< dim > &point)
void hyper_L(Triangulation< dim > &tria, const double left=-1., const double right=1., const bool colorize=false)
void subdivided_hyper_L(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &bottom_left, const Point< dim > &top_right, const std::vector< int > &n_cells_to_remove)
void refine(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold, const unsigned int max_to_mark=numbers::invalid_unsigned_int)
void coarsen(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
@ matrix
Contents is actually a matrix.
@ symmetric
Matrix is symmetric.
@ diagonal
Matrix is diagonal.
@ general
No special properties.
std::vector< std::shared_ptr< const Triangulation< dim, spacedim > > > create_geometric_coarsening_sequence(const Triangulation< dim, spacedim > &tria)
unsigned int create_next_polynomial_coarsening_degree(const unsigned int degree, const PolynomialCoarseningSequenceType &p_sequence)
std::vector< unsigned int > create_polynomial_coarsening_sequence(const unsigned int max_degree, const PolynomialCoarseningSequenceType &p_sequence)
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)
void compute_matrix(const MatrixFree< dim, Number, VectorizedArrayType > &matrix_free, const AffineConstraints< Number > &constraints, MatrixType &matrix, 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)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
FESeries::Legendre< dim, spacedim > default_fe_series(const hp::FECollection< dim, spacedim > &fe_collection, const unsigned int component=numbers::invalid_unsigned_int)
void coefficient_decay(FESeries::Legendre< dim, spacedim > &fe_legendre, const DoFHandler< dim, spacedim > &dof_handler, const VectorType &solution, Vector< float > &smoothness_indicators, const VectorTools::NormType regression_strategy=VectorTools::Linfty_norm, const double smallest_abs_coefficient=1e-10, const bool only_flagged_cells=false)
void partition(const SparsityPattern &sparsity_pattern, const unsigned int n_partitions, std::vector< unsigned int > &partition_indices, const Partitioner partitioner=Partitioner::metis)
void call(const std::function< RT()> &function, internal::return_value< RT > &ret_val)
void free(T *&pointer)
Definition cuda.h:97
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:150
T max(const T &t, const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:161
std::vector< T > gather(const MPI_Comm comm, const T &object_to_send, const unsigned int root_process=0)
void interpolate_boundary_values(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const std::map< types::boundary_id, const Function< spacedim, number > * > &function_map, std::map< types::global_dof_index, number > &boundary_values, const ComponentMask &component_mask=ComponentMask())
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
bool limit_p_level_difference(const DoFHandler< dim, spacedim > &dof_handler, const unsigned int max_difference=1, const unsigned int contains_fe_index=0)
void predict_error(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &error_indicators, Vector< Number > &predicted_errors, const double gamma_p=std::sqrt(0.4), const double gamma_h=2., const double gamma_n=1.)
void p_adaptivity_fixed_number(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &criteria, const double p_refine_fraction=0.5, const double p_coarsen_fraction=0.5, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_refine=std::greater_equal< Number >(), const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_coarsen=std::less_equal< Number >())
Definition hp.h:118
int(&) functions(const void *v1, const void *v2)
Definition mg.h:82
const types::material_id invalid_material_id
Definition types.h:250
const types::subdomain_id invalid_subdomain_id
Definition types.h:298
static const unsigned int invalid_unsigned_int
Definition types.h:213
void refine_and_coarsen_fixed_number(::Triangulation< dim, spacedim > &tria, const ::Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const types::global_cell_index max_n_cells=std::numeric_limits< types::global_cell_index >::max())
double legendre(unsigned int l, double x)
Definition cmath.h:75
STL namespace.
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
Definition types.h:33
unsigned short int fe_index
Definition types.h:60
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
boost::signals2::signal< void()> post_p4est_refinement
Definition tria.h:2419