Compare commits
127 Commits
hand-in-hp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c4aeee410 | ||
|
|
d5bfdf9724 | ||
|
|
1ad7ea0237 | ||
|
|
1a51dd5a1e | ||
|
|
605a31cc7c | ||
|
|
a562281187 | ||
|
|
256d6325d6 | ||
|
|
3e97e530bc | ||
|
|
04b42c4b89 | ||
|
|
d2f028122e | ||
|
|
19cc372b6a | ||
|
|
135830e030 | ||
|
|
520810bc3e | ||
|
|
b66feed2d2 | ||
|
|
b6ce5a32f4 | ||
|
|
4866977e72 | ||
|
|
bdc4f156de | ||
|
|
5b144aea3a | ||
|
|
3e270ee01c | ||
|
|
a20d5d53e6 | ||
|
|
3701dea34e | ||
|
|
30d2099d8a | ||
|
|
47ad909a9c | ||
|
|
91ae02cbf1 | ||
|
|
06b890fe81 | ||
|
|
c8d1b08e28 | ||
|
|
9ca0735654 | ||
| e1a135f8e2 | |||
|
|
13226e8668 | ||
|
|
56fc8185d5 | ||
|
|
a312abfe05 | ||
|
|
8fcc77bc60 | ||
|
|
3612dcf034 | ||
|
|
5c68f8b6b2 | ||
|
|
477d943bf0 | ||
|
|
d3843fb2a3 | ||
|
|
13f6556f54 | ||
|
|
a796acbc1d | ||
|
|
432621f227 | ||
|
|
636fcfaec3 | ||
|
|
bed888d1fc | ||
|
|
6981373deb | ||
|
|
a986242852 | ||
|
|
8d83eeef29 | ||
|
|
ac693caea9 | ||
|
|
74b46f111b | ||
|
|
c01d8e8607 | ||
|
|
00b0583504 | ||
|
|
f7dbf3abaf | ||
|
|
e64e8dfd5e | ||
|
|
449647010a | ||
|
|
5193f36e1f | ||
|
|
9d2c4b1485 | ||
|
|
f86836f637 | ||
|
|
b13337279f | ||
|
|
bd3cdde0fb | ||
|
|
7ae35dccf9 | ||
|
|
cb0c21eab9 | ||
|
|
2dc959b993 | ||
|
|
332f419faf | ||
|
|
b104fdcf52 | ||
|
|
f71bf2371f | ||
|
|
3ffa0ef624 | ||
|
|
8c0687a6cd | ||
|
|
1679dc84d2 | ||
|
|
eb14c53314 | ||
|
|
4328ef3436 | ||
|
|
4867261f9d | ||
|
|
2f737ce09e | ||
|
|
48000710c7 | ||
|
|
69690c0afa | ||
|
|
fbb0c08024 | ||
|
|
61d6cfc5cb | ||
|
|
f651c1d158 | ||
|
|
db1a2b2e5c | ||
|
|
8824dc1fd9 | ||
|
|
cdfc42ac9c | ||
|
|
e2bf3a7662 | ||
|
|
ee77b5f7f3 | ||
|
|
141028548b | ||
|
|
32d6a4e3ec | ||
|
|
97b43e1a16 | ||
|
|
39541a2054 | ||
|
|
5a39f5377e | ||
|
|
77914ea69f | ||
|
|
8456f2212d | ||
|
|
0471f3d8f9 | ||
|
|
8cfb61587d | ||
|
|
9a3fc67885 | ||
|
|
5196c36ec5 | ||
|
|
ba627b6624 | ||
|
|
46f9cef3a9 | ||
|
|
00cafb70dc | ||
|
|
5099fd23a9 | ||
|
|
8e5c1ad035 | ||
|
|
819db24e18 | ||
|
|
6c1ccb90fd | ||
|
|
ef1ccd4c14 | ||
|
|
a0d835e243 | ||
|
|
0eba63f875 | ||
|
|
edaad7cc04 | ||
|
|
f0d5220a48 | ||
|
|
a21023ec9d | ||
|
|
587bb5a622 | ||
|
|
c5979cd6f4 | ||
|
|
4b4c439c68 | ||
|
|
da6d004e16 | ||
|
|
61a4b0ae8a | ||
|
|
3106c2b8d5 | ||
|
|
8af03777b8 | ||
|
|
2096ee5cc3 | ||
|
|
2483019b89 | ||
|
|
ce09f0d8c8 | ||
|
|
b9c4474f5a | ||
|
|
d7608a7330 | ||
|
|
0d34752837 | ||
|
|
5490216bc0 | ||
|
|
42ad07c252 | ||
|
|
bf4444fc84 | ||
|
|
40710a0b39 | ||
|
|
f8bdfe39ea | ||
|
|
72107c944d | ||
|
|
fab0f35ed0 | ||
|
|
7d05320f24 | ||
|
|
6e388f3d99 | ||
|
|
c8163bb596 | ||
|
|
c7efae395f |
1
.gitignore
vendored
@ -10,3 +10,4 @@ compile_commands.json
|
||||
/iwyu/
|
||||
.Rhistory
|
||||
.vscode/
|
||||
.codechecker/
|
||||
@ -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
@ -0,0 +1,43 @@
|
||||
# This CITATION.cff file was generated with cffinit.
|
||||
# Visit https://bit.ly/cffinit to generate yours today!
|
||||
|
||||
cff-version: 1.2.0
|
||||
title: 'tug: Transport on Uniform Grids'
|
||||
message: >-
|
||||
If you use this software, please cite it using the
|
||||
metadata from this file.
|
||||
type: software
|
||||
authors:
|
||||
- given-names: Marco
|
||||
family-names: De Lucia
|
||||
email: delucia@gfz.de
|
||||
orcid: 'https://orcid.org/0009-0000-3058-8472'
|
||||
- given-names: Max
|
||||
family-names: Lübke
|
||||
email: mluebke@uni-potsdam.de
|
||||
orcid: 'https://orcid.org/0009-0008-9773-3038'
|
||||
- given-names: Bettina
|
||||
family-names: Schnor
|
||||
email: schnor@cs.uni-potsdam.de
|
||||
orcid: 'https://orcid.org/0000-0001-7369-8057'
|
||||
- given-names: Hannes
|
||||
family-names: Signer
|
||||
email: signer@uni-potsdam.de
|
||||
orcid: 'https://orcid.org/0009-0000-3058-8472'
|
||||
- given-names: Philipp
|
||||
family-names: Ungrund
|
||||
email: ungrund@uni-potsdam.de
|
||||
orcid: 'https://orcid.org/0009-0007-0182-4051'
|
||||
repository-code: 'https://github.com/POET-Simulator/tug'
|
||||
abstract: >-
|
||||
tug implements different numerical approaches for
|
||||
transport problems, notably diffusion with implicit BTCS
|
||||
(Backward Time, Central Space) Euler and parallel 2D ADI
|
||||
(Alternating Direction Implicit).
|
||||
keywords:
|
||||
- diffusion
|
||||
- advection
|
||||
- BTCS
|
||||
- FTCS
|
||||
- Thomas-Algorithm
|
||||
license: GPL-2.0
|
||||
@ -1,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
@ -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
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
tug
|
||||
Copyright (C) 2023 naaice
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
103
README.md
Normal file
@ -0,0 +1,103 @@
|
||||

|
||||
|
||||
`tug` implements different numerical approaches for transport
|
||||
problems, notably diffusion with implicit BTCS (Backward Time, Central
|
||||
Space) Euler and parallel 2D ADI (Alternating Direction Implicit).
|
||||
|
||||
# About
|
||||
|
||||
This project aims to provide a library for solving transport problems -
|
||||
diffusion, advection - on uniform grids implemented in C++. The library
|
||||
is built on top of
|
||||
[Eigen](https://eigen.tuxfamily.org/index.php?title=Main_Page),
|
||||
providing easy access to its optimized data structures and linear
|
||||
equation solvers.
|
||||
|
||||
We designed the API to be as flexible as possible. Nearly every
|
||||
built-in, framework or third-party data structure can be used to model a
|
||||
problem, as long a pointer to continuous memory can be provided. We also
|
||||
provide parallelization using [OpenMP](https://www.openmp.org/), which
|
||||
can be easily turned on/off at compile time.
|
||||
|
||||
At the current state, both 1D and 2D diffusion problems on a regular
|
||||
grid with constant alpha for all grid cells can be solved reliably.
|
||||
|
||||
# Requirements
|
||||
|
||||
- C++17 compliant compiler
|
||||
- [CMake](https://cmake.org/) >= 3.20
|
||||
- [Eigen](https://eigen.tuxfamily.org/) >= 3.4.0
|
||||
|
||||
# Getting started
|
||||
|
||||
`tug` is designed as a framework library and it relies on
|
||||
[CMake](https://cmake.org/) for building. If you already use
|
||||
`CMake` as your build toolkit for your application, you\'re
|
||||
good to go. If you decide not to use `CMake`, you need to
|
||||
manually link your application/library to `tug`.
|
||||
|
||||
1. Create project directory.
|
||||
|
||||
```bash
|
||||
mkdir sample_project && cd sample_project
|
||||
```
|
||||
|
||||
2. Clone this repository into path of choice project directory
|
||||
|
||||
3. Add the following line into `CMakeLists.txt` file:
|
||||
|
||||
```bash
|
||||
add_subdirectory(path_to_tug EXCLUDE_FROM_ALL)
|
||||
```
|
||||
|
||||
4. Write application/library using `tug`\'s API, notably
|
||||
including relevant headers (see examples).
|
||||
|
||||
5. Link target application/library against `tug`. Do this by
|
||||
adding into according `CMakeLists.txt` file:
|
||||
|
||||
```bash
|
||||
target_link_libraries(<your_target> tug)
|
||||
```
|
||||
|
||||
6. Build your application/library with `CMake`.
|
||||
|
||||
# Usage in an application
|
||||
|
||||
Using `tug` can be summarized into the following steps:
|
||||
|
||||
1. Define problem dimensionality
|
||||
2. Set grid sizes for each dimension
|
||||
3. Set the timestep
|
||||
4. Define boundary conditions
|
||||
5. Run the simulation!
|
||||
|
||||
This will run a simulation on the defined grid for one species. See the
|
||||
source code documentation of `tug` and the examples in the
|
||||
`examples/` directory for more details.
|
||||
|
||||
# Contributing
|
||||
|
||||
In this early stage of development every help is welcome. To do so,
|
||||
there are currently the following options:
|
||||
|
||||
Given you have an account for GFZ\'s `gitlab` instance:
|
||||
|
||||
1. Fork this project, create a branch and push your changes. If your
|
||||
changes are done or you feel the need for some feedback create a
|
||||
merge request with the destination set to the **main** branch of
|
||||
this project.
|
||||
2. Ask for access to this repository. You most likely will get access
|
||||
as a developer which allows you to create branches and merge
|
||||
requests inside this repository.
|
||||
|
||||
If you can\'t get access to this `gitlab` instance:
|
||||
|
||||
- Download this repository and note down the SHA of the downloaded commit. Apply
|
||||
your changes and send a mail to <mluebke@gfz-potsdam.de> or
|
||||
<delucia@gfz-potsdam.de> with the patch/diff compared to your starting
|
||||
point. Please split different patch types (feature, fixes, improvements ...)
|
||||
into seperate files. Also provide us the SHA of the commit you\'ve
|
||||
downloaded.
|
||||
|
||||
Thank you for your contributions in advance!
|
||||
126
README.org
@ -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
@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
@ -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
@ -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
|
||||
@ -1,4 +0,0 @@
|
||||
Grid
|
||||
====
|
||||
|
||||
.. doxygenclass:: Grid
|
||||
@ -1,4 +0,0 @@
|
||||
Simulation
|
||||
==========
|
||||
|
||||
.. doxygenclass:: Simulation
|
||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@ -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>
|
||||
@ -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')
|
||||
|
||||
1
docs_sphinx/contributors.rst
Normal file
@ -0,0 +1 @@
|
||||
.. mdinclude:: ../CONTRIBUTORS.md
|
||||
@ -1,2 +0,0 @@
|
||||
Developper API
|
||||
==============
|
||||
@ -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
|
||||
-------------------------------------------------------
|
||||
-------------------------------------------------------
|
||||
|
||||
1413
docs_sphinx/images/adi_scheme.svg
Normal file
|
After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 41 KiB |
367
docs_sphinx/images/conservation_law.svg
Normal file
|
After Width: | Height: | Size: 47 KiB |
563
docs_sphinx/images/discretization_1D.svg
Normal file
@ -0,0 +1,563 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
width="650.3999"
|
||||
height="226.6021"
|
||||
viewBox="0 0 650.3999 226.60209"
|
||||
sodipodi:docname="discretization_1D.pdf"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 393.40646 H 0 Z"
|
||||
transform="scale(0.33298893)"
|
||||
clip-rule="evenodd"
|
||||
id="path2" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path4" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath6">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path6" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath8">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path8" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath10">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path10" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path12" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath14">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-67.109375,-65.999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path14" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath16">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-127.10937,-65.999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path16" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-187.10938,-65.999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path18" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath20">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-247.10938,-65.999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path20" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath22">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-307.10938,-65.999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path22" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath24">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(22.390179,0,0,-22.480592,-4236.1206,1319.6518)"
|
||||
clip-rule="evenodd"
|
||||
id="path24" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath26">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.669276,0,0,-31.797165,-6847.4122,2078.7157)"
|
||||
clip-rule="evenodd"
|
||||
id="path26" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath28">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(22.390179,0,0,-24.643363,-8270.5209,1404.1114)"
|
||||
clip-rule="evenodd"
|
||||
id="path28" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath30">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.669276,0,0,-34.85626,-12553.777,2198.1785)"
|
||||
clip-rule="evenodd"
|
||||
id="path30" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath32">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(22.390179,0,0,-24.643363,-12304.921,1404.1114)"
|
||||
clip-rule="evenodd"
|
||||
id="path32" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath34">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.669276,0,0,-34.85626,-18260.142,2198.1785)"
|
||||
clip-rule="evenodd"
|
||||
id="path34" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath36">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(22.390179,0,0,-22.480592,-16339.322,1319.6518)"
|
||||
clip-rule="evenodd"
|
||||
id="path36" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath38">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.669276,0,0,-31.797165,-23966.51,2078.7157)"
|
||||
clip-rule="evenodd"
|
||||
id="path38" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath40">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(22.390179,0,0,-24.643363,-20373.722,1404.1114)"
|
||||
clip-rule="evenodd"
|
||||
id="path40" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath42">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.669276,0,0,-34.85626,-29672.875,2198.1785)"
|
||||
clip-rule="evenodd"
|
||||
id="path42" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath44">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path44" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath46">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path46" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath48">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path48" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath50">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.193035,0,0,-34.583265,-14238.804,12659.567)"
|
||||
clip-rule="evenodd"
|
||||
id="path50" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath52">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(31.193035,0,0,-34.583265,-15071.803,12659.567)"
|
||||
clip-rule="evenodd"
|
||||
id="path52" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath54">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path54" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath56">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path56" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath58">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(0.33298893,0,0,0.33298893,-0.49999999,-0.49999999)"
|
||||
clip-rule="evenodd"
|
||||
id="path58" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath60">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(32.334989,0,0,-32.96522,-4686.8436,11507.254)"
|
||||
clip-rule="evenodd"
|
||||
id="path60" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath62">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(32.334989,0,0,-32.96522,-5519.8439,11507.254)"
|
||||
clip-rule="evenodd"
|
||||
id="path62" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath64">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(32.334989,0,0,-32.96522,-5139.344,12869.254)"
|
||||
clip-rule="evenodd"
|
||||
id="path64" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath66">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(32.334989,0,0,-32.96522,-4466.8437,12183.255)"
|
||||
clip-rule="evenodd"
|
||||
id="path66" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath68">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(19.846142,0,0,-26.124175,-1668.8001,5166.3952)"
|
||||
clip-rule="evenodd"
|
||||
id="path68" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath70">
|
||||
<path
|
||||
d="M 0,0 H 1129.1666 V 390.40335 H 0 Z"
|
||||
transform="matrix(21.454004,0,0,-25.15296,-22356.717,4974.3261)"
|
||||
clip-rule="evenodd"
|
||||
id="path70" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<g
|
||||
id="g1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="1"
|
||||
transform="translate(144.53332,50.356026)">
|
||||
<path
|
||||
id="path1"
|
||||
d="M 0,0 H 376 V 131 H 0 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-144.53332,-50.356024)"
|
||||
clip-path="url(#clipPath2)" />
|
||||
<path
|
||||
id="path3"
|
||||
d="m 40,32 h 60 V 92 H 40 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath4)" />
|
||||
<path
|
||||
id="path5"
|
||||
d="m 100,32 h 60 v 60 h -60 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath6)" />
|
||||
<path
|
||||
id="path7"
|
||||
d="m 160,32 h 60 v 60 h -60 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath8)" />
|
||||
<path
|
||||
id="path9"
|
||||
d="m 220,32 h 60 v 60 h -60 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath10)" />
|
||||
<path
|
||||
id="path11"
|
||||
d="m 280,32 h 60 v 60 h -60 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath12)" />
|
||||
<path
|
||||
id="path13"
|
||||
d="M 3.1386108,-0.82895751 V -3.7556182 H 0.21195013 V -4.2271357 H 3.1386108 v -2.9104015 h 0.4715176 v 2.9104015 H 6.536789 v 0.4715175 H 3.6101284 v 2.92666069 z m 0,0"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
aria-label="+"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-28.448384,63.809926)"
|
||||
clip-path="url(#clipPath14)" />
|
||||
<path
|
||||
id="path15"
|
||||
d="M 3.1392194,-0.82895751 V -3.7556182 H 0.21255876 V -4.2271357 H 3.1392194 V -7.1375372 H 3.610737 v 2.9104015 h 2.9266607 v 0.4715175 H 3.610737 v 2.92666069 z m 0,0"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
aria-label="+"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,75.338842,63.809926)"
|
||||
clip-path="url(#clipPath16)" />
|
||||
<path
|
||||
id="path17"
|
||||
d="M 3.1398231,-0.82895751 V -3.7556182 H 0.21316239 V -4.2271357 H 3.1398231 v -2.9104015 h 0.4715175 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6113406 v 2.92666069 z m 0,0"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
aria-label="+"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,179.12609,63.809926)"
|
||||
clip-path="url(#clipPath18)" />
|
||||
<path
|
||||
id="path19"
|
||||
d="M 3.1363669,-0.82895751 V -3.7556182 H 0.20970622 V -4.2271357 H 3.1363669 v -2.9104015 h 0.4715175 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6078844 v 2.92666069 z m 0,0"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
aria-label="+"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,282.91331,63.809926)"
|
||||
clip-path="url(#clipPath20)" />
|
||||
<path
|
||||
id="path21"
|
||||
d="M 3.1369755,-0.82895751 V -3.7556182 H 0.21031485 V -4.2271357 H 3.1369755 v -2.9104015 h 0.4715176 v 2.9104015 h 2.9266607 v 0.4715175 H 3.6084931 v 2.92666069 z m 0,0"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
aria-label="+"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,386.70054,63.809926)"
|
||||
clip-path="url(#clipPath22)" />
|
||||
<path
|
||||
id="path23"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1337"
|
||||
transform="matrix(0.02572556,0,0,-0.0256221,-35.556726,-16.543775)"
|
||||
clip-path="url(#clipPath24)" />
|
||||
<path
|
||||
id="path25"
|
||||
d="m 96,585 c 37.33334,54 88.33333,81 153,81 32,0 64,-8.66669 96,-26 32,-17.33331 58,-48 78,-92 24.66666,-55.33334 37,-131.33331 37,-228 C 460,216.66666 445.66666,137.66667 417,83 403.66666,55 385.33334,32.666668 362,16 338.66666,-0.66666794 318.33334,-11 301,-15 c -17.33334,-4 -34.33334,-6.333334 -51,-7 -17.33333,0 -34.66667,2 -52,6 -17.33333,4 -37.66667,14.666666 -61,32 C 113.66666,33.333336 95.333336,55.666664 82,83 53.333332,137.66667 39,216.66666 39,320 39,436 58,524.33331 96,585 Z m 225,12 c -20,21.33331 -43.66666,32 -71,32 -28,0 -52,-10.66669 -72,-32 -16.66667,-17.33331 -27.66667,-41.33331 -33,-72 -5.33333,-30.66666 -8,-94.66666 -8,-192 0,-105.33334 2.66667,-174.66666 8,-208 5.33333,-33.333336 17.33333,-59.666664 36,-79 18.66667,-20 41.66667,-30 69,-30 26.66666,0 49.33334,10 68,30 19.33334,20 31.33334,48 36,84 4.66666,36 7.33334,103.66666 8,203 0,96.66669 -2.66666,160.33334 -8,191 -5.33334,30.66669 -16.33334,55 -33,73 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0945"
|
||||
transform="matrix(0.01818797,0,0,-0.01811482,-19.992763,-12.70046)"
|
||||
clip-path="url(#clipPath26)" />
|
||||
<path
|
||||
id="path27"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
|
||||
transform="matrix(0.02572556,0,0,-0.02337343,68.230498,-17.537123)"
|
||||
clip-path="url(#clipPath28)" />
|
||||
<path
|
||||
id="path29"
|
||||
d="m 213,578 -13,-5 c -9.33333,-3.33331 -22.66667,-6.66669 -40,-10 -17.33333,-3.33331 -36.66666,-5.66669 -58,-7 H 83 v 46 h 19 c 31.33333,1.33331 60.33333,6.33331 87,15 26.66667,8.66669 45.33333,16.66669 56,24 10.66667,7.33331 20,14.66669 28,22 1.33334,2 5.33334,3 12,3 6,0 11.66666,-2 17,-6 V 361 l 1,-300 c 4.66666,-4.666668 8.66666,-7.666668 12,-9 3.33334,-1.333332 11.33334,-2.666668 24,-4 12.66666,-1.333332 33.33334,-2 62,-2 h 26 V 0 H 416 C 402,2 349,3 257,3 166.33333,3 114,2 100,0 H 88 v 46 h 26 c 14.66667,0 27.33333,0 38,0 10.66667,0 19,0.333332 25,1 6,0.666668 11.33333,1.666668 16,3 4.66667,1.333332 7.33333,2 8,2 0.66667,0 2.66667,1.666668 6,5 3.33333,3.333332 5.33333,4.666668 6,4 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
|
||||
transform="matrix(0.01818797,0,0,-0.01652501,83.79445,-14.031106)"
|
||||
clip-path="url(#clipPath30)" />
|
||||
<path
|
||||
id="path31"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
|
||||
transform="matrix(0.02572556,0,0,-0.02337343,172.01772,-17.537123)"
|
||||
clip-path="url(#clipPath32)" />
|
||||
<path
|
||||
id="path33"
|
||||
d="m 109,429 c -18,0 -32.333336,6 -43,18 -10.666668,12 -16,26.66666 -16,44 0,47.33331 17.666664,88.33331 53,123 35.33334,34.66669 79.33333,52 132,52 60.66666,0 111.33334,-18.66669 152,-56 40.66666,-37.33331 61.33334,-85.66669 62,-145 0,-28.66666 -6.66666,-56 -20,-82 -13.33334,-26 -29.33334,-48.66666 -48,-68 -18.66666,-19.33334 -45.33334,-44 -80,-74 -24,-20.66667 -57.33333,-51.33333 -100,-92 l -59,-56 76,-1 c 104.66669,0 160.33334,1.666664 167,5 4.66666,1.333336 12.66666,31 24,89 v 3 h 40 v -3 C 448.33334,184 444,153.66667 436,95 428,36.333332 423,5.666667 421,3 V 0 H 50 v 19 12 c 0,4.666668 2,9.666668 6,15 4,5.333332 14,17 30,35 19.33334,21.33334 36,40 50,56 6,6.66667 17.33333,19 34,37 16.66667,18 28,30.33333 34,37 6,6.66667 15.66667,17.66667 29,33 13.33333,15.33334 22.66667,26.66666 28,34 5.33334,7.33334 13,17.33334 23,30 10,12.66666 17,23.33334 21,32 4,8.66666 9,18.33334 15,29 6,10.66666 10.33334,21.33334 13,32 2.66666,10.66666 5,20.66666 7,30 2,9.33334 3,20.33334 3,33 0,42 -11.33334,78.33331 -34,109 -22.66666,30.66669 -55,46 -97,46 -22,0 -41.33333,-5.66669 -58,-17 -16.66667,-11.33331 -28.33334,-22.33331 -35,-33 -6.66666,-10.66669 -10,-17 -10,-19 0,-0.66669 1.66666,-1 5,-1 12,0 24.33333,-4.66669 37,-14 12.66667,-9.33331 19,-24.66666 19,-46 0,-16.66666 -5.33333,-30.66666 -16,-42 -10.66667,-11.33334 -25.66667,-17.33334 -45,-18 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
|
||||
transform="matrix(0.01818797,0,0,-0.01652501,187.58168,-14.031106)"
|
||||
clip-path="url(#clipPath34)" />
|
||||
<path
|
||||
id="path35"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1337"
|
||||
transform="matrix(0.02572556,0,0,-0.0256221,275.80495,-16.543775)"
|
||||
clip-path="url(#clipPath36)" />
|
||||
<path
|
||||
id="path37"
|
||||
d="m 127,463 c -18,0 -32,5.66666 -42,17 -10,11.33334 -15.333336,26 -16,44 0,36.66669 16,69.33331 48,98 32,28.66669 70.66666,43 116,43 23.33334,0 38,-0.33331 44,-1 49.33334,-8 87,-25.66669 113,-53 26,-27.33331 39.33334,-57 40,-89 0,-34.66666 -11.33334,-68.33334 -34,-101 -22.66666,-32.66666 -54,-56.33334 -94,-71 l -3,-2 c 0,-0.66666 3,-1.66666 9,-3 6,-1.33334 15.66666,-4.33334 29,-9 13.33334,-4.66666 26,-11.66666 38,-21 54.66666,-35.33334 82,-82 82,-140 C 457,122.33333 436.33334,76.333336 395,37 353.66666,-2.3333359 301.33334,-22 238,-22 184.66666,-22 138.66667,-7.6666679 100,21 61.333332,49.666668 42,86 42,130 c 0,18.66667 6,33.66667 18,45 12,11.33333 27,17.33333 45,18 18.66667,0 34,-6 46,-18 12,-12 18,-27 18,-45 0,-7.33334 -1,-14 -3,-20 -2,-6 -4.33333,-11.333336 -7,-16 -2.66667,-4.666664 -6.33333,-8.666664 -11,-12 -4.66667,-3.333336 -8.66667,-6 -12,-8 -3.33333,-2 -6.66667,-3.333336 -10,-4 -3.33334,-0.666664 -6,-1.666664 -8,-3 l -4,-1 c 34,-30 75.33333,-45 124,-45 36.66666,0 64.33334,17.666664 83,53 11.33334,22 17,55.66666 17,101 v 20 c 0,63.33334 -21.33334,105.66666 -64,127 -10,4 -30.33333,6.33334 -61,7 l -42,1 -3,2 c -1.33333,2 -2,7.33334 -2,16 0,12 2.66667,18 8,18 18.66667,0 38,1.66666 58,5 22.66667,3.33334 43.33334,17.33334 62,42 18.66666,24.66666 28,62 28,112 v 8 c 0,38 -11.66666,64.33331 -35,79 -14.66666,9.33331 -30.33334,14 -47,14 -21.33333,0 -41,-3.66669 -59,-11 -18,-7.33331 -30.66667,-15 -38,-23 -7.33333,-8 -11,-12 -11,-12 h 3 c 2,-0.66669 4.66667,-1.33331 8,-2 3.33333,-0.66669 6.66667,-2.33331 10,-5 3.33333,-2.66669 7.33333,-5 12,-7 4.66667,-2 8,-5.66669 10,-11 2,-5.33331 4.66667,-10.33331 8,-15 3.33333,-4.66669 4.33333,-11.33331 3,-20 0,-14.66666 -4.66667,-27.66666 -14,-39 -9.33333,-11.33334 -24.33333,-17.33334 -45,-18 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0945"
|
||||
transform="matrix(0.01818797,0,0,-0.01811482,291.36894,-12.70046)"
|
||||
clip-path="url(#clipPath38)" />
|
||||
<path
|
||||
id="path39"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1277"
|
||||
transform="matrix(0.02572556,0,0,-0.02337343,379.59218,-17.537123)"
|
||||
clip-path="url(#clipPath40)" />
|
||||
<path
|
||||
id="path41"
|
||||
d="M 462,0 C 450,2 407,3 333,3 255.66666,3 211,2 199,0 h -9 v 46 h 31 c 13.33333,0 22.33333,0 27,0 4.66667,0 10.33334,0.666668 17,2 6.66666,1.333332 11.33334,3 14,5 2.66666,2 5,4.666668 7,8 0.66666,1.333332 1,19.333328 1,54 v 50 H 28 v 46 l 151,231 c 102,154.66669 153.66666,232.33331 155,233 1.33334,1.33331 8.33334,2 21,2 h 18 l 6,-6 V 211 h 92 v -46 h -92 v -51 c 0,-27.333336 0,-43.333336 0,-48 0,-4.666668 2,-8.666668 6,-12 5.33334,-4.666668 24.33334,-7.333332 57,-8 h 29 V 0 Z M 293,211 V 545 L 74,212 183,211 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0903"
|
||||
transform="matrix(0.01818797,0,0,-0.01652501,395.15616,-14.031106)"
|
||||
clip-path="url(#clipPath42)" />
|
||||
<path
|
||||
id="path43"
|
||||
d="M 136.11999,102.14 159,102 l 22.88,0.14"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath44)" />
|
||||
<path
|
||||
id="path45"
|
||||
d="m 130.11999,102.18 5.99001,-2.04 0.02,4 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath46)" />
|
||||
<path
|
||||
id="path47"
|
||||
d="m 187.88,102.18 -6.01,1.96 0.02,-4 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath48)" />
|
||||
<path
|
||||
id="path49"
|
||||
d="m 51,0 c -3.333332,2.6666667 -5,5 -5,7 0,1.333333 56.33333,118 169,350 112.66669,232 170.33334,349.33331 173,352 2,4.66669 11.33334,7 28,7 15.33334,0 24.66666,-2.33331 28,-7 2,-2.66669 59.33331,-120 172,-352 C 728.66669,125 785.33331,8.333333 786,7 786,5 784.33331,2.6666667 781,0 Z M 507,344 384,596 137,92 383,91 h 247 c 0,1.333336 -41,85.66666 -123,253 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0913"
|
||||
transform="matrix(0.01846566,0,0,-0.01665545,118.3956,160.49481)"
|
||||
clip-path="url(#clipPath50)" />
|
||||
<path
|
||||
id="path51"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0913"
|
||||
transform="matrix(0.01846566,0,0,-0.01665545,133.77748,160.49481)"
|
||||
clip-path="url(#clipPath52)" />
|
||||
<path
|
||||
id="path53"
|
||||
d="m 47.119999,102.14 15.760002,-0.09"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath54)" />
|
||||
<path
|
||||
id="path55"
|
||||
d="m 41.119999,102.18 5.990002,-2.04 0.02,4 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath56)" />
|
||||
<path
|
||||
id="path57"
|
||||
d="m 68.879997,102.01 -5.989998,2.04 -0.02,-4 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.7297871,0,0,1.7297871,-143.66842,-49.491131)"
|
||||
clip-path="url(#clipPath58)" />
|
||||
<path
|
||||
id="path59"
|
||||
d="m 51,0 c -3.333332,2.6666667 -5,5 -5,7 0,1.333333 56.33333,118 169,350 112.66669,232 170.33334,349.33331 173,352 2,4.66669 11.33334,7 28,7 15.33334,0 24.66666,-2.33331 28,-7 2,-2.66669 59.33331,-120 172,-352 C 728.66669,125 785.33331,8.333333 786,7 786,5 784.33331,2.6666667 781,0 Z M 507,344 384,596 137,92 383,91 h 247 c 0,1.333336 -41,85.66666 -123,253 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
|
||||
transform="matrix(0.01781352,0,0,-0.01747296,-61.044135,150.70977)"
|
||||
clip-path="url(#clipPath60)" />
|
||||
<path
|
||||
id="path61"
|
||||
d="m 52,289 c 4.666668,28 22.666664,60.33334 54,97 31.33333,36.66666 70,55.33334 116,56 23.33333,0 44.66666,-6 64,-18 19.33334,-12 33.66666,-27 43,-45 28,42 61.66666,63 101,63 24.66666,0 46,-7.33334 64,-22 18,-14.66666 27.33331,-34.33334 28,-59 0,-19.33334 -4.66669,-35 -14,-47 -9.33334,-12 -18.33334,-19.33334 -27,-22 -8.66666,-2.66666 -16.33334,-4 -23,-4 -12.66666,0 -23,3.66666 -31,11 -8,7.33334 -12,17 -12,29 0,30.66666 16.66666,51.66666 50,63 -7.33334,8.66666 -20.66666,13 -40,13 -8.66666,0 -15,-0.66666 -19,-2 -25.33334,-10.66666 -44,-32.66666 -56,-66 -40,-147.33334 -60,-233.33333 -60,-258 0,-18.666668 5.33334,-32 16,-40 10.66666,-8 22.33334,-12 35,-12 24.66666,0 49,11 73,33 24,22 40.33334,49 49,81 2,6.66667 4,10.33333 6,11 2,0.66667 7.33334,1.33333 16,2 h 4 c 10,0 15,-2.66667 15,-8 0,-0.66667 -0.66666,-4.33333 -2,-11 C 491.33334,96 470.66666,62.333336 440,33 409.33334,3.666666 373.66666,-11 333,-11 286.33334,-11 251,10 227,52 199.66667,10.666664 168.33334,-10 133,-10 h -6 C 94.333328,-10 71,-1.333334 57,16 43,33.333336 35.666668,51.666664 35,71 c 0,21.333336 6.333332,38.66666 19,52 12.666664,13.33333 27.666664,20 45,20 28.66667,0 43,-14 43,-42 C 142,87.666664 138,76 130,66 122,56 114.33334,49.333332 107,46 99.666664,42.666668 95.333336,41 94,41 l -3,-1 c 0,-0.666668 2,-2 6,-4 4,-2 9.33334,-4.333334 16,-7 6.66666,-2.666666 13,-3.666666 19,-3 24,0 44.66667,15 62,45 6,10.666664 13.66667,33.33333 23,68 9.33333,34.66667 18.66667,70.66666 28,108 9.33333,37.33334 14.66666,59.33334 16,66 3.33334,18 5,31 5,39 0,18.66666 -5,32 -15,40 -10,8 -21.33333,12 -34,12 -26.66667,0 -51.66667,-10.66666 -75,-32 -23.33334,-21.33334 -39.66666,-48.66666 -49,-82 -1.333336,-6 -3,-9.33334 -5,-10 -2,-0.66666 -7.333336,-1.33334 -16,-2 H 58 c -4,4 -6,7.66666 -6,11 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
|
||||
transform="matrix(0.01781352,0,0,-0.01747296,-46.205468,150.70977)"
|
||||
clip-path="url(#clipPath62)" />
|
||||
<path
|
||||
id="path63"
|
||||
d="m 109,429 c -18,0 -32.333336,6 -43,18 -10.666668,12 -16,26.66666 -16,44 0,47.33331 17.666664,88.33331 53,123 35.33334,34.66669 79.33333,52 132,52 60.66666,0 111.33334,-18.66669 152,-56 40.66666,-37.33331 61.33334,-85.66669 62,-145 0,-28.66666 -6.66666,-56 -20,-82 -13.33334,-26 -29.33334,-48.66666 -48,-68 -18.66666,-19.33334 -45.33334,-44 -80,-74 -24,-20.66667 -57.33333,-51.33333 -100,-92 l -59,-56 76,-1 c 104.66669,0 160.33334,1.666664 167,5 4.66666,1.333336 12.66666,31 24,89 v 3 h 40 v -3 C 448.33334,184 444,153.66667 436,95 428,36.333332 423,5.666667 421,3 V 0 H 50 v 19 12 c 0,4.666668 2,9.666668 6,15 4,5.333332 14,17 30,35 19.33334,21.33334 36,40 50,56 6,6.66667 17.33333,19 34,37 16.66667,18 28,30.33333 34,37 6,6.66667 15.66667,17.66667 29,33 13.33333,15.33334 22.66667,26.66666 28,34 5.33334,7.33334 13,17.33334 23,30 10,12.66666 17,23.33334 21,32 4,8.66666 9,18.33334 15,29 6,10.66666 10.33334,21.33334 13,32 2.66666,10.66666 5,20.66666 7,30 2,9.33334 3,20.33334 3,33 0,42 -11.33334,78.33331 -34,109 -22.66666,30.66669 -55,46 -97,46 -22,0 -41.33333,-5.66669 -58,-17 -16.66667,-11.33331 -28.33334,-22.33331 -35,-33 -6.66666,-10.66669 -10,-17 -10,-19 0,-0.66669 1.66666,-1 5,-1 12,0 24.33333,-4.66669 37,-14 12.66667,-9.33331 19,-24.66666 19,-46 0,-16.66666 -5.33333,-30.66666 -16,-42 -10.66667,-11.33334 -25.66667,-17.33334 -45,-18 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.0919"
|
||||
transform="matrix(0.01781352,0,0,-0.01747296,-52.98351,174.50793)"
|
||||
clip-path="url(#clipPath64)" />
|
||||
<path
|
||||
id="path65"
|
||||
d="m 120,220 h 1605 v 60 H 120 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
transform="matrix(0.01781352,0,0,-0.01747296,-64.963108,162.5215)"
|
||||
clip-path="url(#clipPath66)" />
|
||||
<path
|
||||
id="path67"
|
||||
d="m 117,59 c 0,-22 8.33333,-33 25,-33 24.66667,0 45.66667,35 63,105 4,13.33333 7.33333,20.33333 10,21 1.33333,0.66667 4.66667,1 10,1 h 4 c 6,0 10,0 12,0 2,0 3.66667,-0.66667 5,-2 1.33333,-1.33333 2,-3.66667 2,-7 -0.66667,-4 -1.66667,-9.33333 -3,-16 -1.33333,-6.66666 -5,-19.33333 -11,-38 C 228,71.333328 221.33333,55.666668 214,43 206.66667,30.333332 196.33333,18 183,6 169.66667,-6 154.33333,-11.666667 137,-11 113,-11 90.666672,-3.666667 70,11 49.333332,25.666668 38.666668,50.333332 38,85 c 0,8 0.333332,13.666664 1,17 l 65,258 c 42,170 63,257.66669 63,263 0,2 -0.33333,3.66669 -1,5 -0.66667,1.33331 -2,2.66669 -4,4 -2,1.33331 -3.66667,2 -5,2 -1.33333,0 -4,0.33331 -8,1 -4,0.66669 -6.66667,1 -8,1 -1.33333,0 -4.33333,0.33331 -9,1 -4.66666,0.66669 -8,0.66669 -10,0 -6.66666,0 -11,0 -13,0 -2,0 -4.66666,0.33331 -8,1 -3.333336,0.66669 -5.333336,1.66669 -6,3 -0.666664,1.33331 -1,3.33331 -1,6 0,1.33331 0.666664,6 2,14 3.333336,12.66669 7,19.66669 11,21 4,1.33331 28,3.33331 72,6 10,0.66669 21.33333,1.33331 34,2 12.66667,0.66669 22.66667,1.66669 30,3 7.33333,1.33331 11,1.66669 11,1 8,0 12,-2.66669 12,-8 0,-7.33331 -24.33333,-107.33331 -73,-300 C 144.33333,193.33333 119.33334,92.333336 118,83 c 0,-1.333336 0,-4 0,-8 0,-4 -0.33334,-7.333336 -1,-10 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1318"
|
||||
transform="matrix(0.02902327,0,0,-0.02204854,-96.099279,63.555458)"
|
||||
clip-path="url(#clipPath68)" />
|
||||
<path
|
||||
id="path69"
|
||||
d="m 21,287 c 0.666666,2 1.333334,4.66666 2,8 0.666666,3.33334 2.333334,10.66666 5,22 2.666666,11.33334 6,21.66666 10,31 4,9.33334 9,20.33334 15,33 6,12.66666 12.666664,22.66666 20,30 7.333336,7.33334 16,14.66666 26,22 10,7.33334 21,10.33334 33,9 19.33333,0 36.33333,-4 51,-12 14.66667,-8 25,-15.33334 31,-22 6,-6.66666 9.66667,-13.33334 11,-20 1.33333,-4 2.33333,-6 3,-6 0.66667,0 3.33333,2.33334 8,7 32,34.66666 69,52 111,52 h 3 c 32,0 56,-13.66666 72,-41 5.33334,-12.66666 8,-25 8,-37 0,-20 -4.33334,-36 -13,-48 -8.66666,-12 -17.33334,-19.66666 -26,-23 -8.66666,-3.33334 -17,-4.66666 -25,-4 -13.33334,0 -24,3.66666 -32,11 -8,7.33334 -12,17 -12,29 0,32 18.66666,53.33334 56,64 -14.66666,8.66666 -26.66666,13 -36,13 -37.33334,0 -71.66666,-24.66666 -103,-74 -6.66667,-10.66666 -11.66667,-21.66666 -15,-33 C 220.66667,286.66666 209.33333,242.33334 190,165 167.33333,71.666664 154.33333,22 151,16 c -8.66667,-18 -23,-27 -43,-27 -8.666664,0 -15.666664,2 -21,6 -5.333336,4 -9,8 -11,12 -2,4 -2.666664,7.333333 -2,10 0,8.666668 13.333328,66 40,172 26.66667,106 40,165 40,177 0,26 -8.66667,39 -26,39 -14,0 -26,-9.33334 -36,-28 C 82,358.33334 74,338 68,316 62,294 58.333332,282 57,280 55.666668,278.66666 50.333336,278 41,278 H 27 c -4,4 -6,7 -6,9 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.1291"
|
||||
transform="matrix(0.02684813,0,0,-0.02289989,455.70282,63.555487)"
|
||||
clip-path="url(#clipPath70)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 44 KiB |
607
docs_sphinx/images/explicit_scheme.svg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
docs_sphinx/images/grid_with_boundaries_example.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
607
docs_sphinx/images/implicit_scheme.svg
Normal file
|
After Width: | Height: | Size: 173 KiB |
475
docs_sphinx/images/numeric_overview.svg
Normal file
|
After Width: | Height: | Size: 108 KiB |
@ -49,3 +49,4 @@ Table of Contents
|
||||
developer
|
||||
examples
|
||||
visualization
|
||||
contributors
|
||||
|
||||
@ -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, let’s
|
||||
imagine a long tube filled with a liquid or gaseous medium into which we add a
|
||||
marker substance within a small volume element at the location x.
|
||||
|
||||
.. figure:: images/conservation_law.svg
|
||||
|
||||
Visualization of the law of mass conservation
|
||||
|
||||
The boundaries of the volume element are still permeable, so that mass transport
|
||||
is possible. We refer to the difference of the substance entering and leaving
|
||||
the volume element as the flux :math:`\Phi` and the concentration of the marker
|
||||
substance at location :math:`x` at time :math:`t` as :math:`C`. According to the
|
||||
conservation of mass, it is not possible for this to be created or destroyed.
|
||||
Therefore, a change in concentration over time must be due to a change in flux
|
||||
over the volume element. [LOGAN15]_ [SALSA22]_ Formally this leads to
|
||||
|
||||
.. _`mass conservation`:
|
||||
|
||||
.. math::
|
||||
|
||||
\frac{\partial C}{\partial t} + \frac{\partial \Phi}{\partial x} = 0.
|
||||
|
||||
Diffusion relies on random particle collisions based on thermal energy.
|
||||
The higher the concentration of a substance, the higher the probability
|
||||
of a particle collision. Fick’s first law describes this relationship as
|
||||
follows
|
||||
|
||||
.. _`ficks law`:
|
||||
|
||||
.. math::
|
||||
|
||||
\Phi = -\alpha \cdot \frac{\partial C}{\partial x}.
|
||||
|
||||
The law states that the flux moves in the direction of decreasing concentration
|
||||
[1]_, proportional to the gradient of the concentration in the spatial direction
|
||||
and a diffusion coefficient :math:`\alpha`. In this context, the diffusion
|
||||
coefficient represents a measure of the mobility of particles and is given in
|
||||
the SI unit :math:`\frac{m^2}{s}`. Substituting `ficks law`_ into `mass
|
||||
conservation`_ now yields the diffusion equation for the one-dimensional case,
|
||||
which gives the temporal change of the concentration as a function of the
|
||||
spatial concentration gradient
|
||||
|
||||
.. _`one dimensional PDE`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{\partial C}{\partial t} - \frac{\partial}{\partial x}\left(\alpha \cdot \frac{\partial C}{\partial x}\right) = 0 \\
|
||||
\frac{\partial C}{\partial t} = \alpha \cdot \frac{\partial^2 C}{\partial x^2}.
|
||||
\label{eq:}
|
||||
\end{aligned}
|
||||
|
||||
An extension to two dimensions is easily possible and gives
|
||||
|
||||
.. _`two dimensional PDE`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{\partial C}{\partial t} = \alpha_x \cdot \frac{\partial^2 C}{\partial x^2} + \alpha_y \cdot \frac{\partial^2 C}{\partial y^2}.
|
||||
\label{eq:pde_two_dimensional}
|
||||
\end{aligned}
|
||||
|
||||
Since this equation now contains both a first order temporal and a
|
||||
second order spatial derivative, it belongs to the group of partial
|
||||
differential equations (PDE). In particular, the diffusion problem
|
||||
belongs to the group of parabolic PDEs. With these, we describe the
|
||||
evolution of the problem via a first-order time derivative.
|
||||
[CHAPRA15]_ Furthermore, it describes diffusion under
|
||||
the assumption of a constant diffusion coefficient in each direction.
|
||||
Therefore, we distinguish once into the case of homogeneous but
|
||||
anisotropic diffusion, where the diffusion coefficients are constant in
|
||||
one spatial direction but may vary between them, and the case of
|
||||
heterogeneous diffusion. In this case, the diffusion can also change
|
||||
within one spatial direction. This distinction is important, because the
|
||||
subsequent discretization of the problem for numerical solution differs
|
||||
with respect to these two variants.
|
||||
|
||||
|
||||
**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. Taylor’s theorem forms the
|
||||
basis for this possibility. In essence, Taylor states that for a smooth
|
||||
function [2]_ :math:`f: \mathbb{R} \rightarrow \mathbb{R}`, it is
|
||||
possible to predict the function value :math:`f(x)` at one point using
|
||||
the function value and its derivative at another point
|
||||
:math:`a \in \mathbb{R}`. Formally we can write this as
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
f(x) =& f(a) + f'(a) (x-a) + \frac{f''(a)}{2!}(x-a)^2 + ... + \frac{f^{(n)}(a)}{n!}(x-a)^n + R_n\\
|
||||
R_n =& \int_{a}^x \frac{(x-t)^n}{n!} f^{(n+1)}(t) dt.
|
||||
\end{aligned}
|
||||
|
||||
:math:`R_n` denotes the residual term, which also indicates the error in
|
||||
the case of premature termination of the series. An approximation of the
|
||||
first derivative is now done simply by truncating the Taylor series
|
||||
after the first derivative and transforming accordingly
|
||||
[CHAPRA15]_. This leads to
|
||||
|
||||
.. _`first order approximation`:
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
f(x_{i+1}) =& f(x_i) + f'(x_i) (x_{i+1}-x_i) + R_1 \\
|
||||
f'(x_{i}) =& \underbrace{\frac{f(x_{i+1}) - f(x_i)}{x_{i+1} - x_i}}_{\text{first order approximation}} - \underbrace{\frac{R_1}{x_{i+1} - x_i}}_{\text{truncation error}}.
|
||||
\end{aligned}
|
||||
|
||||
The `first order approximation`_ shows a so-called forward finite difference
|
||||
approximation in space, since we use the points at :math:`i` and :math:`i+1` for
|
||||
the approximation of :math:`f'(x_i)`. Correspondingly, a backward and centered
|
||||
finite difference approximation is possible applying the appropriate values.
|
||||
|
||||
For the approximation of higher derivatives, the combination of several
|
||||
Taylor series expansions is possible. For example, we set up the
|
||||
following two Taylor series to derive the centered approximation of the
|
||||
second derivative
|
||||
|
||||
.. _`second order approximations`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
f(x_{i+1}) =& f(x_i) + f'(x_i) \underbrace{(x_{i+1} - x_i)}_{h} + \frac{f^{''}(x_i)}{2} \underbrace{(x_{i+1} - x_{i})^2}_{h^2} + R_2 \\
|
||||
f(x_{i-1}) =& f(x_i) - f'(x_i) \underbrace{(x_{i-1} - x_i)}_{h} + \frac{f^{''}(x_i)}{2} \underbrace{(x_{i-1} - x_{i})^2}_{h^2} +R_2.
|
||||
\end{aligned}
|
||||
|
||||
Adding [3]_ both `second order approximations`_ and rearranging them according to
|
||||
the second derivative yields the corresponding approximation
|
||||
|
||||
|
||||
.. _`second order approximation`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
f^{''}(x_i) = \frac{f(x_{i+1}) - 2\cdot f(x_i) + f(x_{i-1})}{h^2}.
|
||||
\end{aligned}
|
||||
|
||||
Another possibility of the derivation for the second order approximation results
|
||||
from the following consideration. The second derivation is just another
|
||||
derivation of the first one. In this respect we can represent the second
|
||||
derivative also as the difference of the approximation of the first derivative
|
||||
[CHAPRA15]_. This results in the following representation, which after
|
||||
simplification agrees with equation `second order approximation`_
|
||||
|
||||
.. _`first order derivative`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
f^{''}(x_i) = \frac{\frac{f(x_{i+1}) - f(x_i)}{h} - \frac{f(x_i) - f(x_{i-1})}{h}}{h}.
|
||||
\label{eq:first_order_derivative}
|
||||
\end{aligned}
|
||||
|
||||
The above equations are all related to a step size :math:`h`. Let us imagine a
|
||||
bar, which we want to discretize by dividing it into individual cells.
|
||||
|
||||
.. figure:: images/discretization_1D.svg
|
||||
|
||||
Discretization of a one-dimensional surface
|
||||
|
||||
|
||||
In the discretization, the step size :math:`h` would correspond to the distance
|
||||
between the cell centers :math:`\Delta x`. Now, the question arises how to deal
|
||||
with the boundaries? This question is a topic of its own, whereby numerous
|
||||
boundary conditions exist. At this point, only the cases relevant to this work
|
||||
will be presented. First, let us look at the problem intuitively and consider
|
||||
what possible boundary conditions can occur. A common case is an environment
|
||||
that is isolated from the outside world. That is, the change in concentration at
|
||||
the boundary over time has to be zero. We can generalize this condition to the
|
||||
*Neumann boundary condition*, where the derivative at the boundary is given. The
|
||||
case of a closed system then arises with a derivative of zero, so that there can
|
||||
be no flow across the boundary of the system. Thus, the Neumann boundary
|
||||
condition for the left side yields the following formulation
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\text{Neumann condition for the left boundary:~} \frac{\partial C(x_{0-\frac{\Delta x}{2}}, t)}{\partial t} = g(t) \text{,~} t > 0.
|
||||
\end{aligned}
|
||||
|
||||
The second common case is a constant boundary. Again, the case can be
|
||||
generalized, this time to the so-called *Dirichlet boundary condition*. Here,
|
||||
the function value of the boundary is given instead of its derivative. For the
|
||||
example we can write this boundary condition as
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\text{Dirichlet condition for the left boundary:~} C(x_{0 - \frac{\Delta x}{2}}, t) = h(t) \text{, ~} t > 0.
|
||||
\end{aligned}
|
||||
|
||||
In practise, constants are often used instead of time-dependent
|
||||
functions. [LOGAN15]_ [CHAPRA15]_ [SALSA22]_
|
||||
|
||||
|
||||
With this knowledge, we can now turn to the concrete implementation of
|
||||
finite differences for the explicit and implicit scheme. Centered
|
||||
differences are always used for the spatial component. Only in the time
|
||||
domain we distinguish into forward (FTCS method) and backward (BTCS
|
||||
method) methods, which influence the corresponding solution
|
||||
possibilities and stability properties.
|
||||
|
||||
Explicit Scheme (FTCS)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
FTCS stands for *Forward Time, Centered Space* and is an explicit
|
||||
procedure that can be solved iteratively. Explicit methods are generally
|
||||
more accurate, but are limited in their time step, so that incorrectly
|
||||
chosen time steps lead to instability of the method. Hoewever, there are
|
||||
estimates, such as the Courant-Friedrich-Lewy stability conditions,
|
||||
which gives the corresponding maximum possible time steps.
|
||||
[CHAPRA15]_
|
||||
|
||||
The goal now is to approximate both sides of the `one dimensional PDE`_. As the
|
||||
name of the method FTCS indicates, we use a forward approximation for the left
|
||||
temporal component. For the right-hand side, we use a central approximation
|
||||
using the `first order derivative`_. This results in the following approximation
|
||||
for the inner cells (in the example the cells :math:`x_1`, :math:`x_2` and
|
||||
:math:`x_3`) and a constant diffusion coefficient :math:`\alpha`
|
||||
|
||||
.. _`approximate first order diffusion`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{C_i^{t+1}-C_i^t}{\Delta t}=\alpha \frac{\frac{C_{i+1}^t-C_i^t}{\Delta x}-\frac{C_i^t-C_{i-1}^t}{\Delta x}}{\Delta x}.
|
||||
\label{eq:approx_first_order_diffusion}
|
||||
\end{aligned}
|
||||
|
||||
By rearranging this equation according to the concentration value for the next
|
||||
time step :math:``C_i^{t+1}`, we get
|
||||
|
||||
.. _`explicit scheme`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
C_i^{t+1} = C_i^t + \frac{\alpha \cdot \Delta t}{\Delta x^2} \left[C_{i+1}^t - 2C_i^t + C_{i-1}^t \right].
|
||||
\label{eq:explicit_scheme}
|
||||
\end{aligned}
|
||||
|
||||
At this point, it should be clear why this method is an explicit procedure. On
|
||||
the right side of the `explicit scheme`_, there are only known quantities, so
|
||||
that an explicit calculation rule is given. The basic procedure is shown in
|
||||
Figure 4. In the case of an explicit procedure, we use the values of the
|
||||
neighboring cells of the current time step to approximate the value of a cell
|
||||
for the next time step.
|
||||
|
||||
.. figure:: images/explicit_scheme.svg
|
||||
|
||||
Illustration for using the existing values in the time domain (green) for approximation of the next time step (red)
|
||||
|
||||
|
||||
As described above, the `explicit scheme`_ applies only to the inner cells. For
|
||||
the edges we now have to differentiate between the already presented boundary
|
||||
conditions of Dirichlet and Neumann. To do this, we look again at the
|
||||
`approximate first order diffusion`_ and consider what would have to be changed
|
||||
in the case of constant boundary conditions (Dirichlet). It quickly becomes
|
||||
apparent that in the case of the left cell the value :math:`C_{i-1}` corresponds
|
||||
to the constant value of the left boundary :math:`l` and in the case of the
|
||||
right cell :math:`C_{i+1}` to the value of the right boundary :math:`r`. In
|
||||
addition, the length difference between the cells is now no longer :math:`\Delta
|
||||
x`, but only :math:`\frac{\Delta x}{2}`, as we go from the center of the cell to
|
||||
the edge. For a given flux, the first derivative has to take this value. In our
|
||||
case, this is approximated with the help of the difference quotient, so that
|
||||
this is omitted in the case of a closed boundary (the flux is equal to zero) or
|
||||
accordingly assumes a constant value. For the left-hand boundary, this results
|
||||
in the following modifications, whereas the values for the right-hand boundary
|
||||
can be derived equivalently
|
||||
|
||||
.. _`boundary conditions`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\text{Dirichlet for the left boundary:~}& \frac{C_0^{t+1}-C_0^t}{\Delta t}=\alpha \frac{\frac{C_{1}^t-C_0^t}{\Delta x}-\frac{C_0^t-l}{{\frac{\Delta x}{2}}}}{\Delta x}\\
|
||||
&C_{0}^{t+1} = C_{0}^t + \frac{\alpha \Delta t}{\Delta x^2} \left [C_{1}^t - 3 C_0^t + 2l \right]\\
|
||||
\text{Closed Neumann for the left boundary:~}& \frac{C_0^{t+1}-C_0^t}{\Delta t}=\alpha \frac{\frac{C_{1}^t-C_0^t}{\Delta x}-\cancelto{0}{l}}{\Delta x}\\
|
||||
&C_{0}^{t+1} = C_{0}^t + \frac{\alpha \Delta t}{\Delta x^2} \left [C_1^t - C_0^t \right].
|
||||
\label{eq:boundary_conditions}
|
||||
\end{aligned}
|
||||
|
||||
Again, it is straightforward to extend the `explicit scheme`_ to the second
|
||||
dimension, so that the following formula is simply obtained for the inner cells
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
C_{i,j}^{t+1} = C_{i,j}^t +& \frac{\alpha_x \cdot \Delta t}{\Delta x^2} \left[C_{i, j+1}^t - 2C_{i,j}^t + C_{i, j-1} \right] \\
|
||||
+& \frac{\alpha_y \cdot \Delta t}{\Delta y^2} \left[C_{i+1, j}^t - 2C_{i,j}^t + C_{i-1, j} \right].
|
||||
\end{aligned}
|
||||
|
||||
Here, it is important to note that the indices :math:`i` and :math:`j`
|
||||
for the two-dimensional cases are now used as usual in the matrix
|
||||
notation. That means :math:`i` marks the elements in x-direction and
|
||||
:math:`j` in y-direction.
|
||||
|
||||
The previous equations referred to homogeneous diffusion coefficients, i.e. the
|
||||
diffusion properties along a spatial direction are identical. However, we are
|
||||
often interested in applications where different diffusion coefficients act in
|
||||
each discretized cell. This extension of the problem also leads to a slightly
|
||||
modified variant of our `one dimensional PDE`_, where the diffusion coefficient
|
||||
now is also a function of the spatial component
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{\partial C}{\partial t} = \frac{\partial}{\partial x}\left[\alpha(x) \frac{\partial C}{\partial x} \right].
|
||||
\end{aligned}
|
||||
|
||||
For this case, the literature recommends discretizing the problem
|
||||
directly at the boundaries of the grid cells, i.e. halfway between the
|
||||
grid points [FROLKOVIC1990]_. If we
|
||||
follow this scheme and approximate the first derivative for C at the
|
||||
appropriate cell boundaries, we obtain the following expressions
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{cases}
|
||||
\alpha(x_{i+\frac{1}{2}}) \frac{\partial C}{\partial x}(x_{i+\frac{1}{2}}) = \alpha_{i+\frac{1}{2}} \left(\frac{C_{i+1} - C_i}{\Delta x}\right)\\
|
||||
\alpha(x_{i-\frac{1}{2}}) \frac{\partial C}{\partial x}(x_{i-\frac{1}{2}}) = \alpha_{i-\frac{1}{2}} \left(\frac{C_{i} - C_{i-1}}{\Delta x}\right).
|
||||
\end{cases}
|
||||
\end{aligned}
|
||||
|
||||
With a further derivation we now obtain a spatially centered
|
||||
approximation with
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{\partial }{\partial x}\left(\alpha(x) \frac{\partial C}{\partial x} \right) \simeq& \frac{1}{\Delta x}\left[\alpha_{i+\frac{1}{2}}\left(\frac{C^t_{i+1}-C^t_i}{\Delta x}\right)-\alpha_{i-\frac{1}{2}}\left(\frac{C^t_i-C^t_{i-1}}{\Delta x}\right)\right] \\
|
||||
\frac{C^{t+1}_{i}-C^t_i}{\Delta t}=& \frac{1}{\Delta x^2}\left[\alpha_{i+\frac{1}{2}} C^t_{i+1}-\left(\alpha_{i+\frac{1}{2}}+\alpha_{i-\frac{1}{2}}\right) C^t_i+\alpha_{i-\frac{1}{2}} C^t_{i-1}\right]\\
|
||||
C^{t+1}_{i} =& C^t_i + \frac{\Delta t}{\Delta x^2} \left[\alpha_{i+\frac{1}{2}} C^t_{i+1}-\left(\alpha_{i+\frac{1}{2}}+\alpha_{i-\frac{1}{2}}\right) C^t_i+\alpha_{i-\frac{1}{2}} C^t_{i-1} \right].
|
||||
\end{aligned}
|
||||
|
||||
The value for the inter-cell diffusion coefficients can be determined by
|
||||
either the arithmetic or harmonic mean of both cells, with the harmonic
|
||||
mean being preferred for the default case. Again, we can extend this
|
||||
equation to two dimensions, resulting in the following iteration rule
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
C_{i, j}^{t+1}= & C_{i, j}^t+ \frac{\Delta t}{\Delta x^2}\left[\alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^t-\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right) C_{i, j}^t+\alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^t \right]+ \\ & \frac{\Delta t}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^t-\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right) C_{i, j}^t+\alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^t \right].
|
||||
\end{aligned}
|
||||
|
||||
The corresponding equations for the boundary conditions can be derived
|
||||
analogously to the homogeneous case (cf. `boundary conditions`_) by adjusting
|
||||
the relevant difference quotients to the respective boundary condition. As
|
||||
described initially, the FTCS procedure is accurate but not stable for each time
|
||||
step. The Courant-Friedrichs-Lewy stability condition states that the time step
|
||||
must always be less than or equal the following value
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\Delta t \leq \frac{\min (\Delta x^2, \Delta y^2)}{4 \cdot \max (\alpha^x, \alpha^y)}.
|
||||
\end{aligned}
|
||||
|
||||
That is, the maximum time step is quadratically related to the
|
||||
discretization and linearly to the maximum diffusion coefficient.
|
||||
Especially for very fine-resolution grids, this condition has a
|
||||
particularly strong effect on that required computing time. This is in
|
||||
contrast to the implicit methods, which we will now look at in more
|
||||
detail. Unlike the explicit methods, the implicit methods are
|
||||
unconditionally stable.
|
||||
|
||||
Implicit Scheme (BTCS)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The main difference to the explicit method is that the discretization is not
|
||||
done at the time step :math:`t` as in the `approximate first order diffusion`_,
|
||||
but now at the time step :math:`t+1`. Hence, the neighboring cells in the next
|
||||
time step are used for the approximation of the middle cell.
|
||||
|
||||
.. figure:: images/implicit_scheme.svg
|
||||
|
||||
Illustration of the implicit method, where the values of the neighboring cells in the next time step are used for the approximation
|
||||
|
||||
|
||||
The `approximate first order diffusion`_ thus changes to
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{C_i^{t+1}-C_i^t}{\Delta t} & =\alpha \frac{\frac{C_{i+1}^{t+1}-C_i^{t+1}}{\Delta x}-\frac{C_i^{t+1}-C_{i-1}^{t+1}}{\Delta x}}{\Delta x} \\
|
||||
& =\alpha \frac{C_{i-1}^{t+1}-2 C_i^{t+1}+C_{i+1}^{t+1}}{\Delta x^2}
|
||||
\end{aligned}
|
||||
|
||||
Now there is no possibility to change the equation to :math:`C^{t+1}_i`
|
||||
so that all values are given. That means :math:`C^{t+1}_i` is only
|
||||
implicitly indicated. If we know put all known and unkown values on one
|
||||
side each and define :math:`s_x = \frac{\alpha \Delta t}{\Delta x^2}`
|
||||
for simplicity, we get the following expression
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
s_x \cdot C_{i+1}^{t+1} + (-1-s_x) \cdot C_i^{t+1} + s_x \cdot C_{i-1}^{t+1} &= -C_i^t.
|
||||
\label{eq:implicit_inner_grid}
|
||||
\end{aligned}
|
||||
|
||||
This applies only to the inner grid without boundaries. To derive the equations
|
||||
for the boundaries, the reader can equivalently use the procedure from the FTCS
|
||||
method with given `boundary conditions`_. Thus, for constant boundaries with the
|
||||
values :math:`l` and :math:`r` for the left and right boundaries, respectively,
|
||||
the following two equations are obtained
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\left(-1-3 s_x\right) \cdot C_0^{t+1}+s_x \cdot C_1^{t+1} &= -C_0^t - 2s_x \cdot l \\
|
||||
s_x \cdot C_{n-1}^{t+1}+\left(-1-3 s_x\right) \cdot C_n^{t+1} &= -C_n^t - 2s_x \cdot r.
|
||||
\end{aligned}
|
||||
|
||||
The question now arises how values from the next time step can be used for the
|
||||
approximation. Here, the answer lies in the simultaneous solution of the
|
||||
equations. Let’s look again at the example in Figure for conservation law and
|
||||
establish the implicit equations for the same. We obtain the following system of
|
||||
five equations and five unknowns
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{cases}
|
||||
\left(-1-3 s_x\right) \cdot C_0^{t+1}+s_x \cdot C_1^{t+1} &= -C_0^t - 2s_x \cdot l \\
|
||||
s_x \cdot C_{2}^{t+1} + (-1-s_x) \cdot C_1^{t+1} + s_x \cdot C_{0}^{t+1} &= -C_1^t \\
|
||||
s_x \cdot C_{3}^{t+1} + (-1-s_x) \cdot C_2^{t+1} + s_x \cdot C_{1}^{t+1} &= -C_2^t \\
|
||||
s_x \cdot C_{4}^{t+1} + (-1-s_x) \cdot C_3^{t+1} + s_x \cdot C_{2}^{t+1} &= -C_3^t \\
|
||||
s_x \cdot C_{3}^{t+1}+\left(-1-3 s_x\right) \cdot C_4^{t+1} &= -C_4^t - 2s_x \cdot r.
|
||||
\end{cases}
|
||||
\end{aligned}
|
||||
|
||||
If we transfer this system of equations to a matrix-vector system, we
|
||||
get
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{bmatrix}
|
||||
-1-3s_x & s_x & 0 & 0 & 0 \\
|
||||
s_x & -1-3s_x & s_x & 0 & 0 \\
|
||||
0 & s_x & -1-3s_x & s_x & 0 \\
|
||||
0 & 0 & s_x & -1-3s_x & s_x \\
|
||||
0 & 0 & 0 & s_x & -1-3s_x
|
||||
\end{bmatrix}
|
||||
\cdot
|
||||
\begin{bmatrix}
|
||||
-C_0^{t+1}\\
|
||||
-C_1^{t+1}\\
|
||||
-C_2^{t+1}\\
|
||||
-C_3^{t+1}\\
|
||||
-C_4^{t+1}
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
-C_0^t - 2s_x \cdot l \\
|
||||
-C_1^t \\
|
||||
-C_2^t \\
|
||||
-C_3^t \\
|
||||
-C_4^t - 2s_x \cdot r
|
||||
\end{bmatrix}.
|
||||
\end{aligned}
|
||||
|
||||
This system can now be solved very efficiently, since it is a so-called
|
||||
tridiagonal system. Very fast solution algorithms exist for this, such
|
||||
as the Thomas algorithm.
|
||||
|
||||
In principle, this procedure can be extended again to the
|
||||
two-dimensional case, but here no tridiagonal system arises any more, so
|
||||
that the efficient solution algorithms are no longer applicable.
|
||||
Therefore, one uses a trick and solves the equations in two half time
|
||||
steps. In the first time step from :math:`t\rightarrow t+\frac{1}{2}`
|
||||
one space direction is calculated implicitly and the other one
|
||||
explicitly, whereas in the second time step from
|
||||
:math:`t+\frac{1}{2} \rightarrow t+1` the whole process is reversed and
|
||||
the other space direction is solved by the implicit method. This
|
||||
approach is called the alternative-direction implicit method, which we
|
||||
will now examine in more detail.
|
||||
|
||||
First, we consider the `two dimensional PDE`_ again, which represents the PDE
|
||||
for diffusion in the two-dimensional case with homogeneous and anisotropic
|
||||
diffusion coefficients. As explained in the upper sections, we can approximate
|
||||
the second derivative for each spatial direction as follows
|
||||
|
||||
.. _`Taylor series`:
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{\partial^2 C^t_{i, j}}{\partial x^2} =& \frac{C^t_{i, j-1} - 2C^t_{i,j} + C^t_{i, j+1}}{\Delta x^2} \label{eq:taylor_2_x}\\
|
||||
\frac{\partial^2 C^t_{i, j}}{\partial y^2} =& \frac{C^t_{i-1, j} - 2C^t_{i,j} + C^t_{i+1, j}}{\Delta y^2}. \label{eq:taylor_2_y}
|
||||
\end{aligned}
|
||||
|
||||
Explicit solution methods, as can be used in the one-dimensional case,
|
||||
require smaller time steps to remain stable when applied to solve
|
||||
two-dimensional problems. This leads to a significant increase in the
|
||||
computational effort. Implicit techniques also require more
|
||||
computational capacity, since the matrices can no longer be represented
|
||||
tridiagonally in the two- or three-dimensional case and thus cannot be
|
||||
solved efficiently. A solution to this problem is provided by the ADI
|
||||
(alternating-direction implicit) scheme, which transforms
|
||||
two-dimensional problems back into tridiagonal matrices. Here, each time
|
||||
step is performed in two half-steps, each time solving implicitly in one
|
||||
axis direction. [CHAPRA15]_ For the above equations,
|
||||
the ADI scheme is defined as follows
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{cases}
|
||||
\frac{C_{i,j}^{t+\frac{1}{2}}-C_{i,j}^t}{\frac{\Delta t}{2}} = \alpha_x \cdot \frac{\partial^2 C_{i,j}^{t+\frac{1}{2}}}{\partial x^2} + \alpha_y \frac{\partial^2 C_{i,j}^t}{\partial y^2} \\
|
||||
\frac{C_{i,j}^{t+1}-C_{i,j}^{t+\frac{1}{2}}}{\frac{\Delta t}{2}} = \alpha_x \cdot \frac{\partial^2 C_{i,j}^{t+\frac{1}{2}}}{\partial x^2} + \alpha_y \frac{\partial^2 C_{i,j}^{t+1}}{\partial y^2}
|
||||
\end{cases}.
|
||||
\label{eq:adi_scheme}
|
||||
\end{aligned}
|
||||
|
||||
Now the `Taylor series`_ can be substituted into the equation and the term
|
||||
:math:`\frac{\Delta t}{2}` can be placed on the right side of the equation. The
|
||||
following expression is generated when introducing :math:`s_x = \frac{\alpha_x
|
||||
\cdot \Delta t}{2 \cdot \Delta x^2}` and :math:`s_y = \frac{\alpha_y \cdot
|
||||
\Delta t}{2 \cdot \Delta y^2}` as new variables
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
C_{i,j}^{t+\frac{1}{2}} - C_{i,j}^t &= s_x \cdot \left[~C_{i, j-1}^{t+\frac{1}{2}}-2 C_{i,j}^{t+\frac{1}{2}}+C_{i, j+1}^{t+\frac{1}{2}}\right] + s_y~ \cdot \left[~ C_{i-1, j}^t - 2\cdot C_{i,j}^t + C_{i+1,j}^t\right] \\
|
||||
C_{i,j}^{t+1} - C_{i,j}^{t+\frac{1}{2}} &= s_x \cdot \left[C_{i, j-1}^{t+\frac{1}{2}}-2 C_{i,j}^{t+\frac{1}{2}}+C_{i, j+1}^{t+\frac{1}{2}}\right] + s_y \cdot ~\left[ C_{i-1, j}^{t+1} - 2\cdot C_{i,j}^{t+1} + C_{i+1,j}^{t+1}\right].
|
||||
\end{aligned}
|
||||
|
||||
As it can be obtained by the following Figure the above equations are only valid
|
||||
for the inner grid.
|
||||
|
||||
.. figure:: images/grid_with_boundaries_example.png
|
||||
|
||||
Example grid with boundary conditions
|
||||
|
||||
At the edge of
|
||||
the grid, starting from the cell center, it is not possible to take a
|
||||
full step :math:`dx`, but only a half step :math:`\frac{dx}{2}`.
|
||||
Therefore, the approximation for the inner cells (shown as an example
|
||||
for the x-direction)
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{C_{i,j}^{t+1} - C_{i,j}^t}{\Delta t} = \frac{\frac{C_{i, j+1}^{t+1} - C_{i,j}^{t+1}}{\Delta x} - \frac{C_{i, j}^{t+1} - C_{i, j-1}^{t+1}}{\Delta x}}{\Delta x} = \frac{C_{i, j+1}^{t+1} - 2 \cdot C_{i, j}^{t+1} + C_{i, j-1}^{t+1}}{\Delta x^2}
|
||||
\end{aligned}
|
||||
|
||||
is no longer valid and we have to perform the same changes to the
|
||||
boundary conditions as for the other methods by replacing the
|
||||
corresponding concentration values or difference quotiens with the
|
||||
respective boundary concentrations or fluxes depending on the boundary
|
||||
condition (Neumann or Dirichlet).
|
||||
|
||||
We can now quickly introduce the ADI scheme for heterogeneous diffusion
|
||||
by using the same discretization logic as for the FTCS procedure and
|
||||
discretizing the points between cells.
|
||||
|
||||
This results in the following ADI scheme
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{cases}
|
||||
\frac{C_{i, j}^{t+\frac{1}{2}}-C_{i, j}^t}{\Delta \frac{t}{2}}= & \frac{1}{\Delta x^2}\left[\alpha_{i, j+ \frac{1}{2}} C_{i, j+1}^{t+ \frac{1}{2}}-\left(\alpha_{i, j+ \frac{1}{2}}+\alpha_{i, j- \frac{1}{2}}\right) C_{i, j}^{t+ \frac{1}{2}}+\alpha_{i, j-\frac{1}{2}} C_{i, j-1}^{t+\frac{1}{2}}\right]+ \\ & \frac{1}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j} C_{i+1, j}^t-\left(\alpha_{i+\frac{1}{2}, j}+\alpha_{i-\frac{1}{2}, j}\right) C_{i, j}^t+\alpha_{i-\frac{1}{2}, j} C_{i-1, j}^t\right] \\
|
||||
\frac{C_{i, j}^{t+1}-C_{i, j}^{t+\frac{1}{2}}}{\Delta \frac{t}{2}}= & \frac{1}{\Delta x^2}\left[\alpha_{i, j+\frac{1}{2}} C_{i, j+1}^{t+\frac{1}{2}}-\left(\alpha_{i, j+\frac{1}{2}}+\alpha_{i, j-\frac{1}{2}}\right) C_{i, j}^{t+\frac{1}{2}}+\alpha_{i, j-\frac{1}{2}} C_{i, j-1}^{t+\frac{1}{2}}\right]+ \\ & \frac{1}{\Delta y^2}\left[\alpha_{i+\frac{1}{2}, j} C_{i+1, j}^{t+1}-\left(\alpha_{i+\frac{1}{2}, j}+\alpha_{i-\frac{1}{2}, j}\right) C_{i, j}^{t+1}+\alpha_{i-\frac{1}{2}, j} C_{i-1, j}^{t+1}\right].
|
||||
\end{cases}
|
||||
\end{aligned}
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\begin{cases}
|
||||
-s_x \alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^{t+\frac{1}{2}}+\left(1+s_x\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right)\right) C_{i, j}^{t+\frac{1}{2}}-s_x \alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^{t+\frac{1}{2}}= \\ s_y \alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^t+\left(1-s_y\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right)\right) C_{i, j}^t+s_y \alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^t\\
|
||||
-s_y \alpha_{i+\frac{1}{2}, j}^y C_{i+1, j}^{t+1}+\left(1+s_y\left(\alpha_{i+\frac{1}{2}, j}^y+\alpha_{i-\frac{1}{2}, j}^y\right)\right) C_{i, j}^{t+1}-s_y \alpha_{i-\frac{1}{2}, j}^y C_{i-1, j}^{t+1}= \\ s_x \alpha_{i, j+\frac{1}{2}}^x C_{i, j+1}^{t+\frac{1}{2}}+\left(1-s_x\left(\alpha_{i, j+\frac{1}{2}}^x+\alpha_{i, j-\frac{1}{2}}^x\right)\right) C_{i, j}^{t+\frac{1}{2}}+s_x \alpha_{i, j-\frac{1}{2}}^x C_{i, j-1}^{t+\frac{1}{2}}.
|
||||
\end{cases}
|
||||
\end{aligned}
|
||||
|
||||
|
||||
Rearranging the terms according to known and unknown quantities gives us again
|
||||
the scheme as in the one-dimensional case and allows us to set up a tridiagonal
|
||||
system for each row of the grid. We also have to take into account that the ADI
|
||||
method always requires two calculation steps for each complete time step.
|
||||
However, since the implicit method is unconditionally stable, it shows its
|
||||
adavantage especially with larger time steps. The basic procedure of the ADI
|
||||
scheme with the two time steps is shown here:
|
||||
|
||||
.. figure:: images/adi_scheme.svg
|
||||
|
||||
Illustration of the iterative procedure in the ADI process
|
||||
|
||||
Crank-Nicolson method
|
||||
---------------------
|
||||
|
||||
Another implicit method is the Crank-Nicolson (CRNI) method, which uses both
|
||||
forward and backward approximations. As this method does not represent a main
|
||||
goal of this work, it is only briefly mentioned here. Essentially, the
|
||||
Crank-Nicolson method averages the two finite differences variants at the
|
||||
corresponding point. For the one-dimensional case this results in
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{aligned}
|
||||
\frac{C^{t+1}_i - C_i^{t}}{\Delta t} = \frac{1}{2} \cdot \left[\frac{C^{t}_{i+1} - 2C^t_i + C_{i+1}^t}{\Delta x^2} + \frac{C^{t+1}_{i+1} - 2C_i^{t+1} + C_i^{t+1}}{\Delta x^2} \right].
|
||||
\end{aligned}
|
||||
|
||||
By transforming the terms accordingly, it is also possible to generate a
|
||||
tridiagonal matrix, as in the BTCS method, which can be solved very
|
||||
efficiently with the Thomas algorithm. [CHAPRA15]_
|
||||
Like the BTCS method, CRNI is unconditionally stable, but becomes
|
||||
inaccurate if the time steps are too large. Compared to the FTCS and
|
||||
BTCS methods, which have a temporal truncation error of
|
||||
:math:`\mathcal{O}(\Delta t)`, the CRNI method with an error of
|
||||
:math:`\mathcal{O}(\Delta t^2)` has an advantage and should be preferred
|
||||
for time-accurate solutions.
|
||||
|
||||
.. [LOGAN15] Logan, J. David. Applied Partial Differential Equations.
|
||||
Undergraduate Texts in Mathematics. Cham: Springer International
|
||||
Publishing, 2015. https://doi.org/10.1007/978-3-319-12493-3.
|
||||
|
||||
.. [SALSA22] Salsa, Sandro, and Gianmaria Verzini. Partial Differential
|
||||
Equations in Action. Vol. 147. UNITEXT. Cham: Springer
|
||||
International Publishing, 2022.
|
||||
https://doi.org/10.1007/978-3-031-21853-8.
|
||||
|
||||
.. [CHAPRA15] Chapra, Steven C., and Raymond P. Canale. Numerical Methods for
|
||||
Engineers, 2015.
|
||||
|
||||
|
||||
.. [BAEHR19] Baehr, Hans Dieter, and Karl Stephan. Wärme- Und Stoffübertragung.
|
||||
Berlin, Heidelberg: Springer Berlin Heidelberg, 2019.
|
||||
https://doi.org/10.1007/978-3-662-58441-5.
|
||||
|
||||
.. [FROLKOVIC1990] Frolkovič, Peter. “Numerical Recipes: The Art of Scientific
|
||||
Computing.” Acta Applicandae Mathematicae 19, no. 3 (June
|
||||
1990): 297–99. https://doi.org/10.1007/BF01321860.
|
||||
|
||||
.. [1]
|
||||
The minus sign ensures that the direction of the flux follows the
|
||||
decrease in concentration.
|
||||
|
||||
.. [2]
|
||||
A function is called smooth if it is arbitrarily often
|
||||
differentiable.
|
||||
|
||||
.. [3]
|
||||
The remainder term is not considered further at this point, but is
|
||||
:math:`\mathcal{O}(h^2)` in the centered variant. Thus, the centered
|
||||
variant is more accurate than the forward or backward oriented
|
||||
method, whose errors can be estimated with :math:`\mathcal{O}(h)`.
|
||||
[CHAPRA15]_
|
||||
|
||||
@ -3,7 +3,5 @@ User API
|
||||
|
||||
.. toctree::
|
||||
|
||||
:maxdepth: 2
|
||||
Grid
|
||||
Boundary
|
||||
Simulation
|
||||
Diffusion
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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_
|
||||
|
||||
388
include/tug/Core/BaseSimulation.hpp
Normal file
@ -0,0 +1,388 @@
|
||||
#pragma once
|
||||
|
||||
#include "tug/Boundary.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tug/Core/Matrix.hpp>
|
||||
#include <tug/Core/TugUtils.hpp>
|
||||
|
||||
namespace tug {
|
||||
|
||||
/**
|
||||
* @brief Enum holding different options for .csv output.
|
||||
*
|
||||
*/
|
||||
enum class CSV_OUTPUT {
|
||||
OFF, /*!< do not produce csv output */
|
||||
ON, /*!< produce csv output with last concentration matrix */
|
||||
VERBOSE, /*!< produce csv output with all concentration matrices */
|
||||
XTREME /*!< csv output like VERBOSE but additional boundary
|
||||
conditions at beginning */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enum holding different options for console output.
|
||||
*
|
||||
*/
|
||||
enum class CONSOLE_OUTPUT {
|
||||
OFF, /*!< do not print any output to console */
|
||||
ON, /*!< print before and after concentrations to console */
|
||||
VERBOSE /*!< print all concentration matrices to console */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enum holding different options for time measurement.
|
||||
*
|
||||
*/
|
||||
enum class TIME_MEASURE {
|
||||
OFF, /*!< do not print any time measures */
|
||||
ON /*!< print time measure after last iteration */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A base class for simulation grids.
|
||||
*
|
||||
* This class provides a base implementation for simulation grids, including
|
||||
* methods for setting and getting grid dimensions, domain sizes, and output
|
||||
* options. It also includes methods for running simulations, which must be
|
||||
* implemented by derived classes.
|
||||
*
|
||||
* @tparam T The type of the elements in the grid.
|
||||
*/
|
||||
template <typename T> class BaseSimulationGrid {
|
||||
private:
|
||||
CSV_OUTPUT csv_output{CSV_OUTPUT::OFF};
|
||||
CONSOLE_OUTPUT console_output{CONSOLE_OUTPUT::OFF};
|
||||
TIME_MEASURE time_measure{TIME_MEASURE::OFF};
|
||||
|
||||
int iterations{1};
|
||||
RowMajMatMap<T> concentrationMatrix;
|
||||
Boundary<T> boundaryConditions;
|
||||
|
||||
const std::uint8_t dim;
|
||||
|
||||
T delta_col;
|
||||
T delta_row;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a BaseSimulationGrid from a given RowMajMat object.
|
||||
*
|
||||
* This constructor initializes a BaseSimulationGrid using the data, number of
|
||||
* rows, and number of columns from the provided RowMajMat object.
|
||||
*
|
||||
* @tparam T The type of the elements in the RowMajMat.
|
||||
* @param origin The RowMajMat object from which to initialize the
|
||||
* BaseSimulationGrid.
|
||||
*/
|
||||
BaseSimulationGrid(RowMajMat<T> &origin)
|
||||
: BaseSimulationGrid(origin.data(), origin.rows(), origin.cols()) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a BaseSimulationGrid object.
|
||||
*
|
||||
* @tparam T The type of the data elements.
|
||||
* @param data Pointer to the data array.
|
||||
* @param rows Number of rows in the grid.
|
||||
* @param cols Number of columns in the grid.
|
||||
*
|
||||
* Initializes the concentration_matrix with the provided data, rows, and
|
||||
* columns. Sets delta_col and delta_row to 1. Determines the dimension (dim)
|
||||
* based on the number of rows: if rows == 1, dim is set to 1; otherwise, dim
|
||||
* is set to 2.
|
||||
*/
|
||||
BaseSimulationGrid(T *data, std::size_t rows, std::size_t cols)
|
||||
: concentrationMatrix(data, rows, cols), boundaryConditions(rows, cols),
|
||||
delta_col(1), delta_row(1), dim(rows == 1 ? 1 : 2) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a BaseSimulationGrid with a single dimension.
|
||||
*
|
||||
* This constructor initializes a BaseSimulationGrid object with the provided
|
||||
* data and length. It assumes the grid has only one dimension.
|
||||
*
|
||||
* @param data Pointer to the data array.
|
||||
* @param length The length of the data array.
|
||||
*/
|
||||
BaseSimulationGrid(T *data, std::size_t length)
|
||||
: BaseSimulationGrid(data, 1, length) {}
|
||||
|
||||
/**
|
||||
* @brief Overloaded function call operator to access elements in a
|
||||
* one-dimensional grid.
|
||||
*
|
||||
* This operator provides access to elements in the concentration matrix using
|
||||
* a single index. It asserts that the grid is one-dimensional before
|
||||
* accessing the element.
|
||||
*
|
||||
* @tparam T The type of elements in the concentration matrix.
|
||||
* @param index The index of the element to access.
|
||||
* @return A reference to the element at the specified index in the
|
||||
* concentration matrix.
|
||||
*/
|
||||
constexpr T &operator()(std::size_t index) {
|
||||
tug_assert(dim == 1, "Grid is not one dimensional, use 2D index operator!");
|
||||
|
||||
return concentrationMatrix(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overloaded function call operator to access elements in a 2D
|
||||
* concentration matrix.
|
||||
*
|
||||
* This operator allows accessing elements in the concentration matrix using
|
||||
* row and column indices. It asserts that the grid is two-dimensional before
|
||||
* accessing the element.
|
||||
*
|
||||
* @param row The row index of the element to access.
|
||||
* @param col The column index of the element to access.
|
||||
* @return A reference to the element at the specified row and column in the
|
||||
* concentration matrix.
|
||||
*/
|
||||
constexpr T &operator()(std::size_t row, std::size_t col) {
|
||||
tug_assert(dim == 2, "Grid is not two dimensional, use 1D index operator!");
|
||||
|
||||
return concentrationMatrix(row, col);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the concentration matrix.
|
||||
*
|
||||
* @tparam T The data type of the elements in the concentration matrix.
|
||||
* @return RowMajMat<T>& Reference to the concentration matrix.
|
||||
*/
|
||||
RowMajMatMap<T> &getConcentrationMatrix() { return concentrationMatrix; }
|
||||
|
||||
const RowMajMatMap<T> &getConcentrationMatrix() const {
|
||||
return concentrationMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the boundary conditions for the simulation.
|
||||
*
|
||||
* @tparam T The type parameter for the Boundary class.
|
||||
* @return Boundary<T>& A reference to the boundary conditions.
|
||||
*/
|
||||
Boundary<T> &getBoundaryConditions() { return boundaryConditions; }
|
||||
|
||||
const Boundary<T> &getBoundaryConditions() const {
|
||||
return boundaryConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the dimension value.
|
||||
*
|
||||
* @return The dimension value as an 8-bit unsigned integer.
|
||||
*/
|
||||
std::uint8_t getDim() const { return dim; }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of rows in the concentration matrix.
|
||||
*
|
||||
* @return std::size_t The number of rows in the concentration matrix.
|
||||
*/
|
||||
std::size_t rows() const { return concentrationMatrix.rows(); }
|
||||
|
||||
/**
|
||||
* @brief Get the number of columns in the concentration matrix.
|
||||
*
|
||||
* @return std::size_t The number of columns in the concentration matrix.
|
||||
*/
|
||||
std::size_t cols() const { return concentrationMatrix.cols(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the cell size in meter of the x-direction.
|
||||
*
|
||||
* This function returns the value of the delta column, which is used
|
||||
* to represent the difference or change in the column value.
|
||||
*
|
||||
* @return T The cell size in meter of the x-direction.
|
||||
*/
|
||||
T deltaCol() const { return delta_col; }
|
||||
|
||||
/**
|
||||
* @brief Returns the cell size in meter of the y-direction.
|
||||
*
|
||||
* This function asserts that the grid is two-dimensional. If the grid is not
|
||||
* two-dimensional, an assertion error is raised with the message "Grid is not
|
||||
* two dimensional, there is no delta in y-direction!".
|
||||
*
|
||||
* @return The cell size in meter of the y-direction.
|
||||
*/
|
||||
T deltaRow() const {
|
||||
tug_assert(
|
||||
dim == 2,
|
||||
"Grid is not two dimensional, there is no delta in y-direction!");
|
||||
|
||||
return delta_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the domain size in the X direction.
|
||||
*
|
||||
* This function calculates the size of the domain in the X direction by
|
||||
* multiplying the column spacing (delta_col) by the number of columns (cols).
|
||||
*
|
||||
* @return The size of the domain in the X direction.
|
||||
*/
|
||||
T domainX() const { return delta_col * cols(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the domain in the y-direction.
|
||||
*
|
||||
* This function calculates the size of the domain in the y-direction
|
||||
* by multiplying the row spacing (delta_row) by the number of rows.
|
||||
* It asserts that the grid is two-dimensional before performing the
|
||||
* calculation.
|
||||
*
|
||||
* @return The size of the domain in the y-direction.
|
||||
*/
|
||||
T domainY() const {
|
||||
tug_assert(
|
||||
dim == 2,
|
||||
"Grid is not two dimensional, there is no domain in y-direction!");
|
||||
|
||||
return delta_row * rows();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the domain length for a one-dimensional grid.
|
||||
*
|
||||
* This function sets the domain length for a one-dimensional grid and
|
||||
* calculates the column width (delta_col) based on the given domain length
|
||||
* and the number of columns. It asserts that the grid is one-dimensional and
|
||||
* that the given domain length is positive.
|
||||
*
|
||||
* @param domain_length The length of the domain. Must be positive.
|
||||
*/
|
||||
void setDomain(T domain_length) {
|
||||
tug_assert(dim == 1, "Grid is not one dimensional, use 2D domain setter!");
|
||||
tug_assert(domain_length > 0, "Given domain length is not positive!");
|
||||
|
||||
delta_col = domain_length / cols();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the domain size for a 2D grid simulation.
|
||||
*
|
||||
* This function sets the domain size in the x and y directions for a
|
||||
* two-dimensional grid simulation. It asserts that the grid is indeed
|
||||
* two-dimensional and that the provided domain sizes are positive.
|
||||
*
|
||||
* @tparam T The type of the domain size parameters.
|
||||
* @param domain_row The size of the domain in the y-direction.
|
||||
* @param domain_col The size of the domain in the x-direction.
|
||||
*/
|
||||
void setDomain(T domain_row, T domain_col) {
|
||||
tug_assert(dim == 2, "Grid is not two dimensional, use 1D domain setter!");
|
||||
tug_assert(domain_col > 0,
|
||||
"Given domain size in x-direction is not positive!");
|
||||
tug_assert(domain_row > 0,
|
||||
"Given domain size in y-direction is not positive!");
|
||||
|
||||
delta_row = domain_row / rows();
|
||||
delta_col = domain_col / cols();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the option to output the results to a CSV file. Off by default.
|
||||
*
|
||||
*
|
||||
* @param csv_output Valid output option. The following options can be set
|
||||
* here:
|
||||
* - CSV_OUTPUT_OFF: do not produce csv output
|
||||
* - CSV_OUTPUT_ON: produce csv output with last
|
||||
* concentration matrix
|
||||
* - CSV_OUTPUT_VERBOSE: produce csv output with all
|
||||
* concentration matrices
|
||||
* - CSV_OUTPUT_XTREME: produce csv output with all
|
||||
* concentration matrices and simulation environment
|
||||
*/
|
||||
void setOutputCSV(CSV_OUTPUT csv_output) { this->csv_output = csv_output; }
|
||||
|
||||
/**
|
||||
* @brief Retrieves the CSV output.
|
||||
*
|
||||
* This function returns the CSV output associated with the simulation.
|
||||
*
|
||||
* @return CSV_OUTPUT The CSV output of the simulation.
|
||||
*/
|
||||
constexpr CSV_OUTPUT getOutputCSV() const { return this->csv_output; }
|
||||
|
||||
/**
|
||||
* @brief Set the options for outputting information to the console. Off by
|
||||
* default.
|
||||
*
|
||||
* @param console_output Valid output option. The following options can be set
|
||||
* here:
|
||||
* - CONSOLE_OUTPUT_OFF: do not print any output to
|
||||
* console
|
||||
* - CONSOLE_OUTPUT_ON: print before and after
|
||||
* concentrations to console
|
||||
* - CONSOLE_OUTPUT_VERBOSE: print all concentration
|
||||
* matrices to console
|
||||
*/
|
||||
void setOutputConsole(CONSOLE_OUTPUT console_output) {
|
||||
this->console_output = console_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the console output.
|
||||
*
|
||||
* This function returns the current state of the console output.
|
||||
*
|
||||
* @return CONSOLE_OUTPUT The current console output.
|
||||
*/
|
||||
constexpr CONSOLE_OUTPUT getOutputConsole() const {
|
||||
return this->console_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the Time Measure option. Off by default.
|
||||
*
|
||||
* @param time_measure The following options are allowed:
|
||||
* - TIME_MEASURE_OFF: Time of simulation is not printed
|
||||
* to console
|
||||
* - TIME_MEASURE_ON: Time of simulation run is printed to
|
||||
* console
|
||||
*/
|
||||
void setTimeMeasure(TIME_MEASURE time_measure) {
|
||||
this->time_measure = time_measure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the current time measurement.
|
||||
*
|
||||
* @return TIME_MEASURE The current time measurement.
|
||||
*/
|
||||
constexpr TIME_MEASURE getTimeMeasure() const { return this->time_measure; }
|
||||
|
||||
/**
|
||||
* @brief Set the desired iterations to be calculated. A value greater
|
||||
* than zero must be specified here. Setting iterations is required.
|
||||
*
|
||||
* @param iterations Number of iterations to be simulated.
|
||||
*/
|
||||
void setIterations(int iterations) {
|
||||
tug_assert(iterations > 0,
|
||||
"Number of iterations must be greater than zero.");
|
||||
|
||||
this->iterations = iterations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the currently set iterations to be calculated.
|
||||
*
|
||||
* @return int Number of iterations.
|
||||
*/
|
||||
int getIterations() const { return this->iterations; }
|
||||
|
||||
/**
|
||||
* @brief Method starts the simulation process with the previously set
|
||||
* parameters.
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
virtual void setTimestep(T timestep) = 0;
|
||||
};
|
||||
} // namespace tug
|
||||
21
include/tug/Core/Matrix.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace tug {
|
||||
/**
|
||||
* @brief Alias template for a row-major matrix using Eigen library.
|
||||
*
|
||||
* This alias template defines a type `RowMajMat` which represents a row-major
|
||||
* matrix using the Eigen library. It is a template that takes a type `T` as its
|
||||
* template parameter. The matrix is dynamically sized with `Eigen::Dynamic` for
|
||||
* both rows and columns. The matrix is stored in row-major order.
|
||||
*
|
||||
* @tparam T The type of the matrix elements.
|
||||
*/
|
||||
template <typename T>
|
||||
using RowMajMat =
|
||||
Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
|
||||
|
||||
template <typename T> using RowMajMatMap = Eigen::Map<RowMajMat<T>>;
|
||||
} // namespace tug
|
||||
484
include/tug/Core/Numeric/BTCS.hpp
Normal file
@ -0,0 +1,484 @@
|
||||
/**
|
||||
* @file BTCS.hpp
|
||||
* @brief Implementation of heterogenous BTCS (backward time-centered space)
|
||||
* solution of diffusion equation in 1D and 2D space. Internally the
|
||||
* alternating-direction implicit (ADI) method is used. Version 2, because
|
||||
* Version 1 was an implementation for the homogeneous BTCS solution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BTCS_H_
|
||||
#define BTCS_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <tug/Boundary.hpp>
|
||||
#include <tug/Core/Matrix.hpp>
|
||||
#include <tug/Core/Numeric/SimulationInput.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#else
|
||||
#define omp_get_thread_num() 0
|
||||
#endif
|
||||
|
||||
namespace tug {
|
||||
|
||||
// optimization to remove Eigen sparse matrix
|
||||
template <class T> class Diagonals {
|
||||
public:
|
||||
Diagonals() : left(), center(), right() {};
|
||||
Diagonals(std::size_t size) : left(size), center(size), right(size) {};
|
||||
|
||||
public:
|
||||
std::vector<T> left;
|
||||
std::vector<T> center;
|
||||
std::vector<T> right;
|
||||
};
|
||||
|
||||
// calculates coefficient for boundary in constant case
|
||||
template <class T>
|
||||
constexpr std::pair<T, T> calcBoundaryCoeffConstant(T alpha_center,
|
||||
T alpha_side, T sx) {
|
||||
const T centerCoeff = 1 + sx * (calcAlphaIntercell(alpha_center, alpha_side) +
|
||||
2 * alpha_center);
|
||||
const T sideCoeff = -sx * calcAlphaIntercell(alpha_center, alpha_side);
|
||||
|
||||
return {centerCoeff, sideCoeff};
|
||||
}
|
||||
|
||||
// calculates coefficient for boundary in closed case
|
||||
template <class T>
|
||||
constexpr std::pair<T, T> calcBoundaryCoeffClosed(T alpha_center, T alpha_side,
|
||||
T sx) {
|
||||
const T centerCoeff = 1 + sx * calcAlphaIntercell(alpha_center, alpha_side);
|
||||
|
||||
const T sideCoeff = -sx * calcAlphaIntercell(alpha_center, alpha_side);
|
||||
|
||||
return {centerCoeff, sideCoeff};
|
||||
}
|
||||
|
||||
// creates coefficient matrix for next time step from alphas in x-direction
|
||||
template <class T>
|
||||
static Diagonals<T>
|
||||
createCoeffMatrix(const RowMajMat<T> &alpha,
|
||||
const std::vector<BoundaryElement<T>> &bcLeft,
|
||||
const std::vector<BoundaryElement<T>> &bcRight,
|
||||
const std::vector<std::pair<bool, T>> &inner_bc, int numCols,
|
||||
int rowIndex, T sx) {
|
||||
|
||||
// square matrix of column^2 dimension for the coefficients
|
||||
Diagonals<T> cm(numCols);
|
||||
|
||||
// left column
|
||||
if (inner_bc[0].first) {
|
||||
cm.center[0] = 1;
|
||||
} else {
|
||||
switch (bcLeft[rowIndex].getType()) {
|
||||
case BC_TYPE_CONSTANT: {
|
||||
auto [centerCoeffTop, rightCoeffTop] =
|
||||
calcBoundaryCoeffConstant(alpha(rowIndex, 0), alpha(rowIndex, 1), sx);
|
||||
cm.center[0] = centerCoeffTop;
|
||||
cm.right[0] = rightCoeffTop;
|
||||
break;
|
||||
}
|
||||
case BC_TYPE_CLOSED: {
|
||||
auto [centerCoeffTop, rightCoeffTop] =
|
||||
calcBoundaryCoeffClosed(alpha(rowIndex, 0), alpha(rowIndex, 1), sx);
|
||||
cm.center[0] = centerCoeffTop;
|
||||
cm.right[0] = rightCoeffTop;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw_invalid_argument(
|
||||
"Undefined Boundary Condition Type somewhere on Left or Top!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inner columns
|
||||
int n = numCols - 1;
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (inner_bc[i].first) {
|
||||
cm.center[i] = 1;
|
||||
continue;
|
||||
}
|
||||
cm.left[i] =
|
||||
-sx * calcAlphaIntercell(alpha(rowIndex, i - 1), alpha(rowIndex, i));
|
||||
cm.center[i] =
|
||||
1 +
|
||||
sx * (calcAlphaIntercell(alpha(rowIndex, i), alpha(rowIndex, i + 1)) +
|
||||
calcAlphaIntercell(alpha(rowIndex, i - 1), alpha(rowIndex, i)));
|
||||
cm.right[i] =
|
||||
-sx * calcAlphaIntercell(alpha(rowIndex, i), alpha(rowIndex, i + 1));
|
||||
}
|
||||
|
||||
// right column
|
||||
if (inner_bc[n].first) {
|
||||
cm.center[n] = 1;
|
||||
} else {
|
||||
switch (bcRight[rowIndex].getType()) {
|
||||
case BC_TYPE_CONSTANT: {
|
||||
auto [centerCoeffBottom, leftCoeffBottom] = calcBoundaryCoeffConstant(
|
||||
alpha(rowIndex, n), alpha(rowIndex, n - 1), sx);
|
||||
cm.left[n] = leftCoeffBottom;
|
||||
cm.center[n] = centerCoeffBottom;
|
||||
break;
|
||||
}
|
||||
case BC_TYPE_CLOSED: {
|
||||
auto [centerCoeffBottom, leftCoeffBottom] = calcBoundaryCoeffClosed(
|
||||
alpha(rowIndex, n), alpha(rowIndex, n - 1), sx);
|
||||
cm.left[n] = leftCoeffBottom;
|
||||
cm.center[n] = centerCoeffBottom;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw_invalid_argument(
|
||||
"Undefined Boundary Condition Type somewhere on Right or Bottom!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cm;
|
||||
}
|
||||
|
||||
// calculates explicit concentration at boundary in closed case
|
||||
template <typename T>
|
||||
constexpr T calcExplicitConcentrationsBoundaryClosed(T conc_center,
|
||||
T alpha_center,
|
||||
T alpha_neigbor, T sy) {
|
||||
return sy * calcAlphaIntercell(alpha_center, alpha_neigbor) * conc_center +
|
||||
(1 - sy * (calcAlphaIntercell(alpha_center, alpha_neigbor))) *
|
||||
conc_center;
|
||||
}
|
||||
|
||||
// calculates explicity concentration at boundary in constant case
|
||||
template <typename T>
|
||||
constexpr T calcExplicitConcentrationsBoundaryConstant(T conc_center, T conc_bc,
|
||||
T alpha_center,
|
||||
T alpha_neighbor, T sy) {
|
||||
const T inter_cell = calcAlphaIntercell(alpha_center, alpha_neighbor);
|
||||
return sy * inter_cell * conc_center +
|
||||
(1 - sy * (inter_cell + alpha_center)) * conc_center +
|
||||
sy * alpha_center * conc_bc;
|
||||
}
|
||||
|
||||
// creates a solution vector for next time step from the current state of
|
||||
// concentrations
|
||||
template <class T, class EigenType>
|
||||
static Eigen::VectorX<T>
|
||||
createSolutionVector(const EigenType &concentrations,
|
||||
const RowMajMat<T> &alphaX, const RowMajMat<T> &alphaY,
|
||||
const std::vector<BoundaryElement<T>> &bcLeft,
|
||||
const std::vector<BoundaryElement<T>> &bcRight,
|
||||
const std::vector<BoundaryElement<T>> &bcTop,
|
||||
const std::vector<BoundaryElement<T>> &bcBottom,
|
||||
const std::vector<std::pair<bool, T>> &inner_bc,
|
||||
int length, int rowIndex, T sx, T sy) {
|
||||
|
||||
Eigen::VectorX<T> sv(length);
|
||||
const std::size_t numRows = concentrations.rows();
|
||||
|
||||
// inner rows
|
||||
if (rowIndex > 0 && rowIndex < numRows - 1) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (inner_bc[i].first) {
|
||||
sv(i) = inner_bc[i].second;
|
||||
continue;
|
||||
}
|
||||
sv(i) =
|
||||
sy *
|
||||
calcAlphaIntercell(alphaY(rowIndex, i), alphaY(rowIndex + 1, i)) *
|
||||
concentrations(rowIndex + 1, i) +
|
||||
(1 - sy * (calcAlphaIntercell(alphaY(rowIndex, i),
|
||||
alphaY(rowIndex + 1, i)) +
|
||||
calcAlphaIntercell(alphaY(rowIndex - 1, i),
|
||||
alphaY(rowIndex, i)))) *
|
||||
concentrations(rowIndex, i) +
|
||||
sy *
|
||||
calcAlphaIntercell(alphaY(rowIndex - 1, i), alphaY(rowIndex, i)) *
|
||||
concentrations(rowIndex - 1, i);
|
||||
}
|
||||
}
|
||||
|
||||
// first row
|
||||
else if (rowIndex == 0) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (inner_bc[i].first) {
|
||||
sv(i) = inner_bc[i].second;
|
||||
continue;
|
||||
}
|
||||
switch (bcTop[i].getType()) {
|
||||
case BC_TYPE_CONSTANT: {
|
||||
sv(i) = calcExplicitConcentrationsBoundaryConstant(
|
||||
concentrations(rowIndex, i), bcTop[i].getValue(),
|
||||
alphaY(rowIndex, i), alphaY(rowIndex + 1, i), sy);
|
||||
break;
|
||||
}
|
||||
case BC_TYPE_CLOSED: {
|
||||
sv(i) = calcExplicitConcentrationsBoundaryClosed(
|
||||
concentrations(rowIndex, i), alphaY(rowIndex, i),
|
||||
alphaY(rowIndex + 1, i), sy);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw_invalid_argument(
|
||||
"Undefined Boundary Condition Type somewhere on Left or Top!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last row
|
||||
else if (rowIndex == numRows - 1) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (inner_bc[i].first) {
|
||||
sv(i) = inner_bc[i].second;
|
||||
continue;
|
||||
}
|
||||
switch (bcBottom[i].getType()) {
|
||||
case BC_TYPE_CONSTANT: {
|
||||
sv(i) = calcExplicitConcentrationsBoundaryConstant(
|
||||
concentrations(rowIndex, i), bcBottom[i].getValue(),
|
||||
alphaY(rowIndex, i), alphaY(rowIndex - 1, i), sy);
|
||||
break;
|
||||
}
|
||||
case BC_TYPE_CLOSED: {
|
||||
sv(i) = calcExplicitConcentrationsBoundaryClosed(
|
||||
concentrations(rowIndex, i), alphaY(rowIndex, i),
|
||||
alphaY(rowIndex - 1, i), sy);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw_invalid_argument(
|
||||
"Undefined Boundary Condition Type somewhere on Right or Bottom!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first column -> additional fixed concentration change from perpendicular
|
||||
// dimension in constant bc case
|
||||
if (bcLeft[rowIndex].getType() == BC_TYPE_CONSTANT && !inner_bc[0].first) {
|
||||
sv(0) += 2 * sx * alphaX(rowIndex, 0) * bcLeft[rowIndex].getValue();
|
||||
}
|
||||
|
||||
// last column -> additional fixed concentration change from perpendicular
|
||||
// dimension in constant bc case
|
||||
if (bcRight[rowIndex].getType() == BC_TYPE_CONSTANT &&
|
||||
!inner_bc[length - 1].first) {
|
||||
sv(length - 1) +=
|
||||
2 * sx * alphaX(rowIndex, length - 1) * bcRight[rowIndex].getValue();
|
||||
}
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
// solver for linear equation system; A corresponds to coefficient matrix,
|
||||
// b to the solution vector
|
||||
// use of EigenLU solver
|
||||
template <class T>
|
||||
static Eigen::VectorX<T> EigenLUAlgorithm(Diagonals<T> &A,
|
||||
Eigen::VectorX<T> &b) {
|
||||
|
||||
// convert A to Eigen sparse matrix
|
||||
size_t dim_A = A.center.size();
|
||||
Eigen::SparseMatrix<T> A_sparse(dim_A, dim_A);
|
||||
|
||||
A_sparse.insert(0, 0) = A.center[0];
|
||||
A_sparse.insert(0, 1) = A.right[0];
|
||||
|
||||
for (size_t i = 1; i < dim_A - 1; i++) {
|
||||
A_sparse.insert(i, i - 1) = A.left[i];
|
||||
A_sparse.insert(i, i) = A.center[i];
|
||||
A_sparse.insert(i, i + 1) = A.right[i];
|
||||
}
|
||||
|
||||
A_sparse.insert(dim_A - 1, dim_A - 2) = A.left[dim_A - 1];
|
||||
A_sparse.insert(dim_A - 1, dim_A - 1) = A.center[dim_A - 1];
|
||||
|
||||
Eigen::SparseLU<Eigen::SparseMatrix<T>> solver;
|
||||
solver.analyzePattern(A_sparse);
|
||||
solver.factorize(A_sparse);
|
||||
|
||||
return solver.solve(b);
|
||||
}
|
||||
|
||||
// solver for linear equation system; A corresponds to coefficient matrix,
|
||||
// b to the solution vector
|
||||
// implementation of Thomas Algorithm
|
||||
template <class T>
|
||||
static Eigen::VectorX<T> ThomasAlgorithm(Diagonals<T> &A,
|
||||
Eigen::VectorX<T> &b) {
|
||||
Eigen::Index n = b.size();
|
||||
Eigen::VectorX<T> x_vec = b;
|
||||
|
||||
// HACK: write CSV to file
|
||||
#ifdef WRITE_THOMAS_CSV
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
static std::uint32_t file_index = 0;
|
||||
std::string file_name = "Thomas_" + std::to_string(file_index++) + ".csv";
|
||||
|
||||
std::ofstream out_file;
|
||||
|
||||
out_file.open(file_name, std::ofstream::trunc | std::ofstream::out);
|
||||
|
||||
// print header
|
||||
out_file << "Aa, Ab, Ac, b\n";
|
||||
|
||||
// iterate through all elements
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
out_file << a_diag[i] << ", " << b_diag[i] << ", " << c_diag[i] << ", "
|
||||
<< b[i] << "\n";
|
||||
}
|
||||
|
||||
out_file.close();
|
||||
#endif
|
||||
|
||||
// start solving - c_diag and x_vec are overwritten
|
||||
n--;
|
||||
A.right[0] /= A.center[0];
|
||||
x_vec[0] /= A.center[0];
|
||||
|
||||
for (Eigen::Index i = 1; i < n; i++) {
|
||||
A.right[i] /= A.center[i] - A.left[i] * A.right[i - 1];
|
||||
x_vec[i] = (x_vec[i] - A.left[i] * x_vec[i - 1]) /
|
||||
(A.center[i] - A.left[i] * A.right[i - 1]);
|
||||
}
|
||||
|
||||
x_vec[n] = (x_vec[n] - A.left[n] * x_vec[n - 1]) /
|
||||
(A.center[n] - A.left[n] * A.right[n - 1]);
|
||||
|
||||
for (Eigen::Index i = n; i-- > 0;) {
|
||||
x_vec[i] -= A.right[i] * x_vec[i + 1];
|
||||
}
|
||||
|
||||
return x_vec;
|
||||
}
|
||||
|
||||
// BTCS solution for 1D grid
|
||||
template <class T>
|
||||
static void BTCS_1D(SimulationInput<T> &input,
|
||||
Eigen::VectorX<T> (*solverFunc)(Diagonals<T> &A,
|
||||
Eigen::VectorX<T> &b)) {
|
||||
const std::size_t &length = input.colMax;
|
||||
T sx = input.timestep / (input.deltaCol * input.deltaCol);
|
||||
|
||||
Eigen::VectorX<T> concentrations_t1(length);
|
||||
|
||||
Diagonals<T> A;
|
||||
Eigen::VectorX<T> b(length);
|
||||
|
||||
const auto &alpha = input.alphaX;
|
||||
|
||||
const auto &bc = input.boundaries;
|
||||
|
||||
const auto &bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
|
||||
const auto &bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
|
||||
|
||||
const auto inner_bc = bc.getInnerBoundaryRow(0);
|
||||
|
||||
RowMajMatMap<T> &concentrations = input.concentrations;
|
||||
int rowIndex = 0;
|
||||
A = createCoeffMatrix(alpha, bcLeft, bcRight, inner_bc, length, rowIndex,
|
||||
sx); // this is exactly same as in 2D
|
||||
for (int i = 0; i < length; i++) {
|
||||
b(i) = concentrations(0, i);
|
||||
}
|
||||
if (bc.getBoundaryElementType(BC_SIDE_LEFT, 0) == BC_TYPE_CONSTANT &&
|
||||
!inner_bc[0].first) {
|
||||
b(0) += 2 * sx * alpha(0, 0) * bcLeft[0].getValue();
|
||||
}
|
||||
if (bc.getBoundaryElementType(BC_SIDE_RIGHT, 0) == BC_TYPE_CONSTANT &&
|
||||
!inner_bc[length - 1].first) {
|
||||
b(length - 1) += 2 * sx * alpha(0, length - 1) * bcRight[0].getValue();
|
||||
}
|
||||
|
||||
concentrations = solverFunc(A, b);
|
||||
}
|
||||
|
||||
// BTCS solution for 2D grid
|
||||
template <class T>
|
||||
static void BTCS_2D(SimulationInput<T> &input,
|
||||
Eigen::VectorX<T> (*solverFunc)(Diagonals<T> &A,
|
||||
Eigen::VectorX<T> &b),
|
||||
int numThreads) {
|
||||
const std::size_t &rowMax = input.rowMax;
|
||||
const std::size_t &colMax = input.colMax;
|
||||
const T sx = input.timestep / (2 * input.deltaCol * input.deltaCol);
|
||||
const T sy = input.timestep / (2 * input.deltaRow * input.deltaRow);
|
||||
|
||||
RowMajMat<T> concentrations_t1(rowMax, colMax);
|
||||
|
||||
Diagonals<T> A;
|
||||
Eigen::VectorX<T> b;
|
||||
|
||||
const RowMajMat<T> &alphaX = input.alphaX;
|
||||
const RowMajMat<T> &alphaY = input.alphaY;
|
||||
|
||||
const auto &bc = input.boundaries;
|
||||
|
||||
const auto &bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
|
||||
const auto &bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
|
||||
const auto &bcTop = bc.getBoundarySide(BC_SIDE_TOP);
|
||||
const auto &bcBottom = bc.getBoundarySide(BC_SIDE_BOTTOM);
|
||||
|
||||
RowMajMatMap<T> &concentrations = input.concentrations;
|
||||
|
||||
#pragma omp parallel for num_threads(numThreads) private(A, b)
|
||||
for (int i = 0; i < rowMax; i++) {
|
||||
auto inner_bc = bc.getInnerBoundaryRow(i);
|
||||
|
||||
A = createCoeffMatrix(alphaX, bcLeft, bcRight, inner_bc, colMax, i, sx);
|
||||
b = createSolutionVector(concentrations, alphaX, alphaY, bcLeft, bcRight,
|
||||
bcTop, bcBottom, inner_bc, colMax, i, sx, sy);
|
||||
|
||||
concentrations_t1.row(i) = solverFunc(A, b);
|
||||
}
|
||||
|
||||
concentrations_t1.transposeInPlace();
|
||||
const RowMajMat<T> alphaX_t = alphaX.transpose();
|
||||
const RowMajMat<T> alphaY_t = alphaY.transpose();
|
||||
|
||||
#pragma omp parallel for num_threads(numThreads) private(A, b)
|
||||
for (int i = 0; i < colMax; i++) {
|
||||
auto inner_bc = bc.getInnerBoundaryCol(i);
|
||||
// swap alphas, boundary conditions and sx/sy for column-wise calculation
|
||||
A = createCoeffMatrix(alphaY_t, bcTop, bcBottom, inner_bc, rowMax, i, sy);
|
||||
b = createSolutionVector(concentrations_t1, alphaY_t, alphaX_t, bcTop,
|
||||
bcBottom, bcLeft, bcRight, inner_bc, rowMax, i, sy,
|
||||
sx);
|
||||
|
||||
concentrations.col(i) = solverFunc(A, b);
|
||||
}
|
||||
}
|
||||
|
||||
// entry point for EigenLU solver; differentiate between 1D and 2D grid
|
||||
template <class T> void BTCS_LU(SimulationInput<T> &input, int numThreads) {
|
||||
tug_assert(input.dim <= 2,
|
||||
"Error: Only 1- and 2-dimensional grids are defined!");
|
||||
|
||||
if (input.dim == 1) {
|
||||
BTCS_1D(input, EigenLUAlgorithm);
|
||||
} else {
|
||||
BTCS_2D(input, EigenLUAlgorithm, numThreads);
|
||||
}
|
||||
}
|
||||
|
||||
// entry point for Thomas algorithm solver; differentiate 1D and 2D grid
|
||||
template <class T> void BTCS_Thomas(SimulationInput<T> &input, int numThreads) {
|
||||
tug_assert(input.dim <= 2,
|
||||
"Error: Only 1- and 2-dimensional grids are defined!");
|
||||
|
||||
if (input.dim == 1) {
|
||||
BTCS_1D(input, ThomasAlgorithm);
|
||||
} else {
|
||||
BTCS_2D(input, ThomasAlgorithm, numThreads);
|
||||
}
|
||||
}
|
||||
} // namespace tug
|
||||
|
||||
#endif // BTCS_H_
|
||||
240
include/tug/Core/Numeric/FTCS.hpp
Normal file
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* @file FTCS.hpp
|
||||
* @brief Implementation of heterogenous FTCS (forward time-centered space)
|
||||
* solution of diffusion equation in 1D and 2D space.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FTCS_H_
|
||||
#define FTCS_H_
|
||||
|
||||
#include "tug/Core/TugUtils.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <tug/Boundary.hpp>
|
||||
#include <tug/Core/Matrix.hpp>
|
||||
#include <tug/Core/Numeric/SimulationInput.hpp>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#else
|
||||
#define omp_get_thread_num() 0
|
||||
#endif
|
||||
|
||||
namespace tug {
|
||||
|
||||
template <class T>
|
||||
constexpr T calcChangeInner(T conc_c, T conc_left, T conc_right, T alpha_c,
|
||||
T alpha_left, T alpha_right) {
|
||||
const T alpha_center_left = calcAlphaIntercell(alpha_left, alpha_c);
|
||||
const T alpha_center_right = calcAlphaIntercell(alpha_right, alpha_c);
|
||||
|
||||
return alpha_center_left * conc_left -
|
||||
(alpha_center_left + alpha_center_right) * conc_c +
|
||||
alpha_center_right * conc_right;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr T calcChangeBoundary(T conc_c, T conc_neighbor, T alpha_center,
|
||||
T alpha_neighbor, const BoundaryElement<T> &bc) {
|
||||
const T alpha_center_neighbor =
|
||||
calcAlphaIntercell(alpha_center, alpha_neighbor);
|
||||
const T &conc_boundary = bc.getValue();
|
||||
|
||||
switch (bc.getType()) {
|
||||
case BC_TYPE_CONSTANT: {
|
||||
return 2 * alpha_center * conc_boundary -
|
||||
(alpha_center_neighbor + 2 * alpha_center) * conc_c +
|
||||
alpha_center_neighbor * conc_neighbor;
|
||||
}
|
||||
case BC_TYPE_CLOSED: {
|
||||
return (alpha_center_neighbor * (conc_neighbor - conc_c));
|
||||
}
|
||||
}
|
||||
|
||||
tug_assert(false, "Undefined Boundary Condition Type!");
|
||||
}
|
||||
|
||||
// FTCS solution for 1D grid
|
||||
template <class T> static void FTCS_1D(SimulationInput<T> &input) {
|
||||
const std::size_t &colMax = input.colMax;
|
||||
const T &deltaCol = input.deltaCol;
|
||||
const T ×tep = input.timestep;
|
||||
|
||||
RowMajMatMap<T> &concentrations_grid = input.concentrations;
|
||||
// matrix for concentrations at time t+1
|
||||
RowMajMat<T> concentrations_t1 = concentrations_grid;
|
||||
|
||||
const auto &alphaX = input.alphaX;
|
||||
const auto &bc = input.boundaries;
|
||||
|
||||
// only one row in 1D case -> row constant at index 0
|
||||
int row = 0;
|
||||
|
||||
// inner cells
|
||||
// independent of boundary condition type
|
||||
for (int col = 1; col < colMax - 1; col++) {
|
||||
const T &conc_c = concentrations_grid(row, col);
|
||||
const T &conc_left = concentrations_grid(row, col - 1);
|
||||
const T &conc_right = concentrations_grid(row, col + 1);
|
||||
|
||||
const T &alpha_c = alphaX(row, col);
|
||||
const T &alpha_left = alphaX(row, col - 1);
|
||||
const T &alpha_right = alphaX(row, col + 1);
|
||||
|
||||
concentrations_t1(row, col) =
|
||||
concentrations_grid(row, col) +
|
||||
timestep / (deltaCol * deltaCol) *
|
||||
calcChangeInner(conc_c, conc_left, conc_right, alpha_c, alpha_left,
|
||||
alpha_right);
|
||||
}
|
||||
|
||||
// left boundary; hold column constant at index 0
|
||||
{
|
||||
int col = 0;
|
||||
const T &conc_c = concentrations_grid(row, col);
|
||||
const T &conc_right = concentrations_grid(row, col + 1);
|
||||
const T &alpha_c = alphaX(row, col);
|
||||
const T &alpha_right = alphaX(row, col + 1);
|
||||
const BoundaryElement<T> &bc_element =
|
||||
input.boundaries.getBoundaryElement(BC_SIDE_LEFT, row);
|
||||
|
||||
concentrations_t1(row, col) =
|
||||
concentrations_grid(row, col) +
|
||||
timestep / (deltaCol * deltaCol) *
|
||||
calcChangeBoundary(conc_c, conc_right, alpha_c, alpha_right,
|
||||
bc_element);
|
||||
}
|
||||
|
||||
// right boundary; hold column constant at max index
|
||||
{
|
||||
int col = colMax - 1;
|
||||
const T &conc_c = concentrations_grid(row, col);
|
||||
const T &conc_left = concentrations_grid(row, col - 1);
|
||||
const T &alpha_c = alphaX(row, col);
|
||||
const T &alpha_left = alphaX(row, col - 1);
|
||||
const BoundaryElement<T> &bc_element =
|
||||
bc.getBoundaryElement(BC_SIDE_RIGHT, row);
|
||||
|
||||
concentrations_t1(row, col) =
|
||||
concentrations_grid(row, col) +
|
||||
timestep / (deltaCol * deltaCol) *
|
||||
calcChangeBoundary(conc_c, conc_left, alpha_c, alpha_left,
|
||||
bc_element);
|
||||
}
|
||||
// overwrite obsolete concentrations
|
||||
concentrations_grid = concentrations_t1;
|
||||
}
|
||||
|
||||
// FTCS solution for 2D grid
|
||||
template <class T>
|
||||
static void FTCS_2D(SimulationInput<T> &input, int numThreads) {
|
||||
const std::size_t &rowMax = input.rowMax;
|
||||
const std::size_t &colMax = input.colMax;
|
||||
const T &deltaRow = input.deltaRow;
|
||||
const T &deltaCol = input.deltaCol;
|
||||
const T ×tep = input.timestep;
|
||||
|
||||
RowMajMatMap<T> &concentrations_grid = input.concentrations;
|
||||
|
||||
// matrix for concentrations at time t+1
|
||||
RowMajMat<T> concentrations_t1 = concentrations_grid;
|
||||
|
||||
const auto &alphaX = input.alphaX;
|
||||
const auto &alphaY = input.alphaY;
|
||||
const auto &bc = input.boundaries;
|
||||
|
||||
const T sx = timestep / (deltaCol * deltaCol);
|
||||
const T sy = timestep / (deltaRow * deltaRow);
|
||||
|
||||
#pragma omp parallel for num_threads(numThreads)
|
||||
for (std::size_t row_i = 0; row_i < rowMax; row_i++) {
|
||||
for (std::size_t col_i = 0; col_i < colMax; col_i++) {
|
||||
// horizontal change
|
||||
T horizontal_change;
|
||||
{
|
||||
|
||||
const T &conc_c = concentrations_grid(row_i, col_i);
|
||||
const T &alpha_c = alphaX(row_i, col_i);
|
||||
|
||||
if (col_i == 0 || col_i == colMax - 1) {
|
||||
// left or right boundary
|
||||
const T &conc_neigbor =
|
||||
concentrations_grid(row_i, col_i == 0 ? col_i + 1 : col_i - 1);
|
||||
const T &alpha_neigbor =
|
||||
alphaX(row_i, col_i == 0 ? col_i + 1 : col_i - 1);
|
||||
|
||||
const BoundaryElement<T> &bc_element = bc.getBoundaryElement(
|
||||
col_i == 0 ? BC_SIDE_LEFT : BC_SIDE_RIGHT, row_i);
|
||||
|
||||
horizontal_change = calcChangeBoundary(conc_c, conc_neigbor, alpha_c,
|
||||
alpha_neigbor, bc_element);
|
||||
} else {
|
||||
// inner cell
|
||||
const T &conc_left = concentrations_grid(row_i, col_i - 1);
|
||||
const T &conc_right = concentrations_grid(row_i, col_i + 1);
|
||||
|
||||
const T &alpha_left = alphaX(row_i, col_i - 1);
|
||||
const T &alpha_right = alphaX(row_i, col_i + 1);
|
||||
|
||||
horizontal_change = calcChangeInner(conc_c, conc_left, conc_right,
|
||||
alpha_c, alpha_left, alpha_right);
|
||||
}
|
||||
}
|
||||
|
||||
// vertical change
|
||||
T vertical_change;
|
||||
{
|
||||
const T &conc_c = concentrations_grid(row_i, col_i);
|
||||
const T &alpha_c = alphaY(row_i, col_i);
|
||||
|
||||
if (row_i == 0 || row_i == rowMax - 1) {
|
||||
// top or bottom boundary
|
||||
const T &conc_neigbor =
|
||||
concentrations_grid(row_i == 0 ? row_i + 1 : row_i - 1, col_i);
|
||||
|
||||
const T &alpha_neigbor =
|
||||
alphaY(row_i == 0 ? row_i + 1 : row_i - 1, col_i);
|
||||
|
||||
const BoundaryElement<T> &bc_element = bc.getBoundaryElement(
|
||||
row_i == 0 ? BC_SIDE_TOP : BC_SIDE_BOTTOM, col_i);
|
||||
|
||||
vertical_change = calcChangeBoundary(conc_c, conc_neigbor, alpha_c,
|
||||
alpha_neigbor, bc_element);
|
||||
} else {
|
||||
// inner cell
|
||||
const T &conc_bottom = concentrations_grid(row_i - 1, col_i);
|
||||
const T &conc_top = concentrations_grid(row_i + 1, col_i);
|
||||
|
||||
const T &alpha_bottom = alphaY(row_i - 1, col_i);
|
||||
const T &alpha_top = alphaY(row_i + 1, col_i);
|
||||
|
||||
vertical_change = calcChangeInner(conc_c, conc_bottom, conc_top,
|
||||
alpha_c, alpha_bottom, alpha_top);
|
||||
}
|
||||
}
|
||||
|
||||
concentrations_t1(row_i, col_i) = concentrations_grid(row_i, col_i) +
|
||||
sx * horizontal_change +
|
||||
sy * vertical_change;
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite obsolete concentrations
|
||||
concentrations_grid = concentrations_t1;
|
||||
}
|
||||
|
||||
// entry point; differentiate between 1D and 2D grid
|
||||
template <class T> void FTCS(SimulationInput<T> &input, int &numThreads) {
|
||||
tug_assert(input.dim <= 2,
|
||||
"Error: Only 1- and 2-dimensional grids are defined!");
|
||||
|
||||
if (input.dim == 1) {
|
||||
FTCS_1D(input);
|
||||
} else {
|
||||
FTCS_2D(input, numThreads);
|
||||
}
|
||||
}
|
||||
} // namespace tug
|
||||
|
||||
#endif // FTCS_H_
|
||||
21
include/tug/Core/Numeric/SimulationInput.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <tug/Boundary.hpp>
|
||||
#include <tug/Core/Matrix.hpp>
|
||||
|
||||
namespace tug {
|
||||
|
||||
template <typename T> struct SimulationInput {
|
||||
RowMajMatMap<T> &concentrations;
|
||||
const RowMajMat<T> &alphaX;
|
||||
const RowMajMat<T> &alphaY;
|
||||
const Boundary<T> boundaries;
|
||||
|
||||
const std::uint8_t dim;
|
||||
const T timestep;
|
||||
const std::size_t rowMax;
|
||||
const std::size_t colMax;
|
||||
const T deltaRow;
|
||||
const T deltaCol;
|
||||
};
|
||||
} // namespace tug
|
||||
@ -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
@ -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
|
||||
@ -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
|
||||
|
||||
};
|
||||
@ -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
@ -0,0 +1,167 @@
|
||||
#include <Eigen/Eigen>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tug/Diffusion.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "files.hpp"
|
||||
|
||||
using namespace tug;
|
||||
|
||||
/**
|
||||
* Try to parse an input string into a given template type.
|
||||
*/
|
||||
template <typename T> inline T parseString(const std::string &str) {
|
||||
T result;
|
||||
std::istringstream iss(str);
|
||||
|
||||
if (!(iss >> result)) {
|
||||
throw std::invalid_argument("Invalid input for parsing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a given string into a vector by using a delimiter character.
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<T> tokenize(const std::string &input, char delimiter) {
|
||||
std::vector<T> tokens;
|
||||
std::istringstream tokenStream(input);
|
||||
std::string token;
|
||||
|
||||
while (std::getline(tokenStream, token, delimiter)) {
|
||||
tokens.push_back(parseString<T>(token));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file containing CSV and transform it into row-major 2D STL vector.
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> CSVToVector(const char *filename) {
|
||||
std::ifstream in_file(filename);
|
||||
if (!in_file.is_open()) {
|
||||
throw std::runtime_error("Error opening file \'" + std::string(filename) +
|
||||
"\'.");
|
||||
}
|
||||
|
||||
std::vector<std::vector<T>> csv_data;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in_file, line)) {
|
||||
csv_data.push_back(tokenize<T>(line, ','));
|
||||
}
|
||||
|
||||
in_file.close();
|
||||
|
||||
return csv_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D STL vector, where values are stored row-major into a
|
||||
* column-major Eigen::Matrix.
|
||||
*/
|
||||
template <typename T>
|
||||
Eigen::MatrixXd rmVecTocmMatrix(const std::vector<std::vector<T>> &vec,
|
||||
std::uint32_t exp_rows,
|
||||
std::uint32_t exp_cols) {
|
||||
if (exp_rows != vec.size()) {
|
||||
throw std::runtime_error(
|
||||
"Mismatch in y dimension while converting to Eigen::Matrix.");
|
||||
}
|
||||
|
||||
Eigen::MatrixXd out_mat(exp_rows, exp_cols);
|
||||
|
||||
for (std::uint32_t ri = 0; ri < exp_rows; ri++) {
|
||||
const auto &vec_row = vec[ri];
|
||||
if (vec[ri].size() != exp_cols) {
|
||||
throw std::runtime_error(
|
||||
"Mismatch in x dimension while converting to Eigen::Matrix.");
|
||||
}
|
||||
for (std::uint32_t cj = 0; cj < exp_cols; cj++) {
|
||||
out_mat(ri, cj) = vec_row[cj];
|
||||
}
|
||||
}
|
||||
|
||||
return out_mat;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// EASY_PROFILER_ENABLE;
|
||||
// profiler::startListen();
|
||||
// **************
|
||||
// **** GRID ****
|
||||
// **************
|
||||
// profiler::startListen();
|
||||
// create a grid with a 5 x 10 field
|
||||
constexpr int row = 5;
|
||||
constexpr int col = 10;
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
|
||||
const auto init_values_vec = CSVToVector<double>(INPUT_CONC_FILE);
|
||||
Eigen::MatrixXd concentrations = rmVecTocmMatrix(init_values_vec, row, col);
|
||||
Grid64 grid(concentrations);
|
||||
|
||||
grid.setDomain(0.005, 0.01);
|
||||
const double sum_init = concentrations.sum();
|
||||
|
||||
// // (optional) set alphas of the grid, e.g.:
|
||||
const auto alphax_vec = CSVToVector<double>(INPUT_ALPHAX_FILE);
|
||||
Eigen::MatrixXd alphax = rmVecTocmMatrix(alphax_vec, row, col);
|
||||
|
||||
constexpr double alphay_val = 5e-10;
|
||||
Eigen::MatrixXd alphay =
|
||||
Eigen::MatrixXd::Constant(row, col, alphay_val); // row,col,value
|
||||
grid.setAlpha(alphax, alphay);
|
||||
|
||||
// // ******************
|
||||
// // **** BOUNDARY ****
|
||||
// // ******************
|
||||
|
||||
// create a boundary with constant values
|
||||
Boundary bc = Boundary(grid);
|
||||
bc.setBoundarySideClosed(BC_SIDE_LEFT);
|
||||
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
|
||||
bc.setBoundarySideClosed(BC_SIDE_TOP);
|
||||
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
|
||||
|
||||
// // ************************
|
||||
// // **** SIMULATION ENV ****
|
||||
// // ************************
|
||||
|
||||
// set up a simulation environment
|
||||
Diffusion simulation(grid, bc); // grid,boundary
|
||||
|
||||
// set the timestep of the simulation
|
||||
simulation.setTimestep(360); // timestep
|
||||
|
||||
// set the number of iterations
|
||||
simulation.setIterations(1);
|
||||
|
||||
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
|
||||
// CSV_OUTPUT_VERBOSE]
|
||||
simulation.setOutputCSV(CSV_OUTPUT_ON);
|
||||
|
||||
// set output to the console to 'ON'
|
||||
simulation.setOutputConsole(CONSOLE_OUTPUT_ON);
|
||||
|
||||
// // **** RUN SIMULATION ****
|
||||
simulation.run();
|
||||
|
||||
const double sum_after = grid.getConcentrations().sum();
|
||||
|
||||
std::cout << "Sum of init field: " << std::to_string(sum_init)
|
||||
<< "\nSum after iteration: " << std::to_string(sum_after)
|
||||
<< std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
naaice/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
add_executable(naaice BTCS_2D_NAAICE.cpp)
|
||||
add_executable(NAAICE_dble_vs_float NAAICE_dble_vs_float.cpp)
|
||||
|
||||
get_filename_component(IN_CONC_FILE "init_conc.csv" REALPATH)
|
||||
get_filename_component(IN_ALPHAX_FILE "alphax.csv" REALPATH)
|
||||
|
||||
configure_file(files.hpp.in files.hpp)
|
||||
target_include_directories(naaice PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(NAAICE_dble_vs_float PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_compile_definitions(naaice PRIVATE WRITE_THOMAS_CSV)
|
||||
|
||||
target_link_libraries(naaice PUBLIC tug)
|
||||
target_link_libraries(NAAICE_dble_vs_float PUBLIC tug)
|
||||
191
naaice/NAAICE_dble_vs_float.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
#include <Eigen/Eigen>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <files.hpp>
|
||||
#include <tug/Diffusion.hpp>
|
||||
|
||||
using namespace tug;
|
||||
|
||||
/**
|
||||
* Try to parse an input string into a given template type.
|
||||
*/
|
||||
template <typename T> inline T parseString(const std::string &str) {
|
||||
T result;
|
||||
std::istringstream iss(str);
|
||||
|
||||
if (!(iss >> result)) {
|
||||
throw std::invalid_argument("Invalid input for parsing.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a given string into a vector by using a delimiter character.
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<T> tokenize(const std::string &input, char delimiter) {
|
||||
std::vector<T> tokens;
|
||||
std::istringstream tokenStream(input);
|
||||
std::string token;
|
||||
|
||||
while (std::getline(tokenStream, token, delimiter)) {
|
||||
tokens.push_back(parseString<T>(token));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file containing CSV and transform it into row-major 2D STL vector.
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> CSVToVector(const char *filename) {
|
||||
std::ifstream in_file(filename);
|
||||
if (!in_file.is_open()) {
|
||||
throw std::runtime_error("Error opening file \'" + std::string(filename) +
|
||||
"\'.");
|
||||
}
|
||||
|
||||
std::vector<std::vector<T>> csv_data;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in_file, line)) {
|
||||
csv_data.push_back(tokenize<T>(line, ','));
|
||||
}
|
||||
|
||||
in_file.close();
|
||||
|
||||
return csv_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D STL vector, where values are stored row-major into a
|
||||
* column-major Eigen::Matrix.
|
||||
*/
|
||||
template <typename T>
|
||||
Eigen::MatrixXd rmVecTocmMatrix(const std::vector<std::vector<T>> &vec,
|
||||
std::uint32_t exp_rows,
|
||||
std::uint32_t exp_cols) {
|
||||
if (exp_rows != vec.size()) {
|
||||
throw std::runtime_error(
|
||||
"Mismatch in y dimension while converting to Eigen::Matrix.");
|
||||
}
|
||||
|
||||
Eigen::MatrixXd out_mat(exp_rows, exp_cols);
|
||||
|
||||
for (std::uint32_t ri = 0; ri < exp_rows; ri++) {
|
||||
const auto &vec_row = vec[ri];
|
||||
if (vec[ri].size() != exp_cols) {
|
||||
throw std::runtime_error(
|
||||
"Mismatch in x dimension while converting to Eigen::Matrix.");
|
||||
}
|
||||
for (std::uint32_t cj = 0; cj < exp_cols; cj++) {
|
||||
out_mat(ri, cj) = vec_row[cj];
|
||||
}
|
||||
}
|
||||
|
||||
return out_mat;
|
||||
}
|
||||
|
||||
template <class T, tug::APPROACH app> int doWork(int ngrid) {
|
||||
|
||||
constexpr T dt = 10;
|
||||
|
||||
// create a grid
|
||||
std::string name;
|
||||
|
||||
if constexpr (std::is_same_v<T, double>) {
|
||||
name = "DOUBLE";
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
name = "FLOAT";
|
||||
} else {
|
||||
name = "unknown";
|
||||
}
|
||||
|
||||
std::cout << name << " grid: " << ngrid << std::endl;
|
||||
|
||||
// Grid64 grid(ngrid, ngrid);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
|
||||
Eigen::MatrixX<T> initConc64 = Eigen::MatrixX<T>::Constant(ngrid, ngrid, 0);
|
||||
initConc64(50, 50) = 1E-6;
|
||||
Grid<T> grid(initConc64);
|
||||
grid.setDomain(0.1, 0.1);
|
||||
|
||||
const T sum_init64 = initConc64.sum();
|
||||
|
||||
constexpr T alphax_val = 5e-10;
|
||||
Eigen::MatrixX<T> alphax =
|
||||
Eigen::MatrixX<T>::Constant(ngrid, ngrid, alphax_val); // row,col,value
|
||||
constexpr T alphay_val = 1e-10;
|
||||
Eigen::MatrixX<T> alphay =
|
||||
Eigen::MatrixX<T>::Constant(ngrid, ngrid, alphay_val); // row,col,value
|
||||
grid.setAlpha(alphax, alphay);
|
||||
|
||||
// create a boundary with constant values
|
||||
Boundary bc = Boundary(grid);
|
||||
bc.setBoundarySideClosed(BC_SIDE_LEFT);
|
||||
bc.setBoundarySideClosed(BC_SIDE_RIGHT);
|
||||
bc.setBoundarySideClosed(BC_SIDE_TOP);
|
||||
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
|
||||
|
||||
// set up a simulation environment
|
||||
Diffusion Sim(grid, bc); // grid_64,boundary,simulation-approach
|
||||
|
||||
// Sim64.setSolver(THOMAS_ALGORITHM_SOLVER);
|
||||
|
||||
// set the timestep of the simulation
|
||||
Sim.setTimestep(dt); // timestep
|
||||
|
||||
// set the number of iterations
|
||||
Sim.setIterations(2);
|
||||
|
||||
// set kind of output [CSV_OUTPUT_OFF (default), CSV_OUTPUT_ON,
|
||||
// CSV_OUTPUT_VERBOSE]
|
||||
Sim.setOutputCSV(CSV_OUTPUT_ON);
|
||||
|
||||
// set output to the console to 'ON'
|
||||
Sim.setOutputConsole(CONSOLE_OUTPUT_OFF);
|
||||
|
||||
// // **** RUN SIM64 ****
|
||||
auto begin_t = std::chrono::high_resolution_clock::now();
|
||||
|
||||
Sim.run();
|
||||
|
||||
auto end_t = std::chrono::high_resolution_clock::now();
|
||||
auto ms_t =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end_t - begin_t);
|
||||
|
||||
const double sum_after64 = grid.getConcentrations().sum();
|
||||
|
||||
std::cout << "Sum of init field: " << std::setprecision(15) << sum_init64
|
||||
<< "\nSum after 2 iterations: " << sum_after64
|
||||
<< "\nMilliseconds: " << ms_t.count() << std::endl
|
||||
<< std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
int n[] = {101, 201, 501, 1001, 2001};
|
||||
|
||||
for (int i = 0; i < std::size(n); i++) {
|
||||
doWork<float, tug::BTCS_APPROACH>(n[i]);
|
||||
doWork<double, tug::BTCS_APPROACH>(n[i]);
|
||||
doWork<float, tug::FTCS_APPROACH>(n[i]);
|
||||
doWork<double, tug::FTCS_APPROACH>(n[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
70
naaice/README.md
Normal file
@ -0,0 +1,70 @@
|
||||
This directory contains a concise benchmark designed for validating FPGA
|
||||
offloading of the Thomas algorithm, primarily employed for solving linear
|
||||
equation systems structured within a tridiagonal matrix.
|
||||
|
||||
|
||||
# Benchmark Setup
|
||||
|
||||
The benchmark defines a domain measuring $1 \text{cm} \times 0.5 \text{cm}$ (easting $\times$ northing),
|
||||
discretized in a $10 \times 5$ grid. Each grid cell initially
|
||||
contains a specific concentration. The concentration in the left domain half is set to $6.92023 \times 10^{-7}$, while in the right half to
|
||||
$2.02396 \times 10^{-8}$, creating an horizontal concentration discontinuity at
|
||||
the center of the grid. These initial concentrations are read from headerless csv file [init_conc.csv](./init_conc.csv).
|
||||
|
||||
A diffusion time step is simulated with the
|
||||
heterogeneous 2D-ADI approach detailed in the
|
||||
[ADI_scheme.pdf](../doc/ADI_scheme.pdf) file. The x component of the
|
||||
diffusion coefficients, read from headerless csv file [alphax.csv](./alphax.csv) ranges from $\alpha = 10^{-9}$ to $10^{-10}$ (distributed randomly), while the
|
||||
y-component is held constant at $5 \times 10^{-10}$. Closed
|
||||
boundary conditions are enforced at all domain boundaries, meaning that concentration cannot enter or exit
|
||||
the system, or in other terms, that the sum of concentrations over the domain must stay constant. The benchmark simulates a single iteration with a
|
||||
time step ($\Delta t$) of 360 seconds.
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
To generate new makefiles using the `-DTUG_NAAICE_EXAMPLE=ON` option in CMake,
|
||||
compile the executable, and run it to generate the benchmark output, follow
|
||||
these steps:
|
||||
|
||||
1. Navigate to your project's build directory.
|
||||
2. Run the following CMake command with the `-DTUG_NAAICE_EXAMPLE=ON` option to
|
||||
generate the makefiles:
|
||||
|
||||
cmake -DTUG_NAAICE_EXAMPLE=ON ..
|
||||
|
||||
3. After CMake configuration is complete, build the `naaice` executable by running `make`:
|
||||
|
||||
make naaice
|
||||
|
||||
4. Once the compilation is successful, navigate to the build directory by `cd
|
||||
<build_dir>/naaice`
|
||||
|
||||
5. Finally, run the `naaice` executable to generate the benchmark output:
|
||||
|
||||
./naaice
|
||||
|
||||
|
||||
## Output Files
|
||||
|
||||
|
||||
### `Thomas_<n>.csv`
|
||||
|
||||
These files contain the values of the tridiagonal coefficient matrix $A$, where:
|
||||
|
||||
- $Aa$ represents the leftmost value,
|
||||
- $Ab$ represents the middle value, and
|
||||
- $Ac$ represents the rightmost value of one row of the matrix.
|
||||
|
||||
Additionally, the corresponding values of the right-hand-side vector $b$ are
|
||||
provided.
|
||||
|
||||
Since the 2D-ADI BTCS scheme processes each row first and then proceeds
|
||||
column-wise through the grid, each iteration is saved separately in
|
||||
consecutively numbered files.
|
||||
|
||||
|
||||
### `BTCS_5_10_1.csv`
|
||||
|
||||
The result of the simulation, **separated by whitespaces**!
|
||||
|
||||
5
naaice/alphax.csv
Normal file
@ -0,0 +1,5 @@
|
||||
9.35863547772169e-10,4.1475358910393e-10,7.40997499064542e-10,4.63898729439825e-10,5.50232032872736e-10,1.83670640387572e-10,5.84096802584827e-10,4.72976535512134e-10,2.92526249657385e-10,5.8247926668264e-10
|
||||
8.08492869278416e-10,3.5943602763582e-10,6.87254608655348e-10,1.52116895909421e-10,5.95404988038354e-10,8.90064929216169e-10,6.13143724552356e-10,1.23507722397335e-10,2.898759260308e-10,9.90528965741396e-10
|
||||
3.12359050870873e-10,6.34084914484993e-10,8.28234328492545e-10,1.60584925650619e-10,1.31777092232369e-10,7.34155903453939e-10,9.86383097898215e-10,5.42474469379522e-10,1.23030534153804e-10,2.33146838657558e-10
|
||||
7.76231796317734e-10,1.69479642040096e-10,6.74477553134784e-10,2.4903867142275e-10,2.70820381003432e-10,1.67315319390036e-10,7.1631961625535e-10,4.51548034301959e-10,2.41610987577587e-10,4.98075650655665e-10
|
||||
7.33895266707987e-10,5.82150935800746e-10,1.49088777275756e-10,5.00345975253731e-10,8.26257051993161e-10,5.28838745504618e-10,9.94136832957156e-10,7.44971914449707e-10,7.53557282453403e-10,7.54089470859617e-10
|
||||
|
7
naaice/files.hpp.in
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef FILES_H_
|
||||
#define FILES_H_
|
||||
|
||||
const char *INPUT_CONC_FILE = "@IN_CONC_FILE@";
|
||||
const char *INPUT_ALPHAX_FILE = "@IN_ALPHAX_FILE@";
|
||||
|
||||
#endif // FILES_H_
|
||||
5
naaice/init_conc.csv
Normal file
@ -0,0 +1,5 @@
|
||||
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
|
||||
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
|
||||
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
|
||||
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
|
||||
6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,6.92023e-07,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08,2.02396e-08
|
||||
|
@ -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
|
||||
|
||||
437
src/BTCSv2.cpp
@ -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!");
|
||||
}
|
||||
}
|
||||
162
src/Boundary.cpp
@ -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();
|
||||
}
|
||||
|
||||
@ -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)
|
||||
433
src/FTCS.cpp
@ -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 ×tep) {
|
||||
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 ×tep, 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 ×tep, 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!");
|
||||
}
|
||||
}
|
||||
164
src/Grid.cpp
@ -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;
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
@ -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")
|
||||
|
||||
@ -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
@ -0,0 +1,51 @@
|
||||
#include "tug/Core/Matrix.hpp"
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Dense>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#define TUG_TEST(x) TEST(Tug, x)
|
||||
|
||||
inline tug::RowMajMat<double> CSV2Eigen(std::string file2Convert) {
|
||||
|
||||
std::vector<double> matrixEntries;
|
||||
|
||||
std::ifstream matrixDataFile(file2Convert);
|
||||
if (matrixDataFile.fail()) {
|
||||
throw std::invalid_argument("File probably non-existent!");
|
||||
}
|
||||
|
||||
std::string matrixRowString;
|
||||
std::string matrixEntry;
|
||||
int matrixRowNumber = 0;
|
||||
|
||||
while (getline(matrixDataFile, matrixRowString)) {
|
||||
std::stringstream matrixRowStringStream(matrixRowString);
|
||||
while (getline(matrixRowStringStream, matrixEntry, ' ')) {
|
||||
matrixEntries.push_back(stod(matrixEntry));
|
||||
}
|
||||
if (matrixRowString.length() > 1) {
|
||||
matrixRowNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
return tug::RowMajMatMap<double>(matrixEntries.data(), matrixRowNumber,
|
||||
matrixEntries.size() / matrixRowNumber);
|
||||
}
|
||||
|
||||
inline bool checkSimilarity(tug::RowMajMat<double> &a,
|
||||
tug::RowMajMatMap<double> &b,
|
||||
double precision = 1e-5) {
|
||||
return a.isApprox(b, precision);
|
||||
}
|
||||
|
||||
inline bool checkSimilarityV2(tug::RowMajMat<double> &a,
|
||||
tug::RowMajMatMap<double> &b, double maxDiff) {
|
||||
|
||||
tug::RowMajMat<double> diff = a - b;
|
||||
double maxCoeff = diff.maxCoeff();
|
||||
return abs(maxCoeff) < maxDiff;
|
||||
}
|
||||
@ -1,2 +1,7 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/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();
|
||||
}
|
||||
@ -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
@ -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());
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||