LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
convergence_linked_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 <cmath>
19#include <cstdlib>
20#include <filesystem>
21#include <fstream>
22#include <iostream>
23#include <stdexcept>
24#include <string>
25
26#include "strangsplitting.h"
27
30
31double getMeshSize(const std::shared_ptr<const lf::mesh::Mesh>& mesh_p) {
32 double mesh_size = 0.0;
33 /* Find maximal edge length */
34 double edge_length;
35 for (const lf::mesh::Entity* edge : mesh_p->Entities(1)) {
36 /* Compute the length of the edge */
37 auto endpoints = lf::geometry::Corners(*(edge->Geometry()));
38 edge_length = (endpoints.col(0) - endpoints.col(1)).norm();
39 if (mesh_size < edge_length) {
40 mesh_size = edge_length;
41 }
42 }
43 return mesh_size;
44}
45
46int main(int /*argc*/, char** /*argv*/) {
47 /* Obtain mesh */
48 std::unique_ptr<lf::mesh::hybrid2d::MeshFactory> mesh_factory =
49 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
50 std::filesystem::path here = __FILE__;
51 auto mesh_file = (here.parent_path() / "/meshes/test1.msh").string();
52 lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
53 std::shared_ptr<lf::mesh::Mesh> mesh_p = reader.mesh();
54 /* Mesh hierarchy */
55 std::unique_ptr<lf::mesh::hybrid2d::MeshFactory> mesh_factory2 =
56 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
57 lf::refinement::MeshHierarchy hierarchy(mesh_p, std::move(mesh_factory2));
58 /* Finite element space on finest mesh */
59 std::shared_ptr<lf::uscalfe::UniformScalarFESpace<double>> fe_space_fine;
60
61 Eigen::VectorXi numDofs(5);
62 numDofs.setZero();
63 Eigen::VectorXd meshsizes(5);
64 meshsizes.setZero();
65 Eigen::VectorXi m(5);
66 m.setZero();
67 Eigen::VectorXd tau(5);
68 tau.setZero();
69
70 /* Final time */
71 double T = 1.;
72
73 m(0) = 100;
74 tau(0) = T / m(0);
75 meshsizes(0) = 3.52544;
76
77 Eigen::VectorXd mu1(39);
78 mu1.setZero();
79 Eigen::VectorXd mu2(125);
80 mu2.setZero();
81 Eigen::VectorXd mu3(444);
82 mu3.setZero();
83 Eigen::VectorXd mu4(1670);
84 mu4.setZero();
85 Eigen::VectorXd mu5(6474);
86 mu5.setZero();
87
88 Eigen::VectorXd mu1_ipol(6474);
89 mu1_ipol.setZero();
90 Eigen::VectorXd mu2_ipol(6474);
91 mu2_ipol.setZero();
92 Eigen::VectorXd mu3_ipol(6474);
93 mu3_ipol.setZero();
94 Eigen::VectorXd mu4_ipol(6474);
95 mu4_ipol.setZero();
96
97 /* Diffusion Coefficient */
98 auto c = [](const Eigen::Vector2d & /*x*/) -> double { return 1.2; };
99 /* Growth Factor */
100 double lambda = 2.1;
101
102 hierarchy.RefineRegular();
103 hierarchy.RefineRegular();
104 hierarchy.RefineRegular();
105 hierarchy.RefineRegular();
106
107 for (int l = 4; l >= 0; l--) {
108 mesh_p = hierarchy.getMesh(l);
109 /* Finite Element Space */
110 std::shared_ptr<lf::uscalfe::UniformScalarFESpace<double>> fe_space =
111 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);
112 /* Dofhandler */
113 const lf::assemble::DofHandler& dofh{fe_space->LocGlobMap()};
114 const lf::uscalfe::size_type N_dofs(dofh.NumDofs());
115
116 numDofs(l) = N_dofs;
117 meshsizes(l) = getMeshSize(mesh_p);
118
119 std::cout << "Num of Dofs " << N_dofs << std::endl;
120 std::cout << "Meshsizes " << meshsizes(l) << std::endl;
121
122 /* Initial Population density */
123 Eigen::VectorXd u0(N_dofs);
124 u0.setZero();
125
126 if (l == 0) {
127 u0(13) = 0.3;
128 } else if (l == 1) {
129 u0(26) = 0.3;
130 } else if (l == 2) {
131 u0(52) = 0.3;
132 } else if (l == 3) {
133 u0(104) = 0.3;
134 } else if (l == 4) {
135 u0(208) = 0.3;
136 }
137
138 /* Non Local Boundary Conditions */
139
140 /* This predicate returns true for nodes on the boundary */
141 auto bd_flags{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 2)};
142 auto boundary_nodes = [&bd_flags, &dofh](unsigned int idx) -> bool {
143 return bd_flags(dofh.Entity(idx));
144 };
145
146 /* This predicate returns true for edges on the boundary */
147 auto bd_flags_edge{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 1)};
148 auto edge_pred = [&bd_flags_edge](const lf::mesh::Entity& edge) -> bool {
149 return bd_flags_edge(edge);
150 };
151
152 /* This h is to be used as a function handle for the gain term.
153 * Use it in the MassEdgeMatrix Provider.
154 */
155 auto h = [fe_space, mesh_p, edge_pred](const Eigen::Vector2d& x) -> double {
156 double res = 0.0;
157
158 auto g = [&x](const Eigen::Vector2d& y) -> double {
159 double tmp_res = 0.0;
160 if ((x - y).norm() >= 15 && (x - y).norm() <= 35) {
161 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
162 }
163 return tmp_res;
164 };
165
166 const auto* fe =
167 fe_space->ShapeFunctionLayout(lf::base::RefEl::kSegment());
168 res = localQuadFunction(
169 *mesh_p,
172 2 * fe->Degree())},
175 g, 1, edge_pred);
176 return res;
177 };
178
179 /* In what follows, the loss term is assembled. */
180 Eigen::MatrixXd L(N_dofs, N_dofs);
181 for (int j = 0; j < N_dofs; j++) {
182 if (boundary_nodes(j)) {
183 auto L_j = [fe_space, &dofh, N_dofs, edge_pred,
184 j](const Eigen::Vector2d& x) -> double {
185 auto g_j = [&x](const Eigen::Vector2d& y) -> double {
186 double tmp_res = 0.0;
187 if ((x - y).norm() >= 15 && (x - y).norm() <= 35) {
188 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
189 }
190 return tmp_res;
191 };
192
193 Eigen::VectorXd L1(N_dofs);
194 L1.setZero();
195
197 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_g),
198 decltype(edge_pred)>
199 edgeVec_y(fe_space, mf_g, edge_pred);
200 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_y, L1);
201
202 return L1(j);
203 };
204
205 Eigen::VectorXd L2(N_dofs);
206 L2.setZero();
207
209 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_L),
210 decltype(edge_pred)>
211 edgeVec_x(fe_space, mf_L, edge_pred);
212 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_x, L2);
213
214 L.col(j) = L2;
215 }
216
217 else {
218 L.col(j) = Eigen::VectorXd::Zero(N_dofs);
219 }
220 }
221 std::cout << "L norm " << L.norm() << std::endl;
222
223 /* Strang Splitting Method
224 * SDIRK-2 evolution of linear parabolic term
225 * Exact Evolution for nonlinear reaction term
226 */
227
228 if (l > 0) {
229 tau(l) = (tau(0) / meshsizes(0)) * meshsizes(l);
230 m(l) = std::round(T / tau(l));
231 }
232
233 std::cout << "tau " << tau(l) << std::endl;
234 std::cout << "M " << m(l) << std::endl;
235
236 /* Carrying Capacity */
237 Eigen::VectorXd cap(N_dofs);
238 cap.setZero();
239 cap = 0.8 * Eigen::VectorXd::Ones(N_dofs);
240
241 /* Now we may compute the solution */
242 if (l == 0) {
243 StrangSplit StrangSplitter1(fe_space, T, m(0), lambda, c, h, L);
244 mu1 = StrangSplitter1.Evolution(cap, u0);
245 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu1);
246 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
247 0, 4);
248 mu1_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
249 }
250
251 if (l == 1) {
252 StrangSplit StrangSplitter2(fe_space, T, m(1), lambda, c, h, L);
253 mu2 = StrangSplitter2.Evolution(cap, u0);
254 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu2);
255 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
256 1, 4);
257 mu2_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
258 }
259 if (l == 2) {
260 StrangSplit StrangSplitter3(fe_space, T, m(2), lambda, c, h, L);
261 mu3 = StrangSplitter3.Evolution(cap, u0);
262 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu3);
263 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
264 2, 4);
265 mu3_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
266 }
267
268 if (l == 3) {
269 StrangSplit StrangSplitter4(fe_space, T, m(3), lambda, c, h, L);
270 mu4 = StrangSplitter4.Evolution(cap, u0);
271 const lf::fe::MeshFunctionFE mf_coarse(fe_space, mu4);
272 const lf::refinement::MeshFunctionTransfer mf_fine(hierarchy, mf_coarse,
273 3, 4);
274 mu4_ipol = lf::fe::NodalProjection(*fe_space_fine, mf_fine);
275 }
276
277 if (l == 4) {
278 StrangSplit StrangSplitter5(fe_space, T, m(4), lambda, c, h, L);
279 mu5 = StrangSplitter5.Evolution(cap, u0);
280 fe_space_fine = fe_space;
281 }
282 }
283
284 /* Compare the solution to the Reference solution on the finest mesh. */
285 Eigen::Vector4d eL2;
286 eL2.setZero();
287
288 eL2(0) = (mu1_ipol - mu5).lpNorm<2>();
289 eL2(1) = (mu2_ipol - mu5).lpNorm<2>();
290 eL2(2) = (mu3_ipol - mu5).lpNorm<2>();
291 eL2(3) = (mu4_ipol - mu5).lpNorm<2>();
292
293 for (int l = 0; l < 4; l++) {
294 std::cout << "numdofs for l = " << l << " : " << numDofs(l) << std::endl;
295 std::cout << "meshsize for l = " << l << " : " << meshsizes(l) << std::endl;
296 std::cout << "for l = " << l << "number of timesteps m : " << m(l)
297 << std::endl;
298 std::cout << "for l = " << l << "timestep size : " << tau(l) << std::endl;
299
300 std::cout << "L2 error of solution with respect to reference solution on "
301 "finest mesh, for l = "
302 << l << " : " << eL2(l) << std::endl;
303 }
304
305 /* Define output file format */
306 const static Eigen::IOFormat CSVFormat(Eigen::StreamPrecision,
307 Eigen::DontAlignCols, ", ", "\n");
308 std::ofstream file;
309
310 file.open("eL2.csv");
311 file << eL2.format(CSVFormat);
312 file.close();
313
314 file.open("m.csv");
315 file << m.format(CSVFormat);
316 file.close();
317
318 file.open("tau.csv");
319 file << tau.format(CSVFormat);
320 file.close();
321
322 file.open("NumDofs.csv");
323 file << numDofs.format(CSVFormat);
324 file.close();
325
326 file.open("meshsizes.csv");
327 file << meshsizes.format(CSVFormat);
328 file.close();
329
330 /* SOLUTION */
331 file.open("mu1.csv");
332 file << mu1.format(CSVFormat);
333 file.close();
334
335 file.open("mu2.csv");
336 file << mu2.format(CSVFormat);
337 file.close();
338
339 file.open("mu3.csv");
340 file << mu3.format(CSVFormat);
341 file.close();
342
343 file.open("mu4.csv");
344 file << mu4.format(CSVFormat);
345 file.close();
346
347 file.open("mu5.csv");
348 file << mu5.format(CSVFormat);
349 file.close();
350
351 /* Interpolated Solution */
352 file.open("mu1_ipol.csv");
353 file << mu1_ipol.format(CSVFormat);
354 file.close();
355
356 file.open("mu2_ipol.csv");
357 file << mu2_ipol.format(CSVFormat);
358 file.close();
359
360 file.open("mu3_ipol.csv");
361 file << mu3_ipol.format(CSVFormat);
362 file.close();
363
364 file.open("mu4_ipol.csv");
365 file << mu4_ipol.format(CSVFormat);
366 file.close();
367
368 return 0;
369}
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