Compare commits

...

127 Commits

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

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

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

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

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

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

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

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

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

All simulation steps are done in place.

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

Closes #14

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

BREAKING CHANGE: added FTCS scheme

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

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ compile_commands.json
/iwyu/
.Rhistory
.vscode/
.codechecker/

View File

@ -1,34 +1,33 @@
image: git.gfz-potsdam.de:5000/naaice/tug: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
- doc
build_release:
stage: build
artifacts:
paths:
- build/test/testTug
expire_in: 100s
script:
- mkdir build && cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DTUG_ENABLE_TESTING=ON ..
- make -j$(nproc)
- cp ../test/FTCS_11_11_7000.csv test/
test:
stage: test
script:
- ./build/test/testTug
- 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
pages:
stage: doc
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
- pip install --upgrade pip && pip install Sphinx Pillow breathe sphinx-rtd-theme sphinx-mdinclude
- mkdir public
script:
- pushd docs_sphinx
@ -40,12 +39,28 @@ pages:
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:
stage: static_analyze
before_script:
- apk add clang-extra-tools
- 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-*, modernize-*" -DTUG_ENABLE_TESTING=OFF ..
- cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-checks=cppcoreguidelines-*,clang-analyzer-*,performance-*" -DTUG_ENABLE_TESTING=OFF ..
- make tug
when: manual

43
CITATION.cff Normal file
View File

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

View File

@ -1,44 +1,86 @@
#debian stable (currently bullseye)
cmake_minimum_required(VERSION 3.18)
# debian stable (currently bullseye)
cmake_minimum_required(VERSION 3.20)
project(tug CXX)
set(CMAKE_CXX_STANDARD 17)
project(
tug
VERSION 0.4
LANGUAGES CXX)
find_package(Eigen3 REQUIRED NO_MODULE)
find_package(OpenMP)
# find_package(easy_profiler)
# option(EASY_OPTION_LOG "Verbose easy_profiler" 1)
## SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -mfma")
option(TUG_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(TUG_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(TUG_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(TUG_ENABLE_TESTING
"Run tests after succesfull compilation"
ON)
if(TUG_USE_UNSAFE_MATH_OPT)
target_compile_options(tug INTERFACE -ffast-math)
endif()
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})
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()
add_subdirectory(examples)
if(TUG_HANNESPHILIPP_EXAMPLES)
add_subdirectory(examples)
endif()
if(TUG_NAAICE_EXAMPLE)
add_subdirectory(naaice)
endif()

11
CONTRIBUTORS.md Normal file
View File

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

339
LICENSE Normal file
View File

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

103
README.md Normal file
View File

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

View File

@ -1,126 +0,0 @@
#+TITLE: TUG: a C++ framework to solve Transport on Uniform Grids
[[./doc/images/tug_logo_small.png]]
=tug= implements different numerical approaches for transport
problems, notably diffusion with implicit BTCS (Backward Time, Central
Space) Euler and parallel 2D ADI (Alternating Direction Implicit).
* About
This project aims to provide a library for solving transport
problems - diffusion, advection - on uniform grids implemented in C++.
The library is built on top of [[https://eigen.tuxfamily.org/index.php?title=Main_Page][Eigen]], 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 [[https://www.openmp.org/][OpenMP]], 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.
* Getting started
=tug= is designed as a framework library and it relies on [[https://cmake.org/][CMake]] 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.
#+BEGIN_SRC
$ mkdir sample_project && cd sample_project
#+END_SRC
2. Clone this repository into path of choice project directory
- with =ssh=:
#+BEGIN_SRC
$ git clone git@git.gfz-potsdam.de:sec34/tug.git
#+END_SRC
- with =https=:
#+BEGIN_SRC
$ git clone https://git.gfz-potsdam.de/sec34/tug.git
#+END_SRC
3. Add the following line into =CMakeLists.txt= file:
#+BEGIN_SRC
add_subdirectory(path_to_tug EXCLUDE_FROM_ALL)
#+END_SRC
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:
#+BEGIN_SRC
target_link_libraries(your_libapp tug)
#+END_SRC
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.
* Roadmap
- [X] 1D diffusion using BTCS
- [X] 2D diffusion with ADI
- [ ] 3D diffusion (?)
- [X] R-API (see [[https://git.gfz-potsdam.de/sec34/rcppbtcs][RcppBTCS]])
- [-] Python-API (?)
- [X] Testing
* Contributing
** *PLEASE NOTE*
Starting from version v0.2 we would like to use more meaningful commit
messages. An overview of good practices and conventions can be found
[[https://www.conventionalcommits.org/en/v1.0.0/][here]].
** Workflow
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:
3. Download this repository and note down the SHA of the downloaded
commit. Apply your changes and send a mail to
[[mailto:mluebke@gfz-potsdam.de][mluebke@gfz-potsdam.de]] or [[mailto:delucia@gfz-potsdam.de][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!
* License
TODO?

@ -1 +0,0 @@
Subproject commit b7c21ec5ceeadb4951b00396fc1e4642dd347e5f

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

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

View File

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

10
docs_sphinx/Diffusion.rst Normal file
View File

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

View File

@ -1,4 +0,0 @@
Grid
====
.. doxygenclass:: Grid

View File

@ -1,4 +0,0 @@
Simulation
==========
.. doxygenclass:: Simulation

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,224 +0,0 @@
<!DOCTYPE html>
<div id="container"></div>
<form>
<label for="c_file">Select an output file: </label>
<input type="file" id="c_file" name="c_file" accept="text/csv" />
</form>
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
// Declare the chart dimensions and margins.
const width = 600;
const height = 600;
const svgMargin = { top: 50, right: 50, bottom: 50, left: 50 };
var gridWidth = width - svgMargin.left - svgMargin.right;
var gridHeight = height - svgMargin.top - svgMargin.bottom;
const gap = 1;
var state = 0;
var numIterations = 0;
var cellXcount = 0;
var cellYcount = 0;
var concentrations = [];
// Create the outer SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill-opacity", 0)
.attr("stroke-width", 4)
.attr("stroke", "black");
var grid = svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${svgMargin.left}, ${svgMargin.top})`);
// color scale
var colorScale = d3.scaleLinear()
.range(["#d0d0ec", "#7f0000"])
.domain([1, 2000]);
function getColor(c, max) {
if (c == -1) {
return "#000000";
}
colorScale.domain([0, max]);
return colorScale(c);
}
function calcMaxConcentrationInIteration(state) {
var maxRow = concentrations[state].map(function(row){ return Math.max.apply(Math, row); });
var maxC = Math.max.apply(null, maxRow);
return maxC;
}
// Load data from file
async function createVisualFromFile(csv_input) {
// console.log(csv_input);
// var data = await d3.text(csv_input).then(function(csv){
// return d3.dsvFormat(" ").parseRows(csv);
// })
var data = d3.dsvFormat(" ").parseRows(csv_input);
// console.log(data);
var leftBoundary = data[0];
var rightBoundary = data[1];
var topBoundary = data[2];
var bottomBoundary = data[3];
cellXcount = topBoundary.length;
cellYcount = leftBoundary.length;
concentrations = []; // reset concentrations
grid.selectAll("rect").remove();
svg.selectAll("rect").remove();
state = 0;
console.log(topBoundary);
var cellWidth = gridWidth / cellXcount;
var cellHeight = gridHeight / cellYcount;
var iteration = [];
numIterations = (data.length - 6) / (cellYcount + 2);
console.log(numIterations);
for (let i = 0; i < numIterations; i++) {
iteration = [];
for (let j = 0; j < cellYcount; j++) {
iteration.push(data[i * (cellYcount + 2) + 6 + j])
}
concentrations.push(iteration);
}
console.log(concentrations);
var maxC = calcMaxConcentrationInIteration(state);
// Create grid
for (let i = 0; i < cellYcount; i++) {
for (let j = 0; j < cellXcount; j++) {
grid.append("rect")
.attr("x", i * cellWidth + gap/2)
.attr("y", j * cellHeight + gap/2)
.attr("width", cellWidth - gap)
.attr("height", cellHeight - gap)
.attr("fill", getColor(concentrations[state][i][j], maxC));
}
}
// Create Boundaries
// left and right
for (let j = 0; j < cellYcount; j++) {
svg.append("rect")
.attr("x", svgMargin.left - 10)
.attr("y", svgMargin.top + j * cellHeight + gap/2)
.attr("width", 7)
.attr("height", cellHeight - gap)
.attr("fill", getColor(leftBoundary[j], maxC));
svg.append("rect")
.attr("x", width - svgMargin.right + 3)
.attr("y", svgMargin.top + j * cellHeight + gap/2)
.attr("width", 7)
.attr("height", cellHeight - gap)
.attr("fill", getColor(rightBoundary[j], maxC));
}
// top and bottom
for (let i = 0; i < cellXcount; i++) {
svg.append("rect")
.attr("x", svgMargin.left + i * cellWidth + gap/2)
.attr("y", svgMargin.top - 10)
.attr("width", cellWidth - gap)
.attr("height", 7)
.attr("fill", getColor(topBoundary[i], maxC));
svg.append("rect")
.attr("x", svgMargin.left + i * cellWidth + gap/2)
.attr("y", height - svgMargin.bottom + 3)
.attr("width", cellWidth - gap)
.attr("height", 7)
.attr("fill", getColor(bottomBoundary[i], maxC));
}
}
function updateGrid(new_state) {
var maxC = calcMaxConcentrationInIteration(new_state);
console.log(maxC);
grid.selectAll("rect")
.attr("fill", function (d,i) {
var row = Math.floor(i/20);
var col = i%20;
return getColor(concentrations[new_state][row][col], maxC);
})
}
// key events for changing grid iteration state
addEventListener("keydown", (event) => {
if (event.isComposing || event.keyCode === 81) {
if (state > 0) {
state -= 1;
updateGrid(state);
}
}
if (event.isComposing || event.keyCode === 69) {
if (state < numIterations-1) {
state += 1;
updateGrid(state);
}
}
if (event.isComposing || event.keyCode === 65) {
if (state > 9) {
state -= 10;
} else {
state = 0;
}
updateGrid(state);
}
if (event.isComposing || event.keyCode === 68) {
if (state < numIterations-10) {
state += 10;
} else {
state = numIterations-1;
}
updateGrid(state);
}
});
async function logFile() {
var file = document.getElementById("c_file").files[0];
const reader = new FileReader();
reader.readAsText(file);
console.log(reader.result);
}
function reader(file, callback) {
const fr = new FileReader();
fr.onload = () => callback(null, fr.result);
fr.onerror = (err) => callback(err);
fr.readAsText(file);
}
// document.getElementById("c_file").addEventListener("change", logFile);
document.getElementById("c_file").addEventListener("change", (evt) => {
// No files, do nothing.
if (!evt.target.files) {
return;
}
reader(evt.target.files[0], (err, res) => {
console.log(res); // Base64 `data:image/...` String result.
createVisualFromFile(res);
});
});
// Append the SVG element.
container.append(svg.node());
</script>

View File

@ -22,7 +22,7 @@ subprocess.call('doxygen Doxyfile.in', shell=True)
# -- Project information -----------------------------------------------------
project = 'TUG'
copyright = 'MIT'
copyright = 'GPL2'
author = 'Philipp Ungrund, Hannes Signer'
@ -41,7 +41,8 @@ extensions = [
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx.ext.inheritance_diagram',
'breathe'
'breathe',
'sphinx_mdinclude'
]
html_baseurl = "/index.html"
@ -94,4 +95,4 @@ breathe_projects = {
"Tug": "_build/xml/"
}
breathe_default_project = "Tug"
breathe_default_members = ('members', 'undoc-members')
breathe_default_members = ('members', 'undoc-members')

View File

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

View File

@ -1,2 +0,0 @@
Developper API
==============

View File

@ -11,18 +11,21 @@ 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 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.
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 grid = Grid(row,col);
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);
@ -40,14 +43,17 @@ of the grid are set as constant edges with a concentration of 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.
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 simulation = Simulation(grid, bc, FTCS_APPROACH);
Simulation<double, FTCS_APPROACH> simulation(grid, bc);
simulation.setTimestep(0.1);
simulation.setIterations(1000);
simulation.setOutputCSV(CSV_OUTPUT_VERBOSE);
@ -59,4 +65,4 @@ one below the other into the corresponding CSV file.
Setting special boundary conditions on individual cells
-------------------------------------------------------
-------------------------------------------------------

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 126 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

View File

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

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 173 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -49,3 +49,4 @@ Table of Contents
developer
examples
visualization
contributors

View File

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

View File

@ -3,7 +3,5 @@ User API
.. toctree::
:maxdepth: 2
Grid
Boundary
Simulation
Diffusion

View File

@ -1,48 +0,0 @@
#include <tug/Simulation.hpp>
int main(int argc, char *argv[]) {
// **************
// **** GRID ****
// **************
// create a linear grid with 20 cells
int cells = 20;
Grid grid = Grid(cells);
MatrixXd concentrations = MatrixXd::Constant(1,20,0);
concentrations(0,0) = 2000;
// TODO add option to set concentrations with a vector in 1D case
grid.setConcentrations(concentrations);
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
bc.setBoundarySideConstant(BC_SIDE_LEFT, 0);
bc.setBoundarySideConstant(BC_SIDE_RIGHT, 0);
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Simulation simulation = Simulation(grid, bc, BTCS_APPROACH); // grid,boundary,simulation-approach
// set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// set the number of iterations
simulation.setIterations(100);
// 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();
}

View File

@ -1,84 +1,84 @@
#include <tug/Simulation.hpp>
#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;
Grid grid = Grid(row,col);
// 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 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;
grid.setConcentrations(concentrations);
// (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);
// (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 ****
// ******************
// ******************
// **** 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);
// 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 ****
// ************************
// (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);
// set up a simulation environment
Diffusion simulation(grid, bc); // grid,boundary
// set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// ************************
// **** SIMULATION ENV ****
// ************************
// set the number of iterations
simulation.setIterations(300);
// set up a simulation environment
Simulation simulation = Simulation(grid, bc, BTCS_APPROACH); // grid,boundary,simulation-approach
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_XTREME);
// set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// **** RUN SIMULATION ****
// set the number of iterations
simulation.setIterations(300);
// run the simulation
// 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();
}
// EASY_BLOCK("SIMULATION")
simulation.run();
// EASY_END_BLOCK;
// profiler::dumpBlocksToFile("test_profile.prof");
// profiler::stopListen();
}

View File

@ -1,20 +1,7 @@
add_executable(FTCS_1D_proto_example FTCS_1D_proto_example.cpp)
add_executable(FTCS_2D_proto_example FTCS_2D_proto_example.cpp)
add_executable(BTCS_1D_proto_example BTCS_1D_proto_example.cpp)
add_executable(BTCS_2D_proto_example BTCS_2D_proto_example.cpp)
add_executable(CRNI_2D_proto_example CRNI_2D_proto_example.cpp)
add_executable(reference-FTCS_2D_closed reference-FTCS_2D_closed.cpp)
add_executable(profiling_openmp profiling_openmp.cpp)
target_link_libraries(FTCS_1D_proto_example tug)
target_link_libraries(FTCS_2D_proto_example tug)
target_link_libraries(BTCS_1D_proto_example tug)
target_link_libraries(BTCS_2D_proto_example tug)
target_link_libraries(CRNI_2D_proto_example tug)
target_link_libraries(reference-FTCS_2D_closed tug)
target_link_libraries(profiling_openmp tug)
add_executable(FTCS_2D_proto_example_mdl FTCS_2D_proto_example_mdl.cpp)
add_executable(FTCS_2D_proto_closed_mdl FTCS_2D_proto_closed_mdl.cpp)
target_link_libraries(BTCS_2D_proto_example tug)
target_link_libraries(FTCS_2D_proto_closed_mdl tug)
target_link_libraries(FTCS_2D_proto_example_mdl tug)

View File

@ -1,24 +0,0 @@
#include <tug/Simulation.hpp>
int main(int argc, char *argv[]) {
int row = 20;
int col = 20;
Grid grid(row, col);
MatrixXd concentrations = MatrixXd::Constant(row,col,0);
concentrations(10,10) = 2000;
grid.setConcentrations(concentrations);
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 simulation = Simulation(grid, bc, CRANK_NICOLSON_APPROACH);
simulation.setTimestep(0.1);
simulation.setIterations(50);
simulation.setOutputCSV(CSV_OUTPUT_XTREME);
simulation.run();
}

View File

@ -1,47 +0,0 @@
#include "tug/Boundary.hpp"
#include <tug/Simulation.hpp>
int main(int argc, char *argv[]) {
// **************
// **** GRID ****
// **************
// create a linear grid with 20 cells
int cells = 20;
Grid grid = Grid(cells);
MatrixXd concentrations = MatrixXd::Constant(1,20,20);
grid.setConcentrations(concentrations);
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
bc.setBoundarySideConstant(BC_SIDE_LEFT, 1);
bc.setBoundarySideConstant(BC_SIDE_RIGHT, 1);
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Simulation simulation = Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
// (optional) set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// (optional) 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_OFF);
// **** RUN SIMULATION ****
// run the simulation
simulation.run();
}

View File

@ -1,85 +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
*
* @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/Simulation.hpp>
#include <tug/Diffusion.hpp>
using namespace Eigen;
using namespace tug;
int main(int argc, char *argv[]) {
int row = 64;
int row = 64;
if (argc == 2) {
// no cmd line argument, take col=row=64
row = atoi(argv[1]);
}
int col=row;
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 ****
// **************
std::cout << "Nrow =" << row << std::endl;
// **************
// **** GRID ****
// **************
// create a grid with a 20 x 20 field
int n2 = row/2-1;
Grid grid = Grid(row,col);
// 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 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;
grid.setConcentrations(concentrations);
// (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);
// (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 ****
// ******************
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
// 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);
// (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
// ************************
// **** SIMULATION ENV ****
// ************************
// set the timestep of the simulation
simulation.setTimestep(10000); // timestep
// set up a simulation environment
Simulation simulation = Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
// set the number of iterations
simulation.setIterations(100);
// set the timestep of the simulation
simulation.setTimestep(10000); // timestep
// (optional) set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
// CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_VERBOSE);
// set the number of iterations
simulation.setIterations(100);
// **** RUN SIMULATION ****
// (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();
// run the simulation
simulation.run();
return 0;
return 0;
}

View File

@ -1,92 +0,0 @@
/**
* @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 <tug/Simulation.hpp>
// #include <easy/profiler.h>
// #define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
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 = 20;
int col = 20;
Grid grid = Grid(row,col);
// (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(0,0) = 1999;
grid.setConcentrations(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.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
Simulation simulation = Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
// set the timestep of the simulation
simulation.setTimestep(0.1); // timestep
// set the number of iterations
simulation.setIterations(10000);
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON, CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_VERBOSE);
// **** RUN SIMULATION ****
// run the simulation
// EASY_BLOCK("SIMULATION")
simulation.run();
// EASY_END_BLOCK;
// profiler::dumpBlocksToFile("test_profile.prof");
// profiler::stopListen();
}

View File

@ -1,77 +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
*
* @brief Creates a prototypical standard TUG simulation in 2D with FTCS
* approach and constant boundary condition
*
*/
#include <tug/Simulation.hpp>
#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;
Grid grid = Grid(row,col);
// **************
// **** GRID ****
// **************
// (optional) set the domain, e.g.:
// grid.setDomain(20, 20);
// create a grid with a 20 x 20 field
int row = 64;
int col = 64;
int n2 = row / 2 - 1;
// (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;
grid.setConcentrations(concentrations);
// (optional) set the domain, e.g.:
// grid.setDomain(20, 20);
// (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);
// (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 ****
// ******************
// ******************
// **** BOUNDARY ****
// ******************
// create a boundary with constant values
Boundary bc = Boundary(grid);
// 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);
// (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 ****
// ************************
// ************************
// **** SIMULATION ENV ****
// ************************
// set up a simulation environment
Diffusion<double, tug::FTCS_APPROACH> simulation(
grid, bc); // grid,boundary,simulation-approach
// set up a simulation environment
Simulation simulation = Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
// (optional) set the timestep of the simulation
simulation.setTimestep(1000); // timestep
// (optional) set the timestep of the simulation
simulation.setTimestep(1000); // timestep
// (optional) set the number of iterations
simulation.setIterations(5);
// (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);
// (optional) set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON, CSV_OUTPUT_VERBOSE]
simulation.setOutputCSV(CSV_OUTPUT_OFF);
// **** RUN SIMULATION ****
// **** RUN SIMULATION ****
// run the simulation
simulation.run();
// run the simulation
simulation.run();
return 0;
return 0;
}

@ -1 +0,0 @@
Subproject commit b7c21ec5ceeadb4951b00396fc1e4642dd347e5f

View File

@ -1,67 +0,0 @@
#include <string>
#include <tug/Simulation.hpp>
#include <iostream>
#include <fstream>
#include <chrono>
int main(int argc, char *argv[]) {
int n[] = {2000};
int threads[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int iterations[1] = {1};
int repetition = 10;
for(int l=0; l<size(threads); l++){
//string filename = "ftcs_openmp_" + to_string(threads[l]) + ".csv";
ofstream myfile;
myfile.open("speedup_1000.csv", std::ios::app);
myfile << "Number threads: " << threads[l] << endl;
for (int i = 0; i < size(n); i++){
cout << "Grid size: " << n[i] << " x " << n[i] << endl << endl;
//myfile << "Grid size: " << n[i] << " x " << n[i] << endl << endl;
for(int j = 0; j < size(iterations); j++){
cout << "Iterations: " << iterations[j] << endl;
//myfile << "Iterations: " << iterations[j] << endl;
for (int k = 0; k < repetition; k++){
cout << "Wiederholung: " << k << endl;
Grid grid = Grid(n[i], n[i]);
grid.setDomain(1, 1);
MatrixXd concentrations = MatrixXd::Constant(n[i], n[i], 0);
concentrations(n[i]/2,n[i]/2) = 1;
grid.setConcentrations(concentrations);
MatrixXd alpha = MatrixXd::Constant(n[i], n[i], 0.5);
Boundary bc = Boundary(grid);
Simulation sim = Simulation(grid, bc, BTCS_APPROACH);
sim.setSolver(THOMAS_ALGORITHM_SOLVER);
if(argc == 2){
int numThreads = atoi(argv[1]);
sim.setNumberThreads(numThreads);
}
else{
sim.setNumberThreads(threads[l]);
}
sim.setTimestep(0.01);
sim.setIterations(iterations[j]);
sim.setOutputCSV(CSV_OUTPUT_OFF);
auto begin = std::chrono::high_resolution_clock::now();
sim.run();
auto end = std::chrono::high_resolution_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
myfile << milliseconds.count() << endl;
}
}
cout << endl;
myfile << endl;
}
myfile.close();
}
}

View File

@ -1,67 +0,0 @@
#include <string>
#include <tug/Simulation.hpp>
#include <iostream>
#include <fstream>
#include <chrono>
int main(int argc, char *argv[]) {
int n[] = {2000};
int threads[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int iterations[1] = {1};
int repetition = 10;
for(int l=0; l<size(threads); l++){
//string filename = "ftcs_openmp_" + to_string(threads[l]) + ".csv";
ofstream myfile;
myfile.open("speedup_1000.csv", std::ios::app);
myfile << "Number threads: " << threads[l] << endl;
for (int i = 0; i < size(n); i++){
cout << "Grid size: " << n[i] << " x " << n[i] << endl << endl;
//myfile << "Grid size: " << n[i] << " x " << n[i] << endl << endl;
for(int j = 0; j < size(iterations); j++){
cout << "Iterations: " << iterations[j] << endl;
//myfile << "Iterations: " << iterations[j] << endl;
for (int k = 0; k < repetition; k++){
cout << "Wiederholung: " << k << endl;
Grid grid = Grid(n[i], n[i]);
grid.setDomain(1, 1);
MatrixXd concentrations = MatrixXd::Constant(n[i], n[i], 0);
concentrations(n[i]/2,n[i]/2) = 1;
grid.setConcentrations(concentrations);
MatrixXd alpha = MatrixXd::Constant(n[i], n[i], 0.5);
Boundary bc = Boundary(grid);
Simulation sim = Simulation(grid, bc, BTCS_APPROACH);
sim.setSolver(THOMAS_ALGORITHM_SOLVER);
if(argc == 2){
int numThreads = atoi(argv[1]);
sim.setNumberThreads(numThreads);
}
else{
sim.setNumberThreads(threads[l]);
}
sim.setTimestep(0.01);
sim.setIterations(iterations[j]);
sim.setOutputCSV(CSV_OUTPUT_OFF);
auto begin = std::chrono::high_resolution_clock::now();
sim.run();
auto end = std::chrono::high_resolution_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
myfile << milliseconds.count() << endl;
}
}
cout << endl;
myfile << endl;
}
myfile.close();
}
}

View File

@ -1,55 +0,0 @@
#include <tug/Simulation.hpp>
#include "Eigen/Core"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
int row = 50;
int col = 50;
int domain_row = 10;
int domain_col = 10;
// Grid
Grid grid = Grid(row, col);
grid.setDomain(domain_row, domain_col);
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
concentrations(5,5) = 1;
grid.setConcentrations(concentrations);
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;
}
}
for (int i = 0; i < 5; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.001;
}
}
for (int i = 5; i < 11; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.1;
}
}
grid.setAlpha(alpha, alpha);
// Boundary
Boundary bc = Boundary(grid);
// Simulation
Simulation sim = Simulation(grid, bc, FTCS_APPROACH);
sim.setTimestep(0.001);
sim.setIterations(10000);
sim.setOutputCSV(CSV_OUTPUT_OFF);
sim.setOutputConsole(CONSOLE_OUTPUT_OFF);
// RUN
sim.run();
}

View File

@ -1,215 +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.
*
* @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 "Grid.hpp"
#include <cstdint>
#include <map>
#include <stdexcept>
#include <utility>
#include <vector>
using namespace std;
using namespace Eigen;
namespace tug {
/**
* @brief Enum defining the two implemented boundary conditions.
*
* @brief Enum defining the two implemented boundary conditions.
*
*/
enum BC_TYPE {
BC_TYPE_CLOSED,
BC_TYPE_CONSTANT
};
enum BC_TYPE { BC_TYPE_CLOSED, BC_TYPE_CONSTANT };
/**
* @brief Enum defining all 4 possible sides to a 1D and 2D grid.
*
* @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
};
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
*/
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();
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(double value);
/**
* @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);
/**
* @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);
/**
* @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 Return the type of the boundary condition, i.e. whether the
* boundary is considered closed or constant.
*
* @return BC_TYPE Type of boundary condition, either BC_TYPE_CLOSED or
BC_TYPE_CONSTANT.
*/
BC_TYPE getType();
/**
* @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 concentration value for the constant boundary condition.
*
* @return double Value of the concentration.
*/
double getValue();
/**
* @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; }
private:
BC_TYPE type;
double value;
/**
* @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
*/
class Boundary {
public:
/**
* @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(Grid grid);
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 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);
/**
* @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
/**
* @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);
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);
/**
* @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 setBoundaryElementClosed(BC_SIDE side, int index);
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 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);
/**
* @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.");
/**
* @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 vector<BoundaryElement> Contains the boundary conditions as BoundaryElement objects.
*/
const vector<BoundaryElement> getBoundarySide(BC_SIDE side);
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
const int n = is_vertical ? this->rows : this->cols;
/**
* @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 VectorXd Vector with values as doubles.
*/
VectorXd getBoundarySideValues(BC_SIDE side);
this->boundaries[side] =
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>());
}
/**
* @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 BoundaryElement Boundary condition as a BoundaryElement object.
*/
BoundaryElement getBoundaryElement(BC_SIDE side, int index);
/**
* @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.");
/**
* @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 BC_TYPE Boundary Type of the corresponding boundary condition.
*/
BC_TYPE getBoundaryElementType(BC_SIDE side, int index);
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
const int n = is_vertical ? this->rows : this->cols;
/**
* @brief Returns the concentration value of a corresponding
* BoundaryElement 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 double Concentration of the corresponding BoundaryElement object.
*/
double getBoundaryElementValue(BC_SIDE side, int index);
this->boundaries[side] =
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>(value));
}
private:
Grid grid; // Boundary is directly dependent on the dimensions of a predefined
vector<vector<BoundaryElement>> boundaries; // Vector with Boundary Element information
/**
* @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;
};
#endif
} // namespace tug
#endif // BOUNDARY_H_

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,25 +1,18 @@
#include <chrono>
#include <stdexcept>
#include <string>
#include <fstream>
#pragma once
using namespace std;
#include <cassert>
// used for throwing an invalid argument message
#define throw_invalid_argument(msg) \
throw std::invalid_argument(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + \
std::string(msg))
// used for throwing an out of range message
#define throw_out_of_range(msg) \
throw std::out_of_range(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + std::string(msg))
// get current time
#define time_marker() std::chrono::high_resolution_clock::now()
// calculates difference between two time points
#define diff_time(start, end) \
({ \
std::chrono::duration<double> duration = \
@ -28,11 +21,19 @@ using namespace std;
duration.count(); \
})
#define tug_assert(expr, msg) assert((expr) && msg)
// calculates arithmetic or harmonic mean of alpha between two cells
static double calcAlphaIntercell(const double &alpha1, const double &alpha2, bool useHarmonic = true) {
if (useHarmonic) {
return double(2) / ((double(1)/alpha1) + (double(1)/alpha2));
} else {
return 0.5 * (alpha1 + alpha2);
}
template <typename T>
constexpr T calcAlphaIntercell(T alpha1, T alpha2, bool useHarmonic = true) {
if (useHarmonic) {
const T operand1 = alpha1 == 0 ? 0 : 1 / alpha1;
const T operand2 = alpha2 == 0 ? 0 : 1 / alpha2;
const T denom = operand1 + operand2;
return denom == 0 ? 0 : 2. / denom;
} else {
return 0.5 * (alpha1 + alpha2);
}
}

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

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

View File

@ -1,175 +0,0 @@
/**
* @file Grid.hpp
* @brief API of Grid class, that holds a matrix with concenctrations and a
* respective matrix/matrices of alpha coefficients.
*
*/
#include <Eigen/Core>
#include <Eigen/Sparse>
using namespace Eigen;
class Grid {
public:
/**
* @brief Constructs a new 1D-Grid object of a given length, which holds a matrix
* with concentrations and a respective matrix of alpha coefficients.
* The domain length is per default the same as the length. The concentrations
* are all 20 by default and the alpha coefficients are 1.
*
* @param length Length of the 1D-Grid. Must be greater than 3.
*/
Grid(int length);
/**
* @brief Constructs a new 2D-Grid object of given dimensions, which holds a matrix
* with concentrations and the respective matrices of alpha coefficient for
* each direction. The domain in x- and y-direction is per default equal to
* the col length and row length, respectively.
* The concentrations are all 20 by default across the entire grid and the
* alpha coefficients 1 in both directions.
*
* @param row Length of the 2D-Grid in y-direction. Must be greater than 3.
* @param col Length of the 2D-Grid in x-direction. Must be greater than 3.
*/
Grid(int row, int col);
/**
* @brief Sets the concentrations matrix for a 1D or 2D-Grid.
*
* @param concentrations An Eigen3 MatrixXd holding the concentrations. Matrix must
* have correct dimensions as defined in row and col. (Or length,
* in 1D case).
*/
void setConcentrations(MatrixXd concentrations);
/**
* @brief Gets the concentrations matrix for a Grid.
*
* @return MatrixXd An Eigen3 matrix holding the concentrations and having the
* same dimensions as the grid.
*/
const MatrixXd getConcentrations();
/**
* @brief Set the alpha coefficients of a 1D-Grid. Grid must be one dimensional.
*
* @param alpha An Eigen3 MatrixXd with 1 row holding the alpha coefficients. Matrix
* columns must have same size as length of grid.
*/
void setAlpha(MatrixXd alpha);
/**
* @brief Set the alpha coefficients of a 2D-Grid. Grid must be two dimensional.
*
* @param alphaX An Eigen3 MatrixXd holding the alpha coefficients in x-direction.
* Matrix must be of same size as the grid.
* @param alphaY An Eigen3 MatrixXd holding the alpha coefficients in y-direction.
* Matrix must be of same size as the grid.
*/
void setAlpha(MatrixXd alphaX, MatrixXd alphaY);
/**
* @brief Gets the matrix of alpha coefficients of a 1D-Grid. Grid must be one dimensional.
*
* @return MatrixXd A matrix with 1 row holding the alpha coefficients.
*/
const MatrixXd getAlpha();
/**
* @brief Gets the matrix of alpha coefficients in x-direction of a 2D-Grid. Grid must be
* two dimensional.
*
* @return MatrixXd A matrix holding the alpha coefficients in x-direction.
*/
const MatrixXd getAlphaX();
/**
* @brief Gets the matrix of alpha coefficients in y-direction of a 2D-Grid. Grid must be
* two dimensional.
*
* @return MatrixXd A matrix holding the alpha coefficients in y-direction.
*/
const MatrixXd getAlphaY();
/**
* @brief Gets the dimensions of the grid.
*
* @return int Dimensions, either 1 or 2.
*/
int getDim();
/**
* @brief Gets length of 1D grid. Must be one dimensional grid.
*
* @return int Length of 1D grid.
*/
int getLength();
/**
* @brief Gets the number of rows of the grid.
*
* @return int Number of rows.
*/
int getRow();
/**
* @brief Gets the number of columns of the grid.
*
* @return int Number of columns.
*/
int getCol();
/**
* @brief Sets the domain length of a 1D-Grid. Grid must be one dimensional.
*
* @param domainLength A double value of the domain length. Must be positive.
*/
void setDomain(double domainLength);
/**
* @brief Sets the domain size of a 2D-Grid. Grid must be two dimensional.
*
* @param domainRow A double value of the domain size in y-direction. Must be positive.
* @param domainCol A double value of the domain size in x-direction. Must be positive.
*/
void setDomain(double domainRow,double domainCol);
/**
* @brief Gets the delta value for 1D-Grid. Grid must be one dimensional.
*
* @return double Delta value.
*/
double getDelta();
/**
* @brief Gets the delta value in x-direction.
*
* @return double Delta value in x-direction.
*/
double getDeltaCol();
/**
* @brief Gets the delta value in y-direction. Must be two dimensional grid.
*
* @return double Delta value in y-direction.
*/
double getDeltaRow();
private:
int col; // number of grid columns
int row; // number of grid rows
int dim; // 1D or 2D
double domainCol; // number of domain columns
double domainRow; // number of domain rows
double deltaCol; // delta in x-direction (between columns)
double deltaRow; // delta in y-direction (between rows)
MatrixXd concentrations; // Matrix holding grid concentrations
MatrixXd alphaX; // Matrix holding alpha coefficients in x-direction
MatrixXd alphaY; // Matrix holding alpha coefficients in y-direction
};

View File

@ -1,213 +0,0 @@
/**
* @file Simulation.hpp
* @brief API of Simulation class, that holds all information regarding a specific simulation
* run like its timestep, number of iterations and output options. Simulation object
* also holds a predefined Grid and Boundary object.
*
*/
#include "Boundary.hpp"
#include <ios>
using namespace std;
/**
* @brief Enum defining the two implemented solution approaches.
*
*/
enum APPROACH {
FTCS_APPROACH, // Forward Time-Centered Space
BTCS_APPROACH, // Backward Time-Centered Space solved with EigenLU solver
CRANK_NICOLSON_APPROACH
};
/**
* @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 Enum holding different options for .csv output.
*
*/
enum CSV_OUTPUT {
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 // csv output like VERBOSE but additional boundary conditions at beginning
};
/**
* @brief Enum holding different options for console output.
*
*/
enum CONSOLE_OUTPUT {
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
};
/**
* @brief Enum holding different options for time measurement.
*
*/
enum TIME_MEASURE {
TIME_MEASURE_OFF, // do not print any time measures
TIME_MEASURE_ON // print time measure after last iteration
};
/**
* @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.
*
*/
class Simulation {
public:
/**
* @brief Set up a simulation environment. The timestep and number of iterations
* must be set. For the BTCS approach, the Thomas algorithm is used as
* the default linear equation solver as this is faster for tridiagonal
* matrices. CSV output, console output and time measure are off by default.
* Also, the number of cores is set to the maximum number of cores -1 by default.
*
* @param grid Valid grid object
* @param bc Valid boundary condition object
* @param approach Approach to solving the problem. Either FTCS or BTCS.
*/
Simulation(Grid &grid, Boundary &bc, APPROACH approach);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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(double timestep);
/**
* @brief Currently set time step is returned.
*
* @return double timestep
*/
double getTimestep();
/**
* @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);
/**
* @brief Set the desired linear equation solver to be used for BTCS approach. Without effect
* in case of FTCS approach.
*
* @param solver Solver to be used. Default is Thomas Algorithm as it is more efficient for
* tridiagonal Matrices.
*/
void setSolver(SOLVER solver);
/**
* @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 num_threads);
/**
* @brief Return the currently set iterations to be calculated.
*
* @return int Number of iterations.
*/
int getIterations();
/**
* @brief Outputs the current concentrations of the grid on the console.
*
*/
void printConcentrationsConsole();
/**
* @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.
*/
string createCSVfile();
/**
* @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(string filename);
/**
* @brief Method starts the simulation process with the previously set
* parameters.
*/
void run();
private:
double timestep;
int iterations;
int innerIterations;
int numThreads;
CSV_OUTPUT csv_output;
CONSOLE_OUTPUT console_output;
TIME_MEASURE time_measure;
Grid &grid;
Boundary &bc;
APPROACH approach;
SOLVER solver;
};

167
naaice/BTCS_2D_NAAICE.cpp Normal file
View File

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

14
naaice/CMakeLists.txt Normal file
View File

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

View File

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

70
naaice/README.md Normal file
View File

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

5
naaice/alphax.csv Normal file
View File

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

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

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

5
naaice/init_conc.csv Normal file
View File

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

View File

@ -286,7 +286,11 @@ ADIHetDir <- function(field, dt, iter, alpha) {
return(out)
}
harm <- function(x,y) 1/(1/x+1/y)
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)
@ -428,10 +432,10 @@ FTCS_2D <- function(field, dt, iter, alpha) {
## 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]) * mean(alpha[i+1,j],alpha[i,j]) -
(res[i,j]-res[i-1,j]) * mean(alpha[i-1,j],alpha[i,j])) +
+ tsteps[innerit]/dx/dx * ((res[i,j+1]-res[i,j]) * mean(alpha[i,j+1],alpha[i,j]) -
(res[i,j]-res[i,j-1]) * mean(alpha[i,j-1],alpha[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

View File

@ -1,437 +0,0 @@
/**
* @file BTCSv2.cpp
* @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.
*
*/
#include "FTCS.cpp"
#include <tug/Boundary.hpp>
#include <omp.h>
#define NUM_THREADS_BTCS 10
using namespace Eigen;
// calculates coefficient for left boundary in constant case
static tuple<double, double> calcLeftBoundaryCoeffConstant(MatrixXd &alpha, int rowIndex, double sx) {
double centerCoeff;
double rightCoeff;
centerCoeff = 1 + sx * (calcAlphaIntercell(alpha(rowIndex,0), alpha(rowIndex,1))
+ 2 * alpha(rowIndex,0));
rightCoeff = -sx * calcAlphaIntercell(alpha(rowIndex,0), alpha(rowIndex,1));
return {centerCoeff, rightCoeff};
}
// calculates coefficient for left boundary in closed case
static tuple<double, double> calcLeftBoundaryCoeffClosed(MatrixXd &alpha, int rowIndex, double sx) {
double centerCoeff;
double rightCoeff;
centerCoeff = 1 + sx * calcAlphaIntercell(alpha(rowIndex,0), alpha(rowIndex,1));
rightCoeff = -sx * calcAlphaIntercell(alpha(rowIndex,0), alpha(rowIndex,1));
return {centerCoeff, rightCoeff};
}
// calculates coefficient for right boundary in constant case
static tuple<double, double> calcRightBoundaryCoeffConstant(MatrixXd &alpha, int rowIndex, int n, double sx) {
double leftCoeff;
double centerCoeff;
leftCoeff = -sx * calcAlphaIntercell(alpha(rowIndex,n-1), alpha(rowIndex,n));
centerCoeff = 1 + sx * (calcAlphaIntercell(alpha(rowIndex,n-1), alpha(rowIndex,n))
+ 2 * alpha(rowIndex,n));
return {leftCoeff, centerCoeff};
}
// calculates coefficient for right boundary in closed case
static tuple<double, double> calcRightBoundaryCoeffClosed(MatrixXd &alpha, int rowIndex, int n, double sx) {
double leftCoeff;
double centerCoeff;
leftCoeff = -sx * calcAlphaIntercell(alpha(rowIndex,n-1), alpha(rowIndex,n));
centerCoeff = 1 + sx * calcAlphaIntercell(alpha(rowIndex,n-1), alpha(rowIndex,n));
return {leftCoeff, centerCoeff};
}
// creates coefficient matrix for next time step from alphas in x-direction
static SparseMatrix<double> createCoeffMatrix(MatrixXd &alpha, vector<BoundaryElement> &bcLeft, vector<BoundaryElement> &bcRight, int numCols, int rowIndex, double sx) {
// square matrix of column^2 dimension for the coefficients
SparseMatrix<double> cm(numCols, numCols);
cm.reserve(VectorXi::Constant(numCols, 3));
// left column
BC_TYPE type = bcLeft[rowIndex].getType();
if (type == BC_TYPE_CONSTANT) {
auto [centerCoeffTop, rightCoeffTop] = calcLeftBoundaryCoeffConstant(alpha, rowIndex, sx);
cm.insert(0,0) = centerCoeffTop;
cm.insert(0,1) = rightCoeffTop;
} else if (type == BC_TYPE_CLOSED) {
auto [centerCoeffTop, rightCoeffTop] = calcLeftBoundaryCoeffClosed(alpha, rowIndex, sx);
cm.insert(0,0) = centerCoeffTop;
cm.insert(0,1) = rightCoeffTop;
} else {
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++) {
cm.insert(i,i-1) = -sx * calcAlphaIntercell(alpha(rowIndex,i-1), alpha(rowIndex,i));
cm.insert(i,i) = 1 + sx * (
calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex,i+1))
+ calcAlphaIntercell(alpha(rowIndex,i-1), alpha(rowIndex,i))
)
;
cm.insert(i,i+1) = -sx * calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex,i+1));
}
// right column
type = bcRight[rowIndex].getType();
if (type == BC_TYPE_CONSTANT) {
auto [leftCoeffBottom, centerCoeffBottom] = calcRightBoundaryCoeffConstant(alpha, rowIndex, n, sx);
cm.insert(n,n-1) = leftCoeffBottom;
cm.insert(n,n) = centerCoeffBottom;
} else if (type == BC_TYPE_CLOSED) {
auto [leftCoeffBottom, centerCoeffBottom] = calcRightBoundaryCoeffClosed(alpha, rowIndex, n, sx);
cm.insert(n,n-1) = leftCoeffBottom;
cm.insert(n,n) = centerCoeffBottom;
} else {
throw_invalid_argument("Undefined Boundary Condition Type somewhere on Right or Bottom!");
}
cm.makeCompressed(); // important for Eigen solver
return cm;
}
// calculates explicity concentration at top boundary in constant case
static double calcExplicitConcentrationsTopBoundaryConstant(MatrixXd &concentrations,
MatrixXd &alpha, vector<BoundaryElement> &bcTop, int rowIndex, int i, double sy) {
double c;
c = sy * calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex+1,i))
* concentrations(rowIndex,i)
+ (
1 - sy * (
calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex+1,i))
+ 2 * alpha(rowIndex,i)
)
) * concentrations(rowIndex,i)
+ sy * alpha(rowIndex,i) * bcTop[i].getValue();
return c;
}
// calculates explicit concentration at top boundary in closed case
static double calcExplicitConcentrationsTopBoundaryClosed(MatrixXd &concentrations,
MatrixXd &alpha, int rowIndex, int i, double sy) {
double c;
c = sy * calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex+1,i))
* concentrations(rowIndex,i)
+ (
1 - sy * (
calcAlphaIntercell(alpha(rowIndex,i), alpha(rowIndex+1,i))
)
) * concentrations(rowIndex,i);
return c;
}
// calculates explicit concentration at bottom boundary in constant case
static double calcExplicitConcentrationsBottomBoundaryConstant(MatrixXd &concentrations,
MatrixXd &alpha, vector<BoundaryElement> &bcBottom, int rowIndex, int i, double sy) {
double c;
c = sy * alpha(rowIndex,i) * bcBottom[i].getValue()
+ (
1 - sy * (
2 * alpha(rowIndex,i)
+ calcAlphaIntercell(alpha(rowIndex-1,i), alpha(rowIndex,i))
)
) * concentrations(rowIndex,i)
+ sy * calcAlphaIntercell(alpha(rowIndex-1,i), alpha(rowIndex,i))
* concentrations(rowIndex-1,i);
return c;
}
// calculates explicit concentration at bottom boundary in closed case
static double calcExplicitConcentrationsBottomBoundaryClosed(MatrixXd &concentrations,
MatrixXd &alpha, int rowIndex, int i, double sy) {
double c;
c = (
1 - sy * (
+ calcAlphaIntercell(alpha(rowIndex-1,i), alpha(rowIndex,i))
)
) * concentrations(rowIndex,i)
+ sy * calcAlphaIntercell(alpha(rowIndex-1,i), alpha(rowIndex,i))
* concentrations(rowIndex-1,i);
return c;
}
// creates a solution vector for next time step from the current state of concentrations
static VectorXd createSolutionVector(MatrixXd &concentrations, MatrixXd &alphaX, MatrixXd &alphaY,
vector<BoundaryElement> &bcLeft, vector<BoundaryElement> &bcRight,
vector<BoundaryElement> &bcTop, vector<BoundaryElement> &bcBottom,
int length, int rowIndex, double sx, double sy) {
VectorXd sv(length);
int numRows = concentrations.rows();
BC_TYPE type;
// inner rows
if (rowIndex > 0 && rowIndex < numRows-1) {
for (int i = 0; i < length; i++) {
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++) {
type = bcTop[i].getType();
if (type == BC_TYPE_CONSTANT) {
sv(i) = calcExplicitConcentrationsTopBoundaryConstant(concentrations, alphaY, bcTop, rowIndex, i, sy);
} else if (type == BC_TYPE_CLOSED) {
sv(i) = calcExplicitConcentrationsTopBoundaryClosed(concentrations, alphaY, rowIndex, i, sy);
} else {
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++) {
type = bcBottom[i].getType();
if (type == BC_TYPE_CONSTANT) {
sv(i) = calcExplicitConcentrationsBottomBoundaryConstant(concentrations, alphaY, bcBottom, rowIndex, i, sy);
} else if (type == BC_TYPE_CLOSED) {
sv(i) = calcExplicitConcentrationsBottomBoundaryClosed(concentrations, alphaY, rowIndex, i, sy);
} else {
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) {
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) {
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
static VectorXd EigenLUAlgorithm(SparseMatrix<double> &A, VectorXd &b) {
SparseLU<SparseMatrix<double>> solver;
solver.analyzePattern(A);
solver.factorize(A);
return solver.solve(b);
}
// solver for linear equation system; A corresponds to coefficient matrix,
// b to the solution vector
// implementation of Thomas Algorithm
static VectorXd ThomasAlgorithm(SparseMatrix<double> &A, VectorXd &b) {
uint32_t n = b.size();
Eigen::VectorXd a_diag(n);
Eigen::VectorXd b_diag(n);
Eigen::VectorXd c_diag(n);
Eigen::VectorXd x_vec = b;
// Fill diagonals vectors
b_diag[0] = A.coeff(0, 0);
c_diag[0] = A.coeff(0, 1);
for (int i = 1; i < n - 1; i++) {
a_diag[i] = A.coeff(i, i - 1);
b_diag[i] = A.coeff(i, i);
c_diag[i] = A.coeff(i, i + 1);
}
a_diag[n - 1] = A.coeff(n - 1, n - 2);
b_diag[n - 1] = A.coeff(n - 1, n - 1);
// start solving - c_diag and x_vec are overwritten
n--;
c_diag[0] /= b_diag[0];
x_vec[0] /= b_diag[0];
for (int i = 1; i < n; i++) {
c_diag[i] /= b_diag[i] - a_diag[i] * c_diag[i - 1];
x_vec[i] = (x_vec[i] - a_diag[i] * x_vec[i - 1]) /
(b_diag[i] - a_diag[i] * c_diag[i - 1]);
}
x_vec[n] = (x_vec[n] - a_diag[n] * x_vec[n - 1]) /
(b_diag[n] - a_diag[n] * c_diag[n - 1]);
for (int i = n; i-- > 0;) {
x_vec[i] -= c_diag[i] * x_vec[i + 1];
}
return x_vec;
}
// BTCS solution for 1D grid
static void BTCS_1D(Grid &grid, Boundary &bc, double timestep, VectorXd (*solverFunc) (SparseMatrix<double> &A, VectorXd &b)) {
int length = grid.getLength();
double sx = timestep / (grid.getDelta() * grid.getDelta());
VectorXd concentrations_t1(length);
SparseMatrix<double> A;
VectorXd b(length);
MatrixXd alpha = grid.getAlpha();
vector<BoundaryElement> bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
vector<BoundaryElement> bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
MatrixXd concentrations = grid.getConcentrations();
int rowIndex = 0;
A = createCoeffMatrix(alpha, bcLeft, bcRight, 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) {
b(0) += 2 * sx * alpha(0,0) * bcLeft[0].getValue();
}
if (bc.getBoundaryElementType(BC_SIDE_RIGHT, 0) == BC_TYPE_CONSTANT) {
b(length-1) += 2 * sx * alpha(0,length-1) * bcRight[0].getValue();
}
concentrations_t1 = solverFunc(A, b);
for (int j = 0; j < length; j++) {
concentrations(0,j) = concentrations_t1(j);
}
grid.setConcentrations(concentrations);
}
// BTCS solution for 2D grid
static void BTCS_2D(Grid &grid, Boundary &bc, double timestep, VectorXd (*solverFunc) (SparseMatrix<double> &A, VectorXd &b), int numThreads) {
int rowMax = grid.getRow();
int colMax = grid.getCol();
double sx = timestep / (2 * grid.getDeltaCol() * grid.getDeltaCol());
double sy = timestep / (2 * grid.getDeltaRow() * grid.getDeltaRow());
MatrixXd concentrations_t1 = MatrixXd::Constant(rowMax, colMax, 0);
VectorXd row_t1(colMax);
SparseMatrix<double> A;
VectorXd b;
MatrixXd alphaX = grid.getAlphaX();
MatrixXd alphaY = grid.getAlphaY();
vector<BoundaryElement> bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
vector<BoundaryElement> bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
vector<BoundaryElement> bcTop = bc.getBoundarySide(BC_SIDE_TOP);
vector<BoundaryElement> bcBottom = bc.getBoundarySide(BC_SIDE_BOTTOM);
MatrixXd concentrations = grid.getConcentrations();
#pragma omp parallel for num_threads(numThreads) private(A, b, row_t1)
for (int i = 0; i < rowMax; i++) {
A = createCoeffMatrix(alphaX, bcLeft, bcRight, colMax, i, sx);
b = createSolutionVector(concentrations, alphaX, alphaY, bcLeft, bcRight,
bcTop, bcBottom, colMax, i, sx, sy);
SparseLU<SparseMatrix<double>> solver;
row_t1 = solverFunc(A, b);
concentrations_t1.row(i) = row_t1;
}
concentrations_t1.transposeInPlace();
concentrations.transposeInPlace();
alphaX.transposeInPlace();
alphaY.transposeInPlace();
#pragma omp parallel for num_threads(numThreads) private(A, b, row_t1)
for (int i = 0; i < colMax; i++) {
// swap alphas, boundary conditions and sx/sy for column-wise calculation
A = createCoeffMatrix(alphaY, bcTop, bcBottom, rowMax, i, sy);
b = createSolutionVector(concentrations_t1, alphaY, alphaX, bcTop, bcBottom,
bcLeft, bcRight, rowMax, i, sy, sx);
row_t1 = solverFunc(A, b);
concentrations.row(i) = row_t1;
}
concentrations.transposeInPlace();
grid.setConcentrations(concentrations);
}
// entry point for EigenLU solver; differentiate between 1D and 2D grid
static void BTCS_LU(Grid &grid, Boundary &bc, double timestep, int numThreads) {
if (grid.getDim() == 1) {
BTCS_1D(grid, bc, timestep, EigenLUAlgorithm);
} else if (grid.getDim() == 2) {
BTCS_2D(grid, bc, timestep, EigenLUAlgorithm, numThreads);
} else {
throw_invalid_argument("Error: Only 1- and 2-dimensional grids are defined!");
}
}
// entry point for Thomas algorithm solver; differentiate 1D and 2D grid
static void BTCS_Thomas(Grid &grid, Boundary &bc, double timestep, int numThreads) {
if (grid.getDim() == 1) {
BTCS_1D(grid, bc, timestep, ThomasAlgorithm);
} else if (grid.getDim() == 2) {
BTCS_2D(grid, bc, timestep, ThomasAlgorithm, numThreads);
} else {
throw_invalid_argument("Error: Only 1- and 2-dimensional grids are defined!");
}
}

View File

@ -1,162 +0,0 @@
#include "TugUtils.cpp"
#include <iostream>
#include <omp.h>
#include <tug/Boundary.hpp>
#include <stdexcept>
using namespace std;
BoundaryElement::BoundaryElement() {
this->type = BC_TYPE_CLOSED;
this->value = -1; // without meaning in closed case
}
BoundaryElement::BoundaryElement(double value) {
this->type = BC_TYPE_CONSTANT;
this->value = value;
}
void BoundaryElement::setType(BC_TYPE type) {
this->type = type;
}
void BoundaryElement::setValue(double value) {
if(value < 0){
throw_invalid_argument("No negative concentration allowed.");
}
if(type == BC_TYPE_CLOSED){
throw_invalid_argument(
"No constant boundary concentrations can be set for closed "
"boundaries. Please change type first.");
}
this->value = value;
}
BC_TYPE BoundaryElement::getType() {
return this->type;
}
double BoundaryElement::getValue() {
return this->value;
}
Boundary::Boundary(Grid grid) : grid(grid) {
if (grid.getDim() == 1) {
this->boundaries = vector<vector<BoundaryElement>>(2); // in 1D only left and right boundary
this->boundaries[BC_SIDE_LEFT].push_back(BoundaryElement());
this->boundaries[BC_SIDE_RIGHT].push_back(BoundaryElement());
} else if (grid.getDim() == 2) {
this->boundaries = vector<vector<BoundaryElement>>(4);
this->boundaries[BC_SIDE_LEFT] = vector<BoundaryElement>(grid.getRow(), BoundaryElement());
this->boundaries[BC_SIDE_RIGHT] = vector<BoundaryElement>(grid.getRow(), BoundaryElement());
this->boundaries[BC_SIDE_TOP] = vector<BoundaryElement>(grid.getCol(), BoundaryElement());
this->boundaries[BC_SIDE_BOTTOM] = vector<BoundaryElement>(grid.getCol(), BoundaryElement());
}
}
void Boundary::setBoundarySideClosed(BC_SIDE side) {
if(grid.getDim() == 1){
if((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)){
throw_invalid_argument(
"For the one-dimensional case, only the BC_SIDE_LEFT and "
"BC_SIDE_RIGHT borders exist.");
}
}
int n;
if (side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT) {
n = grid.getRow();
} else {
n = grid.getCol();
}
this->boundaries[side] = vector<BoundaryElement>(n, BoundaryElement());
}
void Boundary::setBoundarySideConstant(BC_SIDE side, double value) {
if(grid.getDim() == 1){
if((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)){
throw_invalid_argument(
"For the one-dimensional case, only the BC_SIDE_LEFT and "
"BC_SIDE_RIGHT borders exist.");
}
}
int n;
if (side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT) {
n = grid.getRow();
} else {
n = grid.getCol();
}
this->boundaries[side] = vector<BoundaryElement>(n, BoundaryElement(value));
}
void Boundary::setBoundaryElementClosed(BC_SIDE side, int index) {
// tests whether the index really points to an element of the boundary side.
if((boundaries[side].size() < index) || index < 0){
throw_invalid_argument("Index is selected either too large or too small.");
}
this->boundaries[side][index].setType(BC_TYPE_CLOSED);
}
void Boundary::setBoundaryElementConstant(BC_SIDE side, int index, double value) {
// tests whether the index really points to an element of the boundary side.
if((boundaries[side].size() < index) || index < 0){
throw_invalid_argument("Index is selected either too large or too small.");
}
this->boundaries[side][index].setType(BC_TYPE_CONSTANT);
this->boundaries[side][index].setValue(value);
}
const vector<BoundaryElement> Boundary::getBoundarySide(BC_SIDE side) {
if(grid.getDim() == 1){
if((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)){
throw_invalid_argument(
"For the one-dimensional trap, only the BC_SIDE_LEFT and "
"BC_SIDE_RIGHT borders exist.");
}
}
return this->boundaries[side];
}
VectorXd Boundary::getBoundarySideValues(BC_SIDE side) {
int length = boundaries[side].size();
VectorXd 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;
}
BoundaryElement Boundary::getBoundaryElement(BC_SIDE side, int index) {
if((boundaries[side].size() < index) || index < 0){
throw_invalid_argument("Index is selected either too large or too small.");
}
return this->boundaries[side][index];
}
BC_TYPE Boundary::getBoundaryElementType(BC_SIDE side, int index) {
if((boundaries[side].size() < index) || index < 0){
throw_invalid_argument("Index is selected either too large or too small.");
}
return this->boundaries[side][index].getType();
}
double Boundary::getBoundaryElementValue(BC_SIDE side, int index) {
if((boundaries[side].size() < index) || index < 0){
throw_invalid_argument("Index is selected either too large or too small.");
}
if(boundaries[side][index].getType() != BC_TYPE_CONSTANT){
throw_invalid_argument("A value can only be output if it is a constant boundary condition.");
}
return this->boundaries[side][index].getValue();
}

View File

@ -1,9 +0,0 @@
add_library(tug Boundary.cpp Grid.cpp Simulation.cpp FTCS.cpp BTCSv2.cpp)
target_link_libraries(tug Eigen3::Eigen)
if(TUG_USE_OPENMP AND OpenMP_CXX_FOUND)
target_link_libraries(tug OpenMP::OpenMP_CXX)
endif()
target_include_directories(tug PUBLIC ../include)

View File

@ -1,433 +0,0 @@
/**
* @file FTCS.cpp
* @brief Implementation of heterogenous FTCS (forward time-centered space) solution
* of diffusion equation in 1D and 2D space.
*
*/
#include "TugUtils.cpp"
#include <cstddef>
#include <tug/Boundary.hpp>
#include <iostream>
#include <omp.h>
using namespace std;
// calculates horizontal change on one cell independent of boundary type
static double calcHorizontalChange(Grid &grid, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaX()(row,col+1), grid.getAlphaX()(row,col))
* grid.getConcentrations()(row,col+1)
- (
calcAlphaIntercell(grid.getAlphaX()(row,col+1), grid.getAlphaX()(row,col))
+ calcAlphaIntercell(grid.getAlphaX()(row,col-1), grid.getAlphaX()(row,col))
)
* grid.getConcentrations()(row,col)
+ calcAlphaIntercell(grid.getAlphaX()(row,col-1), grid.getAlphaX()(row,col))
* grid.getConcentrations()(row,col-1);
return result;
}
// calculates vertical change on one cell independent of boundary type
static double calcVerticalChange(Grid &grid, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaY()(row+1,col), grid.getAlphaY()(row,col))
* grid.getConcentrations()(row+1,col)
- (
calcAlphaIntercell(grid.getAlphaY()(row+1,col), grid.getAlphaY()(row,col))
+ calcAlphaIntercell(grid.getAlphaY()(row-1,col), grid.getAlphaY()(row,col))
)
* grid.getConcentrations()(row,col)
+ calcAlphaIntercell(grid.getAlphaY()(row-1,col), grid.getAlphaY()(row,col))
* grid.getConcentrations()(row-1,col);
return result;
}
// calculates horizontal change on one cell with a constant left boundary
static double calcHorizontalChangeLeftBoundaryConstant(Grid &grid, Boundary &bc, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaX()(row,col+1), grid.getAlphaX()(row,col))
* grid.getConcentrations()(row,col+1)
- (
calcAlphaIntercell(grid.getAlphaX()(row,col+1), grid.getAlphaX()(row,col))
+ 2 * grid.getAlphaX()(row,col)
)
* grid.getConcentrations()(row,col)
+ 2 * grid.getAlphaX()(row,col) * bc.getBoundaryElementValue(BC_SIDE_LEFT, row);
return result;
}
// calculates horizontal change on one cell with a closed left boundary
static double calcHorizontalChangeLeftBoundaryClosed(Grid &grid, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaX()(row, col+1), grid.getAlphaX()(row, col))
* (grid.getConcentrations()(row, col+1) - grid.getConcentrations()(row, col));
return result;
}
// checks boundary condition type for a cell on the left edge of grid
static double calcHorizontalChangeLeftBoundary(Grid &grid, Boundary &bc, int &row, int &col) {
if (bc.getBoundaryElementType(BC_SIDE_LEFT, col) == BC_TYPE_CONSTANT) {
return calcHorizontalChangeLeftBoundaryConstant(grid, bc, row, col);
} else if (bc.getBoundaryElementType(BC_SIDE_LEFT, col) == BC_TYPE_CLOSED) {
return calcHorizontalChangeLeftBoundaryClosed(grid, row, col);
} else {
throw_invalid_argument("Undefined Boundary Condition Type!");
}
}
// calculates horizontal change on one cell with a constant right boundary
static double calcHorizontalChangeRightBoundaryConstant(Grid &grid, Boundary &bc, int &row, int &col) {
double result =
2 * grid.getAlphaX()(row,col) * bc.getBoundaryElementValue(BC_SIDE_RIGHT, row)
- (
calcAlphaIntercell(grid.getAlphaX()(row,col-1), grid.getAlphaX()(row,col))
+ 2 * grid.getAlphaX()(row,col)
)
* grid.getConcentrations()(row,col)
+ calcAlphaIntercell(grid.getAlphaX()(row,col-1), grid.getAlphaX()(row,col))
* grid.getConcentrations()(row,col-1);
return result;
}
// calculates horizontal change on one cell with a closed right boundary
static double calcHorizontalChangeRightBoundaryClosed(Grid &grid, int &row, int &col) {
double result =
- (calcAlphaIntercell(grid.getAlphaX()(row, col-1), grid.getAlphaX()(row, col))
* (grid.getConcentrations()(row, col) - grid.getConcentrations()(row, col-1)));
return result;
}
// checks boundary condition type for a cell on the right edge of grid
static double calcHorizontalChangeRightBoundary(Grid &grid, Boundary &bc, int &row, int &col) {
if (bc.getBoundaryElementType(BC_SIDE_RIGHT, col) == BC_TYPE_CONSTANT) {
return calcHorizontalChangeRightBoundaryConstant(grid, bc, row, col);
} else if (bc.getBoundaryElementType(BC_SIDE_RIGHT, col) == BC_TYPE_CLOSED) {
return calcHorizontalChangeRightBoundaryClosed(grid, row, col);
} else {
throw_invalid_argument("Undefined Boundary Condition Type!");
}
}
// calculates vertical change on one cell with a constant top boundary
static double calcVerticalChangeTopBoundaryConstant(Grid &grid, Boundary &bc, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaY()(row+1, col), grid.getAlphaY()(row, col))
* grid.getConcentrations()(row+1,col)
- (
calcAlphaIntercell(grid.getAlphaY()(row+1, col), grid.getAlphaY()(row, col))
+ 2 * grid.getAlphaY()(row, col)
)
* grid.getConcentrations()(row, col)
+ 2 * grid.getAlphaY()(row, col) * bc.getBoundaryElementValue(BC_SIDE_TOP, col);
return result;
}
// calculates vertical change on one cell with a closed top boundary
static double calcVerticalChangeTopBoundaryClosed(Grid &grid, int &row, int &col) {
double result =
calcAlphaIntercell(grid.getAlphaY()(row+1, col), grid.getConcentrations()(row, col))
* (grid.getConcentrations()(row+1, col) - grid.getConcentrations()(row, col));
return result;
}
// checks boundary condition type for a cell on the top edge of grid
static double calcVerticalChangeTopBoundary(Grid &grid, Boundary &bc, int &row, int &col) {
if (bc.getBoundaryElementType(BC_SIDE_TOP, col) == BC_TYPE_CONSTANT) {
return calcVerticalChangeTopBoundaryConstant(grid, bc, row, col);
} else if (bc.getBoundaryElementType(BC_SIDE_TOP, col) == BC_TYPE_CLOSED) {
return calcVerticalChangeTopBoundaryClosed(grid, row, col);
} else {
throw_invalid_argument("Undefined Boundary Condition Type!");
}
}
// calculates vertical change on one cell with a constant bottom boundary
static double calcVerticalChangeBottomBoundaryConstant(Grid &grid, Boundary &bc, int &row, int &col) {
double result =
2 * grid.getAlphaY()(row, col) * bc.getBoundaryElementValue(BC_SIDE_BOTTOM, col)
- (
calcAlphaIntercell(grid.getAlphaY()(row, col), grid.getAlphaY()(row-1, col))
+ 2 * grid.getAlphaY()(row, col)
)
* grid.getConcentrations()(row, col)
+ calcAlphaIntercell(grid.getAlphaY()(row, col), grid.getAlphaY()(row-1, col))
* grid.getConcentrations()(row-1,col);
return result;
}
// calculates vertical change on one cell with a closed bottom boundary
static double calcVerticalChangeBottomBoundaryClosed(Grid &grid, int &row, int &col) {
double result =
- (calcAlphaIntercell(grid.getAlphaY()(row, col), grid.getAlphaY()(row-1, col))
* (grid.getConcentrations()(row, col) - grid.getConcentrations()(row-1, col)));
return result;
}
// checks boundary condition type for a cell on the bottom edge of grid
static double calcVerticalChangeBottomBoundary(Grid &grid, Boundary &bc, int &row, int &col) {
if (bc.getBoundaryElementType(BC_SIDE_BOTTOM, col) == BC_TYPE_CONSTANT) {
return calcVerticalChangeBottomBoundaryConstant(grid, bc, row, col);
} else if (bc.getBoundaryElementType(BC_SIDE_BOTTOM, col) == BC_TYPE_CLOSED) {
return calcVerticalChangeBottomBoundaryClosed(grid, row, col);
} else {
throw_invalid_argument("Undefined Boundary Condition Type!");
}
}
// FTCS solution for 1D grid
static void FTCS_1D(Grid &grid, Boundary &bc, double &timestep) {
int colMax = grid.getCol();
double deltaCol = grid.getDeltaCol();
// matrix for concentrations at time t+1
MatrixXd concentrations_t1 = MatrixXd::Constant(1, colMax, 0);
// 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++) {
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChange(grid, row, col)
)
;
}
// left boundary; hold column constant at index 0
int col = 0;
concentrations_t1(row, col) = grid.getConcentrations()(row,col)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChangeLeftBoundary(grid, bc, row, col)
)
;
// right boundary; hold column constant at max index
col = colMax-1;
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChangeRightBoundary(grid, bc, row, col)
)
;
// overwrite obsolete concentrations
grid.setConcentrations(concentrations_t1);
}
// FTCS solution for 2D grid
static void FTCS_2D(Grid &grid, Boundary &bc, double &timestep, int numThreads) {
int rowMax = grid.getRow();
int colMax = grid.getCol();
double deltaRow = grid.getDeltaRow();
double deltaCol = grid.getDeltaCol();
// matrix for concentrations at time t+1
MatrixXd concentrations_t1 = MatrixXd::Constant(rowMax, colMax, 0);
// inner cells
// these are independent of the boundary condition type
// omp_set_num_threads(10);
#pragma omp parallel for num_threads(numThreads)
for (int row = 1; row < rowMax-1; row++) {
for (int col = 1; col < colMax-1; col++) {
concentrations_t1(row, col) = grid.getConcentrations()(row, col)
+ timestep / (deltaRow*deltaRow)
* (
calcVerticalChange(grid, row, col)
)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChange(grid, row, col)
)
;
}
}
// boundary conditions
// left without corners / looping over rows
// hold column constant at index 0
int col = 0;
#pragma omp parallel for num_threads(numThreads)
for (int row = 1; row < rowMax-1; row++) {
concentrations_t1(row, col) = grid.getConcentrations()(row,col)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChangeLeftBoundary(grid, bc, row, col)
)
+ timestep / (deltaRow*deltaRow)
* (
calcVerticalChange(grid, row, col)
)
;
}
// right without corners / looping over rows
// hold column constant at max index
col = colMax-1;
#pragma omp parallel for num_threads(numThreads)
for (int row = 1; row < rowMax-1; row++) {
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChangeRightBoundary(grid, bc, row, col)
)
+ timestep / (deltaRow*deltaRow)
* (
calcVerticalChange(grid, row, col)
)
;
}
// top without corners / looping over columns
// hold row constant at index 0
int row = 0;
#pragma omp parallel for num_threads(numThreads)
for (int col=1; col<colMax-1;col++){
concentrations_t1(row, col) = grid.getConcentrations()(row, col)
+ timestep / (deltaRow*deltaRow)
* (
calcVerticalChangeTopBoundary(grid, bc, row, col)
)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChange(grid, row, col)
)
;
}
// bottom without corners / looping over columns
// hold row constant at max index
row = rowMax-1;
#pragma omp parallel for num_threads(numThreads)
for(int col=1; col<colMax-1;col++){
concentrations_t1(row, col) = grid.getConcentrations()(row, col)
+ timestep / (deltaRow*deltaRow)
* (
calcVerticalChangeBottomBoundary(grid, bc, row, col)
)
+ timestep / (deltaCol*deltaCol)
* (
calcHorizontalChange(grid, row, col)
)
;
}
// corner top left
// hold row and column constant at 0
row = 0;
col = 0;
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep/(deltaCol*deltaCol)
* (
calcHorizontalChangeLeftBoundary(grid, bc, row, col)
)
+ timestep/(deltaRow*deltaRow)
* (
calcVerticalChangeTopBoundary(grid, bc, row, col)
)
;
// corner top right
// hold row constant at 0 and column constant at max index
row = 0;
col = colMax-1;
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep/(deltaCol*deltaCol)
* (
calcHorizontalChangeRightBoundary(grid, bc, row, col)
)
+ timestep/(deltaRow*deltaRow)
* (
calcVerticalChangeTopBoundary(grid, bc, row, col)
)
;
// corner bottom left
// hold row constant at max index and column constant at 0
row = rowMax-1;
col = 0;
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep/(deltaCol*deltaCol)
* (
calcHorizontalChangeLeftBoundary(grid, bc, row, col)
)
+ timestep/(deltaRow*deltaRow)
* (
calcVerticalChangeBottomBoundary(grid, bc, row, col)
)
;
// corner bottom right
// hold row and column constant at max index
row = rowMax-1;
col = colMax-1;
concentrations_t1(row,col) = grid.getConcentrations()(row,col)
+ timestep/(deltaCol*deltaCol)
* (
calcHorizontalChangeRightBoundary(grid, bc, row, col)
)
+ timestep/(deltaRow*deltaRow)
* (
calcVerticalChangeBottomBoundary(grid, bc, row, col)
)
;
// overwrite obsolete concentrations
grid.setConcentrations(concentrations_t1);
// }
}
// entry point; differentiate between 1D and 2D grid
static void FTCS(Grid &grid, Boundary &bc, double &timestep, int &numThreads) {
if (grid.getDim() == 1) {
FTCS_1D(grid, bc, timestep);
} else if (grid.getDim() == 2) {
FTCS_2D(grid, bc, timestep, numThreads);
} else {
throw_invalid_argument("Error: Only 1- and 2-dimensional grids are defined!");
}
}

View File

@ -1,164 +0,0 @@
#include "TugUtils.cpp"
#include <tug/Grid.hpp>
#include <iostream>
Grid::Grid(int length) {
if (length <= 3) {
throw_invalid_argument("Given grid length too small. Must be greater than 3.");
}
this->row = 1;
this->col = length;
this->domainCol = length; // default: same size as length
this->deltaCol = double(this->domainCol)/double(this->col); // -> 1
this->dim = 1;
this->concentrations = MatrixXd::Constant(1, col, 20);
this->alphaX = MatrixXd::Constant(1, col, 1);
}
Grid::Grid(int row, int col) {
if (row <= 3 || col <= 3) {
throw_invalid_argument("Given grid dimensions too small. Must each be greater than 3.");
}
this->row = row;
this->col = col;
this->domainRow = row; // default: same size as row
this->domainCol = col; // default: same size as col
this->deltaRow = double(this->domainRow)/double(this->row); // -> 1
this->deltaCol = double(this->domainCol)/double(this->col); // -> 1
this->dim = 2;
this->concentrations = MatrixXd::Constant(row, col, 20);
this->alphaX = MatrixXd::Constant(row, col, 1);
this->alphaY = MatrixXd::Constant(row, col, 1);
}
void Grid::setConcentrations(MatrixXd concentrations) {
if (concentrations.rows() != this->row || concentrations.cols() != this->col) {
throw_invalid_argument("Given matrix of concentrations mismatch with Grid dimensions!");
}
this->concentrations = concentrations;
}
const MatrixXd Grid::getConcentrations() {
return this->concentrations;
}
void Grid::setAlpha(MatrixXd alpha) {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probably use 2D setter function!");
}
if (alpha.rows() != 1 || alpha.cols() != this->col) {
throw_invalid_argument("Given matrix of alpha coefficients mismatch with Grid dimensions!");
}
this->alphaX = alpha;
}
void Grid::setAlpha(MatrixXd alphaX, MatrixXd alphaY) {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use 1D setter function!");
}
if (alphaX.rows() != this->row || alphaX.cols() != this->col) {
throw_invalid_argument("Given matrix of alpha coefficients in x-direction mismatch with GRid dimensions!");
}
if (alphaY.rows() != this->row || alphaY.cols() != this->col) {
throw_invalid_argument("Given matrix of alpha coefficients in y-direction mismatch with GRid dimensions!");
}
this->alphaX = alphaX;
this->alphaY = alphaY;
}
const MatrixXd Grid::getAlpha() {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probably use either getAlphaX() or getAlphaY()!");
}
return this->alphaX;
}
const MatrixXd Grid::getAlphaX() {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use getAlpha()!");
}
return this->alphaX;
}
const MatrixXd Grid::getAlphaY() {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use getAlpha()!");
}
return this->alphaY;
}
int Grid::getDim() {
return dim;
}
int Grid::getLength() {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probably use getRow() or getCol()!");
}
return col;
}
int Grid::getRow() {
return row;
}
int Grid::getCol() {
return col;
}
void Grid::setDomain(double domainLength) {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probaly use the 2D domain setter!");
}
if (domainLength <= 0) {
throw_invalid_argument("Given domain length is not positive!");
}
this->domainCol = domainLength;
this->deltaCol = double(this->domainCol)/double(this->col);
}
void Grid::setDomain(double domainRow, double domainCol) {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use the 1D domain setter!");
}
if (domainRow <= 0 || domainCol <= 0) {
throw_invalid_argument("Given domain size is not positive!");
}
this->domainRow = domainRow;
this->domainCol = domainCol;
this->deltaRow = double(this->domainRow)/double(this->row);
this->deltaCol = double(this->domainCol)/double(this->col);
}
double Grid::getDelta() {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probably use the 2D delta getters");
}
return this->deltaCol;
}
double Grid::getDeltaCol() {
return this->deltaCol;
}
double Grid::getDeltaRow() {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, meaning there is no delta in y-direction!");
}
return this->deltaRow;
}

View File

@ -1,36 +0,0 @@
<h1>src-Directory</h1>
This is the src-directory that holds the source code to the implementation of the TUG framework.
<pre>
src/
├── CMakeFiles/
├── Boundary.cpp
├── BTCSv2.cpp
├── CMakeLists.txt
├── FTCS.cpp
├── Grid.cpp
├── README.md
├── Simulation.cpp
└── TugUtils.hpp
</pre>
**src/** Directory with the source code.
**CMakeFiles/** Automatically generated directory by CMake.
**Boundary.cpp** Implementation of Boundary class, that holds the boundary conditions.
**BTCSv2.cpp** Implementation of BTCS solution to heterogeneous diffusion in 1D and 2D.
**CMakeLists.txt** CMakeLists for this directory.
**FTCS.cpp** Implementation of FTCS solution to heterogeneous diffusion in 1D and 2D.
**Grid.cpp** Implementation of Grid class, that holds all of the concentrations alpha coefficient in x- and y-direction.
**README.md** <i>This</i> file.
**Simulation.cpp** Implementation of Simulation class, that holds all of the information for a specific simulation run, as well as the Boundary and Grid objects.
**TugUtils.hpp** Helper functions for other source files.

View File

@ -1,331 +0,0 @@
#include <cmath>
#include <cstddef>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <tug/Simulation.hpp>
#include <omp.h>
#include <fstream>
#ifndef SIMULATION_H_
#define SIMULATION_H_
#include "BTCSv2.cpp"
using namespace std;
Simulation::Simulation(Grid &grid, Boundary &bc, APPROACH approach) : grid(grid), bc(bc) {
this->approach = approach;
this->solver = THOMAS_ALGORITHM_SOLVER;
this->timestep = -1; // error per default; needs to be set
this->iterations = -1;
this->innerIterations = 1;
this->numThreads = omp_get_num_procs()-1;
this->csv_output = CSV_OUTPUT_OFF;
this->console_output = CONSOLE_OUTPUT_OFF;
this->time_measure = TIME_MEASURE_OFF;
}
void Simulation::setOutputCSV(CSV_OUTPUT csv_output) {
if (csv_output < CSV_OUTPUT_OFF && csv_output > CSV_OUTPUT_VERBOSE) {
throw_invalid_argument("Invalid CSV output option given!");
}
this->csv_output = csv_output;
}
void Simulation::setOutputConsole(CONSOLE_OUTPUT console_output) {
if (console_output < CONSOLE_OUTPUT_OFF && console_output > CONSOLE_OUTPUT_VERBOSE) {
throw_invalid_argument("Invalid console output option given!");
}
this->console_output = console_output;
}
void Simulation::setTimeMeasure(TIME_MEASURE time_measure) {
if (time_measure < TIME_MEASURE_OFF && time_measure > TIME_MEASURE_ON) {
throw_invalid_argument("Invalid time measure option given!");
}
this->time_measure = time_measure;
}
void Simulation::setTimestep(double timestep) {
if(timestep <= 0){
throw_invalid_argument("Timestep has to be greater than zero.");
}
if (approach == FTCS_APPROACH || approach == CRANK_NICOLSON_APPROACH) {
double deltaRowSquare;
double deltaColSquare = grid.getDeltaCol() * grid.getDeltaCol();
double minDeltaSquare;
double maxAlphaX, maxAlphaY, maxAlpha;
string dim;
if (grid.getDim() == 2) {
dim = "2D";
deltaRowSquare = grid.getDeltaRow() * grid.getDeltaRow();
minDeltaSquare = (deltaRowSquare < deltaColSquare) ? deltaRowSquare : deltaColSquare;
maxAlphaX = grid.getAlphaX().maxCoeff();
maxAlphaY = grid.getAlphaY().maxCoeff();
maxAlpha = (maxAlphaX > maxAlphaY) ? maxAlphaX : maxAlphaY;
} else if (grid.getDim() == 1) {
dim = "1D";
minDeltaSquare = deltaColSquare;
maxAlpha = grid.getAlpha().maxCoeff();
} else {
throw_invalid_argument("Critical error: Undefined number of dimensions!");
}
// Courant-Friedrichs-Lewy condition
double cfl = minDeltaSquare / (4*maxAlpha);
// stability equation from Wikipedia; might be useful if applied cfl does not work in some cases
// double CFL_Wiki = 1 / (4 * maxAlpha * ((1/deltaRowSquare) + (1/deltaColSquare)));
string approachPrefix = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
cout << approachPrefix << "_" << dim << " :: CFL condition: " << cfl << endl;
cout << approachPrefix << "_" << dim << " :: required dt=" << timestep << endl;
if (timestep > cfl) {
this->innerIterations = (int)ceil(timestep / cfl);
this->timestep = timestep / (double)innerIterations;
cerr << "Warning :: Timestep was adjusted, because of stability "
"conditions. Time duration was approximately preserved by "
"adjusting internal number of iterations."
<< endl;
cout << approachPrefix << "_" << dim << " :: Required " << this->innerIterations
<< " inner iterations with dt=" << this->timestep << endl;
} else {
this->timestep = timestep;
cout << approachPrefix << "_" << dim << " :: No inner iterations required, dt=" << timestep << endl;
}
} else {
this->timestep = timestep;
}
}
double Simulation::getTimestep() {
return this->timestep;
}
void Simulation::setIterations(int iterations) {
if(iterations <= 0){
throw_invalid_argument("Number of iterations must be greater than zero.");
}
this->iterations = iterations;
}
void Simulation::setSolver(SOLVER solver) {
if (this->approach == FTCS_APPROACH) {
cerr << "Warning: Solver was set, but FTCS approach initialized. Setting the solver "
"is thus without effect."
<< endl;
}
this->solver = solver;
}
void Simulation::setNumberThreads(int numThreads){
if(numThreads>0 && numThreads<=omp_get_num_procs()){
this->numThreads=numThreads;
}
else{
int maxThreadNumber = omp_get_num_procs();
string outputMessage = "Number of threads exceeds the number of processor cores ("
+ to_string(maxThreadNumber) + ") or is less than 1.";
throw_invalid_argument(outputMessage);
}
}
int Simulation::getIterations() {
return this->iterations;
}
void Simulation::printConcentrationsConsole() {
cout << grid.getConcentrations() << endl;
cout << endl;
}
string Simulation::createCSVfile() {
ofstream file;
int appendIdent = 0;
string appendIdentString;
// string approachString = (approach == 0) ? "FTCS" : "BTCS";
string approachString = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
string row = to_string(grid.getRow());
string col = to_string(grid.getCol());
string numIterations = to_string(iterations);
string filename = approachString + "_" + row + "_" + col + "_" + numIterations + ".csv";
while (filesystem::exists(filename)) {
appendIdent += 1;
appendIdentString = 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 (csv_output == CSV_OUTPUT_XTREME) {
IOFormat one_row(StreamPrecision, DontAlignCols, "", " ");
file << bc.getBoundarySideValues(BC_SIDE_LEFT).format(one_row) << endl; // boundary left
file << bc.getBoundarySideValues(BC_SIDE_RIGHT).format(one_row) << endl; // boundary right
file << bc.getBoundarySideValues(BC_SIDE_TOP).format(one_row) << endl; // boundary top
file << bc.getBoundarySideValues(BC_SIDE_BOTTOM).format(one_row) << endl; // boundary bottom
file << endl << endl;
}
file.close();
return filename;
}
void Simulation::printConcentrationsCSV(string filename) {
ofstream file;
file.open(filename, std::ios_base::app);
if (!file) {
exit(1);
}
IOFormat do_not_align(StreamPrecision, DontAlignCols);
file << grid.getConcentrations().format(do_not_align) << endl;
file << endl << endl;
file.close();
}
void Simulation::run() {
if (this->timestep == -1) {
throw_invalid_argument("Timestep is not set!");
}
if (this->iterations == -1) {
throw_invalid_argument("Number of iterations are not set!");
}
string filename;
if (this->console_output > CONSOLE_OUTPUT_OFF) {
printConcentrationsConsole();
}
if (this->csv_output > CSV_OUTPUT_OFF) {
filename = createCSVfile();
}
auto begin = std::chrono::high_resolution_clock::now();
if (approach == FTCS_APPROACH) { // FTCS case
for (int i = 0; i < iterations * innerIterations; i++) {
if (console_output == CONSOLE_OUTPUT_VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (csv_output >= CSV_OUTPUT_VERBOSE) {
printConcentrationsCSV(filename);
}
FTCS(this->grid, this->bc, this->timestep, 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 (approach == BTCS_APPROACH) { // BTCS case
if (solver == EIGEN_LU_SOLVER) {
for (int i = 0; i < iterations; i++) {
if (console_output == CONSOLE_OUTPUT_VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (csv_output >= CSV_OUTPUT_VERBOSE) {
printConcentrationsCSV(filename);
}
BTCS_LU(this->grid, this->bc, this->timestep, this->numThreads);
}
} else if (solver == THOMAS_ALGORITHM_SOLVER) {
for (int i = 0; i < iterations; i++) {
if (console_output == CONSOLE_OUTPUT_VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (csv_output >= CSV_OUTPUT_VERBOSE) {
printConcentrationsCSV(filename);
}
BTCS_Thomas(this->grid, this->bc, this->timestep, this->numThreads);
}
}
} else if (approach == CRANK_NICOLSON_APPROACH) { // Crank-Nicolson case
double 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
MatrixXd concentrations;
MatrixXd concentrationsFTCS;
MatrixXd concentrationsResult;
for (int i = 0; i < iterations * innerIterations; i++) {
if (console_output == CONSOLE_OUTPUT_VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (csv_output >= CSV_OUTPUT_VERBOSE) {
printConcentrationsCSV(filename);
}
concentrations = grid.getConcentrations();
FTCS(this->grid, this->bc, this->timestep, this->numThreads);
concentrationsFTCS = grid.getConcentrations();
grid.setConcentrations(concentrations);
BTCS_Thomas(this->grid, this->bc, this->timestep, this->numThreads);
concentrationsResult = beta * concentrationsFTCS + (1-beta) * grid.getConcentrations();
grid.setConcentrations(concentrationsResult);
}
}
auto end = std::chrono::high_resolution_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
if (this->console_output > CONSOLE_OUTPUT_OFF) {
printConcentrationsConsole();
}
if (this->csv_output > CSV_OUTPUT_OFF) {
printConcentrationsCSV(filename);
}
if (this->time_measure > TIME_MEASURE_OFF) {
string approachString = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
string dimString = (grid.getDim() == 1) ? "-1D" : "-2D";
cout << approachString << dimString << ":: run() finished in " << milliseconds.count() << "ms" << endl;
}
}
#endif

View File

@ -1,27 +1,28 @@
find_library(DOCTEST_LIB doctest)
include(FetchContent)
if(NOT DOCTEST_LIB)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
FetchContent_Declare(
DocTest
GIT_REPOSITORY https://github.com/doctest/doctest.git
GIT_TAG v2.4.9)
FetchContent_MakeAvailable(googletest)
FetchContent_MakeAvailable(DocTest)
endif()
add_executable(testTug setup.cpp testSimulation.cpp testGrid.cpp testFTCS.cpp)
target_link_libraries(testTug doctest tug)
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 CACHE)
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}")
add_custom_target(
check
COMMAND $<TARGET_FILE:testTug>
DEPENDS testTug)
target_include_directories(testTug PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/src")

View File

@ -1,47 +0,0 @@
#include <ios>
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <fstream>
#include <sstream>
#include <stdexcept>
using namespace std;
using namespace Eigen;
MatrixXd CSV2Eigen(string file2Convert) {
vector<double> matrixEntries;
ifstream matrixDataFile(file2Convert);
if (matrixDataFile.fail()) {
throw invalid_argument("File probably non-existent!");
}
string matrixRowString;
string matrixEntry;
int matrixRowNumber = 0;
while(getline(matrixDataFile, matrixRowString)){
stringstream matrixRowStringStream(matrixRowString);
while(getline(matrixRowStringStream, matrixEntry, ' ')){
matrixEntries.push_back(stod(matrixEntry));
}
if (matrixRowString.length() > 1) {
matrixRowNumber++;
}
}
return Map<Matrix<double, Dynamic, Dynamic, RowMajor>>(matrixEntries.data(), matrixRowNumber, matrixEntries.size() / matrixRowNumber);
}
bool checkSimilarity(MatrixXd a, MatrixXd b, double precision=1e-5) {
return a.isApprox(b, precision);
}
bool checkSimilarityV2(MatrixXd a, MatrixXd b, double maxDiff) {
MatrixXd diff = a - b;
double maxCoeff = diff.maxCoeff();
return abs(maxCoeff) < maxDiff;
}

51
test/TestUtils.hpp Normal file
View File

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

View File

@ -1,2 +1,7 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/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();
}

View File

@ -1,68 +1,102 @@
#include <stdio.h>
#include <doctest/doctest.h>
#include "gtest/gtest.h"
#include <stdexcept>
#include <tug/Boundary.hpp>
#include <string>
#include <typeinfo>
#include <iostream>
#include <utility>
#include <vector>
TEST_CASE("BoundaryElement"){
using namespace std;
using namespace tug;
SUBCASE("Closed case"){
BoundaryElement boundaryElementClosed = BoundaryElement();
CHECK_NOTHROW(BoundaryElement());
CHECK_EQ(boundaryElementClosed.getType(), BC_TYPE_CLOSED);
CHECK_EQ(isnan(boundaryElementClosed.getValue()), isnan(NAN));
CHECK_THROWS(boundaryElementClosed.setValue(0.2));
}
#include <gtest/gtest.h>
SUBCASE("Constant case"){
BoundaryElement boundaryElementConstant = BoundaryElement(0.1);
CHECK_NOTHROW(BoundaryElement(0.1));
CHECK_EQ(boundaryElementConstant.getType(), BC_TYPE_CONSTANT);
CHECK_EQ(boundaryElementConstant.getValue(), 0.1);
CHECK_NOTHROW(boundaryElementConstant.setValue(0.2));
CHECK_EQ(boundaryElementConstant.getValue(), 0.2);
}
#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);
}
TEST_CASE("Boundary Class"){
Grid grid1D = Grid(10);
Grid grid2D = Grid(10, 12);
Boundary boundary1D = Boundary(grid1D);
Boundary boundary2D = Boundary(grid2D);
vector<BoundaryElement> boundary1DVector(1, BoundaryElement(1.0));
BOUNDARY_TEST(Class) {
Boundary<double> boundary1D(10);
Boundary<double> boundary2D(10, 12);
vector<BoundaryElement<double>> boundary1DVector(1, BoundaryElement(1.0));
SUBCASE("Boundaries 1D case"){
CHECK_NOTHROW(Boundary boundary(grid1D));
CHECK_EQ(boundary1D.getBoundarySide(BC_SIDE_LEFT).size(), 1);
CHECK_EQ(boundary1D.getBoundarySide(BC_SIDE_RIGHT).size(), 1);
CHECK_EQ(boundary1D.getBoundaryElementType(BC_SIDE_LEFT, 0), BC_TYPE_CLOSED);
CHECK_THROWS(boundary1D.getBoundarySide(BC_SIDE_TOP));
CHECK_THROWS(boundary1D.getBoundarySide(BC_SIDE_BOTTOM));
CHECK_NOTHROW(boundary1D.setBoundarySideClosed(BC_SIDE_LEFT));
CHECK_THROWS(boundary1D.setBoundarySideClosed(BC_SIDE_TOP));
CHECK_NOTHROW(boundary1D.setBoundarySideConstant(BC_SIDE_LEFT, 1.0));
CHECK_EQ(boundary1D.getBoundaryElementValue(BC_SIDE_LEFT, 0), 1.0);
CHECK_THROWS(boundary1D.getBoundaryElementValue(BC_SIDE_LEFT, 2));
CHECK_EQ(boundary1D.getBoundaryElementType(BC_SIDE_LEFT, 0), BC_TYPE_CONSTANT);
CHECK_EQ(boundary1D.getBoundaryElement(BC_SIDE_LEFT, 0).getType(), boundary1DVector[0].getType());
}
constexpr double inner_condition_value = -5;
constexpr std::pair<bool, double> innerBoundary =
std::make_pair(true, inner_condition_value);
SUBCASE("Boundaries 2D case"){
CHECK_NOTHROW(Boundary boundary(grid1D));
CHECK_EQ(boundary2D.getBoundarySide(BC_SIDE_LEFT).size(), 10);
CHECK_EQ(boundary2D.getBoundarySide(BC_SIDE_RIGHT).size(), 10);
CHECK_EQ(boundary2D.getBoundarySide(BC_SIDE_TOP).size(), 12);
CHECK_EQ(boundary2D.getBoundarySide(BC_SIDE_BOTTOM).size(), 12);
CHECK_EQ(boundary2D.getBoundaryElementType(BC_SIDE_LEFT, 0), BC_TYPE_CLOSED);
CHECK_NOTHROW(boundary2D.getBoundarySide(BC_SIDE_TOP));
CHECK_NOTHROW(boundary2D.getBoundarySide(BC_SIDE_BOTTOM));
CHECK_NOTHROW(boundary2D.setBoundarySideClosed(BC_SIDE_LEFT));
CHECK_NOTHROW(boundary2D.setBoundarySideClosed(BC_SIDE_TOP));
CHECK_NOTHROW(boundary2D.setBoundarySideConstant(BC_SIDE_LEFT, 1.0));
CHECK_EQ(boundary2D.getBoundaryElementValue(BC_SIDE_LEFT, 0), 1.0);
CHECK_THROWS(boundary2D.getBoundaryElementValue(BC_SIDE_LEFT, 12));
CHECK_EQ(boundary2D.getBoundaryElementType(BC_SIDE_LEFT, 0), BC_TYPE_CONSTANT);
CHECK_EQ(boundary2D.getBoundaryElement(BC_SIDE_LEFT, 0).getType(), boundary1DVector[0].getType());
}
}
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);
}
}

245
test/testDiffusion.cpp Normal file
View File

@ -0,0 +1,245 @@
#include "TestUtils.hpp"
#include "tug/Core/Matrix.hpp"
#include "gtest/gtest.h"
#include <gtest/gtest.h>
#include <stdexcept>
#include <tug/Diffusion.hpp>
#include <Eigen/src/Core/Matrix.h>
#include <string>
// include the configured header file
#include <testSimulation.hpp>
#define DIFFUSION_TEST(x) TEST(Diffusion, x)
using namespace Eigen;
using namespace std;
using namespace tug;
constexpr int row = 11;
constexpr int col = 11;
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;
// Grid
// RowMajMat<double> concentrations = MatrixXd::Constant(row, col, 0);
concentrations(5, 5) = 1;
Diffusion<double, approach, solver> diffusiongrid(concentrations);
diffusiongrid.getConcentrationMatrix() = concentrations;
diffusiongrid.setDomain(domain_row, domain_col);
diffusiongrid.setTimestep(timestep);
diffusiongrid.setIterations(iterations);
diffusiongrid.setDomain(domain_row, domain_col);
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;
}
}
for (int i = 0; i < 5; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.001;
}
}
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());
}

View File

@ -1,19 +1,18 @@
#include <doctest/doctest.h>
#include <../src/FTCS.cpp>
#include <limits>
#include <gtest/gtest.h>
#include <tug/Core/TugUtils.hpp>
TEST_CASE("Maths") {
SUBCASE("mean between two alphas") {
double alpha1 = 10;
double alpha2 = 20;
double average = 15;
double harmonicMean = double(2) / ((double(1)/alpha1)+(double(1)/alpha2));
#include <gtest/gtest.h>
// double difference = std::fabs(calcAlphaIntercell(alpha1, alpha2) - harmonicMean);
// CHECK(difference < std::numeric_limits<double>::epsilon());
CHECK_EQ(calcAlphaIntercell(alpha1, alpha2), harmonicMean);
CHECK_EQ(calcAlphaIntercell(alpha1, alpha2, false), average);
}
TEST(FTCS, calcAlphaIntercell) {
double alpha1 = 10;
double alpha2 = 20;
double average = 15;
double harmonicMean =
double(2) / ((double(1) / alpha1) + (double(1) / alpha2));
}
// double difference = std::fabs(calcAlphaIntercell(alpha1, alpha2) -
// harmonicMean); CHECK(difference <
// std::numeric_limits<double>::epsilon());
EXPECT_DOUBLE_EQ(calcAlphaIntercell(alpha1, alpha2), harmonicMean);
EXPECT_DOUBLE_EQ(calcAlphaIntercell(alpha1, alpha2, false), average);
}

View File

@ -1,251 +0,0 @@
#include <doctest/doctest.h>
#include <tug/Grid.hpp>
#include <Eigen/Core>
TEST_CASE("1D Grid, too small length") {
int l = 2;
CHECK_THROWS(Grid(l));
}
TEST_CASE("2D Grid, too small side") {
int r = 2;
int c = 4;
CHECK_THROWS(Grid(r, c));
r = 4;
c = 2;
CHECK_THROWS(Grid(r, c));
}
TEST_CASE("1D Grid") {
int l = 12;
Grid grid(l);
SUBCASE("correct construction") {
CHECK_EQ(grid.getDim(), 1);
CHECK_EQ(grid.getLength(), l);
CHECK_EQ(grid.getCol(), l);
CHECK_EQ(grid.getRow(), 1);
CHECK_EQ(grid.getConcentrations().rows(), 1);
CHECK_EQ(grid.getConcentrations().cols(), l);
CHECK_EQ(grid.getAlpha().rows(), 1);
CHECK_EQ(grid.getAlpha().cols(), l);
CHECK_EQ(grid.getDeltaCol(), 1);
CHECK_THROWS(grid.getAlphaX());
CHECK_THROWS(grid.getAlphaY());
CHECK_THROWS(grid.getDeltaRow());
}
SUBCASE("setting concentrations") {
// correct concentrations matrix
MatrixXd concentrations = MatrixXd::Constant(1, l, 3);
CHECK_NOTHROW(grid.setConcentrations(concentrations));
// false concentrations matrix
MatrixXd wConcentrations = MatrixXd::Constant(2, l, 4);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
}
SUBCASE("setting alpha") {
// correct alpha matrix
MatrixXd alpha = MatrixXd::Constant(1, l, 3);
CHECK_NOTHROW(grid.setAlpha(alpha));
CHECK_THROWS(grid.setAlpha(alpha, alpha));
grid.setAlpha(alpha);
CHECK_EQ(grid.getAlpha(), alpha);
CHECK_THROWS(grid.getAlphaX());
CHECK_THROWS(grid.getAlphaY());
// false alpha matrix
MatrixXd wAlpha = MatrixXd::Constant(3, l, 2);
CHECK_THROWS(grid.setAlpha(wAlpha));
}
SUBCASE("setting domain") {
int d = 8;
// set 1D domain
CHECK_NOTHROW(grid.setDomain(d));
// set 2D domain
CHECK_THROWS(grid.setDomain(d, d));
grid.setDomain(d);
CHECK_EQ(grid.getDeltaCol(), double(d)/double(l));
CHECK_THROWS(grid.getDeltaRow());
// set too small domain
d = 0;
CHECK_THROWS(grid.setDomain(d));
d = -2;
CHECK_THROWS(grid.setDomain(d));
}
}
TEST_CASE("2D Grid quadratic") {
int rc = 12;
Grid grid(rc, rc);
SUBCASE("correct construction") {
CHECK_EQ(grid.getDim(), 2);
CHECK_THROWS(grid.getLength());
CHECK_EQ(grid.getCol(), rc);
CHECK_EQ(grid.getRow(), rc);
CHECK_EQ(grid.getConcentrations().rows(), rc);
CHECK_EQ(grid.getConcentrations().cols(), rc);
CHECK_THROWS(grid.getAlpha());
CHECK_EQ(grid.getAlphaX().rows(), rc);
CHECK_EQ(grid.getAlphaX().cols(), rc);
CHECK_EQ(grid.getAlphaY().rows(), rc);
CHECK_EQ(grid.getAlphaY().cols(), rc);
CHECK_EQ(grid.getDeltaRow(), 1);
CHECK_EQ(grid.getDeltaCol(), 1);
}
SUBCASE("setting concentrations") {
// correct concentrations matrix
MatrixXd concentrations = MatrixXd::Constant(rc, rc, 2);
CHECK_NOTHROW(grid.setConcentrations(concentrations));
// false concentrations matrix
MatrixXd wConcentrations = MatrixXd::Constant(rc, rc+3, 1);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
wConcentrations = MatrixXd::Constant(rc+3, rc, 4);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
wConcentrations = MatrixXd::Constant(rc+2, rc+4, 6);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
}
SUBCASE("setting alphas") {
// correct alpha matrices
MatrixXd alphax = MatrixXd::Constant(rc, rc, 2);
MatrixXd alphay = MatrixXd::Constant(rc, rc, 4);
CHECK_NOTHROW(grid.setAlpha(alphax, alphay));
CHECK_THROWS(grid.setAlpha(alphax));
grid.setAlpha(alphax, alphay);
CHECK_EQ(grid.getAlphaX(), alphax);
CHECK_EQ(grid.getAlphaY(), alphay);
CHECK_THROWS(grid.getAlpha());
// false alpha matrices
alphax = MatrixXd::Constant(rc+3, rc+1, 3);
CHECK_THROWS(grid.setAlpha(alphax, alphay));
alphay = MatrixXd::Constant(rc+2, rc+1, 3);
CHECK_THROWS(grid.setAlpha(alphax, alphay));
}
SUBCASE("setting domain") {
int dr = 8;
int dc = 9;
// set 1D domain
CHECK_THROWS(grid.setDomain(dr));
// set 2D domain
CHECK_NOTHROW(grid.setDomain(dr, dc));
grid.setDomain(dr, dc);
CHECK_EQ(grid.getDeltaCol(), double(dc)/double(rc));
CHECK_EQ(grid.getDeltaRow(), double(dr)/double(rc));
// set too small domain
dr = 0;
CHECK_THROWS(grid.setDomain(dr, dc));
dr = 8;
dc = 0;
CHECK_THROWS(grid.setDomain(dr, dc));
dr = -2;
CHECK_THROWS(grid.setDomain(dr, dc));
}
}
TEST_CASE("2D Grid non-quadratic") {
int r = 12;
int c = 15;
Grid grid(r, c);
SUBCASE("correct construction") {
CHECK_EQ(grid.getDim(), 2);
CHECK_THROWS(grid.getLength());
CHECK_EQ(grid.getCol(), c);
CHECK_EQ(grid.getRow(), r);
CHECK_EQ(grid.getConcentrations().rows(), r);
CHECK_EQ(grid.getConcentrations().cols(), c);
CHECK_THROWS(grid.getAlpha());
CHECK_EQ(grid.getAlphaX().rows(), r);
CHECK_EQ(grid.getAlphaX().cols(), c);
CHECK_EQ(grid.getAlphaY().rows(), r);
CHECK_EQ(grid.getAlphaY().cols(), c);
CHECK_EQ(grid.getDeltaRow(), 1);
CHECK_EQ(grid.getDeltaCol(), 1);
}
SUBCASE("setting concentrations") {
// correct concentrations matrix
MatrixXd concentrations = MatrixXd::Constant(r, c, 2);
CHECK_NOTHROW(grid.setConcentrations(concentrations));
// false concentrations matrix
MatrixXd wConcentrations = MatrixXd::Constant(r, c+3, 6);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
wConcentrations = MatrixXd::Constant(r+3, c, 3);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
wConcentrations = MatrixXd::Constant(r+2, c+4, 2);
CHECK_THROWS(grid.setConcentrations(wConcentrations));
}
SUBCASE("setting alphas") {
// correct alpha matrices
MatrixXd alphax = MatrixXd::Constant(r, c, 2);
MatrixXd alphay = MatrixXd::Constant(r, c, 4);
CHECK_NOTHROW(grid.setAlpha(alphax, alphay));
CHECK_THROWS(grid.setAlpha(alphax));
grid.setAlpha(alphax, alphay);
CHECK_EQ(grid.getAlphaX(), alphax);
CHECK_EQ(grid.getAlphaY(), alphay);
CHECK_THROWS(grid.getAlpha());
// false alpha matrices
alphax = MatrixXd::Constant(r+3, c+1, 3);
CHECK_THROWS(grid.setAlpha(alphax, alphay));
alphay = MatrixXd::Constant(r+2, c+1, 5);
CHECK_THROWS(grid.setAlpha(alphax, alphay));
}
SUBCASE("setting domain") {
int dr = 8;
int dc = 9;
// set 1D domain
CHECK_THROWS(grid.setDomain(dr));
// set 2D domain
CHECK_NOTHROW(grid.setDomain(dr, dc));
grid.setDomain(dr, dc);
CHECK_EQ(grid.getDeltaCol(), double(dc)/double(c));
CHECK_EQ(grid.getDeltaRow(), double(dr)/double(r));
// set too small domain
dr = 0;
CHECK_THROWS(grid.setDomain(dr, dc));
dr = 8;
dc = -1;
CHECK_THROWS(grid.setDomain(dr, dc));
dr = -2;
CHECK_THROWS(grid.setDomain(dr, dc));
}
}

View File

@ -1,110 +0,0 @@
#include <stdio.h>
#include <doctest/doctest.h>
#include <tug/Simulation.hpp>
#include "TestUtils.cpp"
#include <string>
// include the configured header file
#include <testSimulation.hpp>
static Grid setupSimulation(APPROACH approach, double timestep, int iterations) {
int row = 11;
int col = 11;
int domain_row = 10;
int domain_col = 10;
// Grid
Grid grid = Grid(row, col);
grid.setDomain(domain_row, domain_col);
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
concentrations(5,5) = 1;
grid.setConcentrations(concentrations);
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;
}
}
for (int i = 0; i < 5; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.001;
}
}
for (int i = 5; i < 11; i++) {
for (int j = 6; j < 11; j++) {
alpha(i, j) = 0.1;
}
}
grid.setAlpha(alpha, alpha);
// Boundary
Boundary bc = Boundary(grid);
// Simulation
Simulation sim = Simulation(grid, bc, approach);
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
sim.setTimestep(timestep);
sim.setIterations(iterations);
sim.run();
// RUN
return grid;
}
TEST_CASE("equality to reference matrix with FTCS") {
// set string from the header file
string test_path = testSimulationCSVDir;
MatrixXd reference = CSV2Eigen(test_path);
cout << "FTCS Test: " << endl;
Grid grid = setupSimulation(FTCS_APPROACH, 0.001, 7000);
cout << endl;
CHECK(checkSimilarity(reference, grid.getConcentrations(), 0.1) == true);
}
TEST_CASE("equality to reference matrix with BTCS") {
// set string from the header file
string test_path = testSimulationCSVDir;
MatrixXd reference = CSV2Eigen(test_path);
cout << "BTCS Test: " << endl;
Grid grid = setupSimulation(BTCS_APPROACH, 1, 7);
cout << endl;
CHECK(checkSimilarityV2(reference, grid.getConcentrations(), 0.01) == true);
}
TEST_CASE("Initialize environment"){
int rc = 12;
Grid grid(rc, rc);
Boundary boundary(grid);
CHECK_NOTHROW(Simulation sim(grid, boundary, BTCS_APPROACH));
}
TEST_CASE("Simulation environment"){
int rc = 12;
Grid grid(rc, rc);
Boundary boundary(grid);
Simulation sim(grid, boundary, FTCS_APPROACH);
SUBCASE("default paremeters") {
CHECK_EQ(sim.getIterations(), -1);
}
SUBCASE("set iterations") {
CHECK_NOTHROW(sim.setIterations(2000));
CHECK_EQ(sim.getIterations(), 2000);
CHECK_THROWS(sim.setIterations(-300));
}
SUBCASE("set timestep") {
CHECK_NOTHROW(sim.setTimestep(0.1));
CHECK_EQ(sim.getTimestep(), 0.1);
CHECK_THROWS(sim.setTimestep(-0.3));
}
}

View File

@ -1,4 +1,14 @@
FROM alpine
MAINTAINER Max Luebke <mluebke@uni-potsdam.de>
LABEL maintainer="Max Luebke <mluebke@uni-potsdam.de"
RUN apk add --no-cache build-base openmp cmake git eigen-dev
RUN git clone https://github.com/doctest/doctest.git /doctest \
&& cd /doctest \
&& mkdir build \
&& cd build \
&& cmake .. \
&& make install \
&& cd / \
&& rm -rf /doctest