LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
convergence_meshwidth_main.cc
1
8#include <lf/assemble/dofhandler.h>
9#include <lf/fe/fe.h>
10#include <lf/io/io.h>
11#include <lf/mesh/hybrid2d/hybrid2d.h>
12#include <lf/mesh/utils/utils.h>
13#include <lf/refinement/mesh_function_transfer.h>
14#include <lf/refinement/mesh_hierarchy.h>
15#include <lf/refinement/refinement.h>
16#include <lf/uscalfe/uscalfe.h>
17
18#include <cstdlib>
19#include <filesystem>
20#include <fstream>
21#include <iostream>
22#include <stdexcept>
23#include <string>
24
25#include "strangsplitting.h"
26
29
30double getMeshSize(const std::shared_ptr<const lf::mesh::Mesh> &mesh_p) {
31 double mesh_size = 0.0;
32 /* Find maximal edge length */
33 double edge_length;
34 for (const lf::mesh::Entity *edge : mesh_p->Entities(1)) {
35 /* Compute the length of the edge */
36 auto endpoints = lf::geometry::Corners(*(edge->Geometry()));
37 edge_length = (endpoints.col(0) - endpoints.col(1)).norm();
38 if (mesh_size < edge_length) {
39 mesh_size = edge_length;
40 }
41 }
42 return mesh_size;
43}
44
45int main(int /*argc*/, char ** /*argv*/) {
46 /* Obtain mesh */
47 std::unique_ptr<lf::mesh::hybrid2d::MeshFactory> mesh_factory =
48 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
49 std::filesystem::path here = __FILE__;
50 auto mesh_file = (here.parent_path() / "/meshes/test1.msh").string();
51 lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
52 std::shared_ptr<lf::mesh::Mesh> mesh_p = reader.mesh();
53 /* Mesh hierarchy */
54 std::unique_ptr<lf::mesh::hybrid2d::MeshFactory> mesh_factory2 =
55 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
56 lf::refinement::MeshHierarchy hierarchy(mesh_p, std::move(mesh_factory2));
57
58 std::shared_ptr<lf::uscalfe::UniformScalarFESpace<double>> fe_space_fine;
59
60 Eigen::VectorXd numDofs(5);
61 numDofs.setZero();
62 Eigen::VectorXd meshsizes(5);
63 meshsizes.setZero();
64
65 Eigen::VectorXd mu1(39);
66 mu1.setZero();
67 Eigen::VectorXd mu2(125);
68 mu2.setZero();
69 Eigen::VectorXd mu3(444);
70 mu3.setZero();
71 Eigen::VectorXd mu4(1670);
72 mu4.setZero();
73 Eigen::VectorXd mu5(6474);
74 mu5.setZero();
75
76 Eigen::VectorXd mu1_ipol(39);
77 mu1_ipol.setZero();
78 Eigen::VectorXd mu2_ipol(125);
79 mu2_ipol.setZero();
80 Eigen::VectorXd mu3_ipol(444);
81 mu3_ipol.setZero();
82 Eigen::VectorXd mu4_ipol(1670);
83 mu4_ipol.setZero();
84
85 /* Diffusion Coefficient */
86 auto c = [](const Eigen::Vector2d & /*x*/) -> double { return 1.2; };
87 /* Growth Factor */
88 double lambda = 2.1;
89
90 /* Total number of timesteps */
91 unsigned int m = 100;
92 double T = 1.;
93
94 hierarchy.RefineRegular();
95 hierarchy.RefineRegular();
96 hierarchy.RefineRegular();
97 hierarchy.RefineRegular();
98
99 for (int l = 4; l >= 0; l--) {
100 mesh_p = hierarchy.getMesh(l);
101
102 /* Finite Element Space */
103 std::shared_ptr<lf::uscalfe::UniformScalarFESpace<double>> fe_space =
104 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);
105 /* Dofhandler */
106 const lf::assemble::DofHandler &dofh{fe_space->LocGlobMap()};
107 const lf::uscalfe::size_type N_dofs(dofh.NumDofs());
108
109 numDofs(l) = N_dofs;
110 meshsizes(l) = getMeshSize(mesh_p);
111
112 std::cout << "Num of Dofs " << N_dofs << std::endl;
113
114 /* Initial Population density */
115 Eigen::VectorXd u0(N_dofs);
116 u0.setZero();
117
118 if (l == 0) {
119 u0(13) = 0.3;
120 } else if (l == 1) {
121 u0(26) = 0.3;
122 } else if (l == 2) {
123 u0(52) = 0.3;
124 } else if (l == 3) {
125 u0(104) = 0.3;
126 } else if (l == 4) {
127 u0(208) = 0.3;
128 }
129
130 /* Non Local Boundary Conditions */
131
132 /* This predicate returns true for nodes on the boundary */
133 auto bd_flags{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 2)};
134 auto boundary_nodes = [&bd_flags, &dofh](unsigned int idx) -> bool {
135 return bd_flags(dofh.Entity(idx));
136 };
137
138 /* This predicate returns true for edges on the boundary */
139 auto bd_flags_edge{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 1)};
140 auto edge_pred = [&bd_flags_edge](const lf::mesh::Entity &edge) -> bool {
141 return bd_flags_edge(edge);
142 };
143
144 /* This h is to be used as a function handle for the gain term.
145 * Use it in the MassEdgeMatrix Provider.
146 */
147 auto h = [fe_space, mesh_p, edge_pred](const Eigen::Vector2d &x) -> double {
148 double res = 0.0;
149
150 auto g = [&x](const Eigen::Vector2d &y) -> double {
151 double tmp_res = 0.0;
152 if ((x - y).norm() >= 15 && (x - y).norm() <= 35) {
153 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
154 }
155 return tmp_res;
156 };
157
158 const auto *fe =
159 fe_space->ShapeFunctionLayout(lf::base::RefEl::kSegment());
160 res = localQuadFunction(
161 *mesh_p,
164 2 * fe->Degree())},
167 g, 1, edge_pred);
168 return res;
169 };
170
171 /* In what follows, the loss term is assembled. */
172 Eigen::MatrixXd L(N_dofs, N_dofs);
173 for (int j = 0; j < N_dofs; j++) {
174 if (boundary_nodes(j)) {
175 auto L_j = [fe_space, &dofh, N_dofs, edge_pred,
176 j](const Eigen::Vector2d &x) -> double {
177 auto g_j = [&x](const Eigen::Vector2d &y) -> double {
178 double tmp_res = 0.0;
179 if ((x - y).norm() >= 15 && (x - y).norm() <= 35) {
180 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
181 }
182 return tmp_res;
183 };
184
185 Eigen::VectorXd L1(N_dofs);
186 L1.setZero();
187
189 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_g),
190 decltype(edge_pred)>
191 edgeVec_y(fe_space, mf_g, edge_pred);
192 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_y, L1);
193
194 return L1(j);
195 };
196
197 Eigen::VectorXd L2(N_dofs);
198 L2.setZero();
199
201 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_L),
202 decltype(edge_pred)>
203 edgeVec_x(fe_space, mf_L, edge_pred);
204 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_x, L2);
205
206 L.col(j) = L2;
207 }
208
209 else {
210 L.col(j) = Eigen::VectorXd::Zero(N_dofs);
211 }
212 }
213 std::cout << "L norm " << L.norm() << std::endl;
214
215 /* Strang Splitting Method
216 * SDIRK-2 evolution of linear parabolic term
217 * Exact Evolution for nonlinear reaction term
218 */
219
220 /* Now we may compute the solution */
221 StrangSplit StrangSplitter(fe_space, T, m, lambda, c, h, L);
222
223 /* Carrying Capacity */
224 Eigen::VectorXd cap(N_dofs);
225 cap.setZero();
226 cap = 0.8 * Eigen::VectorXd::Ones(N_dofs);
227
228 Eigen::VectorXd sol(N_dofs);
229 sol = StrangSplitter.Evolution(cap, u0);
230
231 if (l == 0) {
232 mu1 = sol;
233 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu1);
234 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
235 0, 4);
236 mu1_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
237
238 } else if (l == 1) {
239 mu2 = sol;
240 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu2);
241 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
242 1, 4);
243 mu2_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
244
245 } else if (l == 2) {
246 mu3 = sol;
247 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu3);
248 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
249 2, 4);
250 mu3_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
251
252 } else if (l == 3) {
253 mu4 = sol;
254 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu4);
255 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
256 3, 4);
257 mu4_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
258
259 } else if (l == 4) {
260 mu5 = sol;
261 fe_space_fine = fe_space;
262 }
263 }
264
265 Eigen::Vector4d eL2;
266 eL2.setZero();
267
268 eL2(0) = (mu1_ipol - mu5).lpNorm<2>();
269 eL2(1) = (mu2_ipol - mu5).lpNorm<2>();
270 eL2(2) = (mu3_ipol - mu5).lpNorm<2>();
271 eL2(3) = (mu4_ipol - mu5).lpNorm<2>();
272
273 for (int l = 0; l < 4; l++) {
274 std::cout << "numdofs for l = " << l << " : " << numDofs(l) << std::endl;
275 std::cout << "meshsize for l = " << l << " : " << meshsizes(l) << std::endl;
276
277 std::cout << "L2 error of solution with respect to reference solution on "
278 "finest mesh, for l = "
279 << l << " : " << eL2(l) << std::endl;
280 }
281
282 /* Define output file format */
283 const static Eigen::IOFormat CSVFormat(Eigen::StreamPrecision,
284 Eigen::DontAlignCols, ", ", "\n");
285
286 std::ofstream file;
287
288 file.open("errorL2.csv");
289 file << eL2.format(CSVFormat);
290 file.close();
291
292 file.open("NumDofs.csv");
293 file << numDofs.format(CSVFormat);
294 file.close();
295
296 file.open("meshsizes.csv");
297 file << meshsizes.format(CSVFormat);
298 file.close();
299
300 /* SOLUTION */
301 file.open("mu1.csv");
302 file << mu1.format(CSVFormat);
303 file.close();
304
305 file.open("mu2.csv");
306 file << mu2.format(CSVFormat);
307 file.close();
308
309 file.open("mu3.csv");
310 file << mu3.format(CSVFormat);
311 file.close();
312
313 file.open("mu4.csv");
314 file << mu4.format(CSVFormat);
315 file.close();
316
317 file.open("mu5.csv");
318 file << mu5.format(CSVFormat);
319 file.close();
320
321 /* Interpolated Solution */
322 file.open("mu1_ipol.csv");
323 file << mu1_ipol.format(CSVFormat);
324 file.close();
325
326 file.open("mu2_ipol.csv");
327 file << mu2_ipol.format(CSVFormat);
328 file.close();
329
330 file.open("mu3_ipol.csv");
331 file << mu3_ipol.format(CSVFormat);
332 file.close();
333
334 file.open("mu4_ipol.csv");
335 file << mu4_ipol.format(CSVFormat);
336 file.close();
337
338 return 0;
339}
A general (interface) class for DOF handling, see Lecture Document Paragraph 2.7.4....
Definition: dofhandler.h:109
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
Definition: ref_el.h:150
static constexpr RefEl kTria()
Returns the reference triangle.
Definition: ref_el.h:158
A MeshFunction representing an element from a ScalarUniformFESpace (e.g. solution of BVP)
Reads a Gmsh *.msh file into a mesh::MeshFactory and provides a link between mesh::Entity objects and...
Definition: gmsh_reader.h:70
Interface class representing a topological entity in a cellular complex
Definition: entity.h:39
MeshFunction wrapper for a simple function of physical coordinates.
auto squaredNorm(const A &a)
Pointwise squared norm of another mesh function.
A MeshFunction representing interpolation on a lf::refinement::MeshHierarchy.
A hierarchy of nested 2D hybrid meshes created by refinement.
Local edge contributions to element vector.
void AssembleVectorLocally(dim_t codim, const DofHandler &dof_handler, ENTITY_VECTOR_PROVIDER &entity_vector_provider, VECTOR &resultvector)
entity-local assembly of (right-hand-side) vectors from element vectors
Definition: assembler.h:297
QuadRule make_TriaQR_EdgeMidpointRule()
edge midpoint quadrature rule for reference triangles
auto localQuadFunction(const lf::mesh::Mesh &mesh, std::map< lf::base::RefEl, lf::quad::QuadRule > quadrules, FUNCTOR &&f, unsigned int codim, const std::function< bool(const lf::mesh::Entity &)> &pred)
auto NodalProjection(const lf::fe::ScalarFESpace< SCALAR > &fe_space, MF &&u, SELECTOR &&pred=base::PredicateTrue{})
Computes nodal projection of a mesh function and returns the finite element basis expansion coefficie...
Definition: fe_tools.h:199
Eigen::MatrixXd Corners(const Geometry &geo)
The corners of a shape with piecewise smooth boundary.
CodimMeshDataSet< bool > flagEntitiesOnBoundary(const std::shared_ptr< const Mesh > &mesh_p, lf::base::dim_t codim)
flag entities of a specific co-dimension located on the boundary
QuadRule make_QuadRule(base::RefEl ref_el, unsigned degree)
Returns a QuadRule object for the given Reference Element and Degree.
lf::assemble::size_type size_type
Definition: lagr_fe.h:30