LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
step.cc
1
6#include <build_system_matrix.h>
7#include <lf/assemble/assemble.h>
8#include <lf/assemble/coomatrix.h>
9#include <lf/assemble/dofhandler.h>
10#include <lf/base/base.h>
11#include <lf/io/gmsh_reader.h>
12#include <lf/io/io.h>
13#include <lf/io/vtk_writer.h>
14#include <lf/mesh/hybrid2d/mesh.h>
15#include <lf/mesh/hybrid2d/mesh_factory.h>
16#include <lf/mesh/utils/utils.h>
17#include <lf/quad/quad.h>
18#include <lf/refinement/mesh_function_transfer.h>
19#include <lf/refinement/refinement.h>
20#include <lf/uscalfe/uscalfe.h>
21#include <mesh_function_velocity.h>
22#include <norms.h>
23#include <piecewise_const_element_matrix_provider.h>
24#include <piecewise_const_element_vector_provider.h>
25#include <solution_to_mesh_data_set.h>
26
27#include <filesystem>
28#include <iostream>
29#include <string>
30#include <vector>
31
32using lf::uscalfe::operator-;
33
40template <typename... Args>
41static std::string concat(Args &&... args) {
42 std::ostringstream ss;
43 (ss << ... << args);
44 return ss.str();
45}
46
55double poiseuilleVelocity(double h, double flowrate, double y) {
56 const double u_max = flowrate * 3 / 4 / h;
57 return u_max * (1 - y * y / h / h);
58}
59
69Eigen::VectorXd solveStep(const std::shared_ptr<const lf::mesh::Mesh> &mesh,
70 const lf::assemble::DofHandler &dofh, double flowrate,
71 bool modified_penalty) {
72 // No volume forces are present
73 auto f = [](const Eigen::Vector2d & /*unused*/) -> Eigen::Vector2d {
74 return Eigen::Vector2d::Zero();
75 };
76 // Enforce a Poiseuille in- and outflow and no-slip boundary conditions at the
77 // tube boundaries
78 auto dirichlet_funct = [&](const lf::mesh::Entity &edge) -> Eigen::Vector2d {
79 static constexpr double eps = 1e-10;
80 const auto *const geom = edge.Geometry();
81 const auto vertices = geom->Global(edge.RefEl().NodeCoords());
82 const Eigen::Vector2d midpoint = vertices.rowwise().sum() / 2;
83 Eigen::Vector2d v;
84 if (vertices(0, 0) >= -3 - eps && vertices(0, 0) <= -3 + eps &&
85 vertices(0, 1) >= -3 - eps && vertices(0, 1) <= -3 + eps) {
86 // The edge is part of the inflow boundary
87 v << poiseuilleVelocity(0.5, flowrate, midpoint[1] - 0.5), 0;
88 return v;
89 }
90 if (vertices(0, 0) >= 3 - eps && vertices(0, 0) <= 3 + eps &&
91 vertices(0, 1) >= 3 - eps && vertices(0, 1) <= 3 + eps) {
92 // The edge is part of the outflow boundary
93 v << poiseuilleVelocity(1, flowrate, midpoint[1]), 0;
94 return v;
95 }
96 return Eigen::Vector2d::Zero();
97 };
98
99 // Solve the system using sparse LU
100 const auto [A, rhs, offset_function] =
102 mesh, dofh, f, dirichlet_funct, 1,
103 lf::quad::make_TriaQR_MidpointRule(), modified_penalty);
104 lf::io::VtkWriter writer(mesh, "offset_function.vtk");
105 writer.WriteCellData("v",
107 mesh, dofh, offset_function));
108 writer.WritePointData(
109 "c",
111 mesh, dofh, offset_function));
112 auto As = A.makeSparse();
113 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
114 solver.compute(As);
115 return solver.solve(rhs) + offset_function;
116}
117
121struct ProblemSolution {
122 std::shared_ptr<const lf::mesh::Mesh> mesh;
123 std::shared_ptr<const lf::assemble::DofHandler> dofh;
124 Eigen::SparseMatrix<double> A;
125 Eigen::SparseMatrix<double> A_modified;
126 Eigen::VectorXd rhs;
127 Eigen::VectorXd solution;
128 Eigen::VectorXd solution_modified;
129};
130
134int main() {
135 const unsigned refinement_level = 6;
136 const double flowrate = 1;
137
138 // Read the mesh from the gmsh file
139 std::filesystem::path meshpath = __FILE__;
140 meshpath = meshpath.parent_path() / "step.msh";
141 std::unique_ptr<lf::mesh::MeshFactory> factory =
142 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
143 lf::io::GmshReader reader(std::move(factory), meshpath.string());
144 auto mesh0 = reader.mesh();
145
146 // Generate a mesh hierarchy by regular refinement
147 const auto mesh_hierarchy =
149 refinement_level);
150
151 std::vector<ProblemSolution> solutions(refinement_level + 1);
152 for (lf::base::size_type lvl = 0; lvl < refinement_level + 1; ++lvl) {
153 const auto &mesh = mesh_hierarchy->getMesh(lvl);
154 auto &sol = solutions[lvl];
155 sol.mesh = mesh;
156 sol.dofh = std::shared_ptr<const lf::assemble::DofHandler>(
158 mesh, {{lf::base::RefEl::kPoint(), 1},
160 // No volume forces are present
161 auto f = [](const Eigen::Vector2d & /*unused*/) -> Eigen::Vector2d {
162 return Eigen::Vector2d::Zero();
163 };
164 // Enforce a Poiseuille in- and outflow and no-slip boundary conditions at
165 // the tube boundaries
166 auto dirichlet_funct =
167 [&](const lf::mesh::Entity &edge) -> Eigen::Vector2d {
168 static constexpr double eps = 1e-10;
169 const auto *const geom = edge.Geometry();
170 const auto vertices = geom->Global(edge.RefEl().NodeCoords());
171 const Eigen::Vector2d midpoint = vertices.rowwise().sum() / 2;
172 Eigen::Vector2d v;
173 if (vertices(0, 0) >= -3 - eps && vertices(0, 0) <= -3 + eps &&
174 vertices(0, 1) >= -3 - eps && vertices(0, 1) <= -3 + eps) {
175 // The edge is part of the inflow boundary
176 v << poiseuilleVelocity(0.5, flowrate, midpoint[1] - 0.5), 0;
177 return v;
178 }
179 if (vertices(0, 0) >= 3 - eps && vertices(0, 0) <= 3 + eps &&
180 vertices(0, 1) >= 3 - eps && vertices(0, 1) <= 3 + eps) {
181 // The edge is part of the outflow boundary
182 v << poiseuilleVelocity(1, flowrate, midpoint[1]), 0;
183 return v;
184 }
185 return Eigen::Vector2d::Zero();
186 };
187 const auto [A, rhs, offset_function] =
189 sol.mesh, *(sol.dofh), f, dirichlet_funct, 1,
191 sol.A = A.makeSparse();
192 sol.rhs = rhs;
193 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
194 solver.compute(sol.A);
195 sol.solution = solver.solve(rhs) + offset_function;
196 const auto [A_modified, rhs_modified, offset_function_modified] =
198 sol.mesh, *(sol.dofh), f, dirichlet_funct, 1,
200 sol.A_modified = A_modified.makeSparse();
201 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver_modified(
202 sol.A_modified);
203 sol.solution_modified =
204 solver_modified.solve(rhs_modified) + offset_function_modified;
205 }
206
207 // Perform post processing on the data
208 const auto fe_space_fine =
209 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(
210 solutions.back().mesh);
212 velocity_exact(fe_space_fine, solutions.back().solution);
214 velocity_exact_modified(fe_space_fine,
215 solutions.back().solution_modified);
216 lf::io::VtkWriter writer(solutions.back().mesh, "result.vtk");
217 for (lf::base::size_type lvl = 0; lvl < refinement_level; ++lvl) {
218 const auto fe_space_lvl =
219 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(
220 solutions[lvl].mesh);
222 velocity_lvl(fe_space_lvl, solutions[lvl].solution);
224 velocity_lvl_modified(fe_space_lvl, solutions[lvl].solution_modified);
226 *mesh_hierarchy, velocity_lvl, lvl, mesh_hierarchy->NumLevels() - 1);
227 lf::refinement::MeshFunctionTransfer velocity_fine_modified(
228 *mesh_hierarchy, velocity_lvl_modified, lvl,
229 mesh_hierarchy->NumLevels() - 1);
230
232 0);
233 for (const auto *ep : solutions.back().mesh->Entities(0)) {
234 v(*ep) = velocity_fine(*ep, Eigen::Vector2d::Constant(1. / 3))[0];
235 }
236 writer.WriteCellData(concat("v_", solutions[lvl].mesh->NumEntities(2)), v);
237
239 solutions.back().mesh, 0);
240 for (const auto *ep : solutions.back().mesh->Entities(0)) {
241 v_modified(*ep) =
242 velocity_fine_modified(*ep, Eigen::Vector2d::Constant(1. / 3))[0];
243 }
244 writer.WriteCellData(
245 concat("v_modified", solutions[lvl].mesh->NumEntities(2)), v_modified);
246 const auto qr_provider = [](const lf::mesh::Entity &e) {
247 return lf::quad::make_QuadRule(e.RefEl(), 0);
248 };
249 const auto diff = velocity_fine - velocity_exact;
250 const auto diff_modified = velocity_fine_modified - velocity_exact_modified;
252 solutions.back().mesh, diff, qr_provider);
254 solutions.back().mesh, diff,
256 Eigen::Matrix2d::Zero()),
257 qr_provider);
258 const double L2_modified = projects::ipdg_stokes::post_processing::L2norm(
259 solutions.back().mesh, diff_modified, qr_provider);
260 const double DG_modified = projects::ipdg_stokes::post_processing::DGnorm(
261 solutions.back().mesh, diff_modified,
263 Eigen::Matrix2d::Zero()),
264 qr_provider);
265 std::cout << lvl << ' ' << solutions[lvl].mesh->NumEntities(2) << ' ' << L2
266 << ' ' << DG << ' ' << L2_modified << ' ' << DG_modified
267 << std::endl;
268 }
269
270 return 0;
271}
A general (interface) class for DOF handling, see Lecture Document Paragraph 2.7.4....
Definition: dofhandler.h:109
Dofhandler for uniform finite element spaces.
Definition: dofhandler.h:257
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
Definition: ref_el.h:150
static constexpr RefEl kPoint()
Returns the (0-dimensional) reference point.
Definition: ref_el.h:141
Reads a Gmsh *.msh file into a mesh::MeshFactory and provides a link between mesh::Entity objects and...
Definition: gmsh_reader.h:70
Write a mesh along with mesh data into a vtk file.
Definition: vtk_writer.h:272
Interface class representing a topological entity in a cellular complex
Definition: entity.h:39
A MeshDataSet that attaches data of type T to every entity of a mesh that has a specified codimension...
A MeshFunction which takes the same constant value on the whole mesh.
A MeshFunction representing interpolation on a lf::refinement::MeshHierarchy.
A MeshFunction returning the velocity computed from the basis function coefficients of a vector poten...
unsigned int size_type
general type for variables related to size of arrays
Definition: base.h:24
QuadRule make_TriaQR_MidpointRule()
midpoint quadrature rule for triangles
QuadRule make_QuadRule(base::RefEl ref_el, unsigned degree)
Returns a QuadRule object for the given Reference Element and Degree.
std::shared_ptr< MeshHierarchy > GenerateMeshHierarchyByUniformRefinemnt(const std::shared_ptr< lf::mesh::Mesh > &mesh_p, lf::base::size_type ref_lev, RefPat ref_pat)
Generated a sequence of nested 2D hybrid mehes by regular or barycentric refinement.
std::tuple< lf::assemble::COOMatrix< double >, Eigen::VectorXd, Eigen::VectorXd > buildSystemMatrixInOutFlow(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const lf::assemble::DofHandler &dofh, const std::function< Eigen::Vector2d(const Eigen::Vector2d &)> &f, const std::function< Eigen::Vector2d(const lf::mesh::Entity &)> &dirichlet_data, double sigma, const lf::quad::QuadRule &quadrule, bool modified_penalty)
Build the system matrix for the stokes system with in- and out flow boundary conditions.
lf::mesh::utils::CodimMeshDataSet< Eigen::Vector2d > extractVelocity(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const lf::assemble::DofHandler &dofh, const Eigen::VectorXd &solution)
Extract the flow velocity on cells from the solution vector.
lf::mesh::utils::CodimMeshDataSet< double > extractBasisFunctionCoefficients(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const lf::assemble::DofHandler &dofh, const Eigen::VectorXd &solution)
Extract the basis function coefficients from the solution vector.
double DGnorm(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const MF_F &f, const MF_GRAD &f_grad, const QR_SELECTOR &qr_selector)
Compute the DG-norm of a vector valued function over a mesh.
Definition: norms.h:68
double L2norm(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const MF &f, const QR_SELECTOR &qr_selector)
Compute the -norm of a vector valued function over a mesh.
Definition: norms.h:40
stores information to recover convergence properties
Definition: convergence.cc:50
Eigen::VectorXd solution
Definition: convergence.cc:56
Eigen::SparseMatrix< double > A_modified
Definition: convergence.cc:54
Eigen::VectorXd solution_modified
Definition: convergence.cc:57
Eigen::SparseMatrix< double > A
Definition: convergence.cc:53
std::shared_ptr< const lf::mesh::Mesh > mesh
Definition: convergence.cc:51
Eigen::VectorXd rhs
Definition: convergence.cc:55
std::shared_ptr< const lf::assemble::DofHandler > dofh
Definition: convergence.cc:52