LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
poiseuille.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/io.h>
12#include <lf/io/vtk_writer.h>
13#include <lf/mesh/hybrid2d/mesh.h>
14#include <lf/mesh/hybrid2d/mesh_factory.h>
15#include <lf/mesh/utils/tp_triag_mesh_builder.h>
16#include <lf/mesh/utils/utils.h>
17#include <lf/quad/quad.h>
18#include <lf/refinement/refinement.h>
19#include <mesh_function_velocity.h>
20#include <norms.h>
21#include <piecewise_const_element_matrix_provider.h>
22#include <piecewise_const_element_vector_provider.h>
23#include <solution_to_mesh_data_set.h>
24
25#include <cstring>
26#include <iostream>
27#include <string>
28#include <vector>
29
30using lf::uscalfe::operator-;
31
38template <typename... Args>
39static std::string concat(Args &&... args) {
40 std::ostringstream ss;
41 (ss << ... << args);
42 return ss.str();
43}
44
53double poiseuilleVelocity(double h, double flowrate, double y) {
54 const double u_max = flowrate * 3 / 4 / h;
55 return u_max * (1 - y * y / h / h);
56}
57
61struct ProblemSolution {
62 std::shared_ptr<const lf::mesh::Mesh> mesh;
63 std::shared_ptr<const lf::assemble::DofHandler> dofh;
64 Eigen::SparseMatrix<double> A;
65 Eigen::SparseMatrix<double> A_modified;
66 Eigen::VectorXd rhs;
67 Eigen::VectorXd solution;
68 Eigen::VectorXd solution_modified;
69};
70
74int main(int argc, char *argv[]) {
75 const unsigned refinement_level = 6;
76 const double flowrate = 1;
77 const double h = 0.25;
78
79 if (argc != 2) {
80 std::cerr << "Usage: " << argv[0] << "[regular,stretched]" << std::endl;
81 std::cerr << "\tregular : Builds a mesh where the resolution in the x- "
82 "and y-direction are equal"
83 << std::endl;
84 std::cerr << "\tirregular: Builds a mesh where the resolution in the "
85 "x-direction is 10 times less than in the y-direction"
86 << std::endl;
87 exit(1);
88 }
89
90 // Read the mesh from the gmsh file
91 std::unique_ptr<lf::mesh::MeshFactory> factory =
92 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
93 lf::mesh::utils::TPTriagMeshBuilder builder(std::move(factory));
94 builder.setBottomLeftCorner(0, 0);
95 builder.setTopRightCorner(2, 2 * h);
96 if (strcmp(argv[1], "irregular") == 0) {
97 builder.setNumXCells(std::max(2, 2 * static_cast<int>(1.0 / h) / 10));
98 } else {
99 builder.setNumXCells(2 * static_cast<int>(1.0 / h));
100 }
101 builder.setNumYCells(2);
102 auto mesh0 = builder.Build();
103
104 // Generate a mesh hierarchy by regular refinement
105 const auto mesh_hierarchy =
107 refinement_level);
108
109 // Solve the problem for each mesh in the hierarchy
110 std::vector<ProblemSolution> solutions(refinement_level + 1);
111 for (lf::base::size_type lvl = 0; lvl < refinement_level + 1; ++lvl) {
112 const auto &mesh = mesh_hierarchy->getMesh(lvl);
113 auto &sol = solutions[lvl];
114 sol.mesh = mesh;
115 sol.dofh = std::shared_ptr<const lf::assemble::DofHandler>(
117 mesh, {{lf::base::RefEl::kPoint(), 1},
119 // No volume forces are present
120 auto f = [](const Eigen::Vector2d & /*unused*/) -> Eigen::Vector2d {
121 return Eigen::Vector2d::Zero();
122 };
123 // Enforce a Poiseuille in- and outflow and no-slip boundary conditions at
124 // the tube boundaries
125 auto dirichlet_funct =
126 [&](const lf::mesh::Entity &edge) -> Eigen::Vector2d {
127 static constexpr double eps = 1e-10;
128 const auto *const geom = edge.Geometry();
129 const auto vertices = geom->Global(edge.RefEl().NodeCoords());
130 const Eigen::Vector2d midpoint = vertices.rowwise().sum() / 2;
131 Eigen::Vector2d v = Eigen::Vector2d::Zero();
132 if (vertices(0, 0) >= -eps && vertices(0, 0) <= eps &&
133 vertices(0, 1) >= -eps && vertices(0, 1) <= eps) {
134 // The edge is part of the inflow boundary
135 v << poiseuilleVelocity(h, flowrate, midpoint[1] - h), 0;
136 }
137 if (vertices(0, 0) >= 2 - eps && vertices(0, 0) <= 2 + eps &&
138 vertices(0, 1) >= 2 - eps && vertices(0, 1) <= 2 + eps) {
139 // The edge is part of the outflow boundary
140 v << poiseuilleVelocity(h, flowrate, midpoint[1] - h), 0;
141 }
142 return v;
143 };
144 const auto [A, rhs, offset_function] =
146 sol.mesh, *(sol.dofh), f, dirichlet_funct, 100,
148 sol.A = A.makeSparse();
149 sol.rhs = rhs;
150 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
151 solver.compute(sol.A);
152 sol.solution = solver.solve(rhs) + offset_function;
153 const auto [A_modified, rhs_modified, offset_function_modified] =
155 sol.mesh, *(sol.dofh), f, dirichlet_funct, 100,
157 sol.A_modified = A_modified.makeSparse();
158 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver_modified(
159 sol.A_modified);
160 sol.solution_modified =
161 solver_modified.solve(rhs_modified) + offset_function_modified;
162 }
163
164 // Compute the analytic solution of the problem
165 auto analytic_velocity = [&](const Eigen::Vector2d &x) -> Eigen::Vector2d {
166 const double u_max = flowrate * 3 / 4 / h;
167 Eigen::Vector2d v;
168 v << u_max * (1 - (x[1] - h) * (x[1] - h) / h / h), 0;
169 return v;
170 };
171 auto analytic_gradient = [&](const Eigen::Vector2d &x) -> Eigen::Matrix2d {
172 const double u_max = flowrate * 3 / 4 / h;
173 Eigen::Matrix2d g;
174 g << 0, 0, u_max * 2 * (x[1] - h) / h / h, 0;
175 return g;
176 };
177
178 // Perform post processing on the data
179 lf::io::VtkWriter writer(solutions.back().mesh, "result.vtk");
180 for (lf::base::size_type lvl = 0; lvl <= refinement_level; ++lvl) {
181 const auto fe_space =
182 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(
183 solutions[lvl].mesh);
184 const auto velocity_exact =
185 lf::mesh::utils::MeshFunctionGlobal(analytic_velocity);
186 const auto gradient_exact =
187 lf::mesh::utils::MeshFunctionGlobal(analytic_gradient);
188 const auto velocity =
190 double>(
191 fe_space, solutions[lvl].solution);
192 const auto velocity_modified =
194 double>(
195 fe_space, solutions[lvl].solution_modified);
196 // Store the result on the finest mesh to vtk
197 if (lvl == refinement_level) {
199 solutions.back().mesh, 0);
200 for (const auto *ep : solutions.back().mesh->Entities(0)) {
201 const Eigen::Vector2d center =
202 ep->Geometry()->Global(ep->RefEl().NodeCoords()).rowwise().sum() /
203 ep->RefEl().NumNodes();
204 v(*ep) = analytic_velocity(center);
205 }
206 writer.WriteCellData(concat("v_", solutions[lvl].mesh->NumEntities(2)),
207 velocity);
208
210 solutions.back().mesh, 0);
211 for (const auto *ep : solutions.back().mesh->Entities(0)) {
212 v_modified(*ep) =
213 velocity_modified(*ep, Eigen::Vector2d::Constant(1. / 3))[0];
214 }
215 writer.WriteCellData(
216 concat("v__modified_", solutions[lvl].mesh->NumEntities(2)),
217 v_modified);
218 writer.WriteCellData("analytic", v);
219 }
220 // Compute the error in the velocity
221 auto diff_v = velocity - velocity_exact;
222 auto diff_v_modified = velocity_modified - velocity_exact;
223 // Compute the error in the gradient of the velocity
224 auto diff_g = -gradient_exact;
225 auto diff_g_modified = -gradient_exact;
226 const auto qr_provider = [](const lf::mesh::Entity &e) {
227 return lf::quad::make_QuadRule(e.RefEl(), 0);
228 };
230 solutions[lvl].mesh, diff_v, qr_provider);
232 solutions[lvl].mesh, diff_v, diff_g, qr_provider);
233 const double L2_modified = projects::ipdg_stokes::post_processing::L2norm(
234 solutions[lvl].mesh, diff_v_modified, qr_provider);
235 const double DG_modified = projects::ipdg_stokes::post_processing::DGnorm(
236 solutions[lvl].mesh, diff_v_modified, diff_g_modified, qr_provider);
237 std::cout << lvl << ' ' << solutions[lvl].mesh->NumEntities(2) << ' ' << L2
238 << ' ' << DG << ' ' << L2_modified << ' ' << DG_modified
239 << std::endl;
240 }
241
242 return 0;
243}
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
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...
MeshFunction wrapper for a simple function of physical coordinates.
Implements a MeshBuilder that generates a triangular structured mesh.
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.
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