for (const unsigned int i : fe_values.dof_indices())
{
const unsigned int component_i =
fe.system_to_component_index(i).first;
for (const unsigned int j : fe_values.dof_indices())
{
const unsigned int component_j =
fe.system_to_component_index(j).first;
for (const unsigned int q_point :
fe_values.quadrature_point_indices())
{
cell_matrix(i, j) +=
(
(fe_values.shape_grad(i, q_point)[component_i] *
fe_values.shape_grad(j, q_point)[component_j] *
lambda_values[q_point])
+
(fe_values.shape_grad(i, q_point)[component_j] *
fe_values.shape_grad(j, q_point)[component_i] *
mu_values[q_point])
+
((component_i == component_j) ?
(fe_values.shape_grad(i, q_point) *
fe_values.shape_grad(j, q_point) *
mu_values[q_point]) :
0)
) *
fe_values.JxW(q_point);
}
}
}
*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright (C) 2021 - 2024 by the deal.II authors
*
* This file is part of the deal.II library.
*
* Part of the source code is dual licensed under Apache-2.0 WITH
* LLVM-exception OR LGPL-2.1-or-later. Detailed license information
* governing the source code and code contributions can be found in
* LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
*
* ------------------------------------------------------------------------
*
* <br>
*
* <i>
* This program was contributed by Peter Munch. This work and the required
* generalizations of the internal data structures of deal.II form part of the
* project "Virtual Materials Design" funded by the Helmholtz Association of
* German Research Centres.
* </i>
*
*
* <a name="Intro"></a>
* <h1>Introduction</h1>
*
* <h3>Motivation</h3>
*
* Many freely available mesh-generation tools produce meshes that consist of
* simplices (triangles in 2d; tetrahedra in 3d). The reason for this is that
* generating such kind of meshes for complex geometries is simpler than the
* generation of hex-only meshes. This tutorial shows how to work on such kind
* of meshes with the experimental simplex features in deal.II. For this
* purpose, we solve the Poisson problem from @ref step_3 "step-3" in 2d with a mesh only
* consisting of triangles.
*
*
* <h3>Working on simplex meshes</h3>
*
* To be able to work on simplex meshes, one has to select appropriate finite
* elements, quadrature rules, and mapping objects. In @ref step_3 "step-3", we used FE_Q,
* QGauss, and (implicitly by not specifying a mapping) MappingQ1. The
* equivalent classes for the first two classes in the context of simplices are
* FE_SimplexP and QGaussSimplex, which we will utilize here. For mapping
* purposes, we use the class MappingFE, which implements an isoparametric
* mapping. We initialize it with an FE_SimplexP object so that it can be
* applied on simplex meshes.
*
*
* <h3>Mesh generation</h3>
*
* In contrast to @ref step_3 "step-3", we do not use a function from the GridGenerator
* namespace, but rather read an externally generated mesh. For this tutorial,
* we have created the mesh (square with width and height of one) with Gmsh with
* the following journal file "box_2D_tri.geo":
*
* @code
* Rectangle(1) = {0, 0, 0, 1, 1, 0};
* Mesh 2;
* Save "box_2D_tri.msh";
* @endcode
*
* The journal file can be processed by Gmsh generating the actual mesh with the
* ending ".geo":
*
* @code
* gmsh box_2D_tri.geo
* @endcode
*
* We have included in the tutorial folder both the journal file and the mesh
* file in the event that one does not have access to Gmsh.
*
* The mesh can be simply read by deal.II with methods provided by the GridIn
* class, as shown below.
*
*/
#include
<deal.II/base/function.h>
#include
<deal.II/dofs/dof_handler.h>
#include
<deal.II/dofs/dof_tools.h>
#include
<deal.II/fe/fe_values.h>
#include
<deal.II/grid/tria.h>
#include
<deal.II/lac/dynamic_sparsity_pattern.h>
#include
<deal.II/lac/full_matrix.h>
#include
<deal.II/lac/precondition.h>
#include
<deal.II/lac/solver_cg.h>
#include
<deal.II/lac/sparse_matrix.h>
#include
<deal.II/lac/vector.h>
#include
<deal.II/numerics/data_out.h>
#include
<deal.II/numerics/matrix_tools.h>
#include
<deal.II/numerics/vector_tools.h>
#include
<fstream>
#include
<iostream>
#include
<deal.II/base/quadrature_lib.h>
#include
<deal.II/fe/fe_simplex_p.h>
#include
<deal.II/fe/mapping_fe.h>
#include
<deal.II/grid/grid_in.h>
using namespace dealii;
class Step3
{
public:
Step3();
void run();
private:
void make_grid();
void setup_system();
void assemble_system();
void solve();
void output_results() const;
Triangulation<2> triangulation;
const MappingFE<2> mapping;
const FE_SimplexP<2> fe;
const QGaussSimplex<2> quadrature_formula;
DoFHandler<2> dof_handler;
SparsityPattern sparsity_pattern;
SparseMatrix<double> system_matrix;
Vector<double> solution;
Vector<double> system_rhs;
};
Step3::Step3()
: mapping(FE_SimplexP<2>(1))
, fe(2)
, quadrature_formula(3)
, dof_handler(triangulation)
{}
void Step3::make_grid()
{
GridIn<2>(triangulation).read("box_2D_tri.msh");
std::cout << "Number of active cells: " << triangulation.n_active_cells()
<< std::endl;
}
void Step3::setup_system()
{
dof_handler.distribute_dofs(fe);
std::cout << "Number of degrees of freedom: " << dof_handler.n_dofs()
<< std::endl;
DynamicSparsityPattern dsp(dof_handler.n_dofs());
DoFTools::make_sparsity_pattern(dof_handler, dsp);
sparsity_pattern.copy_from(dsp);
system_matrix.reinit(sparsity_pattern);
solution.reinit(dof_handler.n_dofs());
system_rhs.reinit(dof_handler.n_dofs());
}
void Step3::assemble_system()
{
FEValues<2> fe_values(mapping,
fe,
quadrature_formula,
update_values | update_gradients | update_JxW_values);
const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
Vector<double> cell_rhs(dofs_per_cell);
std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
for (const auto &cell : dof_handler.active_cell_iterators())
{
fe_values.reinit(cell);
cell_matrix = 0;
cell_rhs = 0;
for (const unsigned int q_index : fe_values.quadrature_point_indices())
{
for (const unsigned int i : fe_values.dof_indices())
for (const unsigned int j : fe_values.dof_indices())
cell_matrix(i, j) +=
(fe_values.shape_grad(i, q_index) *
fe_values.shape_grad(j, q_index) *
fe_values.JxW(q_index));
for (const unsigned int i : fe_values.dof_indices())
cell_rhs(i) += (fe_values.shape_value(i, q_index) *
1. *
fe_values.JxW(q_index));
}
cell->get_dof_indices(local_dof_indices);
for (const unsigned int i : fe_values.dof_indices())
for (const unsigned int j : fe_values.dof_indices())
system_matrix.add(local_dof_indices[i],
local_dof_indices[j],
cell_matrix(i, j));
for (const unsigned int i : fe_values.dof_indices())
system_rhs(local_dof_indices[i]) += cell_rhs(i);
}
std::map<types::global_dof_index, double> boundary_values;
VectorTools::interpolate_boundary_values(
mapping, dof_handler, 0, Functions::ZeroFunction<2>(), boundary_values);
MatrixTools::apply_boundary_values(boundary_values,
system_matrix,
solution,
system_rhs);
}
void Step3::solve()
{
SolverControl solver_control(1000, 1e-12);
SolverCG<Vector<double>> solver(solver_control);
solver.solve(system_matrix, solution, system_rhs, PreconditionIdentity());
std::cout << solver_control.last_step()
<< " CG iterations needed to obtain convergence." << std::endl;
}
void Step3::output_results() const
{
DataOut<2> data_out;
DataOutBase::VtkFlags flags;
flags.write_higher_order_cells = true;
data_out.set_flags(flags);
data_out.attach_dof_handler(dof_handler);
data_out.add_data_vector(solution, "solution");
data_out.build_patches(mapping, 2);
std::ofstream output("solution.vtk");
data_out.write_vtk(output);
}
void Step3::run()
{
make_grid();
setup_system();
assemble_system();
solve();
output_results();
}
int main()
{
Step3 laplace_problem;
laplace_problem.run();
return 0;
}