Compare commits
377 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c4aeee410 | ||
|
|
d5bfdf9724 | ||
|
|
1ad7ea0237 | ||
|
|
1a51dd5a1e | ||
|
|
605a31cc7c | ||
|
|
a562281187 | ||
|
|
256d6325d6 | ||
|
|
3e97e530bc | ||
|
|
04b42c4b89 | ||
|
|
d2f028122e | ||
|
|
19cc372b6a | ||
|
|
135830e030 | ||
|
|
520810bc3e | ||
|
|
b66feed2d2 | ||
|
|
b6ce5a32f4 | ||
|
|
4866977e72 | ||
|
|
bdc4f156de | ||
|
|
5b144aea3a | ||
|
|
3e270ee01c | ||
|
|
a20d5d53e6 | ||
|
|
3701dea34e | ||
|
|
30d2099d8a | ||
|
|
47ad909a9c | ||
|
|
91ae02cbf1 | ||
|
|
06b890fe81 | ||
|
|
c8d1b08e28 | ||
|
|
9ca0735654 | ||
| e1a135f8e2 | |||
|
|
13226e8668 | ||
|
|
56fc8185d5 | ||
|
|
a312abfe05 | ||
|
|
8fcc77bc60 | ||
|
|
3612dcf034 | ||
|
|
5c68f8b6b2 | ||
|
|
477d943bf0 | ||
|
|
d3843fb2a3 | ||
|
|
13f6556f54 | ||
|
|
a796acbc1d | ||
|
|
432621f227 | ||
|
|
636fcfaec3 | ||
|
|
bed888d1fc | ||
|
|
6981373deb | ||
|
|
a986242852 | ||
|
|
8d83eeef29 | ||
|
|
ac693caea9 | ||
|
|
74b46f111b | ||
|
|
c01d8e8607 | ||
|
|
00b0583504 | ||
|
|
f7dbf3abaf | ||
|
|
e64e8dfd5e | ||
|
|
449647010a | ||
|
|
5193f36e1f | ||
|
|
9d2c4b1485 | ||
|
|
f86836f637 | ||
|
|
b13337279f | ||
|
|
bd3cdde0fb | ||
|
|
7ae35dccf9 | ||
|
|
cb0c21eab9 | ||
|
|
2dc959b993 | ||
|
|
332f419faf | ||
|
|
b104fdcf52 | ||
|
|
f71bf2371f | ||
|
|
3ffa0ef624 | ||
|
|
8c0687a6cd | ||
|
|
1679dc84d2 | ||
|
|
eb14c53314 | ||
|
|
4328ef3436 | ||
|
|
4867261f9d | ||
|
|
2f737ce09e | ||
|
|
48000710c7 | ||
|
|
69690c0afa | ||
|
|
fbb0c08024 | ||
|
|
61d6cfc5cb | ||
|
|
f651c1d158 | ||
|
|
db1a2b2e5c | ||
|
|
8824dc1fd9 | ||
|
|
cdfc42ac9c | ||
|
|
e2bf3a7662 | ||
|
|
ee77b5f7f3 | ||
|
|
141028548b | ||
|
|
32d6a4e3ec | ||
|
|
97b43e1a16 | ||
|
|
39541a2054 | ||
|
|
5a39f5377e | ||
|
|
77914ea69f | ||
|
|
8456f2212d | ||
|
|
0471f3d8f9 | ||
|
|
8cfb61587d | ||
|
|
9a3fc67885 | ||
|
|
5196c36ec5 | ||
|
|
ba627b6624 | ||
|
|
46f9cef3a9 | ||
|
|
00cafb70dc | ||
|
|
5099fd23a9 | ||
|
|
8e5c1ad035 | ||
|
|
819db24e18 | ||
|
|
6c1ccb90fd | ||
|
|
ef1ccd4c14 | ||
|
|
a0d835e243 | ||
|
|
0eba63f875 | ||
|
|
edaad7cc04 | ||
|
|
f0d5220a48 | ||
|
|
a21023ec9d | ||
|
|
587bb5a622 | ||
|
|
c5979cd6f4 | ||
|
|
4b4c439c68 | ||
|
|
da6d004e16 | ||
|
|
61a4b0ae8a | ||
|
|
3106c2b8d5 | ||
|
|
8af03777b8 | ||
|
|
2096ee5cc3 | ||
|
|
2483019b89 | ||
|
|
ce09f0d8c8 | ||
|
|
b9c4474f5a | ||
|
|
d7608a7330 | ||
|
|
0d34752837 | ||
|
|
81774e72c1 | ||
|
|
443f8b6cd1 | ||
|
|
684fcd217f | ||
|
|
9d663d8140 | ||
|
|
eb42377f30 | ||
|
|
fc4689461e | ||
|
|
d2e3ef23de | ||
|
|
5490216bc0 | ||
|
|
42ad07c252 | ||
|
|
bf4444fc84 | ||
|
|
40710a0b39 | ||
|
|
f8bdfe39ea | ||
|
|
72107c944d | ||
|
|
fab0f35ed0 | ||
|
|
7d05320f24 | ||
|
|
6e388f3d99 | ||
|
|
55509c1934 | ||
|
|
e8a783f00c | ||
|
|
8fcc8812e7 | ||
|
|
4cb51f4241 | ||
|
|
d24b65db63 | ||
|
|
a0ab764870 | ||
|
|
12bc93a3ed | ||
|
|
dada94b1ae | ||
|
|
6a3c2b014c | ||
|
|
cc37891bf6 | ||
|
|
2d50f32e37 | ||
|
|
6363585a00 | ||
|
|
a16b67b15e | ||
|
|
eb2a9774a5 | ||
|
|
ad67980baa | ||
|
|
2294922a3e | ||
|
|
49209412e2 | ||
|
|
d92ccc05aa | ||
|
|
3db0e73efe | ||
|
|
3344835b20 | ||
|
|
1dbee6d8d9 | ||
|
|
6b8368d9f7 | ||
|
|
32b05a8a87 | ||
|
|
27829a1463 | ||
|
|
9d695b3aa8 | ||
|
|
af056b4d82 | ||
|
|
c0e46bd82f | ||
|
|
6814b8f434 | ||
|
|
996149785d | ||
|
|
7266588573 | ||
|
|
fc1a94bdf2 | ||
|
|
feef0057e1 | ||
|
|
194a53c001 | ||
|
|
0b5548e8c7 | ||
|
|
567318b8e9 | ||
|
|
ae7cefc657 | ||
|
|
ef466594d8 | ||
|
|
40fb615d68 | ||
|
|
40d03a62f5 | ||
|
|
81a57c42b4 | ||
|
|
da7823f7fc | ||
|
|
3551e9274f | ||
|
|
dde7fb4783 | ||
|
|
06f5eb5f2a | ||
|
|
fb397897c8 | ||
|
|
d563ff583a | ||
|
|
525e85cfd7 | ||
|
|
65493cd207 | ||
|
|
8cce22601f | ||
|
|
9acbe90343 | ||
|
|
5ae5aea48f | ||
|
|
c9c0f02a5a | ||
|
|
f1b5138bcc | ||
|
|
4108038a62 | ||
|
|
c56c5c8ec2 | ||
|
|
fdb5c436ea | ||
|
|
435314ba61 | ||
|
|
94ef22902e | ||
|
|
2810db0d52 | ||
|
|
0fc6bf4a79 | ||
|
|
cc46a893a4 | ||
|
|
e8d322fb75 | ||
|
|
2a97bb65c6 | ||
|
|
e6035c47e7 | ||
|
|
a510ad1b21 | ||
|
|
3cce7fb357 | ||
|
|
18abb0fd0e | ||
|
|
a7bb5b4d41 | ||
|
|
916987c1bd | ||
|
|
04a2280311 | ||
|
|
ab08d5af1a | ||
|
|
498f813d2d | ||
|
|
20067a6898 | ||
|
|
5b01149642 | ||
|
|
9d1d8a3980 | ||
|
|
e1b7038490 | ||
|
|
e5ca6144d8 | ||
|
|
25f8c3fe6e | ||
|
|
ff611d7a97 | ||
|
|
ea7c9f0df3 | ||
|
|
28d2316f7d | ||
|
|
0b19b7c197 | ||
|
|
30bc676604 | ||
|
|
aa4ce6a086 | ||
|
|
eb339667c6 | ||
|
|
69f1483006 | ||
|
|
154091e405 | ||
|
|
da39b9064a | ||
|
|
2c2851a037 | ||
|
|
8596f3ffda | ||
|
|
4e043e712e | ||
|
|
ab22436283 | ||
|
|
ad2fdabac9 | ||
|
|
fbdeba00a2 | ||
|
|
d38e14d6f4 | ||
|
|
604d511f9a | ||
|
|
70268f58f3 | ||
|
|
ba7a38f72f | ||
|
|
bc9e92e3ef | ||
|
|
63b4e49f99 | ||
|
|
fba3c93e7d | ||
|
|
95a9e694b0 | ||
|
|
05275a9e56 | ||
|
|
37b6a52d7f | ||
|
|
d22f8cf71b | ||
|
|
7839a412e6 | ||
|
|
b9393a4524 | ||
|
|
85c5e55601 | ||
|
|
78cf41f57e | ||
|
|
e19171feaa | ||
|
|
7bb50a575f | ||
|
|
8215a1238d | ||
|
|
33fd35a65a | ||
|
|
695b80beaf | ||
|
|
f4924ac8b2 | ||
|
|
9e200305ff | ||
|
|
a72217f6a2 | ||
|
|
f5a59def6d | ||
|
|
788e59c4ef | ||
|
|
f8cb62fa94 | ||
|
|
2d293469ff | ||
|
|
ffa48347b8 | ||
|
|
0872639c54 | ||
|
|
9703dba718 | ||
|
|
0a0e16bb56 | ||
|
|
4680e9823f | ||
|
|
4e8933862e | ||
|
|
16d3663909 | ||
|
|
605000de5e | ||
|
|
c9b714b241 | ||
|
|
3364042af7 | ||
|
|
10bb5f5012 | ||
|
|
c5862d4c18 | ||
|
|
88b00931f8 | ||
|
|
8f48830bda | ||
|
|
049fc319db | ||
|
|
7cf4ad4772 | ||
|
|
60d01a83c4 | ||
|
|
23da250cba | ||
|
|
22d7ce45f7 | ||
|
|
69ca773afa | ||
|
|
5c39743c6f | ||
|
|
58f620e6b9 | ||
|
|
c3461a46fa | ||
|
|
be94e760af | ||
|
|
0ebc8d30e8 | ||
|
|
c8163bb596 | ||
|
|
c7efae395f | ||
|
|
ef01e3f473 | ||
|
|
b7561b93e0 | ||
|
|
d88d7956a5 | ||
|
|
67f289c1f8 | ||
|
|
17ed5cf75f | ||
|
|
54b9a31f16 | ||
|
|
7e4dc1e383 | ||
|
|
5bee2d20e9 | ||
|
|
3d80b7e02a | ||
|
|
d457c2b9a7 | ||
|
|
99925dbd4f | ||
|
|
542601fdcd | ||
|
|
0a9b58e8ff | ||
|
|
b93dc46aed | ||
|
|
da65be3cca | ||
|
|
01a589889f | ||
|
|
2ab924a162 | ||
|
|
470ebbd2ab | ||
|
|
a6a704a176 | ||
|
|
16640fe122 | ||
|
|
fc999f09b3 | ||
|
|
65f569380a | ||
|
|
40573b81b2 | ||
|
|
eb9f8503d6 | ||
|
|
efcb2d1494 | ||
|
|
25c52e6b29 | ||
|
|
5688c82da2 | ||
|
|
214cb717d3 | ||
|
|
e57c5462eb | ||
|
|
916f455174 | ||
|
|
45827c0402 | ||
|
|
5e70b319c9 | ||
|
|
8c37a6998d | ||
|
|
25985f096c | ||
|
|
be68985600 | ||
|
|
72509832d4 | ||
|
|
ecd3e95477 | ||
|
|
1df7f82553 | ||
|
|
c798c61706 | ||
|
|
2d9d318981 | ||
|
|
165e72e978 | ||
|
|
4e1ed11b6f | ||
|
|
25855da6b2 | ||
|
|
459922629d | ||
|
|
06d5d737b0 | ||
|
|
ebca11f4fd | ||
|
|
f24ce43f5a | ||
|
|
adb2e325be | ||
|
|
79957da687 | ||
|
|
cea941853b | ||
|
|
79d7a32fc2 | ||
|
|
d4e3ab8544 | ||
|
|
e4ec0a11da | ||
|
|
9815ebce9c | ||
|
|
7bd40639d5 | ||
|
|
e853a96839 | ||
|
|
0abd4e04ae | ||
|
|
0be1586d69 | ||
|
|
6120acdaf4 | ||
|
|
590884dabb | ||
|
|
592f59dbc5 | ||
|
|
94e83b5eb8 | ||
|
|
02a9531544 | ||
|
|
e482d71779 | ||
|
|
97889cde5e | ||
|
|
32f3658861 | ||
|
|
b5db1056c4 | ||
|
|
443ea15c58 | ||
|
|
822f50d887 | ||
|
|
9abd35879c | ||
|
|
c96655241f | ||
|
|
40f37bf104 | ||
|
|
baaa85182f | ||
|
|
c99f770182 | ||
|
|
1822bcd98d | ||
|
|
bdd56bec82 | ||
|
|
46185571c0 | ||
|
|
832b61bd19 | ||
|
|
b7c9dbb535 | ||
|
|
67ca71404c | ||
|
|
9461ebd8f3 | ||
|
|
f9280b1274 | ||
|
|
37c2dd70ec | ||
|
|
be50ae4777 | ||
|
|
b6eb212bcb | ||
|
|
fd7e2f5b63 | ||
|
|
85c9fc5be2 | ||
|
|
19fc29dc52 | ||
|
|
2e2ba6cd82 | ||
|
|
efed757a9e | ||
|
|
c4334164a3 | ||
|
|
652bcc71a9 | ||
|
|
1c8b3b7c95 | ||
|
|
38700f2968 | ||
|
|
c1cb489a69 | ||
|
|
02e65afb60 | ||
|
|
e9f5b34968 |
30
.chglog/CHANGELOG.tpl.md
Executable 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
@ -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
@ -8,3 +8,6 @@ compile_commands.json
|
||||
.cache/
|
||||
.ccls-cache/
|
||||
/iwyu/
|
||||
.Rhistory
|
||||
.vscode/
|
||||
.codechecker/
|
||||
@ -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
@ -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
|
||||
@ -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
@ -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
@ -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
@ -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
@ -0,0 +1,103 @@
|
||||

|
||||
|
||||
`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!
|
||||
87
README.org
@ -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?
|
||||
@ -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)
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
@ -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
115
doc/ValidationHetDiff.org
Normal 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
|
||||
|
||||
BIN
doc/grid.png
|
Before Width: | Height: | Size: 151 KiB |
BIN
doc/images/deSolve_AlphaHet1.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
2629
doc/images/tug_logo.svg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
doc/images/tug_logo_crop.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
doc/images/tug_logo_small.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
8
docs_sphinx/Boundary.rst
Normal 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
@ -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
20
docs_sphinx/Makefile
Normal 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
@ -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')
|
||||
1
docs_sphinx/contributors.rst
Normal file
@ -0,0 +1 @@
|
||||
.. mdinclude:: ../CONTRIBUTORS.md
|
||||
38
docs_sphinx/developer.rst
Normal 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
@ -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
|
||||
-------------------------------------------------------
|
||||
4
docs_sphinx/images/activity_diagram_BTCS.svg
Normal file
|
After Width: | Height: | Size: 35 KiB |
4
docs_sphinx/images/activity_diagram_FTCS.svg
Normal file
|
After Width: | Height: | Size: 19 KiB |
4
docs_sphinx/images/activity_diagram_run.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
1413
docs_sphinx/images/adi_scheme.svg
Normal file
|
After Width: | Height: | Size: 126 KiB |
4
docs_sphinx/images/class_diagram.svg
Normal file
|
After Width: | Height: | Size: 41 KiB |
367
docs_sphinx/images/conservation_law.svg
Normal file
|
After Width: | Height: | Size: 47 KiB |
563
docs_sphinx/images/discretization_1D.svg
Normal 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 |
607
docs_sphinx/images/explicit_scheme.svg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
docs_sphinx/images/grid_with_boundaries_example.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
607
docs_sphinx/images/implicit_scheme.svg
Normal file
|
After Width: | Height: | Size: 173 KiB |
475
docs_sphinx/images/numeric_overview.svg
Normal file
|
After Width: | Height: | Size: 108 KiB |
2629
docs_sphinx/images/tug_logo.svg
Normal file
|
After Width: | Height: | Size: 138 KiB |
52
docs_sphinx/index.rst
Normal 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
|
||||
3
docs_sphinx/installation.rst
Normal file
@ -0,0 +1,3 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
2
docs_sphinx/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
breathe
|
||||
sphinx-rtd-theme
|
||||
698
docs_sphinx/theory.rst
Normal 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, let’s
|
||||
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. Fick’s 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. Taylor’s 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. Let’s 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): 297–99. 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
|
After Width: | Height: | Size: 138 KiB |
7
docs_sphinx/user.rst
Normal file
@ -0,0 +1,7 @@
|
||||
User API
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
|
||||
Boundary
|
||||
Diffusion
|
||||
3
docs_sphinx/visualization.rst
Normal file
@ -0,0 +1,3 @@
|
||||
Visualization
|
||||
=============
|
||||
|
||||
84
examples/BTCS_2D_proto_example.cpp
Normal 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
@ -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)
|
||||
90
examples/FTCS_2D_proto_closed_mdl.cpp
Normal 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;
|
||||
}
|
||||
81
examples/FTCS_2D_proto_example_mdl.cpp
Normal 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;
|
||||
}
|
||||
@ -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_
|
||||
@ -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_
|
||||
@ -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
@ -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_
|
||||
388
include/tug/Core/BaseSimulation.hpp
Normal 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
|
||||
21
include/tug/Core/Matrix.hpp
Normal 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
|
||||
484
include/tug/Core/Numeric/BTCS.hpp
Normal 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_
|
||||
240
include/tug/Core/Numeric/FTCS.hpp
Normal 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 ×tep = 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 ×tep = 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_
|
||||
21
include/tug/Core/Numeric/SimulationInput.hpp
Normal 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
|
||||
39
include/tug/Core/TugUtils.hpp
Normal 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
@ -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
@ -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
@ -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)
|
||||
191
naaice/NAAICE_dble_vs_float.cpp
Normal 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
@ -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
@ -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
|
||||
|
7
naaice/files.hpp.in
Normal 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
@ -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,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
@ -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
@ -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
|
||||
|
600
scripts/hannes-philip/BTCS.ipynb
Normal file
497
scripts/hannes-philip/FTCS.ipynb
Normal 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]};
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
@ -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
@ -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
|
||||
|
||||
|
||||
|
51
test/TestUtils.hpp
Normal 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;
|
||||
}
|
||||
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
@ -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);
|
||||
}
|
||||
7
test/testSimulation.hpp.in
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef TESTSIMULATION_H_
|
||||
#define TESTSIMULATION_H_
|
||||
|
||||
// CSV file needed for validation
|
||||
const char *testSimulationCSVDir = "@testSimulationCSV@";
|
||||
|
||||
#endif // TESTSIMULATION_H_
|
||||