Compare commits

...

377 Commits
v0.1 ... main

Author SHA1 Message Date
Max Lübke
9c4aeee410 Merge branch 'ml/bump-cmake-version' into 'main'
relax Eigen3 version constraint

See merge request naaice/tug!45
2025-10-24 08:49:19 +02:00
Max Lübke
d5bfdf9724 build(deps): relax Eigen3 version constraint 2025-10-24 08:48:56 +02:00
Max Lübke
1ad7ea0237 Merge branch 'ml/bump-cmake-version' into 'main'
Bump dependency versions

See merge request naaice/tug!44
2025-10-24 08:43:04 +02:00
Max Lübke
1a51dd5a1e build(deps): update Eigen3 dependency version range 2025-10-24 08:42:14 +02:00
Max Lübke
605a31cc7c build(deps): update minimum CMake version to 3.20 2025-10-24 08:41:56 +02:00
Max Lübke
a562281187 Merge branch 'ml/fix-citation' into 'main'
docs(citation): update citation metadata details

See merge request naaice/tug!43
2025-10-16 11:21:55 +02:00
Max Lübke
256d6325d6 docs(citation): update citation metadata details 2025-10-16 11:21:00 +02:00
Max Lübke
3e97e530bc Merge branch 'diagonal' into 'main'
Replace eigen sparse matrices with the diagonal optimization

See merge request naaice/tug!39
2025-10-16 09:22:25 +02:00
Max Lübke
04b42c4b89 Merge branch 'citation-add-author' into 'main'
add philipp as author

See merge request naaice/tug!42
2025-10-16 09:21:05 +02:00
Max Lübke
d2f028122e docs: update project title for citation 2025-10-16 09:20:24 +02:00
Hannes Martin Signer
19cc372b6a Update file CITATION.cff 2025-10-15 17:34:33 +02:00
Hannes Signer
135830e030 add phillips orcid 2025-10-15 16:32:27 +02:00
Hannes Martin Signer
520810bc3e Merge branch 'citation' into 'main'
add citation file

See merge request naaice/tug!41
2025-10-15 13:09:05 +02:00
Hannes Signer
b66feed2d2 add citation file 2025-10-15 13:08:00 +02:00
Max Lübke
b6ce5a32f4 Merge branch 'ml/mirror-to-github' into 'main'
Ml/mirror to github

See merge request naaice/tug!40
2025-10-15 10:25:37 +02:00
Max Lübke
4866977e72 ci(pipeline): reorder CI stages 2025-10-15 10:24:59 +02:00
Max Lübke
bdc4f156de ci(mirror): add automatic repository sync to GitHub 2025-10-15 10:23:52 +02:00
Hannes Signer
5b144aea3a add solver 2025-10-14 19:12:02 +02:00
Hannes Signer
3e270ee01c add solver 2025-10-14 19:10:03 +02:00
Hannes Signer
a20d5d53e6 fix test case 2025-10-14 19:08:16 +02:00
Hannes Signer
3701dea34e fix test case 2025-10-14 19:05:25 +02:00
Hannes Signer
30d2099d8a add solver to template 2025-10-14 19:01:32 +02:00
Hannes Signer
47ad909a9c Merge branch 'diagonal' of git.gfz-potsdam.de:naaice/tug into diagonal 2025-10-14 18:54:44 +02:00
Hannes Signer
91ae02cbf1 fix error for missing matching function 2025-10-14 18:54:18 +02:00
Hannes Martin Signer
06b890fe81 add EigenLUSolver test case 2025-10-14 18:49:02 +02:00
Hannes Signer
c8d1b08e28 add diagonal optimization approach 2025-10-14 18:33:20 +02:00
Max Lübke
9ca0735654 Merge branch 'main' of git.gfz-potsdam.de:naaice/tug 2025-02-07 08:19:41 +01:00
max
e1a135f8e2 Merge pull request 'BREAKING CHANGE: Refactor simulation grid' (#1) from ml/refactor-simulation-grid into main 2025-02-05 15:44:38 +01:00
Max Lübke
13226e8668 refactor: simplify FTCS_2D by removing unused code 2025-02-05 12:53:15 +01:00
Max Lübke
56fc8185d5 Merge branch 'ml/refactor-simulation-grid' into 'main'
BREAKING CHANGE: Rename Simulation to Diffusion

See merge request naaice/tug!38
2025-01-31 16:13:27 +01:00
Max Luebke
a312abfe05 ci: Fix pages pipeline 2025-01-31 16:12:45 +01:00
Max Luebke
8fcc77bc60 doc: Add documentation for new Diffusion constructors and functions 2025-01-31 15:58:13 +01:00
Max Luebke
3612dcf034 BREAKING CHANGE: Rework Grid definition
Now the API does not rely on `Grid` structure but lay a wrapper around
an existing memory region, which defines for example a diffusion grid.

All simulation steps are done in place.

The user has to make sure the memory existing throughout the lifetime of
a simulation grid.
2025-01-31 15:46:06 +01:00
Max Luebke
5c68f8b6b2 refactor: Change enums to scoped enums and simplify output option validation 2024-12-11 19:56:54 +01:00
Max Luebke
477d943bf0 refactor: Introduce BaseSimulationGrid template class and update domain handling 2024-12-11 19:53:45 +01:00
Max Lübke
d3843fb2a3 refactor: Update testDiffusion.cpp and Diffusion.hpp
Refactor testDiffusion.cpp and Diffusion.hpp to improve code readability and maintainability. Remove unnecessary exception throwing and replace with assert statements for invalid arguments.
2024-12-10 10:42:53 +01:00
Max Lübke
13f6556f54 refactor: Use assert instead of custom throw for invalid argument in TugUtils.hpp 2024-12-10 08:59:16 +01:00
Max Lübke
a796acbc1d BREAKING CHANGE: Rename Simulation to Diffusion
chore: Cleanup of applications
2024-12-10 08:55:50 +01:00
Max Lübke
432621f227 Merge branch '14-documentation-update' into 'main'
Resolve "Documentation Update"

Closes #14

See merge request naaice/tug!36
2024-12-10 08:21:12 +01:00
Max Lübke
636fcfaec3 feat: Update CMake configuration and add README documentation 2024-12-10 08:20:26 +01:00
Max Lübke
bed888d1fc Merge branch 'ml/port-to-gtest' into 'main'
feat: Integrate GoogleTest for unit testing and update CI configuration

See merge request naaice/tug!35
2024-12-06 11:22:56 +01:00
Max Lübke
6981373deb feat: Integrate GoogleTest for unit testing and update CI configuration 2024-12-06 09:52:45 +01:00
Marco De Lucia
a986242852 Merge branch 'fixreadme' into 'main'
fix: links/gitlab group in README

See merge request naaice/tug!34
2024-11-27 11:26:42 +01:00
Marco De Lucia
8d83eeef29 fixed links/gitlab group in README 2024-11-27 11:23:21 +01:00
Max Lübke
ac693caea9 Merge branch 'ml/row-major-mat' into 'main'
feat: Add support for setting concentrations from a pointer

See merge request naaice/tug!32
2024-06-10 16:05:09 +02:00
Max Luebke
74b46f111b perf: Minimize copy operations 2024-06-10 16:04:13 +02:00
Max Luebke
c01d8e8607 refactor: Use Row-major matrix internally 2024-06-10 16:01:47 +02:00
Max Lübke
00b0583504 Merge branch 'fix-Simulation-setNumberThreads-parameter' into 'main'
[Fix] Fix `setNumberThreads()`-method parameter in Simulation.hpp.

See merge request naaice/tug!31
2024-06-06 11:53:24 +02:00
DannyPuhan
f7dbf3abaf [Fix] Fix setNumberThreads()-method parameter in Simulation.hpp. 2024-06-06 08:59:02 +02:00
Max Luebke
e64e8dfd5e feat: Add support for setting concentrations from a pointer
refactor: Use Row-major matrix internally
2024-06-03 23:48:54 +02:00
Max Lübke
449647010a Merge branch 'get-set-inner' into 'main'
Add methods to get and set inner constant boundary conditions

See merge request naaice/tug!30
2024-04-04 14:45:31 +02:00
Max Luebke
5193f36e1f Add methods to get and set inner constant boundary conditions 2024-04-04 12:45:04 +00:00
Max Lübke
9d2c4b1485 Merge branch 'extend-testcase' into 'main'
Add check for concentration at grid position (2, 2)

See merge request naaice/tug!29
2024-04-04 14:36:46 +02:00
Max Luebke
f86836f637 Add check for concentration at grid position (2, 2) 2024-04-04 14:36:23 +02:00
Max Lübke
b13337279f Merge branch 'inner_boundaries' into 'main'
Add support for inner boundaries with 2D-ADI

See merge request naaice/tug!28
2024-04-04 14:34:09 +02:00
Max Luebke
bd3cdde0fb Add constant inner cell concentration with test cases 2024-04-04 14:33:19 +02:00
Max Luebke
7ae35dccf9 Add functions to retrieve inner boundary rows and columns 2024-04-04 14:18:12 +02:00
Max Luebke
cb0c21eab9 Add inner boundary conditions for 1D and 2D grids 2024-04-04 13:48:10 +02:00
Max Lübke
2dc959b993 Merge branch 'poet' into 'main'
Add changes required for POET into main

See merge request naaice/tug!27
2024-04-04 13:09:28 +02:00
Max Luebke
332f419faf Add doctest library to ci.Dockerfile 2024-04-04 13:08:04 +02:00
Max Luebke
b104fdcf52 Remove doctest-src subproject 2024-04-02 10:22:26 +00:00
Max Luebke
f71bf2371f Update doctest library and fix target link 2024-04-02 10:21:00 +00:00
Max Lübke
3ffa0ef624 Update testGrid.cpp with correct dimensions for 2D Grid64 2024-03-27 20:37:42 +00:00
Max Lübke
8c0687a6cd Update grid dimensions validation to handle 1D grids 2024-03-27 20:11:45 +00:00
Max Lübke
1679dc84d2 Add serialization and deserialization methods to Boundary class 2024-03-27 15:35:16 +00:00
Max Lübke
eb14c53314 Merge branch 'fix_neg_values' into 'main'
Fix possible NaN @ calcAlphaIntercell

See merge request naaice/tug!26
2024-03-14 09:10:52 +01:00
Max Luebke
4328ef3436 Fix possible NaN @ calcAlphaIntercell 2024-03-14 09:08:33 +01:00
Max Lübke
4867261f9d Merge branch 'fix-alpha' into 'main'
Fix alpha intercell calculation

See merge request naaice/tug!25
2024-03-05 10:26:43 +01:00
Max Lübke
2f737ce09e Fix alpha intercell calculation 2024-03-05 10:26:15 +01:00
Max Lübke
48000710c7 Merge branch 'fix-boundaries' into 'main'
Remove check for negative concentration in Boundary.hpp

See merge request naaice/tug!24
2024-03-04 21:29:21 +01:00
Max Lübke
69690c0afa Remove check for negative concentration in Boundary.hpp 2024-03-04 21:28:30 +01:00
Max Lübke
fbb0c08024 Merge branch 'fix-btcs' into 'main'
fix: remove factor 2 in 'middle' coefficient of explicit part (closed)

See merge request naaice/tug!23
2023-12-19 16:08:48 +01:00
Max Lübke
61d6cfc5cb fix: remove factor 2 in 'middle' coefficient of explicit part (closed)
test: add test case with homogeneous field and same values as const
boundary condition
2023-12-19 16:04:57 +01:00
Max Lübke
f651c1d158 Merge branch 'fix-dt' into 'main'
fix: use maximum alpha for CFL condition

See merge request naaice/tug!22
2023-12-04 09:13:55 +01:00
Max Lübke
db1a2b2e5c fix: use maximum alpha for CFL condition 2023-12-04 09:12:10 +01:00
Max Lübke
8824dc1fd9 Merge branch 'fix-FTCS' into 'main'
fix: Typos in FTCS

See merge request naaice/tug!21
2023-11-30 09:16:15 +01:00
Max Lübke
cdfc42ac9c fix: Typos in FTCS
In the calculation of alpha intercell values, the concentration of alpha
and its neighboring concentrations were utilized, as opposed to
employing neighboring alpha concentrations.

For evaluating the left/right boundary conditions, there was an error in
indexing - specifically, column indexing was erroneously employed
instead of the intended row indexing.
2023-11-30 08:58:46 +01:00
Max Lübke
e2bf3a7662 chore: update .gitignore 2023-11-30 08:54:14 +01:00
Max Lübke
ee77b5f7f3 Merge branch 'theory' into 'main'
docs: add theory part from HP report

See merge request naaice/tug!20
2023-11-01 10:41:19 +01:00
Max Lübke
141028548b docs: add theory part from HP report 2023-11-01 10:40:38 +01:00
Max Lübke
32d6a4e3ec Merge branch 'contributors' into 'main'
doc: adding contributors

See merge request naaice/tug!19
2023-11-01 09:05:26 +01:00
Max Lübke
97b43e1a16 doc: adding contributors 2023-11-01 09:04:55 +01:00
Max Lübke
39541a2054 Merge branch 'naaice' into 'main'
BREAKING CHANGE: tug as header-only library

See merge request naaice/tug!18
2023-10-27 13:18:03 +02:00
Max Lübke
5a39f5377e doc: update example pages 2023-10-19 13:09:22 +02:00
Max Lübke
77914ea69f fix: include optional output of csv during thomas algorithm
fix: marco's benchmark
2023-10-19 12:23:23 +02:00
Max Lübke
8456f2212d BREAKING CHANGE: tug as header-only library
build: installation of library is now possible
2023-10-19 12:20:39 +02:00
Marco De Lucia
0471f3d8f9 fix: readded "using namespace tug;" in naaice/BTCS_2D_NAAICE.cpp 2023-10-16 17:34:52 +02:00
Marco De Lucia
8cfb61587d feat: add naaice/NAAICE_dble_vs_float.cpp 2023-10-16 17:31:43 +02:00
Marco De Lucia
9a3fc67885 Fix: Eigen::MatrixX<T> instead of Eigen::MatrixXd in Grid.hpp 2023-10-16 12:11:52 +02:00
Max Lübke
5196c36ec5 fix: reintroduce tug namespace 2023-09-15 11:38:13 +02:00
Max Lübke
ba627b6624 feat: rewrite library as template library 2023-09-15 11:38:08 +02:00
Max Lübke
46f9cef3a9 Merge branch 'main' into naaice 2023-09-15 08:15:19 +02:00
Max Lübke
00cafb70dc BREAKING CHANGE: reworked API
BREAKING CHANGE: added heterogeneous diffusion

BREAKING CHANGE: added FTCS scheme

See merge request naaice/tug!16
2023-09-15 07:49:53 +02:00
Max Lübke
5099fd23a9 fix: add namespaces to example executables 2023-09-15 07:48:21 +02:00
Max Lübke
8e5c1ad035 refactor: implement coeff boundary functions as template constexpr 2023-09-15 07:35:05 +02:00
Max Lübke
819db24e18 Merge branch 'main' into 'hannes-philipp'
# Conflicts:
#   scripts/Adi2D_Reference.R
#   scripts/HetDiff.R
2023-09-14 16:27:39 +00:00
Max Lübke
6c1ccb90fd refactor: implement coeff boundary functions as template constexpr 2023-09-14 16:21:45 +02:00
Max Lübke
ef1ccd4c14 refactor: various changes to BTCS functions 2023-09-14 16:21:20 +02:00
Max Lübke
a0d835e243 refactor: inline Coeff Functions 2023-09-14 16:21:20 +02:00
Max Lübke
0eba63f875 refactor: core adjustments to Simulation class
perf: const qualification of local vairables
2023-09-14 16:21:20 +02:00
Max Lübke
edaad7cc04 refactor: core adjustment to Boundary class
perf: const qualification of local variables
2023-09-14 16:21:20 +02:00
Max Lübke
f0d5220a48 refactor: adjust const qualifications for Grid class 2023-09-14 14:54:34 +02:00
Max Lübke
a21023ec9d feat: make OpenMP parallelization optional 2023-09-14 13:31:31 +02:00
Max Lübke
587bb5a622 ci: remove 'modernize' from clang-tidy list 2023-09-14 13:12:38 +02:00
Max Lübke
c5979cd6f4 ci: add clang openmp includes 2023-09-14 12:40:06 +02:00
Max Lübke
4b4c439c68 doc: slight change to instructions
doc: add LICENSE
2023-09-14 12:34:12 +02:00
Max Lübke
da6d004e16 chore: cleanup of repository 2023-09-14 12:22:52 +02:00
Max Lübke
61a4b0ae8a build: make compilation of examples optional
build: set default values of optional options to OFF
2023-09-14 12:16:35 +02:00
Max Lübke
3106c2b8d5 test: add Boundary test case 2023-09-14 12:11:37 +02:00
Max Lübke
8af03777b8 fix: add namespaces to executables 2023-09-14 12:05:14 +02:00
Max Lübke
2096ee5cc3 refactor: remove all 'using namespaces' from library 2023-09-14 12:04:03 +02:00
Max Lübke
2483019b89 fix: remove guards in source files 2023-09-14 11:46:45 +02:00
Max Lübke
ce09f0d8c8 fix: remove all source file includes 2023-09-14 11:34:41 +02:00
Max Lübke
b9c4474f5a fix: add header guards to includes 2023-09-14 11:02:40 +02:00
Max Lübke
d7608a7330 Revert "Change TugUtils.hpp to TugUtils.cpp"
This reverts commit 81774e72c1b7b5332ce3d3a57397ee4a4b521d4a.
2023-09-14 10:27:07 +02:00
Max Lübke
0d34752837 refactor: format all source files to LLVM standard 2023-09-14 10:27:05 +02:00
philippun
81774e72c1 Change TugUtils.hpp to TugUtils.cpp 2023-09-13 10:56:35 +02:00
philippun
443f8b6cd1 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-09-13 10:55:12 +02:00
philippun
684fcd217f changed default numbeer of cores to max-1 2023-09-13 10:55:05 +02:00
Hannes Signer
9d663d8140 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-09-11 11:10:44 +02:00
Hannes Signer
eb42377f30 change experiment files 2023-09-11 11:10:27 +02:00
philippun
fc4689461e Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-09-08 15:30:33 +02:00
philippun
d2e3ef23de improved commentary, refactored TugUtils into .cpp, and added CRNI example 2023-09-08 15:30:27 +02:00
Max Lübke
5490216bc0 feat: print sums of input and output field 2023-09-07 11:10:56 +02:00
Marco De Lucia
42ad07c252 Fix: some updates to naaice/README.md 2023-09-07 09:24:03 +02:00
Max Lübke
bf4444fc84 add comments and rename function 2023-09-06 14:39:27 +02:00
Max Lübke
40710a0b39 Usage instead of setup 2023-09-06 14:25:02 +02:00
Max Lübke
f8bdfe39ea Use Markdown over Org-Mode 2023-09-06 14:24:05 +02:00
Max Lübke
72107c944d Add readme 2023-09-06 14:21:22 +02:00
Max Lübke
fab0f35ed0 Add benchmark 2023-09-06 13:20:40 +02:00
Max Lübke
7d05320f24 apply format changes (LLVM) 2023-09-06 09:21:04 +02:00
Max Lübke
6e388f3d99 write input of thomas algortithm to file 2023-09-06 09:21:04 +02:00
Max Lübke
55509c1934 Merge branch 'hotfix-domain-size' into 'hannes-philipp'
fix!: domain size can also be real number

See merge request naaice/tug!15
2023-09-06 09:18:51 +02:00
Max Lübke
e8a783f00c fix: domain size can also be real number 2023-09-06 09:15:08 +02:00
Hannes Signer
8fcc8812e7 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-29 10:42:23 +02:00
philippun
4cb51f4241 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-29 10:40:11 +02:00
philippun
d24b65db63 updated documentation 2023-08-29 10:39:59 +02:00
Hannes Signer
a0ab764870 add comments for setNumberThreads 2023-08-29 10:38:51 +02:00
Philipp Ungrund
12bc93a3ed Delete .readthedocs.yaml; not needed anymore after deploying pages on gitlab 2023-08-29 10:30:50 +02:00
Philipp Ungrund
dada94b1ae Merge branch 'sphinx-ci' into 'hannes-philipp'
ci: Enable sphinx-generated documentation deployment on main branch

See merge request naaice/tug!14
2023-08-29 10:29:45 +02:00
Max Lübke
6a3c2b014c ci: Enable sphinx-generated documentation deployment on main branch 2023-08-29 10:29:45 +02:00
Marco De Lucia
cc37891bf6 fix: removed spourios refs to easy_profile in CMakeLists 2023-08-27 14:00:06 +02:00
Marco De Lucia
2d50f32e37 fix: reverted local profiling_openmp.cpp to commit 1dbee6d8, small updates in index.rst 2023-08-27 13:56:44 +02:00
Marco De Lucia
6363585a00 Revert "Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp"
This reverts commit a16b67b15e02ddc6c98b655c9d85a4820e52438b
2023-08-27 13:41:15 +02:00
Marco De Lucia
a16b67b15e Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-27 13:20:28 +02:00
Marco De Lucia
eb2a9774a5 MDL updating docs and docs_sphinx 2023-08-27 13:19:32 +02:00
philippun
ad67980baa small performance optimization, added TODOs with further optimization idea and another TODO 2023-08-24 22:46:14 +02:00
Marco De Lucia
2294922a3e Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-24 17:29:58 +02:00
philippun
49209412e2 little optimizations and added output visualization file to utils 2023-08-24 16:00:32 +02:00
Marco De Lucia
d92ccc05aa Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-24 12:22:14 +02:00
Marco De Lucia
3db0e73efe mdl: profiling_openmp with easy_profile 2023-08-24 12:22:02 +02:00
philippun
3344835b20 optimizations 2023-08-24 11:37:31 +02:00
Hannes Signer
1dbee6d8d9 Adjustment to set the threads via command line 2023-08-23 18:35:26 +02:00
Hannes Signer
6b8368d9f7 add setNumberThreads method 2023-08-23 18:34:48 +02:00
philippun
32b05a8a87 added Thomas Solver with option to choose solver and cleaned up the repository 2023-08-23 12:24:35 +02:00
philippun
27829a1463 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-22 13:32:13 +02:00
philippun
9d695b3aa8 added example for meeting 2023-08-22 13:32:02 +02:00
Hannes Signer
af056b4d82 delete openmp curly bracket 2023-08-21 11:05:37 +02:00
Hannes Signer
c0e46bd82f add activity diagrams 2023-08-18 14:30:18 +02:00
Hannes Signer
6814b8f434 images for sphinx 2023-08-18 12:13:55 +02:00
Hannes Signer
996149785d add class and activity diagram 2023-08-18 12:09:55 +02:00
Hannes Signer
7266588573 add tug logo 2023-08-18 11:39:40 +02:00
Hannes Signer
fc1a94bdf2 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-17 12:11:23 +02:00
Hannes Signer
feef0057e1 update visulization 2023-08-17 12:10:39 +02:00
Hannes Martin Signer
194a53c001 Update file .readthedocs.yaml 2023-08-17 11:55:29 +02:00
Hannes Signer
0b5548e8c7 update sphinx 2023-08-17 11:51:37 +02:00
Hannes Signer
567318b8e9 omp functionality and profiling 2023-08-17 10:22:56 +02:00
Hannes Martin Signer
ae7cefc657 Update file requirements.txt 2023-08-15 16:05:44 +02:00
Hannes Signer
ef466594d8 add requirements.txt 2023-08-15 16:02:47 +02:00
Hannes Signer
40fb615d68 update readthedocs file 2023-08-15 16:01:14 +02:00
Hannes Signer
40d03a62f5 update conf.py 2023-08-15 15:51:27 +02:00
Hannes Signer
81a57c42b4 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-15 15:47:42 +02:00
Hannes Signer
da7823f7fc update sphinx 2023-08-15 15:47:33 +02:00
Hannes Martin Signer
3551e9274f Add new file 2023-08-15 12:57:32 +02:00
Hannes Signer
dde7fb4783 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-15 12:55:40 +02:00
Hannes Signer
06f5eb5f2a add sphinx documentation 2023-08-15 12:55:01 +02:00
philippun
fb397897c8 improved commentary 2023-08-15 11:55:26 +02:00
Philipp Ungrund
d563ff583a Update README.md 2023-08-14 11:54:59 +02:00
philippun
525e85cfd7 updated readme 2023-08-14 11:54:07 +02:00
philippun
65493cd207 updated readme 2023-08-14 11:53:41 +02:00
philippun
8cce22601f updated readme 2023-08-14 11:53:05 +02:00
philippun
9acbe90343 updated README.md in src 2023-08-14 11:51:55 +02:00
philippun
5ae5aea48f added comments 2023-08-14 11:42:01 +02:00
philippun
c9c0f02a5a adjusted XTREME csv output 2023-08-11 15:15:08 +02:00
philippun
f1b5138bcc implementation of closed 1D and 2D cases 2023-08-11 12:19:33 +02:00
philippun
4108038a62 removed some random include that was not added intentionally 2023-08-10 23:55:21 +02:00
philippun
c56c5c8ec2 working BTCS 1D and 2D simulation with constant boundary 2023-08-10 23:49:22 +02:00
philippun
fdb5c436ea implemented BTCS 2D constant case 2023-08-10 22:21:15 +02:00
Hannes Signer
435314ba61 Merge branch 'hannes-philipp' into progress-bar 2023-08-10 14:22:58 +02:00
philippun
94ef22902e removed commentary 2023-08-10 14:22:33 +02:00
philippun
2810db0d52 added BTCSv2.cpp 2023-08-10 14:21:00 +02:00
Hannes Signer
0fc6bf4a79 add progress bar 2023-08-10 14:20:48 +02:00
philippun
cc46a893a4 finished BTCS prototype 2023-08-10 11:26:41 +02:00
philippun
e8d322fb75 finished BTCS prototype with closed boundary condition 2023-08-10 11:25:54 +02:00
philippun
2a97bb65c6 fixed a bug in which non-square grids did not work 2023-08-09 17:56:07 +02:00
philippun
e6035c47e7 fixed a bug in which non-square grids did not work 2023-08-09 17:55:48 +02:00
philippun
a510ad1b21 commentary 2023-08-09 17:34:57 +02:00
philippun
3cce7fb357 fixed a bug resulting in too high results 2023-08-09 17:33:58 +02:00
philippun
18abb0fd0e working BTCS prototype 2023-08-09 15:48:33 +02:00
philippun
a7bb5b4d41 working BTCS prototype 2023-08-09 15:48:05 +02:00
Hannes Signer
916987c1bd error in solution vector 2023-08-08 18:42:06 +02:00
philippun
04a2280311 added more BTCS proto stub functions 2023-08-08 17:34:38 +02:00
philippun
ab08d5af1a fixed a wrong index 2023-08-08 12:04:34 +02:00
philippun
498f813d2d BTCS create_coeff_matrix 2023-08-08 12:02:30 +02:00
philippun
20067a6898 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-07 17:24:26 +02:00
Philipp Ungrund
5b01149642 Merge branch 'fix_confusion_inner_outer' into 'hannes-philipp'
fix confusion between CFL/inner and outer iterations

See merge request naaice/tug!11
2023-08-07 17:23:04 +02:00
Marco De Lucia
9d1d8a3980 Merge branch 'fix_confusion_inner_outer_proposal' into 'fix_confusion_inner_outer'
proposal implementation for MDL merge request

See merge request naaice/tug!12
2023-08-07 17:17:08 +02:00
philippun
e1b7038490 proposal implementation for MDL merge request 2023-08-07 16:51:44 +02:00
philippun
e5ca6144d8 added BTCS proto 2023-08-07 14:32:40 +02:00
Marco De Lucia
25f8c3fe6e MDL: distinguish between "inner" (= due to CFL) and "outer" iterations 2023-08-07 12:35:11 +02:00
Hannes Martin Signer
ff611d7a97 Merge branch '11-comment-and-error-handling' into 'hannes-philipp'
Resolve "comment and error handling"

See merge request naaice/tug!10
2023-08-07 10:21:08 +02:00
philippun
ea7c9f0df3 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-07 10:16:51 +02:00
philippun
28d2316f7d FTCS test 2023-08-07 10:16:40 +02:00
Hannes Signer
0b19b7c197 add test cases for Boundary class and additional input validation 2023-08-06 22:14:03 +02:00
Hannes Signer
30bc676604 add tests for Simulation class 2023-08-06 19:24:17 +02:00
Hannes Signer
aa4ce6a086 comments for Simulation files 2023-08-05 21:28:44 +02:00
Hannes Signer
eb339667c6 Merge branch 'hannes-philipp' into 11-comment-and-error-handling 2023-08-05 16:21:12 +02:00
Hannes Signer
69f1483006 add omp header and time measurement 2023-08-05 16:20:23 +02:00
Hannes Signer
154091e405 add user input validation 2023-08-04 15:39:02 +02:00
philippun
da39b9064a added tests for Grid class 2023-08-04 10:46:46 +02:00
Hannes Signer
2c2851a037 comment and input validation of Boundary class 2023-08-04 00:23:40 +02:00
philippun
8596f3ffda added first grid test 2023-08-03 22:33:55 +02:00
philippun
4e043e712e added commentary to FTCS.cpp 2023-08-03 21:43:42 +02:00
philippun
ab22436283 finished commentary and checks for Grid class 2023-08-03 18:17:21 +02:00
philippun
ad2fdabac9 commentary 2023-08-03 16:52:16 +02:00
Hannes Signer
fbdeba00a2 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-03 14:57:48 +02:00
Hannes Signer
d38e14d6f4 change: all variables as pass by reference 2023-08-03 14:57:44 +02:00
Marco De Lucia
604d511f9a Improved doc about CFL for 2D FTCS scheme 2023-08-03 13:08:08 +02:00
Philipp Ungrund
70268f58f3 Merge branch 'csv-to-eigen' into 'hannes-philipp'
Csv to eigen

See merge request naaice/tug!9
2023-08-03 11:25:31 +02:00
Philipp Ungrund
ba7a38f72f Merge branch 'hannes-philipp' into 'csv-to-eigen'
# Conflicts:
#   src/Simulation.cpp
2023-08-03 09:23:09 +00:00
Hannes Martin Signer
bc9e92e3ef Merge branch 'fix-pipeline' into 'csv-to-eigen'
Fix handling of CSV file

See merge request naaice/tug!8
2023-08-03 11:12:03 +02:00
Max Lübke
63b4e49f99 Fix handling of CSV file 2023-08-03 11:12:02 +02:00
Hannes Martin Signer
fba3c93e7d change path 2023-08-03 10:31:36 +02:00
Hannes Martin Signer
95a9e694b0 change test path 2023-08-03 10:30:12 +02:00
Hannes Martin Signer
05275a9e56 experiment 2 2023-08-03 10:27:38 +02:00
Hannes Martin Signer
37b6a52d7f experiment pipeline 2023-08-03 10:23:08 +02:00
Hannes Signer
d22f8cf71b pipeline test path 2023-08-02 14:23:00 +02:00
Hannes Signer
7839a412e6 change: delete test output 2023-08-02 14:19:09 +02:00
Hannes Signer
b9393a4524 change: update path to csv test file 2023-08-02 14:17:40 +02:00
philippun
85c5e55601 fixed a bug that returned the wrong grid to the test method 2023-08-02 13:19:33 +02:00
philippun
78cf41f57e implemented some util methods and started with a first test case 2023-08-02 12:36:06 +02:00
Hannes Signer
e19171feaa switch from hpp to cpp 2023-08-02 10:50:14 +02:00
philippun
7bb50a575f Merge branch '7-validation-of-user-inputs' into hannes-philipp 2023-08-02 10:48:40 +02:00
philippun
8215a1238d bug fixes 2023-08-02 10:46:58 +02:00
Hannes Signer
33fd35a65a add csv2matrix function 2023-08-02 10:35:36 +02:00
Hannes Signer
695b80beaf Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-08-01 16:56:34 +02:00
Hannes Signer
f4924ac8b2 add: Implementation of max time step 2023-08-01 16:56:25 +02:00
philippun
9e200305ff added CSV output optimizations 2023-08-01 16:06:32 +02:00
Hannes Signer
a72217f6a2 feat: use references instead of objects 2023-08-01 14:20:51 +02:00
Marco De Lucia
f5a59def6d Merge branch 'mdl_test' into 'hannes-philipp'
Add docu and validation files

See merge request naaice/tug!7
2023-07-31 16:52:32 +02:00
Marco De Lucia
788e59c4ef Add docu and validation files 2023-07-31 16:52:31 +02:00
philippun
f8cb62fa94 added reference example FTCS 2D closed 2023-07-31 14:31:04 +02:00
Hannes Martin Signer
2d293469ff Merge branch 'dynamic-boundary-conditions-functionality' into 'hannes-philipp'
Dynamic boundary conditions functionality

See merge request naaice/tug!6
2023-07-28 16:00:51 +02:00
philippun
ffa48347b8 removed some old commentary 2023-07-28 15:55:59 +02:00
philippun
0872639c54 feat: implementation of dynamic boundary conditions in FTCS 2023-07-28 15:54:03 +02:00
philippun
9703dba718 Merge branch 'boundaries-closed-case' into dynamic-boundary-conditions-functionality 2023-07-28 13:03:51 +02:00
philippun
0a0e16bb56 feat: added getters to Boundary 2023-07-28 13:02:45 +02:00
philippun
4680e9823f feat: reimplented Boundary to incorporate different types for each boundary element 2023-07-28 12:57:09 +02:00
Philipp Ungrund
4e8933862e Merge branch 'boundaries-closed-case' into 'dynamic-boundary-conditions-functionality'
feat: add boundary conditons for closed cases in independent functions

See merge request naaice/tug!5
2023-07-28 11:05:28 +02:00
philippun
16d3663909 commented out the probably last easy profiler reference 2023-07-28 11:02:56 +02:00
philippun
605000de5e commented out another easy profiler include 2023-07-28 11:00:21 +02:00
philippun
c9b714b241 commented out easy profiler 2023-07-28 10:59:08 +02:00
philippun
3364042af7 unset easyprofiler as required 2023-07-28 10:55:28 +02:00
philippun
10bb5f5012 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-07-28 10:51:15 +02:00
philippun
c5862d4c18 test commit 2023-07-28 10:49:01 +02:00
philippun
88b00931f8 test commit 2023-07-28 10:48:07 +02:00
philippun
8f48830bda reviewed closed case functions 2023-07-28 10:46:05 +02:00
Hannes Signer
049fc319db feat: add boundary conditons for closed cases in independent functions 2023-07-26 22:17:37 +02:00
philippun
7cf4ad4772 optimized example 2023-07-26 16:02:23 +02:00
Hannes Signer
60d01a83c4 Merge branch 'hannes-philipp' of git.gfz-potsdam.de:naaice/tug into hannes-philipp 2023-07-24 18:22:06 +02:00
Hannes Signer
23da250cba add easy profiler to CMAKE files 2023-07-24 18:16:35 +02:00
philippun
22d7ce45f7 added basic structure to allow for separate boundary condition types at each boundary cell later on 2023-07-24 17:56:38 +02:00
philippun
69ca773afa added stub function for vertical change in closed case 2023-07-24 16:14:26 +02:00
philippun
5c39743c6f Merge branch 'main' into hannes-philipp 2023-07-24 16:12:15 +02:00
philippun
58f620e6b9 added function 2023-07-24 16:03:31 +02:00
philippun
c3461a46fa removed in progress commentary 2023-07-24 15:42:28 +02:00
philippun
be94e760af Refactoring of function calc_alpha_intercell into calcAlphaIntercell and adjusting of example parameters 2023-07-24 15:40:30 +02:00
philippun
0ebc8d30e8 implemented the 1D FTCS case and created an example prototype 2023-07-22 14:02:44 +02:00
Marco De Lucia
c8163bb596 Merge branch 'main' of git.gfz-potsdam.de:naaice/tug 2023-07-21 13:20:49 +02:00
Marco De Lucia
c7efae395f MDL: added HetDiff.R script to generate and visualize reference simulations in R 2023-07-21 13:20:37 +02:00
philippun
ef01e3f473 added functions for the summands of the different directions 2023-07-20 16:09:59 +02:00
philippun
b7561b93e0 changed type auto to specific type 2023-07-20 14:59:31 +02:00
philippun
d88d7956a5 calculated several examples and implemented csv out 2023-07-20 11:47:24 +02:00
philippun
67f289c1f8 fixed several bugs in FTCS_2D_proto_example and improved readability 2023-07-19 16:24:13 +02:00
Max Lübke
17ed5cf75f Merge branch 'fix-ci' into 'main'
Fix CI pipeline

See merge request naaice/tug!3
2023-07-19 12:10:26 +02:00
Max Luebke
54b9a31f16 ci: remove readability from SA checks 2023-07-19 12:07:37 +02:00
Max Luebke
7e4dc1e383 ci: provide dockerfile for image creation 2023-07-19 12:02:24 +02:00
Max Luebke
5bee2d20e9 ci: use reworked docker image for ci 2023-07-19 12:00:14 +02:00
Max Luebke
3d80b7e02a build: only fetch doctest if it is not present yet 2023-07-19 11:31:59 +02:00
philippun
d457c2b9a7 fixed Eigen implementation bugs 2023-07-19 11:19:00 +02:00
Hannes Signer
99925dbd4f remove bug because of Matrix2d and Vector2d 2023-07-19 00:35:51 +02:00
philippun
542601fdcd implemented Simulation with FTCS and tried a first example 2023-07-18 17:14:16 +02:00
philippun
0a9b58e8ff implementing FTCS 2023-07-18 15:27:17 +02:00
philippun
b93dc46aed implemeted simulation.cpp with run() 2023-07-18 11:43:33 +02:00
philippun
da65be3cca implemented Boundary 2023-07-18 11:02:16 +02:00
Hannes Signer
01a589889f change: Boundardy.hpp, Grd.hpp, Simulation.hpp, Boundary.cpp, Grid.cpp | change constructors 2023-07-14 00:02:49 +02:00
philippun
2ab924a162 added basic functionality to Grid and Boundary 2023-07-13 15:52:56 +02:00
philippun
470ebbd2ab add: Boundary.cpp, FTCS.cpp, Grid.cpp, Simulation.cpp | added implementations files, changed simulation.hpp 2023-07-13 14:41:12 +02:00
philippun
a6a704a176 add: Boundary.hpp Simulation.hpp | adding basic outline 2023-07-13 11:29:53 +02:00
philippun
16640fe122 change: Grid.hpp | finished outline Grid class 2023-07-13 10:54:48 +02:00
philippun
fc999f09b3 change: Grid.hpp | added class and constructors 2023-07-13 10:15:18 +02:00
philippun
65f569380a add: pseudo_example.cpp, Grid.hpp | creating new API file and pseudo example 2023-07-12 15:48:29 +02:00
philippun
40573b81b2 change: FTCS second_example | comparison of FTCS cases against ADI implementation 2023-07-06 15:52:47 +02:00
Hannes Signer
eb9f8503d6 Change: FTCS | add closed boundardy conditions 2023-06-29 18:01:02 +02:00
philippun
efcb2d1494 layout 2023-06-29 10:14:36 +02:00
philippun
25c52e6b29 fixed confusion about variable naming with x, y, i, j, m, n 2023-06-28 16:53:38 +02:00
philippun
5688c82da2 cross testing FTCS prototype with existing ADI solution 2023-06-28 14:39:54 +02:00
philippun
214cb717d3 improved prototype FTCS 2023-06-28 11:16:11 +02:00
Hannes Signer
e57c5462eb changed: FTCS | fixed subscript issue 2023-06-28 10:06:49 +02:00
Hannes Signer
916f455174 test commit 2023-06-25 00:56:06 +02:00
Hannes Signer
45827c0402 Change: FTCS.ipynb | iteration size for widget 2023-06-23 14:42:52 +02:00
Hannes Martin Signer
5e70b319c9 Change: FTCS.ipynb | ipwidget functionality 2023-06-23 14:28:07 +02:00
philippun
8c37a6998d add imshow to visually follow changes 2023-06-23 13:51:31 +02:00
philippun
25985f096c running example FTCS 2023-06-22 16:06:25 +02:00
philippun
be68985600 FTCS prototyping 2023-06-22 11:57:46 +02:00
philippun
72509832d4 prototyping directory for explicit scheme 2023-06-22 10:43:54 +02:00
philippun
ecd3e95477 commentary 2023-06-08 15:58:30 +02:00
philippun
1df7f82553 added print statement for experimental purposes to first_example 2023-06-06 10:56:22 +02:00
philippun
c798c61706 additional example 2023-06-06 10:10:12 +02:00
philippun
2d9d318981 added some commentary with questions and TODOs 2023-05-31 13:49:13 +02:00
philippun
165e72e978 added some commentary with TODOs and questions 2023-05-31 12:33:49 +02:00
philippun
4e1ed11b6f Second example, additional commentary 2023-05-24 13:47:57 +02:00
Marco De Lucia
25855da6b2 Merge branch 'main' of git.gfz-potsdam.de:naaice/tug 2023-05-11 17:38:37 +02:00
Marco De Lucia
459922629d MDL: Written down 2D explicit FTCS scheme in docs/ADI_scheme.org; rough implementation in scripts/Adi2D_Reference.R 2023-05-11 17:38:29 +02:00
philippun
06d5d737b0 first example 2023-05-11 17:01:33 +02:00
Max Lübke
ebca11f4fd Merge branch '1-remove-bits-stdc-h-as-header' into 'main'
Resolve "Remove <bits/stdc++.h> as header"

Closes #1

See merge request naaice/tug!2
2023-05-03 12:12:05 +02:00
Max Lübke
f24ce43f5a fix: remove bits/* header 2023-05-03 12:11:13 +02:00
Marco De Lucia
adb2e325be MDL: add compiled ADI_scheme.pdf to /doc 2023-03-17 11:47:03 +01:00
Marco De Lucia
79957da687 Merge branch 'advection_naaice' into 'main'
Merge heterogeneous diffusion

See merge request naaice/tug!1
2023-03-17 11:42:08 +01:00
Marco De Lucia
cea941853b Adi2D_Ref 2023-03-17 10:57:07 +01:00
Max Luebke
79d7a32fc2 fix: conversion of inner index when using inner boundary conditions 2023-01-24 12:01:56 +01:00
Max Lübke
d4e3ab8544 perf: remove OpenMP pragma from ortho calc, since we already forked
OpenMP threads in 2D ADI
2023-01-06 16:18:06 +01:00
Marco De Lucia
e4ec0a11da MDL fixing docs 2022-12-27 15:37:41 +01:00
Marco De Lucia
9815ebce9c Small fixes 2022-12-21 18:54:15 +01:00
Marco De Lucia
7bd40639d5 MDL: direct discretization scheme, theory in doc and dirty implementation in scripts 2022-12-21 13:55:18 +01:00
Marco De Lucia
e853a96839 Working out heter.diff scheme (not working yet) 2022-12-20 22:53:21 +01:00
Max Lübke
0abd4e04ae chore: update Changelog to version 0.3 2022-09-08 14:24:49 +02:00
Max Lübke
0be1586d69 chore: remove unneeded test file 2022-09-08 14:22:22 +02:00
Max Lübke
6120acdaf4 refactor: move includes into subdirectory 'tug' 2022-09-08 14:21:52 +02:00
Max Lübke
590884dabb chore: Bump changelog to version 0.3 2022-09-08 12:49:26 +02:00
Max Lübke
592f59dbc5 perf: represent inner boundary conditions with a std::map
refactor: inner boundary conditions are now accessible via methods

test: update test cases
2022-09-08 12:46:26 +02:00
Max Lübke
94e83b5eb8 feat: allow undefined boundary conditions
It is now possible to not define any boundary condition object.

In this case a grid with closed boundary conditions is assumed during
diffusion simulation.

refactor: various adjustments according to clang-tidy
2022-09-08 11:53:25 +02:00
Max Lübke
02a9531544 feat: add helper functions to TugInput struct 2022-09-01 16:43:14 +02:00
Max Luebke
e482d71779 feat: Remove class BTCSDiffusion
BREAKING CHANGE: Functionality is now provided by function calls and
scheme generation is decoupled from LEqS solving.
2022-09-01 16:43:14 +02:00
Max Lübke
97889cde5e fix: grid dimensions were stored and accessed incorrectly 2022-08-24 14:48:46 +02:00
Max Lübke
32f3658861 ci: linting needs to be triggered manually now 2022-08-24 07:21:14 +00:00
Max Lübke
b5db1056c4 test: enable building of tests per default 2022-08-24 09:08:31 +02:00
Max Lübke
443ea15c58 test: add target check 2022-08-24 09:05:12 +02:00
Max Lübke
822f50d887 chore: update Changelog link to new name 2022-08-23 14:22:50 +02:00
Max Lübke
9abd35879c chore: Change URL of repo and and description for CI 2022-08-23 14:13:42 +02:00
Max Lübke
c96655241f docs: Update and extending README
build: Set internal variables to new project name

ci: Use new CMake variables as basis
2022-08-23 11:35:08 +02:00
Marco De Lucia
40f37bf104 chore: moved Comp*.R to scripts/ 2022-08-18 10:51:43 +02:00
Max Lübke
baaa85182f build: remove BoundaryCondition as extra library 2022-08-17 12:13:57 +02:00
Max Lübke
c99f770182 refactor: move BoundaryCondition header and source 2022-08-17 12:07:21 +02:00
Max Lübke
1822bcd98d refactor: rename BoundaryCondition class 2022-08-17 12:05:16 +02:00
Max Lübke
bdd56bec82 refactor: rename and expand namespace 2022-08-17 11:58:18 +02:00
Max Lübke
46185571c0 doc: remove old stuff from ADI documentation 2022-08-17 09:56:46 +02:00
Max Lübke
832b61bd19 chore: bump Changelog to version 0.2 2022-08-16 15:40:31 +02:00
Max Lübke
b7c9dbb535 style: fix various code style recommendations from clang 2022-08-16 15:35:14 +02:00
Max Lübke
67ca71404c chore: configure git-chglog for new commit style 2022-08-16 15:24:15 +02:00
Max Lübke
9461ebd8f3 test: add new test case for diffusion module
Test case defines a 2D grid with closed boundaries and 1 constant cell
in the middle.

Every other cell is set to 0.

After each iteration the sum of all cells must be greater in comparison
to the previous state of field.
2022-08-16 15:24:08 +02:00
Max Luebke
f9280b1274 feat: support for inner closed cells in diffusion module 2022-08-16 15:22:32 +02:00
Max Lübke
37c2dd70ec refactor: remove BTCSUtils header from include API 2022-08-16 15:22:32 +02:00
Max Lübke
be50ae4777 test: add tests for inner boundary conditions 2022-08-16 15:22:32 +02:00
Max Lübke
b6eb212bcb feat: add setting of inner boundary conditions
It is possible to define inner grid cells with a type of either CLOSED,
FLUX, CLOSED or UNSET.
2022-08-16 15:22:32 +02:00
Max Lübke
fd7e2f5b63 build: fetch doctest during configuration 2022-08-16 11:30:35 +00:00
Max Lübke
85c9fc5be2 ci: disable testing during static analyze 2022-08-16 11:30:35 +00:00
Max Lübke
19fc29dc52 ci: add git as dependency 2022-08-16 11:30:35 +00:00
Max Lübke
2e2ba6cd82 Merge branch 'update-bc-style' into 'main'
Update bc style

See merge request sec34/btcsdiffusion!36
2022-08-12 21:16:43 +02:00
Max Lübke
efed757a9e style: Use enumerations for macros and use more useful function names
Update the pre-compiler macros defined by `BTCSBoundaryCondition` to enumerations. 

Update method names for getting row and column boundary conditions.
2022-08-12 21:16:43 +02:00
Max Lübke
c4334164a3 Merge branch 'update-readme' into 'main'
docs: update Roadmap and add Contributing section

See merge request sec34/btcsdiffusion!35
2022-08-12 20:57:57 +02:00
Max Luebke
652bcc71a9 docs: update Roadmap and add Contributing section 2022-08-12 18:56:24 +00:00
Max Lübke
1c8b3b7c95 Merge branch 'add-changelog' into 'main'
Add changelog

See merge request sec34/btcsdiffusion!34
2022-08-12 20:27:16 +02:00
Max Luebke
38700f2968 Add Changelog 2022-08-12 20:25:37 +02:00
Max Luebke
c1cb489a69 Add git-chglog template for changelog creation 2022-08-12 19:02:23 +02:00
Max Lübke
02e65afb60 Move includes 2022-08-12 13:06:36 +00:00
Max Lübke
e9f5b34968 Move apps to examples 2022-08-12 14:53:27 +02:00
102 changed files with 22601 additions and 8722 deletions

30
.chglog/CHANGELOG.tpl.md Executable file
View File

@ -0,0 +1,30 @@
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
* {{ .Subject }}
{{ end }}
{{ end -}}
{{- if .RevertCommits -}}
### Reverts
{{ range .RevertCommits -}}
* {{ .Revert.Header }}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}

33
.chglog/config.yml Executable file
View File

@ -0,0 +1,33 @@
style: gitlab
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://git.gfz-potsdam.de/sec34/tug
options:
commits:
# filters:
# Type:
# - feat
# - fix
# - perf
# - refactor
commit_groups:
title_maps:
feat: Features
fix: Bug Fixes
perf: Performance Improvements
refactor: Code Refactoring
chore: Housework
build: Build System
docs: Documentation
style: Code Style
test: Testing
ci: Continious Integration
header:
pattern: "^(\\w*)\\:\\s(.*)$"
pattern_maps:
- Type
- Subject
notes:
keywords:
- BREAKING CHANGE

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ compile_commands.json
.cache/
.ccls-cache/
/iwyu/
.Rhistory
.vscode/
.codechecker/

View File

@ -1,33 +1,66 @@
image: sobc/gitlab-ci
image: gcc:14
before_script:
- apt-get update && apt-get install -y cmake ninja-build libeigen3-dev git
stages:
- build
- test
- release
- static_analyze
build_release:
before_script:
- apt-get update && apt-get install -y libeigen3-dev
stage: build
artifacts:
paths:
- build/test/test
expire_in: 100s
script:
- mkdir build && cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DBTCS_ENABLE_TESTING=ON ..
- make -j$(nproc)
test:
stage: test
script:
- ./build/test/test
- mkdir build && cd build
- cmake -DCMAKE_BUILD_TYPE=Debug -DTUG_ENABLE_TESTING=ON -G Ninja ..
- ninja
- ctest --output-junit test_results.xml
artifacts:
when: always
paths:
- build/test_results.xml
reports:
junit: build/test_results.xml
doc:
stage: release
image: python:slim
before_script:
- apt-get update && apt-get install --no-install-recommends -y graphviz imagemagick doxygen make
- pip install --upgrade pip && pip install Sphinx Pillow breathe sphinx-rtd-theme sphinx-mdinclude
- mkdir public
script:
- pushd docs_sphinx
- make html
- popd && mv docs_sphinx/_build/html/* public/
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
push:
stage: release
variables:
GITHUB_REPOSITORY: 'git@github.com:POET-Simulator/tug.git'
ORIGINAL_REPO_URL: 'https://git.gfz-potsdam.de/naaice/tug.git'
ORIGINAL_REPO_NAME: 'tug'
before_script:
# I know that there is this file env variable in gitlab, but somehow it does not work for me (still complaining about white spaces ...)
# Therefore, the ssh key is stored as a base64 encoded string
- mkdir -p ~/.ssh && echo $GITHUB_SSH_PRIVATE_KEY | base64 -d > ~/.ssh/id_ed25519 && chmod 0600 ~/.ssh/id_ed25519
- ssh-keyscan github.com >> ~/.ssh/known_hosts
script:
- rm -rf $ORIGINAL_REPO_NAME.git
- git clone --mirror $ORIGINAL_REPO_URL "$ORIGINAL_REPO_NAME.git" && cd $ORIGINAL_REPO_NAME.git
- git push --mirror $GITHUB_REPOSITORY
lint:
before_script:
- apt-get update && apt-get install -y libeigen3-dev
stage: static_analyze
before_script:
- apk add clang-extra-tools openmp-dev
script:
- mkdir lint && cd lint
- cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-checks=cppcoreguidelines-*,clang-analyzer-*,performance-*,readability-*, modernize-*" ..
- make BTCSDiffusion
- cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-checks=cppcoreguidelines-*,clang-analyzer-*,performance-*" -DTUG_ENABLE_TESTING=OFF ..
- make tug
when: manual

43
CITATION.cff Normal file
View File

@ -0,0 +1,43 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: 'tug: Transport on Uniform Grids'
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Marco
family-names: De Lucia
email: delucia@gfz.de
orcid: 'https://orcid.org/0009-0000-3058-8472'
- given-names: Max
family-names: Lübke
email: mluebke@uni-potsdam.de
orcid: 'https://orcid.org/0009-0008-9773-3038'
- given-names: Bettina
family-names: Schnor
email: schnor@cs.uni-potsdam.de
orcid: 'https://orcid.org/0000-0001-7369-8057'
- given-names: Hannes
family-names: Signer
email: signer@uni-potsdam.de
orcid: 'https://orcid.org/0009-0000-3058-8472'
- given-names: Philipp
family-names: Ungrund
email: ungrund@uni-potsdam.de
orcid: 'https://orcid.org/0009-0007-0182-4051'
repository-code: 'https://github.com/POET-Simulator/tug'
abstract: >-
tug implements different numerical approaches for
transport problems, notably diffusion with implicit BTCS
(Backward Time, Central Space) Euler and parallel 2D ADI
(Alternating Direction Implicit).
keywords:
- diffusion
- advection
- BTCS
- FTCS
- Thomas-Algorithm
license: GPL-2.0

View File

@ -1,41 +1,86 @@
#debian stable (currently bullseye)
cmake_minimum_required(VERSION 3.18)
# debian stable (currently bullseye)
cmake_minimum_required(VERSION 3.20)
project(BTCSDiffusion CXX)
set(CMAKE_CXX_STANDARD 17)
project(
tug
VERSION 0.4
LANGUAGES CXX)
find_package(Eigen3 REQUIRED NO_MODULE)
find_package(OpenMP)
## SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -mfma")
option(BTCS_USE_OPENMP "Compile with OpenMP support" ON)
include(GNUInstallDirs)
set(CMAKE_CXX_FLAGS_GENERICOPT "-O3 -march=native" CACHE STRING
"Flags used by the C++ compiler during opt builds."
FORCE)
option(TUG_USE_OPENMP "Compile tug with OpenMP support" ON)
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel GenericOpt."
FORCE)
option(BTCS_USE_UNSAFE_MATH_OPT
"Use compiler options to break IEEE compliances by
option(
TUG_USE_UNSAFE_MATH_OPT "Use compiler options to break IEEE compliances by
oenabling reordering of instructions when adding/multiplying of floating
points."
OFF)
points." OFF)
if(BTCS_USE_UNSAFE_MATH_OPT)
add_compile_options(-ffast-math)
option(TUG_ENABLE_TESTING "Run tests after succesfull compilation" OFF)
option(TUG_HANNESPHILIPP_EXAMPLES "Compile example applications" OFF)
option(TUG_NAAICE_EXAMPLE "Enables NAAICE examples with export of CSV files"
OFF)
add_library(tug INTERFACE)
target_include_directories(
tug INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_link_libraries(tug INTERFACE Eigen3::Eigen)
target_compile_features(tug INTERFACE cxx_std_17)
if(TUG_USE_OPENMP AND OpenMP_CXX_FOUND)
target_link_libraries(tug INTERFACE OpenMP::OpenMP_CXX)
endif()
option(BTCS_ENABLE_TESTING
"Run tests after succesfull compilation"
OFF)
if(TUG_USE_UNSAFE_MATH_OPT)
target_compile_options(tug INTERFACE -ffast-math)
endif()
add_subdirectory(app)
add_subdirectory(src)
install(
TARGETS tug
EXPORT ${PROJECT_NAME}_Targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(BTCS_ENABLE_TESTING)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"tugConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
install(
EXPORT ${PROJECT_NAME}_Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/tug DESTINATION include)
if(TUG_ENABLE_TESTING)
enable_testing()
add_subdirectory(test)
endif()
if(TUG_HANNESPHILIPP_EXAMPLES)
add_subdirectory(examples)
endif()
if(TUG_NAAICE_EXAMPLE)
add_subdirectory(naaice)
endif()

11
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,11 @@
# Contributors
Thank you to all the dedicated individuals who poured their efforts into tug,
allowing this project to flourish.
## Names
- Hannes Signer
- Philipp Ungrund
Both for their diligent work in implementing the heterogeneous diffusion model and their exceptional documentation published within this repository.

108
Changelog.md Normal file
View File

@ -0,0 +1,108 @@
<a name="v0.3"></a>
## [v0.3](https://git.gfz-potsdam.de/sec34/tug/compare/v0.2...v0.3) (2022-09-08)
### Bug Fixes
* grid dimensions were stored and accessed incorrectly
### Build System
* remove BoundaryCondition as extra library
### Code Refactoring
* move includes into subdirectory 'tug'
* move BoundaryCondition header and source
* rename BoundaryCondition class
* rename and expand namespace
### Continious Integration
* linting needs to be triggered manually now
### Doc
* remove old stuff from ADI documentation
### Documentation
* Update and extending README
### Features
* allow undefined boundary conditions
* add helper functions to TugInput struct
* Remove class BTCSDiffusion
### Housework
* remove unneeded test file
* update Changelog link to new name
* Change URL of repo and and description for CI
* moved Comp*.R to scripts/
### Performance Improvements
* represent inner boundary conditions with a std::map
### Testing
* enable building of tests per default
* add target `check`
### BREAKING CHANGE
Functionality is now provided by function calls and
scheme generation is decoupled from LEqS solving.
<a name="v0.2"></a>
## [v0.2](https://git.gfz-potsdam.de/sec34/tug/compare/v0.1...v0.2) (2022-08-16)
### Build System
* fetch doctest during configuration
### Ci
* disable testing during static analyze
* add git as dependency
### Code Refactoring
* remove BTCSUtils header from include API
### Code Style
* fix various code style recommendations from clang
* Use enumerations for macros and use more useful function names
### Documentation
* update Roadmap and add Contributing section
### Features
* support for inner closed cells in diffusion module
* add setting of inner boundary conditions
### Housework
* configure git-chglog for new commit style
### Testing
* add new test case for diffusion module
* add tests for inner boundary conditions
<a name="v0.1"></a>
## v0.1 (2022-08-09)
* Basic solving of diffusion problems with
- 1D regular and rectangular grids using BTCS scheme and Eigen SparseLU solver
- 2D regular and rectangular grids using 2D-ADI-BTCS scheme and Eigen SparseLU solver
* Definition of boundary conditions via class `BTCSBoundaryCondition` on ghost nodes
* Boundaries types `CLOSED` and `CONSTANT` cells are provided for diffusion problem solving
* Software testing of both `BTCSDiffusion` and `BTCSBoundaryCondition` classes
* Description of both BTCS and 2D-ADI-BTCS schemes are provided in `doc`
* Example applications are attached in `app` subdirectory

339
LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
tug
Copyright (C) 2023 naaice
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

103
README.md Normal file
View File

@ -0,0 +1,103 @@
![tug boat](./doc/images/tug_logo_small.png)
`tug` implements different numerical approaches for transport
problems, notably diffusion with implicit BTCS (Backward Time, Central
Space) Euler and parallel 2D ADI (Alternating Direction Implicit).
# About
This project aims to provide a library for solving transport problems -
diffusion, advection - on uniform grids implemented in C++. The library
is built on top of
[Eigen](https://eigen.tuxfamily.org/index.php?title=Main_Page),
providing easy access to its optimized data structures and linear
equation solvers.
We designed the API to be as flexible as possible. Nearly every
built-in, framework or third-party data structure can be used to model a
problem, as long a pointer to continuous memory can be provided. We also
provide parallelization using [OpenMP](https://www.openmp.org/), which
can be easily turned on/off at compile time.
At the current state, both 1D and 2D diffusion problems on a regular
grid with constant alpha for all grid cells can be solved reliably.
# Requirements
- C++17 compliant compiler
- [CMake](https://cmake.org/) >= 3.20
- [Eigen](https://eigen.tuxfamily.org/) >= 3.4.0
# Getting started
`tug` is designed as a framework library and it relies on
[CMake](https://cmake.org/) for building. If you already use
`CMake` as your build toolkit for your application, you\'re
good to go. If you decide not to use `CMake`, you need to
manually link your application/library to `tug`.
1. Create project directory.
```bash
mkdir sample_project && cd sample_project
```
2. Clone this repository into path of choice project directory
3. Add the following line into `CMakeLists.txt` file:
```bash
add_subdirectory(path_to_tug EXCLUDE_FROM_ALL)
```
4. Write application/library using `tug`\'s API, notably
including relevant headers (see examples).
5. Link target application/library against `tug`. Do this by
adding into according `CMakeLists.txt` file:
```bash
target_link_libraries(<your_target> tug)
```
6. Build your application/library with `CMake`.
# Usage in an application
Using `tug` can be summarized into the following steps:
1. Define problem dimensionality
2. Set grid sizes for each dimension
3. Set the timestep
4. Define boundary conditions
5. Run the simulation!
This will run a simulation on the defined grid for one species. See the
source code documentation of `tug` and the examples in the
`examples/` directory for more details.
# Contributing
In this early stage of development every help is welcome. To do so,
there are currently the following options:
Given you have an account for GFZ\'s `gitlab` instance:
1. Fork this project, create a branch and push your changes. If your
changes are done or you feel the need for some feedback create a
merge request with the destination set to the **main** branch of
this project.
2. Ask for access to this repository. You most likely will get access
as a developer which allows you to create branches and merge
requests inside this repository.
If you can\'t get access to this `gitlab` instance:
- Download this repository and note down the SHA of the downloaded commit. Apply
your changes and send a mail to <mluebke@gfz-potsdam.de> or
<delucia@gfz-potsdam.de> with the patch/diff compared to your starting
point. Please split different patch types (feature, fixes, improvements ...)
into seperate files. Also provide us the SHA of the commit you\'ve
downloaded.
Thank you for your contributions in advance!

View File

@ -1,87 +0,0 @@
#+TITLE: BTCSDiffusion
#+BEGIN_CENTER
A framework solving diffusion problems using BTCS approach.
#+END_CENTER
* About
This project aims to provide a library for solving diffusion problems using the
backward Euler method (BTCS) implemented in C++.
The library is built on top of [[https://eigen.tuxfamily.org/index.php?title=Main_Page][Eigen]], providing easy access to data structures
and the linear equation solver.
We designed the API to be as much flexible as possible. Nearly every built-in,
framework or third-party data structure can be used to model a problem, as long
a pointer to continious memory can be providided.
Also we provide basic parallelization by using [[https://www.openmp.org/][OpenMP]], which can be easily
turned on/off during generation of makefiles.
At the current state, both 1D and @D diffusion problems on a regular grid with
constant alpha for all grid cells can be solved reliably.
* Getting started
As this diffusion module is designed as a framework library and makefile
generation is done by [[https://cmake.org/][CMake]], you're good to go to also use CMake as your build
toolkit. If you decide to not use CMake, you need to manually link your
application/library to BTCSDiffusion.
1. Create project directory.
#+BEGIN_SRC
$ mkdir sample_project && cd sample_project
#+END_SRC
2. Clone this repository into path of choice project directory
#+BEGIN_SRC
$ git clone git@git.gfz-potsdam.de:mluebke/diffusion.git
#+END_SRC
3. Add the following line into =CMakeLists.txt= file:
#+BEGIN_SRC
add_subdirectory(path_to_diffusion_module EXCLUDE_FROM_ALL)
#+END_SRC
4. Write application/library using API of =BTCSDiffusion=.
5. Link target application/library against =BTCSDiffusion=. Do this by adding
into according =CMakeLists.txt= file:
#+BEGIN_SRC
target_link_libraries(your_libapp BTCSDiffusion)
#+END_SRC
6. Build your application/library with CMake.
* Usage
Setting up an enviroment to use the =BTCSDiffusion= module is divided into the
following steps:
1. Defining dimension of diffusion problem.
2. Set grid sizes in according dimensions.
3. Set the timestep to simulate.
4. Defining boundary conditions.
5. Run the simulation!
This will run a simulation on the defined grid for one species. See the source
code documentation of =BTCSDiffusion= and the examples in the =app/= directory
for more information.
* Roadmap
- [X] 1D diffusion
- [ ] 2D diffusion
- [ ] 3D diffusion (?)
- [ ] R-API
- [ ] Python-API (?)
- [ ] Testing
* License
TODO?

View File

@ -1,11 +0,0 @@
add_executable(1D main_1D.cpp)
target_link_libraries(1D PUBLIC BTCSDiffusion)
add_executable(2D main_2D.cpp)
target_link_libraries(2D PUBLIC BTCSDiffusion)
add_executable(Comp2D main_2D_mdl.cpp)
target_link_libraries(Comp2D PUBLIC BTCSDiffusion)
add_executable(Const2D main_2D_const.cpp)
target_link_libraries(Const2D PUBLIC BTCSDiffusion)

View File

@ -1,59 +0,0 @@
#include "../include/diffusion/BTCSDiffusion.hpp"
#include "../include/diffusion/BoundaryCondition.hpp"
#include <algorithm> // for copy, max
#include <cmath>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
#include <Rcpp.h>
using namespace std;
using namespace Diffusion;
using namespace Rcpp;
//using namespace Eigen;
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::export]]
std::vector<double> & diff1D(int n,
double length,
std::vector<double> & field,
std::vector<double> & alpha,
double timestep,
double bc_left,
double bc_right,
int iterations) {
// dimension of grid
int dim = 1;
// create input + diffusion coefficients for each grid cell
// std::vector<double> alpha(n, 1 * pow(10, -1));
// std::vector<double> field(n, 1 * std::pow(10, -6));
std::vector<boundary_condition> bc(n, {0,0});
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(length, n);
// set the boundary condition for the left ghost cell to dirichlet
bc[0] = {Diffusion::BC_CONSTANT, bc_left};
bc[n-1] = {Diffusion::BC_CONSTANT, bc_right};
// set timestep for simulation to 1 second
diffu.setTimestep(timestep);
//cout << setprecision(12);
// loop 100 times
// output is currently generated by the method itself
for (int i = 0; i < iterations; i++) {
diffu.simulate(field.data(), alpha.data(), bc.data());
}
// for (auto & cell : field) {
// Rcout << cell << "\n";
// }
return(field);
}

View File

@ -1,60 +0,0 @@
#include "../include/diffusion/BTCSDiffusion.hpp"
#include "../include/diffusion/BoundaryCondition.hpp"
#include <algorithm> // for copy, max
#include <cmath>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
#include <Rcpp.h>
using namespace std;
using namespace Diffusion;
using namespace Rcpp;
//using namespace Eigen;
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::export]]
std::vector<double> & diff2D(int nx,
int ny,
double lenx,
double leny,
std::vector<double> & field,
std::vector<double> & alpha,
double timestep,
int iterations)
{
// problem dimensionality
int dim = 2;
// total number of grid cells
int n = nx*ny;
std::vector<boundary_condition> bc(n, {0,0});
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(lenx, nx);
diffu.setXDimensions(leny, ny);
// set the boundary condition for the left ghost cell to dirichlet
// bc[0] = {Diffusion::BC_CONSTANT, bc_left};
// bc[n-1] = {Diffusion::BC_CONSTANT, bc_right};
// set timestep for simulation to 1 second
diffu.setTimestep(timestep);
//cout << setprecision(12);
// loop 100 times
// output is currently generated by the method itself
for (int i = 0; i < iterations; i++) {
diffu.simulate(field.data(), alpha.data(), bc.data());
}
// for (auto & cell : field) {
// Rcout << cell << "\n";
// }
return(field);
}

View File

@ -1,159 +0,0 @@
## Time-stamp: "Last modified 2022-03-16 14:01:11 delucia"
library(Rcpp)
library(RcppEigen)
library(ReacTran)
library(deSolve)
options(width=110)
setwd("app")
## This creates the "diff1D" function with our BTCSdiffusion code
sourceCpp("Rcpp-BTCS-1d.cpp")
### FTCS explicit (same name)
sourceCpp("RcppFTCS.cpp")
## Grid 101
## Set initial conditions
N <- 1001
D.coeff <- 1E-3
C0 <- 1 ## Initial concentration (mg/L)
X0 <- 0 ## Location of initial concentration (m)
## Yini <- c(C0, rep(0,N-1))
## Ode1d solution
xgrid <- setup.grid.1D(x.up = 0, x.down = 1, N = N)
x <- xgrid$x.mid
Diffusion <- function (t, Y, parms){
tran <- tran.1D(C = Y, C.up = 0, C.down = 0, D = parms$D, dx = xgrid)
return(list(tran$dC))
}
## gaussian pulse as initial condition
sigma <- 0.02
Yini <- 0.5*exp(-0.5*((x-1/2.0)**2)/sigma**2)
## plot(x, Yini, type="l")
parms1 <- list(D=D.coeff)
# 1 timestep, 10 s
times <- seq(from = 0, to = 1, by = 0.1)
system.time({
out1 <- ode.1D(y = Yini, times = times, func = Diffusion,
parms = parms1, dimens = N)[11,-1]
})
## Now with BTCS
alpha <- rep(D.coeff, N)
system.time({
out2 <- diff1D(n=N, length=1, field=Yini, alpha=alpha, timestep = 0.1, 0, 0, iterations = 10)
})
## plot(out1, out2)
## abline(0,1)
## matplot(cbind(out1,out2),type="l", col=c("black","red"),lty="solid", lwd=2,
## xlab="grid element", ylab="Concentration", las=1)
## legend("topright", c("ReacTran ode1D", "BTCS 1d"), text.col=c("black","red"), bty = "n")
system.time({
out3 <- RcppFTCS(n=N, length=1, field=Yini, alpha=1E-3, bc_left = 0, bc_right = 0, timestep = 1)
})
## Poor man's
mm <- colMeans(rbind(out2,out3))
matplot(cbind(Yini,out1, out2, out3, mm),type="l", col=c("grey","black","red","blue","green4"), lty="solid", lwd=2,
xlab="grid element", ylab="Concentration", las=1)
legend("topright", c("init","ReacTran ode1D", "BTCS 1d", "FTCS", "poor man's CN"), text.col=c("grey","black","red","blue","green4"), bty = "n")
sum(Yini)
sum(out1)
sum(out2)
sum(out3)
sum(mm)
## Yini <- 0.2*sin(pi/0.1*x)+0.2
## plot(Yini)
## plot(out3)
Fun <- function(dx) {
tmp <- diff1D(n=N, length=1, field=Yini, alpha=alpha, timestep = dx, 0, 0, iterations = floor(1/dx))
sqrt(sum({out1-tmp}^2))
}
reso <- optimise(f=Fun, interval=c(1E-5, 1E-1), maximum = FALSE)
dx <- 0.0006038284
floor(1/dx)
1/dx
system.time({
out2o <- diff1D(n=N, length=1, field=Yini, alpha=alpha, timestep = dx, 0, 0, iterations = 1656)
})
matplot(cbind(out1, out2o),type="l", col=c("black","red"), lty="solid", lwd=2,
xlab="grid element", ylab="Concentration", las=1)
legend("topright", c("ReacTran ode1D", "BTCS 1d dx=0.0006"), text.col=c("black","red"), bty = "n")
dx <- 0.05
system.time({
out2o <- diff1D(n=N, length=1, field=Yini, alpha=alpha, timestep = dx, 0, 0, iterations = 1/dx)
})
matplot(cbind(out1, out2o),type="l", col=c("black","red"), lty="solid", lwd=2,
xlab="grid element", ylab="Concentration", las=1)
legend("topright", c("ReacTran ode1D", "BTCS 1d dx=0.0006"), text.col=c("black","red"), bty = "n")
Matplot
## This creates the "diff1D" function with our BTCSdiffusion code
sourceCpp("Rcpp-BTCS-2d.cpp")
n <- 256
a2d <- rep(1E-3, n^2)
init2d <- readRDS("gs1.rds")
ll <- {init2d - min(init2d)}/diff(range(init2d))
system.time({
res1 <- diff2D(nx=N, ny=N, lenx=1, leny=1, field=ll, alpha=a2d, timestep = 0.1, iterations = 10)
})
hist(ll,32)

View File

@ -1,59 +0,0 @@
#include "../include/diffusion/BTCSDiffusion.hpp"
#include "../include/diffusion/BoundaryCondition.hpp"
#include <algorithm> // for copy, max
#include <cmath>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
#include <Rcpp.h>
using namespace std;
using namespace Diffusion;
using namespace Rcpp;
//using namespace Eigen;
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::export]]
std::vector<double> & diff1D(int n,
double length,
std::vector<double> & field,
std::vector<double> & alpha,
double timestep,
double bc_left,
double bc_right,
int iterations) {
// dimension of grid
int dim = 1;
// create input + diffusion coefficients for each grid cell
// std::vector<double> alpha(n, 1 * pow(10, -1));
// std::vector<double> field(n, 1 * std::pow(10, -6));
std::vector<boundary_condition> bc(n, {0,0});
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(length, n);
// set the boundary condition for the left ghost cell to dirichlet
bc[0] = {Diffusion::BC_CONSTANT, bc_left};
bc[n-1] = {Diffusion::BC_CONSTANT, bc_right};
// set timestep for simulation to 1 second
diffu.setTimestep(timestep);
//cout << setprecision(12);
// loop 100 times
// output is currently generated by the method itself
for (int i = 0; i < iterations; i++) {
diffu.simulate(field.data(), alpha.data(), bc.data());
}
// for (auto & cell : field) {
// Rcout << cell << "\n";
// }
return(field);
}

View File

@ -1,40 +0,0 @@
// Time-stamp: "Last modified 2022-03-15 18:15:39 delucia"
#include <Rcpp.h>
#include <iostream> // for std
#include <vector> // for vector
using namespace std;
using namespace Rcpp;
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::export]]
NumericVector RcppFTCS(int n,
double length,
NumericVector & field,
double alpha,
double bc_left,
double bc_right,
double timestep)
{
// dimension of grid
NumericVector ext (clone(field));
double dx = length / ((double) n - 1.);
double dt = 0.25*dx*dx/alpha;
double afac = alpha*dt/dx/dx;
int iter = (int) (timestep/dt);
Rcout << "dt: " << dt << "; inner iterations: " << iter << endl;
for (int it = 0; it < iter; it++){
for (int i = 1; i < ext.size()-1; i++) {
ext[i] = (1. - 2*afac)*ext[i] + afac*(ext[i+1]+ext[i-1]);
}
ext[0] = bc_left;
ext[n-1] = bc_right;
}
return(ext);
}

View File

@ -1,55 +0,0 @@
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <diffusion/BTCSDiffusion.hpp>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
using namespace std;
using namespace Diffusion;
int main(int argc, char *argv[]) {
// dimension of grid
int dim = 1;
int n = 20;
// create input + diffusion coefficients for each grid cell
std::vector<double> alpha(n, 1e-1);
std::vector<double> field(n, 1e-6);
BTCSBoundaryCondition bc;
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(1, n);
// set the boundary condition for the left ghost cell to dirichlet
bc(BC_SIDE_LEFT) = {BC_TYPE_CONSTANT, 5e-6};
// bc[0] = {Diffusion::BC_CONSTANT, 5e-6};
// diffu.setBoundaryCondition(1, 0, BTCSDiffusion::BC_CONSTANT,
// 5. * std::pow(10, -6));
// set timestep for simulation to 1 second
diffu.setTimestep(1.);
cout << setprecision(12);
// loop 100 times
// output is currently generated by the method itself
for (int i = 0; i < 100; i++) {
diffu.simulate(field.data(), alpha.data(), bc);
cout << "Iteration: " << i << "\n\n";
for (auto &cell : field) {
cout << cell << "\n";
}
cout << "\n" << endl;
}
return 0;
}

View File

@ -1,58 +0,0 @@
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <diffusion/BTCSDiffusion.hpp>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
using namespace std;
using namespace Diffusion;
int main(int argc, char *argv[]) {
// dimension of grid
int dim = 2;
int n = 5;
int m = 5;
// create input + diffusion coefficients for each grid cell
std::vector<double> alpha(n * m, 1e-6);
std::vector<double> field(n * m, 0);
BTCSBoundaryCondition bc(n, m);
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(n, n);
diffu.setYDimensions(m, m);
// set inlet to higher concentration without setting bc
field[12] = 1;
// set timestep for simulation to 1 second
diffu.setTimestep(1);
cout << setprecision(12);
for (int t = 0; t < 1000; t++) {
diffu.simulate(field.data(), alpha.data(), bc);
cout << "Iteration: " << t << "\n\n";
double sum = 0;
// iterate through rows
for (int i = 0; i < m; i++) {
// iterate through columns
for (int j = 0; j < n; j++) {
cout << left << std::setw(20) << field[i * n + j];
sum += field[i * n + j];
}
cout << "\n";
}
cout << "sum: " << sum << "\n" << endl;
}
return 0;
}

View File

@ -1,59 +0,0 @@
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <diffusion/BTCSDiffusion.hpp>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
using namespace std;
using namespace Diffusion;
int main(int argc, char *argv[]) {
// dimension of grid
int dim = 2;
int n = 5;
int m = 5;
// create input + diffusion coefficients for each grid cell
std::vector<double> alpha(n * m, 1e-1);
std::vector<double> field(n * m, 1e-6);
BTCSBoundaryCondition bc(n, m);
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(1, n);
diffu.setYDimensions(1, m);
boundary_condition input = {Diffusion::BC_TYPE_CONSTANT, 5e-6};
bc.setSide(BC_SIDE_LEFT, input);
// for (int i = 1; i <= n; i++) {
// bc[(n + 2) * i] = {Diffusion::BC_CONSTANT, 5e-6};
// }
// set timestep for simulation to 1 second
diffu.setTimestep(1.);
cout << setprecision(12);
for (int t = 0; t < 10; t++) {
diffu.simulate(field.data(), alpha.data(), bc);
cout << "Iteration: " << t << "\n\n";
// iterate through rows
for (int i = 0; i < m; i++) {
// iterate through columns
for (int j = 0; j < n; j++) {
cout << left << std::setw(20) << field[i * n + j];
}
cout << "\n";
}
cout << "\n" << endl;
}
return 0;
}

View File

@ -1,67 +0,0 @@
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <diffusion/BTCSDiffusion.hpp>
#include <iomanip>
#include <iostream> // for std
#include <vector> // for vector
using namespace std;
using namespace Diffusion;
int main(int argc, char *argv[]) {
// dimension of grid
int dim = 2;
int n = 501;
int m = 501;
// create input + diffusion coefficients for each grid cell
std::vector<double> alpha(n * m, 1e-3);
std::vector<double> field(n * m, 0.);
BTCSBoundaryCondition bc(n, m);
field[125500] = 1;
// create instance of diffusion module
BTCSDiffusion diffu(dim);
diffu.setXDimensions(1., n);
diffu.setYDimensions(1., m);
// set the boundary condition for the left ghost cell to dirichlet
// diffu.setBoundaryCondition(250, 250, BTCSDiffusion::BC_CONSTANT, 1);
// for (int d=0; d<5;d++){
// diffu.setBoundaryCondition(d, 0, BC_CONSTANT, .1);
// }
// diffu.setBoundaryCondition(1, 1, BTCSDiffusion::BC_CONSTANT, .1);
// diffu.setBoundaryCondition(1, 1, BTCSDiffusion::BC_CONSTANT, .1);
// set timestep for simulation to 1 second
diffu.setTimestep(1.);
cout << setprecision(7);
// First we output the initial state
cout << 0;
for (int i = 0; i < m * n; i++) {
cout << "," << field[i];
}
cout << endl;
// Now we simulate and output 8 steps à 1 sec
for (int t = 1; t < 6; t++) {
double time = diffu.simulate(field.data(), alpha.data(), bc);
cerr << "time elapsed: " << time << " seconds" << endl;
cout << t;
for (int i = 0; i < m * n; i++) {
cout << "," << field[i];
}
cout << endl;
}
return 0;
}

4
cmake/tugConfig.cmake.in Normal file
View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

View File

@ -1,15 +1,17 @@
#+TITLE: Numerical solution of diffusion equation in 2D with ADI Scheme
#+TITLE: Finite Difference Schemes for the numerical solution of heterogeneous diffusion equation in 2D
#+LaTeX_CLASS_OPTIONS: [a4paper,10pt]
#+LATEX_HEADER: \usepackage{fullpage}
#+LATEX_HEADER: \usepackage{amsmath, systeme}
#+LATEX_HEADER: \usepackage{charter}
#+LATEX_HEADER: \usepackage{amsmath, systeme, cancel, xcolor}
#+OPTIONS: toc:nil
* Diffusion in 1D
* Homogeneous diffusion in 1D
** Finite differences with nodes as cells' centres
The 1D diffusion equation is:
The 1D diffusion equation for spatially constant diffusion
coefficients $\alpha$ is:
#+NAME: eqn:1
\begin{align}
@ -19,7 +21,7 @@ The 1D diffusion equation is:
We aim at numerically solving [[eqn:1]] on a spatial grid such as:
[[./grid_pqc.pdf]]
[[./images/grid_pqc.pdf]]
The left boundary is defined on $x=0$ while the center of the first
cell - which are the points constituting the finite difference nodes -
@ -94,7 +96,6 @@ C_n^{t+1} = C_n^{t} + \frac{\alpha \cdot \Delta t}{\Delta x^2} \cdot (C^t_{n-1}
\end{equation}
A similar treatment can be applied to the BTCS implicit scheme.
** Implicit BTCS scheme
@ -306,103 +307,262 @@ form:
#+LATEX: \clearpage
* Old stuff
* Heterogeneous diffusion
** Input
If the diffusion coefficient $\alpha$ is spatially variable, equation
[[eqn:1]] can be rewritten as:
- =c= $\rightarrow c$
- containing current concentrations at each grid cell for species
- size: $N \times M$
- row-major
- =alpha= $\rightarrow \alpha$
- diffusion coefficient for both directions (x and y)
- size: $N \times M$
- row-major
- =boundary_condition= $\rightarrow bc$
- Defines closed or constant boundary condition for each grid cell
- size: $N \times M$
- row-major
#+NAME: eqn:hetdiff
\begin{align}
\frac{\partial C }{\partial t} & = \frac{\partial}{\partial x} \left(\alpha(x) \frac{\partial C }{\partial x} \right)
\end{align}
** Internals
** Discretization of the equation using chain rule
- =A_matrix= $\rightarrow A$
- coefficient matrix for linear equation system implemented as sparse matrix
- size: $((N+2)\cdot M) \times ((N+2)\cdot M)$ (including ghost zones in x direction)
- column-major (not relevant)
\noindent From the product rule for derivatives we obtain:
- =b_vector= $\rightarrow b$
- right hand side of the linear equation system
- size: $(N+2) \cdot M$
- column-major (not relevant)
- =x_vector= $\rightarrow x$
- solutions of the linear equation system
- size: $(N+2) \cdot M$
- column-major (not relevant)
#+NAME: eqn:product
\begin{equation}
\frac{\partial}{\partial x} \left(\alpha(x) \frac{\partial C }{\partial x}\right) = \frac{\partial \alpha}{\partial x} \cdot \frac{\partial C}{\partial x} + \alpha \frac{\partial^2 C }{\partial x^2}
\end{equation}
** Calculation for $\frac{1}{2}$ timestep
\noindent Using a spatially centred second order finite difference
approximation at $x=x_i$ for both $\alpha$ and $C$, we have
#+NAME: eqn:hetdiff_fd
\begin{align}
\frac{\partial \alpha}{\partial x} \cdot \frac{\partial C}{\partial x} +
\alpha \frac{\partial^2 C }{\partial x^2} & \simeq \frac{\alpha_{i+1} - \alpha_{i-1}}{2\Delta x}\cdot\frac{C_{i+1} - C_{i-1}}{2\Delta x} + \alpha_i \frac{C_{i+1} - 2 C_{i} + C_{i-1}}{\Delta x^2} \\ \nonumber
& = \frac{1}{\Delta x^2} \frac{\alpha_{i+1} - \alpha_{i-1}}{4}(C_{i+1} - C_{i-1}) + \frac{\alpha_{i}}{\Delta x^2}(C_{i+1}-2C_i+C_{i-1})\\ \nonumber
& = \frac{1}{\Delta x^2} \left\{A C_{i+1} -2\alpha_i C_i + AC_{i-1})\right\}
\end{align}
\noindent having set
\[ A = \frac{\alpha_{i+1}-\alpha_{i-1}}{4} + \alpha_i \]
** Symbolic addressing of grid cells
[[./grid.png]]
\noindent In 2D the ADI scheme [[eqn:genADI]] with heterogeneous diffusion
coefficients can thus be written:
** Filling of matrix $A$
#+NAME: eqn:genADI_het
\begin{equation}
\systeme{
\displaystyle \frac{C^{t+1/2}_{i,j}-C^{t }_{i,j}}{\Delta t/2} = \displaystyle \frac{\partial}{\partial x} \left( \alpha^x_{i,j} \frac{\partial C^{t+1/2}_{i,j}}{\partial x}\right) + \frac{\partial}{\partial y} \left( \alpha^y_{i,j} \frac{\partial C^{t }_{i,j}}{\partial y}\right),
\displaystyle \frac{C^{t+1 }_{i,j}-C^{t+1/2}_{i,j}}{\Delta t/2} = \displaystyle \frac{\partial}{\partial x} \left( \alpha^x_{i,j} \frac{\partial C^{t+1/2}_{i,j}}{\partial x}\right) + \frac{\partial}{\partial y} \left( \alpha^y_{i,j} \frac{\partial C^{t+1}_{i,j}}{\partial y}\right)
}
\end{equation}
- row-wise iterating with $i$ over =c= and =\alpha= matrix respectively
- addressing each element of a row with $j$
- matrix $A$ also containing $+2$ ghost nodes for each row of input matrix $\alpha$
- $\rightarrow offset = N+2$
- addressing each object $(i,j)$ in matrix $A$ with $(offset \cdot i + j, offset \cdot i + j)$
\noindent We define for compactness $S_x=\frac{\Delta t}{2\Delta x^2}$
and $S_y=\frac{\Delta t}{2\Delta y^2}$ and
*** Rules
#+NAME: eqn:het_AB
\begin{equation}
\systeme{
\displaystyle A_{i,j} = \displaystyle \frac{\alpha^x_{i+1,j} -\alpha^x_{i-1,j }}{4} + \alpha^x_{i,j},
\displaystyle B_{i,j} = \displaystyle \frac{\alpha^y_{i, j+1}-\alpha^y_{i ,j-1}}{4} + \alpha^y_{i,j}
}
\end{equation}
$s_x(i,j) = \frac{\alpha(i,j)*\frac{t}{2}}{\Delta x^2}$ where $x$ defining the domain size in x direction.
\noindent Plugging eq. ([[eqn:hetdiff_fd]]) into the first of equations
([[eqn:genADI_het]]) - so called "sweep by x" - and putting all implicit
terms (at time level $t+1/2$) on the left hand side we obtain:
For the sake of simplicity we assume that each row of the $A$ matrix is addressed correctly with the given offset.
#+NAME: eqn:sweepX_het
\begin{equation}\displaystyle
\begin{split}
-S_x A_{i,j} C^{t+1/2}_{i+1,j} + (1 + 2S_x\alpha^x_{i,j})C^{t+1/2}_{i,j} - S_x A_{i,j}C^{t+1/2}_{i-1,j} = \\
S_y B_{i,j} C^{t }_{i,j+1} + (1 - 2S_y\alpha^y_{i,j})C^{t }_{i,j} + S_y B_{i,j}C^{t }_{i,j-1}
\end{split}
\end{equation}
**** Ghost nodes
\noindent In the same way for the second of eq. [[eqn:genADI_het]] we have:
$A(i,-1) = 1$
#+NAME: eqn:sweepY_het
\begin{equation}\displaystyle
\begin{split}
-S_y B_{i,j} C^{t+1 }_{i,j+1} + (1 + 2S_y\alpha^y_{i,j})C^{t+1 }_{i,j} - S_y B_{i,j}C^{t+1 }_{i,j-1} = \\
S_x A_{i,j} C^{t+1/2}_{i+1,j} + (1 - 2S_x\alpha^x_{i,j})C^{t+1/2}_{i,j} + S_x A_{i,j}C^{t+1/2}_{i-1,j}
\end{split}
\end{equation}
$A(i,N) = 1$
\noindent If the diffusion coefficients are constant,
$A_{i,j}=B_{i,j}=\alpha$ and the scheme reverts to the homogeneous
case. Problem with this discretization is that the terms in $A_{ij}$
and $B_{ij}$ can be negative depending on the derivative of the
diffusion coefficient, resulting in unphysical values for the
concentrations.
**** Inlet
** Direct discretization
$A(i,j) = \begin{cases}
1 & \text{if } bc(i,j) = \text{constant} \\
-1-2*s_x(i,j) & \text{else}
\end{cases}$
As noted in literature (LeVeque and Numerical Recipes) a better way is
to discretize directly the physical problem (eq. [[eqn:hetdiff]]) at
points halfway between grid points:
$A(i,j\pm 1) = \begin{cases}
0 & \text{if } bc(i,j) = \text{constant} \\
s_x(i,j) & \text{else}
\end{cases}$
\begin{align*}
\begin{cases}
\displaystyle \alpha(x_{i+1/2}) \frac{\partial C }{\partial x}(x_{i+1/2}) & \displaystyle = \alpha_{i+1/2} \left( \frac{C_{i+1} -C_{i}}{\Delta x} \right) \\
\displaystyle \alpha(x_{i-1/2}) \frac{\partial C }{\partial x}(x_{i-1/2}) & \displaystyle = \alpha_{i-1/2} \left( \frac{C_{i} -C_{i-1}}{\Delta x} \right)
\end{cases}
\end{align*}
** Filling of vector $b$
\noindent A further differentiation gives us the spatially centered
approximation of $\frac{\partial}{\partial x} \left(\alpha(x)
\frac{\partial C }{\partial x}\right)$:
- each elements assign a concrete value to the according value of the row of matrix $A$
- Adressing would look like this: $(i,j) = b(i \cdot (N+2) + j)$
- $\rightarrow$ for simplicity we will write $b(i,j)$
#+NAME: eqn:CS_het
\begin{equation}
\begin{aligned}
\frac{\partial}{\partial x} \left(\alpha(x)
\frac{\partial C }{\partial x}\right)(x_i) & \simeq \frac{1}{\Delta x}\left[\alpha_{i+1/2} \left( \frac{C_{i+1} -C_{i}}{\Delta x} \right) - \alpha_{i-1/2} \left( \frac{C_{i} -C_{i-1}}{\Delta x} \right) \right]\\
&\displaystyle =\frac{1}{\Delta x^2} \left[ \alpha_{i+1/2}C_{i+1} - (\alpha_{i+1/2}+\alpha_{i-1/2}) C_{i} + \alpha_{i-1/2}C_{i-1}\right]
\end{aligned}
\end{equation}
*** Rules
\noindent The ADI scheme with this approach becomes:
**** Ghost nodes
#+NAME: eqn:genADI_hetdir
\begin{equation}
\left\{
\begin{aligned}
\frac{C^{t+1/2}_{i,j}-C^{t }_{i,j}}{\Delta t/2} = & \frac{1}{\Delta x^2} \left[ \alpha_{i+1/2,j} C^{t+1/2}_{i+1,j} - (\alpha_{i+1/2,j}+\alpha_{i-1/2,j}) C^{t+1/2}_{i,j} + \alpha_{i-1/2,j} C^{t+1/2}_{i-1,j}\right] + \\
& \frac{1}{\Delta y^2} \left[ \alpha_{i,j+1/2}C^{t}_{i,j+1} - (\alpha_{i,j+1/2}+\alpha_{i,j-1/2}) C^t_{i,j} + \alpha_{i,j-1/2}C^{t}_{i,j-1}\right]\\
\frac{C^{t+1 }_{i,j}-C^{t+1/2}_{i,j}}{\Delta t/2} = & \frac{1}{\Delta y^2} \left[ \alpha_{i+1/2,j}C^{t+1/2}_{i+1,j} - (\alpha_{i+1/2,j}+\alpha_{i-1/2,j}) C^t_{i,j} + \alpha_{i-1/2,j}C^{t+1/2}_{i-1,j}\right] + \\
& \frac{1}{\Delta x^2} \left[ \alpha_{i,j+1/2}C^{t+1}_{i,j+1} - (\alpha_{i,j+1/2}+\alpha_{i,j-1/2}) C^{t+1}_{i,j} + \alpha_{i,j-1/2}C^{t+1}_{i,j-1}\right]
\end{aligned}
\right.
\end{equation}
$b(i,-1) = \begin{cases}
0 & \text{if } bc(i,0) = \text{constant} \\
c(i,0) & \text{else}
\end{cases}$
\noindent Doing the usual algebra and separating implicit from
explicit terms, the two sweeps become:
$b(i,N) = \begin{cases}
0 & \text{if } bc(i,N-1) = \text{constant} \\
c(i,N-1) & \text{else}
\end{cases}$
#+NAME: eqn:sweepX_hetdir
\begin{equation}
\left\{
\begin{aligned}
-S_x \alpha^x_{i+1/2,j} C^{t+1/2}_{i+1,j} + (1 + S_x(\alpha^x_{i+1/2,j}+ \alpha^x_{i-1/2,j}))C^{t+1/2}_{i,j} - S_x \alpha^x_{i-1/2,j} C^{t+1/2}_{i-1,j} = \quad & \\
S_y \alpha^y_{i,j+1/2} C^{t }_{i,j+1} + (1 - S_y(\alpha^y_{i,j+1/2}+ \alpha^y_{i,j-1/2}))C^{t }_{i,j} + S_y \alpha^y_{i,j-1/2} C^{t }_{i,j-1} & \\[1em]
-S_y \alpha^y_{i,j+1/2} C^{t+1 }_{i,j+1} + (1 + S_y(\alpha^y_{i,j+1/2}+ \alpha^y_{i,j-1/2}))C^{t+1 }_{i,j} - S_y \alpha^y_{i,j-1/2} C^{t+1 }_{i,j-1} = \qquad & \\
S_x \alpha^x_{i+1/2,j} C^{t+1/2}_{i+1,j} + (1 - S_x(\alpha^x_{i+1/2,j}+ \alpha^x_{i-1/2,j}))C^{t+1/2}_{i,j} + S_x \alpha^x_{i-1/2,j} C^{t+1/2}_{i-1,j}
\end{aligned}
\right.
\end{equation}
*** Inlet
\bigskip
$p(i,j) = \frac{\Delta t}{2}\alpha(i,j)\frac{c(i-1,j) - 2\cdot c(i,j) + c(i+1,j)}{\Delta x^2}$
\noindent The "interblock" diffusion coefficients $\alpha_{i+1/2,j}$
can be arithmetic mean:
\noindent $p$ is called =t0_c= inside code
\[
\displaystyle \alpha_{i+1/2, j} = \displaystyle \frac{\alpha_{i+1, j} + \alpha_{i, j}}{2}
\]
$b(i,j) = \begin{cases}
bc(i,j).\text{value} & \text{if } bc(i,N-1) = \text{constant} \\
-c(i,j)-p(i,j) & \text{else}
\end{cases}$
\noindent or the harmonic mean:
\[
\displaystyle \alpha_{i+1/2, j} = \displaystyle \frac{2}{\frac{1}{\alpha_{i+1, j}} + \frac{1}{\alpha_{i, j}}}
\]
\pagebreak
* Explicit scheme for 2D heterogeneous diffusion
A classical explicit FTCS scheme (forward in time, central in space)
for 2D heterogeneous diffusion can be expressed simply leveraging the
discretization of equation [[eqn:CS_het]]:
#+NAME: eqn:2DHeterFTCS
\begin{equation}
\begin{aligned}
\frac{C_{i,j}^{t+1} - C_{i,j}^{t}}{\Delta t} = & \frac{1}{\Delta x^2} \left[ \alpha^x_{i+1/2, j}C^t_{i+1, j} - (\alpha^x_{i+1/2, j} + \alpha^x_{i-1/2, j}) C^t_{i,j} + \alpha^x_{i-1/2,j}C^t_{i-1,j}\right] + \\
& \frac{1}{\Delta y^2} \left[ \alpha^y_{i, j+1/2}C^t_{i, j+1} - (\alpha^y_{i, j+1/2} + \alpha^y_{i, j-1/2}) C^t_{i,j} + \alpha^y_{i,j-1/2}C^t_{i,j-1}\right]
\end{aligned}
\end{equation}
\noindent where in the RHS only the known concentrations at time $t$
appear. Rearranging the terms, we get:
#+NAME: eqn:2DHeterFTCS_final
\begin{equation}
\begin{aligned}
C_{i,j}^{t+1} = & C^t_{i,j} +\\
& \frac{\Delta t}{\Delta x^2} \left[ \alpha^x_{i+1/2, j}C^t_{i+1, j} - (\alpha^x_{i+1/2, j} + \alpha^x_{i-1/2, j}) C^t_{i,j} + \alpha^x_{i-1/2,j}C^t_{i-1,j}\right] + \\
& \frac{\Delta t}{\Delta y^2} \left[ \alpha^y_{i, j+1/2}C^t_{i, j+1} - (\alpha^y_{i, j+1/2} + \alpha^y_{i, j-1/2}) C^t_{i,j} + \alpha^y_{i,j-1/2}C^t_{i,j-1}\right]
\end{aligned}
\end{equation}
The Courant-Friedrichs-Lewy stability criterion (cfr Lee, 2017) for
this scheme reads:
#+NAME: eqn:CFL2DFTCS_Lee
\begin{equation}
\Delta t \leq \frac{1}{2 \max(\alpha_{i,j})} \cdot \frac{1}{\frac{1}{\Delta x^2} + \frac{1}{\Delta y^2}}
\end{equation}
Note that other derivations for the CFL condition are found in
literature. For example, the sources cited by [[https://en.wikipedia.org/wiki/FTCS_scheme][Wikipedia solution]] give:
#+NAME: eqn:CFL2DFTCS_wiki
\begin{equation}
\displaystyle \Delta t\leq {\frac {1}{4 \max(\alpha) \left({\frac {1}{\Delta x^{2}}}+{\frac {1}{\Delta y^{2}}}\right)}}
\end{equation}
We can produce a more restrictive condition than equation
[[eqn:CFL2DFTCS_Lee]] by considering the min of the $\Delta x$ and $\Delta
y$:
#+NAME: eqn:CFL2DFTCS
\begin{equation}
\Delta t \leq \frac{\min(\Delta x, \Delta y)^2}{4 \max(\alpha_{i,j})}
\end{equation}
In practice for the implementation it is advantageous to specify an
optional parameter $C$, $C \in [0, 1]$ so that the user can restrict
the "inner time stepping":
#+NAME: eqn:CFL2DFTCS_impl
\begin{equation}
\Delta t \leq C \cdot \frac{\min(\Delta x, \Delta y)^2}{4 \max(\alpha_{i,j})}
\end{equation}
** Boundary conditions
In analogy to the treatment of the 1D homogeneous FTCS scheme (cfr
section 1), we need to differentiate the domain boundaries ($i=0$ and
$i=n_x$; the same applies to $j$ of course) accounting for the
discrepancy in the discretization.
For the zero-th (left) cell, whose center is at $x=dx/2$, we can
evaluate the left gradient with the left boundary using such distance,
calling $l$ the numerical value of a constant boundary condition,
equation [[eqn:CS_het]] becomes:
#+NAME: eqn:2D_FTCS_left
\begin{equation}
\begin{aligned}
\frac{\partial}{\partial x} \left(\alpha(x)
\frac{\partial C }{\partial x}\right)(x_0) & \simeq \frac{1}{\Delta x}\left[\alpha_{i+1/2} \left( \frac{C_{i+1} -C_{i}}{\Delta x} \right) - \alpha_{i} \left( \frac{C_{i} - l }{\frac{\Delta x}{2}} \right) \right]\\
&\displaystyle =\frac{1}{\Delta x^2} \left[ \alpha_{i+1/2}C_{i+1} - (\alpha_{i+1/2}+ 2\alpha_i) C_{i} + 2 \alpha_{i}\cdot l\right]
\end{aligned}
\end{equation}
\noindent Similarly, for $i=n_x$,
#+NAME: eqn:2D_FTCS_right
\begin{equation}
\begin{aligned}
\frac{\partial}{\partial x} \left(\alpha(x)
\frac{\partial C }{\partial x}\right)(x_n) & \simeq \frac{1}{\Delta x}\left[\alpha_{i} \left( \frac{r -C_{i}}{\frac{\Delta x}{2}} \right) - \alpha_{i-1/2} \left( \frac{C_{i} - C_{i-1}} {\Delta x} \right) \right]\\
&\displaystyle =\frac{1}{\Delta x^2} \left[ 2 \alpha_{i} r - (\alpha_{i+1/2}+ 2\alpha_i) C_{i} + \alpha_{i-1/2}\cdot C_{i-1}\right]
\end{aligned}
\end{equation}
If on the right boundary we have *closed* or Neumann condition, the
left derivative becomes zero and we are left with:
#+NAME: eqn:2D_FTCS_rightclosed
\begin{equation}
\begin{aligned}
\frac{\partial}{\partial x} \left(\alpha(x)
\frac{\partial C }{\partial x}\right)(x_n) & \simeq \frac{1}{\Delta x}\left[\cancel{\alpha_{i+1/2} \left( \frac{C_{i+1} -C_{i}}{\Delta x} \right)} - \alpha_{i-1/2} \left( \frac{C_{i} -C_{i-1}}{\Delta x} \right) \right]\\
&\displaystyle =\frac{\alpha_{i-1/2}}{\Delta x^2} (C_{i-1} - C_i)
\end{aligned}
\end{equation}

BIN
doc/ADI_scheme.pdf Normal file

Binary file not shown.

115
doc/ValidationHetDiff.org Normal file
View File

@ -0,0 +1,115 @@
#+TITLE: Validation Examples for 2D Heterogeneous Diffusion
#+AUTHOR: MDL <delucia@gfz-potsdam.de>
#+DATE: 2023-08-26
#+STARTUP: inlineimages
#+LATEX_CLASS_OPTIONS: [a4paper,9pt]
#+LATEX_HEADER: \usepackage{fullpage}
#+LATEX_HEADER: \usepackage{amsmath, systeme}
#+LATEX_HEADER: \usepackage{graphicx}
#+LATEX_HEADER: \usepackage{charter}
#+OPTIONS: toc:nil
* Simple setup using deSolve/ReacTran
- Square of side 10
- Discretization: 11 \times 11 cells
- All boundaries closed
- Initial state: 0 everywhere, 1 in the center (6,6)
- Time step: 1 s, 10 iterations
The matrix of spatially variable diffusion coefficients \alpha is
constant in 4 quadrants:
\alpha_{x,y} =
| 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |
| 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |
| 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |
| 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |
| 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.01 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
The relevant part of the R script used to produce these results is
presented in listing 1; the whole script is at [[file:scripts/HetDiff.R]].
A visualization of the output of the reference simulation is given in
figure [[fig:1][1]].
Note: all results from this script are stored in the =outc= matrix by
the =deSolve= function. I stored a different version into
[[file:../scripts/gold/HetDiff1.csv]]: this file contains 11 columns (one
for each time step including initial conditions) and 121 rows, one for
each domain element, with no headers.
#+caption: Result of ReacTran/deSolve solution of the above problem at 4
#+name: fig:1
[[./images/deSolve_AlphaHet1.png]]
#+name: lst:1
#+begin_src R :language R :frame single :caption Listing 1, generate reference simulation using R packages deSolve/ReacTran :captionpos b :label lst:1
library(ReacTran)
library(deSolve)
## harmonic mean
harm <- function(x,y) {
if (length(x) != 1 || length(y) != 1)
stop("x & y have different lengths")
2/(1/x+1/y)
}
N <- 11 # number of grid cells
ini <- 1 # initial value at x=0
N2 <- ceiling(N/2)
L <- 10 # domain side
## Define diff.coeff per cell, in 4 quadrants
alphas <- matrix(0, N, N)
alphas[1:N2, 1:N2] <- 1
alphas[1:N2, seq(N2+1,N)] <- 0.1
alphas[seq(N2+1,N), 1:N2] <- 0.01
alphas[seq(N2+1,N), seq(N2+1,N)] <- 0.001
cmpharm <- function(x) {
y <- c(0, x, 0)
ret <- numeric(length(x)+1)
for (i in seq(2, length(y))) {
ret[i-1] <- harm(y[i], y[i-1])
}
ret
}
## Construction of the 2D grid
x.grid <- setup.grid.1D(x.up = 0, L = L, N = N)
y.grid <- setup.grid.1D(x.up = 0, L = L, N = N)
grid2D <- setup.grid.2D(x.grid, y.grid)
dx <- dy <- L/N
D.grid <- list()
## Diffusion coefs on x-interfaces
D.grid$x.int <- apply(alphas, 1, cmpharm)
## Diffusion coefs on y-interfaces
D.grid$y.int <- t(apply(alphas, 2, cmpharm))
# The model
Diff2Dc <- function(t, y, parms) {
CONC <- matrix(nrow = N, ncol = N, data = y)
dCONC <- tran.2D(CONC, dx = dx, dy = dy, D.grid = D.grid)$dC
return(list(dCONC))
}
## initial condition: 0 everywhere, except in central point
y <- matrix(nrow = N, ncol = N, data = 0)
y[N2, N2] <- ini # initial concentration in the central point...
## solve for 10 time units
times <- 0:10
outc <- ode.2D(y = y, func = Diff2Dc, t = times, parms = NULL,
dim = c(N, N), lrw = 1860000)
#+end_src

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

2629
doc/images/tug_logo.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

8
docs_sphinx/Boundary.rst Normal file
View File

@ -0,0 +1,8 @@
Boundary
========
.. doxygenenum:: tug::BC_TYPE
.. doxygenenum:: tug::BC_SIDE
.. doxygenclass:: tug::Boundary
.. doxygenclass:: tug::BoundaryElement

10
docs_sphinx/Diffusion.rst Normal file
View File

@ -0,0 +1,10 @@
Diffusion
==========
.. doxygenenum:: tug::APPROACH
.. doxygenenum:: tug::SOLVER
.. doxygenenum:: tug::CSV_OUTPUT
.. doxygenenum:: tug::CONSOLE_OUTPUT
.. doxygenenum:: tug::TIME_MEASURE
.. doxygenclass:: tug::Diffusion

2771
docs_sphinx/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

20
docs_sphinx/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

98
docs_sphinx/conf.py Normal file
View File

@ -0,0 +1,98 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from sphinx.builders.html import StandaloneHTMLBuilder
import subprocess, os
# Doxygen
subprocess.call('doxygen Doxyfile.in', shell=True)
# -- Project information -----------------------------------------------------
project = 'TUG'
copyright = 'GPL2'
author = 'Philipp Ungrund, Hannes Signer'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.autosectionlabel',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx.ext.inheritance_diagram',
'breathe',
'sphinx_mdinclude'
]
html_baseurl = "/index.html"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
highlight_language = 'c++'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'canonical_url': '',
'analytics_id': '', # Provided by Google in your dashboard
'display_version': True,
'prev_next_buttons_location': 'bottom',
'style_external_links': False,
'logo_only': False,
# Toc options
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False
}
# html_logo = ''
# github_url = ''
# html_baseurl = ''
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_logo = "images/tug_logo.svg"
# -- Breathe configuration -------------------------------------------------
breathe_projects = {
"Tug": "_build/xml/"
}
breathe_default_project = "Tug"
breathe_default_members = ('members', 'undoc-members')

View File

@ -0,0 +1 @@
.. mdinclude:: ../CONTRIBUTORS.md

38
docs_sphinx/developer.rst Normal file
View File

@ -0,0 +1,38 @@
Developer Guide
===============
=========================
Class Diagram of user API
=========================
The following graphic shows the class diagram of the user API. The FTCS and
BTCS functionalities are externally outsourced and not visible to the user.
.. image:: images/class_diagram.svg
:width: 2000
:alt: Class diagram for the user API
====================================================
Activity Diagram for run routine in simulation class
====================================================
The following activity diagram represents the actions when the run method is called within the simulation class.
For better distinction, the activities of the calculation methods FTCS and BTCS are shown in two separate activity diagrams.
.. image:: images/activity_diagram_run.svg
:width: 2000
:alt: Activity diagram for the run method in the simulation class
**Activity Diagram for FTCS method**
.. image:: images/activity_diagram_FTCS.svg
:width: 400
:alt: Activity diagram for the FTCS method
**Activity Diagram for BTCS method**
.. image:: images/activity_diagram_BTCS.svg
:width: 400
:alt: Activity diagram for the BTCS method

68
docs_sphinx/examples.rst Normal file
View File

@ -0,0 +1,68 @@
Examples
========
At this point, some typical commented examples are presented to illustrate how Tug works.
In general, each simulation is divided into three blocks:
- the initialization of the grid, which is to be simulated
- the setting of the boundary conditions
- the setting of the simulation parameters and the start of the simulation
Two dimensional grid with constant boundaries and FTCS method
-------------------------------------------------------------
**Initialization of the grid**
For example, the initalization of a grid with 20 by 20 cells using double values
and a domain size (physical extent of the grid) of also 20 by 20 length units
can be done as follows. The setting of the domain is optional here and is set to
the same size as the number of cells in the standard case. As seen in the code,
the cells of the grid are set to an initial value of 0 and only in the upper
left corner (0,0) the starting concentration is set to the value 20.
.. code-block:: cpp
int row = 20
int col = 20;
Grid<double> grid(row,col);
grid.setDomain(row, col);
MatrixXd concentrations = MatrixXd::Constant(row,col,0);
// or MatrixX<double> concentrations = MatrixX<double>::Constant(row,col,0);
concentrations(0,0) = 20;
grid.setConcentrations(concentrations);
**Setting of the boundary conditions**
First, a boundary class is created and then the corresponding boundary conditions are set. In this case, all four sides
of the grid are set as constant edges with a concentration of 0.
.. code-block:: cpp
Boundary bc = Boundary(grid);
bc.setBoundarySideConstant(BC_SIDE_LEFT, 0);
bc.setBoundarySideConstant(BC_SIDE_RIGHT, 0);
bc.setBoundarySideConstant(BC_SIDE_TOP, 0);
bc.setBoundarySideConstant(BC_SIDE_BOTTOM, 0);
**Setting of the simulation parameters and simulation start**
In the last block, a simulation class is created and the objects of the grid and
the boundary conditions are passed. The solution method is also specified
(either FCTS or BTCS). Furthermore, the desired time step and the number of
iterations are set. The penultimate parameter specifies the output of the
simulated results in a CSV file. In the present case, the result of each
iteration step is written one below the other into the corresponding CSV file.
.. code-block:: cpp
Simulation<double, FTCS_APPROACH> simulation(grid, bc);
simulation.setTimestep(0.1);
simulation.setIterations(1000);
simulation.setOutputCSV(CSV_OUTPUT_VERBOSE);
simulation.run();
Setting special boundary conditions on individual cells
-------------------------------------------------------

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 126 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,563 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg1"
width="650.3999"
height="226.6021"
viewBox="0 0 650.3999 226.60209"
sodipodi:docname="discretization_1D.pdf"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2">
<path
d="M 0,0 H 1129.1666 V 393.40646 H 0 Z"
transform="scale(0.33298893)"
clip-rule="evenodd"
id="path2" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path4" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath6">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path6" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath8">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path8" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath10">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path10" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath12">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path12" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath14">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-67.109375,-65.999999)"
clip-rule="evenodd"
id="path14" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-127.10937,-65.999999)"
clip-rule="evenodd"
id="path16" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath18">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-187.10938,-65.999999)"
clip-rule="evenodd"
id="path18" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath20">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-247.10938,-65.999999)"
clip-rule="evenodd"
id="path20" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath22">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-307.10938,-65.999999)"
clip-rule="evenodd"
id="path22" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath24">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(22.390179,0,0,-22.480592,-4236.1206,1319.6518)"
clip-rule="evenodd"
id="path24" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath26">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.669276,0,0,-31.797165,-6847.4122,2078.7157)"
clip-rule="evenodd"
id="path26" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath28">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(22.390179,0,0,-24.643363,-8270.5209,1404.1114)"
clip-rule="evenodd"
id="path28" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath30">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.669276,0,0,-34.85626,-12553.777,2198.1785)"
clip-rule="evenodd"
id="path30" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath32">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(22.390179,0,0,-24.643363,-12304.921,1404.1114)"
clip-rule="evenodd"
id="path32" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath34">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.669276,0,0,-34.85626,-18260.142,2198.1785)"
clip-rule="evenodd"
id="path34" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath36">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(22.390179,0,0,-22.480592,-16339.322,1319.6518)"
clip-rule="evenodd"
id="path36" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath38">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.669276,0,0,-31.797165,-23966.51,2078.7157)"
clip-rule="evenodd"
id="path38" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath40">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(22.390179,0,0,-24.643363,-20373.722,1404.1114)"
clip-rule="evenodd"
id="path40" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath42">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.669276,0,0,-34.85626,-29672.875,2198.1785)"
clip-rule="evenodd"
id="path42" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath44">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path44" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath46">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path46" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath48">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path48" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath50">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.193035,0,0,-34.583265,-14238.804,12659.567)"
clip-rule="evenodd"
id="path50" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath52">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(31.193035,0,0,-34.583265,-15071.803,12659.567)"
clip-rule="evenodd"
id="path52" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath54">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path54" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath56">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path56" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath58">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
clip-rule="evenodd"
id="path58" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath60">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(32.334989,0,0,-32.96522,-4686.8436,11507.254)"
clip-rule="evenodd"
id="path60" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath62">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(32.334989,0,0,-32.96522,-5519.8439,11507.254)"
clip-rule="evenodd"
id="path62" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath64">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(32.334989,0,0,-32.96522,-5139.344,12869.254)"
clip-rule="evenodd"
id="path64" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath66">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(32.334989,0,0,-32.96522,-4466.8437,12183.255)"
clip-rule="evenodd"
id="path66" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath68">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(19.846142,0,0,-26.124175,-1668.8001,5166.3952)"
clip-rule="evenodd"
id="path68" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath70">
<path
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
transform="matrix(21.454004,0,0,-25.15296,-22356.717,4974.3261)"
clip-rule="evenodd"
id="path70" />
</clipPath>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1" />
<g
id="g1"
inkscape:groupmode="layer"
inkscape:label="1"
transform="translate(144.53332,50.356026)">
<path
id="path1"
d="M 0,0 H 376 V 131 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
transform="matrix(1.7297871,0,0,1.7297871,-144.53332,-50.356024)"
clip-path="url(#clipPath2)" />
<path
id="path3"
d="m 40,32 h 60 V 92 H 40 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath4)" />
<path
id="path5"
d="m 100,32 h 60 v 60 h -60 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath6)" />
<path
id="path7"
d="m 160,32 h 60 v 60 h -60 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath8)" />
<path
id="path9"
d="m 220,32 h 60 v 60 h -60 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath10)" />
<path
id="path11"
d="m 280,32 h 60 v 60 h -60 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath12)" />
<path
id="path13"
d="M 3.1386108,-0.82895751 V -3.7556182 H 0.21195013 V -4.2271357 H 3.1386108 v -2.9104015 h 0.4715176 v 2.9104015 H 6.536789 v 0.4715175 H 3.6101284 v 2.92666069 z m 0,0"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
aria-label="+"
transform="matrix(1.7297871,0,0,1.7297871,-28.448384,63.809926)"
clip-path="url(#clipPath14)" />
<path
id="path15"
d="M 3.1392194,-0.82895751 V -3.7556182 H 0.21255876 V -4.2271357 H 3.1392194 V -7.1375372 H 3.610737 v 2.9104015 h 2.9266607 v 0.4715175 H 3.610737 v 2.92666069 z m 0,0"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
aria-label="+"
transform="matrix(1.7297871,0,0,1.7297871,75.338842,63.809926)"
clip-path="url(#clipPath16)" />
<path
id="path17"
d="M 3.1398231,-0.82895751 V -3.7556182 H 0.21316239 V -4.2271357 H 3.1398231 v -2.9104015 h 0.4715175 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6113406 v 2.92666069 z m 0,0"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
aria-label="+"
transform="matrix(1.7297871,0,0,1.7297871,179.12609,63.809926)"
clip-path="url(#clipPath18)" />
<path
id="path19"
d="M 3.1363669,-0.82895751 V -3.7556182 H 0.20970622 V -4.2271357 H 3.1363669 v -2.9104015 h 0.4715175 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6078844 v 2.92666069 z m 0,0"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
aria-label="+"
transform="matrix(1.7297871,0,0,1.7297871,282.91331,63.809926)"
clip-path="url(#clipPath20)" />
<path
id="path21"
d="M 3.1369755,-0.82895751 V -3.7556182 H 0.21031485 V -4.2271357 H 3.1369755 v -2.9104015 h 0.4715176 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6084931 v 2.92666069 z m 0,0"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
aria-label="+"
transform="matrix(1.7297871,0,0,1.7297871,386.70054,63.809926)"
clip-path="url(#clipPath22)" />
<path
id="path23"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1337"
transform="matrix(0.02572556,0,0,-0.0256221,-35.556726,-16.543775)"
clip-path="url(#clipPath24)" />
<path
id="path25"
d="m 96,585 c 37.33334,54 88.33333,81 153,81 32,0 64,-8.66669 96,-26 32,-17.33331 58,-48 78,-92 24.66666,-55.33334 37,-131.33331 37,-228 C 460,216.66666 445.66666,137.66667 417,83 403.66666,55 385.33334,32.666668 362,16 338.66666,-0.66666794 318.33334,-11 301,-15 c -17.33334,-4 -34.33334,-6.333334 -51,-7 -17.33333,0 -34.66667,2 -52,6 -17.33333,4 -37.66667,14.666666 -61,32 C 113.66666,33.333336 95.333336,55.666664 82,83 53.333332,137.66667 39,216.66666 39,320 39,436 58,524.33331 96,585 Z m 225,12 c -20,21.33331 -43.66666,32 -71,32 -28,0 -52,-10.66669 -72,-32 -16.66667,-17.33331 -27.66667,-41.33331 -33,-72 -5.33333,-30.66666 -8,-94.66666 -8,-192 0,-105.33334 2.66667,-174.66666 8,-208 5.33333,-33.333336 17.33333,-59.666664 36,-79 18.66667,-20 41.66667,-30 69,-30 26.66666,0 49.33334,10 68,30 19.33334,20 31.33334,48 36,84 4.66666,36 7.33334,103.66666 8,203 0,96.66669 -2.66666,160.33334 -8,191 -5.33334,30.66669 -16.33334,55 -33,73 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0945"
transform="matrix(0.01818797,0,0,-0.01811482,-19.992763,-12.70046)"
clip-path="url(#clipPath26)" />
<path
id="path27"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
transform="matrix(0.02572556,0,0,-0.02337343,68.230498,-17.537123)"
clip-path="url(#clipPath28)" />
<path
id="path29"
d="m 213,578 -13,-5 c -9.33333,-3.33331 -22.66667,-6.66669 -40,-10 -17.33333,-3.33331 -36.66666,-5.66669 -58,-7 H 83 v 46 h 19 c 31.33333,1.33331 60.33333,6.33331 87,15 26.66667,8.66669 45.33333,16.66669 56,24 10.66667,7.33331 20,14.66669 28,22 1.33334,2 5.33334,3 12,3 6,0 11.66666,-2 17,-6 V 361 l 1,-300 c 4.66666,-4.666668 8.66666,-7.666668 12,-9 3.33334,-1.333332 11.33334,-2.666668 24,-4 12.66666,-1.333332 33.33334,-2 62,-2 h 26 V 0 H 416 C 402,2 349,3 257,3 166.33333,3 114,2 100,0 H 88 v 46 h 26 c 14.66667,0 27.33333,0 38,0 10.66667,0 19,0.333332 25,1 6,0.666668 11.33333,1.666668 16,3 4.66667,1.333332 7.33333,2 8,2 0.66667,0 2.66667,1.666668 6,5 3.33333,3.333332 5.33333,4.666668 6,4 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
transform="matrix(0.01818797,0,0,-0.01652501,83.79445,-14.031106)"
clip-path="url(#clipPath30)" />
<path
id="path31"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
transform="matrix(0.02572556,0,0,-0.02337343,172.01772,-17.537123)"
clip-path="url(#clipPath32)" />
<path
id="path33"
d="m 109,429 c -18,0 -32.333336,6 -43,18 -10.666668,12 -16,26.66666 -16,44 0,47.33331 17.666664,88.33331 53,123 35.33334,34.66669 79.33333,52 132,52 60.66666,0 111.33334,-18.66669 152,-56 40.66666,-37.33331 61.33334,-85.66669 62,-145 0,-28.66666 -6.66666,-56 -20,-82 -13.33334,-26 -29.33334,-48.66666 -48,-68 -18.66666,-19.33334 -45.33334,-44 -80,-74 -24,-20.66667 -57.33333,-51.33333 -100,-92 l -59,-56 76,-1 c 104.66669,0 160.33334,1.666664 167,5 4.66666,1.333336 12.66666,31 24,89 v 3 h 40 v -3 C 448.33334,184 444,153.66667 436,95 428,36.333332 423,5.666667 421,3 V 0 H 50 v 19 12 c 0,4.666668 2,9.666668 6,15 4,5.333332 14,17 30,35 19.33334,21.33334 36,40 50,56 6,6.66667 17.33333,19 34,37 16.66667,18 28,30.33333 34,37 6,6.66667 15.66667,17.66667 29,33 13.33333,15.33334 22.66667,26.66666 28,34 5.33334,7.33334 13,17.33334 23,30 10,12.66666 17,23.33334 21,32 4,8.66666 9,18.33334 15,29 6,10.66666 10.33334,21.33334 13,32 2.66666,10.66666 5,20.66666 7,30 2,9.33334 3,20.33334 3,33 0,42 -11.33334,78.33331 -34,109 -22.66666,30.66669 -55,46 -97,46 -22,0 -41.33333,-5.66669 -58,-17 -16.66667,-11.33331 -28.33334,-22.33331 -35,-33 -6.66666,-10.66669 -10,-17 -10,-19 0,-0.66669 1.66666,-1 5,-1 12,0 24.33333,-4.66669 37,-14 12.66667,-9.33331 19,-24.66666 19,-46 0,-16.66666 -5.33333,-30.66666 -16,-42 -10.66667,-11.33334 -25.66667,-17.33334 -45,-18 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
transform="matrix(0.01818797,0,0,-0.01652501,187.58168,-14.031106)"
clip-path="url(#clipPath34)" />
<path
id="path35"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1337"
transform="matrix(0.02572556,0,0,-0.0256221,275.80495,-16.543775)"
clip-path="url(#clipPath36)" />
<path
id="path37"
d="m 127,463 c -18,0 -32,5.66666 -42,17 -10,11.33334 -15.333336,26 -16,44 0,36.66669 16,69.33331 48,98 32,28.66669 70.66666,43 116,43 23.33334,0 38,-0.33331 44,-1 49.33334,-8 87,-25.66669 113,-53 26,-27.33331 39.33334,-57 40,-89 0,-34.66666 -11.33334,-68.33334 -34,-101 -22.66666,-32.66666 -54,-56.33334 -94,-71 l -3,-2 c 0,-0.66666 3,-1.66666 9,-3 6,-1.33334 15.66666,-4.33334 29,-9 13.33334,-4.66666 26,-11.66666 38,-21 54.66666,-35.33334 82,-82 82,-140 C 457,122.33333 436.33334,76.333336 395,37 353.66666,-2.3333359 301.33334,-22 238,-22 184.66666,-22 138.66667,-7.6666679 100,21 61.333332,49.666668 42,86 42,130 c 0,18.66667 6,33.66667 18,45 12,11.33333 27,17.33333 45,18 18.66667,0 34,-6 46,-18 12,-12 18,-27 18,-45 0,-7.33334 -1,-14 -3,-20 -2,-6 -4.33333,-11.333336 -7,-16 -2.66667,-4.666664 -6.33333,-8.666664 -11,-12 -4.66667,-3.333336 -8.66667,-6 -12,-8 -3.33333,-2 -6.66667,-3.333336 -10,-4 -3.33334,-0.666664 -6,-1.666664 -8,-3 l -4,-1 c 34,-30 75.33333,-45 124,-45 36.66666,0 64.33334,17.666664 83,53 11.33334,22 17,55.66666 17,101 v 20 c 0,63.33334 -21.33334,105.66666 -64,127 -10,4 -30.33333,6.33334 -61,7 l -42,1 -3,2 c -1.33333,2 -2,7.33334 -2,16 0,12 2.66667,18 8,18 18.66667,0 38,1.66666 58,5 22.66667,3.33334 43.33334,17.33334 62,42 18.66666,24.66666 28,62 28,112 v 8 c 0,38 -11.66666,64.33331 -35,79 -14.66666,9.33331 -30.33334,14 -47,14 -21.33333,0 -41,-3.66669 -59,-11 -18,-7.33331 -30.66667,-15 -38,-23 -7.33333,-8 -11,-12 -11,-12 h 3 c 2,-0.66669 4.66667,-1.33331 8,-2 3.33333,-0.66669 6.66667,-2.33331 10,-5 3.33333,-2.66669 7.33333,-5 12,-7 4.66667,-2 8,-5.66669 10,-11 2,-5.33331 4.66667,-10.33331 8,-15 3.33333,-4.66669 4.33333,-11.33331 3,-20 0,-14.66666 -4.66667,-27.66666 -14,-39 -9.33333,-11.33334 -24.33333,-17.33334 -45,-18 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0945"
transform="matrix(0.01818797,0,0,-0.01811482,291.36894,-12.70046)"
clip-path="url(#clipPath38)" />
<path
id="path39"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
transform="matrix(0.02572556,0,0,-0.02337343,379.59218,-17.537123)"
clip-path="url(#clipPath40)" />
<path
id="path41"
d="M 462,0 C 450,2 407,3 333,3 255.66666,3 211,2 199,0 h -9 v 46 h 31 c 13.33333,0 22.33333,0 27,0 4.66667,0 10.33334,0.666668 17,2 6.66666,1.333332 11.33334,3 14,5 2.66666,2 5,4.666668 7,8 0.66666,1.333332 1,19.333328 1,54 v 50 H 28 v 46 l 151,231 c 102,154.66669 153.66666,232.33331 155,233 1.33334,1.33331 8.33334,2 21,2 h 18 l 6,-6 V 211 h 92 v -46 h -92 v -51 c 0,-27.333336 0,-43.333336 0,-48 0,-4.666668 2,-8.666668 6,-12 5.33334,-4.666668 24.33334,-7.333332 57,-8 h 29 V 0 Z M 293,211 V 545 L 74,212 183,211 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
transform="matrix(0.01818797,0,0,-0.01652501,395.15616,-14.031106)"
clip-path="url(#clipPath42)" />
<path
id="path43"
d="M 136.11999,102.14 159,102 l 22.88,0.14"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath44)" />
<path
id="path45"
d="m 130.11999,102.18 5.99001,-2.04 0.02,4 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath46)" />
<path
id="path47"
d="m 187.88,102.18 -6.01,1.96 0.02,-4 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath48)" />
<path
id="path49"
d="m 51,0 c -3.333332,2.6666667 -5,5 -5,7 0,1.333333 56.33333,118 169,350 112.66669,232 170.33334,349.33331 173,352 2,4.66669 11.33334,7 28,7 15.33334,0 24.66666,-2.33331 28,-7 2,-2.66669 59.33331,-120 172,-352 C 728.66669,125 785.33331,8.333333 786,7 786,5 784.33331,2.6666667 781,0 Z M 507,344 384,596 137,92 383,91 h 247 c 0,1.333336 -41,85.66666 -123,253 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0913"
transform="matrix(0.01846566,0,0,-0.01665545,118.3956,160.49481)"
clip-path="url(#clipPath50)" />
<path
id="path51"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0913"
transform="matrix(0.01846566,0,0,-0.01665545,133.77748,160.49481)"
clip-path="url(#clipPath52)" />
<path
id="path53"
d="m 47.119999,102.14 15.760002,-0.09"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath54)" />
<path
id="path55"
d="m 41.119999,102.18 5.990002,-2.04 0.02,4 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath56)" />
<path
id="path57"
d="m 68.879997,102.01 -5.989998,2.04 -0.02,-4 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
clip-path="url(#clipPath58)" />
<path
id="path59"
d="m 51,0 c -3.333332,2.6666667 -5,5 -5,7 0,1.333333 56.33333,118 169,350 112.66669,232 170.33334,349.33331 173,352 2,4.66669 11.33334,7 28,7 15.33334,0 24.66666,-2.33331 28,-7 2,-2.66669 59.33331,-120 172,-352 C 728.66669,125 785.33331,8.333333 786,7 786,5 784.33331,2.6666667 781,0 Z M 507,344 384,596 137,92 383,91 h 247 c 0,1.333336 -41,85.66666 -123,253 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
transform="matrix(0.01781352,0,0,-0.01747296,-61.044135,150.70977)"
clip-path="url(#clipPath60)" />
<path
id="path61"
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
transform="matrix(0.01781352,0,0,-0.01747296,-46.205468,150.70977)"
clip-path="url(#clipPath62)" />
<path
id="path63"
d="m 109,429 c -18,0 -32.333336,6 -43,18 -10.666668,12 -16,26.66666 -16,44 0,47.33331 17.666664,88.33331 53,123 35.33334,34.66669 79.33333,52 132,52 60.66666,0 111.33334,-18.66669 152,-56 40.66666,-37.33331 61.33334,-85.66669 62,-145 0,-28.66666 -6.66666,-56 -20,-82 -13.33334,-26 -29.33334,-48.66666 -48,-68 -18.66666,-19.33334 -45.33334,-44 -80,-74 -24,-20.66667 -57.33333,-51.33333 -100,-92 l -59,-56 76,-1 c 104.66669,0 160.33334,1.666664 167,5 4.66666,1.333336 12.66666,31 24,89 v 3 h 40 v -3 C 448.33334,184 444,153.66667 436,95 428,36.333332 423,5.666667 421,3 V 0 H 50 v 19 12 c 0,4.666668 2,9.666668 6,15 4,5.333332 14,17 30,35 19.33334,21.33334 36,40 50,56 6,6.66667 17.33333,19 34,37 16.66667,18 28,30.33333 34,37 6,6.66667 15.66667,17.66667 29,33 13.33333,15.33334 22.66667,26.66666 28,34 5.33334,7.33334 13,17.33334 23,30 10,12.66666 17,23.33334 21,32 4,8.66666 9,18.33334 15,29 6,10.66666 10.33334,21.33334 13,32 2.66666,10.66666 5,20.66666 7,30 2,9.33334 3,20.33334 3,33 0,42 -11.33334,78.33331 -34,109 -22.66666,30.66669 -55,46 -97,46 -22,0 -41.33333,-5.66669 -58,-17 -16.66667,-11.33331 -28.33334,-22.33331 -35,-33 -6.66666,-10.66669 -10,-17 -10,-19 0,-0.66669 1.66666,-1 5,-1 12,0 24.33333,-4.66669 37,-14 12.66667,-9.33331 19,-24.66666 19,-46 0,-16.66666 -5.33333,-30.66666 -16,-42 -10.66667,-11.33334 -25.66667,-17.33334 -45,-18 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
transform="matrix(0.01781352,0,0,-0.01747296,-52.98351,174.50793)"
clip-path="url(#clipPath64)" />
<path
id="path65"
d="m 120,220 h 1605 v 60 H 120 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
transform="matrix(0.01781352,0,0,-0.01747296,-64.963108,162.5215)"
clip-path="url(#clipPath66)" />
<path
id="path67"
d="m 117,59 c 0,-22 8.33333,-33 25,-33 24.66667,0 45.66667,35 63,105 4,13.33333 7.33333,20.33333 10,21 1.33333,0.66667 4.66667,1 10,1 h 4 c 6,0 10,0 12,0 2,0 3.66667,-0.66667 5,-2 1.33333,-1.33333 2,-3.66667 2,-7 -0.66667,-4 -1.66667,-9.33333 -3,-16 -1.33333,-6.66666 -5,-19.33333 -11,-38 C 228,71.333328 221.33333,55.666668 214,43 206.66667,30.333332 196.33333,18 183,6 169.66667,-6 154.33333,-11.666667 137,-11 113,-11 90.666672,-3.666667 70,11 49.333332,25.666668 38.666668,50.333332 38,85 c 0,8 0.333332,13.666664 1,17 l 65,258 c 42,170 63,257.66669 63,263 0,2 -0.33333,3.66669 -1,5 -0.66667,1.33331 -2,2.66669 -4,4 -2,1.33331 -3.66667,2 -5,2 -1.33333,0 -4,0.33331 -8,1 -4,0.66669 -6.66667,1 -8,1 -1.33333,0 -4.33333,0.33331 -9,1 -4.66666,0.66669 -8,0.66669 -10,0 -6.66666,0 -11,0 -13,0 -2,0 -4.66666,0.33331 -8,1 -3.333336,0.66669 -5.333336,1.66669 -6,3 -0.666664,1.33331 -1,3.33331 -1,6 0,1.33331 0.666664,6 2,14 3.333336,12.66669 7,19.66669 11,21 4,1.33331 28,3.33331 72,6 10,0.66669 21.33333,1.33331 34,2 12.66667,0.66669 22.66667,1.66669 30,3 7.33333,1.33331 11,1.66669 11,1 8,0 12,-2.66669 12,-8 0,-7.33331 -24.33333,-107.33331 -73,-300 C 144.33333,193.33333 119.33334,92.333336 118,83 c 0,-1.333336 0,-4 0,-8 0,-4 -0.33334,-7.333336 -1,-10 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1318"
transform="matrix(0.02902327,0,0,-0.02204854,-96.099279,63.555458)"
clip-path="url(#clipPath68)" />
<path
id="path69"
d="m 21,287 c 0.666666,2 1.333334,4.66666 2,8 0.666666,3.33334 2.333334,10.66666 5,22 2.666666,11.33334 6,21.66666 10,31 4,9.33334 9,20.33334 15,33 6,12.66666 12.666664,22.66666 20,30 7.333336,7.33334 16,14.66666 26,22 10,7.33334 21,10.33334 33,9 19.33333,0 36.33333,-4 51,-12 14.66667,-8 25,-15.33334 31,-22 6,-6.66666 9.66667,-13.33334 11,-20 1.33333,-4 2.33333,-6 3,-6 0.66667,0 3.33333,2.33334 8,7 32,34.66666 69,52 111,52 h 3 c 32,0 56,-13.66666 72,-41 5.33334,-12.66666 8,-25 8,-37 0,-20 -4.33334,-36 -13,-48 -8.66666,-12 -17.33334,-19.66666 -26,-23 -8.66666,-3.33334 -17,-4.66666 -25,-4 -13.33334,0 -24,3.66666 -32,11 -8,7.33334 -12,17 -12,29 0,32 18.66666,53.33334 56,64 -14.66666,8.66666 -26.66666,13 -36,13 -37.33334,0 -71.66666,-24.66666 -103,-74 -6.66667,-10.66666 -11.66667,-21.66666 -15,-33 C 220.66667,286.66666 209.33333,242.33334 190,165 167.33333,71.666664 154.33333,22 151,16 c -8.66667,-18 -23,-27 -43,-27 -8.666664,0 -15.666664,2 -21,6 -5.333336,4 -9,8 -11,12 -2,4 -2.666664,7.333333 -2,10 0,8.666668 13.333328,66 40,172 26.66667,106 40,165 40,177 0,26 -8.66667,39 -26,39 -14,0 -26,-9.33334 -36,-28 C 82,358.33334 74,338 68,316 62,294 58.333332,282 57,280 55.666668,278.66666 50.333336,278 41,278 H 27 c -4,4 -6,7 -6,9 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1291"
transform="matrix(0.02684813,0,0,-0.02289989,455.70282,63.555487)"
clip-path="url(#clipPath70)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 173 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 108 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 138 KiB

52
docs_sphinx/index.rst Normal file
View File

@ -0,0 +1,52 @@
.. Tug documentation master file, created by
sphinx-quickstart on Mon Aug 14 11:30:23 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Tug's documentation!
===============================
Welcome to the documentation of the TUG project, a simulation program
for solving transport equations in one- and two-dimensional uniform
grids using cell centered finite differences.
---------
Diffusion
---------
TUG can solve diffusion problems with heterogeneous and anisotropic
diffusion coefficients. The partial differential equation expressing
diffusion reads:
.. math::
\frac{\partial C}{\partial t} = \nabla \cdot \left[ \mathbf{\alpha} \nabla C \right]
In 2D, the equation reads:
.. math::
\frac{\partial C}{\partial t} = \frac{\partial}{\partial x}\left[ \alpha_x \frac{\partial C}{\partial x}\right] + \frac{\partial}{\partial y}\left[ \alpha_y \frac{\partial C}{\partial y}\right]
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
Table of Contents
^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
self
installation
theory
user
developer
examples
visualization
contributors

View File

@ -0,0 +1,3 @@
Installation
============

View File

@ -0,0 +1,2 @@
breathe
sphinx-rtd-theme

698
docs_sphinx/theory.rst Normal file
View File

@ -0,0 +1,698 @@
.. Converted content of report from Hannes Signer and Philipp Ungrund
(https://www.cs.uni-potsdam.de/bs/teaching/docs/lectures/2023/tug-project.pdf)
Theoretical Foundations
=======================
This section describes the theoretical foundation underlying this research
project by answering questions about the background with the mathematical and
scientific equations, the goal of numerically solving these, and their use
cases. It also discusses discretization approaches to the equations that are
needed to calculate results digitally. [BAEHR19]_
Diffusion Equation
------------------
The diffusion equation builds the theoretical bedrock to this research project.
It is a parabolic partial differential equation that is applied in various
scientific fields. It can describe high-level movement of particles suspended in
a liquid or gaseous medium over time and space based on Brownian molecular
motion.
The derivation of the equation simply follows the conservation of mass theorem.
In the following, the derivation always refers to the one-dimensional case
first, but an extension to further dimensions is straightforward. First, lets
imagine a long tube filled with a liquid or gaseous medium into which we add a
marker substance within a small volume element at the location x.
.. figure:: images/conservation_law.svg
Visualization of the law of mass conservation
The boundaries of the volume element are still permeable, so that mass transport
is possible. We refer to the difference of the substance entering and leaving
the volume element as the flux :math:`\Phi` and the concentration of the marker
substance at location :math:`x` at time :math:`t` as :math:`C`. According to the
conservation of mass, it is not possible for this to be created or destroyed.
Therefore, a change in concentration over time must be due to a change in flux
over the volume element. [LOGAN15]_ [SALSA22]_ Formally this leads to
.. _`mass conservation`:
.. math::
\frac{\partial C}{\partial t} + \frac{\partial \Phi}{\partial x} = 0.
Diffusion relies on random particle collisions based on thermal energy.
The higher the concentration of a substance, the higher the probability
of a particle collision. Ficks first law describes this relationship as
follows
.. _`ficks law`:
.. math::
\Phi = -\alpha \cdot \frac{\partial C}{\partial x}.
The law states that the flux moves in the direction of decreasing concentration
[1]_, proportional to the gradient of the concentration in the spatial direction
and a diffusion coefficient :math:`\alpha`. In this context, the diffusion
coefficient represents a measure of the mobility of particles and is given in
the SI unit :math:`\frac{m^2}{s}`. Substituting `ficks law`_ into `mass
conservation`_ now yields the diffusion equation for the one-dimensional case,
which gives the temporal change of the concentration as a function of the
spatial concentration gradient
.. _`one dimensional PDE`:
.. math::
\begin{aligned}
\frac{\partial C}{\partial t} - \frac{\partial}{\partial x}\left(\alpha \cdot \frac{\partial C}{\partial x}\right) = 0 \\
\frac{\partial C}{\partial t} = \alpha \cdot \frac{\partial^2 C}{\partial x^2}.
\label{eq:}
\end{aligned}
An extension to two dimensions is easily possible and gives
.. _`two dimensional PDE`:
.. math::
\begin{aligned}
\frac{\partial C}{\partial t} = \alpha_x \cdot \frac{\partial^2 C}{\partial x^2} + \alpha_y \cdot \frac{\partial^2 C}{\partial y^2}.
\label{eq:pde_two_dimensional}
\end{aligned}
Since this equation now contains both a first order temporal and a
second order spatial derivative, it belongs to the group of partial
differential equations (PDE). In particular, the diffusion problem
belongs to the group of parabolic PDEs. With these, we describe the
evolution of the problem via a first-order time derivative.
[CHAPRA15]_ Furthermore, it describes diffusion under
the assumption of a constant diffusion coefficient in each direction.
Therefore, we distinguish once into the case of homogeneous but
anisotropic diffusion, where the diffusion coefficients are constant in
one spatial direction but may vary between them, and the case of
heterogeneous diffusion. In this case, the diffusion can also change
within one spatial direction. This distinction is important, because the
subsequent discretization of the problem for numerical solution differs
with respect to these two variants.
Numeric and Discretization
--------------------------
The solution of the diffusion equation described above can be solved
analytically for simple cases. For more complicated geometries or
heterogeneous diffusion behavior, this is no longer feasible. In order
to obtain sufficiently accurate solutions, various numerical methods are
available. Basically, we can distinguish between methods of finite
differences and finite elements. The former are mathematically easier to
handle, but limited to a simpler geometry, whereas the latter are more
difficult to implement, but can simulate arbitrarily complicated shapes.
[BAEHR19]_
Since this work is limited to the simulation of two-dimensional grids
with heterogeneous diffusion as the most complicated form, we restrict
ourselves to the application of finite differences and focus on a
performant computation.
There are in principle two methods for solving finite differences with
opposite approaches, as well as one method that uses both methods
equally the so-called Crank-Nicolson (CRNI) method.
.. figure:: images/numeric_overview.svg
Methods of finite differences
The idea of finite differences is to replace the partial derivatives of
the PDE to be solved by the corresponding difference quotients for a
sufficiently finely discretized problem. Taylors theorem forms the
basis for this possibility. In essence, Taylor states that for a smooth
function [2]_ :math:`f: \mathbb{R} \rightarrow \mathbb{R}`, it is
possible to predict the function value :math:`f(x)` at one point using
the function value and its derivative at another point
:math:`a \in \mathbb{R}`. Formally we can write this as
.. math::
\begin{aligned}
f(x) =& f(a) + f'(a) (x-a) + \frac{f''(a)}{2!}(x-a)^2 + ... + \frac{f^{(n)}(a)}{n!}(x-a)^n + R_n\\
R_n =& \int_{a}^x \frac{(x-t)^n}{n!} f^{(n+1)}(t) dt.
\end{aligned}
:math:`R_n` denotes the residual term, which also indicates the error in
the case of premature termination of the series. An approximation of the
first derivative is now done simply by truncating the Taylor series
after the first derivative and transforming accordingly
[CHAPRA15]_. This leads to
.. _`first order approximation`:
.. math::
\begin{aligned}
f(x_{i+1}) =& f(x_i) + f'(x_i) (x_{i+1}-x_i) + R_1 \\
f'(x_{i}) =& \underbrace{\frac{f(x_{i+1}) - f(x_i)}{x_{i+1} - x_i}}_{\text{first order approximation}} - \underbrace{\frac{R_1}{x_{i+1} - x_i}}_{\text{truncation error}}.
\end{aligned}
The `first order approximation`_ shows a so-called forward finite difference
approximation in space, since we use the points at :math:`i` and :math:`i+1` for
the approximation of :math:`f'(x_i)`. Correspondingly, a backward and centered
finite difference approximation is possible applying the appropriate values.
For the approximation of higher derivatives, the combination of several
Taylor series expansions is possible. For example, we set up the
following two Taylor series to derive the centered approximation of the
second derivative
.. _`second order approximations`:
.. math::
\begin{aligned}
f(x_{i+1}) =& f(x_i) + f'(x_i) \underbrace{(x_{i+1} - x_i)}_{h} + \frac{f^{''}(x_i)}{2} \underbrace{(x_{i+1} - x_{i})^2}_{h^2} + R_2 \\
f(x_{i-1}) =& f(x_i) - f'(x_i) \underbrace{(x_{i-1} - x_i)}_{h} + \frac{f^{''}(x_i)}{2} \underbrace{(x_{i-1} - x_{i})^2}_{h^2} +R_2.
\end{aligned}
Adding [3]_ both `second order approximations`_ and rearranging them according to
the second derivative yields the corresponding approximation
.. _`second order approximation`:
.. math::
\begin{aligned}
f^{''}(x_i) = \frac{f(x_{i+1}) - 2\cdot f(x_i) + f(x_{i-1})}{h^2}.
\end{aligned}
Another possibility of the derivation for the second order approximation results
from the following consideration. The second derivation is just another
derivation of the first one. In this respect we can represent the second
derivative also as the difference of the approximation of the first derivative
[CHAPRA15]_. This results in the following representation, which after
simplification agrees with equation `second order approximation`_
.. _`first order derivative`:
.. math::
\begin{aligned}
f^{''}(x_i) = \frac{\frac{f(x_{i+1}) - f(x_i)}{h} - \frac{f(x_i) - f(x_{i-1})}{h}}{h}.
\label{eq:first_order_derivative}
\end{aligned}
The above equations are all related to a step size :math:`h`. Let us imagine a
bar, which we want to discretize by dividing it into individual cells.
.. figure:: images/discretization_1D.svg
Discretization of a one-dimensional surface
In the discretization, the step size :math:`h` would correspond to the distance
between the cell centers :math:`\Delta x`. Now, the question arises how to deal
with the boundaries? This question is a topic of its own, whereby numerous
boundary conditions exist. At this point, only the cases relevant to this work
will be presented. First, let us look at the problem intuitively and consider
what possible boundary conditions can occur. A common case is an environment
that is isolated from the outside world. That is, the change in concentration at
the boundary over time has to be zero. We can generalize this condition to the
*Neumann boundary condition*, where the derivative at the boundary is given. The
case of a closed system then arises with a derivative of zero, so that there can
be no flow across the boundary of the system. Thus, the Neumann boundary
condition for the left side yields the following formulation
.. math::
\begin{aligned}
\text{Neumann condition for the left boundary:~} \frac{\partial C(x_{0-\frac{\Delta x}{2}}, t)}{\partial t} = g(t) \text{,~} t > 0.
\end{aligned}
The second common case is a constant boundary. Again, the case can be
generalized, this time to the so-called *Dirichlet boundary condition*. Here,
the function value of the boundary is given instead of its derivative. For the
example we can write this boundary condition as
.. math::
\begin{aligned}
\text{Dirichlet condition for the left boundary:~} C(x_{0 - \frac{\Delta x}{2}}, t) = h(t) \text{, ~} t > 0.
\end{aligned}
In practise, constants are often used instead of time-dependent
functions. [LOGAN15]_ [CHAPRA15]_ [SALSA22]_
With this knowledge, we can now turn to the concrete implementation of
finite differences for the explicit and implicit scheme. Centered
differences are always used for the spatial component. Only in the time
domain we distinguish into forward (FTCS method) and backward (BTCS
method) methods, which influence the corresponding solution
possibilities and stability properties.
Explicit Scheme (FTCS)
~~~~~~~~~~~~~~~~~~~~~~
FTCS stands for *Forward Time, Centered Space* and is an explicit
procedure that can be solved iteratively. Explicit methods are generally
more accurate, but are limited in their time step, so that incorrectly
chosen time steps lead to instability of the method. Hoewever, there are
estimates, such as the Courant-Friedrich-Lewy stability conditions,
which gives the corresponding maximum possible time steps.
[CHAPRA15]_
The goal now is to approximate both sides of the `one dimensional PDE`_. As the
name of the method FTCS indicates, we use a forward approximation for the left
temporal component. For the right-hand side, we use a central approximation
using the `first order derivative`_. This results in the following approximation
for the inner cells (in the example the cells :math:`x_1`, :math:`x_2` and
:math:`x_3`) and a constant diffusion coefficient :math:`\alpha`
.. _`approximate first order diffusion`:
.. math::
\begin{aligned}
\frac{C_i^{t+1}-C_i^t}{\Delta t}=\alpha \frac{\frac{C_{i+1}^t-C_i^t}{\Delta x}-\frac{C_i^t-C_{i-1}^t}{\Delta x}}{\Delta x}.
\label{eq:approx_first_order_diffusion}
\end{aligned}
By rearranging this equation according to the concentration value for the next
time step :math:``C_i^{t+1}`, we get
.. _`explicit scheme`:
.. math::
\begin{aligned}
C_i^{t+1} = C_i^t + \frac{\alpha \cdot \Delta t}{\Delta x^2} \left[C_{i+1}^t - 2C_i^t + C_{i-1}^t \right].
\label{eq:explicit_scheme}
\end{aligned}
At this point, it should be clear why this method is an explicit procedure. On
the right side of the `explicit scheme`_, there are only known quantities, so
that an explicit calculation rule is given. The basic procedure is shown in
Figure 4. In the case of an explicit procedure, we use the values of the
neighboring cells of the current time step to approximate the value of a cell
for the next time step.
.. figure:: images/explicit_scheme.svg
Illustration for using the existing values in the time domain (green) for approximation of the next time step (red)
As described above, the `explicit scheme`_ applies only to the inner cells. For
the edges we now have to differentiate between the already presented boundary
conditions of Dirichlet and Neumann. To do this, we look again at the
`approximate first order diffusion`_ and consider what would have to be changed
in the case of constant boundary conditions (Dirichlet). It quickly becomes
apparent that in the case of the left cell the value :math:`C_{i-1}` corresponds
to the constant value of the left boundary :math:`l` and in the case of the
right cell :math:`C_{i+1}` to the value of the right boundary :math:`r`. In
addition, the length difference between the cells is now no longer :math:`\Delta
x`, but only :math:`\frac{\Delta x}{2}`, as we go from the center of the cell to
the edge. For a given flux, the first derivative has to take this value. In our
case, this is approximated with the help of the difference quotient, so that
this is omitted in the case of a closed boundary (the flux is equal to zero) or
accordingly assumes a constant value. For the left-hand boundary, this results
in the following modifications, whereas the values for the right-hand boundary
can be derived equivalently
.. _`boundary conditions`:
.. math::
\begin{aligned}
\text{Dirichlet for the left boundary:~}& \frac{C_0^{t+1}-C_0^t}{\Delta t}=\alpha \frac{\frac{C_{1}^t-C_0^t}{\Delta x}-\frac{C_0^t-l}{{\frac{\Delta x}{2}}}}{\Delta x}\\
&C_{0}^{t+1} = C_{0}^t + \frac{\alpha \Delta t}{\Delta x^2} \left [C_{1}^t - 3 C_0^t + 2l \right]\\
\text{Closed Neumann for the left boundary:~}& \frac{C_0^{t+1}-C_0^t}{\Delta t}=\alpha \frac{\frac{C_{1}^t-C_0^t}{\Delta x}-\cancelto{0}{l}}{\Delta x}\\
&C_{0}^{t+1} = C_{0}^t + \frac{\alpha \Delta t}{\Delta x^2} \left [C_1^t - C_0^t \right].
\label{eq:boundary_conditions}
\end{aligned}
Again, it is straightforward to extend the `explicit scheme`_ to the second
dimension, so that the following formula is simply obtained for the inner cells
.. math::
\begin{aligned}
C_{i,j}^{t+1} = C_{i,j}^t +& \frac{\alpha_x \cdot \Delta t}{\Delta x^2} \left[C_{i, j+1}^t - 2C_{i,j}^t + C_{i, j-1} \right] \\
+& \frac{\alpha_y \cdot \Delta t}{\Delta y^2} \left[C_{i+1, j}^t - 2C_{i,j}^t + C_{i-1, j} \right].
\end{aligned}
Here, it is important to note that the indices :math:`i` and :math:`j`
for the two-dimensional cases are now used as usual in the matrix
notation. That means :math:`i` marks the elements in x-direction and
:math:`j` in y-direction.
The previous equations referred to homogeneous diffusion coefficients, i.e. the
diffusion properties along a spatial direction are identical. However, we are
often interested in applications where different diffusion coefficients act in
each discretized cell. This extension of the problem also leads to a slightly
modified variant of our `one dimensional PDE`_, where the diffusion coefficient
now is also a function of the spatial component
.. math::
\begin{aligned}
\frac{\partial C}{\partial t} = \frac{\partial}{\partial x}\left[\alpha(x) \frac{\partial C}{\partial x} \right].
\end{aligned}
For this case, the literature recommends discretizing the problem
directly at the boundaries of the grid cells, i.e. halfway between the
grid points [FROLKOVIC1990]_. If we
follow this scheme and approximate the first derivative for C at the
appropriate cell boundaries, we obtain the following expressions
.. math::
\begin{aligned}
\begin{cases}
\alpha(x_{i+\frac{1}{2}}) \frac{\partial C}{\partial x}(x_{i+\frac{1}{2}}) = \alpha_{i+\frac{1}{2}} \left(\frac{C_{i+1} - C_i}{\Delta x}\right)\\
\alpha(x_{i-\frac{1}{2}}) \frac{\partial C}{\partial x}(x_{i-\frac{1}{2}}) = \alpha_{i-\frac{1}{2}} \left(\frac{C_{i} - C_{i-1}}{\Delta x}\right).
\end{cases}
\end{aligned}
With a further derivation we now obtain a spatially centered
approximation with
.. math::
\begin{aligned}
\frac{\partial }{\partial x}\left(\alpha(x) \frac{\partial C}{\partial x} \right) \simeq& \frac{1}{\Delta x}\left[\alpha_{i+\frac{1}{2}}\left(\frac{C^t_{i+1}-C^t_i}{\Delta x}\right)-\alpha_{i-\frac{1}{2}}\left(\frac{C^t_i-C^t_{i-1}}{\Delta x}\right)\right] \\
\frac{C^{t+1}_{i}-C^t_i}{\Delta t}=& \frac{1}{\Delta x^2}\left[\alpha_{i+\frac{1}{2}} C^t_{i+1}-\left(\alpha_{i+\frac{1}{2}}+\alpha_{i-\frac{1}{2}}\right) C^t_i+\alpha_{i-\frac{1}{2}} C^t_{i-1}\right]\\
C^{t+1}_{i} =& C^t_i + \frac{\Delta t}{\Delta x^2} \left[\alpha_{i+\frac{1}{2}} C^t_{i+1}-\left(\alpha_{i+\frac{1}{2}}+\alpha_{i-\frac{1}{2}}\right) C^t_i+\alpha_{i-\frac{1}{2}} C^t_{i-1} \right].
\end{aligned}
The value for the inter-cell diffusion coefficients can be determined by
either the arithmetic or harmonic mean of both cells, with the harmonic
mean being preferred for the default case. Again, we can extend this
equation to two dimensions, resulting in the following iteration rule
.. math::
\begin{aligned}
C_{i, j}^{t+1}= & C_{i, j}^t+ \frac{\Delta t}{\Delta x^2}\left[\alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^t-\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right) C_{i, j}^t+\alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^t \right]+ \\ & \frac{\Delta t}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^t-\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right) C_{i, j}^t+\alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^t \right].
\end{aligned}
The corresponding equations for the boundary conditions can be derived
analogously to the homogeneous case (cf. `boundary conditions`_) by adjusting
the relevant difference quotients to the respective boundary condition. As
described initially, the FTCS procedure is accurate but not stable for each time
step. The Courant-Friedrichs-Lewy stability condition states that the time step
must always be less than or equal the following value
.. math::
\begin{aligned}
\Delta t \leq \frac{\min (\Delta x^2, \Delta y^2)}{4 \cdot \max (\alpha^x, \alpha^y)}.
\end{aligned}
That is, the maximum time step is quadratically related to the
discretization and linearly to the maximum diffusion coefficient.
Especially for very fine-resolution grids, this condition has a
particularly strong effect on that required computing time. This is in
contrast to the implicit methods, which we will now look at in more
detail. Unlike the explicit methods, the implicit methods are
unconditionally stable.
Implicit Scheme (BTCS)
~~~~~~~~~~~~~~~~~~~~~~
The main difference to the explicit method is that the discretization is not
done at the time step :math:`t` as in the `approximate first order diffusion`_,
but now at the time step :math:`t+1`. Hence, the neighboring cells in the next
time step are used for the approximation of the middle cell.
.. figure:: images/implicit_scheme.svg
Illustration of the implicit method, where the values of the neighboring cells in the next time step are used for the approximation
The `approximate first order diffusion`_ thus changes to
.. math::
\begin{aligned}
\frac{C_i^{t+1}-C_i^t}{\Delta t} & =\alpha \frac{\frac{C_{i+1}^{t+1}-C_i^{t+1}}{\Delta x}-\frac{C_i^{t+1}-C_{i-1}^{t+1}}{\Delta x}}{\Delta x} \\
& =\alpha \frac{C_{i-1}^{t+1}-2 C_i^{t+1}+C_{i+1}^{t+1}}{\Delta x^2}
\end{aligned}
Now there is no possibility to change the equation to :math:`C^{t+1}_i`
so that all values are given. That means :math:`C^{t+1}_i` is only
implicitly indicated. If we know put all known and unkown values on one
side each and define :math:`s_x = \frac{\alpha \Delta t}{\Delta x^2}`
for simplicity, we get the following expression
.. math::
\begin{aligned}
s_x \cdot C_{i+1}^{t+1} + (-1-s_x) \cdot C_i^{t+1} + s_x \cdot C_{i-1}^{t+1} &= -C_i^t.
\label{eq:implicit_inner_grid}
\end{aligned}
This applies only to the inner grid without boundaries. To derive the equations
for the boundaries, the reader can equivalently use the procedure from the FTCS
method with given `boundary conditions`_. Thus, for constant boundaries with the
values :math:`l` and :math:`r` for the left and right boundaries, respectively,
the following two equations are obtained
.. math::
\begin{aligned}
\left(-1-3 s_x\right) \cdot C_0^{t+1}+s_x \cdot C_1^{t+1} &= -C_0^t - 2s_x \cdot l \\
s_x \cdot C_{n-1}^{t+1}+\left(-1-3 s_x\right) \cdot C_n^{t+1} &= -C_n^t - 2s_x \cdot r.
\end{aligned}
The question now arises how values from the next time step can be used for the
approximation. Here, the answer lies in the simultaneous solution of the
equations. Lets look again at the example in Figure for conservation law and
establish the implicit equations for the same. We obtain the following system of
five equations and five unknowns
.. math::
\begin{aligned}
\begin{cases}
\left(-1-3 s_x\right) \cdot C_0^{t+1}+s_x \cdot C_1^{t+1} &= -C_0^t - 2s_x \cdot l \\
s_x \cdot C_{2}^{t+1} + (-1-s_x) \cdot C_1^{t+1} + s_x \cdot C_{0}^{t+1} &= -C_1^t \\
s_x \cdot C_{3}^{t+1} + (-1-s_x) \cdot C_2^{t+1} + s_x \cdot C_{1}^{t+1} &= -C_2^t \\
s_x \cdot C_{4}^{t+1} + (-1-s_x) \cdot C_3^{t+1} + s_x \cdot C_{2}^{t+1} &= -C_3^t \\
s_x \cdot C_{3}^{t+1}+\left(-1-3 s_x\right) \cdot C_4^{t+1} &= -C_4^t - 2s_x \cdot r.
\end{cases}
\end{aligned}
If we transfer this system of equations to a matrix-vector system, we
get
.. math::
\begin{aligned}
\begin{bmatrix}
-1-3s_x & s_x & 0 & 0 & 0 \\
s_x & -1-3s_x & s_x & 0 & 0 \\
0 & s_x & -1-3s_x & s_x & 0 \\
0 & 0 & s_x & -1-3s_x & s_x \\
0 & 0 & 0 & s_x & -1-3s_x
\end{bmatrix}
\cdot
\begin{bmatrix}
-C_0^{t+1}\\
-C_1^{t+1}\\
-C_2^{t+1}\\
-C_3^{t+1}\\
-C_4^{t+1}
\end{bmatrix}
=
\begin{bmatrix}
-C_0^t - 2s_x \cdot l \\
-C_1^t \\
-C_2^t \\
-C_3^t \\
-C_4^t - 2s_x \cdot r
\end{bmatrix}.
\end{aligned}
This system can now be solved very efficiently, since it is a so-called
tridiagonal system. Very fast solution algorithms exist for this, such
as the Thomas algorithm.
In principle, this procedure can be extended again to the
two-dimensional case, but here no tridiagonal system arises any more, so
that the efficient solution algorithms are no longer applicable.
Therefore, one uses a trick and solves the equations in two half time
steps. In the first time step from :math:`t\rightarrow t+\frac{1}{2}`
one space direction is calculated implicitly and the other one
explicitly, whereas in the second time step from
:math:`t+\frac{1}{2} \rightarrow t+1` the whole process is reversed and
the other space direction is solved by the implicit method. This
approach is called the alternative-direction implicit method, which we
will now examine in more detail.
First, we consider the `two dimensional PDE`_ again, which represents the PDE
for diffusion in the two-dimensional case with homogeneous and anisotropic
diffusion coefficients. As explained in the upper sections, we can approximate
the second derivative for each spatial direction as follows
.. _`Taylor series`:
.. math::
\begin{aligned}
\frac{\partial^2 C^t_{i, j}}{\partial x^2} =& \frac{C^t_{i, j-1} - 2C^t_{i,j} + C^t_{i, j+1}}{\Delta x^2} \label{eq:taylor_2_x}\\
\frac{\partial^2 C^t_{i, j}}{\partial y^2} =& \frac{C^t_{i-1, j} - 2C^t_{i,j} + C^t_{i+1, j}}{\Delta y^2}. \label{eq:taylor_2_y}
\end{aligned}
Explicit solution methods, as can be used in the one-dimensional case,
require smaller time steps to remain stable when applied to solve
two-dimensional problems. This leads to a significant increase in the
computational effort. Implicit techniques also require more
computational capacity, since the matrices can no longer be represented
tridiagonally in the two- or three-dimensional case and thus cannot be
solved efficiently. A solution to this problem is provided by the ADI
(alternating-direction implicit) scheme, which transforms
two-dimensional problems back into tridiagonal matrices. Here, each time
step is performed in two half-steps, each time solving implicitly in one
axis direction. [CHAPRA15]_ For the above equations,
the ADI scheme is defined as follows
.. math::
\begin{aligned}
\begin{cases}
\frac{C_{i,j}^{t+\frac{1}{2}}-C_{i,j}^t}{\frac{\Delta t}{2}} = \alpha_x \cdot \frac{\partial^2 C_{i,j}^{t+\frac{1}{2}}}{\partial x^2} + \alpha_y \frac{\partial^2 C_{i,j}^t}{\partial y^2} \\
\frac{C_{i,j}^{t+1}-C_{i,j}^{t+\frac{1}{2}}}{\frac{\Delta t}{2}} = \alpha_x \cdot \frac{\partial^2 C_{i,j}^{t+\frac{1}{2}}}{\partial x^2} + \alpha_y \frac{\partial^2 C_{i,j}^{t+1}}{\partial y^2}
\end{cases}.
\label{eq:adi_scheme}
\end{aligned}
Now the `Taylor series`_ can be substituted into the equation and the term
:math:`\frac{\Delta t}{2}` can be placed on the right side of the equation. The
following expression is generated when introducing :math:`s_x = \frac{\alpha_x
\cdot \Delta t}{2 \cdot \Delta x^2}` and :math:`s_y = \frac{\alpha_y \cdot
\Delta t}{2 \cdot \Delta y^2}` as new variables
.. math::
\begin{aligned}
C_{i,j}^{t+\frac{1}{2}} - C_{i,j}^t &= s_x \cdot \left[~C_{i, j-1}^{t+\frac{1}{2}}-2 C_{i,j}^{t+\frac{1}{2}}+C_{i, j+1}^{t+\frac{1}{2}}\right] + s_y~ \cdot \left[~ C_{i-1, j}^t - 2\cdot C_{i,j}^t + C_{i+1,j}^t\right] \\
C_{i,j}^{t+1} - C_{i,j}^{t+\frac{1}{2}} &= s_x \cdot \left[C_{i, j-1}^{t+\frac{1}{2}}-2 C_{i,j}^{t+\frac{1}{2}}+C_{i, j+1}^{t+\frac{1}{2}}\right] + s_y \cdot ~\left[ C_{i-1, j}^{t+1} - 2\cdot C_{i,j}^{t+1} + C_{i+1,j}^{t+1}\right].
\end{aligned}
As it can be obtained by the following Figure the above equations are only valid
for the inner grid.
.. figure:: images/grid_with_boundaries_example.png
Example grid with boundary conditions
At the edge of
the grid, starting from the cell center, it is not possible to take a
full step :math:`dx`, but only a half step :math:`\frac{dx}{2}`.
Therefore, the approximation for the inner cells (shown as an example
for the x-direction)
.. math::
\begin{aligned}
\frac{C_{i,j}^{t+1} - C_{i,j}^t}{\Delta t} = \frac{\frac{C_{i, j+1}^{t+1} - C_{i,j}^{t+1}}{\Delta x} - \frac{C_{i, j}^{t+1} - C_{i, j-1}^{t+1}}{\Delta x}}{\Delta x} = \frac{C_{i, j+1}^{t+1} - 2 \cdot C_{i, j}^{t+1} + C_{i, j-1}^{t+1}}{\Delta x^2}
\end{aligned}
is no longer valid and we have to perform the same changes to the
boundary conditions as for the other methods by replacing the
corresponding concentration values or difference quotiens with the
respective boundary concentrations or fluxes depending on the boundary
condition (Neumann or Dirichlet).
We can now quickly introduce the ADI scheme for heterogeneous diffusion
by using the same discretization logic as for the FTCS procedure and
discretizing the points between cells.
This results in the following ADI scheme
.. math::
\begin{aligned}
\begin{cases}
\frac{C_{i, j}^{t+\frac{1}{2}}-C_{i, j}^t}{\Delta \frac{t}{2}}= & \frac{1}{\Delta x^2}\left[\alpha_{i, j+ \frac{1}{2}} C_{i, j+1}^{t+ \frac{1}{2}}-\left(\alpha_{i, j+ \frac{1}{2}}+\alpha_{i, j- \frac{1}{2}}\right) C_{i, j}^{t+ \frac{1}{2}}+\alpha_{i, j-\frac{1}{2}} C_{i, j-1}^{t+\frac{1}{2}}\right]+ \\ & \frac{1}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j} C_{i+1, j}^t-\left(\alpha_{i+\frac{1}{2}, j}+\alpha_{i-\frac{1}{2}, j}\right) C_{i, j}^t+\alpha_{i-\frac{1}{2}, j} C_{i-1, j}^t\right] \\
\frac{C_{i, j}^{t+1}-C_{i, j}^{t+\frac{1}{2}}}{\Delta \frac{t}{2}}= & \frac{1}{\Delta x^2}\left[\alpha_{i, j+\frac{1}{2}} C_{i, j+1}^{t+\frac{1}{2}}-\left(\alpha_{i, j+\frac{1}{2}}+\alpha_{i, j-\frac{1}{2}}\right) C_{i, j}^{t+\frac{1}{2}}+\alpha_{i, j-\frac{1}{2}} C_{i, j-1}^{t+\frac{1}{2}}\right]+ \\ & \frac{1}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j} C_{i+1, j}^{t+1}-\left(\alpha_{i+\frac{1}{2}, j}+\alpha_{i-\frac{1}{2}, j}\right) C_{i, j}^{t+1}+\alpha_{i-\frac{1}{2}, j} C_{i-1, j}^{t+1}\right].
\end{cases}
\end{aligned}
.. math::
\begin{aligned}
\begin{cases}
-s_x \alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^{t+\frac{1}{2}}+\left(1+s_x\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right)\right) C_{i, j}^{t+\frac{1}{2}}-s_x \alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^{t+\frac{1}{2}}= \\ s_y \alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^t+\left(1-s_y\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right)\right) C_{i, j}^t+s_y \alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^t\\
-s_y \alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^{t+1}+\left(1+s_y\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right)\right) C_{i, j}^{t+1}-s_y \alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^{t+1}= \\ s_x \alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^{t+\frac{1}{2}}+\left(1-s_x\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right)\right) C_{i, j}^{t+\frac{1}{2}}+s_x \alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^{t+\frac{1}{2}}.
\end{cases}
\end{aligned}
Rearranging the terms according to known and unknown quantities gives us again
the scheme as in the one-dimensional case and allows us to set up a tridiagonal
system for each row of the grid. We also have to take into account that the ADI
method always requires two calculation steps for each complete time step.
However, since the implicit method is unconditionally stable, it shows its
adavantage especially with larger time steps. The basic procedure of the ADI
scheme with the two time steps is shown here:
.. figure:: images/adi_scheme.svg
Illustration of the iterative procedure in the ADI process
Crank-Nicolson method
---------------------
Another implicit method is the Crank-Nicolson (CRNI) method, which uses both
forward and backward approximations. As this method does not represent a main
goal of this work, it is only briefly mentioned here. Essentially, the
Crank-Nicolson method averages the two finite differences variants at the
corresponding point. For the one-dimensional case this results in
.. math::
\begin{aligned}
\frac{C^{t+1}_i - C_i^{t}}{\Delta t} = \frac{1}{2} \cdot \left[\frac{C^{t}_{i+1} - 2C^t_i + C_{i+1}^t}{\Delta x^2} + \frac{C^{t+1}_{i+1} - 2C_i^{t+1} + C_i^{t+1}}{\Delta x^2} \right].
\end{aligned}
By transforming the terms accordingly, it is also possible to generate a
tridiagonal matrix, as in the BTCS method, which can be solved very
efficiently with the Thomas algorithm. [CHAPRA15]_
Like the BTCS method, CRNI is unconditionally stable, but becomes
inaccurate if the time steps are too large. Compared to the FTCS and
BTCS methods, which have a temporal truncation error of
:math:`\mathcal{O}(\Delta t)`, the CRNI method with an error of
:math:`\mathcal{O}(\Delta t^2)` has an advantage and should be preferred
for time-accurate solutions.
.. [LOGAN15] Logan, J. David. Applied Partial Differential Equations.
Undergraduate Texts in Mathematics. Cham: Springer International
Publishing, 2015. https://doi.org/10.1007/978-3-319-12493-3.
.. [SALSA22] Salsa, Sandro, and Gianmaria Verzini. Partial Differential
Equations in Action. Vol. 147. UNITEXT. Cham: Springer
International Publishing, 2022.
https://doi.org/10.1007/978-3-031-21853-8.
.. [CHAPRA15] Chapra, Steven C., and Raymond P. Canale. Numerical Methods for
Engineers, 2015.
.. [BAEHR19] Baehr, Hans Dieter, and Karl Stephan. Wärme- Und Stoffübertragung.
Berlin, Heidelberg: Springer Berlin Heidelberg, 2019.
https://doi.org/10.1007/978-3-662-58441-5.
.. [FROLKOVIC1990] Frolkovič, Peter. “Numerical Recipes: The Art of Scientific
Computing.” Acta Applicandae Mathematicae 19, no. 3 (June
1990): 29799. https://doi.org/10.1007/BF01321860.
.. [1]
The minus sign ensures that the direction of the flux follows the
decrease in concentration.
.. [2]
A function is called smooth if it is arbitrarily often
differentiable.
.. [3]
The remainder term is not considered further at this point, but is
:math:`\mathcal{O}(h^2)` in the centered variant. Thus, the centered
variant is more accurate than the forward or backward oriented
method, whose errors can be estimated with :math:`\mathcal{O}(h)`.
[CHAPRA15]_

2629
docs_sphinx/tug_logo.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 138 KiB

7
docs_sphinx/user.rst Normal file
View File

@ -0,0 +1,7 @@
User API
========
.. toctree::
Boundary
Diffusion

View File

@ -0,0 +1,3 @@
Visualization
=============

View File

@ -0,0 +1,84 @@
#include <Eigen/Eigen>
#include <tug/Diffusion.hpp>
using namespace Eigen;
using namespace tug;
int main(int argc, char *argv[]) {
// EASY_PROFILER_ENABLE;
// profiler::startListen();
// **************
// **** GRID ****
// **************
// profiler::startListen();
// create a grid with a 20 x 20 field
int row = 40;
int col = 50;
// (optional) set the domain, e.g.:
// grid.setDomain(20, 20);
// (optional) set the concentrations, e.g.:
// MatrixXd concentrations = MatrixXd::Constant(20,20,1000); //
// #row,#col,value grid.setConcentrations(concentrations);
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
concentrations(10, 10) = 2000;
Grid64 grid(concentrations);
// (optional) set alphas of the grid, e.g.:
// MatrixXd alphax = MatrixXd::Constant(20,20,1); // row,col,value
// MatrixXd alphay = MatrixXd::Constant(20,20,1); // row,col,value
// grid.setAlpha(alphax, alphay);
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
bc.setBoundarySideClosed(BC_SIDE_LEFT);
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
bc.setBoundarySideClosed(BC_SIDE_TOP);
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
// bc.setBoundarySideConstant(BC_SIDE_LEFT, 0);
// bc.setBoundarySideConstant(BC_SIDE_RIGHT, 0);
// bc.setBoundarySideConstant(BC_SIDE_TOP, 0);
// bc.setBoundarySideConstant(BC_SIDE_BOTTOM, 0);
// (optional) set boundary condition values for one side, e.g.:
// VectorXd bc_left_values = VectorXd::Constant(20,1); // length,value
// bc.setBoundaryConditionValue(BC_SIDE_LEFT, bc_left_values); // side,values
// VectorXd bc_zero_values = VectorXd::Constant(20,0);
// bc.setBoundaryConditionValue(BC_SIDE_LEFT, bc_zero_values);
// bc.setBoundaryConditionValue(BC_SIDE_RIGHT, bc_zero_values);
// VectorXd bc_front_values = VectorXd::Constant(20,2000);
// bc.setBoundaryConditionValue(BC_SIDE_TOP, bc_front_values);
// bc.setBoundaryConditionValue(BC_SIDE_BOTTOM, bc_zero_values);
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Diffusion simulation(grid, bc); // grid,boundary
// set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// set the number of iterations
simulation.setIterations(300);
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_XTREME);
// **** RUN SIMULATION ****
// run the simulation
// EASY_BLOCK("SIMULATION")
simulation.run();
// EASY_END_BLOCK;
// profiler::dumpBlocksToFile("test_profile.prof");
// profiler::stopListen();
}

7
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
add_executable(BTCS_2D_proto_example BTCS_2D_proto_example.cpp)
add_executable(FTCS_2D_proto_example_mdl FTCS_2D_proto_example_mdl.cpp)
add_executable(FTCS_2D_proto_closed_mdl FTCS_2D_proto_closed_mdl.cpp)
target_link_libraries(BTCS_2D_proto_example tug)
target_link_libraries(FTCS_2D_proto_closed_mdl tug)
target_link_libraries(FTCS_2D_proto_example_mdl tug)

View File

@ -0,0 +1,90 @@
/**
* @file FTCS_2D_proto_closed_mdl.cpp
* @author Hannes Signer, Philipp Ungrund, MDL
* @brief Creates a TUG simulation in 2D with FTCS approach and closed boundary
* condition; optional command line argument: number of cols and rows
*
*/
#include <Eigen/Eigen>
#include <cstdlib>
#include <iostream>
#include <tug/Diffusion.hpp>
using namespace Eigen;
using namespace tug;
int main(int argc, char *argv[]) {
int row = 64;
if (argc == 2) {
// no cmd line argument, take col=row=64
row = atoi(argv[1]);
}
int col = row;
std::cout << "Nrow =" << row << std::endl;
// **************
// **** GRID ****
// **************
// create a grid with a 20 x 20 field
int n2 = row / 2 - 1;
// (optional) set the domain, e.g.:
// grid.setDomain(20, 20);
// (optional) set the concentrations, e.g.:
// MatrixXd concentrations = MatrixXd::Constant(20,20,1000); //
// #row,#col,value
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
concentrations(n2, n2) = 1;
concentrations(n2, n2 + 1) = 1;
concentrations(n2 + 1, n2) = 1;
concentrations(n2 + 1, n2 + 1) = 1;
Grid64 grid(concentrations);
// (optional) set alphas of the grid, e.g.:
MatrixXd alphax = MatrixXd::Constant(row, col, 1E-4); // row,col,value
MatrixXd alphay = MatrixXd::Constant(row, col, 1E-6); // row,col,value
grid.setAlpha(alphax, alphay);
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
// (optional) set boundary condition values for one side, e.g.:
bc.setBoundarySideClosed(BC_SIDE_LEFT); // side,values
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
bc.setBoundarySideClosed(BC_SIDE_TOP);
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Diffusion<double, FTCS_APPROACH> simulation(
grid, bc); // grid,boundary,simulation-approach
// set the timestep of the simulation
simulation.setTimestep(10000); // timestep
// set the number of iterations
simulation.setIterations(100);
// (optional) set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_VERBOSE);
// **** RUN SIMULATION ****
// run the simulation
simulation.run();
return 0;
}

View File

@ -0,0 +1,81 @@
/**
* @file FTCS_2D_proto_example.cpp
* @author Hannes Signer, Philipp Ungrund
* @brief Creates a prototypical standard TUG simulation in 2D with FTCS
* approach and constant boundary condition
*
*/
#include <Eigen/Eigen>
#include <tug/Diffusion.hpp>
using namespace Eigen;
using namespace tug;
int main(int argc, char *argv[]) {
// **************
// **** GRID ****
// **************
// create a grid with a 20 x 20 field
int row = 64;
int col = 64;
int n2 = row / 2 - 1;
// (optional) set the domain, e.g.:
// grid.setDomain(20, 20);
// (optional) set the concentrations, e.g.:
// MatrixXd concentrations = MatrixXd::Constant(20,20,1000); //
// #row,#col,value
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
concentrations(n2, n2) = 1;
concentrations(n2, n2 + 1) = 1;
concentrations(n2 + 1, n2) = 1;
concentrations(n2 + 1, n2 + 1) = 1;
Grid64 grid(concentrations);
// (optional) set alphas of the grid, e.g.:
MatrixXd alphax = MatrixXd::Constant(row, col, 1E-4); // row,col,value
MatrixXd alphay = MatrixXd::Constant(row, col, 1E-6); // row,col,value
grid.setAlpha(alphax, alphay);
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
// (optional) set boundary condition values for one side, e.g.:
bc.setBoundarySideConstant(BC_SIDE_LEFT, 0); // side,values
bc.setBoundarySideConstant(BC_SIDE_RIGHT, 0);
bc.setBoundarySideConstant(BC_SIDE_TOP, 0);
bc.setBoundarySideConstant(BC_SIDE_BOTTOM, 0);
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Diffusion<double, tug::FTCS_APPROACH> simulation(
grid, bc); // grid,boundary,simulation-approach
// (optional) set the timestep of the simulation
simulation.setTimestep(1000); // timestep
// (optional) set the number of iterations
simulation.setIterations(5);
// (optional) set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_OFF);
// **** RUN SIMULATION ****
// run the simulation
simulation.run();
return 0;
}

View File

@ -1,213 +0,0 @@
#ifndef BOUNDARYCONDITION_H_
#define BOUNDARYCONDITION_H_
#include <array>
#include <bits/stdint-uintn.h>
#include <stdexcept>
#include <stdint.h>
#include <vector>
typedef uint8_t bctype;
namespace Diffusion {
/**
* Defines a closed/Neumann boundary condition.
*/
static const bctype BC_TYPE_CLOSED = 0;
/**
* Defines a flux/Cauchy boundary condition.
*/
static const bctype BC_TYPE_FLUX = 1;
/**
* Defines a constant/Dirichlet boundary condition.
*/
static const bctype BC_TYPE_CONSTANT = 2;
/**
* Defines boundary conditions for the left side of the grid.
*/
static const uint8_t BC_SIDE_LEFT = 0;
/**
* Defines boundary conditions for the right side of the grid.
*/
static const uint8_t BC_SIDE_RIGHT = 1;
/**
* Defines boundary conditions for the top of the grid.
*/
static const uint8_t BC_SIDE_TOP = 2;
/**
* Defines boundary conditions for the bottom of the grid.
*/
static const uint8_t BC_SIDE_BOTTOM = 3;
/**
* Defines the boundary condition type and according value.
*/
typedef struct boundary_condition_s {
bctype type; /**< Type of the boundary condition */
double value; /**< Value of the boundary condition. Either a concrete value of
concentration for BC_TYPE_CONSTANT or gradient when type is
BC_TYPE_FLUX. Unused if BC_TYPE_CLOSED.*/
} boundary_condition;
/**
* Internal use only.
*/
typedef std::array<boundary_condition, 2> bc_tuple;
/**
* Class to define the boundary condition of a grid.
*/
class BTCSBoundaryCondition {
public:
/**
* Creates a new instance with two elements. Used when defining boundary
* conditions of 1D grids.
*/
BTCSBoundaryCondition();
/**
* Creates a new instance with 4 * max(x,y) elements. Used to describe the
* boundary conditions for 2D grids. NOTE: On non-squared grids there are more
* elements than needed and no exception is thrown if some index exceeding
* grid limits.
*
* \param x Number of grid cells in x-direction
* \param y Number of grid cells in y-direction
*
*/
BTCSBoundaryCondition(int x, int y);
/**
* Sets the boundary condition for a specific side of the grid.
*
* \param side Side for which the given boundary condition should be set.
* \param input_bc Instance of struct boundary_condition with desired boundary
* condition.
*
* \throws std::invalid_argument Indicates wrong dimensions of the grid.
* \throws std::out_of_range Indicates a out of range value for side.
*/
void setSide(uint8_t side, boundary_condition &input_bc);
/**
* Sets the boundary condition for a specific side of the grid.
*
* \param side Side for which the given boundary condition should be set.
* \param input_bc Vector of boundary conditions for specific side.
*
* \throws std::invalid_argument Indicates wrong dimensions of the grid.
* \throws std::out_of_range Indicates a out of range value for side or
* invalid size of input vector.
*/
void setSide(uint8_t side, std::vector<boundary_condition> &input_bc);
/**
* Returns a vector of boundary conditions of given side. Can be used to set
* custom boundary conditions and set back via setSide() with vector input.
*
* \param side Side which boundary conditions should be returned
*
* \returns Vector of boundary conditions
*
* \throws std::invalid_argument If given dimension is less or equal to 1.
* \throws std::out_of_range Indicates a out of range value for side.
*/
auto getSide(uint8_t side) -> std::vector<boundary_condition>;
/**
* Get both boundary conditions of a given row (left and right).
*
* \param i Index of row
*
* \returns Left and right boundary values as an array (defined as data
* type bc_tuple).
*/
auto row(uint32_t i) const -> bc_tuple;
/**
* Get both boundary conditions of a given column (top and bottom).
*
* \param i Index of column
*
* \returns Top and bottom boundary values as an array (defined as data
* type bc_tuple).
*/
auto col(uint32_t i) const -> bc_tuple;
/**
* Create an instance of boundary_condition data type. Can be seen as a helper
* function.
*
* \param type Type of the boundary condition.
* \param value According value of condition.
*
* \returns Instance of boundary_condition
*/
static boundary_condition returnBoundaryCondition(bctype type, double value) {
return {type, value};
}
private:
std::vector<boundary_condition> bc_internal;
uint8_t dim;
uint32_t sizes[2];
uint32_t maxsize;
public:
boundary_condition operator()(uint8_t side) const {
if (dim != 1) {
throw std::invalid_argument(
"Only 1D grid support 1 parameter in operator");
}
if (side > 1) {
throw std::out_of_range("1D index out of range");
}
return bc_internal[side];
}
boundary_condition operator()(uint8_t side, uint32_t i) const {
if (dim != 2) {
throw std::invalid_argument(
"Only 2D grids support 2 parameters in operator");
}
if (side > 3) {
throw std::out_of_range("2D index out of range");
}
return bc_internal[side * maxsize + i];
}
boundary_condition &operator()(uint8_t side) {
if (dim != 1) {
throw std::invalid_argument(
"Only 1D grid support 1 parameter in operator");
}
if (side > 1) {
throw std::out_of_range("1D index out of range");
}
return bc_internal[side];
}
boundary_condition &operator()(uint8_t side, uint32_t i) {
if (dim != 2) {
throw std::invalid_argument("Explicit setting of bc value with 2 "
"parameters is only supported for 2D grids");
}
if (side > 3) {
throw std::out_of_range("2D index out of range");
}
return bc_internal[side * maxsize + i];
}
};
} // namespace Diffusion
#endif // BOUNDARYCONDITION_H_

View File

@ -1,158 +0,0 @@
#ifndef BTCSDIFFUSION_H_
#define BTCSDIFFUSION_H_
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <Eigen/Sparse>
#include <Eigen/src/Core/Map.h>
#include <Eigen/src/Core/Matrix.h>
#include <Eigen/src/Core/util/Constants.h>
#include <Eigen/src/SparseCore/SparseMatrix.h>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <vector>
namespace Diffusion {
/*!
* Class implementing a solution for a 1/2/3D diffusion equation using backward
* euler.
*/
class BTCSDiffusion {
public:
/*!
* Creates a diffusion module.
*
* \param dim Number of dimensions. Should not be greater than 3 and not less
* than 1.
*/
BTCSDiffusion(unsigned int dim);
/*!
* Define the grid in x direction.
*
* \param domain_size Size of the domain in x direction.
* \param n_grid_cells Number of grid cells in x direction the domain is
* divided to.
*/
void setXDimensions(double domain_size, unsigned int n_grid_cells);
/*!
* Define the grid in y direction.
*
* Throws an error if the module wasn't initialized at least as a 2D model.
*
* \param domain_size Size of the domain in y direction.
* \param n_grid_cells Number of grid cells in y direction the domain is
* divided to.
*/
void setYDimensions(double domain_size, unsigned int n_grid_cells);
/*!
* Define the grid in z direction.
*
* Throws an error if the module wasn't initialized at least as a 3D model.
*
* \param domain_size Size of the domain in z direction.
* \param n_grid_cells Number of grid cells in z direction the domain is
* divided to.
*/
void setZDimensions(double domain_size, unsigned int n_grid_cells);
/*!
* Returns the number of grid cells in x direction.
*/
auto getXGridCellsN() -> unsigned int;
/*!
* Returns the number of grid cells in y direction.
*/
auto getYGridCellsN() -> unsigned int;
/*!
* Returns the number of grid cells in z direction.
*/
auto getZGridCellsN() -> unsigned int;
/*!
* Returns the domain size in x direction.
*/
auto getXDomainSize() -> double;
/*!
* Returns the domain size in y direction.
*/
auto getYDomainSize() -> double;
/*!
* Returns the domain size in z direction.
*/
auto getZDomainSize() -> double;
/*!
* With given ghost zones simulate diffusion. Only 1D allowed at this moment.
*
* \param c Pointer to continious memory describing the current concentration
* state of each grid cell.
* \param alpha Pointer to memory area of diffusion coefficients for each grid
* element.
* \param bc Instance of boundary condition class with.
*
* \return Time in seconds [s] used to simulate one iteration.
*/
auto simulate(double *c, double *alpha, const BTCSBoundaryCondition &bc)
-> double;
/*!
* Set the timestep of the simulation
*
* \param time_step Time step (in seconds ???)
*/
void setTimestep(double time_step);
private:
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
DMatrixRowMajor;
typedef Eigen::Matrix<double, 1, Eigen::Dynamic, Eigen::RowMajor>
DVectorRowMajor;
static void simulate_base(DVectorRowMajor &c, const bc_tuple &bc,
const DVectorRowMajor &alpha, double dx, double time_step,
int size, const DVectorRowMajor &d_ortho);
void simulate1D(Eigen::Map<DVectorRowMajor> &c,
Eigen::Map<const DVectorRowMajor> &alpha,
const BTCSBoundaryCondition &bc);
void simulate2D(Eigen::Map<DMatrixRowMajor> &c,
Eigen::Map<const DMatrixRowMajor> &alpha,
const BTCSBoundaryCondition &bc);
static auto calc_d_ortho(const DMatrixRowMajor &c, const DMatrixRowMajor &alpha,
const BTCSBoundaryCondition &bc, bool transposed,
double time_step, double dx) -> DMatrixRowMajor;
static void fillMatrixFromRow(Eigen::SparseMatrix<double> &A_matrix,
const DVectorRowMajor &alpha, int size,
double dx, double time_step);
static void fillVectorFromRow(Eigen::VectorXd &b_vector,
const DVectorRowMajor &c,
const DVectorRowMajor &alpha,
const bc_tuple &bc,
const DVectorRowMajor &d_ortho, int size,
double dx, double time_step);
void simulate3D(std::vector<double> &c);
inline static auto getBCFromFlux(Diffusion::boundary_condition bc,
double neighbor_c, double neighbor_alpha)
-> double;
void updateInternals();
double time_step;
unsigned int grid_dim;
std::vector<unsigned int> grid_cells;
std::vector<double> domain_size;
std::vector<double> deltas;
};
} // namespace Diffusion
#endif // BTCSDIFFUSION_H_

View File

@ -1,15 +0,0 @@
#ifndef BTCSUTILS_H_
#define BTCSUTILS_H_
#include <stdexcept>
#include <string>
#define throw_invalid_argument(msg) \
throw std::invalid_argument(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + \
std::string(msg))
#define throw_out_of_range(msg) \
throw std::out_of_range(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + std::string(msg))
#endif // BTCSUTILS_H_

537
include/tug/Boundary.hpp Normal file
View File

@ -0,0 +1,537 @@
/**
* @file Boundary.hpp
* @brief API of Boundary class, that holds all information for each boundary
* condition at the edges of the diffusion grid.
*
*/
#ifndef BOUNDARY_H_
#define BOUNDARY_H_
#include "tug/Core/TugUtils.hpp"
#include <Eigen/Dense>
#include <cstddef>
#include <cstdint>
#include <map>
#include <stdexcept>
#include <utility>
#include <vector>
namespace tug {
/**
* @brief Enum defining the two implemented boundary conditions.
*
*/
enum BC_TYPE { BC_TYPE_CLOSED, BC_TYPE_CONSTANT };
/**
* @brief Enum defining all 4 possible sides to a 1D and 2D grid.
*
*/
enum BC_SIDE { BC_SIDE_LEFT, BC_SIDE_RIGHT, BC_SIDE_TOP, BC_SIDE_BOTTOM };
/**
* This class defines the boundary conditions of individual boundary elements.
* These can be flexibly used and combined later in other classes.
* The class serves as an auxiliary class for structuring the Boundary class.
*
* @tparam T Data type of the boundary condition element
*/
template <class T> class BoundaryElement {
public:
/**
* @brief Construct a new Boundary Element object for the closed case.
* The boundary type is here automatically set to the type
* BC_TYPE_CLOSED, where the value takes -1 and does not hold any
* physical meaning.
*/
BoundaryElement() {};
/**
* @brief Construct a new Boundary Element object for the constant case.
* The boundary type is automatically set to the type
* BC_TYPE_CONSTANT.
*
* @param value Value of the constant concentration to be assumed at the
* corresponding boundary element.
*/
BoundaryElement(T _value) : value(_value), type(BC_TYPE_CONSTANT) {}
/**
* @brief Allows changing the boundary type of a corresponding
* BoundaryElement object.
*
* @param type Type of boundary condition. Either BC_TYPE_CONSTANT or
BC_TYPE_CLOSED.
*/
void setType(BC_TYPE type) { this->type = type; };
/**
* @brief Sets the value of a boundary condition for the constant case.
*
* @param value Concentration to be considered constant for the
* corresponding boundary element.
*/
void setValue(double value) {
if (type == BC_TYPE_CLOSED) {
throw std::invalid_argument(
"No constant boundary concentrations can be set for closed "
"boundaries. Please change type first.");
}
this->value = value;
}
/**
* @brief Return the type of the boundary condition, i.e. whether the
* boundary is considered closed or constant.
*
* @return Type of boundary condition, either BC_TYPE_CLOSED or
BC_TYPE_CONSTANT.
*/
BC_TYPE getType() const { return this->type; }
/**
* @brief Return the concentration value for the constant boundary condition.
*
* @return Value of the concentration.
*/
T getValue() const { return this->value; }
private:
BC_TYPE type{BC_TYPE_CLOSED};
T value{-1};
};
/**
* This class implements the functionality and management of the boundary
* conditions in the grid to be simulated.
*
* @tparam Data type of the boundary condition value
*/
template <class T> class Boundary {
public:
/**
* @brief Creates a boundary object for a 1D grid
*
* @param length Length of the grid
*/
Boundary(std::uint32_t length) : Boundary(1, length) {};
/**
* @brief Creates a boundary object for a 2D grid
*
* @param rows Number of rows of the grid
* @param cols Number of columns of the grid
*/
Boundary(std::uint32_t rows, std::uint32_t cols)
: dim(rows == 1 ? 1 : 2), cols(cols), rows(rows) {
if (this->dim == 1) {
this->boundaries = std::vector<std::vector<BoundaryElement<T>>>(
2); // in 1D only left and right boundary
this->boundaries[BC_SIDE_LEFT].push_back(BoundaryElement<T>());
this->boundaries[BC_SIDE_RIGHT].push_back(BoundaryElement<T>());
} else if (this->dim == 2) {
this->boundaries = std::vector<std::vector<BoundaryElement<T>>>(4);
this->boundaries[BC_SIDE_LEFT] =
std::vector<BoundaryElement<T>>(this->rows, BoundaryElement<T>());
this->boundaries[BC_SIDE_RIGHT] =
std::vector<BoundaryElement<T>>(this->rows, BoundaryElement<T>());
this->boundaries[BC_SIDE_TOP] =
std::vector<BoundaryElement<T>>(this->cols, BoundaryElement<T>());
this->boundaries[BC_SIDE_BOTTOM] =
std::vector<BoundaryElement<T>>(this->cols, BoundaryElement<T>());
}
};
/**
* @brief Creates a boundary object based on the passed grid object and
* initializes the boundaries as closed.
*
* @param grid Grid object on the basis of which the simulation takes place
* and from which the dimensions (in 2D case) are taken.
*/
// Boundary(const Grid<T> &grid)
// : dim(grid.getDim()), cols(grid.getCol()), rows(grid.getRow()) {
// if (this->dim == 1) {
// this->boundaries = std::vector<std::vector<BoundaryElement<T>>>(
// 2); // in 1D only left and right boundary
//
// this->boundaries[BC_SIDE_LEFT].push_back(BoundaryElement<T>());
// this->boundaries[BC_SIDE_RIGHT].push_back(BoundaryElement<T>());
// } else if (this->dim == 2) {
// this->boundaries = std::vector<std::vector<BoundaryElement<T>>>(4);
//
// this->boundaries[BC_SIDE_LEFT] =
// std::vector<BoundaryElement<T>>(this->rows, BoundaryElement<T>());
// this->boundaries[BC_SIDE_RIGHT] =
// std::vector<BoundaryElement<T>>(this->rows, BoundaryElement<T>());
// this->boundaries[BC_SIDE_TOP] =
// std::vector<BoundaryElement<T>>(this->cols, BoundaryElement<T>());
// this->boundaries[BC_SIDE_BOTTOM] =
// std::vector<BoundaryElement<T>>(this->cols, BoundaryElement<T>());
// }
// }
//
/**
* @brief Sets all elements of the specified boundary side to the boundary
* condition closed.
*
* @param side Side to be set to closed, e.g. BC_SIDE_LEFT.
*/
void setBoundarySideClosed(BC_SIDE side) {
tug_assert((this->dim > 1) ||
((side == BC_SIDE_LEFT) || (side == BC_SIDE_RIGHT)),
"For the "
"one-dimensional case, only the BC_SIDE_LEFT and BC_SIDE_RIGHT "
"borders exist.");
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
const int n = is_vertical ? this->rows : this->cols;
this->boundaries[side] =
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>());
}
/**
* @brief Sets all elements of the specified boundary side to the boundary
* condition constant. Thereby the concentration values of the
* boundaries are set to the passed value.
*
* @param side Side to be set to constant, e.g. BC_SIDE_LEFT.
* @param value Concentration to be set for all elements of the specified
* page.
*/
void setBoundarySideConstant(BC_SIDE side, double value) {
tug_assert((this->dim > 1) ||
((side == BC_SIDE_LEFT) || (side == BC_SIDE_RIGHT)),
"For the "
"one-dimensional case, only the BC_SIDE_LEFT and BC_SIDE_RIGHT "
"borders exist.");
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
const int n = is_vertical ? this->rows : this->cols;
this->boundaries[side] =
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>(value));
}
/**
* @brief Specifically sets the boundary element of the specified side
* defined by the index to the boundary condition closed.
*
* @param side Side in which an element is to be defined as closed.
* @param index Index of the boundary element on the corresponding
* boundary side. Must index an element of the corresponding
* side.
*/
void setBoundaryElemenClosed(BC_SIDE side, int index) {
// tests whether the index really points to an element of the boundary side.
tug_assert(boundaries[side].size() > index && index >= 0,
"Index is selected either too large or too small.");
this->boundaries[side][index].setType(BC_TYPE_CLOSED);
}
/**
* @brief Specifically sets the boundary element of the specified side
* defined by the index to the boundary condition constant with the
given concentration value.
*
* @param side Side in which an element is to be defined as constant.
* @param index Index of the boundary element on the corresponding
* boundary side. Must index an element of the corresponding
side.
* @param value Concentration value to which the boundary element should be
set.
*/
void setBoundaryElementConstant(BC_SIDE side, int index, double value) {
// tests whether the index really points to an element of the boundary side.
tug_assert(boundaries[side].size() > index && index >= 0,
"Index is selected either too large or too small.");
this->boundaries[side][index].setType(BC_TYPE_CONSTANT);
this->boundaries[side][index].setValue(value);
}
/**
* @brief Returns the boundary condition of a specified side as a vector
* of BoundarsElement objects.
*
* @param side Boundary side from which the boundary conditions are to be
* returned.
* @return Contains the boundary conditions as
* BoundaryElement<T> objects.
*/
const std::vector<BoundaryElement<T>> &getBoundarySide(BC_SIDE side) const {
tug_assert((this->dim > 1) ||
((side == BC_SIDE_LEFT) || (side == BC_SIDE_RIGHT)),
"For the "
"one-dimensional case, only the BC_SIDE_LEFT and BC_SIDE_RIGHT "
"borders exist.");
return this->boundaries[side];
}
/**
* @brief Get thes Boundary Side Values as a vector. Value is -1 in case some
specific boundary is closed.
*
* @param side Boundary side for which the values are to be returned.
* @return Vector with values as T.
*/
Eigen::VectorX<T> getBoundarySideValues(BC_SIDE side) const {
const std::size_t length = boundaries[side].size();
Eigen::VectorX<T> values(length);
for (int i = 0; i < length; i++) {
if (getBoundaryElementType(side, i) == BC_TYPE_CLOSED) {
values(i) = -1;
continue;
}
values(i) = getBoundaryElementValue(side, i);
}
return values;
}
/**
* @brief Returns the boundary condition of a specified element on a given
* side.
*
* @param side Boundary side in which the boundary condition is located.
* @param index Index of the boundary element on the corresponding
* boundary side. Must index an element of the corresponding
* side.
* @return Boundary condition as a BoundaryElement<T>
* object.
*/
BoundaryElement<T> getBoundaryElement(BC_SIDE side, int index) const {
tug_assert(boundaries[side].size() > index && index >= 0,
"Index is selected either too large or too small.");
return this->boundaries[side][index];
}
/**
* @brief Returns the type of a boundary condition, i.e. either BC_TYPE_CLOSED
or BC_TYPE_CONSTANT.
*
* @param side Boundary side in which the boundary condition type is located.
* @param index Index of the boundary element on the corresponding
* boundary side. Must index an element of the corresponding
side.
* @return Boundary Type of the corresponding boundary condition.
*/
BC_TYPE getBoundaryElementType(BC_SIDE side, int index) const {
tug_assert(boundaries[side].size() > index && index >= 0,
"Index is selected either too large or too small.");
return this->boundaries[side][index].getType();
}
/**
* @brief Returns the concentration value of a corresponding
* BoundaryElement<T> object if it is a constant boundary condition.
*
* @param side Boundary side in which the boundary condition value is
* located.
* @param index Index of the boundary element on the corresponding
* boundary side. Must index an element of the corresponding
* side.
* @return Concentration of the corresponding BoundaryElement<T>
* object.
*/
T getBoundaryElementValue(BC_SIDE side, int index) const {
tug_assert(boundaries[side].size() > index && index >= 0,
"Index is selected either too large or too small.");
tug_assert(
boundaries[side][index].getType() == BC_TYPE_CONSTANT,
"A value can only be output if it is a constant boundary condition.");
return this->boundaries[side][index].getValue();
}
/**
* @brief Serializes the boundary conditions into a vector of BoundaryElement
* objects.
*
* @return Vector with BoundaryElement objects.
*/
std::vector<BoundaryElement<T>> serialize() const {
std::vector<BoundaryElement<T>> serialized;
for (std::size_t side = 0; side < boundaries.size(); side++) {
for (std::size_t index = 0; index < boundaries[side].size(); index++) {
serialized.push_back(boundaries[side][index]);
}
}
return serialized;
}
/**
* @brief Deserializes the boundary conditions from a vector of
* BoundaryElement objects.
*
* @param serialized Vector with BoundaryElement objects.
*/
void deserialize(const std::vector<BoundaryElement<T>> &serialized) {
std::size_t index = 0;
for (std::size_t side = 0; side < boundaries.size(); side++) {
for (std::size_t i = 0; i < boundaries[side].size(); i++) {
boundaries[side][i] = serialized[index];
index++;
}
}
}
/**
*
* @param index Index of the inner constant boundary condition
* @param value Value of the inner constant boundary condition
*/
void setInnerBoundary(std::uint32_t index, T value) {
tug_assert(this->dim == 1, "This function is only available for 1D grids.");
tug_assert(index < this->cols, "Index is out of bounds.");
this->inner_boundary[std::make_pair(0, index)] = value;
}
/**
* @brief Set inner constant boundary condition in 2D case.
*
* @param row Row index of the inner constant boundary condition
* @param col Column index of the inner constant boundary condition
* @param value Value of the inner constant boundary condition
*/
void setInnerBoundary(std::uint32_t row, std::uint32_t col, T value) {
tug_assert(this->dim == 2, "This function is only available for 2D grids.");
tug_assert(row < this->rows && col < this->cols, "Index is out of bounds.");
this->inner_boundary[std::make_pair(row, col)] = value;
}
/**
* @brief Get inner constant boundary condition in 1D case.
*
* @param index Index of the inner constant boundary condition
* @return std::pair<bool, T> Pair of boolean (whether constant boundary was
* set or not) and value of the inner constant boundary condition
*/
std::pair<bool, T> getInnerBoundary(std::uint32_t index) const {
tug_assert(this->dim == 1, "This function is only available for 1D grids.");
tug_assert(index < this->cols, "Index is out of bounds.");
auto it = this->inner_boundary.find(std::make_pair(0, index));
if (it == this->inner_boundary.end()) {
return std::make_pair(false, -1);
}
return std::make_pair(true, it->second);
}
/**
* @brief Get inner constant boundary condition in 2D case.
*
* @param row Row index of the inner constant boundary condition
* @param col Column index of the inner constant boundary condition
* @return std::pair<bool, T> Pair of boolean (whether constant boundary was
* set or not) and value of the inner constant boundary condition
*/
std::pair<bool, T> getInnerBoundary(std::uint32_t row,
std::uint32_t col) const {
tug_assert(this->dim == 2, "This function is only available for 2D grids.");
tug_assert(row < this->rows && col < this->cols, "Index is out of bounds.");
auto it = this->inner_boundary.find(std::make_pair(row, col));
if (it == this->inner_boundary.end()) {
return std::make_pair(false, -1);
}
return std::make_pair(true, it->second);
}
/**
* @brief Get inner constant boundary conditions of a row as a vector. Can be
* used for 1D grids (row == 0) or 2D grids.
*
* @param row Index of the row for which the inner boundary conditions are to
* be returned.
* @return std::vector<std::pair<bool, T>> Vector of pairs of boolean (whether
* constant boundary was set or not) and value of the inner constant boundary
* condition
*/
std::vector<std::pair<bool, T>> getInnerBoundaryRow(std::uint32_t row) const {
tug_assert(row < this->rows, "Index is out of bounds.");
if (inner_boundary.empty()) {
return std::vector<std::pair<bool, T>>(this->cols,
std::make_pair(false, -1));
}
std::vector<std::pair<bool, T>> row_values;
for (std::uint32_t col = 0; col < this->cols; col++) {
row_values.push_back(getInnerBoundary(row, col));
}
return row_values;
}
/**
* @brief Get inner constant boundary conditions of a column as a vector. Can
* only be used for 2D grids.
*
* @param col Index of the column for which the inner boundary conditions are
* to be returned.
* @return std::vector<std::pair<bool, T>> Vector of pairs of boolean (whether
* constant boundary was set or not) and value of the inner constant boundary
* condition
*/
std::vector<std::pair<bool, T>> getInnerBoundaryCol(std::uint32_t col) const {
tug_assert(this->dim == 2, "This function is only available for 2D grids.");
tug_assert(col < this->cols, "Index is out of bounds.");
if (inner_boundary.empty()) {
return std::vector<std::pair<bool, T>>(this->rows,
std::make_pair(false, -1));
}
std::vector<std::pair<bool, T>> col_values;
for (std::uint32_t row = 0; row < this->rows; row++) {
col_values.push_back(getInnerBoundary(row, col));
}
return col_values;
}
/**
* @brief Get inner constant boundary conditions as a map. Can be read by
* setInnerBoundaries.
*
* @return Map of inner constant boundary conditions
*/
std::map<std::pair<std::uint32_t, std::uint32_t>, T>
getInnerBoundaries() const {
return this->inner_boundary;
}
/**
* @brief Set inner constant boundary conditions as a map. Can be obtained by
* getInnerBoundaries.
*
* @param inner_boundary Map of inner constant boundary conditions
*/
void
setInnerBoundaries(const std::map<std::pair<std::uint32_t, std::uint32_t>, T>
&inner_boundary) {
this->inner_boundary = inner_boundary;
}
private:
const std::uint8_t dim;
const std::uint32_t cols;
const std::uint32_t rows;
std::vector<std::vector<BoundaryElement<T>>>
boundaries; // Vector with Boundary Element information
// Inner boundary conditions for 1D and 2D grids identified by (row,col)
std::map<std::pair<std::uint32_t, std::uint32_t>, T> inner_boundary;
};
} // namespace tug
#endif // BOUNDARY_H_

View File

@ -0,0 +1,388 @@
#pragma once
#include "tug/Boundary.hpp"
#include <cstddef>
#include <cstdint>
#include <tug/Core/Matrix.hpp>
#include <tug/Core/TugUtils.hpp>
namespace tug {
/**
* @brief Enum holding different options for .csv output.
*
*/
enum class CSV_OUTPUT {
OFF, /*!< do not produce csv output */
ON, /*!< produce csv output with last concentration matrix */
VERBOSE, /*!< produce csv output with all concentration matrices */
XTREME /*!< csv output like VERBOSE but additional boundary
conditions at beginning */
};
/**
* @brief Enum holding different options for console output.
*
*/
enum class CONSOLE_OUTPUT {
OFF, /*!< do not print any output to console */
ON, /*!< print before and after concentrations to console */
VERBOSE /*!< print all concentration matrices to console */
};
/**
* @brief Enum holding different options for time measurement.
*
*/
enum class TIME_MEASURE {
OFF, /*!< do not print any time measures */
ON /*!< print time measure after last iteration */
};
/**
* @brief A base class for simulation grids.
*
* This class provides a base implementation for simulation grids, including
* methods for setting and getting grid dimensions, domain sizes, and output
* options. It also includes methods for running simulations, which must be
* implemented by derived classes.
*
* @tparam T The type of the elements in the grid.
*/
template <typename T> class BaseSimulationGrid {
private:
CSV_OUTPUT csv_output{CSV_OUTPUT::OFF};
CONSOLE_OUTPUT console_output{CONSOLE_OUTPUT::OFF};
TIME_MEASURE time_measure{TIME_MEASURE::OFF};
int iterations{1};
RowMajMatMap<T> concentrationMatrix;
Boundary<T> boundaryConditions;
const std::uint8_t dim;
T delta_col;
T delta_row;
public:
/**
* @brief Constructs a BaseSimulationGrid from a given RowMajMat object.
*
* This constructor initializes a BaseSimulationGrid using the data, number of
* rows, and number of columns from the provided RowMajMat object.
*
* @tparam T The type of the elements in the RowMajMat.
* @param origin The RowMajMat object from which to initialize the
* BaseSimulationGrid.
*/
BaseSimulationGrid(RowMajMat<T> &origin)
: BaseSimulationGrid(origin.data(), origin.rows(), origin.cols()) {}
/**
* @brief Constructs a BaseSimulationGrid object.
*
* @tparam T The type of the data elements.
* @param data Pointer to the data array.
* @param rows Number of rows in the grid.
* @param cols Number of columns in the grid.
*
* Initializes the concentration_matrix with the provided data, rows, and
* columns. Sets delta_col and delta_row to 1. Determines the dimension (dim)
* based on the number of rows: if rows == 1, dim is set to 1; otherwise, dim
* is set to 2.
*/
BaseSimulationGrid(T *data, std::size_t rows, std::size_t cols)
: concentrationMatrix(data, rows, cols), boundaryConditions(rows, cols),
delta_col(1), delta_row(1), dim(rows == 1 ? 1 : 2) {}
/**
* @brief Constructs a BaseSimulationGrid with a single dimension.
*
* This constructor initializes a BaseSimulationGrid object with the provided
* data and length. It assumes the grid has only one dimension.
*
* @param data Pointer to the data array.
* @param length The length of the data array.
*/
BaseSimulationGrid(T *data, std::size_t length)
: BaseSimulationGrid(data, 1, length) {}
/**
* @brief Overloaded function call operator to access elements in a
* one-dimensional grid.
*
* This operator provides access to elements in the concentration matrix using
* a single index. It asserts that the grid is one-dimensional before
* accessing the element.
*
* @tparam T The type of elements in the concentration matrix.
* @param index The index of the element to access.
* @return A reference to the element at the specified index in the
* concentration matrix.
*/
constexpr T &operator()(std::size_t index) {
tug_assert(dim == 1, "Grid is not one dimensional, use 2D index operator!");
return concentrationMatrix(index);
}
/**
* @brief Overloaded function call operator to access elements in a 2D
* concentration matrix.
*
* This operator allows accessing elements in the concentration matrix using
* row and column indices. It asserts that the grid is two-dimensional before
* accessing the element.
*
* @param row The row index of the element to access.
* @param col The column index of the element to access.
* @return A reference to the element at the specified row and column in the
* concentration matrix.
*/
constexpr T &operator()(std::size_t row, std::size_t col) {
tug_assert(dim == 2, "Grid is not two dimensional, use 1D index operator!");
return concentrationMatrix(row, col);
}
/**
* @brief Retrieves the concentration matrix.
*
* @tparam T The data type of the elements in the concentration matrix.
* @return RowMajMat<T>& Reference to the concentration matrix.
*/
RowMajMatMap<T> &getConcentrationMatrix() { return concentrationMatrix; }
const RowMajMatMap<T> &getConcentrationMatrix() const {
return concentrationMatrix;
}
/**
* @brief Retrieves the boundary conditions for the simulation.
*
* @tparam T The type parameter for the Boundary class.
* @return Boundary<T>& A reference to the boundary conditions.
*/
Boundary<T> &getBoundaryConditions() { return boundaryConditions; }
const Boundary<T> &getBoundaryConditions() const {
return boundaryConditions;
}
/**
* @brief Retrieves the dimension value.
*
* @return The dimension value as an 8-bit unsigned integer.
*/
std::uint8_t getDim() const { return dim; }
/**
* @brief Returns the number of rows in the concentration matrix.
*
* @return std::size_t The number of rows in the concentration matrix.
*/
std::size_t rows() const { return concentrationMatrix.rows(); }
/**
* @brief Get the number of columns in the concentration matrix.
*
* @return std::size_t The number of columns in the concentration matrix.
*/
std::size_t cols() const { return concentrationMatrix.cols(); }
/**
* @brief Returns the cell size in meter of the x-direction.
*
* This function returns the value of the delta column, which is used
* to represent the difference or change in the column value.
*
* @return T The cell size in meter of the x-direction.
*/
T deltaCol() const { return delta_col; }
/**
* @brief Returns the cell size in meter of the y-direction.
*
* This function asserts that the grid is two-dimensional. If the grid is not
* two-dimensional, an assertion error is raised with the message "Grid is not
* two dimensional, there is no delta in y-direction!".
*
* @return The cell size in meter of the y-direction.
*/
T deltaRow() const {
tug_assert(
dim == 2,
"Grid is not two dimensional, there is no delta in y-direction!");
return delta_row;
}
/**
* @brief Computes the domain size in the X direction.
*
* This function calculates the size of the domain in the X direction by
* multiplying the column spacing (delta_col) by the number of columns (cols).
*
* @return The size of the domain in the X direction.
*/
T domainX() const { return delta_col * cols(); }
/**
* @brief Returns the size of the domain in the y-direction.
*
* This function calculates the size of the domain in the y-direction
* by multiplying the row spacing (delta_row) by the number of rows.
* It asserts that the grid is two-dimensional before performing the
* calculation.
*
* @return The size of the domain in the y-direction.
*/
T domainY() const {
tug_assert(
dim == 2,
"Grid is not two dimensional, there is no domain in y-direction!");
return delta_row * rows();
}
/**
* @brief Sets the domain length for a one-dimensional grid.
*
* This function sets the domain length for a one-dimensional grid and
* calculates the column width (delta_col) based on the given domain length
* and the number of columns. It asserts that the grid is one-dimensional and
* that the given domain length is positive.
*
* @param domain_length The length of the domain. Must be positive.
*/
void setDomain(T domain_length) {
tug_assert(dim == 1, "Grid is not one dimensional, use 2D domain setter!");
tug_assert(domain_length > 0, "Given domain length is not positive!");
delta_col = domain_length / cols();
}
/**
* @brief Sets the domain size for a 2D grid simulation.
*
* This function sets the domain size in the x and y directions for a
* two-dimensional grid simulation. It asserts that the grid is indeed
* two-dimensional and that the provided domain sizes are positive.
*
* @tparam T The type of the domain size parameters.
* @param domain_row The size of the domain in the y-direction.
* @param domain_col The size of the domain in the x-direction.
*/
void setDomain(T domain_row, T domain_col) {
tug_assert(dim == 2, "Grid is not two dimensional, use 1D domain setter!");
tug_assert(domain_col > 0,
"Given domain size in x-direction is not positive!");
tug_assert(domain_row > 0,
"Given domain size in y-direction is not positive!");
delta_row = domain_row / rows();
delta_col = domain_col / cols();
}
/**
* @brief Set the option to output the results to a CSV file. Off by default.
*
*
* @param csv_output Valid output option. The following options can be set
* here:
* - CSV_OUTPUT_OFF: do not produce csv output
* - CSV_OUTPUT_ON: produce csv output with last
* concentration matrix
* - CSV_OUTPUT_VERBOSE: produce csv output with all
* concentration matrices
* - CSV_OUTPUT_XTREME: produce csv output with all
* concentration matrices and simulation environment
*/
void setOutputCSV(CSV_OUTPUT csv_output) { this->csv_output = csv_output; }
/**
* @brief Retrieves the CSV output.
*
* This function returns the CSV output associated with the simulation.
*
* @return CSV_OUTPUT The CSV output of the simulation.
*/
constexpr CSV_OUTPUT getOutputCSV() const { return this->csv_output; }
/**
* @brief Set the options for outputting information to the console. Off by
* default.
*
* @param console_output Valid output option. The following options can be set
* here:
* - CONSOLE_OUTPUT_OFF: do not print any output to
* console
* - CONSOLE_OUTPUT_ON: print before and after
* concentrations to console
* - CONSOLE_OUTPUT_VERBOSE: print all concentration
* matrices to console
*/
void setOutputConsole(CONSOLE_OUTPUT console_output) {
this->console_output = console_output;
}
/**
* @brief Retrieves the console output.
*
* This function returns the current state of the console output.
*
* @return CONSOLE_OUTPUT The current console output.
*/
constexpr CONSOLE_OUTPUT getOutputConsole() const {
return this->console_output;
}
/**
* @brief Set the Time Measure option. Off by default.
*
* @param time_measure The following options are allowed:
* - TIME_MEASURE_OFF: Time of simulation is not printed
* to console
* - TIME_MEASURE_ON: Time of simulation run is printed to
* console
*/
void setTimeMeasure(TIME_MEASURE time_measure) {
this->time_measure = time_measure;
}
/**
* @brief Retrieves the current time measurement.
*
* @return TIME_MEASURE The current time measurement.
*/
constexpr TIME_MEASURE getTimeMeasure() const { return this->time_measure; }
/**
* @brief Set the desired iterations to be calculated. A value greater
* than zero must be specified here. Setting iterations is required.
*
* @param iterations Number of iterations to be simulated.
*/
void setIterations(int iterations) {
tug_assert(iterations > 0,
"Number of iterations must be greater than zero.");
this->iterations = iterations;
}
/**
* @brief Return the currently set iterations to be calculated.
*
* @return int Number of iterations.
*/
int getIterations() const { return this->iterations; }
/**
* @brief Method starts the simulation process with the previously set
* parameters.
*/
virtual void run() = 0;
virtual void setTimestep(T timestep) = 0;
};
} // namespace tug

View File

@ -0,0 +1,21 @@
#pragma once
#include <Eigen/Core>
namespace tug {
/**
* @brief Alias template for a row-major matrix using Eigen library.
*
* This alias template defines a type `RowMajMat` which represents a row-major
* matrix using the Eigen library. It is a template that takes a type `T` as its
* template parameter. The matrix is dynamically sized with `Eigen::Dynamic` for
* both rows and columns. The matrix is stored in row-major order.
*
* @tparam T The type of the matrix elements.
*/
template <typename T>
using RowMajMat =
Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
template <typename T> using RowMajMatMap = Eigen::Map<RowMajMat<T>>;
} // namespace tug

View File

@ -0,0 +1,484 @@
/**
* @file BTCS.hpp
* @brief Implementation of heterogenous BTCS (backward time-centered space)
* solution of diffusion equation in 1D and 2D space. Internally the
* alternating-direction implicit (ADI) method is used. Version 2, because
* Version 1 was an implementation for the homogeneous BTCS solution.
*
*/
#ifndef BTCS_H_
#define BTCS_H_
#include <cstddef>
#include <tug/Boundary.hpp>
#include <tug/Core/Matrix.hpp>
#include <tug/Core/Numeric/SimulationInput.hpp>
#include <utility>
#include <vector>
#include <Eigen/Dense>
#include <Eigen/Sparse>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num() 0
#endif
namespace tug {
// optimization to remove Eigen sparse matrix
template <class T> class Diagonals {
public:
Diagonals() : left(), center(), right() {};
Diagonals(std::size_t size) : left(size), center(size), right(size) {};
public:
std::vector<T> left;
std::vector<T> center;
std::vector<T> right;
};
// calculates coefficient for boundary in constant case
template <class T>
constexpr std::pair<T, T> calcBoundaryCoeffConstant(T alpha_center,
T alpha_side, T sx) {
const T centerCoeff = 1 + sx * (calcAlphaIntercell(alpha_center, alpha_side) +
2 * alpha_center);
const T sideCoeff = -sx * calcAlphaIntercell(alpha_center, alpha_side);
return {centerCoeff, sideCoeff};
}
// calculates coefficient for boundary in closed case
template <class T>
constexpr std::pair<T, T> calcBoundaryCoeffClosed(T alpha_center, T alpha_side,
T sx) {
const T centerCoeff = 1 + sx * calcAlphaIntercell(alpha_center, alpha_side);
const T sideCoeff = -sx * calcAlphaIntercell(alpha_center, alpha_side);
return {centerCoeff, sideCoeff};
}
// creates coefficient matrix for next time step from alphas in x-direction
template <class T>
static Diagonals<T>
createCoeffMatrix(const RowMajMat<T> &alpha,
const std::vector<BoundaryElement<T>> &bcLeft,
const std::vector<BoundaryElement<T>> &bcRight,
const std::vector<std::pair<bool, T>> &inner_bc, int numCols,
int rowIndex, T sx) {
// square matrix of column^2 dimension for the coefficients
Diagonals<T> cm(numCols);
// left column
if (inner_bc[0].first) {
cm.center[0] = 1;
} else {
switch (bcLeft[rowIndex].getType()) {
case BC_TYPE_CONSTANT: {
auto [centerCoeffTop, rightCoeffTop] =
calcBoundaryCoeffConstant(alpha(rowIndex, 0), alpha(rowIndex, 1), sx);
cm.center[0] = centerCoeffTop;
cm.right[0] = rightCoeffTop;
break;
}
case BC_TYPE_CLOSED: {
auto [centerCoeffTop, rightCoeffTop] =
calcBoundaryCoeffClosed(alpha(rowIndex, 0), alpha(rowIndex, 1), sx);
cm.center[0] = centerCoeffTop;
cm.right[0] = rightCoeffTop;
break;
}
default: {
throw_invalid_argument(
"Undefined Boundary Condition Type somewhere on Left or Top!");
}
}
}
// inner columns
int n = numCols - 1;
for (int i = 1; i < n; i++) {
if (inner_bc[i].first) {
cm.center[i] = 1;
continue;
}
cm.left[i] =
-sx * calcAlphaIntercell(alpha(rowIndex, i - 1), alpha(rowIndex, i));
cm.center[i] =
1 +
sx * (calcAlphaIntercell(alpha(rowIndex, i), alpha(rowIndex, i + 1)) +
calcAlphaIntercell(alpha(rowIndex, i - 1), alpha(rowIndex, i)));
cm.right[i] =
-sx * calcAlphaIntercell(alpha(rowIndex, i), alpha(rowIndex, i + 1));
}
// right column
if (inner_bc[n].first) {
cm.center[n] = 1;
} else {
switch (bcRight[rowIndex].getType()) {
case BC_TYPE_CONSTANT: {
auto [centerCoeffBottom, leftCoeffBottom] = calcBoundaryCoeffConstant(
alpha(rowIndex, n), alpha(rowIndex, n - 1), sx);
cm.left[n] = leftCoeffBottom;
cm.center[n] = centerCoeffBottom;
break;
}
case BC_TYPE_CLOSED: {
auto [centerCoeffBottom, leftCoeffBottom] = calcBoundaryCoeffClosed(
alpha(rowIndex, n), alpha(rowIndex, n - 1), sx);
cm.left[n] = leftCoeffBottom;
cm.center[n] = centerCoeffBottom;
break;
}
default: {
throw_invalid_argument(
"Undefined Boundary Condition Type somewhere on Right or Bottom!");
}
}
}
return cm;
}
// calculates explicit concentration at boundary in closed case
template <typename T>
constexpr T calcExplicitConcentrationsBoundaryClosed(T conc_center,
T alpha_center,
T alpha_neigbor, T sy) {
return sy * calcAlphaIntercell(alpha_center, alpha_neigbor) * conc_center +
(1 - sy * (calcAlphaIntercell(alpha_center, alpha_neigbor))) *
conc_center;
}
// calculates explicity concentration at boundary in constant case
template <typename T>
constexpr T calcExplicitConcentrationsBoundaryConstant(T conc_center, T conc_bc,
T alpha_center,
T alpha_neighbor, T sy) {
const T inter_cell = calcAlphaIntercell(alpha_center, alpha_neighbor);
return sy * inter_cell * conc_center +
(1 - sy * (inter_cell + alpha_center)) * conc_center +
sy * alpha_center * conc_bc;
}
// creates a solution vector for next time step from the current state of
// concentrations
template <class T, class EigenType>
static Eigen::VectorX<T>
createSolutionVector(const EigenType &concentrations,
const RowMajMat<T> &alphaX, const RowMajMat<T> &alphaY,
const std::vector<BoundaryElement<T>> &bcLeft,
const std::vector<BoundaryElement<T>> &bcRight,
const std::vector<BoundaryElement<T>> &bcTop,
const std::vector<BoundaryElement<T>> &bcBottom,
const std::vector<std::pair<bool, T>> &inner_bc,
int length, int rowIndex, T sx, T sy) {
Eigen::VectorX<T> sv(length);
const std::size_t numRows = concentrations.rows();
// inner rows
if (rowIndex > 0 && rowIndex < numRows - 1) {
for (int i = 0; i < length; i++) {
if (inner_bc[i].first) {
sv(i) = inner_bc[i].second;
continue;
}
sv(i) =
sy *
calcAlphaIntercell(alphaY(rowIndex, i), alphaY(rowIndex + 1, i)) *
concentrations(rowIndex + 1, i) +
(1 - sy * (calcAlphaIntercell(alphaY(rowIndex, i),
alphaY(rowIndex + 1, i)) +
calcAlphaIntercell(alphaY(rowIndex - 1, i),
alphaY(rowIndex, i)))) *
concentrations(rowIndex, i) +
sy *
calcAlphaIntercell(alphaY(rowIndex - 1, i), alphaY(rowIndex, i)) *
concentrations(rowIndex - 1, i);
}
}
// first row
else if (rowIndex == 0) {
for (int i = 0; i < length; i++) {
if (inner_bc[i].first) {
sv(i) = inner_bc[i].second;
continue;
}
switch (bcTop[i].getType()) {
case BC_TYPE_CONSTANT: {
sv(i) = calcExplicitConcentrationsBoundaryConstant(
concentrations(rowIndex, i), bcTop[i].getValue(),
alphaY(rowIndex, i), alphaY(rowIndex + 1, i), sy);
break;
}
case BC_TYPE_CLOSED: {
sv(i) = calcExplicitConcentrationsBoundaryClosed(
concentrations(rowIndex, i), alphaY(rowIndex, i),
alphaY(rowIndex + 1, i), sy);
break;
}
default:
throw_invalid_argument(
"Undefined Boundary Condition Type somewhere on Left or Top!");
}
}
}
// last row
else if (rowIndex == numRows - 1) {
for (int i = 0; i < length; i++) {
if (inner_bc[i].first) {
sv(i) = inner_bc[i].second;
continue;
}
switch (bcBottom[i].getType()) {
case BC_TYPE_CONSTANT: {
sv(i) = calcExplicitConcentrationsBoundaryConstant(
concentrations(rowIndex, i), bcBottom[i].getValue(),
alphaY(rowIndex, i), alphaY(rowIndex - 1, i), sy);
break;
}
case BC_TYPE_CLOSED: {
sv(i) = calcExplicitConcentrationsBoundaryClosed(
concentrations(rowIndex, i), alphaY(rowIndex, i),
alphaY(rowIndex - 1, i), sy);
break;
}
default:
throw_invalid_argument(
"Undefined Boundary Condition Type somewhere on Right or Bottom!");
}
}
}
// first column -> additional fixed concentration change from perpendicular
// dimension in constant bc case
if (bcLeft[rowIndex].getType() == BC_TYPE_CONSTANT && !inner_bc[0].first) {
sv(0) += 2 * sx * alphaX(rowIndex, 0) * bcLeft[rowIndex].getValue();
}
// last column -> additional fixed concentration change from perpendicular
// dimension in constant bc case
if (bcRight[rowIndex].getType() == BC_TYPE_CONSTANT &&
!inner_bc[length - 1].first) {
sv(length - 1) +=
2 * sx * alphaX(rowIndex, length - 1) * bcRight[rowIndex].getValue();
}
return sv;
}
// solver for linear equation system; A corresponds to coefficient matrix,
// b to the solution vector
// use of EigenLU solver
template <class T>
static Eigen::VectorX<T> EigenLUAlgorithm(Diagonals<T> &A,
Eigen::VectorX<T> &b) {
// convert A to Eigen sparse matrix
size_t dim_A = A.center.size();
Eigen::SparseMatrix<T> A_sparse(dim_A, dim_A);
A_sparse.insert(0, 0) = A.center[0];
A_sparse.insert(0, 1) = A.right[0];
for (size_t i = 1; i < dim_A - 1; i++) {
A_sparse.insert(i, i - 1) = A.left[i];
A_sparse.insert(i, i) = A.center[i];
A_sparse.insert(i, i + 1) = A.right[i];
}
A_sparse.insert(dim_A - 1, dim_A - 2) = A.left[dim_A - 1];
A_sparse.insert(dim_A - 1, dim_A - 1) = A.center[dim_A - 1];
Eigen::SparseLU<Eigen::SparseMatrix<T>> solver;
solver.analyzePattern(A_sparse);
solver.factorize(A_sparse);
return solver.solve(b);
}
// solver for linear equation system; A corresponds to coefficient matrix,
// b to the solution vector
// implementation of Thomas Algorithm
template <class T>
static Eigen::VectorX<T> ThomasAlgorithm(Diagonals<T> &A,
Eigen::VectorX<T> &b) {
Eigen::Index n = b.size();
Eigen::VectorX<T> x_vec = b;
// HACK: write CSV to file
#ifdef WRITE_THOMAS_CSV
#include <fstream>
#include <string>
static std::uint32_t file_index = 0;
std::string file_name = "Thomas_" + std::to_string(file_index++) + ".csv";
std::ofstream out_file;
out_file.open(file_name, std::ofstream::trunc | std::ofstream::out);
// print header
out_file << "Aa, Ab, Ac, b\n";
// iterate through all elements
for (std::size_t i = 0; i < n; i++) {
out_file << a_diag[i] << ", " << b_diag[i] << ", " << c_diag[i] << ", "
<< b[i] << "\n";
}
out_file.close();
#endif
// start solving - c_diag and x_vec are overwritten
n--;
A.right[0] /= A.center[0];
x_vec[0] /= A.center[0];
for (Eigen::Index i = 1; i < n; i++) {
A.right[i] /= A.center[i] - A.left[i] * A.right[i - 1];
x_vec[i] = (x_vec[i] - A.left[i] * x_vec[i - 1]) /
(A.center[i] - A.left[i] * A.right[i - 1]);
}
x_vec[n] = (x_vec[n] - A.left[n] * x_vec[n - 1]) /
(A.center[n] - A.left[n] * A.right[n - 1]);
for (Eigen::Index i = n; i-- > 0;) {
x_vec[i] -= A.right[i] * x_vec[i + 1];
}
return x_vec;
}
// BTCS solution for 1D grid
template <class T>
static void BTCS_1D(SimulationInput<T> &input,
Eigen::VectorX<T> (*solverFunc)(Diagonals<T> &A,
Eigen::VectorX<T> &b)) {
const std::size_t &length = input.colMax;
T sx = input.timestep / (input.deltaCol * input.deltaCol);
Eigen::VectorX<T> concentrations_t1(length);
Diagonals<T> A;
Eigen::VectorX<T> b(length);
const auto &alpha = input.alphaX;
const auto &bc = input.boundaries;
const auto &bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
const auto &bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
const auto inner_bc = bc.getInnerBoundaryRow(0);
RowMajMatMap<T> &concentrations = input.concentrations;
int rowIndex = 0;
A = createCoeffMatrix(alpha, bcLeft, bcRight, inner_bc, length, rowIndex,
sx); // this is exactly same as in 2D
for (int i = 0; i < length; i++) {
b(i) = concentrations(0, i);
}
if (bc.getBoundaryElementType(BC_SIDE_LEFT, 0) == BC_TYPE_CONSTANT &&
!inner_bc[0].first) {
b(0) += 2 * sx * alpha(0, 0) * bcLeft[0].getValue();
}
if (bc.getBoundaryElementType(BC_SIDE_RIGHT, 0) == BC_TYPE_CONSTANT &&
!inner_bc[length - 1].first) {
b(length - 1) += 2 * sx * alpha(0, length - 1) * bcRight[0].getValue();
}
concentrations = solverFunc(A, b);
}
// BTCS solution for 2D grid
template <class T>
static void BTCS_2D(SimulationInput<T> &input,
Eigen::VectorX<T> (*solverFunc)(Diagonals<T> &A,
Eigen::VectorX<T> &b),
int numThreads) {
const std::size_t &rowMax = input.rowMax;
const std::size_t &colMax = input.colMax;
const T sx = input.timestep / (2 * input.deltaCol * input.deltaCol);
const T sy = input.timestep / (2 * input.deltaRow * input.deltaRow);
RowMajMat<T> concentrations_t1(rowMax, colMax);
Diagonals<T> A;
Eigen::VectorX<T> b;
const RowMajMat<T> &alphaX = input.alphaX;
const RowMajMat<T> &alphaY = input.alphaY;
const auto &bc = input.boundaries;
const auto &bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
const auto &bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
const auto &bcTop = bc.getBoundarySide(BC_SIDE_TOP);
const auto &bcBottom = bc.getBoundarySide(BC_SIDE_BOTTOM);
RowMajMatMap<T> &concentrations = input.concentrations;
#pragma omp parallel for num_threads(numThreads) private(A, b)
for (int i = 0; i < rowMax; i++) {
auto inner_bc = bc.getInnerBoundaryRow(i);
A = createCoeffMatrix(alphaX, bcLeft, bcRight, inner_bc, colMax, i, sx);
b = createSolutionVector(concentrations, alphaX, alphaY, bcLeft, bcRight,
bcTop, bcBottom, inner_bc, colMax, i, sx, sy);
concentrations_t1.row(i) = solverFunc(A, b);
}
concentrations_t1.transposeInPlace();
const RowMajMat<T> alphaX_t = alphaX.transpose();
const RowMajMat<T> alphaY_t = alphaY.transpose();
#pragma omp parallel for num_threads(numThreads) private(A, b)
for (int i = 0; i < colMax; i++) {
auto inner_bc = bc.getInnerBoundaryCol(i);
// swap alphas, boundary conditions and sx/sy for column-wise calculation
A = createCoeffMatrix(alphaY_t, bcTop, bcBottom, inner_bc, rowMax, i, sy);
b = createSolutionVector(concentrations_t1, alphaY_t, alphaX_t, bcTop,
bcBottom, bcLeft, bcRight, inner_bc, rowMax, i, sy,
sx);
concentrations.col(i) = solverFunc(A, b);
}
}
// entry point for EigenLU solver; differentiate between 1D and 2D grid
template <class T> void BTCS_LU(SimulationInput<T> &input, int numThreads) {
tug_assert(input.dim <= 2,
"Error: Only 1- and 2-dimensional grids are defined!");
if (input.dim == 1) {
BTCS_1D(input, EigenLUAlgorithm);
} else {
BTCS_2D(input, EigenLUAlgorithm, numThreads);
}
}
// entry point for Thomas algorithm solver; differentiate 1D and 2D grid
template <class T> void BTCS_Thomas(SimulationInput<T> &input, int numThreads) {
tug_assert(input.dim <= 2,
"Error: Only 1- and 2-dimensional grids are defined!");
if (input.dim == 1) {
BTCS_1D(input, ThomasAlgorithm);
} else {
BTCS_2D(input, ThomasAlgorithm, numThreads);
}
}
} // namespace tug
#endif // BTCS_H_

View File

@ -0,0 +1,240 @@
/**
* @file FTCS.hpp
* @brief Implementation of heterogenous FTCS (forward time-centered space)
* solution of diffusion equation in 1D and 2D space.
*
*/
#ifndef FTCS_H_
#define FTCS_H_
#include "tug/Core/TugUtils.hpp"
#include <cstddef>
#include <cstring>
#include <tug/Boundary.hpp>
#include <tug/Core/Matrix.hpp>
#include <tug/Core/Numeric/SimulationInput.hpp>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num() 0
#endif
namespace tug {
template <class T>
constexpr T calcChangeInner(T conc_c, T conc_left, T conc_right, T alpha_c,
T alpha_left, T alpha_right) {
const T alpha_center_left = calcAlphaIntercell(alpha_left, alpha_c);
const T alpha_center_right = calcAlphaIntercell(alpha_right, alpha_c);
return alpha_center_left * conc_left -
(alpha_center_left + alpha_center_right) * conc_c +
alpha_center_right * conc_right;
}
template <class T>
constexpr T calcChangeBoundary(T conc_c, T conc_neighbor, T alpha_center,
T alpha_neighbor, const BoundaryElement<T> &bc) {
const T alpha_center_neighbor =
calcAlphaIntercell(alpha_center, alpha_neighbor);
const T &conc_boundary = bc.getValue();
switch (bc.getType()) {
case BC_TYPE_CONSTANT: {
return 2 * alpha_center * conc_boundary -
(alpha_center_neighbor + 2 * alpha_center) * conc_c +
alpha_center_neighbor * conc_neighbor;
}
case BC_TYPE_CLOSED: {
return (alpha_center_neighbor * (conc_neighbor - conc_c));
}
}
tug_assert(false, "Undefined Boundary Condition Type!");
}
// FTCS solution for 1D grid
template <class T> static void FTCS_1D(SimulationInput<T> &input) {
const std::size_t &colMax = input.colMax;
const T &deltaCol = input.deltaCol;
const T &timestep = input.timestep;
RowMajMatMap<T> &concentrations_grid = input.concentrations;
// matrix for concentrations at time t+1
RowMajMat<T> concentrations_t1 = concentrations_grid;
const auto &alphaX = input.alphaX;
const auto &bc = input.boundaries;
// only one row in 1D case -> row constant at index 0
int row = 0;
// inner cells
// independent of boundary condition type
for (int col = 1; col < colMax - 1; col++) {
const T &conc_c = concentrations_grid(row, col);
const T &conc_left = concentrations_grid(row, col - 1);
const T &conc_right = concentrations_grid(row, col + 1);
const T &alpha_c = alphaX(row, col);
const T &alpha_left = alphaX(row, col - 1);
const T &alpha_right = alphaX(row, col + 1);
concentrations_t1(row, col) =
concentrations_grid(row, col) +
timestep / (deltaCol * deltaCol) *
calcChangeInner(conc_c, conc_left, conc_right, alpha_c, alpha_left,
alpha_right);
}
// left boundary; hold column constant at index 0
{
int col = 0;
const T &conc_c = concentrations_grid(row, col);
const T &conc_right = concentrations_grid(row, col + 1);
const T &alpha_c = alphaX(row, col);
const T &alpha_right = alphaX(row, col + 1);
const BoundaryElement<T> &bc_element =
input.boundaries.getBoundaryElement(BC_SIDE_LEFT, row);
concentrations_t1(row, col) =
concentrations_grid(row, col) +
timestep / (deltaCol * deltaCol) *
calcChangeBoundary(conc_c, conc_right, alpha_c, alpha_right,
bc_element);
}
// right boundary; hold column constant at max index
{
int col = colMax - 1;
const T &conc_c = concentrations_grid(row, col);
const T &conc_left = concentrations_grid(row, col - 1);
const T &alpha_c = alphaX(row, col);
const T &alpha_left = alphaX(row, col - 1);
const BoundaryElement<T> &bc_element =
bc.getBoundaryElement(BC_SIDE_RIGHT, row);
concentrations_t1(row, col) =
concentrations_grid(row, col) +
timestep / (deltaCol * deltaCol) *
calcChangeBoundary(conc_c, conc_left, alpha_c, alpha_left,
bc_element);
}
// overwrite obsolete concentrations
concentrations_grid = concentrations_t1;
}
// FTCS solution for 2D grid
template <class T>
static void FTCS_2D(SimulationInput<T> &input, int numThreads) {
const std::size_t &rowMax = input.rowMax;
const std::size_t &colMax = input.colMax;
const T &deltaRow = input.deltaRow;
const T &deltaCol = input.deltaCol;
const T &timestep = input.timestep;
RowMajMatMap<T> &concentrations_grid = input.concentrations;
// matrix for concentrations at time t+1
RowMajMat<T> concentrations_t1 = concentrations_grid;
const auto &alphaX = input.alphaX;
const auto &alphaY = input.alphaY;
const auto &bc = input.boundaries;
const T sx = timestep / (deltaCol * deltaCol);
const T sy = timestep / (deltaRow * deltaRow);
#pragma omp parallel for num_threads(numThreads)
for (std::size_t row_i = 0; row_i < rowMax; row_i++) {
for (std::size_t col_i = 0; col_i < colMax; col_i++) {
// horizontal change
T horizontal_change;
{
const T &conc_c = concentrations_grid(row_i, col_i);
const T &alpha_c = alphaX(row_i, col_i);
if (col_i == 0 || col_i == colMax - 1) {
// left or right boundary
const T &conc_neigbor =
concentrations_grid(row_i, col_i == 0 ? col_i + 1 : col_i - 1);
const T &alpha_neigbor =
alphaX(row_i, col_i == 0 ? col_i + 1 : col_i - 1);
const BoundaryElement<T> &bc_element = bc.getBoundaryElement(
col_i == 0 ? BC_SIDE_LEFT : BC_SIDE_RIGHT, row_i);
horizontal_change = calcChangeBoundary(conc_c, conc_neigbor, alpha_c,
alpha_neigbor, bc_element);
} else {
// inner cell
const T &conc_left = concentrations_grid(row_i, col_i - 1);
const T &conc_right = concentrations_grid(row_i, col_i + 1);
const T &alpha_left = alphaX(row_i, col_i - 1);
const T &alpha_right = alphaX(row_i, col_i + 1);
horizontal_change = calcChangeInner(conc_c, conc_left, conc_right,
alpha_c, alpha_left, alpha_right);
}
}
// vertical change
T vertical_change;
{
const T &conc_c = concentrations_grid(row_i, col_i);
const T &alpha_c = alphaY(row_i, col_i);
if (row_i == 0 || row_i == rowMax - 1) {
// top or bottom boundary
const T &conc_neigbor =
concentrations_grid(row_i == 0 ? row_i + 1 : row_i - 1, col_i);
const T &alpha_neigbor =
alphaY(row_i == 0 ? row_i + 1 : row_i - 1, col_i);
const BoundaryElement<T> &bc_element = bc.getBoundaryElement(
row_i == 0 ? BC_SIDE_TOP : BC_SIDE_BOTTOM, col_i);
vertical_change = calcChangeBoundary(conc_c, conc_neigbor, alpha_c,
alpha_neigbor, bc_element);
} else {
// inner cell
const T &conc_bottom = concentrations_grid(row_i - 1, col_i);
const T &conc_top = concentrations_grid(row_i + 1, col_i);
const T &alpha_bottom = alphaY(row_i - 1, col_i);
const T &alpha_top = alphaY(row_i + 1, col_i);
vertical_change = calcChangeInner(conc_c, conc_bottom, conc_top,
alpha_c, alpha_bottom, alpha_top);
}
}
concentrations_t1(row_i, col_i) = concentrations_grid(row_i, col_i) +
sx * horizontal_change +
sy * vertical_change;
}
}
// overwrite obsolete concentrations
concentrations_grid = concentrations_t1;
}
// entry point; differentiate between 1D and 2D grid
template <class T> void FTCS(SimulationInput<T> &input, int &numThreads) {
tug_assert(input.dim <= 2,
"Error: Only 1- and 2-dimensional grids are defined!");
if (input.dim == 1) {
FTCS_1D(input);
} else {
FTCS_2D(input, numThreads);
}
}
} // namespace tug
#endif // FTCS_H_

View File

@ -0,0 +1,21 @@
#pragma once
#include <tug/Boundary.hpp>
#include <tug/Core/Matrix.hpp>
namespace tug {
template <typename T> struct SimulationInput {
RowMajMatMap<T> &concentrations;
const RowMajMat<T> &alphaX;
const RowMajMat<T> &alphaY;
const Boundary<T> boundaries;
const std::uint8_t dim;
const T timestep;
const std::size_t rowMax;
const std::size_t colMax;
const T deltaRow;
const T deltaCol;
};
} // namespace tug

View File

@ -0,0 +1,39 @@
#pragma once
#include <cassert>
#define throw_invalid_argument(msg) \
throw std::invalid_argument(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + \
std::string(msg))
#define throw_out_of_range(msg) \
throw std::out_of_range(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + std::string(msg))
#define time_marker() std::chrono::high_resolution_clock::now()
#define diff_time(start, end) \
({ \
std::chrono::duration<double> duration = \
std::chrono::duration_cast<std::chrono::duration<double>>(end - \
start); \
duration.count(); \
})
#define tug_assert(expr, msg) assert((expr) && msg)
// calculates arithmetic or harmonic mean of alpha between two cells
template <typename T>
constexpr T calcAlphaIntercell(T alpha1, T alpha2, bool useHarmonic = true) {
if (useHarmonic) {
const T operand1 = alpha1 == 0 ? 0 : 1 / alpha1;
const T operand2 = alpha2 == 0 ? 0 : 1 / alpha2;
const T denom = operand1 + operand2;
return denom == 0 ? 0 : 2. / denom;
} else {
return 0.5 * (alpha1 + alpha2);
}
}

454
include/tug/Diffusion.hpp Normal file
View File

@ -0,0 +1,454 @@
/**
* @file Diffusion.hpp
* @brief API of Diffusion class, that holds all information regarding a
* specific simulation run like its timestep, number of iterations and output
* options. Diffusion object also holds a predefined Grid and Boundary object.
*
*/
#pragma once
#include "tug/Core/Matrix.hpp"
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <tug/Core/BaseSimulation.hpp>
#include <tug/Core/Numeric/BTCS.hpp>
#include <tug/Core/Numeric/FTCS.hpp>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_num_procs() 1
#endif
namespace tug {
/**
* @brief Enum defining the implemented solution approaches.
*
*/
enum APPROACH {
FTCS_APPROACH, /*!< Forward Time-Centered Space */
BTCS_APPROACH, /*!< Backward Time-Centered Space */
CRANK_NICOLSON_APPROACH /*!< Crank-Nicolson method */
};
/**
* @brief Enum defining the Linear Equation solvers
*
*/
enum SOLVER {
EIGEN_LU_SOLVER, /*!< EigenLU solver */
THOMAS_ALGORITHM_SOLVER /*!< Thomas Algorithm solver; more efficient for
tridiagonal matrices */
};
/**
* @brief The class forms the interface for performing the diffusion simulations
* and contains all the methods for controlling the desired parameters, such as
* time step, number of simulations, etc.
*
* @tparam T the type of the internal data structures for grid, boundary
* condition and timestep
* @tparam approach Set the SLE scheme to be used
* @tparam solver Set the solver to be used
*/
template <class T, APPROACH approach = BTCS_APPROACH,
SOLVER solver = THOMAS_ALGORITHM_SOLVER>
class Diffusion : public BaseSimulationGrid<T> {
private:
T timestep{-1};
int innerIterations{1};
int numThreads{omp_get_num_procs()};
RowMajMat<T> alphaX;
RowMajMat<T> alphaY;
const std::vector<std::string> approach_names = {"FTCS", "BTCS", "CRNI"};
static constexpr T DEFAULT_ALPHA = 1E-8;
void init_alpha() {
this->alphaX =
RowMajMat<T>::Constant(this->rows(), this->cols(), DEFAULT_ALPHA);
if (this->getDim() == 2) {
this->alphaY =
RowMajMat<T>::Constant(this->rows(), this->cols(), DEFAULT_ALPHA);
}
}
public:
/**
* @brief Construct a new Diffusion object from a given Eigen matrix
*
*/
Diffusion(RowMajMat<T> &origin) : BaseSimulationGrid<T>(origin) {
init_alpha();
}
/**
* @brief Construct a new 2D Diffusion object from a given data pointer and
* the dimensions.
*
*/
Diffusion(T *data, int rows, int cols)
: BaseSimulationGrid<T>(data, rows, cols) {
init_alpha();
}
/**
* @brief Construct a new 1D Diffusion object from a given data pointer and
* the length.
*
*/
Diffusion(T *data, std::size_t length) : BaseSimulationGrid<T>(data, length) {
init_alpha();
}
/**
* @brief Get the alphaX matrix.
*
* @return RowMajMat<T>& Reference to the alphaX matrix.
*/
RowMajMat<T> &getAlphaX() { return alphaX; }
/**
* @brief Get the alphaY matrix.
*
* @return RowMajMat<T>& Reference to the alphaY matrix.
*/
RowMajMat<T> &getAlphaY() {
tug_assert(
this->getDim(),
"Grid is not two dimensional, there is no domain in y-direction!");
return alphaY;
}
/**
* @brief Set the alphaX matrix.
*
* @param alphaX The new alphaX matrix.
*/
void setAlphaX(const RowMajMat<T> &alphaX) { this->alphaX = alphaX; }
/**
* @brief Set the alphaY matrix.
*
* @param alphaY The new alphaY matrix.
*/
void setAlphaY(const RowMajMat<T> &alphaY) {
tug_assert(
this->getDim(),
"Grid is not two dimensional, there is no domain in y-direction!");
this->alphaY = alphaY;
}
/**
* @brief Setting the time step for each iteration step. Time step must be
* greater than zero. Setting the timestep is required.
*
* @param timestep Valid timestep greater than zero.
*/
void setTimestep(T timestep) override {
tug_assert(timestep > 0, "Timestep has to be greater than zero.");
if constexpr (approach == FTCS_APPROACH ||
approach == CRANK_NICOLSON_APPROACH) {
T cfl;
if (this->getDim() == 1) {
const T deltaSquare = this->deltaCol();
const T maxAlpha = this->alphaX.maxCoeff();
// Courant-Friedrichs-Lewy condition
cfl = deltaSquare / (4 * maxAlpha);
} else if (this->getDim() == 2) {
const T deltaColSquare = this->deltaCol() * this->deltaCol();
// will be 0 if 1D, else ...
const T deltaRowSquare = this->deltaRow() * this->deltaRow();
const T minDeltaSquare = std::min(deltaColSquare, deltaRowSquare);
const T maxAlpha =
std::max(this->alphaX.maxCoeff(), this->alphaY.maxCoeff());
cfl = minDeltaSquare / (4 * maxAlpha);
}
const std::string dim = std::to_string(this->getDim()) + "D";
const std::string &approachPrefix = this->approach_names[approach];
std::cout << approachPrefix << "_" << dim << " :: CFL condition: " << cfl
<< std::endl;
std::cout << approachPrefix << "_" << dim
<< " :: required dt=" << timestep << std::endl;
if (timestep > cfl) {
this->innerIterations = (int)ceil(timestep / cfl);
this->timestep = timestep / (double)innerIterations;
std::cerr << "Warning :: Timestep was adjusted, because of stability "
"conditions. Time duration was approximately preserved by "
"adjusting internal number of iterations."
<< std::endl;
std::cout << approachPrefix << "_" << dim << " :: Required "
<< this->innerIterations
<< " inner iterations with dt=" << this->timestep
<< std::endl;
} else {
this->timestep = timestep;
std::cout << approachPrefix << "_" << dim
<< " :: No inner iterations required, dt=" << timestep
<< std::endl;
}
} else {
this->timestep = timestep;
}
}
/**
* @brief Currently set time step is returned.
*
* @return double timestep
*/
T getTimestep() const { return this->timestep; }
/**
* @brief Set the number of desired openMP Threads.
*
* @param num_threads Number of desired threads. Must have a value between
* 1 and the maximum available number of processors. The
* maximum number of processors is set as the default case during Simulation
* construction.
*/
void setNumberThreads(int numThreads) {
if (numThreads > 0 && numThreads <= omp_get_num_procs()) {
this->numThreads = numThreads;
} else {
int maxThreadNumber = omp_get_num_procs();
throw std::invalid_argument(
"Number of threads exceeds the number of processor cores (" +
std::to_string(maxThreadNumber) + ") or is less than 1.");
}
}
/**
* @brief Outputs the current concentrations of the grid on the console.
*
*/
void printConcentrationsConsole() const {
std::cout << this->getConcentrationMatrix() << std::endl;
std::cout << std::endl;
}
/**
* @brief Creates a CSV file with a name containing the current simulation
* parameters. If the data name already exists, an additional counter
* is appended to the name. The name of the file is built up as follows:
* <approach> + <number rows> + <number columns> + <number of
* iterations>+<counter>.csv
*
* @return string Filename with configured simulation parameters.
*/
std::string createCSVfile() const {
std::ofstream file;
int appendIdent = 0;
std::string appendIdentString;
// string approachString = (approach == 0) ? "FTCS" : "BTCS";
const std::string &approachString = this->approach_names[approach];
std::string row = std::to_string(this->rows());
std::string col = std::to_string(this->cols());
std::string numIterations = std::to_string(this->getIterations());
std::string filename =
approachString + "_" + row + "_" + col + "_" + numIterations + ".csv";
while (std::filesystem::exists(filename)) {
appendIdent += 1;
appendIdentString = std::to_string(appendIdent);
filename = approachString + "_" + row + "_" + col + "_" + numIterations +
"-" + appendIdentString + ".csv";
}
file.open(filename);
if (!file) {
exit(1);
}
// adds lines at the beginning of verbose output csv that represent the
// boundary conditions and their values -1 in case of closed boundary
if (this->getOutputCSV() == CSV_OUTPUT::XTREME) {
const auto &bc = this->getBoundaryConditions();
Eigen::IOFormat one_row(Eigen::StreamPrecision, Eigen::DontAlignCols, "",
" ");
file << bc.getBoundarySideValues(BC_SIDE_LEFT).format(one_row)
<< std::endl; // boundary left
file << bc.getBoundarySideValues(BC_SIDE_RIGHT).format(one_row)
<< std::endl; // boundary right
file << bc.getBoundarySideValues(BC_SIDE_TOP).format(one_row)
<< std::endl; // boundary top
file << bc.getBoundarySideValues(BC_SIDE_BOTTOM).format(one_row)
<< std::endl; // boundary bottom
file << std::endl << std::endl;
}
file.close();
return filename;
}
/**
* @brief Writes the currently calculated concentration values of the grid
* into the CSV file with the passed filename.
*
* @param filename Name of the file to which the concentration values are
* to be written.
*/
void printConcentrationsCSV(const std::string &filename) const {
std::ofstream file;
file.open(filename, std::ios_base::app);
if (!file) {
exit(1);
}
Eigen::IOFormat do_not_align(Eigen::StreamPrecision, Eigen::DontAlignCols);
file << this->getConcentrationMatrix().format(do_not_align) << std::endl;
file << std::endl << std::endl;
file.close();
}
/**
* @brief Method starts the simulation process with the previously set
* parameters.
*/
void run() override {
tug_assert(this->getTimestep() > 0, "Timestep is not set!");
tug_assert(this->getIterations() > 0, "Number of iterations are not set!");
std::string filename;
if (this->getOutputConsole() > CONSOLE_OUTPUT::OFF) {
printConcentrationsConsole();
}
if (this->getOutputCSV() > CSV_OUTPUT::OFF) {
filename = createCSVfile();
}
auto begin = std::chrono::high_resolution_clock::now();
SimulationInput<T> sim_input = {.concentrations =
this->getConcentrationMatrix(),
.alphaX = this->getAlphaX(),
.alphaY = this->getAlphaY(),
.boundaries = this->getBoundaryConditions(),
.dim = this->getDim(),
.timestep = this->getTimestep(),
.rowMax = this->rows(),
.colMax = this->cols(),
.deltaRow = this->deltaRow(),
.deltaCol = this->deltaCol()};
if constexpr (approach == FTCS_APPROACH) { // FTCS case
for (int i = 0; i < this->getIterations() * innerIterations; i++) {
if (this->getOutputConsole() == CONSOLE_OUTPUT::VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (this->getOutputCSV() >= CSV_OUTPUT::VERBOSE) {
printConcentrationsCSV(filename);
}
FTCS(sim_input, this->numThreads);
// if (i % (iterations * innerIterations / 100) == 0) {
// double percentage = (double)i / ((double)iterations *
// (double)innerIterations) * 100; if ((int)percentage % 10 == 0) {
// cout << "Progress: " << percentage << "%" << endl;
// }
// }
}
} else if constexpr (approach == BTCS_APPROACH) { // BTCS case
if constexpr (solver == EIGEN_LU_SOLVER) {
for (int i = 0; i < this->getIterations(); i++) {
if (this->getOutputConsole() == CONSOLE_OUTPUT::VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (this->getOutputCSV() >= CSV_OUTPUT::VERBOSE) {
printConcentrationsCSV(filename);
}
BTCS_LU(sim_input, this->numThreads);
}
} else if constexpr (solver == THOMAS_ALGORITHM_SOLVER) {
for (int i = 0; i < this->getIterations(); i++) {
if (this->getOutputConsole() == CONSOLE_OUTPUT::VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (this->getOutputCSV() >= CSV_OUTPUT::VERBOSE) {
printConcentrationsCSV(filename);
}
BTCS_Thomas(sim_input, this->numThreads);
}
}
} else if constexpr (approach ==
CRANK_NICOLSON_APPROACH) { // Crank-Nicolson case
constexpr T beta = 0.5;
// TODO this implementation is very inefficient!
// a separate implementation that sets up a specific tridiagonal matrix
// for Crank-Nicolson would be better
RowMajMat<T> concentrations;
RowMajMat<T> concentrationsFTCS;
RowMajMat<T> concentrationsResult;
for (int i = 0; i < this->getIterations() * innerIterations; i++) {
if (this->getOutputConsole() == CONSOLE_OUTPUT::VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (this->getOutputCSV() >= CSV_OUTPUT::VERBOSE) {
printConcentrationsCSV(filename);
}
concentrations = this->getConcentrationMatrix();
FTCS(this->grid, this->bc, this->timestep, this->numThreads);
concentrationsFTCS = this->getConcentrationMatrix();
this->getConcentrationMatrix() = concentrations;
BTCS_Thomas(sim_input, this->numThreads);
concentrationsResult = beta * concentrationsFTCS +
(1 - beta) * this->getConcentrationMatrix();
this->getConcentrationMatrix() = concentrationsResult;
}
}
auto end = std::chrono::high_resolution_clock::now();
auto milliseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
if (this->getOutputConsole() > CONSOLE_OUTPUT::OFF) {
printConcentrationsConsole();
}
if (this->getOutputCSV() > CSV_OUTPUT::OFF) {
printConcentrationsCSV(filename);
}
if (this->getTimeMeasure() > TIME_MEASURE::OFF) {
const std::string &approachString = this->approach_names[approach];
const std::string dimString = std::to_string(this->getDim()) + "D";
std::cout << approachString << dimString << ":: run() finished in "
<< milliseconds.count() << "ms" << std::endl;
}
}
};
} // namespace tug

167
naaice/BTCS_2D_NAAICE.cpp Normal file
View File

@ -0,0 +1,167 @@
#include <Eigen/Eigen>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <tug/Diffusion.hpp>
#include <vector>
#include "files.hpp"
using namespace tug;
/**
* Try to parse an input string into a given template type.
*/
template <typename T> inline T parseString(const std::string &str) {
T result;
std::istringstream iss(str);
if (!(iss >> result)) {
throw std::invalid_argument("Invalid input for parsing.");
}
return result;
}
/**
* Splits a given string into a vector by using a delimiter character.
*/
template <typename T>
std::vector<T> tokenize(const std::string &input, char delimiter) {
std::vector<T> tokens;
std::istringstream tokenStream(input);
std::string token;
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(parseString<T>(token));
}
return tokens;
}
/**
* Opens a file containing CSV and transform it into row-major 2D STL vector.
*/
template <typename T>
std::vector<std::vector<T>> CSVToVector(const char *filename) {
std::ifstream in_file(filename);
if (!in_file.is_open()) {
throw std::runtime_error("Error opening file \'" + std::string(filename) +
"\'.");
}
std::vector<std::vector<T>> csv_data;
std::string line;
while (std::getline(in_file, line)) {
csv_data.push_back(tokenize<T>(line, ','));
}
in_file.close();
return csv_data;
}
/**
* Converts a 2D STL vector, where values are stored row-major into a
* column-major Eigen::Matrix.
*/
template <typename T>
Eigen::MatrixXd rmVecTocmMatrix(const std::vector<std::vector<T>> &vec,
std::uint32_t exp_rows,
std::uint32_t exp_cols) {
if (exp_rows != vec.size()) {
throw std::runtime_error(
"Mismatch in y dimension while converting to Eigen::Matrix.");
}
Eigen::MatrixXd out_mat(exp_rows, exp_cols);
for (std::uint32_t ri = 0; ri < exp_rows; ri++) {
const auto &vec_row = vec[ri];
if (vec[ri].size() != exp_cols) {
throw std::runtime_error(
"Mismatch in x dimension while converting to Eigen::Matrix.");
}
for (std::uint32_t cj = 0; cj < exp_cols; cj++) {
out_mat(ri, cj) = vec_row[cj];
}
}
return out_mat;
}
int main(int argc, char *argv[]) {
// EASY_PROFILER_ENABLE;
// profiler::startListen();
// **************
// **** GRID ****
// **************
// profiler::startListen();
// create a grid with a 5 x 10 field
constexpr int row = 5;
constexpr int col = 10;
// (optional) set the domain, e.g.:
const auto init_values_vec = CSVToVector<double>(INPUT_CONC_FILE);
Eigen::MatrixXd concentrations = rmVecTocmMatrix(init_values_vec, row, col);
Grid64 grid(concentrations);
grid.setDomain(0.005, 0.01);
const double sum_init = concentrations.sum();
// // (optional) set alphas of the grid, e.g.:
const auto alphax_vec = CSVToVector<double>(INPUT_ALPHAX_FILE);
Eigen::MatrixXd alphax = rmVecTocmMatrix(alphax_vec, row, col);
constexpr double alphay_val = 5e-10;
Eigen::MatrixXd alphay =
Eigen::MatrixXd::Constant(row, col, alphay_val); // row,col,value
grid.setAlpha(alphax, alphay);
// // ******************
// // **** BOUNDARY ****
// // ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
bc.setBoundarySideClosed(BC_SIDE_LEFT);
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
bc.setBoundarySideClosed(BC_SIDE_TOP);
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
// // ************************
// // **** SIMULATION ENV ****
// // ************************
// set up a simulation environment
Diffusion simulation(grid, bc); // grid,boundary
// set the timestep of the simulation
simulation.setTimestep(360); // timestep
// set the number of iterations
simulation.setIterations(1);
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_ON);
// set output to the console to 'ON'
simulation.setOutputConsole(CONSOLE_OUTPUT_ON);
// // **** RUN SIMULATION ****
simulation.run();
const double sum_after = grid.getConcentrations().sum();
std::cout << "Sum of init field: " << std::to_string(sum_init)
<< "\nSum after iteration: " << std::to_string(sum_after)
<< std::endl;
return 0;
}

14
naaice/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
add_executable(naaice BTCS_2D_NAAICE.cpp)
add_executable(NAAICE_dble_vs_float NAAICE_dble_vs_float.cpp)
get_filename_component(IN_CONC_FILE "init_conc.csv" REALPATH)
get_filename_component(IN_ALPHAX_FILE "alphax.csv" REALPATH)
configure_file(files.hpp.in files.hpp)
target_include_directories(naaice PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(NAAICE_dble_vs_float PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_compile_definitions(naaice PRIVATE WRITE_THOMAS_CSV)
target_link_libraries(naaice PUBLIC tug)
target_link_libraries(NAAICE_dble_vs_float PUBLIC tug)

View File

@ -0,0 +1,191 @@
#include <Eigen/Eigen>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <files.hpp>
#include <tug/Diffusion.hpp>
using namespace tug;
/**
* Try to parse an input string into a given template type.
*/
template <typename T> inline T parseString(const std::string &str) {
T result;
std::istringstream iss(str);
if (!(iss >> result)) {
throw std::invalid_argument("Invalid input for parsing.");
}
return result;
}
/**
* Splits a given string into a vector by using a delimiter character.
*/
template <typename T>
std::vector<T> tokenize(const std::string &input, char delimiter) {
std::vector<T> tokens;
std::istringstream tokenStream(input);
std::string token;
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(parseString<T>(token));
}
return tokens;
}
/**
* Opens a file containing CSV and transform it into row-major 2D STL vector.
*/
template <typename T>
std::vector<std::vector<T>> CSVToVector(const char *filename) {
std::ifstream in_file(filename);
if (!in_file.is_open()) {
throw std::runtime_error("Error opening file \'" + std::string(filename) +
"\'.");
}
std::vector<std::vector<T>> csv_data;
std::string line;
while (std::getline(in_file, line)) {
csv_data.push_back(tokenize<T>(line, ','));
}
in_file.close();
return csv_data;
}
/**
* Converts a 2D STL vector, where values are stored row-major into a
* column-major Eigen::Matrix.
*/
template <typename T>
Eigen::MatrixXd rmVecTocmMatrix(const std::vector<std::vector<T>> &vec,
std::uint32_t exp_rows,
std::uint32_t exp_cols) {
if (exp_rows != vec.size()) {
throw std::runtime_error(
"Mismatch in y dimension while converting to Eigen::Matrix.");
}
Eigen::MatrixXd out_mat(exp_rows, exp_cols);
for (std::uint32_t ri = 0; ri < exp_rows; ri++) {
const auto &vec_row = vec[ri];
if (vec[ri].size() != exp_cols) {
throw std::runtime_error(
"Mismatch in x dimension while converting to Eigen::Matrix.");
}
for (std::uint32_t cj = 0; cj < exp_cols; cj++) {
out_mat(ri, cj) = vec_row[cj];
}
}
return out_mat;
}
template <class T, tug::APPROACH app> int doWork(int ngrid) {
constexpr T dt = 10;
// create a grid
std::string name;
if constexpr (std::is_same_v<T, double>) {
name = "DOUBLE";
} else if constexpr (std::is_same_v<T, float>) {
name = "FLOAT";
} else {
name = "unknown";
}
std::cout << name << " grid: " << ngrid << std::endl;
// Grid64 grid(ngrid, ngrid);
// (optional) set the domain, e.g.:
Eigen::MatrixX<T> initConc64 = Eigen::MatrixX<T>::Constant(ngrid, ngrid, 0);
initConc64(50, 50) = 1E-6;
Grid<T> grid(initConc64);
grid.setDomain(0.1, 0.1);
const T sum_init64 = initConc64.sum();
constexpr T alphax_val = 5e-10;
Eigen::MatrixX<T> alphax =
Eigen::MatrixX<T>::Constant(ngrid, ngrid, alphax_val); // row,col,value
constexpr T alphay_val = 1e-10;
Eigen::MatrixX<T> alphay =
Eigen::MatrixX<T>::Constant(ngrid, ngrid, alphay_val); // row,col,value
grid.setAlpha(alphax, alphay);
// create a boundary with constant values
Boundary bc = Boundary(grid);
bc.setBoundarySideClosed(BC_SIDE_LEFT);
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
bc.setBoundarySideClosed(BC_SIDE_TOP);
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
// set up a simulation environment
Diffusion Sim(grid, bc); // grid_64,boundary,simulation-approach
// Sim64.setSolver(THOMAS_ALGORITHM_SOLVER);
// set the timestep of the simulation
Sim.setTimestep(dt); // timestep
// set the number of iterations
Sim.setIterations(2);
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
Sim.setOutputCSV(CSV_OUTPUT_ON);
// set output to the console to 'ON'
Sim.setOutputConsole(CONSOLE_OUTPUT_OFF);
// // **** RUN SIM64 ****
auto begin_t = std::chrono::high_resolution_clock::now();
Sim.run();
auto end_t = std::chrono::high_resolution_clock::now();
auto ms_t =
std::chrono::duration_cast<std::chrono::milliseconds>(end_t - begin_t);
const double sum_after64 = grid.getConcentrations().sum();
std::cout << "Sum of init field: " << std::setprecision(15) << sum_init64
<< "\nSum after 2 iterations: " << sum_after64
<< "\nMilliseconds: " << ms_t.count() << std::endl
<< std::endl;
return 0;
}
int main(int argc, char *argv[]) {
int n[] = {101, 201, 501, 1001, 2001};
for (int i = 0; i < std::size(n); i++) {
doWork<float, tug::BTCS_APPROACH>(n[i]);
doWork<double, tug::BTCS_APPROACH>(n[i]);
doWork<float, tug::FTCS_APPROACH>(n[i]);
doWork<double, tug::FTCS_APPROACH>(n[i]);
}
return 0;
}

70
naaice/README.md Normal file
View File

@ -0,0 +1,70 @@
This directory contains a concise benchmark designed for validating FPGA
offloading of the Thomas algorithm, primarily employed for solving linear
equation systems structured within a tridiagonal matrix.
# Benchmark Setup
The benchmark defines a domain measuring $1 \text{cm} \times 0.5 \text{cm}$ (easting $\times$ northing),
discretized in a $10 \times 5$ grid. Each grid cell initially
contains a specific concentration. The concentration in the left domain half is set to $6.92023 \times 10^{-7}$, while in the right half to
$2.02396 \times 10^{-8}$, creating an horizontal concentration discontinuity at
the center of the grid. These initial concentrations are read from headerless csv file [init_conc.csv](./init_conc.csv).
A diffusion time step is simulated with the
heterogeneous 2D-ADI approach detailed in the
[ADI_scheme.pdf](../doc/ADI_scheme.pdf) file. The x component of the
diffusion coefficients, read from headerless csv file [alphax.csv](./alphax.csv) ranges from $\alpha = 10^{-9}$ to $10^{-10}$ (distributed randomly), while the
y-component is held constant at $5 \times 10^{-10}$. Closed
boundary conditions are enforced at all domain boundaries, meaning that concentration cannot enter or exit
the system, or in other terms, that the sum of concentrations over the domain must stay constant. The benchmark simulates a single iteration with a
time step ($\Delta t$) of 360 seconds.
# Usage
To generate new makefiles using the `-DTUG_NAAICE_EXAMPLE=ON` option in CMake,
compile the executable, and run it to generate the benchmark output, follow
these steps:
1. Navigate to your project's build directory.
2. Run the following CMake command with the `-DTUG_NAAICE_EXAMPLE=ON` option to
generate the makefiles:
cmake -DTUG_NAAICE_EXAMPLE=ON ..
3. After CMake configuration is complete, build the `naaice` executable by running `make`:
make naaice
4. Once the compilation is successful, navigate to the build directory by `cd
<build_dir>/naaice`
5. Finally, run the `naaice` executable to generate the benchmark output:
./naaice
## Output Files
### `Thomas_<n>.csv`
These files contain the values of the tridiagonal coefficient matrix $A$, where:
- $Aa$ represents the leftmost value,
- $Ab$ represents the middle value, and
- $Ac$ represents the rightmost value of one row of the matrix.
Additionally, the corresponding values of the right-hand-side vector $b$ are
provided.
Since the 2D-ADI BTCS scheme processes each row first and then proceeds
column-wise through the grid, each iteration is saved separately in
consecutively numbered files.
### `BTCS_5_10_1.csv`
The result of the simulation, **separated by whitespaces**!

5
naaice/alphax.csv Normal file
View File

@ -0,0 +1,5 @@
9.35863547772169e-10,4.1475358910393e-10,7.40997499064542e-10,4.63898729439825e-10,5.50232032872736e-10,1.83670640387572e-10,5.84096802584827e-10,4.72976535512134e-10,2.92526249657385e-10,5.8247926668264e-10
8.08492869278416e-10,3.5943602763582e-10,6.87254608655348e-10,1.52116895909421e-10,5.95404988038354e-10,8.90064929216169e-10,6.13143724552356e-10,1.23507722397335e-10,2.898759260308e-10,9.90528965741396e-10
3.12359050870873e-10,6.34084914484993e-10,8.28234328492545e-10,1.60584925650619e-10,1.31777092232369e-10,7.34155903453939e-10,9.86383097898215e-10,5.42474469379522e-10,1.23030534153804e-10,2.33146838657558e-10
7.76231796317734e-10,1.69479642040096e-10,6.74477553134784e-10,2.4903867142275e-10,2.70820381003432e-10,1.67315319390036e-10,7.1631961625535e-10,4.51548034301959e-10,2.41610987577587e-10,4.98075650655665e-10
7.33895266707987e-10,5.82150935800746e-10,1.49088777275756e-10,5.00345975253731e-10,8.26257051993161e-10,5.28838745504618e-10,9.94136832957156e-10,7.44971914449707e-10,7.53557282453403e-10,7.54089470859617e-10
1 9.35863547772169e-10 4.1475358910393e-10 7.40997499064542e-10 4.63898729439825e-10 5.50232032872736e-10 1.83670640387572e-10 5.84096802584827e-10 4.72976535512134e-10 2.92526249657385e-10 5.8247926668264e-10
2 8.08492869278416e-10 3.5943602763582e-10 6.87254608655348e-10 1.52116895909421e-10 5.95404988038354e-10 8.90064929216169e-10 6.13143724552356e-10 1.23507722397335e-10 2.898759260308e-10 9.90528965741396e-10
3 3.12359050870873e-10 6.34084914484993e-10 8.28234328492545e-10 1.60584925650619e-10 1.31777092232369e-10 7.34155903453939e-10 9.86383097898215e-10 5.42474469379522e-10 1.23030534153804e-10 2.33146838657558e-10
4 7.76231796317734e-10 1.69479642040096e-10 6.74477553134784e-10 2.4903867142275e-10 2.70820381003432e-10 1.67315319390036e-10 7.1631961625535e-10 4.51548034301959e-10 2.41610987577587e-10 4.98075650655665e-10
5 7.33895266707987e-10 5.82150935800746e-10 1.49088777275756e-10 5.00345975253731e-10 8.26257051993161e-10 5.28838745504618e-10 9.94136832957156e-10 7.44971914449707e-10 7.53557282453403e-10 7.54089470859617e-10

7
naaice/files.hpp.in Normal file
View File

@ -0,0 +1,7 @@
#ifndef FILES_H_
#define FILES_H_
const char *INPUT_CONC_FILE = "@IN_CONC_FILE@";
const char *INPUT_ALPHAX_FILE = "@IN_ALPHAX_FILE@";
#endif // FILES_H_

5
naaice/init_conc.csv Normal file
View File

@ -0,0 +1,5 @@
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
1 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08
2 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08
3 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08
4 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08
5 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 6.92023e-07 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08 2.02396e-08

View File

@ -1,3 +1,4 @@
## Time-stamp: "Last modified 2023-07-31 14:26:49 delucia"
## Brutal implementation of 2D ADI scheme
## Square NxN grid with dx=dy=1
@ -23,12 +24,12 @@ ADI <- function(n, dt, iter, alpha) {
tmpY[i,] <- SweepByRow(i, resY, dt=dt, alpha=alpha)
res <- t(tmpY)
out[[it]] <- res }
out[[it]] <- res
}
return(out)
}
## Workhorse function to fill A, B and solve for a given *row* of the
## grid matrix
SweepByRow <- function(i, field, dt, alpha) {
@ -48,12 +49,12 @@ SweepByRow <- function(i, field, dt, alpha) {
B <- numeric(ncol(field))
## We now distinguish the top and bottom rows
if (i == 1){
if (i == 1) {
## top boundary, "i-1" doesn't exist or is at a ghost
## node/cell boundary (TODO)
for (ii in seq_along(B))
B[ii] <- (-1 +2*Sy)*field[i,ii] - Sy*field[i+1,ii]
} else if (i == nrow(field)){
} else if (i == nrow(field)) {
## bottom boundary, "i+1" doesn't exist or is at a ghost
## node/cell boundary (TODO)
for (ii in seq_along(B))
@ -122,3 +123,365 @@ plot(adi2[[length(adi2)]], ref2, log="xy", xlab="ADI", ylab="ode.2D (reference)"
las=1, xlim=c(1E-15, 1), ylim=c(1E-15, 1))
abline(0,1)
## Test heterogeneous scheme, chain rule
ADIHet <- function(field, dt, iter, alpha) {
if (!all.equal(dim(field), dim(alpha)))
stop("field and alpha are not matrix")
## now both field and alpha must be nx*ny matrices
nx <- ncol(field)
ny <- nrow(field)
dx <- dy <- 1
## find out the center of the grid to apply conc=1
cenx <- ceiling(nx/2)
ceny <- ceiling(ny/2)
field[cenx, ceny] <- 1
Aij <- Bij <- alpha
for (i in seq(2,ncol(field)-1)) {
for (j in seq(2,nrow(field)-1)) {
Aij[i,j] <- (alpha[i+1,j]-alpha[i-1,j])/4 + alpha[i,j]
Bij[i,j] <- (alpha[i,j+1]-alpha[i,j-1])/4 + alpha[i,j]
}
}
if (any(Aij<0) || any(Bij<0))
stop("Aij or Bij are negative!")
## prepare containers for computations and outputs
tmpX <- tmpY <- res <- field
out <- vector(mode="list", length=iter)
for (it in seq(1, iter)) {
for (i in seq(1, ny))
tmpX[i,] <- SweepByRowHet(i, res, dt=dt, alpha=alpha, Aij, Bij)
resY <- t(tmpX)
for (i in seq(1, nx))
tmpY[i,] <- SweepByRowHet(i, resY, dt=dt, alpha=alpha, Bij, Aij)
res <- t(tmpY)
out[[it]] <- res
}
return(out)
}
## Workhorse function to fill A, B and solve for a given *row* of the
## grid matrix
SweepByRowHet <- function(i, field, dt, alpha, Aij, Bij) {
dx <- 1 ## fixed in our test
Sx <- Sy <- dt/2/dx/dx
## diagonal of A at once
A <- matrix(0, nrow(field), ncol(field))
diag(A) <- 1+2*Sx*diag(alpha)
## adjacent diagonals "Sx"
for (ii in seq(1, nrow(field)-1)) {
A[ii+1, ii] <- -Sx*Aij[ii+1,ii]
A[ii, ii+1] <- -Sx*Aij[ii,ii+1]
}
B <- numeric(ncol(field))
## We now distinguish the top and bottom rows
if (i == 1) {
## top boundary, "i-1" doesn't exist or is at a ghost
## node/cell boundary (TODO)
for (ii in seq_along(B))
B[ii] <- Sy*Bij[i+1,ii]*field[i+1,ii] + (1-2*Sy*Bij[i,ii])*field[i, ii]
} else if (i == nrow(field)) {
## bottom boundary, "i+1" doesn't exist or is at a ghost
## node/cell boundary (TODO)
for (ii in seq_along(B))
B[ii] <- (1-2*Sy*Bij[i,ii])*field[i, ii] + Sy*Bij[i-1,ii]*field[i-1,ii]
} else {
## inner grid row, full expression
for (ii in seq_along(B))
B[ii] <- Sy*Bij[i+1,ii]*field[i+1,ii] + (1-2*Sy*Bij[i,ii])*field[i, ii] + Sy*Bij[i-1,ii]*field[i-1,ii]
}
x <- solve(A, B)
x
}
## adi2 <- ADI(n=51, dt=10, iter=200, alpha=1E-3)
## ref2 <- DoRef(n=51, alpha=1E-3, dt=10, iter=200)
n <- 51
field <- matrix(0, n, n)
alphas <- matrix(1E-3*runif(n*n, 1,1.2), n, n)
## for (i in seq(1,nrow(alphas)))
## alphas[i,] <- seq(1E-7,1E-3, length=n)
#diag(alphas) <- rep(1E-2, n)
adih1 <- ADIHet(field=field, dt=10, iter=100, alpha=alphas)
adi2 <- ADI(n=n, dt=10, iter=100, alpha=1E-3)
par(mfrow=c(1,3))
image(adi2[[length(adi2)]])
image(adih1[[length(adih1)]])
points(0.5,0.5, col="red",pch=4)
plot(adih1[[length(adih1)]], adi2[[length(adi2)]], pch=4, log="xy")
abline(0,1)
sapply(adih1, sum)
sapply(adi2, sum)
adi2
par(mfrow=c(1,2))
image(alphas)
image(adih1[[length(adih1)]])
points(0.5,0.5, col="red",pch=4)
## Test heterogeneous scheme, direct discretization
ADIHetDir <- function(field, dt, iter, alpha) {
if (!all.equal(dim(field), dim(alpha)))
stop("field and alpha are not matrix")
## now both field and alpha must be nx*ny matrices
nx <- ncol(field)
ny <- nrow(field)
dx <- dy <- 1
## find out the center of the grid to apply conc=1
cenx <- ceiling(nx/2)
ceny <- ceiling(ny/2)
field[cenx, ceny] <- 1
## prepare containers for computations and outputs
tmpX <- tmpY <- res <- field
out <- vector(mode="list", length=iter)
for (it in seq(1, iter)) {
for (i in seq(2, ny-1)) {
Aij <- cbind(colMeans(rbind(alpha[i,], alpha[i-1,])), colMeans(rbind(alpha[i,], alpha[i+1,])))
Bij <- cbind(rowMeans(cbind(alpha[,i], alpha[,i-1])), rowMeans(cbind(alpha[,i], alpha[,i+1])))
tmpX[i,] <- SweepByRowHetDir(i, res, dt=dt, Aij, Bij)
}
resY <- t(tmpX)
for (i in seq(2, nx-1))
tmpY[i,] <- SweepByRowHetDir(i, resY, dt=dt, Bij, Aij)
res <- t(tmpY)
out[[it]] <- res
}
return(out)
}
harm <- function(x,y) {
if (length(x) != 1 || length(y) != 1)
stop("x & z have different lengths")
2/(1/x+1/y)
}
harm(1,4)
## Direct discretization, Workhorse function to fill A, B and solve
## for a given *row* of the grid matrix
SweepByRowHetDir <- function(i, field, dt, Aij, Bij) {
dx <- 1 ## fixed in our test
Sx <- Sy <- dt/2/dx/dx
## diagonal of A at once
A <- matrix(0, nrow(field), ncol(field))
diag(A) <- 1 + Sx*(Aij[,1]+Aij[,2])
## adjacent diagonals "Sx"
for (ii in seq(1, nrow(field)-1)) {
A[ii+1, ii] <- -Sx*Aij[ii,2] # i-1/2
A[ii, ii+1] <- -Sx*Aij[ii,1] # i+1/2
}
B <- numeric(ncol(field))
for (ii in seq_along(B))
B[ii] <- Sy*Bij[ii,2]*field[i+1,ii] + (1 - Sy*(Bij[ii,1]+Bij[ii,2]))*field[i, ii] + Sy*Bij[ii,1]*field[i-1,ii]
lastA <<- A
lastB <<- B
x <- solve(A, B)
x
}
## adi2 <- ADI(n=51, dt=10, iter=200, alpha=1E-3)
## ref2 <- DoRef(n=51, alpha=1E-3, dt=10, iter=200)
n <- 51
field <- matrix(0, n, n)
alphas <- matrix(1E-5*runif(n*n, 1,2), n, n)
## dim(field)
## dim(alphas)
## all.equal(dim(field), dim(alphas))
## alphas1 <- matrix(3E-5, n, 25)
## alphas2 <- matrix(1E-5, n, 26)
## alphas <- cbind(alphas1, alphas2)
## for (i in seq(1,nrow(alphas)))
## alphas[i,] <- seq(1E-7,1E-3, length=n)
#diag(alphas) <- rep(1E-2, n)
adih <- ADIHetDir(field=field, dt=20, iter=500, alpha=alphas)
adi2 <- ADI(n=n, dt=20, iter=500, alpha=1E-5)
par(mfrow=c(1,3))
image(adi2[[length(adi2)]])
image(adih[[length(adih)]])
points(0.5,0.5, col="red",pch=4)
plot(adih[[length(adih)]], adi2[[length(adi2)]], pch=4, log="xy")
abline(0,1)
cchet <- lapply(adih, round, digits=6)
cchom <- lapply(adi2, round, digits=6)
plot(cchet[[length(cchet)]], cchom[[length(cchom)]], pch=4, log="xy", xlim=c(1e-6,1), ylim=c(1e-6,1))
abline(0,1)
cchet[[500]]
str(adih)
sapply(adih, sum)
sapply(adi2, sum)
adi2
par(mfrow=c(1,2))
image(alphas)
points(0.5,0.5, col="red",pch=4)
image(adih[[length(adih)]])
points(0.5,0.5, col="red",pch=4)
options(width=110)
FTCS_2D <- function(field, dt, iter, alpha) {
if (!all.equal(dim(field), dim(alpha)))
stop("field and alpha are not matrix")
## now both field and alpha must be nx*ny matrices
nx <- ncol(field)
ny <- nrow(field)
dx <- dy <- 1
## find out the center of the grid to apply conc=1
cenx <- ceiling(nx/2)
ceny <- ceiling(ny/2)
field[cenx, ceny] <- 1
## prepare containers for computations and outputs
tmp <- res <- field
cflt <- 1/max(alpha)/4
cat(":: CFL allowable time step: ", cflt,"\n")
## inner iterations
inner <- floor(dt/cflt)
if (inner == 0) {
## dt < cflt, no inner iterations
inner <- 1
tsteps <- dt
cat(":: No inner iter. required\n")
} else {
tsteps <- c(rep(cflt, inner), dt-inner*cflt)
cat(":: Number of inner iter. required: ", inner,"\n")
}
out <- vector(mode="list", length=iter)
for (it in seq(1, iter)) {
cat(":: outer it: ", it)
for (innerit in seq_len(inner)) {
for (i in seq(2, ny-1)) {
for (j in seq(2, nx-1)) {
## tmp[i,j] <- res[i,j] +
## + tsteps[innerit]/dx/dx * (res[i+1,j]*mean(alpha[i+1,j],alpha[i,j]) -
## res[i,j] *(mean(alpha[i+1,j],alpha[i,j])+mean(alpha[i-1,j],alpha[i,j])) +
## res[i-1,j]*mean(alpha[i-1,j],alpha[i,j])) +
## + tsteps[innerit]/dy/dy * (res[i,j+1]*mean(alpha[i,j+1],alpha[i,j]) -
## res[i,j] *(mean(alpha[i,j+1],alpha[i,j])+mean(alpha[i,j-1],alpha[i,j])) +
## res[i,j-1]*mean(alpha[i,j-1],alpha[i,j]))
tmp[i,j] <- res[i,j] +
+ tsteps[innerit]/dx/dx * ((res[i+1,j]-res[i,j]) * harm(alpha[i+1,j],alpha[i,j]) -
(res[i,j]-res[i-1,j]) * harm(alpha[i-1,j],alpha[i,j])) +
+ tsteps[innerit]/dx/dx * ((res[i,j+1]-res[i,j]) * harm(alpha[i,j+1],alpha[i,j]) -
(res[i,j]-res[i,j-1]) * harm(alpha[i,j-1],alpha[i,j]))
}
}
## swap back tmp to res for the next inner iteration
res <- tmp
}
cat("- done\n")
## at end of inner it we store
out[[it]] <- res
}
return(out)
}
## testing that FTCS with homog alphas reverts to ADI/Reference sim
n <- 51
field <- matrix(0, n, n)
alphas <- matrix(1E-3, n, n)
adi2 <- ADI(n=51, dt=100, iter=20, alpha=1E-3)
ref <- DoRef(n=51, alpha=1E-3, dt=100, iter=20)
adihet <- ADIHetDir(field=field, dt=100, iter=20, alpha=alphas)
ftcsh <- FTCS_2D(field=field, dt=100, iter=20, alpha=alphas)
par(mfrow=c(2,4))
image(ref, main="Reference ODE.2D")
points(0.5,0.5, col="red",pch=4)
image(ftcsh[[length(ftcsh)]], main="FTCS 2D")
points(0.5,0.5, col="red",pch=4)
image(adihet[[length(adihet)]], main="ADI Heter.")
points(0.5,0.5, col="red",pch=4)
image(adi2[[length(adi2)]], main="ADI Homog.", col=terrain.colors(12))
points(0.5,0.5, col="red",pch=4)
plot(ftcsh[[length(ftcsh)]], ref, pch=4, log="xy", xlim=c(1E-16, 1), ylim=c(1E-16, 1),
main = "FTCS_2D vs ref", xlab="FTCS 2D", ylab="Reference")
abline(0,1)
plot(ftcsh[[length(ftcsh)]], adihet[[length(adihet)]], pch=4, log="xy", xlim=c(1E-16, 1), ylim=c(1E-16, 1),
main = "FTCS_2D vs ADI Het", xlab="FTCS 2D", ylab="ADI 2D Heter.")
abline(0,1)
plot(ftcsh[[length(ftcsh)]], adi2[[length(adi2)]], pch=4, log="xy", xlim=c(1E-16, 1), ylim=c(1E-16, 1),
main = "FTCS_2D vs ADI Hom", xlab="FTCS 2D", ylab="ADI 2D Hom.")
abline(0,1)
plot(adihet[[length(adihet)]], adi2[[length(adi2)]], pch=4, log="xy", xlim=c(1E-16, 1), ylim=c(1E-16, 1),
main = "ADI Het vs ADI Hom", xlab="ADI Het", ylab="ADI 2D Hom.")
abline(0,1)

237
scripts/HetDiff.R Normal file
View File

@ -0,0 +1,237 @@
## Time-stamp: "Last modified 2023-07-31 16:28:48 delucia"
library(ReacTran)
library(deSolve)
options(width=114)
## harmonic mean
harm <- function(x,y) {
if (length(x) != 1 || length(y) != 1)
stop("x & y have different lengths")
2/(1/x+1/y)
}
## harm(0, 1) ## 0
## harm(1, 2) ## 0
############# Providing coeffs on the interfaces
N <- 11 # number of grid cells
ini <- 1 # initial value at x=0
N2 <- ceiling(N/2)
L <- 10 # domain side
## Define diff.coeff per cell, in 4 quadrants
alphas <- matrix(0, N, N)
alphas[1:N2, 1:N2] <- 1
alphas[1:N2, seq(N2+1,N)] <- 0.1
alphas[seq(N2+1,N), 1:N2] <- 0.01
alphas[seq(N2+1,N), seq(N2+1,N)] <- 0.001
image(log10(alphas), col=heat.colors(4))
r180 <- function(x) {
xx <- rev(x)
dim(xx) <- dim(x)
xx
}
mirror <- function (x) {
xx <- as.data.frame(x)
xx <- rev(xx)
xx <- as.matrix(xx)
xx
}
array_to_LaTeX <- function(arr) {
rows <- apply(arr, MARGIN=1, paste, collapse = " & ")
matrix_string <- paste(rows, collapse = " \\\\ ")
return(paste("\\begin{bmatrix}", matrix_string, "\\end{bmatrix}"))
}
cat(array_to_LaTeX(mirror(r180(alphas))))
r180(alphas)
filled.contour(log10(alphas), col=terrain.colors(4), nlevels=4)
cmpharm <- function(x) {
y <- c(0, x, 0)
ret <- numeric(length(x)+1)
for (i in seq(2, length(y))) {
ret[i-1] <- harm(y[i], y[i-1])
}
ret
}
## Construction of the 2D grid
x.grid <- setup.grid.1D(x.up = 0, L = L, N = N)
y.grid <- setup.grid.1D(x.up = 0, L = L, N = N)
grid2D <- setup.grid.2D(x.grid, y.grid)
D.grid <- list()
# Diffusion on x-interfaces
D.grid$x.int <- apply(alphas, 1, cmpharm)
# Diffusion on y-interfaces
## matrix(nrow = N, ncol = N+1, data = rep(c(rep(1E-1, 50),5.E-1,rep(1., 50)), 100) )
D.grid$y.int <- t(apply(alphas, 2, cmpharm))
dx <- L/N
dy <- L/N
# The model equations
Diff2Dc <- function(t, y, parms) {
CONC <- matrix(nrow = N, ncol = N, data = y)
dCONC <- tran.2D(CONC, dx = dx, dy = dy, D.grid = D.grid)$dC
return(list(dCONC))
}
## initial condition: 0 everywhere, except in central point
y <- matrix(nrow = N, ncol = N, data = 0)
y[N2, N2] <- ini # initial concentration in the central point...
## solve for 10 time units
times <- 0:10
outc <- ode.2D(y = y, func = Diff2Dc, t = times, parms = NULL,
dim = c(N, N), lrw = 1860000)
outtimes <- c(0, 4, 7, 10)
## NB: assuming current working dir is "tug"
cairo_pdf("doc/images/deSolve_AlphaHet1.pdf", family="serif", width=12, height=12)
image(outc, ask = FALSE, mfrow = c(2, 2), main = paste("time", outtimes),
legend = TRUE, add.contour = FALSE, subset = time %in% outtimes,
xlab="",ylab="", axes=FALSE, asp=1)
dev.off()
## outc is a matrix with 11 rows and 122 columns (first column is
## simulation time);
str(outc)
## extract only the results and transpose the matrix for storage
ret <- data.matrix(t(outc[ , -1]))
rownames(ret) <- NULL
## NB: assuming current working dir is "tug"
data.table::fwrite(ret, file="scripts/gold/HetDiff1.csv", col.names=FALSE)
#################### 2D visualization
## Version of Rmufits::PlotCartCellData with the ability to fix the
## "breaks" for color coding of 2D simulations
Plot2DCellData <- function(data, grid, nx, ny, contour = TRUE,
nlevels = 12, breaks, palette = "heat.colors",
rev.palette = TRUE, scale = TRUE, plot.axes=TRUE, ...) {
if (!missing(grid)) {
xc <- unique(sort(grid$cell$XCOORD))
yc <- unique(sort(grid$cell$YCOORD))
nx <- length(xc)
ny <- length(yc)
if (!length(data) == nx * ny)
stop("Wrong nx, ny or grid")
} else {
xc <- seq(1, nx)
yc <- seq(1, ny)
}
z <- matrix(round(data, 6), ncol = nx, nrow = ny, byrow = TRUE)
pp <- t(z[rev(seq(1, nrow(z))), ])
if (missing(breaks)) {
breaks <- pretty(data, n = nlevels)
}
breakslen <- length(breaks)
colors <- do.call(palette, list(n = breakslen - 1))
if (rev.palette)
colors <- rev(colors)
if (scale) {
par(mfrow = c(1, 2))
nf <- layout(matrix(c(1, 2), 1, 2, byrow = TRUE), widths = c(4,
1))
}
par(las = 1, mar = c(5, 5, 3, 1))
image(xc, yc, pp, xlab = "X [m]", ylab = "Y[m]", las = 1, asp = 1,
breaks = breaks, col = colors, axes = FALSE, ann=plot.axes,
...)
if (plot.axes) {
axis(1)
axis(2)
}
if (contour)
contour(unique(sort(xc)), unique(sort(yc)), pp, breaks = breaks,
add = TRUE)
if (scale) {
par(las = 1, mar = c(5, 1, 5, 5))
PlotImageScale(data, breaks = breaks, add.axis = FALSE,
axis.pos = 4, col = colors)
axis(4, at = breaks)
}
invisible(pp)
}
PlotImageScale <- function(z, zlim, col = grDevices::heat.colors(12), breaks,
axis.pos = 1, add.axis = TRUE, ...) {
if (!missing(breaks)) {
if (length(breaks) != (length(col) + 1)) {
stop("must have one more break than colour")
}
}
if (missing(breaks) & !missing(zlim)) {
breaks <- seq(zlim[1], zlim[2], length.out = (length(col) + 1))
}
if (missing(breaks) & missing(zlim)) {
zlim <- range(z, na.rm = TRUE)
zlim[2] <- zlim[2] + c(zlim[2] - zlim[1]) * (0.001)
zlim[1] <- zlim[1] - c(zlim[2] - zlim[1]) * (0.001)
breaks <- seq(zlim[1], zlim[2], length.out = (length(col) + 1))
}
poly <- vector(mode = "list", length(col))
for (i in seq(poly)) {
poly[[i]] <- c(breaks[i], breaks[i + 1], breaks[i + 1],
breaks[i])
}
if (axis.pos %in% c(1, 3)) {
ylim <- c(0, 1)
xlim <- range(breaks)
}
if (axis.pos %in% c(2, 4)) {
ylim <- range(breaks)
xlim <- c(0, 1)
}
plot(1, 1, t = "n", ylim = ylim, xlim = xlim, axes = FALSE,
xlab = "", ylab = "", xaxs = "i", yaxs = "i", ...)
for (i in seq(poly)) {
if (axis.pos %in% c(1, 3)) {
polygon(poly[[i]], c(0, 0, 1, 1), col = col[i], border = NA)
}
if (axis.pos %in% c(2, 4)) {
polygon(c(0, 0, 1, 1), poly[[i]], col = col[i], border = NA)
}
}
box()
if (add.axis) {
axis(axis.pos)
}
}
cairo_pdf("AlphaHet1.pdf", family="serif", width=8)
par(mar = c(1,1,1,1))
Plot2DCellData(log10(mirror(alphas)), nx=N, ny=N, nlevels=5, palette = terrain.colors, contour=FALSE, plot.axes=FALSE,
scale = F,
main=expression(log[10](alpha)))
text(3,8,"1")
text(8,8,"0.1")
text(3,3,"0.01")
text(8,3,"0.001")
# title("Diff. Coefficients (log10)")
dev.off()

121
scripts/gold/HetDiff1.csv Normal file
View File

@ -0,0 +1,121 @@
0,1.15723009391899e-05,0.000573422585977779,0.00271483227696389,0.00592017488450261,0.00919024372055321,0.0119992110381186,0.01421611640169,0.0158871549414673,0.0171115136326332,0.0179901833258047
0,4.32496365970161e-05,0.00110003761455522,0.0038588402926536,0.00723956931728921,0.0103665385368182,0.0129275133140941,0.0149018727052112,0.0163722917415511,0.0174425542133075,0.0182067473313329
0,0.000156410551955808,0.0022501860978233,0.00596846775043427,0.00951979828485079,0.0123439077799255,0.0144650737976258,0.0160242190667615,0.0171550743320483,0.0179654704632021,0.0185369538227271
0,0.000449900131743341,0.00397802650798475,0.0085351147388942,0.0120552559726064,0.0144482796517419,0.0160547847343675,0.0171511707942764,0.0179099936829702,0.0184375120234885,0.0188001645647094
0,0.00096247784605253,0.00575161551352107,0.0106416083625011,0.0139078265679269,0.0158706072121663,0.0170493964826529,0.0177834857154376,0.0182584391833927,0.018573228563985,0.0187811039627322
0,0.00139148060908994,0.00653728609085073,0.0112226963812925,0.0141895931925161,0.0159026822227563,0.0168925530607027,0.0174883152516615,0.017865325174865,0.0181135647391488,0.0182789062492789
0,7.88755181655377e-05,0.000890855516524495,0.0025489601065516,0.00451888907818989,0.00640234215109739,0.00803400181298965,0.00938380805959316,0.0104782803692741,0.0113602330285538,0.0120715601783272
0,1.96835046894113e-06,5.20032828635902e-05,0.000248131836356188,0.000630746867714403,0.00117274437938983,0.00182064447664627,0.00252211668991391,0.00323643607872941,0.00393564702867779,0.00460249317726503
0,4.05263620284678e-08,2.41553600349545e-06,1.87665958722685e-05,6.74081501157623e-05,0.000163264089823103,0.000313023273063137,0.000515631508985307,0.000764816320228696,0.00105167332970099,0.00136658044925295
0,7.12888460576857e-10,9.36408593803959e-08,1.16671799220012e-06,5.86484773654181e-06,1.84017858631692e-05,4.34716444970184e-05,8.51739231661631e-05,0.000146375097555955,0.000228524220562364,0.000331765311847696
0,1.11266213313442e-11,3.21569209939452e-09,6.45997582539074e-08,4.58674913815976e-07,1.88637915450085e-06,5.56437226690733e-06,1.31531030829188e-05,2.6577288329037e-05,4.78110315422556e-05,7.86848274922097e-05
0,4.35985871981009e-05,0.0011173013784839,0.00393465640259938,0.00738648425859891,0.0105643563174128,0.0131485926879987,0.0151246465122267,0.016583690239707,0.0176362908990393,0.0183809352240671
0,0.000162940275870536,0.00214415472789055,0.0055971341202626,0.00904266600518215,0.0119317484324307,0.01418453738493,0.0158744170951516,0.0171103507405317,0.0179967639568099,0.0186199630282087
0,0.000589272728343116,0.00438867743664915,0.00866893626959719,0.0119151946783314,0.0142428604635392,0.0159134847420158,0.0171142649822806,0.0179721981758363,0.0185775454158148,0.0189955063245207
0,0.00169526769334991,0.00776694628989879,0.0124247154337811,0.0151382671118746,0.0167367107498905,0.0177366662921586,0.0183940071716407,0.0188368236363926,0.0191342578330509,0.0193277227804878
0,0.00362839452566188,0.0112526737744286,0.0155521246084951,0.0175590393037948,0.0184985843185778,0.0189571544442947,0.0191923705608016,0.0193168273546316,0.0193791769704849,0.0194019806796609
0,0.00525177970919126,0.0128429513741672,0.0165189505188843,0.0180778870279157,0.0187197075826292,0.018968965691842,0.0190518322037241,0.0190652772723224,0.0190482483782045,0.019016041284815
0,0.000385153961868949,0.00229577940499986,0.00480460165448024,0.00711204718132809,0.0089909576548431,0.0104472162269959,0.0115548772926463,0.0123946614477843,0.0130350658282232,0.013528343285464
0,1.16858258297074e-05,0.000164584714174317,0.000570116218846185,0.00119271144355875,0.00194723110061389,0.00275621955267251,0.00356441433332025,0.00433765719695555,0.00505760032168857,0.00571654514201105
0,2.81208380577636e-07,8.97423573740115e-06,5.03422264992399e-05,0.000147427646174569,0.000310274427544454,0.000536832367026033,0.000817602280027168,0.00113989443932459,0.00149069663261676,0.00185828031564749
0,5.63399964199174e-09,3.96964588417007e-07,3.55564115271452e-06,1.44716592579598e-05,3.91537025779409e-05,8.28348829677685e-05,0.000148977185800308,0.000239043494747069,0.00035275053097983,0.000488513445484224
0,9.84398346768558e-11,1.5293568296117e-08,2.20334810416761e-07,1.26108145384204e-06,4.44865566281344e-06,1.16871019830672e-05,2.52195633384727e-05,4.73253788486059e-05,8.00591501276681e-05,0.000125072519296284
0,0.00015939225040812,0.00233357034425003,0.00625562530069953,0.0100135877171516,0.0129676882444301,0.0151374162693059,0.0166874294020243,0.0177764984876221,0.0185308441084237,0.0190433426894296
0,0.000595680647430986,0.0044801608514742,0.00890743169921897,0.0122772025886856,0.0146734676860174,0.0163633416618241,0.0175504470842378,0.0183769265178609,0.018943746718196,0.019322547574371
0,0.00215448195385171,0.0091767592756406,0.0138199838635887,0.0162232592499362,0.0175799863177681,0.0184333811027871,0.0190005234819769,0.0193808295698239,0.0196287328140203,0.01977995018463
0,0.00619992143295609,0.0162622253524985,0.0198667054266907,0.0207089785376823,0.0207820327860086,0.0206824193920932,0.0205605370395911,0.0204471756023412,0.0203412141522796,0.0202389042951594
0,0.0132779136133337,0.0236231555894036,0.0250044710521133,0.0242144479160946,0.0231938485225262,0.0223380815152074,0.0216793603038312,0.0211797341820616,0.020794392809717,0.0204895366674834
0,0.0192489696423083,0.0271184370488408,0.026840217635384,0.0252838506050641,0.023849962885927,0.0227245048675836,0.0218698389880889,0.0212219274277141,0.0207240679736698,0.020334308600127
0,0.00188379770767293,0.00657190925731914,0.0104892667525962,0.0130700680836906,0.0146349533533907,0.015535980568487,0.0160269602381333,0.0162728756742906,0.0163767130755721,0.016400710298742
0,7.01829175866307e-05,0.000581093706531354,0.00153370940121776,0.00269094081762158,0.00386803514667116,0.00496316154582596,0.00593261862345271,0.00676642929553236,0.0074719703393928,0.00806413175855109
0,1.97847089603411e-06,3.69925955384929e-05,0.000157406497985584,0.000384860592255597,0.000709846476804252,0.0011079382186538,0.00155170108970297,0.00201681216519113,0.00248426641823041,0.00294058581228933
0,4.51078087575571e-08,1.85104501980116e-06,1.25057013282593e-05,4.22892094350572e-05,9.98395815944757e-05,0.000189787308802236,0.000312690644970973,0.000466034643916367,0.000645463690913993,0.000845828528229199
0,8.80252798763183e-10,7.92215439962928e-08,8.56765592551646e-07,4.05803525996784e-06,1.2449180396882e-05,2.9295973763362e-05,5.7746988001425e-05,0.0001003733295813,0.000158928915289496,0.00023429575744269
0,0.000466368704740309,0.00424791180746615,0.00926944539909274,0.0131713216643424,0.0157625616947689,0.0174133722552654,0.0184577152890545,0.0191156877417616,0.0195250042740468,0.0197700479556243
0,0.00174300015594418,0.00815930380362134,0.0132116161354557,0.016172754442502,0.0178696343095053,0.0188636958720877,0.0194550603754561,0.0198042367684176,0.0200009359043926,0.0200979478510297
0,0.00630533087106852,0.0167264504542955,0.0205352907522532,0.021433296617797,0.0214908287288404,0.021342901494693,0.0211590163834043,0.0209808421912509,0.020813382900871,0.0206559438303523
0,0.0181516683021334,0.0296877506877057,0.0296188711707772,0.0274991387847508,0.0255697170579367,0.0241218925670229,0.0230708035427124,0.0223012794968431,0.0217225548818433,0.0212745536462067
0,0.0389067518217964,0.0432743493327086,0.037525495598965,0.0324527348417642,0.0288543692365208,0.0263663031569143,0.0246230583185604,0.0233728213888174,0.0224524193870472,0.0217568092848378
0,0.0565396239160925,0.0500897261990562,0.0408387347867424,0.0344846493850116,0.0302543240032215,0.0273643802632421,0.0253287379325492,0.0238539136707014,0.0227584403765214,0.02192583980509
0,0.00794755170946868,0.0172628249510397,0.0219912124473273,0.0237906919799591,0.0240591222673087,0.0235982544067714,0.0228369786001951,0.021996032718532,0.0211823304839058,0.0204423856134907
0,0.000373684524467729,0.00190283496658901,0.00395283626547245,0.00595433814031928,0.00766198835968331,0.0090150942497423,0.0100370905098692,0.0107818312810106,0.0113083452714233,0.0116697843722264
0,1.24794246503553e-05,0.000141270114233274,0.000466648605475989,0.000969170133300241,0.00158727378005724,0.00225743530947124,0.00292973311195242,0.00357075743939607,0.00416127927601125,0.00469265749237697
0,3.25056776407433e-07,7.95461921640221e-06,4.12089493515841e-05,0.000117250753288648,0.00024402042254145,0.000420314286347587,0.000638908995931823,0.000889809192557268,0.00116261453581859,0.00144792147140295
0,7.0854379238238e-09,3.75524392933622e-07,3.08321489667331e-06,1.21925554449326e-05,3.27749649029249e-05,6.95563221553643e-05,0.000126018794389096,0.000204052811537285,0.000304018569398211,0.000425019521621274
0,0.00102836224521133,0.0064274670666241,0.0121491816508084,0.015968397765568,0.0181492003638804,0.019314880428558,0.0199102032969297,0.0201928751968812,0.0203044557297043,0.020320071184116
0,0.00384385388740204,0.0123515070854559,0.0173313777131353,0.0196324133235912,0.0206081184457373,0.0209612687545928,0.0210258040457677,0.0209599611581841,0.0208374482363417,0.0206927731773186
0,0.0139084634226096,0.0253420008838306,0.0269858208570455,0.0260863431776468,0.0248662406090429,0.023805495300228,0.0229587141516884,0.0222941985053244,0.0217679373130175,0.021344966099159
0,0.0400577489259572,0.0450598366852064,0.0390556903095381,0.0336304680408348,0.0297597604093353,0.0270807237205804,0.0252035706273644,0.0238573134643687,0.0228662562144913,0.0221177566182138
0,0.0859596622787531,0.0659645822190159,0.0498440895724295,0.0400638374763904,0.0339433683852068,0.0299353070092985,0.0272039578032914,0.0252772077773997,0.0238777953326208,0.0228341669415018
0,0.125398084679699,0.0772427043755313,0.0551767906604444,0.0434193651923798,0.0363278914606691,0.0317025734216055,0.0285266548704347,0.0262622056865752,0.0246012510386757,0.0233529211513904
0,0.0279719026476077,0.0399912836159061,0.041824217737992,0.0399960375533372,0.0370306079722165,0.0339334669014817,0.0310904350363235,0.0286238301128732,0.0265419722346029,0.0248094228214593
0,0.0016878543861307,0.00546802879678612,0.00908467643641864,0.0118685267101036,0.0137867836918316,0.014992473171871,0.0156668769769553,0.0159679687584234,0.0160191464240754,0.0159109388085067
0,6.66210210366172e-05,0.000466311941721689,0.0012074539008488,0.00214399617569523,0.00313678410870533,0.0040909928004654,0.0049535861902635,0.00570178710489221,0.00633199242057907,0.00685150908457396
0,1.96581789972705e-06,2.90655193631781e-05,0.000116181708737399,0.000279461298722284,0.000515297156287373,0.00080891264649635,0.00114182790936584,0.00149639884138984,0.0018579271762422,0.00221530254986257
0,4.73511582533572e-08,1.48929389156945e-06,9.32003158799015e-06,3.08850626482769e-05,7.30754947692817e-05,0.000140629698309681,0.000235669226477032,0.00035796975388455,0.000505546298234415,0.000675276938000184
0,0.0015836697501133,0.00789553113761796,0.0138353209275783,0.0174900423844659,0.0193829171959464,0.0202511267409907,0.0205839676229735,0.0206522039767607,0.0205960670144616,0.0204840229205406
0,0.00592024795026366,0.015178056357401,0.0197491887189647,0.0215219374447417,0.0220321907378727,0.0220033162511111,0.0217646356223314,0.0214641136669779,0.0211632832330552,0.0208849932589192
0,0.0214261698026196,0.0311627748380295,0.030789710824671,0.0286485315817415,0.0266434190236869,0.0250513323715184,0.023828703885917,0.0228921858496036,0.0221672538845208,0.0215984913078681
0,0.0617381544584604,0.0554953966664163,0.0446769294750762,0.0370604778930011,0.0320153173963507,0.0286236282907804,0.0262786287048439,0.0246098514264461,0.0233900725628177,0.0224762767353041
0,0.132662095447386,0.0815715457115294,0.0573620962224714,0.0444656436513204,0.0367970084501995,0.0318900160341415,0.0285856373696197,0.0262711290074283,0.0245996244789251,0.0233597987792184
1,0.194600594053136,0.0967188378926377,0.0645033312825157,0.0489815255136541,0.0400071807557426,0.0342746856436233,0.0303863403322649,0.0276362424095596,0.0256323186910777,0.0241347191227446
0,0.072774737081717,0.0731608818750184,0.0649513569830106,0.0562393519385766,0.048657668424965,0.0424321620057059,0.0374285709142598,0.0334382577022264,0.0302593069735787,0.0277213581035032
0,0.00546229967617301,0.0117769944796152,0.0160960402630076,0.0186595966553889,0.0199729770538671,0.020458608892436,0.0204223397121343,0.0200740130964532,0.0195534451842866,0.0189514347157146
0,0.000246612705267533,0.00110781620007196,0.00231152127659001,0.00359442519681962,0.00480227348450595,0.00586145140404475,0.00674755083035105,0.00746338966694999,0.00802538295172145,0.00845539933096027
0,8.01024172201437e-06,7.40436516662321e-05,0.000234915899100426,0.000490064251651924,0.000819680663206465,0.00119845837778001,0.00160264998868245,0.00201295787243647,0.00241514785548912,0.00279964947569617
0,2.07955309779492e-07,4.0104486961948e-06,1.96923787162901e-05,5.61707039451707e-05,0.00011992118154755,0.000214121267381263,0.000338952769617425,0.000492335832038914,0.000670725912883758,0.000869802668989746
0,9.15481631874651e-06,0.000117693874573294,0.000377019455101549,0.000741577814721888,0.00115788487944848,0.00159137882061179,0.00202342112731787,0.00244488663055897,0.00285171452271575,0.00324239719313011
0,4.46105775530757e-05,0.000303984477552903,0.000717806961735601,0.00118873254936734,0.00166857393355206,0.00213667568842936,0.00258503807646796,0.00301133151201256,0.00341571030049681,0.00379926644315538
0,0.000217726325331875,0.000873770408536327,0.00159096186837063,0.00224649712918517,0.00282908355295164,0.00334893383349626,0.00381769528011674,0.00424489367607894,0.00463786438443504,0.00500213301923337
0,0.000918933831995098,0.00232435924877404,0.00343741337091249,0.00429315823548186,0.00497145196445075,0.00552787579177755,0.00599805857236328,0.00640506369246164,0.00676421367084299,0.00708600072229698
0,0.00331122058413453,0.00568447123669356,0.00712540944284069,0.00809397304876091,0.00879084024979697,0.0093162979589995,0.00972657995173228,0.0100559006360597,0.010326180081839,0.0105521545670149
0,0.0100043670600405,0.0127097661456827,0.0139937515689219,0.0147091809677894,0.0151257626062355,0.0153636070507909,0.0154869837094123,0.0155343002312855,0.0155299222027964,0.0154901124905561
0,0.000136637116798085,0.000338905367123057,0.000531352383440882,0.000703792701127588,0.000856724436487964,0.000992875271716,0.00111515974854185,0.00122613935565325,0.00132792886232481,0.00142222359205319
0,5.41197942669333e-06,2.66179151421015e-05,6.06837139232639e-05,0.00010289135389313,0.00014958545120669,0.00019826296968678,0.000247306756529671,0.000295722537183644,0.000342937360699378,0.000388655525576309
0,1.72186371215188e-07,1.70696816511905e-06,5.79640506738781e-06,1.29195485396102e-05,2.30515604056451e-05,3.58992272238309e-05,5.10570165270148e-05,6.80982556623907e-05,8.66227559703461e-05,0.000106279145950884
0,4.32364439009495e-09,8.64589599304522e-08,4.3955011662034e-07,1.29702112369114e-06,2.8633412989769e-06,5.28573284839916e-06,8.65071350621842e-06,1.29921291564899e-05,1.83031949023414e-05,2.45484723579094e-05
0,9.12457420765116e-11,3.74315585927427e-09,2.9038454685416e-08,1.15682654773554e-07,3.22205377431422e-07,7.18623792678699e-07,1.37865133447057e-06,2.37328126021504e-06,3.76631554161963e-06,5.61170605601181e-06
0,2.08011169409904e-08,6.36518490608328e-07,3.48383663648875e-06,1.01031069547314e-05,2.13412103139757e-05,3.74893709216357e-05,5.85171433417359e-05,8.42328798186352e-05,0.000114372709309901,0.000148647128283624
0,1.23164013985779e-07,2.01619957543725e-06,8.06746622712326e-06,1.9408951787608e-05,3.62807655737012e-05,5.85218420212134e-05,8.58109894680849e-05,0.000117776723517294,0.000154044674846032,0.00019425785338382
0,7.38055239000408e-07,7.13661815273176e-06,2.1957201331878e-05,4.48051934222451e-05,7.46425998373469e-05,0.000110486321507166,0.000151514246571192,0.000197051618892773,0.000246540311467432,0.00029951243350813
0,3.94931915614762e-06,2.37664477362685e-05,5.85078377488893e-05,0.000104356634872865,0.000158457684921854,0.000218839976363591,0.000284118381194624,0.000353286447661602,0.00042558888273829,0.000500441217237315
0,1.87536920881212e-05,7.42297836614388e-05,0.000150953066786234,0.000240330072584709,0.000337698410142427,0.000440229993907094,0.00054607757879758,0.000653971107402446,0.000763006862111349,0.000872522857886958
0,7.67060564087528e-05,0.000211583511011325,0.000364290214247424,0.000523711941940329,0.000684842914248833,0.000844968149576811,0.00100249413331233,0.00115645811127351,0.00130628569191617,0.00145165008061455
0,1.29198147797745e-07,7.26337646539414e-07,1.8789774469963e-06,3.58894576036159e-06,5.83743184885366e-06,8.59992729697426e-06,1.1850973027563e-05,1.55658536023551e-05,1.97211620171109e-05,2.42949568717564e-05
0,1.89355863862761e-09,2.0149347443802e-08,7.32356468455249e-08,1.74272463156743e-07,3.31196104192811e-07,5.481872642976e-07,8.2692218882057e-07,1.16749109225162e-06,1.56902754100022e-06,2.03013177037545e-06
0,4.63537374431423e-11,9.81049734780514e-10,5.2662674195959e-09,1.63744082880102e-08,3.80213424268806e-08,7.37013316252193e-08,1.26462911129735e-07,1.98832947513683e-07,2.92825213625396e-07,4.09988785684554e-07
0,9.52762309519183e-13,4.03064693534399e-11,3.21954008431541e-10,1.31921085909251e-09,3.77652149951806e-09,8.65283546407388e-09,1.70473483728495e-08,3.01291907541136e-08,4.90801983841489e-08,7.50526597099419e-08
0,1.69833113269482e-14,1.45989004470562e-12,1.76611013922202e-11,9.70786436099117e-11,3.4867494868619e-10,9.60502789076322e-10,2.20870660779088e-09,4.45777595611339e-09,8.15402734448277e-09,1.38148719622932e-08
0,3.8915196963406e-11,2.71147134577767e-09,2.44901018232562e-08,1.01911652264688e-07,2.85170970090303e-07,6.29937137214719e-07,1.19197488155735e-06,2.02429502148315e-06,3.17588061786517e-06,4.69121110232959e-06
0,2.69185075542407e-10,1.00640042548647e-08,6.5998653123948e-08,2.25483644613014e-07,5.52286794898488e-07,1.10885707763952e-06,1.95314930874714e-06,3.13753072469243e-06,4.70863852449393e-06,6.70761014036735e-06
0,1.89155326006871e-09,4.15998208034506e-08,2.08604035348676e-07,6.01200476492204e-07,1.30548633543253e-06,2.39315754080736e-06,3.9233691392589e-06,5.9449170992954e-06,8.49806448732054e-06,1.16159780561656e-05
0,1.20839005460873e-08,1.63063271190061e-07,6.45781047622635e-07,1.61034741128995e-06,3.16276730648069e-06,5.37778778885908e-06,8.30838167561027e-06,1.19919722977472e-05,1.64544997795969e-05,2.17132051523398e-05
0,6.98490407129936e-08,6.0448889346693e-07,1.94055108474347e-06,4.2597649069398e-06,7.66233240335793e-06,1.22016422946016e-05,1.79016972891742e-05,2.476703497099e-05,3.27887852389906e-05,4.19486886713683e-05
0,3.50437914406735e-07,2.02949943667367e-06,5.38075813382185e-06,1.04873870799375e-05,1.73426483579829e-05,2.58972599259919e-05,3.60796128969124e-05,4.78063686693464e-05,6.09885319751288e-05,7.55352438512354e-05
0,2.52830836713648e-10,3.03681967057117e-09,1.23485455469166e-08,3.26088143094103e-08,6.82703234078585e-08,1.23666594902017e-07,2.02940810635938e-07,3.10010651321603e-07,4.48552679684649e-07,6.21997431145188e-07
0,5.62803310698392e-13,1.27640446069762e-11,7.32343235940447e-11,2.42833323537223e-10,6.0008786533641e-10,1.23564347077632e-09,2.24827721329559e-09,3.74216806129839e-09,5.82509155188699e-09,8.60722506603327e-09
0,1.01387223716053e-14,4.50063177569099e-13,3.76367908055613e-12,1.6114550692278e-11,4.81228068266938e-11,1.1484654272007e-10,2.3535141406549e-10,4.32106599374873e-10,7.30344360289206e-10,1.15746018360108e-09
0,1.76594647095188e-16,1.5591711552107e-14,1.93446159551681e-13,1.08935554130944e-12,4.00524857658289e-12,1.12878863819327e-11,2.65435340079881e-11,5.47630083633411e-11,1.02366893517743e-10,1.77190911910303e-10
0,2.72938929463367e-18,4.86658533444932e-16,9.10403238648642e-15,6.85236595138709e-14,3.15126909211129e-13,1.06497145870696e-12,2.91648358432319e-12,6.85836462119616e-12,1.4373187057492e-11,2.75299294530364e-11
0,6.21517803140922e-14,9.59223157644613e-12,1.40071205391153e-10,8.23460517316941e-10,3.01588393968386e-09,8.29925719783238e-09,1.88974373890384e-08,3.76435597362723e-08,6.79236252804073e-08,1.13611867015149e-07
0,4.89626154105836e-13,4.05815676062707e-11,4.27904064325323e-10,2.04932427478431e-09,6.51652855169281e-09,1.61738279532313e-08,3.40432642093828e-08,6.37469204528233e-08,1.09424110270049e-07,1.75651555512571e-07
0,3.92394931909311e-12,1.90205223022698e-10,1.52475376523412e-09,6.12909774497683e-09,1.7202341627476e-08,3.88279099539024e-08,7.57896022082591e-08,1.33417356513312e-07,2.17461256827561e-07,3.33987690067168e-07
0,2.8933367514824e-11,8.49634390328452e-10,5.32093344063423e-09,1.83496200775791e-08,4.62765938242325e-08,9.63901823877104e-08,1.76587279811171e-07,2.95134919493546e-07,4.60499444520641e-07,6.81219273509691e-07
0,1.95394555097804e-10,3.60599662518081e-09,1.80378231091857e-08,5.41614780599373e-08,1.24057163023099e-07,2.4042528230335e-07,4.1611636054975e-07,6.638317374418e-07,9.95928543382885e-07,1.42428849426674e-06
0,1.14722473747572e-09,1.37465796551839e-08,5.57323434828026e-08,1.46670400626854e-07,3.05907787720263e-07,5.51856057412464e-07,9.01663873101398e-07,1.37107045554111e-06,1.97435903337969e-06,2.72436530869665e-06
0,6.01331068747146e-13,1.48666863208301e-11,9.22909151658988e-11,3.28914376451997e-10,8.6846657139908e-10,1.90065440926915e-09,3.65824807559149e-09,6.41362850871594e-09,1.04751195906183e-08,1.61832994397799e-08
0,2.47209629421286e-16,1.20861421473939e-14,1.10842575020953e-13,5.18453630267307e-13,1.68538118789818e-12,4.36405036350932e-12,9.67329876973588e-12,1.91546745620659e-11,3.48213414272979e-11,5.92006053141907e-11
0,1.89201707214526e-18,1.74461832628427e-16,2.25617452264583e-15,1.32210769025309e-14,5.05114773691188e-14,1.47735746305447e-13,3.60121598845708e-13,7.69378961925591e-13,1.48783448922978e-12,2.66185680931199e-12
0,2.82938853517197e-20,5.166160471115e-18,9.88324205334377e-17,7.5994903425817e-16,3.56758223375618e-15,1.23000563514818e-14,3.43481298564478e-14,8.23311838199757e-14,1.75814766445246e-13,3.43039404178284e-13
0,3.8648935971775e-22,1.41879457944149e-19,4.08084894427787e-18,4.18430606959238e-17,2.4523734952935e-16,1.01226704556591e-15,3.28784556445063e-15,8.97275887121034e-15,2.14634799098414e-14,4.63096283150802e-14
0,8.69726640954885e-17,2.92087981800005e-14,6.81319014247032e-13,5.60977017733479e-12,2.67241290013728e-11,9.12044259353017e-11,2.49116450278525e-10,5.80809267726238e-10,1.20365994453422e-09,2.27799582449115e-09
0,7.66381761027786e-16,1.38259280480535e-13,2.31866103567873e-12,1.54608384966719e-11,6.35500210965569e-11,1.94448402919345e-10,4.88264062451473e-10,1.06479889696641e-09,2.08990883233503e-09,3.78080528306937e-09
0,6.87383174408679e-15,7.21096117919272e-13,9.14517774138443e-12,5.09570157318565e-11,1.84185808276166e-10,5.10862690038548e-10,1.1861963221612e-09,2.42559699182021e-09,4.50988933263573e-09,7.78908854017501e-09
0,5.72300830893883e-14,3.59756333500574e-12,3.53239285399593e-11,1.6766501693894e-10,5.4153652527201e-10,1.38002614943133e-09,2.99692285950551e-09,5.80167590670959e-09,1.03017611203301e-08,1.71032265873292e-08
0,4.4015162593732e-13,1.71041885299831e-11,1.32555658504776e-10,5.42962213883109e-10,1.58183574169237e-09,3.73024467366084e-09,7.61934524058875e-09,1.40282403312057e-08,2.38787854849993e-08,3.82284985005463e-08
0,2.94015789946406e-12,7.24491356536544e-11,4.48309024211327e-10,1.59267035200818e-09,4.19217068372523e-09,9.14631653977701e-09,1.75503380261564e-08,3.06757992440402e-08,4.99505561914961e-08,7.69389888117878e-08
0,1.25520662515885e-15,6.34202350482921e-14,5.98701785935928e-13,2.87279812512801e-12,9.55263541481702e-12,2.52380922965864e-11,5.69558750757137e-11,1.14607040894637e-10,2.11363987866348e-10,3.6401786096932e-10
0,2.69293069884163e-19,2.76242819230984e-17,3.94910273521441e-16,2.5438021672209e-15,1.0629681164291e-14,3.38496697614216e-14,8.94675031954185e-14,2.06465258826618e-13,4.29763614969243e-13,8.24924625699432e-13
0,3.40393634256781e-22,6.53632617070714e-20,1.31310268914671e-18,1.05893998255463e-17,5.20829887103865e-17,1.87956724398352e-16,5.48951609054929e-16,1.3751099157628e-15,3.06669713938502e-15,6.2447989892164e-15
0,4.00278935798164e-24,1.50166583042231e-21,4.40866620105182e-20,4.60970828253607e-19,2.75310436445269e-18,1.15735040672427e-17,3.82669184362989e-17,1.06268988633135e-16,2.58592708871164e-16,5.67426085674954e-16
0,4.89860423986417e-26,3.68055944030794e-23,1.6212071379999e-21,2.25639400694407e-20,1.6800782168471e-19,8.44489572108405e-19,3.24448865148392e-18,1.02486142861079e-17,2.79143092738667e-17,6.76923776023426e-17
1 0 1.15723009391899e-05 0.000573422585977779 0.00271483227696389 0.00592017488450261 0.00919024372055321 0.0119992110381186 0.01421611640169 0.0158871549414673 0.0171115136326332 0.0179901833258047
2 0 4.32496365970161e-05 0.00110003761455522 0.0038588402926536 0.00723956931728921 0.0103665385368182 0.0129275133140941 0.0149018727052112 0.0163722917415511 0.0174425542133075 0.0182067473313329
3 0 0.000156410551955808 0.0022501860978233 0.00596846775043427 0.00951979828485079 0.0123439077799255 0.0144650737976258 0.0160242190667615 0.0171550743320483 0.0179654704632021 0.0185369538227271
4 0 0.000449900131743341 0.00397802650798475 0.0085351147388942 0.0120552559726064 0.0144482796517419 0.0160547847343675 0.0171511707942764 0.0179099936829702 0.0184375120234885 0.0188001645647094
5 0 0.00096247784605253 0.00575161551352107 0.0106416083625011 0.0139078265679269 0.0158706072121663 0.0170493964826529 0.0177834857154376 0.0182584391833927 0.018573228563985 0.0187811039627322
6 0 0.00139148060908994 0.00653728609085073 0.0112226963812925 0.0141895931925161 0.0159026822227563 0.0168925530607027 0.0174883152516615 0.017865325174865 0.0181135647391488 0.0182789062492789
7 0 7.88755181655377e-05 0.000890855516524495 0.0025489601065516 0.00451888907818989 0.00640234215109739 0.00803400181298965 0.00938380805959316 0.0104782803692741 0.0113602330285538 0.0120715601783272
8 0 1.96835046894113e-06 5.20032828635902e-05 0.000248131836356188 0.000630746867714403 0.00117274437938983 0.00182064447664627 0.00252211668991391 0.00323643607872941 0.00393564702867779 0.00460249317726503
9 0 4.05263620284678e-08 2.41553600349545e-06 1.87665958722685e-05 6.74081501157623e-05 0.000163264089823103 0.000313023273063137 0.000515631508985307 0.000764816320228696 0.00105167332970099 0.00136658044925295
10 0 7.12888460576857e-10 9.36408593803959e-08 1.16671799220012e-06 5.86484773654181e-06 1.84017858631692e-05 4.34716444970184e-05 8.51739231661631e-05 0.000146375097555955 0.000228524220562364 0.000331765311847696
11 0 1.11266213313442e-11 3.21569209939452e-09 6.45997582539074e-08 4.58674913815976e-07 1.88637915450085e-06 5.56437226690733e-06 1.31531030829188e-05 2.6577288329037e-05 4.78110315422556e-05 7.86848274922097e-05
12 0 4.35985871981009e-05 0.0011173013784839 0.00393465640259938 0.00738648425859891 0.0105643563174128 0.0131485926879987 0.0151246465122267 0.016583690239707 0.0176362908990393 0.0183809352240671
13 0 0.000162940275870536 0.00214415472789055 0.0055971341202626 0.00904266600518215 0.0119317484324307 0.01418453738493 0.0158744170951516 0.0171103507405317 0.0179967639568099 0.0186199630282087
14 0 0.000589272728343116 0.00438867743664915 0.00866893626959719 0.0119151946783314 0.0142428604635392 0.0159134847420158 0.0171142649822806 0.0179721981758363 0.0185775454158148 0.0189955063245207
15 0 0.00169526769334991 0.00776694628989879 0.0124247154337811 0.0151382671118746 0.0167367107498905 0.0177366662921586 0.0183940071716407 0.0188368236363926 0.0191342578330509 0.0193277227804878
16 0 0.00362839452566188 0.0112526737744286 0.0155521246084951 0.0175590393037948 0.0184985843185778 0.0189571544442947 0.0191923705608016 0.0193168273546316 0.0193791769704849 0.0194019806796609
17 0 0.00525177970919126 0.0128429513741672 0.0165189505188843 0.0180778870279157 0.0187197075826292 0.018968965691842 0.0190518322037241 0.0190652772723224 0.0190482483782045 0.019016041284815
18 0 0.000385153961868949 0.00229577940499986 0.00480460165448024 0.00711204718132809 0.0089909576548431 0.0104472162269959 0.0115548772926463 0.0123946614477843 0.0130350658282232 0.013528343285464
19 0 1.16858258297074e-05 0.000164584714174317 0.000570116218846185 0.00119271144355875 0.00194723110061389 0.00275621955267251 0.00356441433332025 0.00433765719695555 0.00505760032168857 0.00571654514201105
20 0 2.81208380577636e-07 8.97423573740115e-06 5.03422264992399e-05 0.000147427646174569 0.000310274427544454 0.000536832367026033 0.000817602280027168 0.00113989443932459 0.00149069663261676 0.00185828031564749
21 0 5.63399964199174e-09 3.96964588417007e-07 3.55564115271452e-06 1.44716592579598e-05 3.91537025779409e-05 8.28348829677685e-05 0.000148977185800308 0.000239043494747069 0.00035275053097983 0.000488513445484224
22 0 9.84398346768558e-11 1.5293568296117e-08 2.20334810416761e-07 1.26108145384204e-06 4.44865566281344e-06 1.16871019830672e-05 2.52195633384727e-05 4.73253788486059e-05 8.00591501276681e-05 0.000125072519296284
23 0 0.00015939225040812 0.00233357034425003 0.00625562530069953 0.0100135877171516 0.0129676882444301 0.0151374162693059 0.0166874294020243 0.0177764984876221 0.0185308441084237 0.0190433426894296
24 0 0.000595680647430986 0.0044801608514742 0.00890743169921897 0.0122772025886856 0.0146734676860174 0.0163633416618241 0.0175504470842378 0.0183769265178609 0.018943746718196 0.019322547574371
25 0 0.00215448195385171 0.0091767592756406 0.0138199838635887 0.0162232592499362 0.0175799863177681 0.0184333811027871 0.0190005234819769 0.0193808295698239 0.0196287328140203 0.01977995018463
26 0 0.00619992143295609 0.0162622253524985 0.0198667054266907 0.0207089785376823 0.0207820327860086 0.0206824193920932 0.0205605370395911 0.0204471756023412 0.0203412141522796 0.0202389042951594
27 0 0.0132779136133337 0.0236231555894036 0.0250044710521133 0.0242144479160946 0.0231938485225262 0.0223380815152074 0.0216793603038312 0.0211797341820616 0.020794392809717 0.0204895366674834
28 0 0.0192489696423083 0.0271184370488408 0.026840217635384 0.0252838506050641 0.023849962885927 0.0227245048675836 0.0218698389880889 0.0212219274277141 0.0207240679736698 0.020334308600127
29 0 0.00188379770767293 0.00657190925731914 0.0104892667525962 0.0130700680836906 0.0146349533533907 0.015535980568487 0.0160269602381333 0.0162728756742906 0.0163767130755721 0.016400710298742
30 0 7.01829175866307e-05 0.000581093706531354 0.00153370940121776 0.00269094081762158 0.00386803514667116 0.00496316154582596 0.00593261862345271 0.00676642929553236 0.0074719703393928 0.00806413175855109
31 0 1.97847089603411e-06 3.69925955384929e-05 0.000157406497985584 0.000384860592255597 0.000709846476804252 0.0011079382186538 0.00155170108970297 0.00201681216519113 0.00248426641823041 0.00294058581228933
32 0 4.51078087575571e-08 1.85104501980116e-06 1.25057013282593e-05 4.22892094350572e-05 9.98395815944757e-05 0.000189787308802236 0.000312690644970973 0.000466034643916367 0.000645463690913993 0.000845828528229199
33 0 8.80252798763183e-10 7.92215439962928e-08 8.56765592551646e-07 4.05803525996784e-06 1.2449180396882e-05 2.9295973763362e-05 5.7746988001425e-05 0.0001003733295813 0.000158928915289496 0.00023429575744269
34 0 0.000466368704740309 0.00424791180746615 0.00926944539909274 0.0131713216643424 0.0157625616947689 0.0174133722552654 0.0184577152890545 0.0191156877417616 0.0195250042740468 0.0197700479556243
35 0 0.00174300015594418 0.00815930380362134 0.0132116161354557 0.016172754442502 0.0178696343095053 0.0188636958720877 0.0194550603754561 0.0198042367684176 0.0200009359043926 0.0200979478510297
36 0 0.00630533087106852 0.0167264504542955 0.0205352907522532 0.021433296617797 0.0214908287288404 0.021342901494693 0.0211590163834043 0.0209808421912509 0.020813382900871 0.0206559438303523
37 0 0.0181516683021334 0.0296877506877057 0.0296188711707772 0.0274991387847508 0.0255697170579367 0.0241218925670229 0.0230708035427124 0.0223012794968431 0.0217225548818433 0.0212745536462067
38 0 0.0389067518217964 0.0432743493327086 0.037525495598965 0.0324527348417642 0.0288543692365208 0.0263663031569143 0.0246230583185604 0.0233728213888174 0.0224524193870472 0.0217568092848378
39 0 0.0565396239160925 0.0500897261990562 0.0408387347867424 0.0344846493850116 0.0302543240032215 0.0273643802632421 0.0253287379325492 0.0238539136707014 0.0227584403765214 0.02192583980509
40 0 0.00794755170946868 0.0172628249510397 0.0219912124473273 0.0237906919799591 0.0240591222673087 0.0235982544067714 0.0228369786001951 0.021996032718532 0.0211823304839058 0.0204423856134907
41 0 0.000373684524467729 0.00190283496658901 0.00395283626547245 0.00595433814031928 0.00766198835968331 0.0090150942497423 0.0100370905098692 0.0107818312810106 0.0113083452714233 0.0116697843722264
42 0 1.24794246503553e-05 0.000141270114233274 0.000466648605475989 0.000969170133300241 0.00158727378005724 0.00225743530947124 0.00292973311195242 0.00357075743939607 0.00416127927601125 0.00469265749237697
43 0 3.25056776407433e-07 7.95461921640221e-06 4.12089493515841e-05 0.000117250753288648 0.00024402042254145 0.000420314286347587 0.000638908995931823 0.000889809192557268 0.00116261453581859 0.00144792147140295
44 0 7.0854379238238e-09 3.75524392933622e-07 3.08321489667331e-06 1.21925554449326e-05 3.27749649029249e-05 6.95563221553643e-05 0.000126018794389096 0.000204052811537285 0.000304018569398211 0.000425019521621274
45 0 0.00102836224521133 0.0064274670666241 0.0121491816508084 0.015968397765568 0.0181492003638804 0.019314880428558 0.0199102032969297 0.0201928751968812 0.0203044557297043 0.020320071184116
46 0 0.00384385388740204 0.0123515070854559 0.0173313777131353 0.0196324133235912 0.0206081184457373 0.0209612687545928 0.0210258040457677 0.0209599611581841 0.0208374482363417 0.0206927731773186
47 0 0.0139084634226096 0.0253420008838306 0.0269858208570455 0.0260863431776468 0.0248662406090429 0.023805495300228 0.0229587141516884 0.0222941985053244 0.0217679373130175 0.021344966099159
48 0 0.0400577489259572 0.0450598366852064 0.0390556903095381 0.0336304680408348 0.0297597604093353 0.0270807237205804 0.0252035706273644 0.0238573134643687 0.0228662562144913 0.0221177566182138
49 0 0.0859596622787531 0.0659645822190159 0.0498440895724295 0.0400638374763904 0.0339433683852068 0.0299353070092985 0.0272039578032914 0.0252772077773997 0.0238777953326208 0.0228341669415018
50 0 0.125398084679699 0.0772427043755313 0.0551767906604444 0.0434193651923798 0.0363278914606691 0.0317025734216055 0.0285266548704347 0.0262622056865752 0.0246012510386757 0.0233529211513904
51 0 0.0279719026476077 0.0399912836159061 0.041824217737992 0.0399960375533372 0.0370306079722165 0.0339334669014817 0.0310904350363235 0.0286238301128732 0.0265419722346029 0.0248094228214593
52 0 0.0016878543861307 0.00546802879678612 0.00908467643641864 0.0118685267101036 0.0137867836918316 0.014992473171871 0.0156668769769553 0.0159679687584234 0.0160191464240754 0.0159109388085067
53 0 6.66210210366172e-05 0.000466311941721689 0.0012074539008488 0.00214399617569523 0.00313678410870533 0.0040909928004654 0.0049535861902635 0.00570178710489221 0.00633199242057907 0.00685150908457396
54 0 1.96581789972705e-06 2.90655193631781e-05 0.000116181708737399 0.000279461298722284 0.000515297156287373 0.00080891264649635 0.00114182790936584 0.00149639884138984 0.0018579271762422 0.00221530254986257
55 0 4.73511582533572e-08 1.48929389156945e-06 9.32003158799015e-06 3.08850626482769e-05 7.30754947692817e-05 0.000140629698309681 0.000235669226477032 0.00035796975388455 0.000505546298234415 0.000675276938000184
56 0 0.0015836697501133 0.00789553113761796 0.0138353209275783 0.0174900423844659 0.0193829171959464 0.0202511267409907 0.0205839676229735 0.0206522039767607 0.0205960670144616 0.0204840229205406
57 0 0.00592024795026366 0.015178056357401 0.0197491887189647 0.0215219374447417 0.0220321907378727 0.0220033162511111 0.0217646356223314 0.0214641136669779 0.0211632832330552 0.0208849932589192
58 0 0.0214261698026196 0.0311627748380295 0.030789710824671 0.0286485315817415 0.0266434190236869 0.0250513323715184 0.023828703885917 0.0228921858496036 0.0221672538845208 0.0215984913078681
59 0 0.0617381544584604 0.0554953966664163 0.0446769294750762 0.0370604778930011 0.0320153173963507 0.0286236282907804 0.0262786287048439 0.0246098514264461 0.0233900725628177 0.0224762767353041
60 0 0.132662095447386 0.0815715457115294 0.0573620962224714 0.0444656436513204 0.0367970084501995 0.0318900160341415 0.0285856373696197 0.0262711290074283 0.0245996244789251 0.0233597987792184
61 1 0.194600594053136 0.0967188378926377 0.0645033312825157 0.0489815255136541 0.0400071807557426 0.0342746856436233 0.0303863403322649 0.0276362424095596 0.0256323186910777 0.0241347191227446
62 0 0.072774737081717 0.0731608818750184 0.0649513569830106 0.0562393519385766 0.048657668424965 0.0424321620057059 0.0374285709142598 0.0334382577022264 0.0302593069735787 0.0277213581035032
63 0 0.00546229967617301 0.0117769944796152 0.0160960402630076 0.0186595966553889 0.0199729770538671 0.020458608892436 0.0204223397121343 0.0200740130964532 0.0195534451842866 0.0189514347157146
64 0 0.000246612705267533 0.00110781620007196 0.00231152127659001 0.00359442519681962 0.00480227348450595 0.00586145140404475 0.00674755083035105 0.00746338966694999 0.00802538295172145 0.00845539933096027
65 0 8.01024172201437e-06 7.40436516662321e-05 0.000234915899100426 0.000490064251651924 0.000819680663206465 0.00119845837778001 0.00160264998868245 0.00201295787243647 0.00241514785548912 0.00279964947569617
66 0 2.07955309779492e-07 4.0104486961948e-06 1.96923787162901e-05 5.61707039451707e-05 0.00011992118154755 0.000214121267381263 0.000338952769617425 0.000492335832038914 0.000670725912883758 0.000869802668989746
67 0 9.15481631874651e-06 0.000117693874573294 0.000377019455101549 0.000741577814721888 0.00115788487944848 0.00159137882061179 0.00202342112731787 0.00244488663055897 0.00285171452271575 0.00324239719313011
68 0 4.46105775530757e-05 0.000303984477552903 0.000717806961735601 0.00118873254936734 0.00166857393355206 0.00213667568842936 0.00258503807646796 0.00301133151201256 0.00341571030049681 0.00379926644315538
69 0 0.000217726325331875 0.000873770408536327 0.00159096186837063 0.00224649712918517 0.00282908355295164 0.00334893383349626 0.00381769528011674 0.00424489367607894 0.00463786438443504 0.00500213301923337
70 0 0.000918933831995098 0.00232435924877404 0.00343741337091249 0.00429315823548186 0.00497145196445075 0.00552787579177755 0.00599805857236328 0.00640506369246164 0.00676421367084299 0.00708600072229698
71 0 0.00331122058413453 0.00568447123669356 0.00712540944284069 0.00809397304876091 0.00879084024979697 0.0093162979589995 0.00972657995173228 0.0100559006360597 0.010326180081839 0.0105521545670149
72 0 0.0100043670600405 0.0127097661456827 0.0139937515689219 0.0147091809677894 0.0151257626062355 0.0153636070507909 0.0154869837094123 0.0155343002312855 0.0155299222027964 0.0154901124905561
73 0 0.000136637116798085 0.000338905367123057 0.000531352383440882 0.000703792701127588 0.000856724436487964 0.000992875271716 0.00111515974854185 0.00122613935565325 0.00132792886232481 0.00142222359205319
74 0 5.41197942669333e-06 2.66179151421015e-05 6.06837139232639e-05 0.00010289135389313 0.00014958545120669 0.00019826296968678 0.000247306756529671 0.000295722537183644 0.000342937360699378 0.000388655525576309
75 0 1.72186371215188e-07 1.70696816511905e-06 5.79640506738781e-06 1.29195485396102e-05 2.30515604056451e-05 3.58992272238309e-05 5.10570165270148e-05 6.80982556623907e-05 8.66227559703461e-05 0.000106279145950884
76 0 4.32364439009495e-09 8.64589599304522e-08 4.3955011662034e-07 1.29702112369114e-06 2.8633412989769e-06 5.28573284839916e-06 8.65071350621842e-06 1.29921291564899e-05 1.83031949023414e-05 2.45484723579094e-05
77 0 9.12457420765116e-11 3.74315585927427e-09 2.9038454685416e-08 1.15682654773554e-07 3.22205377431422e-07 7.18623792678699e-07 1.37865133447057e-06 2.37328126021504e-06 3.76631554161963e-06 5.61170605601181e-06
78 0 2.08011169409904e-08 6.36518490608328e-07 3.48383663648875e-06 1.01031069547314e-05 2.13412103139757e-05 3.74893709216357e-05 5.85171433417359e-05 8.42328798186352e-05 0.000114372709309901 0.000148647128283624
79 0 1.23164013985779e-07 2.01619957543725e-06 8.06746622712326e-06 1.9408951787608e-05 3.62807655737012e-05 5.85218420212134e-05 8.58109894680849e-05 0.000117776723517294 0.000154044674846032 0.00019425785338382
80 0 7.38055239000408e-07 7.13661815273176e-06 2.1957201331878e-05 4.48051934222451e-05 7.46425998373469e-05 0.000110486321507166 0.000151514246571192 0.000197051618892773 0.000246540311467432 0.00029951243350813
81 0 3.94931915614762e-06 2.37664477362685e-05 5.85078377488893e-05 0.000104356634872865 0.000158457684921854 0.000218839976363591 0.000284118381194624 0.000353286447661602 0.00042558888273829 0.000500441217237315
82 0 1.87536920881212e-05 7.42297836614388e-05 0.000150953066786234 0.000240330072584709 0.000337698410142427 0.000440229993907094 0.00054607757879758 0.000653971107402446 0.000763006862111349 0.000872522857886958
83 0 7.67060564087528e-05 0.000211583511011325 0.000364290214247424 0.000523711941940329 0.000684842914248833 0.000844968149576811 0.00100249413331233 0.00115645811127351 0.00130628569191617 0.00145165008061455
84 0 1.29198147797745e-07 7.26337646539414e-07 1.8789774469963e-06 3.58894576036159e-06 5.83743184885366e-06 8.59992729697426e-06 1.1850973027563e-05 1.55658536023551e-05 1.97211620171109e-05 2.42949568717564e-05
85 0 1.89355863862761e-09 2.0149347443802e-08 7.32356468455249e-08 1.74272463156743e-07 3.31196104192811e-07 5.481872642976e-07 8.2692218882057e-07 1.16749109225162e-06 1.56902754100022e-06 2.03013177037545e-06
86 0 4.63537374431423e-11 9.81049734780514e-10 5.2662674195959e-09 1.63744082880102e-08 3.80213424268806e-08 7.37013316252193e-08 1.26462911129735e-07 1.98832947513683e-07 2.92825213625396e-07 4.09988785684554e-07
87 0 9.52762309519183e-13 4.03064693534399e-11 3.21954008431541e-10 1.31921085909251e-09 3.77652149951806e-09 8.65283546407388e-09 1.70473483728495e-08 3.01291907541136e-08 4.90801983841489e-08 7.50526597099419e-08
88 0 1.69833113269482e-14 1.45989004470562e-12 1.76611013922202e-11 9.70786436099117e-11 3.4867494868619e-10 9.60502789076322e-10 2.20870660779088e-09 4.45777595611339e-09 8.15402734448277e-09 1.38148719622932e-08
89 0 3.8915196963406e-11 2.71147134577767e-09 2.44901018232562e-08 1.01911652264688e-07 2.85170970090303e-07 6.29937137214719e-07 1.19197488155735e-06 2.02429502148315e-06 3.17588061786517e-06 4.69121110232959e-06
90 0 2.69185075542407e-10 1.00640042548647e-08 6.5998653123948e-08 2.25483644613014e-07 5.52286794898488e-07 1.10885707763952e-06 1.95314930874714e-06 3.13753072469243e-06 4.70863852449393e-06 6.70761014036735e-06
91 0 1.89155326006871e-09 4.15998208034506e-08 2.08604035348676e-07 6.01200476492204e-07 1.30548633543253e-06 2.39315754080736e-06 3.9233691392589e-06 5.9449170992954e-06 8.49806448732054e-06 1.16159780561656e-05
92 0 1.20839005460873e-08 1.63063271190061e-07 6.45781047622635e-07 1.61034741128995e-06 3.16276730648069e-06 5.37778778885908e-06 8.30838167561027e-06 1.19919722977472e-05 1.64544997795969e-05 2.17132051523398e-05
93 0 6.98490407129936e-08 6.0448889346693e-07 1.94055108474347e-06 4.2597649069398e-06 7.66233240335793e-06 1.22016422946016e-05 1.79016972891742e-05 2.476703497099e-05 3.27887852389906e-05 4.19486886713683e-05
94 0 3.50437914406735e-07 2.02949943667367e-06 5.38075813382185e-06 1.04873870799375e-05 1.73426483579829e-05 2.58972599259919e-05 3.60796128969124e-05 4.78063686693464e-05 6.09885319751288e-05 7.55352438512354e-05
95 0 2.52830836713648e-10 3.03681967057117e-09 1.23485455469166e-08 3.26088143094103e-08 6.82703234078585e-08 1.23666594902017e-07 2.02940810635938e-07 3.10010651321603e-07 4.48552679684649e-07 6.21997431145188e-07
96 0 5.62803310698392e-13 1.27640446069762e-11 7.32343235940447e-11 2.42833323537223e-10 6.0008786533641e-10 1.23564347077632e-09 2.24827721329559e-09 3.74216806129839e-09 5.82509155188699e-09 8.60722506603327e-09
97 0 1.01387223716053e-14 4.50063177569099e-13 3.76367908055613e-12 1.6114550692278e-11 4.81228068266938e-11 1.1484654272007e-10 2.3535141406549e-10 4.32106599374873e-10 7.30344360289206e-10 1.15746018360108e-09
98 0 1.76594647095188e-16 1.5591711552107e-14 1.93446159551681e-13 1.08935554130944e-12 4.00524857658289e-12 1.12878863819327e-11 2.65435340079881e-11 5.47630083633411e-11 1.02366893517743e-10 1.77190911910303e-10
99 0 2.72938929463367e-18 4.86658533444932e-16 9.10403238648642e-15 6.85236595138709e-14 3.15126909211129e-13 1.06497145870696e-12 2.91648358432319e-12 6.85836462119616e-12 1.4373187057492e-11 2.75299294530364e-11
100 0 6.21517803140922e-14 9.59223157644613e-12 1.40071205391153e-10 8.23460517316941e-10 3.01588393968386e-09 8.29925719783238e-09 1.88974373890384e-08 3.76435597362723e-08 6.79236252804073e-08 1.13611867015149e-07
101 0 4.89626154105836e-13 4.05815676062707e-11 4.27904064325323e-10 2.04932427478431e-09 6.51652855169281e-09 1.61738279532313e-08 3.40432642093828e-08 6.37469204528233e-08 1.09424110270049e-07 1.75651555512571e-07
102 0 3.92394931909311e-12 1.90205223022698e-10 1.52475376523412e-09 6.12909774497683e-09 1.7202341627476e-08 3.88279099539024e-08 7.57896022082591e-08 1.33417356513312e-07 2.17461256827561e-07 3.33987690067168e-07
103 0 2.8933367514824e-11 8.49634390328452e-10 5.32093344063423e-09 1.83496200775791e-08 4.62765938242325e-08 9.63901823877104e-08 1.76587279811171e-07 2.95134919493546e-07 4.60499444520641e-07 6.81219273509691e-07
104 0 1.95394555097804e-10 3.60599662518081e-09 1.80378231091857e-08 5.41614780599373e-08 1.24057163023099e-07 2.4042528230335e-07 4.1611636054975e-07 6.638317374418e-07 9.95928543382885e-07 1.42428849426674e-06
105 0 1.14722473747572e-09 1.37465796551839e-08 5.57323434828026e-08 1.46670400626854e-07 3.05907787720263e-07 5.51856057412464e-07 9.01663873101398e-07 1.37107045554111e-06 1.97435903337969e-06 2.72436530869665e-06
106 0 6.01331068747146e-13 1.48666863208301e-11 9.22909151658988e-11 3.28914376451997e-10 8.6846657139908e-10 1.90065440926915e-09 3.65824807559149e-09 6.41362850871594e-09 1.04751195906183e-08 1.61832994397799e-08
107 0 2.47209629421286e-16 1.20861421473939e-14 1.10842575020953e-13 5.18453630267307e-13 1.68538118789818e-12 4.36405036350932e-12 9.67329876973588e-12 1.91546745620659e-11 3.48213414272979e-11 5.92006053141907e-11
108 0 1.89201707214526e-18 1.74461832628427e-16 2.25617452264583e-15 1.32210769025309e-14 5.05114773691188e-14 1.47735746305447e-13 3.60121598845708e-13 7.69378961925591e-13 1.48783448922978e-12 2.66185680931199e-12
109 0 2.82938853517197e-20 5.166160471115e-18 9.88324205334377e-17 7.5994903425817e-16 3.56758223375618e-15 1.23000563514818e-14 3.43481298564478e-14 8.23311838199757e-14 1.75814766445246e-13 3.43039404178284e-13
110 0 3.8648935971775e-22 1.41879457944149e-19 4.08084894427787e-18 4.18430606959238e-17 2.4523734952935e-16 1.01226704556591e-15 3.28784556445063e-15 8.97275887121034e-15 2.14634799098414e-14 4.63096283150802e-14
111 0 8.69726640954885e-17 2.92087981800005e-14 6.81319014247032e-13 5.60977017733479e-12 2.67241290013728e-11 9.12044259353017e-11 2.49116450278525e-10 5.80809267726238e-10 1.20365994453422e-09 2.27799582449115e-09
112 0 7.66381761027786e-16 1.38259280480535e-13 2.31866103567873e-12 1.54608384966719e-11 6.35500210965569e-11 1.94448402919345e-10 4.88264062451473e-10 1.06479889696641e-09 2.08990883233503e-09 3.78080528306937e-09
113 0 6.87383174408679e-15 7.21096117919272e-13 9.14517774138443e-12 5.09570157318565e-11 1.84185808276166e-10 5.10862690038548e-10 1.1861963221612e-09 2.42559699182021e-09 4.50988933263573e-09 7.78908854017501e-09
114 0 5.72300830893883e-14 3.59756333500574e-12 3.53239285399593e-11 1.6766501693894e-10 5.4153652527201e-10 1.38002614943133e-09 2.99692285950551e-09 5.80167590670959e-09 1.03017611203301e-08 1.71032265873292e-08
115 0 4.4015162593732e-13 1.71041885299831e-11 1.32555658504776e-10 5.42962213883109e-10 1.58183574169237e-09 3.73024467366084e-09 7.61934524058875e-09 1.40282403312057e-08 2.38787854849993e-08 3.82284985005463e-08
116 0 2.94015789946406e-12 7.24491356536544e-11 4.48309024211327e-10 1.59267035200818e-09 4.19217068372523e-09 9.14631653977701e-09 1.75503380261564e-08 3.06757992440402e-08 4.99505561914961e-08 7.69389888117878e-08
117 0 1.25520662515885e-15 6.34202350482921e-14 5.98701785935928e-13 2.87279812512801e-12 9.55263541481702e-12 2.52380922965864e-11 5.69558750757137e-11 1.14607040894637e-10 2.11363987866348e-10 3.6401786096932e-10
118 0 2.69293069884163e-19 2.76242819230984e-17 3.94910273521441e-16 2.5438021672209e-15 1.0629681164291e-14 3.38496697614216e-14 8.94675031954185e-14 2.06465258826618e-13 4.29763614969243e-13 8.24924625699432e-13
119 0 3.40393634256781e-22 6.53632617070714e-20 1.31310268914671e-18 1.05893998255463e-17 5.20829887103865e-17 1.87956724398352e-16 5.48951609054929e-16 1.3751099157628e-15 3.06669713938502e-15 6.2447989892164e-15
120 0 4.00278935798164e-24 1.50166583042231e-21 4.40866620105182e-20 4.60970828253607e-19 2.75310436445269e-18 1.15735040672427e-17 3.82669184362989e-17 1.06268988633135e-16 2.58592708871164e-16 5.67426085674954e-16
121 0 4.89860423986417e-26 3.68055944030794e-23 1.6212071379999e-21 2.25639400694407e-20 1.6800782168471e-19 8.44489572108405e-19 3.24448865148392e-18 1.02486142861079e-17 2.79143092738667e-17 6.76923776023426e-17

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,116 +0,0 @@
#include <algorithm>
#include <bits/stdint-uintn.h>
#include <vector>
#include <diffusion/BTCSBoundaryCondition.hpp>
#include <diffusion/BTCSUtils.hpp>
constexpr uint8_t DIM_1D = 2;
constexpr uint8_t DIM_2D = 4;
Diffusion::BTCSBoundaryCondition::BTCSBoundaryCondition() {
this->bc_internal.resize(DIM_1D, {0, 0});
this->dim = 1;
// this value is actually unused
this->maxsize = 1;
this->sizes[0] = 1;
this->sizes[1] = 0;
}
Diffusion::BTCSBoundaryCondition::BTCSBoundaryCondition(int x, int y) {
this->maxsize = (x >= y ? x : y);
this->bc_internal.resize(DIM_2D * maxsize, {0, 0});
this->dim = 2;
this->sizes[0] = x;
this->sizes[1] = y;
}
void Diffusion::BTCSBoundaryCondition::setSide(
uint8_t side, Diffusion::boundary_condition &input_bc) {
if (this->dim == 1) {
throw_invalid_argument("setSide requires at least a 2D grid");
}
if (side > 3) {
throw_out_of_range("Invalid range for 2D grid");
}
uint32_t size =
(side == Diffusion::BC_SIDE_LEFT || side == Diffusion::BC_SIDE_RIGHT
? this->sizes[0]
: this->sizes[1]);
for (uint32_t i = 0; i < size; i++) {
this->bc_internal[side * maxsize + i] = input_bc;
}
}
void Diffusion::BTCSBoundaryCondition::setSide(
uint8_t side, std::vector<Diffusion::boundary_condition> &input_bc) {
if (this->dim == 1) {
throw_invalid_argument("setSide requires at least a 2D grid");
}
if (side > 3) {
throw_out_of_range("Invalid range for 2D grid");
}
uint32_t size =
(side == Diffusion::BC_SIDE_LEFT || side == Diffusion::BC_SIDE_RIGHT
? this->sizes[0]
: this->sizes[1]);
if (input_bc.size() > size) {
throw_out_of_range("Input vector is greater than maximum excpected value");
}
for (int i = 0; i < size; i++) {
bc_internal[this->maxsize * side + i] = input_bc[i];
}
}
auto Diffusion::BTCSBoundaryCondition::getSide(uint8_t side)
-> std::vector<Diffusion::boundary_condition> {
if (this->dim == 1) {
throw_invalid_argument("getSide requires at least a 2D grid");
}
if (side > 3) {
throw_out_of_range("Invalid range for 2D grid");
}
uint32_t size =
(side == Diffusion::BC_SIDE_LEFT || side == Diffusion::BC_SIDE_RIGHT
? this->sizes[0]
: this->sizes[1]);
std::vector<Diffusion::boundary_condition> out(size);
for (int i = 0; i < size; i++) {
out[i] = this->bc_internal[this->maxsize * side + i];
}
return out;
}
auto Diffusion::BTCSBoundaryCondition::col(uint32_t i) const
-> Diffusion::bc_tuple {
if (this->dim == 1) {
throw_invalid_argument("Access of column requires at least 2D grid");
}
if (i >= this->sizes[1]) {
throw_out_of_range("Index out of range");
}
return {this->bc_internal[BC_SIDE_TOP * this->maxsize + i],
this->bc_internal[BC_SIDE_BOTTOM * this->maxsize + i]};
}
auto Diffusion::BTCSBoundaryCondition::row(uint32_t i) const
-> Diffusion::bc_tuple {
if (i >= this->sizes[0]) {
throw_out_of_range("Index out of range");
}
return {this->bc_internal[BC_SIDE_LEFT * this->maxsize + i],
this->bc_internal[BC_SIDE_RIGHT * this->maxsize + i]};
}

View File

@ -1,345 +0,0 @@
#include "diffusion/BTCSDiffusion.hpp"
#include "diffusion/BTCSBoundaryCondition.hpp"
#include <Eigen/SparseLU>
#include <Eigen/src/Core/Map.h>
#include <Eigen/src/Core/Matrix.h>
#include <Eigen/src/SparseCore/SparseMatrix.h>
#include <Eigen/src/SparseCore/SparseMatrixBase.h>
#include <algorithm>
#include <array>
#include <bits/stdint-uintn.h>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <ostream>
#include <tuple>
#include <vector>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num() 0
#endif
constexpr double DOUBLE_MACHINE_EPSILON = 1.93e-34;
constexpr int BTCS_MAX_DEP_PER_CELL = 3;
constexpr int BTCS_2D_DT_SIZE = 2;
Diffusion::BTCSDiffusion::BTCSDiffusion(unsigned int dim) : grid_dim(dim) {
grid_cells.resize(dim, 1);
domain_size.resize(dim, 1);
deltas.resize(dim, 1);
this->time_step = 0;
}
void Diffusion::BTCSDiffusion::setXDimensions(double domain_size,
unsigned int n_grid_cells) {
this->domain_size[0] = domain_size;
this->grid_cells[0] = n_grid_cells;
updateInternals();
}
void Diffusion::BTCSDiffusion::setYDimensions(double domain_size,
unsigned int n_grid_cells) {
this->domain_size[1] = domain_size;
this->grid_cells[1] = n_grid_cells;
updateInternals();
}
void Diffusion::BTCSDiffusion::setZDimensions(double domain_size,
unsigned int n_grid_cells) {
this->domain_size[2] = domain_size;
this->grid_cells[2] = n_grid_cells;
updateInternals();
}
auto Diffusion::BTCSDiffusion::getXGridCellsN() -> unsigned int {
return this->grid_cells[0];
}
auto Diffusion::BTCSDiffusion::getYGridCellsN() -> unsigned int {
return this->grid_cells[1];
}
auto Diffusion::BTCSDiffusion::getZGridCellsN() -> unsigned int {
return this->grid_cells[2];
}
auto Diffusion::BTCSDiffusion::getXDomainSize() -> double {
return this->domain_size[0];
}
auto Diffusion::BTCSDiffusion::getYDomainSize() -> double {
return this->domain_size[1];
}
auto Diffusion::BTCSDiffusion::getZDomainSize() -> double {
return this->domain_size[2];
}
void Diffusion::BTCSDiffusion::updateInternals() {
for (int i = 0; i < grid_dim; i++) {
deltas[i] = (double)domain_size[i] / grid_cells[i];
}
}
void Diffusion::BTCSDiffusion::simulate_base(DVectorRowMajor &c,
const Diffusion::bc_tuple &bc,
const DVectorRowMajor &alpha,
double dx, double time_step,
int size,
const DVectorRowMajor &d_ortho) {
Eigen::SparseMatrix<double> A_matrix;
Eigen::VectorXd b_vector;
Eigen::VectorXd x_vector;
A_matrix.resize(size + 2, size + 2);
A_matrix.reserve(Eigen::VectorXi::Constant(size + 2, BTCS_MAX_DEP_PER_CELL));
b_vector.resize(size + 2);
x_vector.resize(size + 2);
fillMatrixFromRow(A_matrix, alpha, size, dx, time_step);
fillVectorFromRow(b_vector, c, alpha, bc, d_ortho, size, dx, time_step);
// start to solve
Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>>
solver;
solver.analyzePattern(A_matrix);
solver.factorize(A_matrix);
x_vector = solver.solve(b_vector);
c = x_vector.segment(1, size);
}
void Diffusion::BTCSDiffusion::simulate1D(
Eigen::Map<DVectorRowMajor> &c, Eigen::Map<const DVectorRowMajor> &alpha,
const Diffusion::BTCSBoundaryCondition &bc) {
int size = this->grid_cells[0];
double dx = this->deltas[0];
double time_step = this->time_step;
DVectorRowMajor input_field = c.row(0);
simulate_base(input_field, bc.row(0), alpha, dx, time_step, size,
Eigen::VectorXd::Constant(size, 0));
c.row(0) << input_field;
}
void Diffusion::BTCSDiffusion::simulate2D(
Eigen::Map<DMatrixRowMajor> &c, Eigen::Map<const DMatrixRowMajor> &alpha,
const Diffusion::BTCSBoundaryCondition &bc) {
int n_rows = this->grid_cells[1];
int n_cols = this->grid_cells[0];
double dx = this->deltas[0];
double local_dt = this->time_step / BTCS_2D_DT_SIZE;
DMatrixRowMajor d_ortho = calc_d_ortho(c, alpha, bc, false, local_dt, dx);
#pragma omp parallel for schedule(dynamic)
for (int i = 0; i < n_rows; i++) {
DVectorRowMajor input_field = c.row(i);
simulate_base(input_field, bc.row(i), alpha.row(i), dx, local_dt, n_cols,
d_ortho.row(i));
c.row(i) << input_field;
}
dx = this->deltas[1];
d_ortho =
calc_d_ortho(c.transpose(), alpha.transpose(), bc, true, local_dt, dx);
#pragma omp parallel for schedule(dynamic)
for (int i = 0; i < n_cols; i++) {
DVectorRowMajor input_field = c.col(i);
simulate_base(input_field, bc.col(i), alpha.col(i), dx, local_dt, n_rows,
d_ortho.row(i));
c.col(i) << input_field.transpose();
}
}
auto Diffusion::BTCSDiffusion::calc_d_ortho(
const DMatrixRowMajor &c, const DMatrixRowMajor &alpha,
const Diffusion::BTCSBoundaryCondition &bc, bool transposed,
double time_step, double dx) -> DMatrixRowMajor {
uint8_t upper = (transposed ? Diffusion::BC_SIDE_LEFT : Diffusion::BC_SIDE_TOP);
uint8_t lower = (transposed ? Diffusion::BC_SIDE_RIGHT : Diffusion::BC_SIDE_BOTTOM);
int n_rows = c.rows();
int n_cols = c.cols();
DMatrixRowMajor d_ortho(n_rows, n_cols);
std::array<double, 3> y_values{};
// first, iterate over first row
for (int j = 0; j < n_cols; j++) {
boundary_condition tmp_bc = bc(upper, j);
double sy = (time_step * alpha(0, j)) / (dx * dx);
y_values[0] = (tmp_bc.type == Diffusion::BC_TYPE_CONSTANT
? tmp_bc.value
: getBCFromFlux(tmp_bc, c(0, j), alpha(0, j)));
y_values[1] = c(0, j);
y_values[2] = c(1, j);
d_ortho(0, j) = -sy * (2 * y_values[0] - 3 * y_values[1] + y_values[2]);
}
// then iterate over inlet
#pragma omp parallel for private(y_values) schedule(dynamic)
for (int i = 1; i < n_rows - 1; i++) {
for (int j = 0; j < n_cols; j++) {
double sy = (time_step * alpha(i, j)) / (dx * dx);
y_values[0] = c(i - 1, j);
y_values[1] = c(i, j);
y_values[2] = c(i + 1, j);
d_ortho(i, j) = -sy * (y_values[0] - 2 * y_values[1] + y_values[2]);
}
}
int end = n_rows - 1;
// and finally over last row
for (int j = 0; j < n_cols; j++) {
boundary_condition tmp_bc = bc(lower, j);
double sy = (time_step * alpha(end, j)) / (dx * dx);
y_values[0] = c(end - 1, j);
y_values[1] = c(end, j);
y_values[2] = (tmp_bc.type == Diffusion::BC_TYPE_CONSTANT
? tmp_bc.value
: getBCFromFlux(tmp_bc, c(end, j), alpha(end, j)));
d_ortho(end, j) = -sy * (y_values[0] - 3 * y_values[1] + 2 * y_values[2]);
}
return d_ortho;
}
void Diffusion::BTCSDiffusion::fillMatrixFromRow(
Eigen::SparseMatrix<double> &A_matrix, const DVectorRowMajor &alpha,
int size, double dx, double time_step) {
double sx = 0;
int A_size = A_matrix.cols();
A_matrix.insert(0, 0) = 1;
sx = (alpha[0] * time_step) / (dx * dx);
A_matrix.insert(1, 1) = -1. - 3. * sx;
A_matrix.insert(1, 0) = 2. * sx;
A_matrix.insert(1, 2) = sx;
for (int j = 2, k = j - 1; k < size - 1; j++, k++) {
sx = (alpha[k] * time_step) / (dx * dx);
A_matrix.insert(j, j) = -1. - 2. * sx;
A_matrix.insert(j, (j - 1)) = sx;
A_matrix.insert(j, (j + 1)) = sx;
}
sx = (alpha[size - 1] * time_step) / (dx * dx);
A_matrix.insert(A_size - 2, A_size - 2) = -1. - 3. * sx;
A_matrix.insert(A_size - 2, A_size - 3) = sx;
A_matrix.insert(A_size - 2, A_size - 1) = 2. * sx;
A_matrix.insert(A_size - 1, A_size - 1) = 1;
}
void Diffusion::BTCSDiffusion::fillVectorFromRow(
Eigen::VectorXd &b_vector, const DVectorRowMajor &c,
const DVectorRowMajor &alpha, const bc_tuple &bc,
const DVectorRowMajor &d_ortho, int size, double dx, double time_step) {
Diffusion::boundary_condition left = bc[0];
Diffusion::boundary_condition right = bc[1];
bool left_constant = (left.type == Diffusion::BC_TYPE_CONSTANT);
bool right_constant = (right.type == Diffusion::BC_TYPE_CONSTANT);
int b_size = b_vector.size();
for (int j = 0; j < size; j++) {
b_vector[j + 1] = -c[j] + d_ortho[j];
}
// this is not correct currently.We will fix this when we are able to define
// FLUX boundary conditions
b_vector[0] =
(left_constant ? left.value : getBCFromFlux(left, c[0], alpha[0]));
b_vector[b_size - 1] =
(right_constant ? right.value
: getBCFromFlux(right, c[size - 1], alpha[size - 1]));
}
void Diffusion::BTCSDiffusion::setTimestep(double time_step) {
this->time_step = time_step;
}
auto Diffusion::BTCSDiffusion::simulate(
double *c, double *alpha, const Diffusion::BTCSBoundaryCondition &bc)
-> double {
std::chrono::high_resolution_clock::time_point start =
std::chrono::high_resolution_clock::now();
if (this->grid_dim == 1) {
Eigen::Map<DVectorRowMajor> c_in(c, this->grid_cells[0]);
Eigen::Map<const DVectorRowMajor> alpha_in(alpha, this->grid_cells[0]);
simulate1D(c_in, alpha_in, bc);
}
if (this->grid_dim == 2) {
Eigen::Map<DMatrixRowMajor> c_in(c, this->grid_cells[1],
this->grid_cells[0]);
Eigen::Map<const DMatrixRowMajor> alpha_in(alpha, this->grid_cells[1],
this->grid_cells[0]);
simulate2D(c_in, alpha_in, bc);
}
std::chrono::high_resolution_clock::time_point end =
std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration =
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
return duration.count();
}
inline auto Diffusion::BTCSDiffusion::getBCFromFlux(boundary_condition bc,
double neighbor_c,
double neighbor_alpha)
-> double {
double val = 0;
if (bc.type == Diffusion::BC_TYPE_CLOSED) {
val = neighbor_c;
} else if (bc.type == Diffusion::BC_TYPE_FLUX) {
// TODO
// val = bc[index].value;
} else {
// TODO: implement error handling here. Type was set to wrong value.
}
return val;
}

View File

@ -1,9 +0,0 @@
add_library(BTCSDiffusion STATIC BTCSDiffusion.cpp BTCSBoundaryCondition.cpp)
target_link_libraries(BTCSDiffusion Eigen3::Eigen)
if(BTCS_USE_OPENMP AND OpenMP_CXX_FOUND)
target_link_libraries(BTCSDiffusion OpenMP::OpenMP_CXX)
endif()
target_include_directories(BTCSDiffusion PUBLIC ../include)

View File

@ -1,5 +1,28 @@
add_library(doctest INTERFACE)
target_include_directories(doctest INTERFACE doctest)
include(FetchContent)
add_executable(test setup.cpp testBoundaryCondition.cpp testDiffusion.cpp)
target_link_libraries(test doctest BTCSDiffusion)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
FetchContent_MakeAvailable(googletest)
add_executable(testTug
setup.cpp
testDiffusion.cpp
testFTCS.cpp
testBoundary.cpp
)
target_link_libraries(testTug tug GTest::gtest)
include(GoogleTest)
gtest_discover_tests(testTug)
# get relative path of the CSV file
get_filename_component(testSimulationCSV "FTCS_11_11_7000.csv" REALPATH)
# set relative path in header file
configure_file(testSimulation.hpp.in testSimulation.hpp)
# include test directory with generated header file from above
target_include_directories(testTug PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/src")

13
test/FTCS_11_11_7000.csv Normal file
View File

@ -0,0 +1,13 @@
0 0 0 0 0 0 0 0 0 0 0
1.88664e-08 3.39962e-08 7.57021e-08 1.76412e-07 4.15752e-07 9.00973e-07 3.65403e-09 9.6579e-12 3.59442e-13 3.42591e-14 3.27595e-15
1.19102e-06 1.95195e-06 3.92165e-06 8.30575e-06 1.78976e-05 3.60742e-05 2.02843e-07 2.24659e-09 2.35085e-10 2.64988e-11 2.90933e-12
5.85009e-05 8.57948e-05 0.000151499 0.000284105 0.00054607 0.00100251 1.18494e-05 8.26706e-07 1.26394e-07 1.70309e-08 2.20525e-09
0.00202345 0.00258511 0.00381783 0.00599829 0.00972689 0.0154873 0.0011152 0.000247309 5.10506e-05 8.64727e-06 1.37747e-06
0.0205848 0.0217651 0.0238282 0.0262762 0.0285812 0.0303808 0.0374255 0.0204234 0.00674813 0.00160264 0.000338852
0.0199112 0.0210265 0.0229587 0.0252019 0.0272007 0.0285225 0.0310896 0.0156681 0.00495399 0.00114176 0.000235573
0.0184589 0.0194561 0.0211596 0.0230704 0.0246215 0.0253266 0.0228374 0.010038 0.00292986 0.000638799 0.000125942
0.0166888 0.0175517 0.0190015 0.0205611 0.0216793 0.0218694 0.0160278 0.00593307 0.00155164 0.000312583 5.76964e-05
0.0151262 0.0158758 0.0171155 0.0183949 0.019193 0.0190523 0.0115557 0.00356455 0.000817461 0.000148892 2.51893e-05
0.0142177 0.0149034 0.0160255 0.0171522 0.0177843 0.0174891 0.0093846 0.0025221 0.000515469 8.51039e-05 1.31328e-05
1 0 0 0 0 0 0 0 0 0 0 0
2 1.88664e-08 3.39962e-08 7.57021e-08 1.76412e-07 4.15752e-07 9.00973e-07 3.65403e-09 9.6579e-12 3.59442e-13 3.42591e-14 3.27595e-15
3 1.19102e-06 1.95195e-06 3.92165e-06 8.30575e-06 1.78976e-05 3.60742e-05 2.02843e-07 2.24659e-09 2.35085e-10 2.64988e-11 2.90933e-12
4 5.85009e-05 8.57948e-05 0.000151499 0.000284105 0.00054607 0.00100251 1.18494e-05 8.26706e-07 1.26394e-07 1.70309e-08 2.20525e-09
5 0.00202345 0.00258511 0.00381783 0.00599829 0.00972689 0.0154873 0.0011152 0.000247309 5.10506e-05 8.64727e-06 1.37747e-06
6 0.0205848 0.0217651 0.0238282 0.0262762 0.0285812 0.0303808 0.0374255 0.0204234 0.00674813 0.00160264 0.000338852
7 0.0199112 0.0210265 0.0229587 0.0252019 0.0272007 0.0285225 0.0310896 0.0156681 0.00495399 0.00114176 0.000235573
8 0.0184589 0.0194561 0.0211596 0.0230704 0.0246215 0.0253266 0.0228374 0.010038 0.00292986 0.000638799 0.000125942
9 0.0166888 0.0175517 0.0190015 0.0205611 0.0216793 0.0218694 0.0160278 0.00593307 0.00155164 0.000312583 5.76964e-05
10 0.0151262 0.0158758 0.0171155 0.0183949 0.019193 0.0190523 0.0115557 0.00356455 0.000817461 0.000148892 2.51893e-05
11 0.0142177 0.0149034 0.0160255 0.0171522 0.0177843 0.0174891 0.0093846 0.0025221 0.000515469 8.51039e-05 1.31328e-05

51
test/TestUtils.hpp Normal file
View File

@ -0,0 +1,51 @@
#include "tug/Core/Matrix.hpp"
#include <Eigen/Core>
#include <Eigen/Dense>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <gtest/gtest.h>
#define TUG_TEST(x) TEST(Tug, x)
inline tug::RowMajMat<double> CSV2Eigen(std::string file2Convert) {
std::vector<double> matrixEntries;
std::ifstream matrixDataFile(file2Convert);
if (matrixDataFile.fail()) {
throw std::invalid_argument("File probably non-existent!");
}
std::string matrixRowString;
std::string matrixEntry;
int matrixRowNumber = 0;
while (getline(matrixDataFile, matrixRowString)) {
std::stringstream matrixRowStringStream(matrixRowString);
while (getline(matrixRowStringStream, matrixEntry, ' ')) {
matrixEntries.push_back(stod(matrixEntry));
}
if (matrixRowString.length() > 1) {
matrixRowNumber++;
}
}
return tug::RowMajMatMap<double>(matrixEntries.data(), matrixRowNumber,
matrixEntries.size() / matrixRowNumber);
}
inline bool checkSimilarity(tug::RowMajMat<double> &a,
tug::RowMajMatMap<double> &b,
double precision = 1e-5) {
return a.isApprox(b, precision);
}
inline bool checkSimilarityV2(tug::RowMajMat<double> &a,
tug::RowMajMatMap<double> &b, double maxDiff) {
tug::RowMajMat<double> diff = a - b;
double maxCoeff = diff.maxCoeff();
return abs(maxCoeff) < maxDiff;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,7 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include <gtest/gtest.h>
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
GTEST_FLAG_SET(death_test_style, "threadsafe");
return RUN_ALL_TESTS();
}

102
test/testBoundary.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "gtest/gtest.h"
#include <stdexcept>
#include <tug/Boundary.hpp>
#include <utility>
#include <vector>
using namespace std;
using namespace tug;
#include <gtest/gtest.h>
#define BOUNDARY_TEST(x) TEST(Boundary, x)
BOUNDARY_TEST(Element) {
BoundaryElement boundaryElementClosed = BoundaryElement<double>();
EXPECT_NO_THROW(BoundaryElement<double>());
EXPECT_EQ(boundaryElementClosed.getType(), BC_TYPE_CLOSED);
EXPECT_DOUBLE_EQ(boundaryElementClosed.getValue(), -1);
EXPECT_THROW(boundaryElementClosed.setValue(0.2), std::invalid_argument);
BoundaryElement boundaryElementConstant = BoundaryElement(0.1);
EXPECT_NO_THROW(BoundaryElement(0.1));
EXPECT_EQ(boundaryElementConstant.getType(), BC_TYPE_CONSTANT);
EXPECT_DOUBLE_EQ(boundaryElementConstant.getValue(), 0.1);
EXPECT_NO_THROW(boundaryElementConstant.setValue(0.2));
EXPECT_DOUBLE_EQ(boundaryElementConstant.getValue(), 0.2);
}
BOUNDARY_TEST(Class) {
Boundary<double> boundary1D(10);
Boundary<double> boundary2D(10, 12);
vector<BoundaryElement<double>> boundary1DVector(1, BoundaryElement(1.0));
constexpr double inner_condition_value = -5;
constexpr std::pair<bool, double> innerBoundary =
std::make_pair(true, inner_condition_value);
std::vector<std::pair<bool, double>> row_ibc(12, std::make_pair(false, -1));
row_ibc[1] = innerBoundary;
std::vector<std::pair<bool, double>> col_ibc(10, std::make_pair(false, -1));
col_ibc[0] = innerBoundary;
{
EXPECT_EQ(boundary1D.getBoundarySide(BC_SIDE_LEFT).size(), 1);
EXPECT_EQ(boundary1D.getBoundarySide(BC_SIDE_RIGHT).size(), 1);
EXPECT_EQ(boundary1D.getBoundaryElementType(BC_SIDE_LEFT, 0),
BC_TYPE_CLOSED);
EXPECT_DEATH(boundary1D.getBoundarySide(BC_SIDE_TOP),
".*BC_SIDE_LEFT .* BC_SIDE_RIGHT.*");
EXPECT_DEATH(boundary1D.getBoundarySide(BC_SIDE_BOTTOM),
".*BC_SIDE_LEFT .* BC_SIDE_RIGHT.*");
EXPECT_NO_THROW(boundary1D.setBoundarySideClosed(BC_SIDE_LEFT));
EXPECT_DEATH(boundary1D.setBoundarySideClosed(BC_SIDE_TOP),
".*BC_SIDE_LEFT .* BC_SIDE_RIGHT.*");
EXPECT_NO_THROW(boundary1D.setBoundarySideConstant(BC_SIDE_LEFT, 1.0));
EXPECT_DOUBLE_EQ(boundary1D.getBoundaryElementValue(BC_SIDE_LEFT, 0), 1.0);
EXPECT_DEATH(boundary1D.getBoundaryElementValue(BC_SIDE_LEFT, 2),
".*Index is selected either too large or too small.*");
EXPECT_EQ(boundary1D.getBoundaryElementType(BC_SIDE_LEFT, 0),
BC_TYPE_CONSTANT);
EXPECT_EQ(boundary1D.getBoundaryElement(BC_SIDE_LEFT, 0).getType(),
boundary1DVector[0].getType());
EXPECT_NO_THROW(boundary1D.setInnerBoundary(0, inner_condition_value));
EXPECT_DEATH(boundary1D.setInnerBoundary(0, 0, inner_condition_value),
".*only available for 2D grids.*");
EXPECT_EQ(boundary1D.getInnerBoundary(0), innerBoundary);
EXPECT_FALSE(boundary1D.getInnerBoundary(1).first);
}
{
EXPECT_EQ(boundary2D.getBoundarySide(BC_SIDE_LEFT).size(), 10);
EXPECT_EQ(boundary2D.getBoundarySide(BC_SIDE_RIGHT).size(), 10);
EXPECT_EQ(boundary2D.getBoundarySide(BC_SIDE_TOP).size(), 12);
EXPECT_EQ(boundary2D.getBoundarySide(BC_SIDE_BOTTOM).size(), 12);
EXPECT_EQ(boundary2D.getBoundaryElementType(BC_SIDE_LEFT, 0),
BC_TYPE_CLOSED);
EXPECT_NO_THROW(boundary2D.getBoundarySide(BC_SIDE_TOP));
EXPECT_NO_THROW(boundary2D.getBoundarySide(BC_SIDE_BOTTOM));
EXPECT_NO_THROW(boundary2D.setBoundarySideClosed(BC_SIDE_LEFT));
EXPECT_NO_THROW(boundary2D.setBoundarySideClosed(BC_SIDE_TOP));
EXPECT_NO_THROW(boundary2D.setBoundarySideConstant(BC_SIDE_LEFT, 1.0));
EXPECT_DOUBLE_EQ(boundary2D.getBoundaryElementValue(BC_SIDE_LEFT, 0), 1.0);
EXPECT_DEATH(boundary2D.getBoundaryElementValue(BC_SIDE_LEFT, 12),
".*too large or too small.*");
EXPECT_EQ(boundary2D.getBoundaryElementType(BC_SIDE_LEFT, 0),
BC_TYPE_CONSTANT);
EXPECT_EQ(boundary2D.getBoundaryElement(BC_SIDE_LEFT, 0).getType(),
boundary1DVector[0].getType());
EXPECT_DEATH(boundary2D.setInnerBoundary(0, inner_condition_value),
".* 1D .*");
EXPECT_NO_THROW(boundary2D.setInnerBoundary(0, 1, inner_condition_value));
EXPECT_EQ(boundary2D.getInnerBoundary(0, 1), innerBoundary);
EXPECT_FALSE(boundary2D.getInnerBoundary(0, 2).first);
EXPECT_EQ(boundary2D.getInnerBoundaryRow(0), row_ibc);
EXPECT_EQ(boundary2D.getInnerBoundaryCol(1), col_ibc);
}
}

View File

@ -1,95 +0,0 @@
#include <diffusion/BTCSBoundaryCondition.hpp>
#include <doctest.h>
using namespace Diffusion;
#define BC_CONST_VALUE 1e-5
TEST_CASE("1D Boundary Condition") {
BTCSBoundaryCondition bc;
boundary_condition bc_set = {BC_TYPE_CONSTANT, BC_CONST_VALUE};
SUBCASE("valid get") { CHECK_EQ(bc(BC_SIDE_LEFT).value, 0); }
SUBCASE("invalid get") {
CHECK_THROWS(bc(BC_SIDE_TOP));
CHECK_THROWS(bc(BC_SIDE_LEFT, 1));
}
SUBCASE("valid set") {
CHECK_NOTHROW(bc(BC_SIDE_LEFT) = bc_set);
CHECK_EQ(bc(BC_SIDE_LEFT).value, bc_set.value);
CHECK_EQ(bc(BC_SIDE_LEFT).type, bc_set.type);
}
SUBCASE("invalid set") {
CHECK_THROWS(bc(BC_SIDE_TOP) = bc_set);
CHECK_THROWS(bc(BC_SIDE_LEFT, 0) = bc_set);
}
SUBCASE("valid row getter") {
bc(BC_SIDE_LEFT) = bc_set;
bc_tuple tup = bc.row(0);
CHECK_EQ(tup[0].value, BC_CONST_VALUE);
CHECK_EQ(tup[1].value, 0);
}
SUBCASE("invalid row and col getter") {
CHECK_THROWS(bc.row(1));
CHECK_THROWS(bc.col(0));
}
}
TEST_CASE("2D Boundary Condition") {
BTCSBoundaryCondition bc(5, 5);
boundary_condition bc_set = {BC_TYPE_CONSTANT, BC_CONST_VALUE};
SUBCASE("valid get") { CHECK_EQ(bc(BC_SIDE_LEFT, 0).value, 0); }
SUBCASE("invalid get") {
CHECK_THROWS(bc(4, 0));
CHECK_THROWS(bc(BC_SIDE_LEFT));
}
SUBCASE("valid set") {
CHECK_NOTHROW(bc(BC_SIDE_LEFT, 0) = bc_set);
CHECK_EQ(bc(BC_SIDE_LEFT, 0).value, bc_set.value);
CHECK_EQ(bc(BC_SIDE_LEFT, 0).type, bc_set.type);
}
SUBCASE("invalid set") {
CHECK_THROWS(bc(BC_SIDE_LEFT) = bc_set);
CHECK_THROWS(bc(4, 0) = bc_set);
}
SUBCASE("call of setSide") {
CHECK_NOTHROW(bc.setSide(BC_SIDE_BOTTOM, bc_set));
CHECK_EQ(bc(BC_SIDE_BOTTOM, 1).value, bc_set.value);
CHECK_EQ(bc(BC_SIDE_BOTTOM, 1).type, bc_set.type);
}
SUBCASE("get and set of side") {
std::vector<boundary_condition> bc_vec;
CHECK_NOTHROW(bc_vec = bc.getSide(BC_SIDE_BOTTOM));
bc_vec[3] = {BC_TYPE_CONSTANT, 1e-5};
CHECK_NOTHROW(bc.setSide(BC_SIDE_BOTTOM, bc_vec));
CHECK_EQ(bc(BC_SIDE_BOTTOM, 3).type, BC_TYPE_CONSTANT);
CHECK_EQ(bc(BC_SIDE_BOTTOM, 3).value, 1e-5);
CHECK_EQ(bc(BC_SIDE_BOTTOM, 2).value, 0);
}
}
TEST_CASE("Boundary Condition helpers") {
boundary_condition bc_set = {BC_TYPE_CONSTANT, BC_CONST_VALUE};
SUBCASE("return boundary condition skeleton") {
boundary_condition bc_test = BTCSBoundaryCondition::returnBoundaryCondition(
bc_set.type, bc_set.value);
CHECK_EQ(bc_test.value, bc_set.value);
CHECK_EQ(bc_test.type, bc_set.type);
}
}

View File

@ -1,125 +1,245 @@
#include <bits/stdint-uintn.h>
#include <diffusion/BTCSBoundaryCondition.hpp>
#include <diffusion/BTCSDiffusion.hpp>
#include <doctest.h>
#include <vector>
#include "TestUtils.hpp"
#include "tug/Core/Matrix.hpp"
#include "gtest/gtest.h"
#include <gtest/gtest.h>
#include <stdexcept>
#include <tug/Diffusion.hpp>
using namespace Diffusion;
#include <Eigen/src/Core/Matrix.h>
#include <string>
#define DIMENSION 2
#define N 51
#define M 51
#define MID 1300
// include the configured header file
#include <testSimulation.hpp>
static std::vector<double> alpha(N *M, 1e-3);
#define DIFFUSION_TEST(x) TEST(Diffusion, x)
static BTCSDiffusion setupDiffu(uint32_t n, uint32_t m) {
BTCSDiffusion diffu(DIMENSION);
using namespace Eigen;
using namespace std;
using namespace tug;
diffu.setXDimensions(n, n);
diffu.setYDimensions(m, m);
constexpr int row = 11;
constexpr int col = 11;
diffu.setTimestep(1.);
template <tug::APPROACH approach, tug::SOLVER solver>
Diffusion<double, approach, solver>
setupSimulation(RowMajMat<double> &concentrations, double timestep,
int iterations) {
int domain_row = 10;
int domain_col = 10;
return diffu;
}
// Grid
// RowMajMat<double> concentrations = MatrixXd::Constant(row, col, 0);
concentrations(5, 5) = 1;
TEST_CASE("closed boundaries - 1 concentration to 1 - rest 0") {
std::vector<double> field(N * M, 0);
Diffusion<double, approach, solver> diffusiongrid(concentrations);
field[MID] = 1;
diffusiongrid.getConcentrationMatrix() = concentrations;
diffusiongrid.setDomain(domain_row, domain_col);
BTCSDiffusion diffu = setupDiffu(N, M);
BTCSBoundaryCondition bc(N, M);
diffusiongrid.setTimestep(timestep);
diffusiongrid.setIterations(iterations);
diffusiongrid.setDomain(domain_row, domain_col);
uint32_t iterations = 1000;
double sum = 0;
for (int t = 0; t < iterations; t++) {
diffu.simulate(field.data(), alpha.data(), bc);
if (t == iterations - 1) {
// iterate through rows
for (int i = 0; i < M; i++) {
// iterate through columns
for (int j = 0; j < N; j++) {
sum += field[i * N + j];
}
}
MatrixXd alpha = MatrixXd::Constant(row, col, 1);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 6; j++) {
alpha(i, j) = 0.01;
}
}
CAPTURE(sum);
// epsilon of 1e-8
CHECK(sum == doctest::Approx(1).epsilon(1e-6));
}
TEST_CASE("constant boundaries (0) - 1 concentration to 1 - rest 0") {
std::vector<double> field(N * M, 0);
field[MID] = 1;
BTCSDiffusion diffu = setupDiffu(N, M);
BTCSBoundaryCondition bc(N, M);
boundary_condition input = {BC_TYPE_CONSTANT, 0};
bc.setSide(BC_SIDE_LEFT, input);
bc.setSide(BC_SIDE_RIGHT, input);
bc.setSide(BC_SIDE_TOP, input);
bc.setSide(BC_SIDE_BOTTOM, input);
uint32_t max_iterations = 20000;
bool reached = false;
int t = 0;
for (t = 0; t < max_iterations; t++) {
diffu.simulate(field.data(), alpha.data(), bc);
if (field[N * M - 1] > 1e-15) {
reached = true;
break;
for (int i = 0; i < 5; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.001;
}
}
if (!reached) {
CAPTURE(field[N * M - 1]);
FAIL_CHECK(
"Concentration did not reach boundaries after count of iterations: ",
t);
}
}
TEST_CASE(
"constant top and bottom (1 and 0) - left and right closed - 0 inlet") {
std::vector<double> field(N * M, 0);
BTCSDiffusion diffu = setupDiffu(N, M);
BTCSBoundaryCondition bc(N, M);
boundary_condition top =
BTCSBoundaryCondition::returnBoundaryCondition(BC_TYPE_CONSTANT, 1);
boundary_condition bottom =
BTCSBoundaryCondition::returnBoundaryCondition(BC_TYPE_CONSTANT, 0);
bc.setSide(BC_SIDE_TOP, top);
bc.setSide(BC_SIDE_BOTTOM, bottom);
uint32_t max_iterations = 100;
for (int t = 0; t < max_iterations; t++) {
diffu.simulate(field.data(), alpha.data(), bc);
}
for (int i = 0; i < N; i++) {
double above = field[i];
for (int j = 1; j < M; j++) {
double curr = field[j * N + i];
if (curr > above) {
CAPTURE(curr);
CAPTURE(above);
FAIL("Concentration below is greater than above @ cell ", j * N + i);
}
for (int i = 5; i < 11; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.1;
}
}
diffusiongrid.setAlphaX(alpha);
diffusiongrid.setAlphaY(alpha);
return diffusiongrid;
}
constexpr double timestep = 0.001;
constexpr double iterations = 7000;
DIFFUSION_TEST(EqualityFTCS) {
// set string from the header file
string test_path = testSimulationCSVDir;
RowMajMat<double> reference = CSV2Eigen(test_path);
cout << "FTCS Test: " << endl;
RowMajMat<double> concentrations = MatrixXd::Constant(row, col, 0);
Diffusion<double, tug::FTCS_APPROACH, tug::THOMAS_ALGORITHM_SOLVER> sim =
setupSimulation<tug::FTCS_APPROACH, tug::THOMAS_ALGORITHM_SOLVER>(
concentrations, timestep, iterations);
// Boundary bc = Boundary(grid);
// Simulation
// Diffusion<double, tug::FTCS_APPROACH> sim(grid, bc);
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
// sim.setTimestep(timestep);
// sim.setIterations(iterations);
sim.run();
cout << endl;
EXPECT_TRUE(checkSimilarity(reference, sim.getConcentrationMatrix(), 0.1));
}
DIFFUSION_TEST(EqualityBTCS) {
// set string from the header file
string test_path = testSimulationCSVDir;
RowMajMat<double> reference = CSV2Eigen(test_path);
cout << "BTCS Test: " << endl;
RowMajMat<double> concentrations = MatrixXd::Constant(row, col, 0);
Diffusion<double, tug::BTCS_APPROACH, tug::THOMAS_ALGORITHM_SOLVER> sim =
setupSimulation<tug::BTCS_APPROACH, tug::THOMAS_ALGORITHM_SOLVER>(
concentrations, timestep,
iterations); // Boundary
// Boundary bc = Boundary(grid);
// Simulation
// Diffusion<double, tug::FTCS_APPROACH> sim(grid, bc);
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
// sim.setTimestep(timestep);
// sim.setIterations(iterations);
sim.run();
cout << endl;
EXPECT_TRUE(checkSimilarityV2(reference, sim.getConcentrationMatrix(), 0.01));
}
DIFFUSION_TEST(EqualityEigenLU) {
// set string from the header file
string test_path = testSimulationCSVDir;
RowMajMat<double> reference = CSV2Eigen(test_path);
cout << "BTCS Test: " << endl;
RowMajMat<double> concentrations = MatrixXd::Constant(row, col, 0);
Diffusion<double, tug::BTCS_APPROACH, tug::EIGEN_LU_SOLVER> sim =
setupSimulation<tug::BTCS_APPROACH, tug::EIGEN_LU_SOLVER>(
concentrations, timestep,
iterations); // Boundary
// Boundary bc = Boundary(grid);
// Simulation
// Diffusion<double, tug::FTCS_APPROACH> sim(grid, bc);
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
// sim.setTimestep(timestep);
// sim.setIterations(iterations);
sim.run();
cout << endl;
EXPECT_TRUE(checkSimilarityV2(reference, sim.getConcentrationMatrix(), 0.01));
}
DIFFUSION_TEST(InitializeEnvironment) {
int rc = 12;
RowMajMat<double> concentrations(rc, rc);
// Grid64 grid(concentrations);
// Boundary boundary(grid);
EXPECT_NO_FATAL_FAILURE(Diffusion<double> sim(concentrations));
}
// DIFFUSION_TEST(SimulationEnvironment) {
// int rc = 12;
// Eigen::MatrixXd concentrations(rc, rc);
// Grid64 grid(concentrations);
// grid.initAlpha();
// Boundary boundary(grid);
// Diffusion<double, tug::FTCS_APPROACH> sim(grid, boundary);
// EXPECT_EQ(sim.getIterations(), 1);
// EXPECT_NO_THROW(sim.setIterations(2000));
// EXPECT_EQ(sim.getIterations(), 2000);
// EXPECT_THROW(sim.setIterations(-300), std::invalid_argument);
// EXPECT_NO_THROW(sim.setTimestep(0.1));
// EXPECT_DOUBLE_EQ(sim.getTimestep(), 0.1);
// EXPECT_DEATH(sim.setTimestep(-0.3), ".* greater than zero.*");
// }
DIFFUSION_TEST(ClosedBoundaries) {
constexpr std::uint32_t nrows = 5;
constexpr std::uint32_t ncols = 5;
RowMajMat<double> concentrations =
RowMajMat<double>::Constant(nrows, ncols, 1.0);
RowMajMat<double> alphax = RowMajMat<double>::Constant(nrows, ncols, 1E-5);
RowMajMat<double> alphay = RowMajMat<double>::Constant(nrows, ncols, 1E-5);
Diffusion<double> sim(concentrations);
sim.getAlphaX() = alphax;
sim.getAlphaY() = alphay;
// tug::Grid64 grid(concentrations);
// grid.setAlpha(alphax, alphay);
// tug::Boundary bc(grid);
auto &bc = sim.getBoundaryConditions();
bc.setBoundarySideConstant(tug::BC_SIDE_LEFT, 1.0);
bc.setBoundarySideConstant(tug::BC_SIDE_RIGHT, 1.0);
bc.setBoundarySideConstant(tug::BC_SIDE_TOP, 1.0);
bc.setBoundarySideConstant(tug::BC_SIDE_BOTTOM, 1.0);
// tug::Diffusion<double> sim(grid, bc);
sim.setTimestep(1);
sim.setIterations(1);
RowMajMat<double> input_values(concentrations);
sim.run();
EXPECT_TRUE(
checkSimilarityV2(input_values, sim.getConcentrationMatrix(), 1E-12));
}
DIFFUSION_TEST(ConstantInnerCell) {
constexpr std::uint32_t nrows = 5;
constexpr std::uint32_t ncols = 5;
RowMajMat<double> concentrations =
RowMajMat<double>::Constant(nrows, ncols, 1.0);
RowMajMat<double> alphax = RowMajMat<double>::Constant(nrows, ncols, 1E-5);
RowMajMat<double> alphay = RowMajMat<double>::Constant(nrows, ncols, 1E-5);
Diffusion<double> sim(concentrations);
sim.getAlphaX() = alphax;
sim.getAlphaY() = alphay;
// tug::Grid64 grid(concentrations);
// grid.setAlpha(alphax, alphay);
// tug::Boundary bc(grid);
auto &bc = sim.getBoundaryConditions();
// inner
bc.setInnerBoundary(2, 2, 0);
// tug::Diffusion<double> sim(grid, bc);
sim.setTimestep(1);
sim.setIterations(1);
MatrixXd input_values(concentrations);
sim.run();
const auto &concentrations_result = sim.getConcentrationMatrix();
EXPECT_DOUBLE_EQ(concentrations_result(2, 2), 0);
EXPECT_LT(concentrations_result.sum(), input_values.sum());
EXPECT_FALSE((concentrations_result.array() > 1.0).any());
EXPECT_FALSE((concentrations_result.array() < 0.0).any());
}

18
test/testFTCS.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <gtest/gtest.h>
#include <tug/Core/TugUtils.hpp>
#include <gtest/gtest.h>
TEST(FTCS, calcAlphaIntercell) {
double alpha1 = 10;
double alpha2 = 20;
double average = 15;
double harmonicMean =
double(2) / ((double(1) / alpha1) + (double(1) / alpha2));
// double difference = std::fabs(calcAlphaIntercell(alpha1, alpha2) -
// harmonicMean); CHECK(difference <
// std::numeric_limits<double>::epsilon());
EXPECT_DOUBLE_EQ(calcAlphaIntercell(alpha1, alpha2), harmonicMean);
EXPECT_DOUBLE_EQ(calcAlphaIntercell(alpha1, alpha2, false), average);
}

View File

@ -0,0 +1,7 @@
#ifndef TESTSIMULATION_H_
#define TESTSIMULATION_H_
// CSV file needed for validation
const char *testSimulationCSVDir = "@testSimulationCSV@";
#endif // TESTSIMULATION_H_

Some files were not shown because too many files have changed in this diff Show More