\[
- \nabla \cdot \left( \nu \nabla u\right) = f \qquad \mbox{in } \Omega,
\]
For simplicity, we assume that the diffusion coefficient \(\nu\) is constant here. Note that if \(\nu\) is discontinuous, we need to take this into account when computing jump terms on cell faces.
We denote the mesh by \({\mathbb T}_h\), and \(K\in{\mathbb T}_h\) is a mesh cell. The sets of interior and boundary faces are denoted by \({\mathbb F}^i_h\) and \({\mathbb F}^b_h\) respectively. Let \(K^0\) and \(K^1\) be the two cells sharing a face \(f\in F_h^i\), and \(\mathbf n\) be the outer normal vector of \(K^0\). Then the jump operator is given by the "here minus there" formula,
respectively. Note that when \(f\subset \partial \Omega\), we define \(\jump{v} = v\) and \(\average{v}=v\). The discretization using the SIPG is given by the following weak formula (more details can be found in [202] and the references therein)
\begin{align*}
&\sum_{K\in {\mathbb T}_h} (\nabla v_h, \nu \nabla u_h)_K\\
&-\sum_{F \in F_h^i} \left\{
\left< \jump{v_h}, \nu\average{ \nabla u_h} \cdot \mathbf n \right>_F
+\left<\average{ \nabla v_h }\cdot \mathbf n,\nu\jump{u_h}\right>_F
-\left<\jump{v_h},\nu \sigma \jump{u_h} \right>_F
\right\}\\
&-\sum_{F \in F_h^b} \left\{
\left<v_h, \nu \nabla u_h\cdot \mathbf n \right>_F
+ \left< \nabla v_h \cdot \mathbf n , \nu u_h\right>_F
- \left< v_h,\nu \sigma u_h\right>_F
\right\}\\
&=(v_h, f)_\Omega
- \sum_{F \in F_h^b} \left\{
\left< \nabla v_h \cdot \mathbf n, \nu g_D\right>_F - \left<v_h,\nu \sigma g_D\right>_F
\right\}.
\end{align*}
The penalty parameter is defined as \(\sigma = \gamma/h_f\), where \(h_f\) a local length scale associated with the cell face; here we choose an approximation of the length of the cell in the direction normal to the face: \(\frac 1{h_f} = \frac 12 \left(\frac 1{h_K} + \frac 1{h_{K'}}\right)\), where \(K,K'\) are the two cells adjacent to the face \(f\) and we we compute \(h_K = \frac{|K|}{|f|}\).
In the formula above, \(\gamma\) is the penalization constant. To ensure the discrete coercivity, the penalization constant has to be large enough [3]. People do not really have consensus on which of the formulas proposed in the literature should be used. (This is similar to the situation discussed in the "Results" section of step-47.) One can just pick a large constant, while other options could be the multiples of \((p+1)^2\) or \(p(p+1)\). In this code, we follow step-39 and use \(\gamma = p(p+1)\).
In this example, with a slight modification, we use the error estimator by Karakashian and Pascal [142]
\[
\eta^2 = \sum_{K \in {\mathbb T}_h} \eta^2_{K} + \sum_{f_i \in {\mathbb F}^i_h} \eta^2_{f_i} + \sum_{f_b \in F^i_b}\eta^2_{f_b}
\]
\begin{align*}
\eta^2_{K} &= h_K^2 \left\| f + \nu \Delta u_h \right\|_K^2,
\\
\eta^2_{f_i} &= \sigma \left\| \jump{u_h} \right\|_f^2 + h_f \left\| \jump{\nu \nabla u_h} \cdot \mathbf n \right\|_f^2,
\\
\eta_{f_b}^2 &= \sigma \left\| u_h-g_D \right\|_f^2.
\end{align*}
Here we use \(\sigma = \gamma/h_f\) instead of \(\gamma^2/h_f\) for the jump terms of \(u_h\) (the first term in \(\eta^2_{f_i}\) and \(\eta_{f_b}^2\)).
\begin{align*}
\eta_{c}^2 &= h_K^2 \left\| f + \nu \Delta u_h \right\|_K^2,
\\
\eta_{f}^2 &= \sum_{f\in \partial K}\lbrace \sigma \left\| \jump{u_h} \right\|_f^2 + h_f \left\| \jump{\nu \nabla u_h} \cdot \mathbf n \right\|_f^2 \rbrace,
\\
\eta_{b}^2 &= \sum_{f\in \partial K \cap \partial \Omega} \sigma \left\| (u_h -g_D) \right\|_f^2.
\end{align*}
The factor of \(0.5\) results from the fact that the overall error estimator includes each interior face only once, and so the estimators per cell count it with a factor of one half for each of the two adjacent cells. Note that we compute \(\eta_\text{local}^2\) instead of \(\eta_\text{local}\) to simplify the implementation. The error estimate square per cell is then stored in a global vector, whose \(l_1\) norm is equal to \(\eta^2\).
In the first test problem, we run a convergence test using a smooth manufactured solution with \(\nu =1\) in 2D
\begin{align*}
u&=\sin(2\pi x)\sin(2\pi y), &\qquad\qquad &(x,y)\in\Omega=(0,1)\times (0,1),
\\
u&=0, &\qquad\qquad &\text{on } \partial \Omega,
\end{align*}
and \(f= 8\pi^2 u\). We compute errors against the manufactured solution and evaluate the convergence rate.
The first few files have already been covered in previous examples and will thus not be further commented on:
Here we define two test cases: convergence_rate for a smooth function and l_singularity for the Functions::LSingularityFunction.
This function computes the penalty \(\sigma\).
After these preparations, we proceed with the main class of this program, called SIPGLaplace. The overall structure of the class is as in many of the other tutorial programs. Major differences will only come up in the implementation of the assemble functions, since we use FEInterfaceValues to assemble face terms.
The constructor here takes the test case as input and then determines the correct solution and right-hand side classes. The remaining member variables are initialized in the obvious way.
The function starts by defining a local (lambda) function that is used to integrate the cell terms:
Finally, a function that assembles face integrals on interior faces. To reinitialize FEInterfaceValues, we need to pass cells, face and subface indices (for adaptive refinement) to the reinit() function of FEInterfaceValues:
The following lambda function will then copy data into the global matrix and right-hand side. Though there are no hanging node constraints in DG discretization, we define an empty AffineConstraints object that allows us to use the AffineConstraints::distribute_local_to_global() functionality.
Copy data from interior face assembly to the global matrix.
With the assembly functions defined, we can now create ScratchData and CopyData objects, and pass them together with the lambda functions above to MeshWorker::mesh_loop(). In addition, we need to specify that we want to assemble on interior faces exactly once.
The following two functions are entirely standard and without difficulty.
The assembly of the error estimator here is quite similar to that of the global matrix and right-had side and can be handled by the MeshWorker::mesh_loop() framework. To understand what each of the local (lambda) functions is doing, recall first that the local cell residual is defined as \(h_K^2 \left\| f + \nu \Delta u_h \right\|_K^2\):
Next compute boundary terms \(\sum_{f\in \partial K \cap \partial \Omega}
\sigma \left\| [ u_h-g_D ] \right\|_f^2 \):
And finally interior face terms \(\sum_{f\in \partial K}\lbrace \sigma
\left\| [u_h] \right\|_f^2 + h_f \left\| [\nu \nabla u_h \cdot
\mathbf n ] \right\|_f^2 \rbrace\):
Having computed local contributions for each cell, we still need a way to copy these into the global vector that will hold the error estimators for all cells:
After all of this set-up, let's do the actual work: We resize the vector into which the results will be written, and then drive the whole process using the MeshWorker::mesh_loop() function.
Next, we evaluate the accuracy in terms of the energy norm. This function is similar to the assembling of the error estimator above. Here we compute the square of the energy norm defined by
\[
\|u \|_{1,h}^2 = \sum_{K \in \Gamma_h} \nu\|\nabla u \|_K^2 +
\sum_{f \in F_i} \sigma \| [ u ] \|_f^2 +
\sum_{f \in F_b} \sigma \|u\|_f^2.
\]
\[
\|u -u_h \|_{1,h}^2 = \sum_{K \in \Gamma_h} \nu\|\nabla (u_h - u) \|_K^2
+ \sum_{f \in F_i} \sigma \|[ u_h ] \|_f^2 + \sum_{f \in F_b}\sigma
\|u_h-g_D\|_f^2.
\]
Assemble \(\sum_{K \in \Gamma_h} \nu\|\nabla (u_h - u) \|_K^2 \).
Assemble \(\sum_{f \in F_b}\sigma \|u_h-g_D\|_f^2\).
Assemble \(\sum_{f \in F_i} \sigma \| [ u_h ] \|_f^2\).
We compute three errors in the \(L_2\) norm, \(H_1\) seminorm, and the energy norm, respectively. These are then printed to screen, but also stored in a table that records how these errors decay with mesh refinement and which can be output in one step at the end of the program.
Having run all of our computations, let us tell the convergence table how to format its data and output it to screen:
The output of this program consist of the console output and solutions in vtu format.
In the first test case, when you run the program, the screen output should look like the following:
When using the smooth case with polynomial degree 3, the convergence table will look like this:
Theoretically, for polynomial degree \(p\), the order of convergence in \(L_2\) norm and \(H^1\) seminorm should be \(p+1\) and \(p\), respectively. Our numerical results are in good agreement with theory.
In the second test case, when you run the program, the screen output should look like the following:
The following figure provides a log-log plot of the errors versus the number of degrees of freedom for this test case on the L-shaped domain. In order to interpret it, let \(n\) be the number of degrees of freedom, then on uniformly refined meshes, \(h\) is of order \(1/\sqrt{n}\) in 2D. Combining the theoretical results in the previous case, we see that if the solution is sufficiently smooth, we can expect the error in the \(L_2\) norm to be of order \(O(n^{-\frac{p+1}{2}})\) and in \(H^1\) seminorm to be \(O(n^{-\frac{p}{2}})\). It is not a priori clear that one would get the same kind of behavior as a function of \(n\) on adaptively refined meshes like the ones we use for this second test case, but one can certainly hope. Indeed, from the figure, we see that the SIPG with adaptive mesh refinement produces asymptotically the kinds of hoped-for results:
In addition, we observe that the error estimator decreases at almost the same rate as the errors in the energy norm and \(H^1\) seminorm, and one order lower than the \(L_2\) error. This suggests its ability to predict regions with large errors.
#include <fstream>
namespace Step74
{
enum class TestCase
{
convergence_rate,
l_singularity
};
template <int dim>
class SmoothSolution :
public Function<dim>
{
public:
SmoothSolution()
{}
virtual void value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int component = 0) const override;
const unsigned int component = 0) const override;
};
template <int dim>
void SmoothSolution<dim>::value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int ) const
{
for (
unsigned int i = 0; i <
values.size(); ++i)
values[i] =
}
template <int dim>
SmoothSolution<dim>::gradient(
const Point<dim> &point,
const unsigned int ) const
{
return_value[0] =
return_value[1] =
return return_value;
}
template <int dim>
class SmoothRightHandSide :
public Function<dim>
{
public:
SmoothRightHandSide()
{}
virtual void value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int ) const override;
};
template <int dim>
void
SmoothRightHandSide<dim>::value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int ) const
{
for (
unsigned int i = 0; i <
values.size(); ++i)
values[i] = 8. * PI * PI *
std::sin(2. * PI * points[i][0]) *
}
template <int dim>
class SingularRightHandSide :
public Function<dim>
{
public:
SingularRightHandSide()
{}
virtual void value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int ) const override;
private:
};
template <int dim>
void
SingularRightHandSide<dim>::value_list(
const std::vector<
Point<dim>> &points,
std::vector<double> &values,
const unsigned int ) const
{
for (
unsigned int i = 0; i <
values.size(); ++i)
}
double get_penalty_factor(const unsigned int fe_degree,
const double cell_extent_left,
const double cell_extent_right)
{
const unsigned int degree =
std::max(1U, fe_degree);
return degree * (degree + 1.) * 0.5 *
(1. / cell_extent_left + 1. / cell_extent_right);
}
struct CopyDataFace
{
std::vector<types::global_dof_index> joint_dof_indices;
std::array<unsigned int, 2> cell_indices;
};
struct CopyData
{
std::vector<types::global_dof_index> local_dof_indices;
std::vector<CopyDataFace> face_data;
template <class Iterator>
void reinit(
const Iterator &cell,
const unsigned int dofs_per_cell)
{
cell_rhs.
reinit(dofs_per_cell);
local_dof_indices.resize(dofs_per_cell);
cell->get_dof_indices(local_dof_indices);
}
};
template <int dim>
class SIPGLaplace
{
public:
SIPGLaplace(const TestCase &test_case);
private:
void setup_system();
void assemble_system();
void solve();
void refine_grid();
void output_results(const unsigned int cycle) const;
void compute_errors();
void compute_error_estimate();
double compute_energy_norm_error();
const unsigned int degree;
const QGauss<dim - 1> face_quadrature;
const QGauss<dim - 1> face_quadrature_overintegration;
const double diffusion_coefficient = 1.;
const TestCase test_case;
std::unique_ptr<const Function<dim>> exact_solution;
std::unique_ptr<const Function<dim>> rhs_function;
};
template <int dim>
SIPGLaplace<dim>::SIPGLaplace(const TestCase &test_case)
: degree(3)
, quadrature(degree + 1)
, face_quadrature(degree + 1)
, quadrature_overintegration(degree + 2)
, face_quadrature_overintegration(degree + 2)
, mapping()
, fe(degree)
, dof_handler(triangulation)
, test_case(test_case)
{
if (test_case == TestCase::convergence_rate)
{
exact_solution = std::make_unique<const SmoothSolution<dim>>();
rhs_function = std::make_unique<const SmoothRightHandSide<dim>>();
}
else if (test_case == TestCase::l_singularity)
{
exact_solution =
std::make_unique<const Functions::LSingularityFunction>();
rhs_function = std::make_unique<const SingularRightHandSide<dim>>();
}
else
}
template <int dim>
void SIPGLaplace<dim>::setup_system()
{
dof_handler.distribute_dofs(fe);
sparsity_pattern.copy_from(dsp);
system_matrix.reinit(sparsity_pattern);
solution.reinit(dof_handler.n_dofs());
system_rhs.reinit(dof_handler.n_dofs());
}
template <int dim>
void SIPGLaplace<dim>::assemble_system()
{
const auto cell_worker =
ScratchData &scratch_data,
CopyData ©_data) {
copy_data.reinit(cell, dofs_per_cell);
const std::vector<Point<dim>> &q_points =
scratch_data.get_quadrature_points();
const unsigned int n_q_points = q_points.size();
const std::vector<double> &JxW = scratch_data.get_JxW_values();
std::vector<double> rhs(n_q_points);
rhs_function->value_list(q_points, rhs);
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
copy_data.cell_matrix(i, j) +=
diffusion_coefficient *
}
};
const auto boundary_worker =
const unsigned int &face_no,
ScratchData &scratch_data,
CopyData ©_data) {
const std::vector<Point<dim>> &q_points =
scratch_data.get_quadrature_points();
const unsigned int n_q_points = q_points.size();
const std::vector<double> &JxW = scratch_data.get_JxW_values();
const std::vector<Tensor<1, dim>> &normals =
scratch_data.get_normal_vectors();
std::vector<double> g(n_q_points);
exact_solution->value_list(q_points, g);
const double extent1 = cell->measure() / cell->face(face_no)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent1);
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
for (unsigned int i = 0; i < dofs_per_cell; ++i)
for (unsigned int j = 0; j < dofs_per_cell; ++j)
copy_data.cell_matrix(i, j) +=
(-diffusion_coefficient *
- diffusion_coefficient *
+ diffusion_coefficient * penalty *
) *
JxW[point];
for (unsigned int i = 0; i < dofs_per_cell; ++i)
copy_data.cell_rhs(i) +=
(-diffusion_coefficient *
g[point]
+ diffusion_coefficient * penalty *
) *
JxW[point];
}
};
const auto face_worker =
const unsigned int &f,
const unsigned int &sf,
const unsigned int &nf,
const unsigned int &nsf,
ScratchData &scratch_data,
CopyData ©_data) {
scratch_data.
reinit(cell, f, sf, ncell, nf, nsf);
copy_data.face_data.emplace_back();
CopyDataFace ©_data_face = copy_data.face_data.back();
copy_data_face.cell_matrix.reinit(n_dofs_face, n_dofs_face);
const double extent1 = cell->measure() / cell->face(f)->measure();
const double extent2 = ncell->measure() / ncell->face(nf)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent2);
for (const unsigned int point : fe_iv.quadrature_point_indices())
{
for (const unsigned int i : fe_iv.dof_indices())
(-diffusion_coefficient *
fe_iv.jump_in_shape_values(i,
point) *
(fe_iv.average_of_shape_gradients(j,
- diffusion_coefficient *
(fe_iv.average_of_shape_gradients(i,
fe_iv.jump_in_shape_values(j,
point)
+ diffusion_coefficient * penalty *
fe_iv.jump_in_shape_values(i,
point) *
fe_iv.jump_in_shape_values(j,
point)
) *
}
};
const auto copier = [&](const CopyData &c) {
c.cell_rhs,
c.local_dof_indices,
system_matrix,
system_rhs);
for (const CopyDataFace &cdf : c.face_data)
{
cdf.joint_dof_indices,
system_matrix);
}
};
ScratchData scratch_data(
mapping, fe, quadrature, cell_flags, face_quadrature, face_flags);
CopyData copy_data;
dof_handler.end(),
cell_worker,
copier,
scratch_data,
copy_data,
boundary_worker,
face_worker);
}
template <int dim>
void SIPGLaplace<dim>::solve()
{
A_direct.
vmult(solution, system_rhs);
}
template <int dim>
void SIPGLaplace<dim>::output_results(const unsigned int cycle) const
{
".vtu";
std::ofstream output(filename);
}
template <int dim>
void SIPGLaplace<dim>::compute_error_estimate()
{
const auto cell_worker =
ScratchData &scratch_data,
CopyData ©_data) {
copy_data.cell_index = cell->active_cell_index();
const unsigned int n_q_points = q_points.size();
std::vector<Tensor<2, dim>>
hessians(n_q_points);
std::vector<double> rhs(n_q_points);
rhs_function->value_list(q_points, rhs);
const double hk = cell->diameter();
double residual_norm_square = 0;
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
const double residual =
rhs[
point] + diffusion_coefficient *
trace(hessians[point]);
residual_norm_square += residual * residual * JxW[
point];
}
copy_data.value = hk * hk * residual_norm_square;
};
const auto boundary_worker =
const unsigned int &face_no,
ScratchData &scratch_data,
CopyData ©_data) {
const unsigned n_q_points = q_points.size();
std::vector<double> g(n_q_points);
exact_solution->value_list(q_points, g);
std::vector<double> sol_u(n_q_points);
const double extent1 = cell->measure() / cell->face(face_no)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent1);
double difference_norm_square = 0.;
for (
unsigned int point = 0;
point < q_points.size(); ++
point)
{
difference_norm_square += diff * diff * JxW[
point];
}
copy_data.value += penalty * difference_norm_square;
};
const auto face_worker =
const unsigned int &f,
const unsigned int &sf,
const unsigned int &nf,
const unsigned int &nsf,
ScratchData &scratch_data,
CopyData ©_data) {
scratch_data.
reinit(cell, f, sf, ncell, nf, nsf);
copy_data.face_data.emplace_back();
CopyDataFace ©_data_face = copy_data.face_data.back();
copy_data_face.cell_indices[0] = cell->active_cell_index();
copy_data_face.cell_indices[1] = ncell->active_cell_index();
const unsigned int n_q_points = q_points.size();
std::vector<double> jump(n_q_points);
std::vector<Tensor<1, dim>> grad_jump(n_q_points);
const double h = cell->face(f)->diameter();
const double extent1 = cell->measure() / cell->face(f)->measure();
const double extent2 = ncell->measure() / ncell->face(nf)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent2);
double flux_jump_square = 0;
double u_jump_square = 0;
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
const double flux_jump = grad_jump[
point] * normals[
point];
flux_jump_square +=
diffusion_coefficient * flux_jump * flux_jump * JxW[
point];
}
copy_data_face.values[0] =
0.5 * h * (flux_jump_square + penalty * u_jump_square);
copy_data_face.values[1] = copy_data_face.values[0];
};
const auto copier = [&](const CopyData ©_data) {
estimated_error_square_per_cell[copy_data.cell_index] +=
copy_data.value;
for (const CopyDataFace &cdf : copy_data.face_data)
for (unsigned
int j = 0; j < 2; ++j)
estimated_error_square_per_cell[cdf.cell_indices[j]] += cdf.values[j];
};
estimated_error_square_per_cell.reinit(triangulation.n_active_cells());
ScratchData scratch_data(
mapping, fe, quadrature, cell_flags, face_quadrature, face_flags);
CopyData copy_data;
dof_handler.end(),
cell_worker,
copier,
scratch_data,
copy_data,
boundary_worker,
face_worker);
}
template <int dim>
double SIPGLaplace<dim>::compute_energy_norm_error()
{
energy_norm_square_per_cell.reinit(triangulation.n_active_cells());
const auto cell_worker =
ScratchData &scratch_data,
CopyData ©_data) {
copy_data.cell_index = cell->active_cell_index();
const unsigned int n_q_points = q_points.size();
std::vector<Tensor<1, dim>> grad_u(n_q_points);
std::vector<Tensor<1, dim>> grad_exact(n_q_points);
exact_solution->gradient_list(q_points, grad_exact);
double norm_square = 0;
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
norm_square +=
}
copy_data.value = diffusion_coefficient * norm_square;
};
const auto boundary_worker =
const unsigned int &face_no,
ScratchData &scratch_data,
CopyData ©_data) {
const unsigned n_q_points = q_points.size();
std::vector<double> g(n_q_points);
exact_solution->value_list(q_points, g);
std::vector<double> sol_u(n_q_points);
const double extent1 = cell->measure() / cell->face(face_no)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent1);
double difference_norm_square = 0.;
for (
unsigned int point = 0;
point < q_points.size(); ++
point)
{
difference_norm_square += diff * diff * JxW[
point];
}
copy_data.value += penalty * difference_norm_square;
};
const auto face_worker =
const unsigned int &f,
const unsigned int &sf,
const unsigned int &nf,
const unsigned int &nsf,
ScratchData &scratch_data,
CopyData ©_data) {
scratch_data.
reinit(cell, f, sf, ncell, nf, nsf);
copy_data.face_data.emplace_back();
CopyDataFace ©_data_face = copy_data.face_data.back();
copy_data_face.cell_indices[0] = cell->active_cell_index();
copy_data_face.cell_indices[1] = ncell->active_cell_index();
const unsigned int n_q_points = q_points.size();
std::vector<double> jump(n_q_points);
const double extent1 = cell->measure() / cell->face(f)->measure();
const double extent2 = ncell->measure() / ncell->face(nf)->measure();
const double penalty = get_penalty_factor(degree, extent1, extent2);
double u_jump_square = 0;
for (
unsigned int point = 0;
point < n_q_points; ++
point)
{
}
copy_data_face.values[0] = 0.5 * penalty * u_jump_square;
copy_data_face.values[1] = copy_data_face.values[0];
};
const auto copier = [&](const CopyData ©_data) {
energy_norm_square_per_cell[copy_data.cell_index] += copy_data.value;
for (const CopyDataFace &cdf : copy_data.face_data)
for (unsigned
int j = 0; j < 2; ++j)
energy_norm_square_per_cell[cdf.cell_indices[j]] += cdf.values[j];
};
const ScratchData scratch_data(mapping,
fe,
quadrature_overintegration,
cell_flags,
face_quadrature_overintegration,
face_flags);
CopyData copy_data;
dof_handler.end(),
cell_worker,
copier,
scratch_data,
copy_data,
boundary_worker,
face_worker);
const double energy_error =
std::sqrt(energy_norm_square_per_cell.l1_norm());
return energy_error;
}
template <int dim>
void SIPGLaplace<dim>::refine_grid()
{
const double refinement_fraction = 0.1;
triangulation, estimated_error_square_per_cell, refinement_fraction, 0.);
triangulation.execute_coarsening_and_refinement();
}
template <int dim>
void SIPGLaplace<dim>::compute_errors()
{
double L2_error, H1_error, energy_error;
{
Vector<float> difference_per_cell(triangulation.n_active_cells());
dof_handler,
solution,
*(exact_solution.get()),
difference_per_cell,
quadrature_overintegration,
difference_per_cell,
convergence_table.add_value("L2", L2_error);
}
{
Vector<float> difference_per_cell(triangulation.n_active_cells());
dof_handler,
solution,
*(exact_solution.get()),
difference_per_cell,
quadrature_overintegration,
difference_per_cell,
convergence_table.add_value("H1", H1_error);
}
{
energy_error = compute_energy_norm_error();
convergence_table.add_value("Energy", energy_error);
}
std::cout << " Error in the L2 norm : " << L2_error << std::endl
<< " Error in the H1 seminorm : " << H1_error << std::endl
<< " Error in the energy norm : " << energy_error
<< std::endl;
}
template <int dim>
void SIPGLaplace<dim>::run()
{
const unsigned int max_cycle =
(test_case == TestCase::convergence_rate ? 6 : 20);
for (unsigned int cycle = 0; cycle < max_cycle; ++cycle)
{
std::cout << "Cycle " << cycle << std::endl;
switch (test_case)
{
case TestCase::convergence_rate:
{
if (cycle == 0)
{
triangulation.refine_global(2);
}
else
{
triangulation.refine_global(1);
}
break;
}
case TestCase::l_singularity:
{
if (cycle == 0)
{
triangulation.refine_global(3);
}
else
{
refine_grid();
}
break;
}
default:
{
}
}
std::cout << " Number of active cells : "
<< triangulation.n_active_cells() << std::endl;
setup_system();
std::cout << " Number of degrees of freedom : " << dof_handler.n_dofs()
<< std::endl;
assemble_system();
solve();
output_results(cycle);
{
convergence_table.add_value("cycle", cycle);
convergence_table.add_value("cells", triangulation.n_active_cells());
convergence_table.add_value("dofs", dof_handler.n_dofs());
}
compute_errors();
if (test_case == TestCase::l_singularity)
{
compute_error_estimate();
std::cout << " Estimated error : "
<<
std::sqrt(estimated_error_square_per_cell.l1_norm())
<< std::endl;
convergence_table.add_value(
"Estimator",
std::sqrt(estimated_error_square_per_cell.l1_norm()));
}
std::cout << std::endl;
}
convergence_table.set_precision("L2", 3);
convergence_table.set_precision("H1", 3);
convergence_table.set_precision("Energy", 3);
convergence_table.set_scientific("L2", true);
convergence_table.set_scientific("H1", true);
convergence_table.set_scientific("Energy", true);
if (test_case == TestCase::convergence_rate)
{
convergence_table.evaluate_convergence_rates(
convergence_table.evaluate_convergence_rates(
}
if (test_case == TestCase::l_singularity)
{
convergence_table.set_precision("Estimator", 3);
convergence_table.set_scientific("Estimator", true);
}
std::cout << "degree = " << degree << std::endl;
convergence_table.write_text(
}
}
{
try
{
using namespace Step74;
const TestCase test_case = TestCase::l_singularity;
SIPGLaplace<2> problem(test_case);
problem.run();
}
catch (std::exception &exc)
{
std::cerr << std::endl
<< std::endl
<< "----------------------------------------------------"
<< std::endl;
std::cerr << "Exception on processing: " << std::endl
<< exc.what() << std::endl
<< "Aborting!" << std::endl
<< "----------------------------------------------------"
<< std::endl;
return 1;
}
catch (...)
{
std::cerr << std::endl
<< std::endl
<< "----------------------------------------------------"
<< std::endl;
std::cerr << "Unknown exception!" << std::endl
<< "Aborting!" << std::endl
<< "----------------------------------------------------"
<< std::endl;
return 1;
};
return 0;
}
void distribute_local_to_global(const InVector &local_vector, const std::vector< size_type > &local_dof_indices, OutVector &global_vector) const
void write_vtu(std::ostream &out) const
void add_data_vector(const VectorType &data, const std::vector< std::string > &names, const DataVectorType type=type_automatic, const std::vector< DataComponentInterpretation::DataComponentInterpretation > &data_component_interpretation={})
virtual void build_patches(const unsigned int n_subdivisions=0)
void reinit(const Triangulation< dim, spacedim > &tria)
const std::vector< double > & get_JxW_values() const
unsigned n_current_interface_dofs() const
const std::vector< Tensor< 1, spacedim > > & get_normal_vectors() const
const std::vector< Point< spacedim > > & get_quadrature_points() const
std::vector< types::global_dof_index > get_interface_dof_indices() const
void get_jump_in_function_values(const InputVector &fe_function, std::vector< typename InputVector::value_type > &values) const
void get_jump_in_function_gradients(const InputVector &fe_function, std::vector< Tensor< 1, spacedim, typename InputVector::value_type > > &gradients) const
const std::vector< double > & get_JxW_values() const
void get_function_values(const ReadVector< Number > &fe_function, std::vector< Number > &values) const
const unsigned int dofs_per_cell
void get_function_hessians(const ReadVector< Number > &fe_function, std::vector< Tensor< 2, spacedim, Number > > &hessians) const
void get_function_gradients(const ReadVector< Number > &fe_function, std::vector< Tensor< 1, spacedim, Number > > &gradients) const
const Tensor< 1, spacedim > & shape_grad(const unsigned int i, const unsigned int q_point) const
const double & shape_value(const unsigned int i, const unsigned int q_point) const
void vmult(Vector< double > &dst, const Vector< double > &src) const
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
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.)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
* * * * std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters const
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 reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)