Reference documentation for deal.II version 9.3.2
\(\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\}}\)
step-26.h
Go to the documentation of this file.
1 ,
679  * const unsigned int component) const
680  * {
681  * (void)component;
682  * Assert(component == 0, ExcIndexRange(component, 0, 1));
683  * return 0;
684  * }
685  *
686  *
687  *
688  * @endcode
689  *
690  *
691  * <a name="ThecodeHeatEquationcodeimplementation"></a>
692  * <h3>The <code>HeatEquation</code> implementation</h3>
693  *
694 
695  *
696  * It is time now for the implementation of the main class. Let's
697  * start with the constructor which selects a linear element, a time
698  * step constant at 1/500 (remember that one period of the source
699  * on the right hand side was set to 0.2 above, so we resolve each
700  * period with 100 time steps) and chooses the Crank Nicolson method
701  * by setting @f$\theta=1/2@f$.
702  *
703  * @code
704  * template <int dim>
705  * HeatEquation<dim>::HeatEquation()
706  * : fe(1)
707  * , dof_handler(triangulation)
708  * , time_step(1. / 500)
709  * , theta(0.5)
710  * {}
711  *
712  *
713  *
714  * @endcode
715  *
716  *
717  * <a name="codeHeatEquationsetup_systemcode"></a>
718  * <h4><code>HeatEquation::setup_system</code></h4>
719  *
720 
721  *
722  * The next function is the one that sets up the DoFHandler object,
723  * computes the constraints, and sets the linear algebra objects
724  * to their correct sizes. We also compute the mass and Laplace
725  * matrix here by simply calling two functions in the library.
726  *
727 
728  *
729  * Note that we do not take the hanging node constraints into account when
730  * assembling the matrices (both functions have an AffineConstraints argument
731  * that defaults to an empty object). This is because we are going to
732  * condense the constraints in run() after combining the matrices for the
733  * current time-step.
734  *
735  * @code
736  * template <int dim>
737  * void HeatEquation<dim>::setup_system()
738  * {
739  * dof_handler.distribute_dofs(fe);
740  *
741  * std::cout << std::endl
742  * << "===========================================" << std::endl
743  * << "Number of active cells: " << triangulation.n_active_cells()
744  * << std::endl
745  * << "Number of degrees of freedom: " << dof_handler.n_dofs()
746  * << std::endl
747  * << std::endl;
748  *
749  * constraints.clear();
750  * DoFTools::make_hanging_node_constraints(dof_handler, constraints);
751  * constraints.close();
752  *
753  * DynamicSparsityPattern dsp(dof_handler.n_dofs());
754  * DoFTools::make_sparsity_pattern(dof_handler,
755  * dsp,
756  * constraints,
757  * /*keep_constrained_dofs = */ true);
758  * sparsity_pattern.copy_from(dsp);
759  *
760  * mass_matrix.reinit(sparsity_pattern);
761  * laplace_matrix.reinit(sparsity_pattern);
762  * system_matrix.reinit(sparsity_pattern);
763  *
764  * MatrixCreator::create_mass_matrix(dof_handler,
765  * QGauss<dim>(fe.degree + 1),
766  * mass_matrix);
767  * MatrixCreator::create_laplace_matrix(dof_handler,
768  * QGauss<dim>(fe.degree + 1),
769  * laplace_matrix);
770  *
771  * solution.reinit(dof_handler.n_dofs());
772  * old_solution.reinit(dof_handler.n_dofs());
773  * system_rhs.reinit(dof_handler.n_dofs());
774  * }
775  *
776  *
777  * @endcode
778  *
779  *
780  * <a name="codeHeatEquationsolve_time_stepcode"></a>
781  * <h4><code>HeatEquation::solve_time_step</code></h4>
782  *
783 
784  *
785  * The next function is the one that solves the actual linear system
786  * for a single time step. There is nothing surprising here:
787  *
788  * @code
789  * template <int dim>
790  * void HeatEquation<dim>::solve_time_step()
791  * {
792  * SolverControl solver_control(1000, 1e-8 * system_rhs.l2_norm());
793  * SolverCG<Vector<double>> cg(solver_control);
794  *
795  * PreconditionSSOR<SparseMatrix<double>> preconditioner;
796  * preconditioner.initialize(system_matrix, 1.0);
797  *
798  * cg.solve(system_matrix, solution, system_rhs, preconditioner);
799  *
800  * constraints.distribute(solution);
801  *
802  * std::cout << " " << solver_control.last_step() << " CG iterations."
803  * << std::endl;
804  * }
805  *
806  *
807  *
808  * @endcode
809  *
810  *
811  * <a name="codeHeatEquationoutput_resultscode"></a>
812  * <h4><code>HeatEquation::output_results</code></h4>
813  *
814 
815  *
816  * Neither is there anything new in generating graphical output other than the
817  * fact that we tell the DataOut object what the current time and time step
818  * number is, so that this can be written into the output file :
819  *
820  * @code
821  * template <int dim>
822  * void HeatEquation<dim>::output_results() const
823  * {
824  * DataOut<dim> data_out;
825  *
826  * data_out.attach_dof_handler(dof_handler);
827  * data_out.add_data_vector(solution, "U");
828  *
829  * data_out.build_patches();
830  *
831  * data_out.set_flags(DataOutBase::VtkFlags(time, timestep_number));
832  *
833  * const std::string filename =
834  * "solution-" + Utilities::int_to_string(timestep_number, 3) + ".vtk";
835  * std::ofstream output(filename);
836  * data_out.write_vtk(output);
837  * }
838  *
839  *
840  * @endcode
841  *
842  *
843  * <a name="codeHeatEquationrefine_meshcode"></a>
844  * <h4><code>HeatEquation::refine_mesh</code></h4>
845  *
846 
847  *
848  * This function is the interesting part of the program. It takes care of
849  * the adaptive mesh refinement. The three tasks
850  * this function performs is to first find out which cells to
851  * refine/coarsen, then to actually do the refinement and eventually
852  * transfer the solution vectors between the two different grids. The first
853  * task is simply achieved by using the well-established Kelly error
854  * estimator on the solution. The second task is to actually do the
855  * remeshing. That involves only basic functions as well, such as the
856  * <code>refine_and_coarsen_fixed_fraction</code> that refines those cells
857  * with the largest estimated error that together make up 60 per cent of the
858  * error, and coarsens those cells with the smallest error that make up for
859  * a combined 40 per cent of the error. Note that for problems such as the
860  * current one where the areas where something is going on are shifting
861  * around, we want to aggressively coarsen so that we can move cells
862  * around to where it is necessary.
863  *
864 
865  *
866  * As already discussed in the introduction, too small a mesh leads to
867  * too small a time step, whereas too large a mesh leads to too little
868  * resolution. Consequently, after the first two steps, we have two
869  * loops that limit refinement and coarsening to an allowable range of
870  * cells:
871  *
872  * @code
873  * template <int dim>
874  * void HeatEquation<dim>::refine_mesh(const unsigned int min_grid_level,
875  * const unsigned int max_grid_level)
876  * {
877  * Vector<float> estimated_error_per_cell(triangulation.n_active_cells());
878  *
879  * KellyErrorEstimator<dim>::estimate(
880  * dof_handler,
881  * QGauss<dim - 1>(fe.degree + 1),
882  * std::map<types::boundary_id, const Function<dim> *>(),
883  * solution,
884  * estimated_error_per_cell);
885  *
886  * GridRefinement::refine_and_coarsen_fixed_fraction(triangulation,
887  * estimated_error_per_cell,
888  * 0.6,
889  * 0.4);
890  *
891  * if (triangulation.n_levels() > max_grid_level)
892  * for (const auto &cell :
893  * triangulation.active_cell_iterators_on_level(max_grid_level))
894  * cell->clear_refine_flag();
895  * for (const auto &cell :
896  * triangulation.active_cell_iterators_on_level(min_grid_level))
897  * cell->clear_coarsen_flag();
898  * @endcode
899  *
900  * These two loops above are slightly different but this is easily
901  * explained. In the first loop, instead of calling
902  * <code>triangulation.end()</code> we may as well have called
903  * <code>triangulation.end_active(max_grid_level)</code>. The two
904  * calls should yield the same iterator since iterators are sorted
905  * by level and there should not be any cells on levels higher than
906  * on level <code>max_grid_level</code>. In fact, this very piece
907  * of code makes sure that this is the case.
908  *
909 
910  *
911  * As part of mesh refinement we need to transfer the solution vectors
912  * from the old mesh to the new one. To this end we use the
913  * SolutionTransfer class and we have to prepare the solution vectors that
914  * should be transferred to the new grid (we will lose the old grid once
915  * we have done the refinement so the transfer has to happen concurrently
916  * with refinement). At the point where we call this function, we will
917  * have just computed the solution, so we no longer need the old_solution
918  * variable (it will be overwritten by the solution just after the mesh
919  * may have been refined, i.e., at the end of the time step; see below).
920  * In other words, we only need the one solution vector, and we copy it
921  * to a temporary object where it is safe from being reset when we further
922  * down below call <code>setup_system()</code>.
923  *
924 
925  *
926  * Consequently, we initialize a SolutionTransfer object by attaching
927  * it to the old DoF handler. We then prepare the triangulation and the
928  * data vector for refinement (in this order).
929  *
930  * @code
931  * SolutionTransfer<dim> solution_trans(dof_handler);
932  *
933  * Vector<double> previous_solution;
934  * previous_solution = solution;
935  * triangulation.prepare_coarsening_and_refinement();
936  * solution_trans.prepare_for_coarsening_and_refinement(previous_solution);
937  *
938  * @endcode
939  *
940  * Now everything is ready, so do the refinement and recreate the DoF
941  * structure on the new grid, and finally initialize the matrix structures
942  * and the new vectors in the <code>setup_system</code> function. Next, we
943  * actually perform the interpolation of the solution from old to new
944  * grid. The final step is to apply the hanging node constraints to the
945  * solution vector, i.e., to make sure that the values of degrees of
946  * freedom located on hanging nodes are so that the solution is
947  * continuous. This is necessary since SolutionTransfer only operates on
948  * cells locally, without regard to the neighborhood.
949  *
950  * @code
951  * triangulation.execute_coarsening_and_refinement();
952  * setup_system();
953  *
954  * solution_trans.interpolate(previous_solution, solution);
955  * constraints.distribute(solution);
956  * }
957  *
958  *
959  *
960  * @endcode
961  *
962  *
963  * <a name="codeHeatEquationruncode"></a>
964  * <h4><code>HeatEquation::run</code></h4>
965  *
966 
967  *
968  * This is the main driver of the program, where we loop over all
969  * time steps. At the top of the function, we set the number of
970  * initial global mesh refinements and the number of initial cycles of
971  * adaptive mesh refinement by repeating the first time step a few
972  * times. Then we create a mesh, initialize the various objects we will
973  * work with, set a label for where we should start when re-running
974  * the first time step, and interpolate the initial solution onto
975  * out mesh (we choose the zero function here, which of course we could
976  * do in a simpler way by just setting the solution vector to zero). We
977  * also output the initial time step once.
978  *
979 
980  *
981  * @note If you're an experienced programmer, you may be surprised
982  * that we use a <code>goto</code> statement in this piece of code!
983  * <code>goto</code> statements are not particularly well liked any
984  * more since Edsgar Dijkstra, one of the greats of computer science,
985  * wrote a letter in 1968 called "Go To Statement considered harmful"
986  * (see <a href="http://en.wikipedia.org/wiki/Considered_harmful">here</a>).
987  * The author of this code subscribes to this notion whole-heartedly:
988  * <code>goto</code> is hard to understand. In fact, deal.II contains
989  * virtually no occurrences: excluding code that was essentially
990  * transcribed from books and not counting duplicated code pieces,
991  * there are 3 locations in about 600,000 lines of code at the time
992  * this note is written; we also use it in 4 tutorial programs, in
993  * exactly the same context as here. Instead of trying to justify
994  * the occurrence here, let's first look at the code and we'll come
995  * back to the issue at the end of function.
996  *
997  * @code
998  * template <int dim>
999  * void HeatEquation<dim>::run()
1000  * {
1001  * const unsigned int initial_global_refinement = 2;
1002  * const unsigned int n_adaptive_pre_refinement_steps = 4;
1003  *
1005  * triangulation.refine_global(initial_global_refinement);
1006  *
1007  * setup_system();
1008  *
1009  * unsigned int pre_refinement_step = 0;
1010  *
1011  * Vector<double> tmp;
1012  * Vector<double> forcing_terms;
1013  *
1014  * start_time_iteration:
1015  *
1016  * time = 0.0;
1017  * timestep_number = 0;
1018  *
1019  * tmp.reinit(solution.size());
1020  * forcing_terms.reinit(solution.size());
1021  *
1022  *
1023  * VectorTools::interpolate(dof_handler,
1025  * old_solution);
1026  * solution = old_solution;
1027  *
1028  * output_results();
1029  *
1030  * @endcode
1031  *
1032  * Then we start the main loop until the computed time exceeds our
1033  * end time of 0.5. The first task is to build the right hand
1034  * side of the linear system we need to solve in each time step.
1035  * Recall that it contains the term @f$MU^{n-1}-(1-\theta)k_n AU^{n-1}@f$.
1036  * We put these terms into the variable system_rhs, with the
1037  * help of a temporary vector:
1038  *
1039  * @code
1040  * while (time <= 0.5)
1041  * {
1042  * time += time_step;
1043  * ++timestep_number;
1044  *
1045  * std::cout << "Time step " << timestep_number << " at t=" << time
1046  * << std::endl;
1047  *
1048  * mass_matrix.vmult(system_rhs, old_solution);
1049  *
1050  * laplace_matrix.vmult(tmp, old_solution);
1051  * system_rhs.add(-(1 - theta) * time_step, tmp);
1052  *
1053  * @endcode
1054  *
1055  * The second piece is to compute the contributions of the source
1056  * terms. This corresponds to the term @f$k_n
1057  * \left[ (1-\theta)F^{n-1} + \theta F^n \right]@f$. The following
1058  * code calls VectorTools::create_right_hand_side to compute the
1059  * vectors @f$F@f$, where we set the time of the right hand side
1060  * (source) function before we evaluate it. The result of this
1061  * all ends up in the forcing_terms variable:
1062  *
1063  * @code
1064  * RightHandSide<dim> rhs_function;
1065  * rhs_function.set_time(time);
1067  * QGauss<dim>(fe.degree + 1),
1068  * rhs_function,
1069  * tmp);
1070  * forcing_terms = tmp;
1071  * forcing_terms *= time_step * theta;
1072  *
1073  * rhs_function.set_time(time - time_step);
1075  * QGauss<dim>(fe.degree + 1),
1076  * rhs_function,
1077  * tmp);
1078  *
1079  * forcing_terms.add(time_step * (1 - theta), tmp);
1080  *
1081  * @endcode
1082  *
1083  * Next, we add the forcing terms to the ones that
1084  * come from the time stepping, and also build the matrix
1085  * @f$M+k_n\theta A@f$ that we have to invert in each time step.
1086  * The final piece of these operations is to eliminate
1087  * hanging node constrained degrees of freedom from the
1088  * linear system:
1089  *
1090  * @code
1091  * system_rhs += forcing_terms;
1092  *
1093  * system_matrix.copy_from(mass_matrix);
1094  * system_matrix.add(theta * time_step, laplace_matrix);
1095  *
1096  * constraints.condense(system_matrix, system_rhs);
1097  *
1098  * @endcode
1099  *
1100  * There is one more operation we need to do before we
1101  * can solve it: boundary values. To this end, we create
1102  * a boundary value object, set the proper time to the one
1103  * of the current time step, and evaluate it as we have
1104  * done many times before. The result is used to also
1105  * set the correct boundary values in the linear system:
1106  *
1107  * @code
1108  * {
1109  * BoundaryValues<dim> boundary_values_function;
1110  * boundary_values_function.set_time(time);
1111  *
1112  * std::map<types::global_dof_index, double> boundary_values;
1114  * 0,
1115  * boundary_values_function,
1116  * boundary_values);
1117  *
1118  * MatrixTools::apply_boundary_values(boundary_values,
1119  * system_matrix,
1120  * solution,
1121  * system_rhs);
1122  * }
1123  *
1124  * @endcode
1125  *
1126  * With this out of the way, all we have to do is solve the
1127  * system, generate graphical data, and...
1128  *
1129  * @code
1130  * solve_time_step();
1131  *
1132  * output_results();
1133  *
1134  * @endcode
1135  *
1136  * ...take care of mesh refinement. Here, what we want to do is
1137  * (i) refine the requested number of times at the very beginning
1138  * of the solution procedure, after which we jump to the top to
1139  * restart the time iteration, (ii) refine every fifth time
1140  * step after that.
1141  *
1142 
1143  *
1144  * The time loop and, indeed, the main part of the program ends
1145  * with starting into the next time step by setting old_solution
1146  * to the solution we have just computed.
1147  *
1148  * @code
1149  * if ((timestep_number == 1) &&
1150  * (pre_refinement_step < n_adaptive_pre_refinement_steps))
1151  * {
1152  * refine_mesh(initial_global_refinement,
1153  * initial_global_refinement +
1154  * n_adaptive_pre_refinement_steps);
1155  * ++pre_refinement_step;
1156  *
1157  * tmp.reinit(solution.size());
1158  * forcing_terms.reinit(solution.size());
1159  *
1160  * std::cout << std::endl;
1161  *
1162  * goto start_time_iteration;
1163  * }
1164  * else if ((timestep_number > 0) && (timestep_number % 5 == 0))
1165  * {
1166  * refine_mesh(initial_global_refinement,
1167  * initial_global_refinement +
1168  * n_adaptive_pre_refinement_steps);
1169  * tmp.reinit(solution.size());
1170  * forcing_terms.reinit(solution.size());
1171  * }
1172  *
1173  * old_solution = solution;
1174  * }
1175  * }
1176  * } // namespace Step26
1177  * @endcode
1178  *
1179  * Now that you have seen what the function does, let us come back to the issue
1180  * of the <code>goto</code>. In essence, what the code does is
1181  * something like this:
1182  * <div class=CodeFragmentInTutorialComment>
1183  * @code
1184  * void run ()
1185  * {
1186  * initialize;
1187  * start_time_iteration:
1188  * for (timestep=1...)
1189  * {
1190  * solve timestep;
1191  * if (timestep==1 && not happy with the result)
1192  * {
1193  * adjust some data structures;
1194  * goto start_time_iteration; // simply try again
1195  * }
1196  * postprocess;
1197  * }
1198  * }
1199  * @endcode
1200  * </div>
1201  * Here, the condition "happy with the result" is whether we'd like to keep
1202  * the current mesh or would rather refine the mesh and start over on the
1203  * new mesh. We could of course replace the use of the <code>goto</code>
1204  * by the following:
1205  * <div class=CodeFragmentInTutorialComment>
1206  * @code
1207  * void run ()
1208  * {
1209  * initialize;
1210  * while (true)
1211  * {
1212  * solve timestep;
1213  * if (not happy with the result)
1214  * adjust some data structures;
1215  * else
1216  * break;
1217  * }
1218  * postprocess;
1219  *
1220 
1221  * for (timestep=2...)
1222  * {
1223  * solve timestep;
1224  * postprocess;
1225  * }
1226  * }
1227  * @endcode
1228  * </div>
1229  * This has the advantage of getting rid of the <code>goto</code>
1230  * but the disadvantage of having to duplicate the code that implements
1231  * the "solve timestep" and "postprocess" operations in two different
1232  * places. This could be countered by putting these parts of the code
1233  * (sizable chunks in the actual implementation above) into their
1234  * own functions, but a <code>while(true)</code> loop with a
1235  * <code>break</code> statement is not really all that much easier
1236  * to read or understand than a <code>goto</code>.
1237  *
1238 
1239  *
1240  * In the end, one might simply agree that <i>in general</i>
1241  * <code>goto</code> statements are a bad idea but be pragmatic and
1242  * state that there may be occasions where they can help avoid code
1243  * duplication and awkward control flow. This may be one of these
1244  * places, and it matches the position Steve McConnell takes in his
1245  * excellent book "Code Complete" @cite CodeComplete about good
1246  * programming practices (see the mention of this book in the
1247  * introduction of @ref step_1 "step-1") that spends a surprising ten pages on the
1248  * question of <code>goto</code> in general.
1249  *
1250 
1251  *
1252  *
1253 
1254  *
1255  *
1256  * <a name="Thecodemaincodefunction"></a>
1257  * <h3>The <code>main</code> function</h3>
1258  *
1259 
1260  *
1261  * Having made it this far, there is, again, nothing
1262  * much to discuss for the main function of this
1263  * program: it looks like all such functions since @ref step_6 "step-6".
1264  *
1265  * @code
1266  * int main()
1267  * {
1268  * try
1269  * {
1270  * using namespace Step26;
1271  *
1272  * HeatEquation<2> heat_equation_solver;
1273  * heat_equation_solver.run();
1274  * }
1275  * catch (std::exception &exc)
1276  * {
1277  * std::cerr << std::endl
1278  * << std::endl
1279  * << "----------------------------------------------------"
1280  * << std::endl;
1281  * std::cerr << "Exception on processing: " << std::endl
1282  * << exc.what() << std::endl
1283  * << "Aborting!" << std::endl
1284  * << "----------------------------------------------------"
1285  * << std::endl;
1286  *
1287  * return 1;
1288  * }
1289  * catch (...)
1290  * {
1291  * std::cerr << std::endl
1292  * << std::endl
1293  * << "----------------------------------------------------"
1294  * << std::endl;
1295  * std::cerr << "Unknown exception!" << std::endl
1296  * << "Aborting!" << std::endl
1297  * << "----------------------------------------------------"
1298  * << std::endl;
1299  * return 1;
1300  * }
1301  *
1302  * return 0;
1303  * }
1304  * @endcode
1305 <a name="Results"></a><h1>Results</h1>
1306 
1307 
1308 As in many of the tutorials, the actual output of the program matters less
1309 than how we arrived there. Nonetheless, here it is:
1310 @code
1311 ===========================================
1312 Number of active cells: 48
1313 Number of degrees of freedom: 65
1314 
1315 Time step 1 at t=0.002
1316  7 CG iterations.
1317 
1318 ===========================================
1319 Number of active cells: 60
1320 Number of degrees of freedom: 81
1321 
1322 
1323 Time step 1 at t=0.002
1324  7 CG iterations.
1325 
1326 ===========================================
1327 Number of active cells: 105
1328 Number of degrees of freedom: 136
1329 
1330 
1331 Time step 1 at t=0.002
1332  7 CG iterations.
1333 
1334 [...]
1335 
1336 Time step 249 at t=0.498
1337  13 CG iterations.
1338 Time step 250 at t=0.5
1339  14 CG iterations.
1340 
1341 ===========================================
1342 Number of active cells: 1803
1343 Number of degrees of freedom: 2109
1344 @endcode
1345 
1346 Maybe of more interest is a visualization of the solution and the mesh on which
1347 it was computed:
1348 
1349 <img src="https://www.dealii.org/images/steps/developer/step-26.movie.gif" alt="Animation of the solution of step 26.">
1350 
1351 The movie shows how the two sources switch on and off and how the mesh reacts
1352 to this. It is quite obvious that the mesh as is is probably not the best we
1353 could come up with. We'll get back to this in the next section.
1354 
1355 
1356 <a name="extensions"></a>
1357 <a name="Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
1358 
1359 
1360 There are at least two areas where one can improve this program significantly:
1361 adaptive time stepping and a better choice of the mesh.
1362 
1363 <a name="Adaptivetimestepping"></a><h4>Adaptive time stepping</h4>
1364 
1365 
1366 Having chosen an implicit time stepping scheme, we are not bound by any
1367 CFL-like condition on the time step. Furthermore, because the time scales on
1368 which change happens on a given cell in the heat equation are not bound to the
1369 cells diameter (unlike the case with the wave equation, where we had a fixed
1370 speed of information transport that couples the temporal and spatial scales),
1371 we can choose the time step as we please. Or, better, choose it as we deem
1372 necessary for accuracy.
1373 
1374 Looking at the solution, it is clear that the action does not happen uniformly
1375 over time: a lot is changing around the time we switch on a source, things
1376 become less dramatic once a source is on for a little while, and we enter a
1377 long phase of decline when both sources are off. During these times, we could
1378 surely get away with a larger time step than before without sacrificing too
1379 much accuracy.
1380 
1381 The literature has many suggestions on how to choose the time step size
1382 adaptively. Much can be learned, for example, from the way ODE solvers choose
1383 their time steps. One can also be inspired by a posteriori error estimators
1384 that can, ideally, be written in a way that the consist of a temporal and a
1385 spatial contribution to the overall error. If the temporal one is too large,
1386 we should choose a smaller time step. Ideas in this direction can be found,
1387 for example, in the PhD thesis of a former principal developer of deal.II,
1388 Ralf Hartmann, published by the University of Heidelberg, Germany, in 2002.
1389 
1390 
1391 <a name="Bettertimesteppingmethods"></a><h4>Better time stepping methods</h4>
1392 
1393 
1394 We here use one of the simpler time stepping methods, namely the second order
1395 in time Crank-Nicolson method. However, more accurate methods such as
1396 Runge-Kutta methods are available and should be used as they do not represent
1397 much additional effort. It is not difficult to implement this for the current
1398 program, but a more systematic treatment is also given in @ref step_52 "step-52".
1399 
1400 
1401 <a name="Betterrefinementcriteria"></a><h4>Better refinement criteria</h4>
1402 
1403 
1404 If you look at the meshes in the movie above, it is clear that they are not
1405 particularly well suited to the task at hand. In fact, they look rather
1406 random.
1407 
1408 There are two factors at play. First, there are some islands where cells
1409 have been refined but that are surrounded by non-refined cells (and there
1410 are probably also a few occasional coarsened islands). These are not terrible,
1411 as they most of the time do not affect the approximation quality of the mesh,
1412 but they also don't help because so many of their additional degrees of
1413 freedom are in fact constrained by hanging node constraints. That said,
1414 this is easy to fix: the Triangulation class takes an argument to its
1415 constructor indicating a level of "mesh smoothing". Passing one of many
1416 possible flags, this instructs the triangulation to refine some additional
1417 cells, or not to refine some cells, so that the resulting mesh does not have
1418 these artifacts.
1419 
1420 The second problem is more severe: the mesh appears to lag the solution.
1421 The underlying reason is that we only adapt the mesh once every fifth
1422 time step, and only allow for a single refinement in these cases. Whenever a
1423 source switches on, the solution had been very smooth in this area before and
1424 the mesh was consequently rather coarse. This implies that the next time step
1425 when we refine the mesh, we will get one refinement level more in this area,
1426 and five time steps later another level, etc. But this is not enough: first,
1427 we should refine immediately when a source switches on (after all, in the
1428 current context we at least know what the right hand side is), and we should
1429 allow for more than one refinement level. Of course, all of this can be done
1430 using deal.II, it just requires a bit of algorithmic thinking in how to make
1431 this work!
1432 
1433 
1434 <a name="Positivitypreservation"></a><h4>Positivity preservation</h4>
1435 
1436 
1437 To increase the accuracy and resolution of your simulation in time, one
1438 typically decreases the time step size @f$k_n@f$. If you start playing around
1439 with the time step in this particular example, you will notice that the
1440 solution becomes partly negative, if @f$k_n@f$ is below a certain threshold.
1441 This is not what we would expect to happen (in nature).
1442 
1443 To get an idea of this behavior mathematically, let us consider a general,
1444 fully discrete problem:
1445 @f{align*}
1446  A u^{n} = B u^{n-1}.
1447 @f}
1448 The general form of the @f$i@f$th equation then reads:
1449 @f{align*}
1450  a_{ii} u^{n}_i &= b_{ii} u^{n-1}_i +
1451  \sum\limits_{j \in S_i} \left( b_{ij} u^{n-1}_j - a_{ij} u^{n}_j \right),
1452 @f}
1453 where @f$S_i@f$ is the set of degrees of freedom that DoF @f$i@f$ couples with (i.e.,
1454 for which either the matrix @f$A@f$ or matrix @f$B@f$ has a nonzero entry at position
1455 @f$(i,j)@f$). If all coefficients
1456 fulfill the following conditions:
1457 @f{align*}
1458  a_{ii} &> 0, & b_{ii} &\geq 0, & a_{ij} &\leq 0, & b_{ij} &\geq 0,
1459  &
1460  \forall j &\in S_i,
1461 @f}
1462 all solutions @f$u^{n}@f$ keep their sign from the previous ones @f$u^{n-1}@f$, and
1463 consequently from the initial values @f$u^0@f$. See e.g.
1464 <a href="http://bookstore.siam.org/cs14/">Kuzmin, H&auml;m&auml;l&auml;inen</a>
1465 for more information on positivity preservation.
1466 
1467 Depending on the PDE to solve and the time integration scheme used, one is
1468 able to deduce conditions for the time step @f$k_n@f$. For the heat equation with
1469 the Crank-Nicolson scheme,
1470 <a href="https://doi.org/10.2478/cmam-2010-0025">Schatz et. al.</a> have
1471 translated it to the following ones:
1472 @f{align*}
1473  (1 - \theta) k a_{ii} &\leq m_{ii},\qquad \forall i,
1474  &
1475  \theta k \left| a_{ij} \right| &\geq m_{ij},\qquad j \neq i,
1476 @f}
1477 where @f$M = m_{ij}@f$ denotes the mass matrix and @f$A = a_{ij}@f$ the stiffness
1478 matrix with @f$a_{ij} \leq 0@f$ for @f$j \neq i@f$, respectively. With
1479 @f$a_{ij} \leq 0@f$, we can formulate bounds for the global time step @f$k@f$ as
1480 follows:
1481 @f{align*}
1482  k_{\text{max}} &= \frac{ 1 }{ 1 - \theta }
1483  \min\left( \frac{ m_{ii} }{ a_{ii} } \right),~ \forall i,
1484  &
1485  k_{\text{min}} &= \frac{ 1 }{ \theta }
1486  \max\left( \frac{ m_{ij} }{ \left|a_{ij}\right| } \right),~ j \neq i.
1487 @f}
1488 In other words, the time step is constrained by <i>both a lower
1489 and upper bound</i> in case of a Crank-Nicolson scheme. These bounds should be
1490 considered along with the CFL condition to ensure significance of the performed
1491 simulations.
1492 
1493 Being unable to make the time step as small as we want to get more
1494 accuracy without losing the positivity property is annoying. It raises
1495 the question of whether we can at least <i>compute</i> the minimal time step
1496 we can choose to ensure positivity preservation in this particular tutorial.
1497 Indeed, we can use
1498 the SparseMatrix objects for both mass and stiffness that are created via
1499 the MatrixCreator functions. Iterating through each entry via SparseMatrixIterators
1500 lets us check for diagonal and off-diagonal entries to set a proper time step
1501 dynamically. For quadratic matrices, the diagonal element is stored as the
1502 first member of a row (see SparseMatrix documentation). An exemplary code
1503 snippet on how to grab the entries of interest from the <code>mass_matrix</code>
1504 is shown below.
1505 
1506 @code
1507 Assert (mass_matrix.m() == mass_matrix.n(), ExcNotQuadratic());
1508 const unsigned int num_rows = mass_matrix.m();
1509 double mass_matrix_min_diag = std::numeric_limits<double>::max(),
1510  mass_matrix_max_offdiag = 0.;
1511 
1512 SparseMatrixIterators::Iterator<double,true> row_it (&mass_matrix, 0);
1513 
1514 for(unsigned int m = 0; m<num_rows; ++m)
1515 {
1516  // check the diagonal element
1517  row_it = mass_matrix.begin(m);
1518  mass_matrix_min_diag = std::min(row_it->value(), mass_matrix_min_diag);
1519  ++row_it;
1520 
1521  // check the off-diagonal elements
1522  for(; row_it != mass_matrix.end(m); ++row_it)
1523  mass_matrix_max_offdiag = std::max(row_it->value(), mass_matrix_max_offdiag);
1524 }
1525 @endcode
1526 
1527 Using the information so computed, we can bound the time step via the formulas
1528 above.
1529  *
1530  *
1531 <a name="PlainProg"></a>
1532 <h1> The plain program</h1>
1533 @include "step-26.cc"
1534 */
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &t)
Point< 2 > second
Definition: grid_out.cc:4588
Point< 2 > first
Definition: grid_out.cc:4587
__global__ void set(Number *val, const Number s, const size_type N)
static ::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
#define Assert(cond, exc)
Definition: exceptions.h:1465
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 add(const std::vector< size_type > &indices, const std::vector< OtherNumber > &values)
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
void random(DoFHandler< dim, spacedim > &dof_handler)
void hyper_L(Triangulation< dim > &tria, const double left=-1., const double right=1., const bool colorize=false)
void refine(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold, const unsigned int max_to_mark=numbers::invalid_unsigned_int)
double diameter(const Triangulation< dim, spacedim > &tria)
Definition: grid_tools.cc:81
@ matrix
Contents is actually a matrix.
static const char A
static const types::blas_int one
void mass_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const double factor=1.)
Definition: l2.h:58
void apply_boundary_values(const std::map< types::global_dof_index, number > &boundary_values, SparseMatrix< number > &matrix, Vector< number > &solution, Vector< number > &right_hand_side, const bool eliminate_columns=true)
Definition: matrix_tools.cc:81
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
VectorType::value_type * end(VectorType &V)
void create_right_hand_side(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim > &q, const Function< spacedim, typename VectorType::value_type > &rhs, VectorType &rhs_vector, const AffineConstraints< typename VectorType::value_type > &constraints=AffineConstraints< typename VectorType::value_type >())
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_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 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:691
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation