LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
strangsplitting_main.cc
1
8#include <lf/io/io.h>
9
10#include <cstdlib>
11#include <filesystem>
12#include <iostream>
13#include <stdexcept>
14#include <string>
15#include <vector>
16
17#include "strangsplitting.h"
18
21
22int main(int /*argc*/, char** /*argv*/) {
23 /* Obtain mesh */
24 auto mesh_factory = std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
25 std::filesystem::path here = __FILE__;
26 auto mesh_file = (here.parent_path() / "/meshes/earth.msh").string();
27 const lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
28 std::shared_ptr<const lf::mesh::Mesh> mesh_p = reader.mesh();
29 /* Finite Element Space */
30 std::shared_ptr<lf::uscalfe::UniformScalarFESpace<double>> fe_space =
31 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);
32 /* Dofhandler */
33 const lf::assemble::DofHandler& dofh{fe_space->LocGlobMap()};
34 const lf::uscalfe::size_type N_dofs(dofh.NumDofs());
35
36 /* Initial Population density */
37 Eigen::VectorXd u0(N_dofs);
38 u0.setZero();
39 u0(277) = 80;
40
41 std::cout << "N_dofs :" << N_dofs << std::endl;
42
43 /* Diffusion Coefficient
44 * APPROACH 1: constant diffusion coefficient.
45 *
46 * auto c = [] (Eigen::Vector2d x) -> double { return 86.0;};
47 *
48 * APPROACH 2: Take topography into account: Mountain chains impede the
49 * dispersal of the population.
50 */
51 Eigen::Vector2d Himalaya(-280, 29);
52 Eigen::Vector2d Alps(-350, 46);
53 Eigen::Vector2d Karakoram(-285, 36);
54 Eigen::Vector2d Hindukush(-288, 36);
55 Eigen::Vector2d RockyMountains(-109, 44);
56 Eigen::Vector2d Ural(-300, 60);
57 Eigen::Vector2d Andes(-66, -21);
58
59 auto c = [&Himalaya, &Alps, &Karakoram, &Hindukush, &RockyMountains, &Ural,
60 &Andes](const Eigen::Vector2d& x) -> double {
61 double diffCoeff = 86.0;
62
63 if ((x - Himalaya).norm() <= 3) {
64 diffCoeff = 5.0;
65 std::cout << "Take Himalaya into account." << std::endl;
66 }
67
68 if ((x - Karakoram).norm() <= 3) {
69 diffCoeff = 6.0;
70 std::cout << "Take Karakoram into account." << std::endl;
71 }
72
73 if ((x - Hindukush).norm() <= 2) {
74 diffCoeff = 6.0;
75 std::cout << "Take Hindukush into account." << std::endl;
76 }
77
78 if ((x - Alps).norm() <= 3) {
79 diffCoeff = 5.0;
80 std::cout << "Take Alps into account." << std::endl;
81 }
82
83 if ((x - Ural).norm() <= 2) {
84 diffCoeff = 8.0;
85 std::cout << "Take Ural into account." << std::endl;
86 }
87
88 if ((x - RockyMountains).norm() <= 2) {
89 diffCoeff = 10.0;
90 std::cout << "Take Himalaya into account." << std::endl;
91 }
92
93 if ((x - Andes).norm() <= 3) {
94 diffCoeff = 9.0;
95 std::cout << "Take Himalaya into account." << std::endl;
96 }
97
98 return diffCoeff;
99 };
100
101 /* Growth Factor */
102 double lambda = 1.94;
103
104 /* Non Local Boundary Conditions */
105
106 /* This predicate returns true for nodes on the boundary */
107 auto bd_flags{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 2)};
108 auto boundary_nodes = [&bd_flags, &dofh](unsigned int idx) -> bool {
109 return bd_flags(dofh.Entity(idx));
110 };
111
112 /* This predicate returns true for edges on the boundary */
113 auto bd_flags_edge{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 1)};
114 auto edge_pred = [&bd_flags_edge](const lf::mesh::Entity& edge) -> bool {
115 return bd_flags_edge(edge);
116 };
117
118 /* Discern local boundary nodes */
119 std::vector<Eigen::MatrixXd> locBoundary;
120 for (int i = 0; i < N_dofs; ++i) {
121 auto coords = lf::geometry::Corners(*(dofh.Entity(i).Geometry()));
122 if (boundary_nodes(i)) {
123 locBoundary.push_back(coords);
124 }
125 }
126
127 /* This h is to be used as a function handle for the gain term.
128 * Use it in the MassEdgeMatrix Provider.
129 */
130 auto h = [fe_space, mesh_p, edge_pred](const Eigen::Vector2d& x) -> double {
131 double res = 0.0;
132 /* Decaying function handle depending on x and y. */
133 auto g = [&x](const Eigen::Vector2d& y) -> double {
134 double tmp_res = 0.0;
135 if (5 <= (x - y).norm() && (x - y).norm() <= 30) {
136 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
137 }
138 return tmp_res;
139 };
140
141 const auto* fe = fe_space->ShapeFunctionLayout(lf::base::RefEl::kSegment());
142 res = localQuadFunction(
143 *mesh_p,
146 2 * fe->Degree())},
148 g, 1, edge_pred);
149
150 std::cout << "res" << res << std::endl;
151 return res;
152 };
153
154 /* In what follows, the loss term is assembled. */
155 Eigen::MatrixXd L(N_dofs, N_dofs);
156 for (int j = 0; j < N_dofs; j++) {
157 if (boundary_nodes(j)) {
158 auto L_j = [fe_space, &dofh, N_dofs, edge_pred,
159 j](const Eigen::Vector2d& x) -> double {
160 auto g_j = [&x](const Eigen::Vector2d& y) -> double {
161 double tmp_res = 0.0;
162 if (5 <= (x - y).norm() && (x - y).norm() <= 30) {
163 tmp_res = (1.0 / (1.0 + (x - y).squaredNorm()));
164 }
165 return tmp_res;
166 };
167
168 Eigen::VectorXd L1(N_dofs);
169 L1.setZero();
170
172 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_g),
173 decltype(edge_pred)>
174 edgeVec_y(fe_space, mf_g, edge_pred);
175 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_y, L1);
176
177 return L1(j);
178 };
179
180 Eigen::VectorXd L2(N_dofs);
181 L2.setZero();
182
184 lf::uscalfe::ScalarLoadEdgeVectorProvider<double, decltype(mf_L),
185 decltype(edge_pred)>
186 edgeVec_x(fe_space, mf_L, edge_pred);
187 lf::assemble::AssembleVectorLocally(1, dofh, edgeVec_x, L2);
188
189 L.col(j) = L2;
190 }
191
192 else {
193 L.col(j) = Eigen::VectorXd::Zero(N_dofs);
194 }
195 }
196
197 /* Strang Splitting Method
198 * SDIRK-2 evolution of linear parabolic term
199 * Exact Evolution for nonlinear reaction term
200 */
201
202 /* Total number of timesteps */
203 unsigned int m = 121;
204 double T = 1.; // the timestepsize tau will equal T/m = 0.0083
205
206 /* First we assemble the carrying capacity maps */
207 Eigen::MatrixXd car_cap(N_dofs, 22);
208
209 Eigen::VectorXd K(N_dofs);
210 K.setZero();
211 K = 0.2 * Eigen::VectorXd::Ones(N_dofs);
212 auto c_cap = [](const Eigen::Vector2d & /*x*/) -> double { return 80.0; };
213 auto h_cap = [](const Eigen::Vector2d & /*x*/) -> double { return 1.0; };
214 Eigen::MatrixXd L_cap(N_dofs, N_dofs);
215 L_cap = Eigen::MatrixXd::Zero(N_dofs, N_dofs);
216 /* NOTE: c = 80, lambda = 0, L = 0, h = 1, K does not matter*/
217 StrangSplit DiffusionCapacity(fe_space, T, m, 0.0, c_cap, h_cap, L_cap);
218
219 Eigen::VectorXd k0(N_dofs);
220
221 /* t = 200 kya - 150 kya */
222 k0.setZero();
223 k0(277) = 80;
224 car_cap.col(0) = DiffusionCapacity.Evolution(K, k0);
225 car_cap.col(1) = DiffusionCapacity.Evolution(K, car_cap.col(0));
226 car_cap.col(2) = DiffusionCapacity.Evolution(K, car_cap.col(1));
227 car_cap.col(2) *= 10;
228
229 std::cout << "Carrying Capacity 200kya - 150kya!" << std::endl;
230
231 /* t = 150 kya - 130 kya */
232 std::cout << "Carrying Capacity 150kya - 130kya!" << std::endl;
233
234 /* t = 130 kya - 100 kya */
235 k0.setZero();
236 k0(334) = 80;
237 car_cap.col(3) = DiffusionCapacity.Evolution(K, k0);
238 car_cap.col(4) = DiffusionCapacity.Evolution(K, car_cap.col(3));
239 car_cap.col(5) = DiffusionCapacity.Evolution(K, car_cap.col(4));
240 car_cap.col(5) *= 10;
241
242 std::cout << "Carrying Capacity 130kya - 100kya!" << std::endl;
243
244 /* t = 100 kya - 70 kya */
245 std::cout << "Carrying Capacity 100kya - 70kya!" << std::endl;
246
247 /* t = 70 kya - 65 kya */
248 k0.setZero();
249 k0(253) = 80;
250 car_cap.col(6) = DiffusionCapacity.Evolution(K, k0);
251 car_cap.col(7) = DiffusionCapacity.Evolution(K, car_cap.col(6));
252 car_cap.col(7) *= 15;
253
254 std::cout << "Carrying Capacity 70kya - 65kya!" << std::endl;
255
256 /* t = 65 kya - 50 kya */
257 k0.setZero();
258 k0(1775) = 80;
259 k0(222) = 80;
260 k0(181) = 80;
261 car_cap.col(8) = DiffusionCapacity.Evolution(K, k0);
262 car_cap.col(9) = DiffusionCapacity.Evolution(K, car_cap.col(8));
263 car_cap.col(9) *= 8;
264
265 std::cout << "Carrying Capacity 65kya - 50kya!" << std::endl;
266
267 /* t = 50 kya - 45 kya */
268 k0.setZero();
269 k0(400) = 200;
270 car_cap.col(10) = DiffusionCapacity.Evolution(K, k0);
271 car_cap.col(10) *= 2;
272
273 std::cout << "Carrying Capacity 50kya - 45kya!" << std::endl;
274
275 /* t = 45 kya - 25 kya */
276 k0.setZero();
277 k0(492) = 80;
278 k0(114) = 80;
279 car_cap.col(11) = DiffusionCapacity.Evolution(K, k0);
280 car_cap.col(12) = DiffusionCapacity.Evolution(K, car_cap.col(11));
281 car_cap.col(12) *= 8;
282
283 std::cout << "Carrying Capacity 45kya - 25kya!" << std::endl;
284
285 /* t = 25 kya - 15 kya */
286 k0.setZero();
287 k0(1342) = 80;
288 car_cap.col(13) = DiffusionCapacity.Evolution(K, k0);
289 car_cap.col(14) = DiffusionCapacity.Evolution(K, car_cap.col(13));
290 car_cap.col(14) *= 25;
291
292 std::cout << "Carrying Capacity 25kya - 15kya!" << std::endl;
293
294 /* t = 15 kya - 0 kya */
295 k0.setZero();
296 k0(1257) = 80;
297 k0(1100) = 80;
298 car_cap.col(15) = DiffusionCapacity.Evolution(K, k0);
299 car_cap.col(15) *= 4;
300
301 std::cout << "Carrying Capacity 15kya - 0kya!" << std::endl;
302 std::cout << "Capacity maps are assembled!" << std::endl;
303
304 /* Now we may compute the solution */
305 StrangSplit StrangSplitter(fe_space, T, m, lambda, c, h, L);
306
307 Eigen::MatrixXd sol(N_dofs, 40);
308 Eigen::VectorXd cap(N_dofs);
309 cap.setZero();
310 /* t = 200 kya - 150 kya */
311 cap = car_cap.col(2);
312 sol.col(0) = StrangSplitter.Evolution(cap, u0);
313
314 std::cout << "Solution 200kya - 150kya!" << std::endl;
315
316 /* t = 150 kya - 130 kya */
317 sol.col(1) = StrangSplitter.Evolution(cap, sol.col(0));
318
319 std::cout << "Solution 150kya - 130kya!" << std::endl;
320
321 /* t = 130 kya - 100 kya */
322 cap = cap + car_cap.col(5);
323 sol.col(2) = StrangSplitter.Evolution(cap, sol.col(1));
324
325 std::cout << "Solution 130kya - 100kya!" << std::endl;
326
327 /* t = 100 kya - 70 kya */
328 sol.col(3) = StrangSplitter.Evolution(cap, sol.col(2));
329
330 std::cout << "Solution 100kya - 70kya!" << std::endl;
331
332 /* t = 70 kya - 65 kya */
333 cap = cap + car_cap.col(7);
334 sol.col(4) = StrangSplitter.Evolution(cap, sol.col(3));
335 sol.col(5) = StrangSplitter.Evolution(cap, sol.col(4));
336
337 std::cout << "Solution 70kya - 65kya!" << std::endl;
338
339 /* t = 65 kya - 50 kya */
340 cap = cap + car_cap.col(9);
341 sol.col(6) = StrangSplitter.Evolution(cap, sol.col(5));
342 sol.col(7) = StrangSplitter.Evolution(cap, sol.col(6));
343
344 std::cout << "Solution 65kya - 50kya!" << std::endl;
345
346 /* t = 50 kya - 45 kya */
347 cap = cap + car_cap.col(10);
348 sol.col(8) = StrangSplitter.Evolution(cap, sol.col(7));
349
350 std::cout << "Solution 50kya - 45kya!" << std::endl;
351
352 /* t = 45 kya - 25 kya */
353 cap = cap + car_cap.col(12);
354 sol.col(9) = StrangSplitter.Evolution(cap, sol.col(8));
355 sol.col(10) = StrangSplitter.Evolution(cap, sol.col(9));
356 sol.col(11) = StrangSplitter.Evolution(cap, sol.col(10));
357 sol.col(12) = StrangSplitter.Evolution(cap, sol.col(11));
358
359 std::cout << "Solution 45kya - 25kya!" << std::endl;
360
361 /* t = 25 kya - 15 kya */
362 cap = cap + car_cap.col(14);
363 sol.col(13) = StrangSplitter.Evolution(cap, sol.col(12));
364 sol.col(14) = StrangSplitter.Evolution(cap, sol.col(13));
365
366 std::cout << "Solution 25kya - 15kya!" << std::endl;
367
368 /* t = 15 kya - 0 kya */
369 cap = cap + car_cap.col(15);
370 sol.col(15) = StrangSplitter.Evolution(cap, sol.col(14));
371 sol.col(16) = StrangSplitter.Evolution(cap, sol.col(15));
372 sol.col(17) = StrangSplitter.Evolution(cap, sol.col(16));
373 sol.col(18) = StrangSplitter.Evolution(cap, sol.col(17));
374 sol.col(19) = StrangSplitter.Evolution(cap, sol.col(18));
375
376 std::cout << "Solution 15kya - 0kya!" << std::endl;
377
378 /* Use VTK-Writer for Visualization of solution */
379 lf::io::VtkWriter vtk_writer_cap(mesh_p, "cap.vtk");
380 auto nodal_data_cap =
381 lf::mesh::utils::make_CodimMeshDataSet<double>(mesh_p, 2);
382 for (int global_idx = 0; global_idx < N_dofs; global_idx++) {
383 nodal_data_cap->operator()(dofh.Entity(global_idx)) = cap[global_idx];
384 }
385 vtk_writer_cap.WritePointData("cap", *nodal_data_cap);
386
387 for (int k = 1; k < 21; k++) {
388 std::stringstream filename;
389 filename << "sol" << k << ".vtk";
390
391 lf::io::VtkWriter vtk_writer(mesh_p, filename.str());
392 auto nodal_data = lf::mesh::utils::make_CodimMeshDataSet<double>(mesh_p, 2);
393 for (int global_idx = 0; global_idx < N_dofs; global_idx++) {
394 nodal_data->operator()(dofh.Entity(global_idx)) =
395 sol.col(k - 1)[global_idx];
396 }
397
398 vtk_writer.WritePointData("sol", *nodal_data);
399 }
400
401 for (int k = 1; k < 17; k++) {
402 std::stringstream filename_cap;
403 filename_cap << "cap" << k << ".vtk";
404
405 lf::io::VtkWriter vtk_writer_caps(mesh_p, filename_cap.str());
406 auto nodal_data_caps =
407 lf::mesh::utils::make_CodimMeshDataSet<double>(mesh_p, 2);
408 for (int global_idx = 0; global_idx < N_dofs; global_idx++) {
409 nodal_data_caps->operator()(dofh.Entity(global_idx)) =
410 car_cap.col(k - 1)[global_idx];
411 }
412
413 vtk_writer_caps.WritePointData("caps", *nodal_data_caps);
414 }
415
416 return 0;
417}
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
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
MeshFunction wrapper for a simple function of physical coordinates.
auto squaredNorm(const A &a)
Pointwise squared norm of another mesh function.
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)
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