214 * #ifndef INCLUDE_DG_UPWIND_H_
215 * #define INCLUDE_DG_UPWIND_H_
218 * #include <deal.II/base/function.h>
219 * #include <deal.II/base/quadrature_lib.h>
221 * #include <deal.II/dofs/dof_handler.h>
222 * #include <deal.II/dofs/dof_tools.h>
224 * #include <deal.II/fe/fe_dgq.h>
225 * #include <deal.II/fe/fe_q.h>
226 * #include <deal.II/fe/fe_values.h>
227 * #include <deal.II/fe/mapping_q1.h>
229 * #include <deal.II/grid/grid_generator.h>
230 * #include <deal.II/grid/grid_out.h>
231 * #include <deal.II/grid/grid_refinement.h>
232 * #include <deal.II/grid/tria.h>
234 * #include <deal.II/lac/dynamic_sparsity_pattern.h>
235 * #include <deal.II/lac/full_matrix.h>
236 * #include <deal.II/lac/sparse_matrix.h>
237 * #include <deal.II/lac/vector.h>
239 * #include <deal.II/numerics/data_out.h>
240 * #include <deal.II/numerics/vector_tools.h>
247 * #include <deal.II/fe/fe_interface_values.h>
253 * #include <deal.II/lac/precondition_block.h>
254 * #include <deal.II/lac/solver_richardson.h>
255 * #include <deal.II/lac/sparse_direct.h>
258 * We are going to use
gradients as refinement indicator.
261 * #include <deal.II/numerics/derivative_approximation.h>
267 * #include <deal.II/base/convergence_table.h>
269 * #include <deal.II/meshworker/
mesh_loop.h>
273 * To enable parameter handling
276 * #include <deal.II/base/function_parser.h>
277 * #include <deal.II/base/parameter_acceptor.h>
278 * #include <deal.II/base/parameter_handler.h>
279 * #include <deal.II/base/parsed_convergence_table.h>
280 * #include <deal.II/base/symbolic_function.h>
282 * #include <deal.II/meshworker/copy_data.h>
283 * #include <deal.II/meshworker/
mesh_loop.h>
284 * #include <deal.II/meshworker/scratch_data.h>
287 * #include <iostream>
292 * This is a
struct used only for throwing an exception when theta parameter is
298 * std::string message;
299 * theta_exc(std::string &&s)
300 * : message{std::move(s)} {};
304 *
return message.c_str();
313 * AdvectionReaction();
315 * initialize_params(
const std::string &filename);
322 * parse_string(
const std::string ¶meters);
332 * output_results(
const unsigned int cycle)
const;
336 * compute_energy_norm();
338 * compute_local_projection_and_estimate();
345 * Furthermore we want to use DG elements.
348 * std::unique_ptr<FE_DGQ<dim>> fe;
361 * So far we declared the usual objects. Hereafter we declare
370 *
unsigned int fe_degree = 1;
374 * and then we define
default values that will be parsed from the following
378 * std::string exact_solution_expression =
379 *
"tanh(100*(x+y-0.5))";
380 * std::string rhs_expression =
381 *
"-200*tanh(100*x + 100*y - 50.0)^2 + tanh(100*x + 100*y - 50.0) + 200";
382 * std::string advection_coefficient_expression =
"1.0";
383 * std::string boundary_conditions_expression =
"tanh(100*x + 100*y - 50.0)";
384 * std::string refinement =
"residual";
385 * std::string output_filename =
"DG_advection_reaction_estimator";
386 * std::map<std::string, double> constants;
389 *
bool use_direct_solver =
true;
390 *
unsigned int n_refinement_cycles = 8;
391 *
unsigned int n_global_refinements = 3;
392 *
double theta = 0.5;
399<a name=
"ann-main.cc"></a>
400<h1>Annotated version of main.cc</h1>
416 * #include
"include/DG_advection_reaction.h"
419 * main(
int argc,
char **argv)
423 * std::string par_name =
"";
426 * par_name = argv[1];
430 * par_name =
"parameters.prm";
433 * AdvectionReaction<2> problem;
434 * problem.initialize_params(par_name);
437 *
catch (std::exception &exc)
439 * std::cerr << std::endl
441 * <<
"----------------------------------------------------"
443 * std::cerr <<
"Exception on processing: " << std::endl
444 * << exc.what() << std::endl
445 * <<
"Aborting!" << std::endl
446 * <<
"----------------------------------------------------"
450 *
catch (
const theta_exc &theta_range)
452 * std::cerr << std::endl
454 * <<
"----------------------------------------------------"
456 * std::cerr <<
"Exception on processing: " << std::endl
457 * << theta_range.what() << std::endl
458 * <<
"Aborting!" << std::endl
459 * <<
"----------------------------------------------------"
465 * std::cerr << std::endl
467 * <<
"----------------------------------------------------"
469 * std::cerr <<
"Unknown exception!" << std::endl
470 * <<
"Aborting!" << std::endl
471 * <<
"----------------------------------------------------"
481<a name=
"ann-source/DG_advection_reaction.cc"></a>
482<h1>Annotated version of source/DG_advection_reaction.cc</h1>
498 * #include
"../include/DG_advection_reaction.h"
502 * Compute and returns the wind field
b
509 *
Assert(dim > 1, ExcNotImplemented());
512 * wind_field[0] = 1.0;
513 * wind_field[1] = 1.0;
521 * <a name=
"source/DG_advection_reaction.cc-TheScratchDataandCopyDataclasses"></a>
522 * <h3>The ScratchData and CopyData classes</h3>
526 * The following objects are the scratch and
copy objects we use in the call
528 * that works similar to
FEValues or FEFacesValues, except that it acts on
529 * an
interface between two cells and allows us to
assemble the interface
530 * terms in our weak form.
547 * : fe_values(mapping, fe, quadrature, update_flags)
548 * , fe_interface_values(mapping, fe, quadrature_face, interface_update_flags)
551 * ScratchData(
const ScratchData<dim> &scratch_data)
552 * : fe_values(scratch_data.fe_values.get_mapping(),
553 * scratch_data.fe_values.get_fe(),
554 * scratch_data.fe_values.get_quadrature(),
555 * scratch_data.fe_values.get_update_flags())
556 * , fe_interface_values(scratch_data.fe_interface_values.get_mapping(),
557 * scratch_data.fe_interface_values.get_fe(),
558 * scratch_data.fe_interface_values.get_quadrature(),
559 * scratch_data.fe_interface_values.get_update_flags())
568 *
struct CopyDataFace
571 * std::vector<types::global_dof_index> joint_dof_indices;
572 * std::array<double, 2>
values;
573 * std::array<unsigned int, 2> cell_indices;
582 * std::vector<types::global_dof_index> local_dof_indices;
583 * std::vector<CopyDataFace> face_data;
586 *
double value_estimator;
592 *
template <
class Iterator>
594 *
reinit(
const Iterator &cell,
unsigned int dofs_per_cell)
596 *
cell_matrix.reinit(dofs_per_cell, dofs_per_cell);
597 * cell_mass_matrix.reinit(dofs_per_cell, dofs_per_cell);
599 * cell_rhs.reinit(dofs_per_cell);
600 * cell_mass_rhs.reinit(dofs_per_cell);
602 * local_dof_indices.resize(dofs_per_cell);
603 * cell->get_dof_indices(local_dof_indices);
612 * <a name=
"source/DG_advection_reaction.cc-Auxiliaryfunction"></a>
613 * <h3>Auxiliary function</h3>
614 * This auxiliary function is taken from @ref step_74
"step-74" and it
's used to
615 * compute the jump of the finite element function @f$u_h@f$ on a face.
620 * get_function_jump(const FEInterfaceValues<dim> &fe_iv,
621 * const Vector<double> &solution,
622 * std::vector<double> &jump)
624 * const unsigned int n_q = fe_iv.n_quadrature_points;
625 * std::array<std::vector<double>, 2> face_values;
627 * for (unsigned int i = 0; i < 2; ++i)
629 * face_values[i].resize(n_q);
630 * fe_iv.get_fe_face_values(i).get_function_values(solution, face_values[i]);
632 * for (unsigned int q = 0; q < n_q; ++q)
633 * jump[q] = face_values[0][q] - face_values[1][q];
639 * AdvectionReaction<dim>::AdvectionReaction()
641 * , dof_handler(triangulation)
643 * Assert(dim > 1, ExcMessage("Not implemented in 1D."));
644 * add_parameter("Finite element degree", fe_degree);
645 * add_parameter("Problem constants", constants);
646 * add_parameter("Output filename", output_filename);
647 * add_parameter("Use direct solver", use_direct_solver);
648 * add_parameter("Number of refinement cycles", n_refinement_cycles);
649 * add_parameter("Number of global refinement", n_global_refinements);
650 * add_parameter("Refinement", refinement);
651 * add_parameter("Exact solution expression", exact_solution_expression);
652 * add_parameter("Boundary conditions expression",
653 * boundary_conditions_expression);
654 * add_parameter("Theta", theta);
655 * add_parameter("Advection coefficient expression",
656 * advection_coefficient_expression);
657 * add_parameter("Right hand side expression", rhs_expression);
659 * this->prm.enter_subsection("Error table");
660 * error_table.add_parameters(this->prm);
661 * this->prm.leave_subsection();
668 * AdvectionReaction<dim>::initialize_params(const std::string &filename)
670 * ParameterAcceptor::initialize(filename,
671 * "last_used_parameters.prm",
672 * ParameterHandler::Short);
673 * if (theta < 0.0 || theta > 10.0 || std::abs(theta) < 1e-12)
676 * theta_exc("Theta parameter is not in a suitable range: see paper by "
677 * "Brezzi, Marini, Suli for an extended discussion"));
685 * AdvectionReaction<dim>::parse_string(const std::string ¶meters)
687 * ParameterAcceptor::prm.parse_input_from_string(parameters);
688 * ParameterAcceptor::parse_all_parameters();
695 * AdvectionReaction<dim>::setup_system()
699 * first need to distribute the DoFs.
704 * fe = std::make_unique<FE_DGQ<dim>>(fe_degree);
705 * const auto vars = dim == 2 ? "x,y" : "x,y,z";
706 * exact_solution.initialize(vars, exact_solution_expression, constants);
707 * rhs.initialize(vars, rhs_expression, constants);
708 * advection_coeff.initialize(vars,
709 * advection_coefficient_expression,
711 * boundary_conditions.initialize(vars,
712 * boundary_conditions_expression,
715 * dof_handler.distribute_dofs(*fe);
719 * To build the sparsity pattern for DG discretizations, we can call the
720 * function analogue to DoFTools::make_sparsity_pattern, which is called
721 * DoFTools::make_flux_sparsity_pattern:
724 * DynamicSparsityPattern dsp(dof_handler.n_dofs());
725 * DoFTools::make_flux_sparsity_pattern(dof_handler,
726 * dsp); // DG sparsity pattern generator
727 * sparsity_pattern.copy_from(dsp);
731 * Finally, we set up the structure of all components of the linear system.
734 * system_matrix.reinit(sparsity_pattern);
735 * solution.reinit(dof_handler.n_dofs());
736 * right_hand_side.reinit(dof_handler.n_dofs());
743 * in the call to MeshWorker::mesh_loop() we only need to specify what should
745 * each cell, each boundary face, and each interior face. These three tasks
746 * are handled by the lambda functions inside the function below.
754 * AdvectionReaction<dim>::assemble_system()
756 * using Iterator = typename DoFHandler<dim>::active_cell_iterator;
758 * const QGauss<dim> quadrature = fe->tensor_degree() + 1;
759 * const QGauss<dim - 1> quadrature_face = fe->tensor_degree() + 1;
763 * This is the function that will be executed for each cell.
766 * const auto cell_worker = [&](const Iterator &cell,
767 * ScratchData<dim> &scratch_data,
768 * CopyData ©_data) {
769 * FEValues<dim> fe_values_continuous(*fe,
771 * update_values | update_gradients |
772 * update_quadrature_points |
773 * update_JxW_values);
775 * const unsigned int n_dofs =
776 * scratch_data.fe_values.get_fe().n_dofs_per_cell();
777 * copy_data.reinit(cell, n_dofs);
778 * scratch_data.fe_values.reinit(cell);
780 * const auto &q_points = scratch_data.fe_values.get_quadrature_points();
782 * const FEValues<dim> &fe_v = scratch_data.fe_values;
783 * const std::vector<double> &JxW = fe_v.get_JxW_values();
785 * for (unsigned int point = 0; point < fe_v.n_quadrature_points; ++point)
787 * auto beta_q = beta(q_points[point]);
788 * for (unsigned int i = 0; i < n_dofs; ++i)
790 * for (unsigned int j = 0; j < n_dofs; ++j)
792 * copy_data.cell_matrix(i, j) +=
794 * * fe_v.shape_grad(i, point) // \nabla \phi_i
795 * * fe_v.shape_value(j, point) // \phi_j
796 * + advection_coeff.value(q_points[point]) * // gamma
797 * fe_v.shape_value(i, point) // phi_i
798 * * fe_v.shape_value(j, point) // phi_j
802 * copy_data.cell_rhs(i) += rhs.value(q_points[point]) // f(x_q)
803 * * fe_v.shape_value(i, point) // phi_i(x_q)
804 * * JxW[point]; // dx
811 * This is the function called for boundary faces and consists of a normal
812 * integration using FEFaceValues. New is the logic to decide if the term
813 * goes into the system matrix (outflow) or the right-hand side (inflow).
816 * const auto boundary_worker = [&](const Iterator &cell,
817 * const unsigned int &face_no,
818 * ScratchData<dim> &scratch_data,
819 * CopyData ©_data) {
820 * scratch_data.fe_interface_values.reinit(cell, face_no);
821 * const FEFaceValuesBase<dim> &fe_face =
822 * scratch_data.fe_interface_values.get_fe_face_values(0);
824 * const auto &q_points = fe_face.get_quadrature_points();
826 * const unsigned int n_facet_dofs = fe_face.get_fe().n_dofs_per_cell();
827 * const std::vector<double> &JxW = fe_face.get_JxW_values();
828 * const std::vector<Tensor<1, dim>> &normals = fe_face.get_normal_vectors();
830 * std::vector<double> g(q_points.size());
831 * exact_solution.value_list(q_points, g);
833 * for (unsigned int point = 0; point < q_points.size(); ++point)
835 * const double beta_dot_n = beta(q_points[point]) * normals[point];
837 * if (beta_dot_n > 0)
839 * for (unsigned int i = 0; i < n_facet_dofs; ++i)
840 * for (unsigned int j = 0; j < n_facet_dofs; ++j)
841 * copy_data.cell_matrix(i, j) +=
842 * fe_face.shape_value(i,
844 * * fe_face.shape_value(j, point) // \phi_j
845 * * beta_dot_n // \beta . n
846 * * JxW[point]; // dx
849 * for (unsigned int i = 0; i < n_facet_dofs; ++i)
850 * copy_data.cell_rhs(i) += -fe_face.shape_value(i, point) // \phi_i
852 * * beta_dot_n // \beta . n
853 * * JxW[point]; // dx
859 * This is the function called on interior faces. The arguments specify
860 * cells, face and subface indices (for adaptive refinement). We just pass
861 * them along to the reinit() function of FEInterfaceValues.
864 * const auto face_worker = [&](const Iterator &cell,
865 * const unsigned int &f,
866 * const unsigned int &sf,
867 * const Iterator &ncell,
868 * const unsigned int &nf,
869 * const unsigned int &nsf,
870 * ScratchData<dim> &scratch_data,
871 * CopyData ©_data) {
872 * FEInterfaceValues<dim> &fe_iv = scratch_data.fe_interface_values;
873 * fe_iv.reinit(cell, f, sf, ncell, nf, nsf);
874 * const auto &q_points = fe_iv.get_quadrature_points();
876 * copy_data.face_data.emplace_back();
877 * CopyDataFace ©_data_face = copy_data.face_data.back();
879 * const unsigned int n_dofs = fe_iv.n_current_interface_dofs();
880 * copy_data_face.joint_dof_indices = fe_iv.get_interface_dof_indices();
882 * copy_data_face.cell_matrix.reinit(n_dofs, n_dofs);
884 * const std::vector<double> &JxW = fe_iv.get_JxW_values();
885 * const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
887 * for (unsigned int qpoint = 0; qpoint < q_points.size(); ++qpoint)
889 * const double beta_dot_n = beta(q_points[qpoint]) * normals[qpoint];
890 * for (unsigned int i = 0; i < n_dofs; ++i)
892 * for (unsigned int j = 0; j < n_dofs; ++j)
894 * copy_data_face.cell_matrix(i, j) +=
895 * (beta(q_points[qpoint]) * normals[qpoint] *
896 * fe_iv.average_of_shape_values(j, qpoint) *
897 * fe_iv.jump_in_shape_values(i, qpoint) +
898 * theta * std::abs(beta_dot_n) *
899 * fe_iv.jump_in_shape_values(j, qpoint) *
900 * fe_iv.jump_in_shape_values(i, qpoint)) *
909 * The following lambda function will handle copying the data from the
910 * cell and face assembly into the global matrix and right-hand side.
914 * While we would not need an AffineConstraints object, because there are
915 * no hanging node constraints in DG discretizations, we use an empty
916 * object here as this allows us to use its `copy_local_to_global`
920 * const AffineConstraints<double> constraints;
922 * const auto copier = [&](const CopyData &c) {
923 * constraints.distribute_local_to_global(c.cell_matrix,
925 * c.local_dof_indices,
929 * for (auto &cdf : c.face_data)
931 * constraints.distribute_local_to_global(cdf.cell_matrix,
932 * cdf.joint_dof_indices,
937 * ScratchData<dim> scratch_data(mapping, *fe, quadrature, quadrature_face);
938 * CopyData copy_data;
942 * Here, we finally handle the assembly. We pass in ScratchData and
943 * CopyData objects, the lambda functions from above, an specify that we
944 * want to assemble interior faces once.
947 * MeshWorker::mesh_loop(dof_handler.begin_active(),
953 * MeshWorker::assemble_own_cells |
954 * MeshWorker::assemble_boundary_faces |
955 * MeshWorker::assemble_own_interior_faces_once,
964 * AdvectionReaction<dim>::solve()
966 * if (use_direct_solver)
968 * SparseDirectUMFPACK system_matrix_inverse;
969 * system_matrix_inverse.initialize(system_matrix);
970 * system_matrix_inverse.vmult(solution, right_hand_side);
976 * Here we have a classic iterative solver, as done in many tutorials:
979 * SolverControl solver_control(1000, 1e-15);
980 * SolverRichardson<Vector<double>> solver(solver_control);
981 * PreconditionBlockSSOR<SparseMatrix<double>> preconditioner;
982 * preconditioner.initialize(system_matrix, fe->n_dofs_per_cell());
983 * solver.solve(system_matrix, solution, right_hand_side, preconditioner);
984 * std::cout << " Solver converged in " << solver_control.last_step()
985 * << " iterations." << std::endl;
994 * <a name="source/DG_advection_reaction.cc-Meshrefinement"></a>
995 * <h3>Mesh refinement</h3>
996 * We refine the grid according the proposed estimator or with an approximation
997 * to the gradient of the solution. The first option is the default one (you can
998 * see it in the header file)
1001 * template <int dim>
1003 * AdvectionReaction<dim>::refine_grid()
1005 * if (refinement == "residual")
1009 * If the `refinement` string is `"residual"`, then we first compute the
1013 * compute_local_projection_and_estimate();
1016 * We then set the refinement fraction and as usual we execute the
1020 * const double refinement_fraction = 0.6;
1021 * GridRefinement::refine_and_coarsen_fixed_fraction(
1022 * triangulation, error_indicator_per_cell, refinement_fraction, 0.0);
1023 * triangulation.execute_coarsening_and_refinement();
1025 * else if (refinement == "gradient")
1027 * Vector<float> gradient_indicator(triangulation.n_active_cells());
1031 * Now the approximate gradients are computed
1034 * DerivativeApproximation::approximate_gradient(mapping,
1037 * gradient_indicator);
1041 * and they are cell-wise scaled by the factor @f$h^{1+d/2}@f$
1044 * unsigned int cell_no = 0;
1045 * for (const auto &cell : dof_handler.active_cell_iterators())
1046 * gradient_indicator(cell_no++) *=
1047 * std::pow(cell->diameter(), 1 + 1.0 * dim / 2);
1051 * Finally they serve as refinement indicator.
1054 * GridRefinement::refine_and_coarsen_fixed_fraction(triangulation,
1055 * gradient_indicator,
1059 * triangulation.execute_coarsening_and_refinement();
1060 * std::cout << gradient_indicator.l2_norm() << '\n
';
1062 * else if (refinement == "global")
1064 * triangulation.refine_global(
1065 * 1); // just for testing on uniformly refined meshes
1069 * Assert(false, ExcInternalError());
1077 * The output of this program consists of a vtk file of the adaptively
1078 * refined grids and the numerical solutions.
1081 * template <int dim>
1083 * AdvectionReaction<dim>::output_results(const unsigned int cycle) const
1085 * const std::string filename = "solution-" + std::to_string(cycle) + ".vtk";
1086 * std::cout << " Writing solution to <" << filename << ">" << std::endl;
1087 * std::ofstream output(filename);
1089 * DataOut<dim> data_out;
1090 * data_out.attach_dof_handler(dof_handler);
1091 * data_out.add_data_vector(solution, "u", DataOut<dim>::type_dof_data);
1092 * data_out.build_patches(mapping);
1093 * data_out.write_vtk(output);
1096 * template <int dim>
1098 * AdvectionReaction<dim>::compute_error()
1100 * error_table.error_from_exact(
1104 * exact_solution); // be careful: a FD approximation of the gradient is used
1107 * to compute the H^1 norm if Solution<dim> doesn't
1108 * implements the Gradient function
1118 * <a name=
"source/DG_advection_reaction.cc-Computetheenergynorm"></a>
1119 * <h3>Compute the energy
norm</h3>
1120 * The energy
norm is defined as @f$ |||\cdot ||| = \Bigl(||\cdot||_{0,\Omega}^2 +
1121 * \sum_{F \in \mathbb{
F}}||c_F^{\frac{1}{2}}[\cdot] ||_{0,
F}^2
1122 * \Bigr)^{\frac{1}{2}}@f$ Notice that in the current
case we have @f$c_f = \frac{|
b
1123 * \cdot n|}{2}@f$ Like in the assembly, all the contributions are handled
1124 * separately by
using ScratchData and CopyData objects.
1127 *
template <
int dim>
1129 * AdvectionReaction<dim>::compute_energy_norm()
1131 * energy_norm_square_per_cell.reinit(
triangulation.n_active_cells());
1137 * We start off by adding cell contributions
1140 *
const auto cell_worker = [&](
const Iterator &cell,
1141 * ScratchData<dim> &scratch_data,
1142 * CopyData ©_data) {
1143 *
const unsigned int n_dofs =
1144 * scratch_data.fe_values.get_fe().n_dofs_per_cell();
1145 * copy_data.reinit(cell, n_dofs);
1146 * scratch_data.fe_values.reinit(cell);
1148 * copy_data.cell_index = cell->active_cell_index();
1150 *
const auto &q_points = scratch_data.fe_values.get_quadrature_points();
1154 *
double error_square_norm{0.0};
1155 * std::vector<double> sol_u(fe_v.n_quadrature_points);
1156 * fe_v.get_function_values(solution, sol_u);
1158 *
for (
unsigned int point = 0;
point < fe_v.n_quadrature_points; ++
point)
1160 *
const double diff =
1161 * (sol_u[point] - exact_solution.value(q_points[point]));
1162 * error_square_norm += diff * diff * JxW[
point];
1164 * copy_data.value = error_square_norm;
1169 * Here we add contributions coming from the
internal faces
1172 *
const auto face_worker = [&](
const Iterator &cell,
1173 *
const unsigned int &f,
1174 *
const unsigned int &sf,
1175 *
const Iterator &ncell,
1176 *
const unsigned int &nf,
1177 *
const unsigned int &nsf,
1178 * ScratchData<dim> &scratch_data,
1179 * CopyData ©_data) {
1181 * fe_iv.
reinit(cell, f, sf, ncell, nf, nsf);
1183 * copy_data.face_data.emplace_back();
1184 * CopyDataFace ©_data_face = copy_data.face_data.back();
1185 * copy_data_face.cell_indices[0] = cell->active_cell_index();
1186 * copy_data_face.cell_indices[1] = ncell->active_cell_index();
1188 *
const auto &q_points = fe_iv.get_quadrature_points();
1189 *
const unsigned n_q_points = q_points.size();
1190 *
const std::vector<double> &JxW = fe_iv.get_JxW_values();
1191 * std::vector<double> g(n_q_points);
1193 * std::vector<double> jump(n_q_points);
1194 * get_function_jump(fe_iv, solution, jump);
1196 *
const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
1198 *
double error_jump_square{0.0};
1199 *
for (
unsigned int point = 0;
point < n_q_points; ++
point)
1201 *
const double beta_dot_n =
1202 * theta *
std::abs(beta(q_points[point]) * normals[point]);
1203 * error_jump_square +=
1207 * copy_data.value = error_jump_square;
1212 * Finally, we add the boundary contributions
1215 *
const auto boundary_worker = [&](
const Iterator &cell,
1216 *
const unsigned int &face_no,
1217 * ScratchData<dim> &scratch_data,
1218 * CopyData ©_data) {
1219 * scratch_data.fe_interface_values.reinit(cell, face_no);
1221 * scratch_data.fe_interface_values.get_fe_face_values(0);
1223 *
const unsigned n_q_points = q_points.size();
1224 *
const std::vector<double> &JxW = fe_fv.get_JxW_values();
1226 * std::vector<double> g(n_q_points);
1228 * std::vector<double> sol_u(n_q_points);
1229 * fe_fv.get_function_values(solution, sol_u);
1231 *
const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
1233 *
double difference_norm_square = 0.;
1234 *
for (
unsigned int point = 0;
point < q_points.size(); ++
point)
1236 *
const double beta_dot_n =
1237 * theta *
std::abs(beta(q_points[point]) * normals[point]);
1238 *
const double diff =
1239 * (boundary_conditions.value(q_points[point]) - sol_u[point]);
1240 * difference_norm_square += beta_dot_n * diff * diff * JxW[
point];
1242 * copy_data.value = difference_norm_square;
1245 *
const auto copier = [&](
const auto ©_data) {
1248 * energy_norm_square_per_cell[copy_data.cell_index] += copy_data.value;
1250 *
for (
auto &cdf : copy_data.face_data)
1251 * for (unsigned
int j = 0; j < 2; ++j)
1252 * energy_norm_square_per_cell[cdf.cell_indices[j]] += cdf.values[j];
1255 * ScratchData<dim> scratch_data(mapping,
1258 *
QGauss<dim - 1>{fe->tensor_degree() + 1});
1260 * CopyData copy_data;
1263 * dof_handler.end(),
1274 *
const double energy_error =
std::sqrt(energy_norm_square_per_cell.l1_norm());
1275 *
return energy_error;
1283 * <a name=
"source/DG_advection_reaction.cc-Computingtheestimator"></a>
1284 * <h3>Computing the estimator</h3>
1285 * In the estimator, we have to compute the term @f$||f- c u_h - \Pi(f- c
1286 * u_h)||_{T}^{2}@f$ over a
generic cell @f$T@f$. To achieve
this, we
first need to
1287 * compute the projection involving the finite element function @f$u_h@f$. Using the
1288 * definition of orthogonal projection, we
're required to solve cellwise
1289 * @f$(v_h,f-c u_h)_T = (v_h,\Pi)_T \qquad \forall v_h \in V_h@f$ for @f$\Pi@f$, which
1290 * means that we have to build a mass-matrix on each cell. Once we have the
1291 * projection, which is a finite element function, we can add its contribution
1292 * in the <code>cell_worker</code> lambda. As done in @ref step_74 "step-74", the square of the
1293 * error indicator is computed.
1299 * template <int dim>
1301 * AdvectionReaction<dim>::compute_local_projection_and_estimate()
1305 * Compute the term @f$||f-c u_h - \Pi(f- cu_h)||_T^2@f$
1308 * using Iterator = typename DoFHandler<dim>::active_cell_iterator;
1309 * error_indicator_per_cell.reinit(triangulation.n_active_cells());
1311 * const auto cell_worker = [&](const Iterator &cell,
1312 * ScratchData<dim> &scratch_data,
1313 * CopyData ©_data) {
1314 * const unsigned int n_dofs =
1315 * scratch_data.fe_values.get_fe().n_dofs_per_cell();
1317 * copy_data.reinit(cell, n_dofs);
1318 * scratch_data.fe_values.reinit(cell);
1319 * copy_data.cell_index = cell->active_cell_index();
1321 * const auto &q_points = scratch_data.fe_values.get_quadrature_points();
1322 * const unsigned n_q_points = q_points.size();
1324 * const FEValues<dim> &fe_v = scratch_data.fe_values;
1325 * const std::vector<double> &JxW = fe_v.get_JxW_values();
1327 * std::vector<double> sol_u_at_quadrature_points(fe_v.n_quadrature_points);
1328 * fe_v.get_function_values(solution, sol_u_at_quadrature_points);
1332 * Compute local L^2 projection of @f$f- c u_h@f$ over the local finite element
1336 * for (unsigned int point = 0; point < n_q_points; ++point)
1338 * for (unsigned int i = 0; i < n_dofs; ++i)
1340 * for (unsigned int j = 0; j < n_dofs; ++j)
1342 * copy_data.cell_mass_matrix(i, j) +=
1343 * fe_v.shape_value(i, point) * // phi_i(x_q)
1344 * fe_v.shape_value(j, point) * // phi_j(x_q)
1345 * JxW[point]; // dx(x_q)
1347 * copy_data.cell_mass_rhs(i) +=
1348 * (rhs.value(q_points[point]) * // f(x_q)
1349 * fe_v.shape_value(i, point) // phi_i(x_q)
1350 * - advection_coeff.value(q_points[point]) *
1351 * fe_v.shape_value(i, point) * // c*phi_i(x_q)
1352 * sol_u_at_quadrature_points[point]) * // u_h(x_q)
1357 * FullMatrix<double> inverse(fe_v.n_quadrature_points,
1358 * fe_v.n_quadrature_points);
1359 * inverse.invert(copy_data.cell_mass_matrix);
1360 * Vector<double> proj(fe_v.n_quadrature_points); // projection of (f-c*U_h) on
1363 * the local fe_space
1366 * inverse.vmult(proj, copy_data.cell_mass_rhs); // M^{-1}*rhs = proj
1368 * double square_norm_over_cell = 0.0;
1369 * for (unsigned int point = 0; point < n_q_points; ++point)
1371 * const double diff = rhs.value(q_points[point]) -
1372 * sol_u_at_quadrature_points[point] - proj[point];
1373 * square_norm_over_cell += diff * diff * JxW[point];
1375 * copy_data.value_estimator = square_norm_over_cell;
1380 * Finally we have the boundary term with @f$||\beta (g-u_h^+)||^2@f$
1383 * const auto boundary_worker = [&](const Iterator &cell,
1384 * const unsigned int &face_no,
1385 * ScratchData<dim> &scratch_data,
1386 * CopyData ©_data) {
1387 * scratch_data.fe_interface_values.reinit(cell, face_no);
1388 * const FEFaceValuesBase<dim> &fe_fv =
1389 * scratch_data.fe_interface_values.get_fe_face_values(0);
1390 * const auto &q_points = fe_fv.get_quadrature_points();
1391 * const unsigned n_q_points = q_points.size();
1392 * const std::vector<double> &JxW = fe_fv.get_JxW_values();
1394 * std::vector<double> g(n_q_points);
1395 * exact_solution.value_list(q_points, g);
1397 * std::vector<double> sol_u(n_q_points);
1398 * fe_fv.get_function_values(solution, sol_u);
1400 * const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
1402 * double square_norm_over_bdary_face = 0.;
1403 * for (unsigned int point = 0; point < q_points.size(); ++point)
1405 * const double beta_dot_n = beta(q_points[point]) * normals[point];
1407 * if (beta_dot_n < 0) //\partial_{-T} \cap \partial_{- \Omega}
1409 * const double diff =
1410 * std::abs(beta_dot_n) * (g[point] - sol_u[point]);
1411 * square_norm_over_bdary_face += diff * diff * JxW[point];
1414 * copy_data.value_estimator += square_norm_over_bdary_face;
1419 * Then compute the interior face terms with @f$|| \sqrt{b \cdot n}[u_h]||^2@f$
1422 * const auto face_worker = [&](const Iterator &cell,
1423 * const unsigned int &f,
1424 * const unsigned int &sf,
1425 * const Iterator &ncell,
1426 * const unsigned int &nf,
1427 * const unsigned int &nsf,
1428 * ScratchData<dim> &scratch_data,
1429 * CopyData ©_data) {
1430 * FEInterfaceValues<dim> &fe_iv = scratch_data.fe_interface_values;
1431 * fe_iv.reinit(cell, f, sf, ncell, nf, nsf);
1433 * copy_data.face_data.emplace_back();
1434 * CopyDataFace ©_data_face = copy_data.face_data.back();
1435 * copy_data_face.cell_indices[0] = cell->active_cell_index();
1436 * copy_data_face.cell_indices[1] = ncell->active_cell_index();
1438 * const auto &q_points = fe_iv.get_quadrature_points();
1439 * const unsigned n_q_points = q_points.size();
1441 * const std::vector<double> &JxW = fe_iv.get_JxW_values();
1442 * std::vector<double> g(n_q_points);
1444 * std::vector<double> jump(n_q_points);
1445 * get_function_jump(fe_iv, solution, jump);
1447 * const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
1449 * double error_jump_square{0.0};
1450 * for (unsigned int point = 0; point < n_q_points; ++point)
1452 * const double beta_dot_n = beta(q_points[point]) * normals[point];
1453 * if (beta_dot_n < 0)
1455 * error_jump_square +=
1456 * std::abs(beta_dot_n) * jump[point] * jump[point] * JxW[point];
1460 * copy_data_face.values[0] = error_jump_square;
1461 * copy_data_face.values[1] = copy_data_face.values[0];
1464 * ScratchData<dim> scratch_data(mapping,
1466 * QGauss<dim>{fe->tensor_degree() + 1},
1467 * QGauss<dim - 1>{fe->tensor_degree() + 1});
1469 * const auto copier = [&](const auto ©_data) {
1470 * if (copy_data.cell_index != numbers::invalid_unsigned_int)
1472 * error_indicator_per_cell[copy_data.cell_index] +=
1473 * copy_data.value_estimator;
1475 * for (auto &cdf : copy_data.face_data)
1477 * for (unsigned int j = 0; j < 2; ++j)
1479 * error_indicator_per_cell[cdf.cell_indices[j]] += cdf.values[j];
1486 * Here, we finally handle the assembly of the Mass matrix (M)_{ij} = (\phi_j,
1487 * \phi_i)_T. We pass in ScratchData and CopyData objects
1490 * CopyData copy_data;
1491 * MeshWorker::mesh_loop(dof_handler.begin_active(),
1492 * dof_handler.end(),
1497 * MeshWorker::assemble_own_cells |
1498 * MeshWorker::assemble_boundary_faces |
1499 * MeshWorker::assemble_own_interior_faces_once,
1508 * Usual <code>run</code> function, which runs over several refinement cycles
1511 * template <int dim>
1513 * AdvectionReaction<dim>::run()
1515 * std::vector<double> energy_errors;
1516 * std::vector<int> dofs_hist;
1517 * for (unsigned int cycle = 0; cycle < n_refinement_cycles; ++cycle)
1519 * std::cout << "Cycle " << cycle << std::endl;
1523 * GridGenerator::hyper_cube(triangulation);
1524 * triangulation.refine_global(n_global_refinements);
1530 * std::cout << " Number of active cells: "
1531 * << triangulation.n_active_cells() << std::endl;
1532 * std::cout << " Number of degrees of freedom: " << dof_handler.n_dofs()
1536 * assemble_system();
1539 * output_results(cycle);
1541 * energy_errors.emplace_back(compute_energy_norm());
1542 * dofs_hist.emplace_back(triangulation.n_active_cells());
1544 * error_table.output_table(std::cout);
1546 * for (unsigned int i = 0; i < n_refinement_cycles; ++i)
1547 * std::cout << "Cycle " << i << "\t" << energy_errors[i] << std::endl;
1551 * Explicit instantiation
1554 * template class AdvectionReaction<1>;
1555 * template class AdvectionReaction<2>;
1556 * template class AdvectionReaction<3>;
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< double > & get_JxW_values() const
const std::vector< Point< spacedim > > & get_quadrature_points() const
unsigned int depth_console(const unsigned int n)
Abstract base class for mapping classes.
The ParsedConvergenceTable class.
#define Assert(cond, exc)
typename ActiveSelector::active_cell_iterator active_cell_iterator
void mesh_loop(const CellIteratorType &begin, const CellIteratorType &end, const CellWorkerFunctionType &cell_worker, const CopierType &copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const AssembleFlags flags=assemble_own_cells, const BoundaryWorkerFunctionType &boundary_worker=BoundaryWorkerFunctionType(), const FaceWorkerFunctionType &face_worker=FaceWorkerFunctionType(), const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
@ 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.
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.)
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
@ assemble_boundary_faces
@ assemble_own_interior_faces_once
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
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)
void copy(const T *begin, const T *end, U *dest)
void assemble(const MeshWorker::DoFInfoBox< dim, DOFINFO > &dinfo, A *assembler)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
static const unsigned int invalid_unsigned_int
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation