deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\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-74.h
Go to the documentation of this file.
1) const
277 *   {
278 *   using numbers::PI;
279 *   for (unsigned int i = 0; i < values.size(); ++i)
280 *   values[i] =
281 *   std::sin(2. * PI * points[i][0]) * std::sin(2. * PI * points[i][1]);
282 *   }
283 *  
284 *  
285 *  
286 *   template <int dim>
288 *   SmoothSolution<dim>::gradient(const Point<dim> &point,
289 *   const unsigned int /*component*/) const
290 *   {
291 *   Tensor<1, dim> return_value;
292 *   using numbers::PI;
293 *   return_value[0] =
294 *   2. * PI * std::cos(2. * PI * point[0]) * std::sin(2. * PI * point[1]);
295 *   return_value[1] =
296 *   2. * PI * std::sin(2. * PI * point[0]) * std::cos(2. * PI * point[1]);
297 *   return return_value;
298 *   }
299 *  
300 *  
301 *  
302 * @endcode
303 *
304 * The corresponding right-hand side of the smooth function:
305 *
306 * @code
307 *   template <int dim>
308 *   class SmoothRightHandSide : public Function<dim>
309 *   {
310 *   public:
311 *   SmoothRightHandSide()
312 *   : Function<dim>()
313 *   {}
314 *  
315 *   virtual void value_list(const std::vector<Point<dim>> &points,
316 *   std::vector<double> &values,
317 *   const unsigned int /*component*/) const override;
318 *   };
319 *  
320 *  
321 *  
322 *   template <int dim>
323 *   void
324 *   SmoothRightHandSide<dim>::value_list(const std::vector<Point<dim>> &points,
325 *   std::vector<double> &values,
326 *   const unsigned int /*component*/) const
327 *   {
328 *   using numbers::PI;
329 *   for (unsigned int i = 0; i < values.size(); ++i)
330 *   values[i] = 8. * PI * PI * std::sin(2. * PI * points[i][0]) *
331 *   std::sin(2. * PI * points[i][1]);
332 *   }
333 *  
334 *  
335 *  
336 * @endcode
337 *
338 * The right-hand side that corresponds to the function
340 * assume that the diffusion coefficient @f$\nu = 1@f$:
341 *
342 * @code
343 *   template <int dim>
344 *   class SingularRightHandSide : public Function<dim>
345 *   {
346 *   public:
347 *   SingularRightHandSide()
348 *   : Function<dim>()
349 *   {}
350 *  
351 *   virtual void value_list(const std::vector<Point<dim>> &points,
352 *   std::vector<double> &values,
353 *   const unsigned int /*component*/) const override;
354 *  
355 *   private:
357 *   };
358 *  
359 *  
360 *  
361 *   template <int dim>
362 *   void
363 *   SingularRightHandSide<dim>::value_list(const std::vector<Point<dim>> &points,
364 *   std::vector<double> &values,
365 *   const unsigned int /*component*/) const
366 *   {
367 *   for (unsigned int i = 0; i < values.size(); ++i)
368 *   values[i] = -ref.laplacian(points[i]);
369 *   }
370 *  
371 *  
372 *  
373 * @endcode
374 *
375 *
376 * <a name="step_74-Auxiliaryfunctions"></a>
377 * <h3>Auxiliary functions</h3>
378 * This function computes the penalty @f$\sigma@f$.
379 *
380 * @code
381 *   double get_penalty_factor(const unsigned int fe_degree,
382 *   const double cell_extent_left,
383 *   const double cell_extent_right)
384 *   {
385 *   const unsigned int degree = std::max(1U, fe_degree);
386 *   return degree * (degree + 1.) * 0.5 *
387 *   (1. / cell_extent_left + 1. / cell_extent_right);
388 *   }
389 *  
390 *  
391 * @endcode
392 *
393 *
394 * <a name="step_74-TheCopyData"></a>
395 * <h3>The CopyData</h3>
396 * In the following, we define "Copy" objects for the MeshWorker::mesh_loop(),
397 * which is essentially the same as @ref step_12 "step-12". Note that the
398 * "Scratch" object is not defined here because we use
399 * MeshWorker::ScratchData<dim> instead. (The use of "Copy" and "Scratch"
400 * objects is extensively explained in the WorkStream namespace documentation.
401 *
402 * @code
403 *   struct CopyDataFace
404 *   {
406 *   std::vector<types::global_dof_index> joint_dof_indices;
407 *   std::array<double, 2> values;
408 *   std::array<unsigned int, 2> cell_indices;
409 *   };
410 *  
411 *  
412 *  
413 *   struct CopyData
414 *   {
416 *   Vector<double> cell_rhs;
417 *   std::vector<types::global_dof_index> local_dof_indices;
418 *   std::vector<CopyDataFace> face_data;
419 *   double value;
420 *   unsigned int cell_index;
421 *  
422 *  
423 *   template <class Iterator>
424 *   void reinit(const Iterator &cell, const unsigned int dofs_per_cell)
425 *   {
426 *   cell_matrix.reinit(dofs_per_cell, dofs_per_cell);
427 *   cell_rhs.reinit(dofs_per_cell);
428 *   local_dof_indices.resize(dofs_per_cell);
429 *   cell->get_dof_indices(local_dof_indices);
430 *   }
431 *   };
432 *  
433 *  
434 *  
435 * @endcode
436 *
437 *
438 * <a name="step_74-TheSIPGLaplaceclass"></a>
439 * <h3>The SIPGLaplace class</h3>
440 * After these preparations, we proceed with the main class of this program,
441 * called `SIPGLaplace`. The overall structure of the class is as in many
442 * of the other tutorial programs. Major differences will only come up in the
443 * implementation of the assemble functions, since we use FEInterfaceValues to
444 * assemble face terms.
445 *
446 * @code
447 *   template <int dim>
448 *   class SIPGLaplace
449 *   {
450 *   public:
451 *   SIPGLaplace(const TestCase &test_case);
452 *   void run();
453 *  
454 *   private:
455 *   void setup_system();
456 *   void assemble_system();
457 *   void solve();
458 *   void refine_grid();
459 *   void output_results(const unsigned int cycle) const;
460 *  
461 *   void compute_errors();
462 *   void compute_error_estimate();
463 *   double compute_energy_norm_error();
464 *  
465 *   Triangulation<dim> triangulation;
466 *   const unsigned int degree;
467 *   const QGauss<dim> quadrature;
468 *   const QGauss<dim - 1> face_quadrature;
469 *   const QGauss<dim> quadrature_overintegration;
470 *   const QGauss<dim - 1> face_quadrature_overintegration;
471 *   const MappingQ1<dim> mapping;
472 *  
473 *   using ScratchData = MeshWorker::ScratchData<dim>;
474 *  
475 *   const FE_DGQ<dim> fe;
476 *   DoFHandler<dim> dof_handler;
477 *  
478 *   SparsityPattern sparsity_pattern;
479 *   SparseMatrix<double> system_matrix;
480 *   Vector<double> solution;
481 *   Vector<double> system_rhs;
482 *  
483 * @endcode
484 *
485 * The remainder of the class's members are used for the following:
486 * - Vectors to store error estimator square and energy norm square per
487 * cell.
488 * - Print convergence rate and errors on the screen.
489 * - The fiffusion coefficient @f$\nu@f$ is set to 1.
490 * - Members that store information about the test case to be computed.
491 *
492 * @code
493 *   Vector<double> estimated_error_square_per_cell;
494 *   Vector<double> energy_norm_square_per_cell;
495 *  
496 *   ConvergenceTable convergence_table;
497 *  
498 *   const double diffusion_coefficient = 1.;
499 *  
500 *   const TestCase test_case;
501 *   std::unique_ptr<const Function<dim>> exact_solution;
502 *   std::unique_ptr<const Function<dim>> rhs_function;
503 *   };
504 *  
505 * @endcode
506 *
507 * The constructor here takes the test case as input and then
508 * determines the correct solution and right-hand side classes. The
509 * remaining member variables are initialized in the obvious way.
510 *
511 * @code
512 *   template <int dim>
513 *   SIPGLaplace<dim>::SIPGLaplace(const TestCase &test_case)
514 *   : degree(3)
515 *   , quadrature(degree + 1)
516 *   , face_quadrature(degree + 1)
517 *   , quadrature_overintegration(degree + 2)
518 *   , face_quadrature_overintegration(degree + 2)
519 *   , mapping()
520 *   , fe(degree)
521 *   , dof_handler(triangulation)
522 *   , test_case(test_case)
523 *   {
524 *   if (test_case == TestCase::convergence_rate)
525 *   {
526 *   exact_solution = std::make_unique<const SmoothSolution<dim>>();
527 *   rhs_function = std::make_unique<const SmoothRightHandSide<dim>>();
528 *   }
529 *  
530 *   else if (test_case == TestCase::l_singularity)
531 *   {
532 *   exact_solution =
533 *   std::make_unique<const Functions::LSingularityFunction>();
534 *   rhs_function = std::make_unique<const SingularRightHandSide<dim>>();
535 *   }
536 *   else
537 *   AssertThrow(false, ExcNotImplemented());
538 *   }
539 *  
540 *  
541 *  
542 *   template <int dim>
543 *   void SIPGLaplace<dim>::setup_system()
544 *   {
545 *   dof_handler.distribute_dofs(fe);
546 *   DynamicSparsityPattern dsp(dof_handler.n_dofs());
547 *   DoFTools::make_flux_sparsity_pattern(dof_handler, dsp);
548 *   sparsity_pattern.copy_from(dsp);
549 *  
550 *   system_matrix.reinit(sparsity_pattern);
551 *   solution.reinit(dof_handler.n_dofs());
552 *   system_rhs.reinit(dof_handler.n_dofs());
553 *   }
554 *  
555 *  
556 *  
557 * @endcode
558 *
559 *
560 * <a name="step_74-Theassemble_systemfunction"></a>
561 * <h3>The assemble_system function</h3>
562 * The assemble function here is similar to that in @ref step_12 "step-12" and @ref step_47 "step-47".
563 * Different from assembling by hand, we just need to focus
564 * on assembling on each cell, each boundary face, and each
565 * interior face. The loops over cells and faces are handled
566 * automatically by MeshWorker::mesh_loop().
567 *
568
569 *
570 * The function starts by defining a local (lambda) function that is
571 * used to integrate the cell terms:
572 *
573 * @code
574 *   template <int dim>
575 *   void SIPGLaplace<dim>::assemble_system()
576 *   {
577 *   const auto cell_worker =
578 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
579 *   ScratchData &scratch_data,
580 *   CopyData &copy_data) {
581 *   const FEValues<dim> &fe_v = scratch_data.reinit(cell);
582 *   const unsigned int dofs_per_cell = fe_v.dofs_per_cell;
583 *   copy_data.reinit(cell, dofs_per_cell);
584 *  
585 *   const std::vector<Point<dim>> &q_points =
586 *   scratch_data.get_quadrature_points();
587 *   const unsigned int n_q_points = q_points.size();
588 *   const std::vector<double> &JxW = scratch_data.get_JxW_values();
589 *  
590 *   std::vector<double> rhs(n_q_points);
591 *   rhs_function->value_list(q_points, rhs);
592 *  
593 *   for (unsigned int point = 0; point < n_q_points; ++point)
594 *   for (unsigned int i = 0; i < fe_v.dofs_per_cell; ++i)
595 *   {
596 *   for (unsigned int j = 0; j < fe_v.dofs_per_cell; ++j)
597 *   copy_data.cell_matrix(i, j) +=
598 *   diffusion_coefficient * // nu
599 *   fe_v.shape_grad(i, point) * // grad v_h
600 *   fe_v.shape_grad(j, point) * // grad u_h
601 *   JxW[point]; // dx
602 *  
603 *   copy_data.cell_rhs(i) += fe_v.shape_value(i, point) * // v_h
604 *   rhs[point] * // f
605 *   JxW[point]; // dx
606 *   }
607 *   };
608 *  
609 * @endcode
610 *
611 * Next, we need a function that assembles face integrals on the boundary:
612 *
613 * @code
614 *   const auto boundary_worker =
615 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
616 *   const unsigned int &face_no,
617 *   ScratchData &scratch_data,
618 *   CopyData &copy_data) {
619 *   const FEFaceValuesBase<dim> &fe_fv = scratch_data.reinit(cell, face_no);
620 *  
621 *   const std::vector<Point<dim>> &q_points =
622 *   scratch_data.get_quadrature_points();
623 *   const unsigned int n_q_points = q_points.size();
624 *   const unsigned int dofs_per_cell = fe_fv.dofs_per_cell;
625 *  
626 *   const std::vector<double> &JxW = scratch_data.get_JxW_values();
627 *   const std::vector<Tensor<1, dim>> &normals =
628 *   scratch_data.get_normal_vectors();
629 *  
630 *   std::vector<double> g(n_q_points);
631 *   exact_solution->value_list(q_points, g);
632 *  
633 *   const double extent1 = cell->measure() / cell->face(face_no)->measure();
634 *   const double penalty = get_penalty_factor(degree, extent1, extent1);
635 *  
636 *   for (unsigned int point = 0; point < n_q_points; ++point)
637 *   {
638 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
639 *   for (unsigned int j = 0; j < dofs_per_cell; ++j)
640 *   copy_data.cell_matrix(i, j) +=
641 *   (-diffusion_coefficient * // - nu
642 *   fe_fv.shape_value(i, point) * // v_h
643 *   (fe_fv.shape_grad(j, point) * // (grad u_h .
644 *   normals[point]) // n)
645 *  
646 *   - diffusion_coefficient * // - nu
647 *   (fe_fv.shape_grad(i, point) * // (grad v_h .
648 *   normals[point]) * // n)
649 *   fe_fv.shape_value(j, point) // u_h
650 *  
651 *   + diffusion_coefficient * penalty * // + nu sigma
652 *   fe_fv.shape_value(i, point) * // v_h
653 *   fe_fv.shape_value(j, point) // u_h
654 *  
655 *   ) *
656 *   JxW[point]; // dx
657 *  
658 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
659 *   copy_data.cell_rhs(i) +=
660 *   (-diffusion_coefficient * // - nu
661 *   (fe_fv.shape_grad(i, point) * // (grad v_h .
662 *   normals[point]) * // n)
663 *   g[point] // g
664 *  
665 *  
666 *   + diffusion_coefficient * penalty * // + nu sigma
667 *   fe_fv.shape_value(i, point) * g[point] // v_h g
668 *  
669 *   ) *
670 *   JxW[point]; // dx
671 *   }
672 *   };
673 *  
674 * @endcode
675 *
676 * Finally, a function that assembles face integrals on interior
677 * faces. To reinitialize FEInterfaceValues, we need to pass
678 * cells, face and subface indices (for adaptive refinement) to
679 * the reinit() function of FEInterfaceValues:
680 *
681 * @code
682 *   const auto face_worker =
683 *   [&](const typename DoFHandler<dim>::cell_iterator &cell,
684 *   const unsigned int &f,
685 *   const unsigned int &sf,
686 *   const typename DoFHandler<dim>::cell_iterator &ncell,
687 *   const unsigned int &nf,
688 *   const unsigned int &nsf,
689 *   ScratchData &scratch_data,
690 *   CopyData &copy_data) {
691 *   const FEInterfaceValues<dim> &fe_iv =
692 *   scratch_data.reinit(cell, f, sf, ncell, nf, nsf);
693 *  
694 *   copy_data.face_data.emplace_back();
695 *   CopyDataFace &copy_data_face = copy_data.face_data.back();
696 *   const unsigned int n_dofs_face = fe_iv.n_current_interface_dofs();
697 *   copy_data_face.joint_dof_indices = fe_iv.get_interface_dof_indices();
698 *   copy_data_face.cell_matrix.reinit(n_dofs_face, n_dofs_face);
699 *  
700 *   const std::vector<double> &JxW = fe_iv.get_JxW_values();
701 *   const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
702 *  
703 *   const double extent1 = cell->measure() / cell->face(f)->measure();
704 *   const double extent2 = ncell->measure() / ncell->face(nf)->measure();
705 *   const double penalty = get_penalty_factor(degree, extent1, extent2);
706 *  
707 *   for (const unsigned int point : fe_iv.quadrature_point_indices())
708 *   {
709 *   for (const unsigned int i : fe_iv.dof_indices())
710 *   for (const unsigned int j : fe_iv.dof_indices())
711 *   copy_data_face.cell_matrix(i, j) +=
712 *   (-diffusion_coefficient * // - nu
713 *   fe_iv.jump_in_shape_values(i, point) * // [v_h]
714 *   (fe_iv.average_of_shape_gradients(j,
715 *   point) * // ({grad u_h} .
716 *   normals[point]) // n)
717 *  
718 *   - diffusion_coefficient * // - nu
719 *   (fe_iv.average_of_shape_gradients(i,
720 *   point) * // (grad v_h .
721 *   normals[point]) * // n)
722 *   fe_iv.jump_in_shape_values(j, point) // [u_h]
723 *  
724 *   + diffusion_coefficient * penalty * // + nu sigma
725 *   fe_iv.jump_in_shape_values(i, point) * // [v_h]
726 *   fe_iv.jump_in_shape_values(j, point) // [u_h]
727 *  
728 *   ) *
729 *   JxW[point]; // dx
730 *   }
731 *   };
732 *  
733 * @endcode
734 *
735 * The following lambda function will then copy data into the
736 * global matrix and right-hand side. Though there are no hanging
737 * node constraints in DG discretization, we define an empty
738 * AffineConstraints object that allows us to use the
739 * AffineConstraints::distribute_local_to_global() functionality.
740 *
741 * @code
742 *   AffineConstraints<double> constraints;
743 *   constraints.close();
744 *   const auto copier = [&](const CopyData &c) {
745 *   constraints.distribute_local_to_global(c.cell_matrix,
746 *   c.cell_rhs,
747 *   c.local_dof_indices,
748 *   system_matrix,
749 *   system_rhs);
750 *  
751 * @endcode
752 *
753 * Copy data from interior face assembly to the global matrix.
754 *
755 * @code
756 *   for (const CopyDataFace &cdf : c.face_data)
757 *   {
758 *   constraints.distribute_local_to_global(cdf.cell_matrix,
759 *   cdf.joint_dof_indices,
760 *   system_matrix);
761 *   }
762 *   };
763 *  
764 *  
765 * @endcode
766 *
767 * With the assembly functions defined, we can now create
768 * ScratchData and CopyData objects, and pass them together with
769 * the lambda functions above to MeshWorker::mesh_loop(). In
770 * addition, we need to specify that we want to assemble on
771 * interior faces exactly once.
772 *
773 * @code
774 *   const UpdateFlags cell_flags = update_values | update_gradients |
775 *   update_quadrature_points | update_JxW_values;
776 *   const UpdateFlags face_flags = update_values | update_gradients |
777 *   update_quadrature_points |
778 *   update_normal_vectors | update_JxW_values;
779 *  
780 *   ScratchData scratch_data(
781 *   mapping, fe, quadrature, cell_flags, face_quadrature, face_flags);
782 *   CopyData copy_data;
783 *  
784 *   MeshWorker::mesh_loop(dof_handler.begin_active(),
785 *   dof_handler.end(),
786 *   cell_worker,
787 *   copier,
788 *   scratch_data,
789 *   copy_data,
790 *   MeshWorker::assemble_own_cells |
791 *   MeshWorker::assemble_boundary_faces |
792 *   MeshWorker::assemble_own_interior_faces_once,
793 *   boundary_worker,
794 *   face_worker);
795 *   }
796 *  
797 *  
798 *  
799 * @endcode
800 *
801 *
802 * <a name="step_74-Thesolveandoutput_resultsfunction"></a>
803 * <h3>The solve() and output_results() function</h3>
804 * The following two functions are entirely standard and without difficulty.
805 *
806 * @code
807 *   template <int dim>
808 *   void SIPGLaplace<dim>::solve()
809 *   {
810 *   SparseDirectUMFPACK A_direct;
811 *   A_direct.initialize(system_matrix);
812 *   A_direct.vmult(solution, system_rhs);
813 *   }
814 *  
815 *  
816 *  
817 *   template <int dim>
818 *   void SIPGLaplace<dim>::output_results(const unsigned int cycle) const
819 *   {
820 *   const std::string filename = "sol_Q" + Utilities::int_to_string(degree, 1) +
821 *   "-" + Utilities::int_to_string(cycle, 2) +
822 *   ".vtu";
823 *   std::ofstream output(filename);
824 *  
825 *   DataOut<dim> data_out;
826 *   data_out.attach_dof_handler(dof_handler);
827 *   data_out.add_data_vector(solution, "u", DataOut<dim>::type_dof_data);
828 *   data_out.build_patches(mapping);
829 *   data_out.write_vtu(output);
830 *   }
831 *  
832 *  
833 * @endcode
834 *
835 *
836 * <a name="step_74-Thecompute_error_estimatefunction"></a>
837 * <h3>The compute_error_estimate() function</h3>
838 * The assembly of the error estimator here is quite similar to
839 * that of the global matrix and right-had side and can be handled
840 * by the MeshWorker::mesh_loop() framework. To understand what
841 * each of the local (lambda) functions is doing, recall first that
842 * the local cell residual is defined as
843 * @f$h_K^2 \left\| f + \nu \Delta u_h \right\|_K^2@f$:
844 *
845 * @code
846 *   template <int dim>
847 *   void SIPGLaplace<dim>::compute_error_estimate()
848 *   {
849 *   const auto cell_worker =
850 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
851 *   ScratchData &scratch_data,
852 *   CopyData &copy_data) {
853 *   const FEValues<dim> &fe_v = scratch_data.reinit(cell);
854 *  
855 *   copy_data.cell_index = cell->active_cell_index();
856 *  
857 *   const std::vector<Point<dim>> &q_points = fe_v.get_quadrature_points();
858 *   const unsigned int n_q_points = q_points.size();
859 *   const std::vector<double> &JxW = fe_v.get_JxW_values();
860 *  
861 *   std::vector<Tensor<2, dim>> hessians(n_q_points);
862 *   fe_v.get_function_hessians(solution, hessians);
863 *  
864 *   std::vector<double> rhs(n_q_points);
865 *   rhs_function->value_list(q_points, rhs);
866 *  
867 *   const double hk = cell->diameter();
868 *   double residual_norm_square = 0;
869 *  
870 *   for (unsigned int point = 0; point < n_q_points; ++point)
871 *   {
872 *   const double residual =
873 *   rhs[point] + diffusion_coefficient * trace(hessians[point]);
874 *   residual_norm_square += residual * residual * JxW[point];
875 *   }
876 *   copy_data.value = hk * hk * residual_norm_square;
877 *   };
878 *  
879 * @endcode
880 *
881 * Next compute boundary terms @f$\sum_{f\in \partial K \cap \partial \Omega}
882 * \sigma \left\| [ u_h-g_D ] \right\|_f^2 @f$:
883 *
884 * @code
885 *   const auto boundary_worker =
886 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
887 *   const unsigned int &face_no,
888 *   ScratchData &scratch_data,
889 *   CopyData &copy_data) {
890 *   const FEFaceValuesBase<dim> &fe_fv = scratch_data.reinit(cell, face_no);
891 *  
892 *   const std::vector<Point<dim>> &q_points = fe_fv.get_quadrature_points();
893 *   const unsigned n_q_points = q_points.size();
894 *  
895 *   const std::vector<double> &JxW = fe_fv.get_JxW_values();
896 *  
897 *   std::vector<double> g(n_q_points);
898 *   exact_solution->value_list(q_points, g);
899 *  
900 *   std::vector<double> sol_u(n_q_points);
901 *   fe_fv.get_function_values(solution, sol_u);
902 *  
903 *   const double extent1 = cell->measure() / cell->face(face_no)->measure();
904 *   const double penalty = get_penalty_factor(degree, extent1, extent1);
905 *  
906 *   double difference_norm_square = 0.;
907 *   for (unsigned int point = 0; point < q_points.size(); ++point)
908 *   {
909 *   const double diff = (g[point] - sol_u[point]);
910 *   difference_norm_square += diff * diff * JxW[point];
911 *   }
912 *   copy_data.value += penalty * difference_norm_square;
913 *   };
914 *  
915 * @endcode
916 *
917 * And finally interior face terms @f$\sum_{f\in \partial K}\lbrace \sigma
918 * \left\| [u_h] \right\|_f^2 + h_f \left\| [\nu \nabla u_h \cdot
919 * \mathbf n ] \right\|_f^2 \rbrace@f$:
920 *
921 * @code
922 *   const auto face_worker =
923 *   [&](const typename DoFHandler<dim>::cell_iterator &cell,
924 *   const unsigned int &f,
925 *   const unsigned int &sf,
926 *   const typename DoFHandler<dim>::cell_iterator &ncell,
927 *   const unsigned int &nf,
928 *   const unsigned int &nsf,
929 *   ScratchData &scratch_data,
930 *   CopyData &copy_data) {
931 *   const FEInterfaceValues<dim> &fe_iv =
932 *   scratch_data.reinit(cell, f, sf, ncell, nf, nsf);
933 *  
934 *   copy_data.face_data.emplace_back();
935 *   CopyDataFace &copy_data_face = copy_data.face_data.back();
936 *  
937 *   copy_data_face.cell_indices[0] = cell->active_cell_index();
938 *   copy_data_face.cell_indices[1] = ncell->active_cell_index();
939 *  
940 *   const std::vector<double> &JxW = fe_iv.get_JxW_values();
941 *   const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
942 *  
943 *   const std::vector<Point<dim>> &q_points = fe_iv.get_quadrature_points();
944 *   const unsigned int n_q_points = q_points.size();
945 *  
946 *   std::vector<double> jump(n_q_points);
947 *   fe_iv.get_jump_in_function_values(solution, jump);
948 *  
949 *   std::vector<Tensor<1, dim>> grad_jump(n_q_points);
950 *   fe_iv.get_jump_in_function_gradients(solution, grad_jump);
951 *  
952 *   const double h = cell->face(f)->diameter();
953 *  
954 *   const double extent1 = cell->measure() / cell->face(f)->measure();
955 *   const double extent2 = ncell->measure() / ncell->face(nf)->measure();
956 *   const double penalty = get_penalty_factor(degree, extent1, extent2);
957 *  
958 *   double flux_jump_square = 0;
959 *   double u_jump_square = 0;
960 *   for (unsigned int point = 0; point < n_q_points; ++point)
961 *   {
962 *   u_jump_square += jump[point] * jump[point] * JxW[point];
963 *   const double flux_jump = grad_jump[point] * normals[point];
964 *   flux_jump_square +=
965 *   diffusion_coefficient * flux_jump * flux_jump * JxW[point];
966 *   }
967 *   copy_data_face.values[0] =
968 *   0.5 * h * (flux_jump_square + penalty * u_jump_square);
969 *   copy_data_face.values[1] = copy_data_face.values[0];
970 *   };
971 *  
972 * @endcode
973 *
974 * Having computed local contributions for each cell, we still
975 * need a way to copy these into the global vector that will hold
976 * the error estimators for all cells:
977 *
978 * @code
979 *   const auto copier = [&](const CopyData &copy_data) {
980 *   if (copy_data.cell_index != numbers::invalid_unsigned_int)
981 *   estimated_error_square_per_cell[copy_data.cell_index] +=
982 *   copy_data.value;
983 *   for (const CopyDataFace &cdf : copy_data.face_data)
984 *   for (unsigned int j = 0; j < 2; ++j)
985 *   estimated_error_square_per_cell[cdf.cell_indices[j]] += cdf.values[j];
986 *   };
987 *  
988 * @endcode
989 *
990 * After all of this set-up, let's do the actual work: We resize
991 * the vector into which the results will be written, and then
992 * drive the whole process using the MeshWorker::mesh_loop()
993 * function.
994 *
995 * @code
996 *   estimated_error_square_per_cell.reinit(triangulation.n_active_cells());
997 *  
998 *   const UpdateFlags cell_flags =
1000 *   const UpdateFlags face_flags = update_values | update_gradients |
1003 *  
1004 *   ScratchData scratch_data(
1005 *   mapping, fe, quadrature, cell_flags, face_quadrature, face_flags);
1006 *  
1007 *   CopyData copy_data;
1008 *   MeshWorker::mesh_loop(dof_handler.begin_active(),
1009 *   dof_handler.end(),
1010 *   cell_worker,
1011 *   copier,
1012 *   scratch_data,
1013 *   copy_data,
1017 *   boundary_worker,
1018 *   face_worker);
1019 *   }
1020 *  
1021 * @endcode
1022 *
1023 *
1024 * <a name="step_74-Thecompute_energy_norm_errorfunction"></a>
1025 * <h3>The compute_energy_norm_error() function</h3>
1026 * Next, we evaluate the accuracy in terms of the energy norm.
1027 * This function is similar to the assembling of the error estimator above.
1028 * Here we compute the square of the energy norm defined by
1029 * @f[
1030 * \|u \|_{1,h}^2 = \sum_{K \in \Gamma_h} \nu\|\nabla u \|_K^2 +
1031 * \sum_{f \in F_i} \sigma \| [ u ] \|_f^2 +
1032 * \sum_{f \in F_b} \sigma \|u\|_f^2.
1033 * @f]
1034 * Therefore the corresponding error is
1035 * @f[
1036 * \|u -u_h \|_{1,h}^2 = \sum_{K \in \Gamma_h} \nu\|\nabla (u_h - u) \|_K^2
1037 * + \sum_{f \in F_i} \sigma \|[ u_h ] \|_f^2 + \sum_{f \in F_b}\sigma
1038 * \|u_h-g_D\|_f^2.
1039 * @f]
1040 *
1041 * @code
1042 *   template <int dim>
1043 *   double SIPGLaplace<dim>::compute_energy_norm_error()
1044 *   {
1045 *   energy_norm_square_per_cell.reinit(triangulation.n_active_cells());
1046 *  
1047 * @endcode
1048 *
1049 * Assemble @f$\sum_{K \in \Gamma_h} \nu\|\nabla (u_h - u) \|_K^2 @f$.
1050 *
1051 * @code
1052 *   const auto cell_worker =
1053 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
1054 *   ScratchData &scratch_data,
1055 *   CopyData &copy_data) {
1056 *   const FEValues<dim> &fe_v = scratch_data.reinit(cell);
1057 *  
1058 *   copy_data.cell_index = cell->active_cell_index();
1059 *  
1060 *   const std::vector<Point<dim>> &q_points = fe_v.get_quadrature_points();
1061 *   const unsigned int n_q_points = q_points.size();
1062 *   const std::vector<double> &JxW = fe_v.get_JxW_values();
1063 *  
1064 *   std::vector<Tensor<1, dim>> grad_u(n_q_points);
1065 *   fe_v.get_function_gradients(solution, grad_u);
1066 *  
1067 *   std::vector<Tensor<1, dim>> grad_exact(n_q_points);
1068 *   exact_solution->gradient_list(q_points, grad_exact);
1069 *  
1070 *   double norm_square = 0;
1071 *   for (unsigned int point = 0; point < n_q_points; ++point)
1072 *   {
1073 *   norm_square +=
1074 *   (grad_u[point] - grad_exact[point]).norm_square() * JxW[point];
1075 *   }
1076 *   copy_data.value = diffusion_coefficient * norm_square;
1077 *   };
1078 *  
1079 * @endcode
1080 *
1081 * Assemble @f$\sum_{f \in F_b}\sigma \|u_h-g_D\|_f^2@f$.
1082 *
1083 * @code
1084 *   const auto boundary_worker =
1085 *   [&](const typename DoFHandler<dim>::active_cell_iterator &cell,
1086 *   const unsigned int &face_no,
1087 *   ScratchData &scratch_data,
1088 *   CopyData &copy_data) {
1089 *   const FEFaceValuesBase<dim> &fe_fv = scratch_data.reinit(cell, face_no);
1090 *  
1091 *   const std::vector<Point<dim>> &q_points = fe_fv.get_quadrature_points();
1092 *   const unsigned n_q_points = q_points.size();
1093 *  
1094 *   const std::vector<double> &JxW = fe_fv.get_JxW_values();
1095 *  
1096 *   std::vector<double> g(n_q_points);
1097 *   exact_solution->value_list(q_points, g);
1098 *  
1099 *   std::vector<double> sol_u(n_q_points);
1100 *   fe_fv.get_function_values(solution, sol_u);
1101 *  
1102 *   const double extent1 = cell->measure() / cell->face(face_no)->measure();
1103 *   const double penalty = get_penalty_factor(degree, extent1, extent1);
1104 *  
1105 *   double difference_norm_square = 0.;
1106 *   for (unsigned int point = 0; point < q_points.size(); ++point)
1107 *   {
1108 *   const double diff = (g[point] - sol_u[point]);
1109 *   difference_norm_square += diff * diff * JxW[point];
1110 *   }
1111 *   copy_data.value += penalty * difference_norm_square;
1112 *   };
1113 *  
1114 * @endcode
1115 *
1116 * Assemble @f$\sum_{f \in F_i} \sigma \| [ u_h ] \|_f^2@f$.
1117 *
1118 * @code
1119 *   const auto face_worker =
1120 *   [&](const typename DoFHandler<dim>::cell_iterator &cell,
1121 *   const unsigned int &f,
1122 *   const unsigned int &sf,
1123 *   const typename DoFHandler<dim>::cell_iterator &ncell,
1124 *   const unsigned int &nf,
1125 *   const unsigned int &nsf,
1126 *   ScratchData &scratch_data,
1127 *   CopyData &copy_data) {
1128 *   const FEInterfaceValues<dim> &fe_iv =
1129 *   scratch_data.reinit(cell, f, sf, ncell, nf, nsf);
1130 *  
1131 *   copy_data.face_data.emplace_back();
1132 *   CopyDataFace &copy_data_face = copy_data.face_data.back();
1133 *  
1134 *   copy_data_face.cell_indices[0] = cell->active_cell_index();
1135 *   copy_data_face.cell_indices[1] = ncell->active_cell_index();
1136 *  
1137 *   const std::vector<double> &JxW = fe_iv.get_JxW_values();
1138 *  
1139 *   const std::vector<Point<dim>> &q_points = fe_iv.get_quadrature_points();
1140 *   const unsigned int n_q_points = q_points.size();
1141 *  
1142 *   std::vector<double> jump(n_q_points);
1143 *   fe_iv.get_jump_in_function_values(solution, jump);
1144 *  
1145 *   const double extent1 = cell->measure() / cell->face(f)->measure();
1146 *   const double extent2 = ncell->measure() / ncell->face(nf)->measure();
1147 *   const double penalty = get_penalty_factor(degree, extent1, extent2);
1148 *  
1149 *   double u_jump_square = 0;
1150 *   for (unsigned int point = 0; point < n_q_points; ++point)
1151 *   {
1152 *   u_jump_square += jump[point] * jump[point] * JxW[point];
1153 *   }
1154 *   copy_data_face.values[0] = 0.5 * penalty * u_jump_square;
1155 *   copy_data_face.values[1] = copy_data_face.values[0];
1156 *   };
1157 *  
1158 *   const auto copier = [&](const CopyData &copy_data) {
1159 *   if (copy_data.cell_index != numbers::invalid_unsigned_int)
1160 *   energy_norm_square_per_cell[copy_data.cell_index] += copy_data.value;
1161 *   for (const CopyDataFace &cdf : copy_data.face_data)
1162 *   for (unsigned int j = 0; j < 2; ++j)
1163 *   energy_norm_square_per_cell[cdf.cell_indices[j]] += cdf.values[j];
1164 *   };
1165 *  
1166 *   const UpdateFlags cell_flags =
1168 *   UpdateFlags face_flags =
1170 *  
1171 *   const ScratchData scratch_data(mapping,
1172 *   fe,
1173 *   quadrature_overintegration,
1174 *   cell_flags,
1175 *   face_quadrature_overintegration,
1176 *   face_flags);
1177 *  
1178 *   CopyData copy_data;
1179 *   MeshWorker::mesh_loop(dof_handler.begin_active(),
1180 *   dof_handler.end(),
1181 *   cell_worker,
1182 *   copier,
1183 *   scratch_data,
1184 *   copy_data,
1188 *   boundary_worker,
1189 *   face_worker);
1190 *   const double energy_error =
1191 *   std::sqrt(energy_norm_square_per_cell.l1_norm());
1192 *   return energy_error;
1193 *   }
1194 *  
1195 *  
1196 *  
1197 * @endcode
1198 *
1199 *
1200 * <a name="step_74-Therefine_gridfunction"></a>
1201 * <h3>The refine_grid() function</h3>
1202 *
1203 * @code
1204 *   template <int dim>
1205 *   void SIPGLaplace<dim>::refine_grid()
1206 *   {
1207 *   const double refinement_fraction = 0.1;
1208 *  
1210 *   triangulation, estimated_error_square_per_cell, refinement_fraction, 0.);
1211 *  
1212 *   triangulation.execute_coarsening_and_refinement();
1213 *   }
1214 *  
1215 *  
1216 *  
1217 * @endcode
1218 *
1219 *
1220 * <a name="step_74-Thecompute_errorsfunction"></a>
1221 * <h3>The compute_errors() function</h3>
1222 * We compute three errors in the @f$L_2@f$ norm, @f$H_1@f$ seminorm, and
1223 * the energy norm, respectively. These are then printed to screen,
1224 * but also stored in a table that records how these errors decay
1225 * with mesh refinement and which can be output in one step at the
1226 * end of the program.
1227 *
1228 * @code
1229 *   template <int dim>
1230 *   void SIPGLaplace<dim>::compute_errors()
1231 *   {
1232 *   double L2_error, H1_error, energy_error;
1233 *  
1234 *   {
1235 *   Vector<float> difference_per_cell(triangulation.n_active_cells());
1237 *   dof_handler,
1238 *   solution,
1239 *   *(exact_solution.get()),
1240 *   difference_per_cell,
1241 *   quadrature_overintegration,
1243 *  
1244 *   L2_error = VectorTools::compute_global_error(triangulation,
1245 *   difference_per_cell,
1247 *   convergence_table.add_value("L2", L2_error);
1248 *   }
1249 *  
1250 *   {
1251 *   Vector<float> difference_per_cell(triangulation.n_active_cells());
1253 *   dof_handler,
1254 *   solution,
1255 *   *(exact_solution.get()),
1256 *   difference_per_cell,
1257 *   quadrature_overintegration,
1259 *  
1260 *   H1_error = VectorTools::compute_global_error(triangulation,
1261 *   difference_per_cell,
1263 *   convergence_table.add_value("H1", H1_error);
1264 *   }
1265 *  
1266 *   {
1267 *   energy_error = compute_energy_norm_error();
1268 *   convergence_table.add_value("Energy", energy_error);
1269 *   }
1270 *  
1271 *   std::cout << " Error in the L2 norm : " << L2_error << std::endl
1272 *   << " Error in the H1 seminorm : " << H1_error << std::endl
1273 *   << " Error in the energy norm : " << energy_error
1274 *   << std::endl;
1275 *   }
1276 *  
1277 *  
1278 *  
1279 * @endcode
1280 *
1281 *
1282 * <a name="step_74-Therunfunction"></a>
1283 * <h3>The run() function</h3>
1284 *
1285 * @code
1286 *   template <int dim>
1287 *   void SIPGLaplace<dim>::run()
1288 *   {
1289 *   const unsigned int max_cycle =
1290 *   (test_case == TestCase::convergence_rate ? 6 : 20);
1291 *   for (unsigned int cycle = 0; cycle < max_cycle; ++cycle)
1292 *   {
1293 *   std::cout << "Cycle " << cycle << std::endl;
1294 *  
1295 *   switch (test_case)
1296 *   {
1297 *   case TestCase::convergence_rate:
1298 *   {
1299 *   if (cycle == 0)
1300 *   {
1301 *   GridGenerator::hyper_cube(triangulation);
1302 *  
1303 *   triangulation.refine_global(2);
1304 *   }
1305 *   else
1306 *   {
1307 *   triangulation.refine_global(1);
1308 *   }
1309 *   break;
1310 *   }
1311 *  
1312 *   case TestCase::l_singularity:
1313 *   {
1314 *   if (cycle == 0)
1315 *   {
1316 *   GridGenerator::hyper_L(triangulation);
1317 *   triangulation.refine_global(3);
1318 *   }
1319 *   else
1320 *   {
1321 *   refine_grid();
1322 *   }
1323 *   break;
1324 *   }
1325 *  
1326 *   default:
1327 *   {
1329 *   }
1330 *   }
1331 *  
1332 *   std::cout << " Number of active cells : "
1333 *   << triangulation.n_active_cells() << std::endl;
1334 *   setup_system();
1335 *  
1336 *   std::cout << " Number of degrees of freedom : " << dof_handler.n_dofs()
1337 *   << std::endl;
1338 *  
1339 *   assemble_system();
1340 *   solve();
1341 *   output_results(cycle);
1342 *   {
1343 *   convergence_table.add_value("cycle", cycle);
1344 *   convergence_table.add_value("cells", triangulation.n_active_cells());
1345 *   convergence_table.add_value("dofs", dof_handler.n_dofs());
1346 *   }
1347 *   compute_errors();
1348 *  
1349 *   if (test_case == TestCase::l_singularity)
1350 *   {
1351 *   compute_error_estimate();
1352 *   std::cout << " Estimated error : "
1353 *   << std::sqrt(estimated_error_square_per_cell.l1_norm())
1354 *   << std::endl;
1355 *  
1356 *   convergence_table.add_value(
1357 *   "Estimator",
1358 *   std::sqrt(estimated_error_square_per_cell.l1_norm()));
1359 *   }
1360 *   std::cout << std::endl;
1361 *   }
1362 *  
1363 * @endcode
1364 *
1365 * Having run all of our computations, let us tell the convergence
1366 * table how to format its data and output it to screen:
1367 *
1368 * @code
1369 *   convergence_table.set_precision("L2", 3);
1370 *   convergence_table.set_precision("H1", 3);
1371 *   convergence_table.set_precision("Energy", 3);
1372 *  
1373 *   convergence_table.set_scientific("L2", true);
1374 *   convergence_table.set_scientific("H1", true);
1375 *   convergence_table.set_scientific("Energy", true);
1376 *  
1377 *   if (test_case == TestCase::convergence_rate)
1378 *   {
1379 *   convergence_table.evaluate_convergence_rates(
1381 *   convergence_table.evaluate_convergence_rates(
1383 *   }
1384 *   if (test_case == TestCase::l_singularity)
1385 *   {
1386 *   convergence_table.set_precision("Estimator", 3);
1387 *   convergence_table.set_scientific("Estimator", true);
1388 *   }
1389 *  
1390 *   std::cout << "degree = " << degree << std::endl;
1391 *   convergence_table.write_text(
1393 *   }
1394 *   } // namespace Step74
1395 *  
1396 *  
1397 *  
1398 * @endcode
1399 *
1400 *
1401 * <a name="step_74-Themainfunction"></a>
1402 * <h3>The main() function</h3>
1403 * The following <code>main</code> function is similar to previous examples as
1404 * well, and need not be commented on.
1405 *
1406 * @code
1407 *   int main()
1408 *   {
1409 *   try
1410 *   {
1411 *   using namespace dealii;
1412 *   using namespace Step74;
1413 *  
1414 *   const TestCase test_case = TestCase::l_singularity;
1415 *  
1416 *   SIPGLaplace<2> problem(test_case);
1417 *   problem.run();
1418 *   }
1419 *   catch (std::exception &exc)
1420 *   {
1421 *   std::cerr << std::endl
1422 *   << std::endl
1423 *   << "----------------------------------------------------"
1424 *   << std::endl;
1425 *   std::cerr << "Exception on processing: " << std::endl
1426 *   << exc.what() << std::endl
1427 *   << "Aborting!" << std::endl
1428 *   << "----------------------------------------------------"
1429 *   << std::endl;
1430 *   return 1;
1431 *   }
1432 *   catch (...)
1433 *   {
1434 *   std::cerr << std::endl
1435 *   << std::endl
1436 *   << "----------------------------------------------------"
1437 *   << std::endl;
1438 *   std::cerr << "Unknown exception!" << std::endl
1439 *   << "Aborting!" << std::endl
1440 *   << "----------------------------------------------------"
1441 *   << std::endl;
1442 *   return 1;
1443 *   };
1444 *  
1445 *   return 0;
1446 *   }
1447 * @endcode
1448<a name="step_74-Results"></a><h1>Results</h1>
1449
1450
1451The output of this program consist of the console output and
1452solutions in vtu format.
1453
1454In the first test case, when you run the program, the screen output should look like the following:
1455@code
1456Cycle 0
1457 Number of active cells : 16
1458 Number of degrees of freedom : 256
1459 Error in the L2 norm : 0.00193285
1460 Error in the H1 seminorm : 0.106087
1461 Error in the energy norm : 0.150625
1462
1463Cycle 1
1464 Number of active cells : 64
1465 Number of degrees of freedom : 1024
1466 Error in the L2 norm : 9.60497e-05
1467 Error in the H1 seminorm : 0.0089954
1468 Error in the energy norm : 0.0113265
1469
1470Cycle 2
1471.
1472.
1473.
1474@endcode
1475
1476When using the smooth case with polynomial degree 3, the convergence
1477table will look like this:
1478<table align="center" class="doxtable">
1479 <tr>
1480 <th>cycle</th>
1481 <th>n_cells</th>
1482 <th>n_dofs</th>
1483 <th>L2 </th>
1484 <th>rate</th>
1485 <th>H1</th>
1486 <th>rate</th>
1487 <th>Energy</th>
1488 </tr>
1489 <tr>
1490 <td align="center">0</td>
1491 <td align="right">16</td>
1492 <td align="right">256</td>
1493 <td align="center">1.933e-03</td>
1494 <td>&nbsp;</td>
1495 <td align="center">1.061e-01</td>
1496 <td>&nbsp;</td>
1497 <td align="center">1.506e-01</td>
1498 </tr>
1499 <tr>
1500 <td align="center">1</td>
1501 <td align="right">64</td>
1502 <td align="right">1024</td>
1503 <td align="center">9.605e-05</td>
1504 <td align="center">4.33</td>
1505 <td align="center">8.995e-03</td>
1506 <td align="center">3.56</td>
1507 <td align="center">1.133e-02</td>
1508 </tr>
1509 <tr>
1510 <td align="center">2</td>
1511 <td align="right">256</td>
1512 <td align="right">4096</td>
1513 <td align="center">5.606e-06</td>
1514 <td align="center">4.10</td>
1515 <td align="center">9.018e-04</td>
1516 <td align="center">3.32</td>
1517 <td align="center">9.736e-04</td>
1518 </tr>
1519 <tr>
1520 <td align="center">3</td>
1521 <td align="right">1024</td>
1522 <td align="right">16384</td>
1523 <td align="center">3.484e-07</td>
1524 <td align="center">4.01</td>
1525 <td align="center">1.071e-04</td>
1526 <td align="center">3.07</td>
1527 <td align="center">1.088e-04</td>
1528 </tr>
1529 <tr>
1530 <td align="center">4</td>
1531 <td align="right">4096</td>
1532 <td align="right">65536</td>
1533 <td align="center">2.179e-08</td>
1534 <td align="center">4.00</td>
1535 <td align="center">1.327e-05</td>
1536 <td align="center">3.01</td>
1537 <td align="center">1.331e-05</td>
1538 </tr>
1539 <tr>
1540 <td align="center">5</td>
1541 <td align="right">16384</td>
1542 <td align="right">262144</td>
1543 <td align="center">1.363e-09</td>
1544 <td align="center">4.00</td>
1545 <td align="center">1.656e-06</td>
1546 <td align="center">3.00</td>
1547 <td align="center">1.657e-06</td>
1548 </tr>
1549</table>
1550
1551Theoretically, for polynomial degree @f$p@f$, the order of convergence in @f$L_2@f$
1552norm and @f$H^1@f$ seminorm should be @f$p+1@f$ and @f$p@f$, respectively. Our numerical
1553results are in good agreement with theory.
1554
1555In the second test case, when you run the program, the screen output should look like the following:
1556@code
1557Cycle 0
1558 Number of active cells : 192
1559 Number of degrees of freedom : 3072
1560 Error in the L2 norm : 0.000323585
1561 Error in the H1 seminorm : 0.0296202
1562 Error in the energy norm : 0.0420478
1563 Estimated error : 0.136067
1564
1565Cycle 1
1566 Number of active cells : 249
1567 Number of degrees of freedom : 3984
1568 Error in the L2 norm : 0.000114739
1569 Error in the H1 seminorm : 0.0186571
1570 Error in the energy norm : 0.0264879
1571 Estimated error : 0.0857186
1572
1573Cycle 2
1574.
1575.
1576.
1577@endcode
1578
1579The following figure provides a log-log plot of the errors versus
1580the number of degrees of freedom for this test case on the L-shaped
1581domain. In order to interpret it, let @f$n@f$ be the number of degrees of
1582freedom, then on uniformly refined meshes, @f$h@f$ is of order
1583@f$1/\sqrt{n}@f$ in 2D. Combining the theoretical results in the previous case,
1584we see that if the solution is sufficiently smooth,
1585we can expect the error in the @f$L_2@f$ norm to be of order @f$O(n^{-\frac{p+1}{2}})@f$
1586and in @f$H^1@f$ seminorm to be @f$O(n^{-\frac{p}{2}})@f$. It is not a priori
1587clear that one would get the same kind of behavior as a function of
1588@f$n@f$ on adaptively refined meshes like the ones we use for this second
1589test case, but one can certainly hope. Indeed, from the figure, we see
1590that the SIPG with adaptive mesh refinement produces asymptotically
1591the kinds of hoped-for results:
1592
1593<img width="600px" src="https://dealii.org/images/steps/developer/step-74.log-log-plot.png" alt="">
1594
1595In addition, we observe that the error estimator decreases
1596at almost the same rate as the errors in the energy norm and @f$H^1@f$ seminorm,
1597and one order lower than the @f$L_2@f$ error. This suggests
1598its ability to predict regions with large errors.
1599
1600While this tutorial is focused on the implementation, the @ref step_59 "step-59" tutorial program achieves an efficient
1601large-scale solver in terms of computing time with matrix-free solution techniques.
1602Note that the @ref step_59 "step-59" tutorial does not work with meshes containing hanging nodes at this moment,
1603because the multigrid interface matrices are not as easily determined,
1604but that is merely the lack of some interfaces in deal.II, nothing fundamental.
1605 *
1606 *
1607<a name="step_74-PlainProg"></a>
1608<h1> The plain program</h1>
1609@include "step-74.cc"
1610*/
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*  *  *  struct InterferenceTaperTransform *  
void reinit(const CellIteratorType &cell, const unsigned int face_no, const unsigned int sub_face_no, const CellNeighborIteratorType &cell_neighbor, const unsigned int face_no_neighbor, const unsigned int sub_face_no_neighbor, const unsigned int q_index=numbers::invalid_unsigned_int, const unsigned int mapping_index=numbers::invalid_unsigned_int, const unsigned int fe_index=numbers::invalid_unsigned_int, const unsigned int fe_index_neighbor=numbers::invalid_unsigned_int)
const std::vector< Point< spacedim > > & get_quadrature_points() const
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
virtual void value_list(const std::vector< Point< dim > > &points, std::vector< RangeNumberType > &values, const unsigned int component=0) const
virtual double laplacian(const Point< 2 > &p, const unsigned int component=0) const override
Definition point.h:111
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
unsigned int cell_index
typename ActiveSelector::cell_iterator cell_iterator
typename ActiveSelector::active_cell_iterator active_cell_iterator
void mesh_loop(const CellIteratorType &begin, const CellIteratorType &end, const CellWorkerFunctionType &cell_worker, const CopierType &copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const AssembleFlags flags=assemble_own_cells, const BoundaryWorkerFunctionType &boundary_worker=BoundaryWorkerFunctionType(), const FaceWorkerFunctionType &face_worker=FaceWorkerFunctionType(), const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
Definition mesh_loop.h:279
UpdateFlags
@ update_hessians
Second derivatives of shape functions.
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
std::vector< index_type > data
Definition mpi.cc:734
void hyper_L(Triangulation< dim > &tria, const double left=-1., const double right=1., const bool colorize=false)
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
void refine_and_coarsen_fixed_number(Triangulation< dim, spacedim > &triangulation, const Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const unsigned int max_n_cells=std::numeric_limits< unsigned int >::max())
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
@ matrix
Contents is actually a matrix.
constexpr char L
constexpr types::blas_int one
void cell_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const FEValuesBase< dim > &fetest, const ArrayView< const std::vector< double > > &velocity, const double factor=1.)
Definition advection.h:72
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Definition divergence.h:469
void L2(Vector< number > &result, const FEValuesBase< dim > &fe, const std::vector< double > &input, const double factor=1.)
Definition l2.h:157
@ assemble_boundary_faces
@ assemble_own_interior_faces_once
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:210
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
double compute_global_error(const Triangulation< dim, spacedim > &tria, const InVector &cellwise_error, const NormType &norm, const double exponent=2.)
void integrate_difference(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const ReadVector< Number > &fe_function, const Function< spacedim, Number > &exact_solution, OutVector &difference, const Quadrature< dim > &q, const NormType &norm, const Function< spacedim, double > *weight=nullptr, const double exponent=2.)
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)
unsigned int n_cells(const internal::TriangulationImplementation::NumberCache< 1 > &c)
Definition tria.cc:15808
int(&) functions(const void *v1, const void *v2)
void assemble(const MeshWorker::DoFInfoBox< dim, DOFINFO > &dinfo, A *assembler)
Definition loop.h:68
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
constexpr double PI
Definition numbers.h:240
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
::VectorizedArray< Number, width > log(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)