Merge branch 'naaice' into 'main'
BREAKING CHANGE: tug as header-only library See merge request naaice/tug!18
This commit is contained in:
commit
39541a2054
104
CMakeLists.txt
104
CMakeLists.txt
@ -1,50 +1,102 @@
|
||||
#debian stable (currently bullseye)
|
||||
# debian stable (currently bullseye)
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
project(tug CXX)
|
||||
project(
|
||||
tug
|
||||
VERSION 0.4
|
||||
LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
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)
|
||||
# find_package(easy_profiler) option(EASY_OPTION_LOG "Verbose easy_profiler" 1)
|
||||
|
||||
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
|
||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel GenericOpt."
|
||||
FORCE)
|
||||
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -mfma")
|
||||
option(TUG_USE_OPENMP "Compile tug with OpenMP support" ON)
|
||||
|
||||
option(TUG_USE_UNSAFE_MATH_OPT
|
||||
"Use compiler options to break IEEE compliances by
|
||||
set(CMAKE_CXX_FLAGS_GENERICOPT
|
||||
"-O3 -march=native"
|
||||
CACHE STRING "Flags used by the C++ compiler during opt builds." FORCE)
|
||||
|
||||
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
|
||||
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"
|
||||
OFF)
|
||||
if(TUG_USE_UNSAFE_MATH_OPT)
|
||||
target_compile_options(tug INTERFACE -ffast-math)
|
||||
endif()
|
||||
|
||||
option(TUG_ENABLE_EXAMPLES
|
||||
"Compile example applications"
|
||||
OFF)
|
||||
install(
|
||||
TARGETS tug
|
||||
EXPORT ${PROJECT_NAME}_Targets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
add_subdirectory(src)
|
||||
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)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if(TUG_ENABLE_EXAMPLES)
|
||||
if(TUG_HANNESPHILIPP_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
if(TUG_NAAICE_EXAMPLE)
|
||||
add_subdirectory(naaice)
|
||||
endif()
|
||||
|
||||
4
cmake/tugConfig.cmake.in
Normal file
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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Grid
|
||||
====
|
||||
|
||||
.. doxygenclass:: Grid
|
||||
.. doxygenclass:: tug::Grid
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
Simulation
|
||||
==========
|
||||
|
||||
.. doxygenclass:: Simulation
|
||||
.. doxygenenum:: tug::APPROACH
|
||||
.. doxygenenum:: tug::SOLVER
|
||||
.. doxygenenum:: tug::CSV_OUTPUT
|
||||
.. doxygenenum:: tug::CONSOLE_OUTPUT
|
||||
.. doxygenenum:: tug::TIME_MEASURE
|
||||
|
||||
.. doxygenclass:: tug::Simulation
|
||||
|
||||
@ -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
|
||||
-------------------------------------------------------
|
||||
-------------------------------------------------------
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 41 KiB |
@ -12,4 +12,4 @@ Numerical Solver
|
||||
**Backward Time-Centered Space (BTCS) Method**
|
||||
|
||||
|
||||
**Forward Time-Centered Space (BTCS) Method**
|
||||
**Forward Time-Centered Space (FTCS) Method**
|
||||
|
||||
@ -3,7 +3,6 @@ User API
|
||||
|
||||
.. toctree::
|
||||
|
||||
:maxdepth: 2
|
||||
Grid
|
||||
Boundary
|
||||
Simulation
|
||||
Simulation
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// **************
|
||||
@ -10,7 +11,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// create a linear grid with 20 cells
|
||||
int cells = 20;
|
||||
Grid grid = Grid(cells);
|
||||
Grid64 grid(cells);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(1, 20, 0);
|
||||
concentrations(0, 0) = 2000;
|
||||
@ -31,8 +32,7 @@ int main(int argc, char *argv[]) {
|
||||
// ************************
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, BTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation simulation = Simulation(grid, bc); // grid,boundary
|
||||
|
||||
// set the timestep of the simulation
|
||||
simulation.setTimestep(0.1); // timestep
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// EASY_PROFILER_ENABLE;
|
||||
@ -13,7 +14,7 @@ int main(int argc, char *argv[]) {
|
||||
// create a grid with a 20 x 20 field
|
||||
int row = 40;
|
||||
int col = 50;
|
||||
Grid grid = Grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
// grid.setDomain(20, 20);
|
||||
@ -61,7 +62,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, BTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation(grid, bc); // grid,boundary
|
||||
|
||||
// set the timestep of the simulation
|
||||
simulation.setTimestep(0.1); // timestep
|
||||
|
||||
@ -2,11 +2,12 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int row = 20;
|
||||
int col = 20;
|
||||
Grid grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
|
||||
concentrations(10, 10) = 2000;
|
||||
@ -18,7 +19,8 @@ int main(int argc, char *argv[]) {
|
||||
bc.setBoundarySideClosed(BC_SIDE_TOP);
|
||||
bc.setBoundarySideClosed(BC_SIDE_BOTTOM);
|
||||
|
||||
Simulation simulation = Simulation(grid, bc, CRANK_NICOLSON_APPROACH);
|
||||
Simulation simulation =
|
||||
Simulation<double, tug::CRANK_NICOLSON_APPROACH>(grid, bc);
|
||||
simulation.setTimestep(0.1);
|
||||
simulation.setIterations(50);
|
||||
simulation.setOutputCSV(CSV_OUTPUT_XTREME);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// **************
|
||||
@ -11,7 +12,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// create a linear grid with 20 cells
|
||||
int cells = 20;
|
||||
Grid grid = Grid(cells);
|
||||
Grid64 grid(cells);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(1, 20, 20);
|
||||
grid.setConcentrations(concentrations);
|
||||
@ -31,7 +32,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation<double, tug::FTCS_APPROACH>(grid, bc); // grid,boundary,simulation-approach
|
||||
|
||||
// (optional) set the timestep of the simulation
|
||||
simulation.setTimestep(0.1); // timestep
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@ -30,7 +31,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// create a grid with a 20 x 20 field
|
||||
int n2 = row / 2 - 1;
|
||||
Grid grid = Grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
// grid.setDomain(20, 20);
|
||||
@ -69,7 +70,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation<double, FTCS_APPROACH>(grid, bc); // grid,boundary,simulation-approach
|
||||
|
||||
// set the timestep of the simulation
|
||||
simulation.setTimestep(10000); // timestep
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
// #include <easy/profiler.h>
|
||||
// #define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
|
||||
|
||||
@ -23,7 +24,7 @@ int main(int argc, char *argv[]) {
|
||||
// create a grid with a 20 x 20 field
|
||||
int row = 20;
|
||||
int col = 20;
|
||||
Grid grid = Grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
// grid.setDomain(20, 20);
|
||||
@ -67,7 +68,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation<double, tug::FTCS_APPROACH>(grid, bc); // grid,boundary,simulation-approach
|
||||
|
||||
// set the timestep of the simulation
|
||||
simulation.setTimestep(0.1); // timestep
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@ -21,7 +22,7 @@ int main(int argc, char *argv[]) {
|
||||
int row = 64;
|
||||
int col = 64;
|
||||
int n2 = row / 2 - 1;
|
||||
Grid grid = Grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
// grid.setDomain(20, 20);
|
||||
@ -60,7 +61,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// set up a simulation environment
|
||||
Simulation simulation =
|
||||
Simulation(grid, bc, FTCS_APPROACH); // grid,boundary,simulation-approach
|
||||
Simulation<double, tug::FTCS_APPROACH>(grid, bc); // grid,boundary,simulation-approach
|
||||
|
||||
// (optional) set the timestep of the simulation
|
||||
simulation.setTimestep(1000); // timestep
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@ -29,7 +30,7 @@ int main(int argc, char *argv[]) {
|
||||
// myfile << "Iterations: " << iterations[j] << endl;
|
||||
for (int k = 0; k < repetition; k++) {
|
||||
cout << "Wiederholung: " << k << endl;
|
||||
Grid grid = Grid(n[i], n[i]);
|
||||
Grid64 grid(n[i], n[i]);
|
||||
grid.setDomain(1, 1);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(n[i], n[i], 0);
|
||||
@ -39,8 +40,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
Boundary bc = Boundary(grid);
|
||||
|
||||
Simulation sim = Simulation(grid, bc, BTCS_APPROACH);
|
||||
sim.setSolver(THOMAS_ALGORITHM_SOLVER);
|
||||
Simulation sim = Simulation(grid, bc);
|
||||
|
||||
if (argc == 2) {
|
||||
int numThreads = atoi(argv[1]);
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@ -29,7 +30,7 @@ int main(int argc, char *argv[]) {
|
||||
// myfile << "Iterations: " << iterations[j] << endl;
|
||||
for (int k = 0; k < repetition; k++) {
|
||||
cout << "Wiederholung: " << k << endl;
|
||||
Grid grid = Grid(n[i], n[i]);
|
||||
Grid64 grid(n[i], n[i]);
|
||||
grid.setDomain(1, 1);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(n[i], n[i], 0);
|
||||
@ -39,8 +40,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
Boundary bc = Boundary(grid);
|
||||
|
||||
Simulation sim = Simulation(grid, bc, BTCS_APPROACH);
|
||||
sim.setSolver(THOMAS_ALGORITHM_SOLVER);
|
||||
Simulation sim = Simulation(grid, bc);
|
||||
|
||||
if (argc == 2) {
|
||||
int numThreads = atoi(argv[1]);
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
using namespace tug;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int row = 50;
|
||||
@ -12,7 +13,7 @@ int main(int argc, char *argv[]) {
|
||||
int domain_col = 10;
|
||||
|
||||
// Grid
|
||||
Grid grid = Grid(row, col);
|
||||
Grid64 grid(row, col);
|
||||
grid.setDomain(domain_row, domain_col);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
|
||||
@ -41,7 +42,7 @@ int main(int argc, char *argv[]) {
|
||||
Boundary bc = Boundary(grid);
|
||||
|
||||
// Simulation
|
||||
Simulation sim = Simulation(grid, bc, FTCS_APPROACH);
|
||||
Simulation sim = Simulation<double, tug::FTCS_APPROACH>(grid, bc);
|
||||
sim.setTimestep(0.001);
|
||||
sim.setIterations(10000);
|
||||
sim.setOutputCSV(CSV_OUTPUT_OFF);
|
||||
|
||||
@ -9,6 +9,10 @@
|
||||
|
||||
#include "Grid.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
|
||||
namespace tug {
|
||||
|
||||
/**
|
||||
* @brief Enum defining the two implemented boundary conditions.
|
||||
@ -26,8 +30,10 @@ 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 {
|
||||
template <class T> class BoundaryElement {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Boundary Element object for the closed case.
|
||||
@ -35,7 +41,7 @@ public:
|
||||
* BC_TYPE_CLOSED, where the value takes -1 and does not hold any
|
||||
* physical meaning.
|
||||
*/
|
||||
BoundaryElement();
|
||||
BoundaryElement(){};
|
||||
|
||||
/**
|
||||
* @brief Construct a new Boundary Element object for the constant case.
|
||||
@ -45,7 +51,7 @@ public:
|
||||
* @param value Value of the constant concentration to be assumed at the
|
||||
* corresponding boundary element.
|
||||
*/
|
||||
BoundaryElement(double value);
|
||||
BoundaryElement(T _value) : value(_value), type(BC_TYPE_CONSTANT) {}
|
||||
|
||||
/**
|
||||
* @brief Allows changing the boundary type of a corresponding
|
||||
@ -54,7 +60,7 @@ public:
|
||||
* @param type Type of boundary condition. Either BC_TYPE_CONSTANT or
|
||||
BC_TYPE_CLOSED.
|
||||
*/
|
||||
void setType(BC_TYPE type);
|
||||
void setType(BC_TYPE type) { this->type = type; };
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a boundary condition for the constant case.
|
||||
@ -62,34 +68,46 @@ public:
|
||||
* @param value Concentration to be considered constant for the
|
||||
* corresponding boundary element.
|
||||
*/
|
||||
void setValue(double value);
|
||||
void setValue(double value) {
|
||||
if (value < 0) {
|
||||
throw std::invalid_argument("No negative concentration allowed.");
|
||||
}
|
||||
if (type == BC_TYPE_CLOSED) {
|
||||
throw std::invalid_argument(
|
||||
"No constant boundary concentrations can be set for closed "
|
||||
"boundaries. Please change type first.");
|
||||
}
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the type of the boundary condition, i.e. whether the
|
||||
* boundary is considered closed or constant.
|
||||
*
|
||||
* @return BC_TYPE Type of boundary condition, either BC_TYPE_CLOSED or
|
||||
* @return Type of boundary condition, either BC_TYPE_CLOSED or
|
||||
BC_TYPE_CONSTANT.
|
||||
*/
|
||||
BC_TYPE getType();
|
||||
BC_TYPE getType() const { return this->type; }
|
||||
|
||||
/**
|
||||
* @brief Return the concentration value for the constant boundary condition.
|
||||
*
|
||||
* @return double Value of the concentration.
|
||||
* @return Value of the concentration.
|
||||
*/
|
||||
double getValue();
|
||||
T getValue() const { return this->value; }
|
||||
|
||||
private:
|
||||
BC_TYPE type{BC_TYPE_CLOSED};
|
||||
double value{-1};
|
||||
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 {
|
||||
template <class T> class Boundary {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a boundary object based on the passed grid object and
|
||||
@ -98,7 +116,27 @@ public:
|
||||
* @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);
|
||||
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
|
||||
@ -106,7 +144,21 @@ public:
|
||||
*
|
||||
* @param side Side to be set to closed, e.g. BC_SIDE_LEFT.
|
||||
*/
|
||||
void setBoundarySideClosed(BC_SIDE side);
|
||||
void setBoundarySideClosed(BC_SIDE side) {
|
||||
if (this->dim == 1) {
|
||||
if ((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)) {
|
||||
throw std::invalid_argument(
|
||||
"For the one-dimensional case, only the BC_SIDE_LEFT and "
|
||||
"BC_SIDE_RIGHT borders exist.");
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
|
||||
const int n = is_vertical ? this->rows : this->cols;
|
||||
|
||||
this->boundaries[side] =
|
||||
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets all elements of the specified boundary side to the boundary
|
||||
@ -117,7 +169,21 @@ public:
|
||||
* @param value Concentration to be set for all elements of the specified
|
||||
* page.
|
||||
*/
|
||||
void setBoundarySideConstant(BC_SIDE side, double value);
|
||||
void setBoundarySideConstant(BC_SIDE side, double value) {
|
||||
if (this->dim == 1) {
|
||||
if ((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)) {
|
||||
throw std::invalid_argument(
|
||||
"For the one-dimensional case, only the BC_SIDE_LEFT and "
|
||||
"BC_SIDE_RIGHT borders exist.");
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
|
||||
const int n = is_vertical ? this->rows : this->cols;
|
||||
|
||||
this->boundaries[side] =
|
||||
std::vector<BoundaryElement<T>>(n, BoundaryElement<T>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Specifically sets the boundary element of the specified side
|
||||
@ -128,7 +194,14 @@ public:
|
||||
* boundary side. Must index an element of the corresponding
|
||||
* side.
|
||||
*/
|
||||
void setBoundaryElementClosed(BC_SIDE side, int index);
|
||||
void setBoundaryElemenClosed(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 std::invalid_argument(
|
||||
"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
|
||||
@ -142,7 +215,15 @@ public:
|
||||
* @param value Concentration value to which the boundary element should be
|
||||
set.
|
||||
*/
|
||||
void setBoundaryElementConstant(BC_SIDE side, int index, double value);
|
||||
void 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 std::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the boundary condition of a specified side as a vector
|
||||
@ -150,19 +231,41 @@ public:
|
||||
*
|
||||
* @param side Boundary side from which the boundary conditions are to be
|
||||
* returned.
|
||||
* @return vector<BoundaryElement> Contains the boundary conditions as
|
||||
* BoundaryElement objects.
|
||||
* @return Contains the boundary conditions as
|
||||
* BoundaryElement<T> objects.
|
||||
*/
|
||||
const std::vector<BoundaryElement> getBoundarySide(BC_SIDE side);
|
||||
const std::vector<BoundaryElement<T>> &getBoundarySide(BC_SIDE side) const {
|
||||
if (this->dim == 1) {
|
||||
if ((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)) {
|
||||
throw std::invalid_argument(
|
||||
"For the one-dimensional trap, 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 VectorXd Vector with values as doubles.
|
||||
* @return Vector with values as T.
|
||||
*/
|
||||
Eigen::VectorXd getBoundarySideValues(BC_SIDE side);
|
||||
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
|
||||
@ -172,9 +275,16 @@ public:
|
||||
* @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.
|
||||
* @return Boundary condition as a BoundaryElement<T>
|
||||
* object.
|
||||
*/
|
||||
BoundaryElement getBoundaryElement(BC_SIDE side, int index);
|
||||
BoundaryElement<T> getBoundaryElement(BC_SIDE side, int index) const {
|
||||
if ((boundaries[side].size() < index) || index < 0) {
|
||||
throw std::invalid_argument(
|
||||
"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
|
||||
@ -184,28 +294,47 @@ public:
|
||||
* @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.
|
||||
* @return Boundary Type of the corresponding boundary condition.
|
||||
*/
|
||||
BC_TYPE getBoundaryElementType(BC_SIDE side, int index);
|
||||
BC_TYPE getBoundaryElementType(BC_SIDE side, int index) const {
|
||||
if ((boundaries[side].size() < index) || index < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Index is selected either too large or too small.");
|
||||
}
|
||||
return this->boundaries[side][index].getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the concentration value of a corresponding
|
||||
* BoundaryElement object if it is a constant boundary condition.
|
||||
* 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 double Concentration of the corresponding BoundaryElement object.
|
||||
* @return Concentration of the corresponding BoundaryElement<T>
|
||||
* object.
|
||||
*/
|
||||
double getBoundaryElementValue(BC_SIDE side, int index);
|
||||
T getBoundaryElementValue(BC_SIDE side, int index) const {
|
||||
if ((boundaries[side].size() < index) || index < 0) {
|
||||
throw std::invalid_argument(
|
||||
"Index is selected either too large or too small.");
|
||||
}
|
||||
if (boundaries[side][index].getType() != BC_TYPE_CONSTANT) {
|
||||
throw std::invalid_argument(
|
||||
"A value can only be output if it is a constant boundary condition.");
|
||||
}
|
||||
return this->boundaries[side][index].getValue();
|
||||
}
|
||||
|
||||
private:
|
||||
Grid grid; // Boundary is directly dependent on the dimensions of a predefined
|
||||
const std::uint8_t dim;
|
||||
const std::uint32_t cols;
|
||||
const std::uint32_t rows;
|
||||
|
||||
std::vector<std::vector<BoundaryElement>>
|
||||
std::vector<std::vector<BoundaryElement<T>>>
|
||||
boundaries; // Vector with Boundary Element information
|
||||
};
|
||||
|
||||
} // namespace tug
|
||||
#endif // BOUNDARY_H_
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file BTCSv2.cpp
|
||||
* @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
|
||||
@ -7,10 +7,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Schemes.hpp"
|
||||
#ifndef BTCS_H_
|
||||
#define BTCS_H_
|
||||
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
#include <Eigen/src/Core/util/Meta.h>
|
||||
#include <cstddef>
|
||||
#include <tug/Boundary.hpp>
|
||||
#include <tug/Grid.hpp>
|
||||
@ -22,6 +23,8 @@
|
||||
#define omp_get_thread_num() 0
|
||||
#endif
|
||||
|
||||
namespace tug {
|
||||
|
||||
// calculates coefficient for boundary in constant case
|
||||
template <class T>
|
||||
constexpr std::pair<T, T> calcBoundaryCoeffConstant(T alpha_center,
|
||||
@ -45,13 +48,15 @@ constexpr std::pair<T, T> calcBoundaryCoeffClosed(T alpha_center, T alpha_side,
|
||||
}
|
||||
|
||||
// creates coefficient matrix for next time step from alphas in x-direction
|
||||
static Eigen::SparseMatrix<double>
|
||||
createCoeffMatrix(Eigen::MatrixXd &alpha, std::vector<BoundaryElement> &bcLeft,
|
||||
std::vector<BoundaryElement> &bcRight, int numCols,
|
||||
int rowIndex, double sx) {
|
||||
template <class T>
|
||||
static Eigen::SparseMatrix<T>
|
||||
createCoeffMatrix(const Eigen::MatrixX<T> &alpha,
|
||||
const std::vector<BoundaryElement<T>> &bcLeft,
|
||||
const std::vector<BoundaryElement<T>> &bcRight, int numCols,
|
||||
int rowIndex, T sx) {
|
||||
|
||||
// square matrix of column^2 dimension for the coefficients
|
||||
Eigen::SparseMatrix<double> cm(numCols, numCols);
|
||||
Eigen::SparseMatrix<T> cm(numCols, numCols);
|
||||
cm.reserve(Eigen::VectorXi::Constant(numCols, 3));
|
||||
|
||||
// left column
|
||||
@ -142,14 +147,18 @@ constexpr T calcExplicitConcentrationsBoundaryConstant(T conc_center, T conc_bc,
|
||||
|
||||
// creates a solution vector for next time step from the current state of
|
||||
// concentrations
|
||||
static Eigen::VectorXd createSolutionVector(
|
||||
Eigen::MatrixXd &concentrations, Eigen::MatrixXd &alphaX,
|
||||
Eigen::MatrixXd &alphaY, std::vector<BoundaryElement> &bcLeft,
|
||||
std::vector<BoundaryElement> &bcRight, std::vector<BoundaryElement> &bcTop,
|
||||
std::vector<BoundaryElement> &bcBottom, int length, int rowIndex, double sx,
|
||||
double sy) {
|
||||
template <class T>
|
||||
static Eigen::VectorX<T>
|
||||
createSolutionVector(const Eigen::MatrixX<T> &concentrations,
|
||||
const Eigen::MatrixX<T> &alphaX,
|
||||
const Eigen::MatrixX<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,
|
||||
int length, int rowIndex, T sx, T sy) {
|
||||
|
||||
Eigen::VectorXd sv(length);
|
||||
Eigen::VectorX<T> sv(length);
|
||||
const std::size_t numRows = concentrations.rows();
|
||||
|
||||
// inner rows
|
||||
@ -235,10 +244,11 @@ static Eigen::VectorXd createSolutionVector(
|
||||
// solver for linear equation system; A corresponds to coefficient matrix,
|
||||
// b to the solution vector
|
||||
// use of EigenLU solver
|
||||
static Eigen::VectorXd EigenLUAlgorithm(Eigen::SparseMatrix<double> &A,
|
||||
Eigen::VectorXd &b) {
|
||||
template <class T>
|
||||
static Eigen::VectorX<T> EigenLUAlgorithm(Eigen::SparseMatrix<T> &A,
|
||||
Eigen::VectorX<T> &b) {
|
||||
|
||||
Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
|
||||
Eigen::SparseLU<Eigen::SparseMatrix<T>> solver;
|
||||
solver.analyzePattern(A);
|
||||
solver.factorize(A);
|
||||
|
||||
@ -248,14 +258,15 @@ static Eigen::VectorXd EigenLUAlgorithm(Eigen::SparseMatrix<double> &A,
|
||||
// solver for linear equation system; A corresponds to coefficient matrix,
|
||||
// b to the solution vector
|
||||
// implementation of Thomas Algorithm
|
||||
static Eigen::VectorXd ThomasAlgorithm(Eigen::SparseMatrix<double> &A,
|
||||
Eigen::VectorXd &b) {
|
||||
template <class T>
|
||||
static Eigen::VectorX<T> ThomasAlgorithm(Eigen::SparseMatrix<T> &A,
|
||||
Eigen::VectorX<T> &b) {
|
||||
Eigen::Index n = b.size();
|
||||
|
||||
Eigen::VectorXd a_diag(n);
|
||||
Eigen::VectorXd b_diag(n);
|
||||
Eigen::VectorXd c_diag(n);
|
||||
Eigen::VectorXd x_vec = b;
|
||||
Eigen::VectorX<T> a_diag(n);
|
||||
Eigen::VectorX<T> b_diag(n);
|
||||
Eigen::VectorX<T> c_diag(n);
|
||||
Eigen::VectorX<T> x_vec = b;
|
||||
|
||||
// Fill diagonals vectors
|
||||
b_diag[0] = A.coeff(0, 0);
|
||||
@ -269,6 +280,29 @@ static Eigen::VectorXd ThomasAlgorithm(Eigen::SparseMatrix<double> &A,
|
||||
a_diag[n - 1] = A.coeff(n - 1, n - 2);
|
||||
b_diag[n - 1] = A.coeff(n - 1, n - 1);
|
||||
|
||||
// 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--;
|
||||
c_diag[0] /= b_diag[0];
|
||||
@ -291,23 +325,24 @@ static Eigen::VectorXd ThomasAlgorithm(Eigen::SparseMatrix<double> &A,
|
||||
}
|
||||
|
||||
// BTCS solution for 1D grid
|
||||
static void
|
||||
BTCS_1D(Grid &grid, Boundary &bc, double timestep,
|
||||
Eigen::VectorXd (*solverFunc)(Eigen::SparseMatrix<double> &A,
|
||||
Eigen::VectorXd &b)) {
|
||||
template <class T>
|
||||
static void BTCS_1D(Grid<T> &grid, Boundary<T> &bc, T timestep,
|
||||
Eigen::VectorX<T> (*solverFunc)(Eigen::SparseMatrix<T> &A,
|
||||
Eigen::VectorX<T> &b)) {
|
||||
int length = grid.getLength();
|
||||
double sx = timestep / (grid.getDelta() * grid.getDelta());
|
||||
T sx = timestep / (grid.getDelta() * grid.getDelta());
|
||||
|
||||
Eigen::VectorXd concentrations_t1(length);
|
||||
Eigen::VectorX<T> concentrations_t1(length);
|
||||
|
||||
Eigen::SparseMatrix<double> A;
|
||||
Eigen::VectorXd b(length);
|
||||
Eigen::SparseMatrix<T> A;
|
||||
Eigen::VectorX<T> b(length);
|
||||
|
||||
Eigen::MatrixXd alpha = grid.getAlpha();
|
||||
std::vector<BoundaryElement> bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
|
||||
std::vector<BoundaryElement> bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
|
||||
const auto &alpha = grid.getAlpha();
|
||||
|
||||
Eigen::MatrixXd concentrations = grid.getConcentrations();
|
||||
const auto &bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
|
||||
const auto &bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
|
||||
|
||||
Eigen::MatrixX<T> concentrations = grid.getConcentrations();
|
||||
int rowIndex = 0;
|
||||
A = createCoeffMatrix(alpha, bcLeft, bcRight, length, rowIndex,
|
||||
sx); // this is exactly same as in 2D
|
||||
@ -331,31 +366,32 @@ BTCS_1D(Grid &grid, Boundary &bc, double timestep,
|
||||
}
|
||||
|
||||
// BTCS solution for 2D grid
|
||||
static void
|
||||
BTCS_2D(Grid &grid, Boundary &bc, double timestep,
|
||||
Eigen::VectorXd (*solverFunc)(Eigen::SparseMatrix<double> &A,
|
||||
Eigen::VectorXd &b),
|
||||
int numThreads) {
|
||||
template <class T>
|
||||
static void BTCS_2D(Grid<T> &grid, Boundary<T> &bc, T timestep,
|
||||
Eigen::VectorX<T> (*solverFunc)(Eigen::SparseMatrix<T> &A,
|
||||
Eigen::VectorX<T> &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());
|
||||
T sx = timestep / (2 * grid.getDeltaCol() * grid.getDeltaCol());
|
||||
T sy = timestep / (2 * grid.getDeltaRow() * grid.getDeltaRow());
|
||||
|
||||
Eigen::MatrixXd concentrations_t1 =
|
||||
Eigen::MatrixXd::Constant(rowMax, colMax, 0);
|
||||
Eigen::VectorXd row_t1(colMax);
|
||||
Eigen::MatrixX<T> concentrations_t1 =
|
||||
Eigen::MatrixX<T>::Constant(rowMax, colMax, 0);
|
||||
Eigen::VectorX<T> row_t1(colMax);
|
||||
|
||||
Eigen::SparseMatrix<double> A;
|
||||
Eigen::VectorXd b;
|
||||
Eigen::SparseMatrix<T> A;
|
||||
Eigen::VectorX<T> b;
|
||||
|
||||
Eigen::MatrixXd alphaX = grid.getAlphaX();
|
||||
Eigen::MatrixXd alphaY = grid.getAlphaY();
|
||||
std::vector<BoundaryElement> bcLeft = bc.getBoundarySide(BC_SIDE_LEFT);
|
||||
std::vector<BoundaryElement> bcRight = bc.getBoundarySide(BC_SIDE_RIGHT);
|
||||
std::vector<BoundaryElement> bcTop = bc.getBoundarySide(BC_SIDE_TOP);
|
||||
std::vector<BoundaryElement> bcBottom = bc.getBoundarySide(BC_SIDE_BOTTOM);
|
||||
auto alphaX = grid.getAlphaX();
|
||||
auto alphaY = grid.getAlphaY();
|
||||
|
||||
Eigen::MatrixXd concentrations = grid.getConcentrations();
|
||||
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);
|
||||
|
||||
Eigen::MatrixX<T> concentrations = grid.getConcentrations();
|
||||
|
||||
#pragma omp parallel for num_threads(numThreads) private(A, b, row_t1)
|
||||
for (int i = 0; i < rowMax; i++) {
|
||||
@ -364,8 +400,6 @@ BTCS_2D(Grid &grid, Boundary &bc, double timestep,
|
||||
b = createSolutionVector(concentrations, alphaX, alphaY, bcLeft, bcRight,
|
||||
bcTop, bcBottom, colMax, i, sx, sy);
|
||||
|
||||
Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
|
||||
|
||||
row_t1 = solverFunc(A, b);
|
||||
|
||||
concentrations_t1.row(i) = row_t1;
|
||||
@ -394,7 +428,8 @@ BTCS_2D(Grid &grid, Boundary &bc, double timestep,
|
||||
}
|
||||
|
||||
// entry point for EigenLU solver; differentiate between 1D and 2D grid
|
||||
void BTCS_LU(Grid &grid, Boundary &bc, double timestep, int numThreads) {
|
||||
template <class T>
|
||||
void BTCS_LU(Grid<T> &grid, Boundary<T> &bc, T timestep, int numThreads) {
|
||||
if (grid.getDim() == 1) {
|
||||
BTCS_1D(grid, bc, timestep, EigenLUAlgorithm);
|
||||
} else if (grid.getDim() == 2) {
|
||||
@ -406,7 +441,8 @@ void BTCS_LU(Grid &grid, Boundary &bc, double timestep, int numThreads) {
|
||||
}
|
||||
|
||||
// entry point for Thomas algorithm solver; differentiate 1D and 2D grid
|
||||
void BTCS_Thomas(Grid &grid, Boundary &bc, double timestep, int numThreads) {
|
||||
template <class T>
|
||||
void BTCS_Thomas(Grid<T> &grid, Boundary<T> &bc, T timestep, int numThreads) {
|
||||
if (grid.getDim() == 1) {
|
||||
BTCS_1D(grid, bc, timestep, ThomasAlgorithm);
|
||||
} else if (grid.getDim() == 2) {
|
||||
@ -416,3 +452,6 @@ void BTCS_Thomas(Grid &grid, Boundary &bc, double timestep, int numThreads) {
|
||||
"Error: Only 1- and 2-dimensional grids are defined!");
|
||||
}
|
||||
}
|
||||
} // namespace tug
|
||||
|
||||
#endif // BTCS_H_
|
||||
@ -1,11 +1,13 @@
|
||||
/**
|
||||
* @file FTCS.cpp
|
||||
* @file FTCS.hpp
|
||||
* @brief Implementation of heterogenous FTCS (forward time-centered space)
|
||||
* solution of diffusion equation in 1D and 2D space.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Schemes.hpp"
|
||||
#ifndef FTCS_H_
|
||||
#define FTCS_H_
|
||||
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
@ -18,8 +20,11 @@
|
||||
#define omp_get_thread_num() 0
|
||||
#endif
|
||||
|
||||
namespace tug {
|
||||
|
||||
// calculates horizontal change on one cell independent of boundary type
|
||||
static inline double calcHorizontalChange(Grid &grid, int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChange(Grid<T> &grid, int &row, int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaX()(row, col + 1),
|
||||
grid.getAlphaX()(row, col)) *
|
||||
@ -35,7 +40,8 @@ static inline double calcHorizontalChange(Grid &grid, int &row, int &col) {
|
||||
}
|
||||
|
||||
// calculates vertical change on one cell independent of boundary type
|
||||
static inline double calcVerticalChange(Grid &grid, int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChange(Grid<T> &grid, int &row, int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaY()(row + 1, col),
|
||||
grid.getAlphaY()(row, col)) *
|
||||
@ -51,10 +57,10 @@ static inline double calcVerticalChange(Grid &grid, int &row, int &col) {
|
||||
}
|
||||
|
||||
// calculates horizontal change on one cell with a constant left boundary
|
||||
static inline double calcHorizontalChangeLeftBoundaryConstant(Grid &grid,
|
||||
Boundary &bc,
|
||||
int &row,
|
||||
int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeLeftBoundaryConstant(Grid<T> &grid,
|
||||
Boundary<T> &bc,
|
||||
int &row, int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaX()(row, col + 1),
|
||||
grid.getAlphaX()(row, col)) *
|
||||
@ -68,8 +74,9 @@ static inline double calcHorizontalChangeLeftBoundaryConstant(Grid &grid,
|
||||
}
|
||||
|
||||
// calculates horizontal change on one cell with a closed left boundary
|
||||
static inline double
|
||||
calcHorizontalChangeLeftBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeLeftBoundaryClosed(Grid<T> &grid, int &row,
|
||||
int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaX()(row, col + 1),
|
||||
grid.getAlphaX()(row, col)) *
|
||||
@ -78,8 +85,9 @@ calcHorizontalChangeLeftBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
}
|
||||
|
||||
// checks boundary condition type for a cell on the left edge of grid
|
||||
static inline double calcHorizontalChangeLeftBoundary(Grid &grid, Boundary &bc,
|
||||
int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeLeftBoundary(Grid<T> &grid, Boundary<T> &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) {
|
||||
@ -90,10 +98,10 @@ static inline double calcHorizontalChangeLeftBoundary(Grid &grid, Boundary &bc,
|
||||
}
|
||||
|
||||
// calculates horizontal change on one cell with a constant right boundary
|
||||
static inline double calcHorizontalChangeRightBoundaryConstant(Grid &grid,
|
||||
Boundary &bc,
|
||||
int &row,
|
||||
int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeRightBoundaryConstant(Grid<T> &grid,
|
||||
Boundary<T> &bc,
|
||||
int &row, int &col) {
|
||||
|
||||
return 2 * grid.getAlphaX()(row, col) *
|
||||
bc.getBoundaryElementValue(BC_SIDE_RIGHT, row) -
|
||||
@ -107,8 +115,9 @@ static inline double calcHorizontalChangeRightBoundaryConstant(Grid &grid,
|
||||
}
|
||||
|
||||
// calculates horizontal change on one cell with a closed right boundary
|
||||
static inline double
|
||||
calcHorizontalChangeRightBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeRightBoundaryClosed(Grid<T> &grid, int &row,
|
||||
int &col) {
|
||||
|
||||
return -(calcAlphaIntercell(grid.getAlphaX()(row, col - 1),
|
||||
grid.getAlphaX()(row, col)) *
|
||||
@ -117,8 +126,10 @@ calcHorizontalChangeRightBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
}
|
||||
|
||||
// checks boundary condition type for a cell on the right edge of grid
|
||||
static inline double calcHorizontalChangeRightBoundary(Grid &grid, Boundary &bc,
|
||||
int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcHorizontalChangeRightBoundary(Grid<T> &grid,
|
||||
Boundary<T> &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) {
|
||||
@ -129,9 +140,10 @@ static inline double calcHorizontalChangeRightBoundary(Grid &grid, Boundary &bc,
|
||||
}
|
||||
|
||||
// calculates vertical change on one cell with a constant top boundary
|
||||
static inline double calcVerticalChangeTopBoundaryConstant(Grid &grid,
|
||||
Boundary &bc,
|
||||
int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeTopBoundaryConstant(Grid<T> &grid,
|
||||
Boundary<T> &bc, int &row,
|
||||
int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaY()(row + 1, col),
|
||||
grid.getAlphaY()(row, col)) *
|
||||
@ -145,8 +157,9 @@ static inline double calcVerticalChangeTopBoundaryConstant(Grid &grid,
|
||||
}
|
||||
|
||||
// calculates vertical change on one cell with a closed top boundary
|
||||
static inline double calcVerticalChangeTopBoundaryClosed(Grid &grid, int &row,
|
||||
int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeTopBoundaryClosed(Grid<T> &grid, int &row,
|
||||
int &col) {
|
||||
|
||||
return calcAlphaIntercell(grid.getAlphaY()(row + 1, col),
|
||||
grid.getConcentrations()(row, col)) *
|
||||
@ -155,8 +168,9 @@ static inline double calcVerticalChangeTopBoundaryClosed(Grid &grid, int &row,
|
||||
}
|
||||
|
||||
// checks boundary condition type for a cell on the top edge of grid
|
||||
static inline double calcVerticalChangeTopBoundary(Grid &grid, Boundary &bc,
|
||||
int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeTopBoundary(Grid<T> &grid, Boundary<T> &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) {
|
||||
@ -167,10 +181,10 @@ static inline double calcVerticalChangeTopBoundary(Grid &grid, Boundary &bc,
|
||||
}
|
||||
|
||||
// calculates vertical change on one cell with a constant bottom boundary
|
||||
static inline double calcVerticalChangeBottomBoundaryConstant(Grid &grid,
|
||||
Boundary &bc,
|
||||
int &row,
|
||||
int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeBottomBoundaryConstant(Grid<T> &grid,
|
||||
Boundary<T> &bc,
|
||||
int &row, int &col) {
|
||||
|
||||
return 2 * grid.getAlphaY()(row, col) *
|
||||
bc.getBoundaryElementValue(BC_SIDE_BOTTOM, col) -
|
||||
@ -184,8 +198,9 @@ static inline double calcVerticalChangeBottomBoundaryConstant(Grid &grid,
|
||||
}
|
||||
|
||||
// calculates vertical change on one cell with a closed bottom boundary
|
||||
static inline double
|
||||
calcVerticalChangeBottomBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeBottomBoundaryClosed(Grid<T> &grid, int &row,
|
||||
int &col) {
|
||||
|
||||
return -(calcAlphaIntercell(grid.getAlphaY()(row, col),
|
||||
grid.getAlphaY()(row - 1, col)) *
|
||||
@ -194,8 +209,9 @@ calcVerticalChangeBottomBoundaryClosed(Grid &grid, int &row, int &col) {
|
||||
}
|
||||
|
||||
// checks boundary condition type for a cell on the bottom edge of grid
|
||||
static inline double calcVerticalChangeBottomBoundary(Grid &grid, Boundary &bc,
|
||||
int &row, int &col) {
|
||||
template <class T>
|
||||
static inline T calcVerticalChangeBottomBoundary(Grid<T> &grid, Boundary<T> &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) {
|
||||
@ -206,12 +222,14 @@ static inline double calcVerticalChangeBottomBoundary(Grid &grid, Boundary &bc,
|
||||
}
|
||||
|
||||
// FTCS solution for 1D grid
|
||||
static void FTCS_1D(Grid &grid, Boundary &bc, double ×tep) {
|
||||
template <class T>
|
||||
static void FTCS_1D(Grid<T> &grid, Boundary<T> &bc, T timestep) {
|
||||
int colMax = grid.getCol();
|
||||
double deltaCol = grid.getDeltaCol();
|
||||
T deltaCol = grid.getDeltaCol();
|
||||
|
||||
// matrix for concentrations at time t+1
|
||||
Eigen::MatrixXd concentrations_t1 = Eigen::MatrixXd::Constant(1, colMax, 0);
|
||||
Eigen::MatrixX<T> concentrations_t1 =
|
||||
Eigen::MatrixX<T>::Constant(1, colMax, 0);
|
||||
|
||||
// only one row in 1D case -> row constant at index 0
|
||||
int row = 0;
|
||||
@ -243,16 +261,17 @@ static void FTCS_1D(Grid &grid, Boundary &bc, double ×tep) {
|
||||
}
|
||||
|
||||
// FTCS solution for 2D grid
|
||||
static void FTCS_2D(Grid &grid, Boundary &bc, double ×tep,
|
||||
template <class T>
|
||||
static void FTCS_2D(Grid<T> &grid, Boundary<T> &bc, T timestep,
|
||||
int numThreads) {
|
||||
int rowMax = grid.getRow();
|
||||
int colMax = grid.getCol();
|
||||
double deltaRow = grid.getDeltaRow();
|
||||
double deltaCol = grid.getDeltaCol();
|
||||
T deltaRow = grid.getDeltaRow();
|
||||
T deltaCol = grid.getDeltaCol();
|
||||
|
||||
// matrix for concentrations at time t+1
|
||||
Eigen::MatrixXd concentrations_t1 =
|
||||
Eigen::MatrixXd::Constant(rowMax, colMax, 0);
|
||||
Eigen::MatrixX<T> concentrations_t1 =
|
||||
Eigen::MatrixX<T>::Constant(rowMax, colMax, 0);
|
||||
|
||||
// inner cells
|
||||
// these are independent of the boundary condition type
|
||||
@ -369,7 +388,8 @@ static void FTCS_2D(Grid &grid, Boundary &bc, double ×tep,
|
||||
}
|
||||
|
||||
// entry point; differentiate between 1D and 2D grid
|
||||
void FTCS(Grid &grid, Boundary &bc, double ×tep, int &numThreads) {
|
||||
template <class T>
|
||||
void FTCS(Grid<T> &grid, Boundary<T> &bc, T timestep, int &numThreads) {
|
||||
if (grid.getDim() == 1) {
|
||||
FTCS_1D(grid, bc, timestep);
|
||||
} else if (grid.getDim() == 2) {
|
||||
@ -379,3 +399,6 @@ void FTCS(Grid &grid, Boundary &bc, double ×tep, int &numThreads) {
|
||||
"Error: Only 1- and 2-dimensional grids are defined!");
|
||||
}
|
||||
}
|
||||
} // namespace tug
|
||||
|
||||
#endif // FTCS_H_
|
||||
@ -10,8 +10,17 @@
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
#include <stdexcept>
|
||||
|
||||
class Grid {
|
||||
namespace tug {
|
||||
|
||||
/**
|
||||
* @brief Holds a matrix with concenctration and respective matrix/matrices of
|
||||
* alpha coefficients.
|
||||
*
|
||||
* @tparam T Type to be used for matrices, e.g. double or float
|
||||
*/
|
||||
template <class T> class Grid {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new 1D-Grid object of a given length, which holds a
|
||||
@ -21,7 +30,19 @@ public:
|
||||
*
|
||||
* @param length Length of the 1D-Grid. Must be greater than 3.
|
||||
*/
|
||||
Grid(int length);
|
||||
Grid(int length) : col(length), domainCol(length) {
|
||||
if (length <= 3) {
|
||||
throw std::invalid_argument(
|
||||
"Given grid length too small. Must be greater than 3.");
|
||||
}
|
||||
|
||||
this->dim = 1;
|
||||
this->deltaCol =
|
||||
static_cast<T>(this->domainCol) / static_cast<T>(this->col); // -> 1
|
||||
|
||||
this->concentrations = Eigen::MatrixX<T>::Constant(1, col, MAT_INIT_VAL);
|
||||
this->alphaX = Eigen::MatrixX<T>::Constant(1, col, MAT_INIT_VAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a new 2D-Grid object of given dimensions, which holds a
|
||||
@ -34,146 +55,282 @@ public:
|
||||
* @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);
|
||||
Grid(int _row, int _col)
|
||||
: row(_row), col(_col), domainRow(_row), domainCol(_col) {
|
||||
if (row <= 3 || col <= 3) {
|
||||
throw std::invalid_argument(
|
||||
"Given grid dimensions too small. Must each be greater than 3.");
|
||||
}
|
||||
|
||||
this->dim = 2;
|
||||
this->deltaRow =
|
||||
static_cast<T>(this->domainRow) / static_cast<T>(this->row); // -> 1
|
||||
this->deltaCol =
|
||||
static_cast<T>(this->domainCol) / static_cast<T>(this->col); // -> 1
|
||||
|
||||
this->concentrations = Eigen::MatrixX<T>::Constant(row, col, MAT_INIT_VAL);
|
||||
this->alphaX = Eigen::MatrixX<T>::Constant(row, col, MAT_INIT_VAL);
|
||||
this->alphaY = Eigen::MatrixX<T>::Constant(row, col, MAT_INIT_VAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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).
|
||||
* @param concentrations An Eigen3 MatrixX<T> holding the concentrations.
|
||||
* Matrix must have correct dimensions as defined in row and col. (Or length,
|
||||
* in 1D case).
|
||||
*/
|
||||
void setConcentrations(const Eigen::MatrixXd &concentrations);
|
||||
void setConcentrations(const Eigen::MatrixX<T> &concentrations) {
|
||||
if (concentrations.rows() != this->row ||
|
||||
concentrations.cols() != this->col) {
|
||||
throw std::invalid_argument(
|
||||
"Given matrix of concentrations mismatch with Grid dimensions!");
|
||||
}
|
||||
|
||||
this->concentrations = 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.
|
||||
* @return An Eigen3 matrix holding the concentrations and having
|
||||
* the same dimensions as the grid.
|
||||
*/
|
||||
const Eigen::MatrixXd &getConcentrations() { return this->concentrations; }
|
||||
const Eigen::MatrixX<T> &getConcentrations() { return this->concentrations; }
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param alpha An Eigen3 MatrixX<T> with 1 row holding the alpha
|
||||
* coefficients. Matrix columns must have same size as length of grid.
|
||||
*/
|
||||
void setAlpha(const Eigen::MatrixXd &alpha);
|
||||
void setAlpha(const Eigen::MatrixX<T> &alpha) {
|
||||
if (dim != 1) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not one dimensional, you should probably "
|
||||
"use 2D setter function!");
|
||||
}
|
||||
if (alpha.rows() != 1 || alpha.cols() != this->col) {
|
||||
throw std::invalid_argument(
|
||||
"Given matrix of alpha coefficients mismatch with Grid dimensions!");
|
||||
}
|
||||
|
||||
this->alphaX = 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
|
||||
* @param alphaX An Eigen3 MatrixX<T> 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
|
||||
* @param alphaY An Eigen3 MatrixX<T> holding the alpha coefficients in
|
||||
* y-direction. Matrix must be of same size as the grid.
|
||||
*/
|
||||
void setAlpha(const Eigen::MatrixXd &alphaX, const Eigen::MatrixXd &alphaY);
|
||||
void setAlpha(const Eigen::MatrixX<T> &alphaX,
|
||||
const Eigen::MatrixX<T> &alphaY) {
|
||||
if (dim != 2) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not two dimensional, you should probably "
|
||||
"use 1D setter function!");
|
||||
}
|
||||
if (alphaX.rows() != this->row || alphaX.cols() != this->col) {
|
||||
throw std::invalid_argument(
|
||||
"Given matrix of alpha coefficients in x-direction "
|
||||
"mismatch with GRid dimensions!");
|
||||
}
|
||||
if (alphaY.rows() != this->row || alphaY.cols() != this->col) {
|
||||
throw std::invalid_argument(
|
||||
"Given matrix of alpha coefficients in y-direction "
|
||||
"mismatch with GRid dimensions!");
|
||||
}
|
||||
|
||||
this->alphaX = alphaX;
|
||||
this->alphaY = 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.
|
||||
* @return A matrix with 1 row holding the alpha coefficients.
|
||||
*/
|
||||
const Eigen::MatrixXd &getAlpha();
|
||||
const Eigen::MatrixX<T> &getAlpha() const {
|
||||
if (dim != 1) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not one dimensional, you should probably "
|
||||
"use either getAlphaX() or getAlphaY()!");
|
||||
}
|
||||
|
||||
return this->alphaX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @return A matrix holding the alpha coefficients in x-direction.
|
||||
*/
|
||||
const Eigen::MatrixXd &getAlphaX();
|
||||
const Eigen::MatrixX<T> &getAlphaX() const {
|
||||
|
||||
if (dim != 2) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not two dimensional, you should probably use getAlpha()!");
|
||||
}
|
||||
|
||||
return this->alphaX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @return A matrix holding the alpha coefficients in y-direction.
|
||||
*/
|
||||
const Eigen::MatrixXd &getAlphaY();
|
||||
const Eigen::MatrixX<T> &getAlphaY() const {
|
||||
|
||||
if (dim != 2) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not two dimensional, you should probably use getAlpha()!");
|
||||
}
|
||||
|
||||
return this->alphaY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the dimensions of the grid.
|
||||
*
|
||||
* @return int Dimensions, either 1 or 2.
|
||||
* @return Dimensions, either 1 or 2.
|
||||
*/
|
||||
int getDim();
|
||||
int getDim() const { return this->dim; }
|
||||
|
||||
/**
|
||||
* @brief Gets length of 1D grid. Must be one dimensional grid.
|
||||
*
|
||||
* @return int Length of 1D grid.
|
||||
* @return Length of 1D grid.
|
||||
*/
|
||||
int getLength();
|
||||
int getLength() const {
|
||||
if (dim != 1) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not one dimensional, you should probably "
|
||||
"use getRow() or getCol()!");
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of rows of the grid.
|
||||
*
|
||||
* @return int Number of rows.
|
||||
* @return Number of rows.
|
||||
*/
|
||||
int getRow();
|
||||
int getRow() const { return this->row; }
|
||||
|
||||
/**
|
||||
* @brief Gets the number of columns of the grid.
|
||||
*
|
||||
* @return int Number of columns.
|
||||
* @return Number of columns.
|
||||
*/
|
||||
int getCol();
|
||||
int getCol() const { return this->col; }
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void setDomain(double domainLength) {
|
||||
if (dim != 1) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not one dimensional, you should probaly "
|
||||
"use the 2D domain setter!");
|
||||
}
|
||||
if (domainLength <= 0) {
|
||||
throw std::invalid_argument("Given domain length is not positive!");
|
||||
}
|
||||
|
||||
this->domainCol = domainLength;
|
||||
this->deltaCol = double(this->domainCol) / double(this->col);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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);
|
||||
void setDomain(double domainRow, double domainCol) {
|
||||
if (dim != 2) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not two dimensional, you should probably "
|
||||
"use the 1D domain setter!");
|
||||
}
|
||||
if (domainRow <= 0 || domainCol <= 0) {
|
||||
throw std::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the delta value for 1D-Grid. Grid must be one dimensional.
|
||||
*
|
||||
* @return double Delta value.
|
||||
* @return Delta value.
|
||||
*/
|
||||
double getDelta();
|
||||
T getDelta() const {
|
||||
|
||||
if (dim != 1) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not one dimensional, you should probably "
|
||||
"use the 2D delta getters");
|
||||
}
|
||||
|
||||
return this->deltaCol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the delta value in x-direction.
|
||||
*
|
||||
* @return double Delta value in x-direction.
|
||||
* @return Delta value in x-direction.
|
||||
*/
|
||||
double getDeltaCol();
|
||||
T getDeltaCol() const { return this->deltaCol; }
|
||||
|
||||
/**
|
||||
* @brief Gets the delta value in y-direction. Must be two dimensional grid.
|
||||
*
|
||||
* @return double Delta value in y-direction.
|
||||
* @return Delta value in y-direction.
|
||||
*/
|
||||
double getDeltaRow();
|
||||
T getDeltaRow() const {
|
||||
if (dim != 2) {
|
||||
throw std::invalid_argument(
|
||||
"Grid is not two dimensional, meaning there is no "
|
||||
"delta in y-direction!");
|
||||
}
|
||||
|
||||
return this->deltaRow;
|
||||
}
|
||||
|
||||
private:
|
||||
int col; // number of grid columns
|
||||
int row{1}; // number of grid rows
|
||||
int dim; // 1D or 2D
|
||||
double domainCol; // number of domain columns
|
||||
double domainRow{0}; // number of domain rows
|
||||
double deltaCol; // delta in x-direction (between columns)
|
||||
double deltaRow{0}; // delta in y-direction (between rows)
|
||||
Eigen::MatrixXd concentrations; // Matrix holding grid concentrations
|
||||
Eigen::MatrixXd alphaX; // Matrix holding alpha coefficients in x-direction
|
||||
Eigen::MatrixXd alphaY; // Matrix holding alpha coefficients in y-direction
|
||||
int col; // number of grid columns
|
||||
int row{1}; // number of grid rows
|
||||
int dim; // 1D or 2D
|
||||
T domainCol; // number of domain columns
|
||||
T domainRow{0}; // number of domain rows
|
||||
T deltaCol; // delta in x-direction (between columns)
|
||||
T deltaRow{0}; // delta in y-direction (between rows)
|
||||
Eigen::MatrixX<T> concentrations; // Matrix holding grid concentrations
|
||||
Eigen::MatrixX<T> alphaX; // Matrix holding alpha coefficients in x-direction
|
||||
Eigen::MatrixX<T> alphaY; // Matrix holding alpha coefficients in y-direction
|
||||
|
||||
static constexpr T MAT_INIT_VAL = 0;
|
||||
};
|
||||
|
||||
using Grid64 = Grid<double>;
|
||||
using Grid32 = Grid<float>;
|
||||
} // namespace tug
|
||||
#endif // GRID_H_
|
||||
|
||||
@ -11,23 +11,34 @@
|
||||
|
||||
#include "Boundary.hpp"
|
||||
#include "Grid.hpp"
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/BTCS.hpp"
|
||||
#include "Core/FTCS.hpp"
|
||||
#include "Core/TugUtils.hpp"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#else
|
||||
#define omp_get_num_procs() 1
|
||||
#endif
|
||||
|
||||
namespace tug {
|
||||
|
||||
/**
|
||||
* @brief Enum defining the two implemented solution approaches.
|
||||
* @brief Enum defining the implemented solution approaches.
|
||||
*
|
||||
*/
|
||||
enum APPROACH {
|
||||
FTCS_APPROACH, // Forward Time-Centered Space
|
||||
BTCS_APPROACH, // Backward Time-Centered Space solved with EigenLU solver
|
||||
CRANK_NICOLSON_APPROACH
|
||||
FTCS_APPROACH, /*!< Forward Time-Centered Space */
|
||||
BTCS_APPROACH, /*!< Backward Time-Centered Space */
|
||||
CRANK_NICOLSON_APPROACH /*!< Crank-Nicolson method */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -35,9 +46,9 @@ enum APPROACH {
|
||||
*
|
||||
*/
|
||||
enum SOLVER {
|
||||
EIGEN_LU_SOLVER, // EigenLU solver
|
||||
THOMAS_ALGORITHM_SOLVER // Thomas Algorithm solver; more efficient for
|
||||
// tridiagonal matrices
|
||||
EIGEN_LU_SOLVER, /*!< EigenLU solver */
|
||||
THOMAS_ALGORITHM_SOLVER /*!< Thomas Algorithm solver; more efficient for
|
||||
tridiagonal matrices */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -45,11 +56,11 @@ enum SOLVER {
|
||||
*
|
||||
*/
|
||||
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
|
||||
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 */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -57,9 +68,9 @@ enum CSV_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
|
||||
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 */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -67,8 +78,8 @@ enum CONSOLE_OUTPUT {
|
||||
*
|
||||
*/
|
||||
enum TIME_MEASURE {
|
||||
TIME_MEASURE_OFF, // do not print any time measures
|
||||
TIME_MEASURE_ON // print time measure after last iteration
|
||||
TIME_MEASURE_OFF, /*!< do not print any time measures */
|
||||
TIME_MEASURE_ON /*!< print time measure after last iteration */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -76,7 +87,13 @@ enum TIME_MEASURE {
|
||||
* 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 Simulation {
|
||||
public:
|
||||
/**
|
||||
@ -91,7 +108,7 @@ public:
|
||||
* @param bc Valid boundary condition object
|
||||
* @param approach Approach to solving the problem. Either FTCS or BTCS.
|
||||
*/
|
||||
Simulation(Grid &grid, Boundary &bc, APPROACH approach);
|
||||
Simulation(Grid<T> &_grid, Boundary<T> &_bc) : grid(_grid), bc(_bc){};
|
||||
|
||||
/**
|
||||
* @brief Set the option to output the results to a CSV file. Off by default.
|
||||
@ -107,7 +124,13 @@ public:
|
||||
* - CSV_OUTPUT_XTREME: produce csv output with all
|
||||
* concentration matrices and simulation environment
|
||||
*/
|
||||
void setOutputCSV(CSV_OUTPUT csv_output);
|
||||
void setOutputCSV(CSV_OUTPUT csv_output) {
|
||||
if (csv_output < CSV_OUTPUT_OFF && csv_output > CSV_OUTPUT_VERBOSE) {
|
||||
throw std::invalid_argument("Invalid CSV output option given!");
|
||||
}
|
||||
|
||||
this->csv_output = csv_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the options for outputting information to the console. Off by
|
||||
@ -122,7 +145,14 @@ public:
|
||||
* - CONSOLE_OUTPUT_VERBOSE: print all concentration
|
||||
* matrices to console
|
||||
*/
|
||||
void setOutputConsole(CONSOLE_OUTPUT console_output);
|
||||
void setOutputConsole(CONSOLE_OUTPUT console_output) {
|
||||
if (console_output < CONSOLE_OUTPUT_OFF &&
|
||||
console_output > CONSOLE_OUTPUT_VERBOSE) {
|
||||
throw std::invalid_argument("Invalid console output option given!");
|
||||
}
|
||||
|
||||
this->console_output = console_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the Time Measure option. Off by default.
|
||||
@ -133,7 +163,13 @@ public:
|
||||
* - TIME_MEASURE_ON: Time of simulation run is printed to
|
||||
* console
|
||||
*/
|
||||
void setTimeMeasure(TIME_MEASURE time_measure);
|
||||
void setTimeMeasure(TIME_MEASURE time_measure) {
|
||||
if (time_measure < TIME_MEASURE_OFF && time_measure > TIME_MEASURE_ON) {
|
||||
throw std::invalid_argument("Invalid time measure option given!");
|
||||
}
|
||||
|
||||
this->time_measure = time_measure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setting the time step for each iteration step. Time step must be
|
||||
@ -141,14 +177,72 @@ public:
|
||||
*
|
||||
* @param timestep Valid timestep greater than zero.
|
||||
*/
|
||||
void setTimestep(double timestep);
|
||||
void setTimestep(T timestep) {
|
||||
if (timestep <= 0) {
|
||||
throw_invalid_argument("Timestep has to be greater than zero.");
|
||||
}
|
||||
|
||||
if constexpr (approach == FTCS_APPROACH ||
|
||||
approach == CRANK_NICOLSON_APPROACH) {
|
||||
T cfl;
|
||||
if (grid.getDim() == 1) {
|
||||
|
||||
const T deltaSquare = grid.getDelta();
|
||||
const T maxAlpha = grid.getAlpha().maxCoeff();
|
||||
|
||||
// Courant-Friedrichs-Lewy condition
|
||||
cfl = deltaSquare / (4 * maxAlpha);
|
||||
} else if (grid.getDim() == 2) {
|
||||
const T deltaColSquare = grid.getDeltaCol() * grid.getDeltaCol();
|
||||
// will be 0 if 1D, else ...
|
||||
const T deltaRowSquare = grid.getDeltaRow() * grid.getDeltaRow();
|
||||
const T minDeltaSquare = std::min(deltaColSquare, deltaRowSquare);
|
||||
|
||||
const T maxAlpha =
|
||||
std::min(grid.getAlphaX().maxCoeff(), grid.getAlphaY().maxCoeff());
|
||||
|
||||
cfl = minDeltaSquare / (4 * maxAlpha);
|
||||
}
|
||||
const std::string dim = std::to_string(grid.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
|
||||
*/
|
||||
double getTimestep();
|
||||
T getTimestep() const { return this->timestep; }
|
||||
|
||||
/**
|
||||
* @brief Set the desired iterations to be calculated. A value greater
|
||||
@ -156,16 +250,13 @@ public:
|
||||
*
|
||||
* @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);
|
||||
void setIterations(int iterations) {
|
||||
if (iterations <= 0) {
|
||||
throw std::invalid_argument(
|
||||
"Number of iterations must be greater than zero.");
|
||||
}
|
||||
this->iterations = iterations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the number of desired openMP Threads.
|
||||
@ -175,20 +266,32 @@ public:
|
||||
* maximum number of processors is set as the default case during Simulation
|
||||
* construction.
|
||||
*/
|
||||
void setNumberThreads(int num_threads);
|
||||
void setNumberThreads(int num_threads) {
|
||||
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 Return the currently set iterations to be calculated.
|
||||
*
|
||||
* @return int Number of iterations.
|
||||
*/
|
||||
int getIterations();
|
||||
int getIterations() const { return this->iterations; }
|
||||
|
||||
/**
|
||||
* @brief Outputs the current concentrations of the grid on the console.
|
||||
*
|
||||
*/
|
||||
void printConcentrationsConsole();
|
||||
inline void printConcentrationsConsole() const {
|
||||
std::cout << grid.getConcentrations() << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a CSV file with a name containing the current simulation
|
||||
@ -199,7 +302,52 @@ public:
|
||||
*
|
||||
* @return string Filename with configured simulation parameters.
|
||||
*/
|
||||
std::string createCSVfile();
|
||||
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(grid.getRow());
|
||||
std::string col = std::to_string(grid.getCol());
|
||||
std::string numIterations = std::to_string(iterations);
|
||||
|
||||
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 (csv_output == CSV_OUTPUT_XTREME) {
|
||||
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
|
||||
@ -208,16 +356,138 @@ public:
|
||||
* @param filename Name of the file to which the concentration values are
|
||||
* to be written.
|
||||
*/
|
||||
void printConcentrationsCSV(const std::string &filename);
|
||||
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 << grid.getConcentrations().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();
|
||||
void 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!");
|
||||
}
|
||||
|
||||
std::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 constexpr (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 constexpr (approach == BTCS_APPROACH) { // BTCS case
|
||||
|
||||
if constexpr (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 constexpr (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 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
|
||||
Eigen::MatrixX<T> concentrations;
|
||||
Eigen::MatrixX<T> concentrationsFTCS;
|
||||
Eigen::MatrixX<T> 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) {
|
||||
const std::string &approachString = this->approach_names[approach];
|
||||
const std::string dimString = std::to_string(grid.getDim()) + "D";
|
||||
std::cout << approachString << dimString << ":: run() finished in "
|
||||
<< milliseconds.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double timestep{-1};
|
||||
T timestep{-1};
|
||||
int iterations{-1};
|
||||
int innerIterations{1};
|
||||
int numThreads{omp_get_num_procs()};
|
||||
@ -225,12 +495,10 @@ private:
|
||||
CONSOLE_OUTPUT console_output{CONSOLE_OUTPUT_OFF};
|
||||
TIME_MEASURE time_measure{TIME_MEASURE_OFF};
|
||||
|
||||
Grid &grid;
|
||||
Boundary &bc;
|
||||
APPROACH approach;
|
||||
SOLVER solver{THOMAS_ALGORITHM_SOLVER};
|
||||
Grid<T> &grid;
|
||||
Boundary<T> &bc;
|
||||
|
||||
const std::vector<std::string> approach_names = {"FTCS", "BTCS", "CRNI"};
|
||||
};
|
||||
|
||||
} // namespace tug
|
||||
#endif // SIMULATION_H_
|
||||
|
||||
169
naaice/BTCS_2D_NAAICE.cpp
Normal file
169
naaice/BTCS_2D_NAAICE.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include <Eigen/Eigen>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tug/Simulation.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;
|
||||
Grid64 grid(row, col);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
grid.setDomain(0.005, 0.01);
|
||||
|
||||
const auto init_values_vec = CSVToVector<double>(INPUT_CONC_FILE);
|
||||
Eigen::MatrixXd concentrations = rmVecTocmMatrix(init_values_vec, row, col);
|
||||
grid.setConcentrations(concentrations);
|
||||
|
||||
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
|
||||
Simulation simulation = 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
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)
|
||||
193
naaice/NAAICE_dble_vs_float.cpp
Normal file
193
naaice/NAAICE_dble_vs_float.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include <Eigen/Eigen>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tug/Simulation.hpp>
|
||||
#include <type_traits>
|
||||
#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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Grid<T> grid(ngrid, ngrid);
|
||||
// Grid64 grid(ngrid, ngrid);
|
||||
|
||||
// (optional) set the domain, e.g.:
|
||||
grid.setDomain(0.1, 0.1);
|
||||
|
||||
Eigen::MatrixX<T> initConc64 = Eigen::MatrixX<T>::Constant(ngrid, ngrid, 0);
|
||||
initConc64(50, 50) = 1E-6;
|
||||
grid.setConcentrations(initConc64);
|
||||
|
||||
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
|
||||
Simulation Sim =
|
||||
Simulation<T, app>(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
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
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
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
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
|
||||
|
149
src/Boundary.cpp
149
src/Boundary.cpp
@ -1,149 +0,0 @@
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <tug/Boundary.hpp>
|
||||
|
||||
BoundaryElement::BoundaryElement() {}
|
||||
|
||||
BoundaryElement::BoundaryElement(double _value)
|
||||
: value(_value), type(BC_TYPE_CONSTANT) {}
|
||||
|
||||
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 = std::vector<std::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 = std::vector<std::vector<BoundaryElement>>(4);
|
||||
|
||||
this->boundaries[BC_SIDE_LEFT] =
|
||||
std::vector<BoundaryElement>(grid.getRow(), BoundaryElement());
|
||||
this->boundaries[BC_SIDE_RIGHT] =
|
||||
std::vector<BoundaryElement>(grid.getRow(), BoundaryElement());
|
||||
this->boundaries[BC_SIDE_TOP] =
|
||||
std::vector<BoundaryElement>(grid.getCol(), BoundaryElement());
|
||||
this->boundaries[BC_SIDE_BOTTOM] =
|
||||
std::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.");
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
|
||||
const int n = is_vertical ? grid.getRow() : grid.getCol();
|
||||
|
||||
this->boundaries[side] = std::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.");
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_vertical = side == BC_SIDE_LEFT || side == BC_SIDE_RIGHT;
|
||||
const int n = is_vertical ? grid.getRow() : grid.getCol();
|
||||
|
||||
this->boundaries[side] =
|
||||
std::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 std::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];
|
||||
}
|
||||
|
||||
Eigen::VectorXd Boundary::getBoundarySideValues(BC_SIDE side) {
|
||||
const std::size_t length = boundaries[side].size();
|
||||
Eigen::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 BTCS.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 ${PROJECT_SOURCE_DIR}/include)
|
||||
167
src/Grid.cpp
167
src/Grid.cpp
@ -1,167 +0,0 @@
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <tug/Grid.hpp>
|
||||
|
||||
constexpr double MAT_INIT_VAL = 0;
|
||||
|
||||
Grid::Grid(int length) : col(length), domainCol(length) {
|
||||
if (length <= 3) {
|
||||
throw_invalid_argument(
|
||||
"Given grid length too small. Must be greater than 3.");
|
||||
}
|
||||
|
||||
this->dim = 1;
|
||||
this->deltaCol = double(this->domainCol) / double(this->col); // -> 1
|
||||
|
||||
this->concentrations = Eigen::MatrixXd::Constant(1, col, MAT_INIT_VAL);
|
||||
this->alphaX = Eigen::MatrixXd::Constant(1, col, MAT_INIT_VAL);
|
||||
}
|
||||
|
||||
Grid::Grid(int _row, int _col)
|
||||
: row(_row), col(_col), domainRow(_row), domainCol(_col) {
|
||||
if (row <= 3 || col <= 3) {
|
||||
throw_invalid_argument(
|
||||
"Given grid dimensions too small. Must each be greater than 3.");
|
||||
}
|
||||
|
||||
this->dim = 2;
|
||||
this->deltaRow = double(this->domainRow) / double(this->row); // -> 1
|
||||
this->deltaCol = double(this->domainCol) / double(this->col); // -> 1
|
||||
|
||||
this->concentrations = Eigen::MatrixXd::Constant(row, col, MAT_INIT_VAL);
|
||||
this->alphaX = Eigen::MatrixXd::Constant(row, col, MAT_INIT_VAL);
|
||||
this->alphaY = Eigen::MatrixXd::Constant(row, col, MAT_INIT_VAL);
|
||||
}
|
||||
|
||||
void Grid::setConcentrations(const Eigen::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;
|
||||
}
|
||||
|
||||
void Grid::setAlpha(const Eigen::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(const Eigen::MatrixXd &alphaX,
|
||||
const Eigen::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 Eigen::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 Eigen::MatrixXd &Grid::getAlphaX() {
|
||||
if (dim != 2) {
|
||||
throw_invalid_argument(
|
||||
"Grid is not two dimensional, you should probably use getAlpha()!");
|
||||
}
|
||||
|
||||
return this->alphaX;
|
||||
}
|
||||
|
||||
const Eigen::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,28 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCHEMES_H_
|
||||
#define SCHEMES_H_
|
||||
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
#include <tug/Boundary.hpp>
|
||||
#include <tug/Grid.hpp>
|
||||
|
||||
// entry point; differentiate between 1D and 2D grid
|
||||
extern void FTCS(Grid &grid, Boundary &bc, double ×tep, int &numThreads);
|
||||
|
||||
// entry point for EigenLU solver; differentiate between 1D and 2D grid
|
||||
extern void BTCS_LU(Grid &grid, Boundary &bc, double timestep, int numThreads);
|
||||
|
||||
// entry point for Thomas algorithm solver; differentiate 1D and 2D grid
|
||||
extern void BTCS_Thomas(Grid &grid, Boundary &bc, double timestep,
|
||||
int numThreads);
|
||||
|
||||
#endif // SCHEMES_H_
|
||||
@ -1,326 +0,0 @@
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <tug/Simulation.hpp>
|
||||
|
||||
#include "Schemes.hpp"
|
||||
#include "TugUtils.hpp"
|
||||
|
||||
Simulation::Simulation(Grid &_grid, Boundary &_bc, APPROACH _approach)
|
||||
: grid(_grid), bc(_bc), approach(_approach) {}
|
||||
|
||||
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) {
|
||||
|
||||
const double deltaColSquare = grid.getDeltaCol() * grid.getDeltaCol();
|
||||
// will be 0 if 1D, else ...
|
||||
const double deltaRowSquare = grid.getDim() != 1
|
||||
? grid.getDeltaRow() * grid.getDeltaRow()
|
||||
: deltaColSquare;
|
||||
const double minDeltaSquare =
|
||||
(deltaRowSquare < deltaColSquare) ? deltaRowSquare : deltaColSquare;
|
||||
|
||||
double maxAlpha = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
// determine maximum alpha
|
||||
if (grid.getDim() == 2) {
|
||||
|
||||
const double maxAlphaX = grid.getAlphaX().maxCoeff();
|
||||
const double maxAlphaY = grid.getAlphaY().maxCoeff();
|
||||
maxAlpha = (maxAlphaX > maxAlphaY) ? maxAlphaX : maxAlphaY;
|
||||
|
||||
} else if (grid.getDim() == 1) {
|
||||
maxAlpha = grid.getAlpha().maxCoeff();
|
||||
|
||||
} else {
|
||||
throw_invalid_argument("Critical error: Undefined number of dimensions!");
|
||||
}
|
||||
|
||||
const std::string dim = std::to_string(grid.getDim()) + "D";
|
||||
|
||||
// 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)));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
std::cerr
|
||||
<< "Warning: Solver was set, but FTCS approach initialized. Setting "
|
||||
"the solver "
|
||||
"is thus without effect."
|
||||
<< std::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();
|
||||
std::string outputMessage =
|
||||
"Number of threads exceeds the number of processor cores (" +
|
||||
std::to_string(maxThreadNumber) + ") or is less than 1.";
|
||||
|
||||
throw_invalid_argument(outputMessage);
|
||||
}
|
||||
}
|
||||
|
||||
int Simulation::getIterations() { return this->iterations; }
|
||||
|
||||
void Simulation::printConcentrationsConsole() {
|
||||
std::cout << grid.getConcentrations() << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::string Simulation::createCSVfile() {
|
||||
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(grid.getRow());
|
||||
std::string col = std::to_string(grid.getCol());
|
||||
std::string numIterations = std::to_string(iterations);
|
||||
|
||||
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 (csv_output == CSV_OUTPUT_XTREME) {
|
||||
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;
|
||||
}
|
||||
|
||||
void Simulation::printConcentrationsCSV(const std::string &filename) {
|
||||
std::ofstream file;
|
||||
|
||||
file.open(filename, std::ios_base::app);
|
||||
if (!file) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Eigen::IOFormat do_not_align(Eigen::StreamPrecision, Eigen::DontAlignCols);
|
||||
file << grid.getConcentrations().format(do_not_align) << std::endl;
|
||||
file << std::endl << std::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!");
|
||||
}
|
||||
|
||||
std::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
|
||||
Eigen::MatrixXd concentrations;
|
||||
Eigen::MatrixXd concentrationsFTCS;
|
||||
Eigen::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) {
|
||||
const std::string &approachString = this->approach_names[approach];
|
||||
const std::string dimString = std::to_string(grid.getDim()) + "D";
|
||||
std::cout << approachString << dimString << ":: run() finished in "
|
||||
<< milliseconds.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
@ -6,12 +6,13 @@
|
||||
#include <typeinfo>
|
||||
|
||||
using namespace std;
|
||||
using namespace tug;
|
||||
|
||||
TEST_CASE("BoundaryElement") {
|
||||
|
||||
SUBCASE("Closed case") {
|
||||
BoundaryElement boundaryElementClosed = BoundaryElement();
|
||||
CHECK_NOTHROW(BoundaryElement());
|
||||
BoundaryElement boundaryElementClosed = BoundaryElement<double>();
|
||||
CHECK_NOTHROW(BoundaryElement<double>());
|
||||
CHECK_EQ(boundaryElementClosed.getType(), BC_TYPE_CLOSED);
|
||||
CHECK_EQ(boundaryElementClosed.getValue(), -1);
|
||||
CHECK_THROWS(boundaryElementClosed.setValue(0.2));
|
||||
@ -28,11 +29,11 @@ TEST_CASE("BoundaryElement") {
|
||||
}
|
||||
|
||||
TEST_CASE("Boundary Class") {
|
||||
Grid grid1D = Grid(10);
|
||||
Grid grid2D = Grid(10, 12);
|
||||
Grid grid1D = Grid64(10);
|
||||
Grid grid2D = Grid64(10, 12);
|
||||
Boundary boundary1D = Boundary(grid1D);
|
||||
Boundary boundary2D = Boundary(grid2D);
|
||||
vector<BoundaryElement> boundary1DVector(1, BoundaryElement(1.0));
|
||||
vector<BoundaryElement<double>> boundary1DVector(1, BoundaryElement(1.0));
|
||||
|
||||
SUBCASE("Boundaries 1D case") {
|
||||
CHECK_NOTHROW(Boundary boundary(grid1D));
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include <TugUtils.hpp>
|
||||
#include <tug/Core/TugUtils.hpp>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <limits>
|
||||
|
||||
@ -4,26 +4,26 @@
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
|
||||
using namespace tug;
|
||||
|
||||
TEST_CASE("1D Grid, too small length") {
|
||||
int l = 2;
|
||||
CHECK_THROWS(Grid(l));
|
||||
CHECK_THROWS(Grid64(l));
|
||||
}
|
||||
|
||||
TEST_CASE("2D Grid, too small side") {
|
||||
TEST_CASE("2D Grid64, too small side") {
|
||||
int r = 2;
|
||||
int c = 4;
|
||||
CHECK_THROWS(Grid(r, c));
|
||||
CHECK_THROWS(Grid64(r, c));
|
||||
|
||||
r = 4;
|
||||
c = 2;
|
||||
CHECK_THROWS(Grid(r, c));
|
||||
CHECK_THROWS(Grid64(r, c));
|
||||
}
|
||||
|
||||
TEST_CASE("1D Grid") {
|
||||
TEST_CASE("1D Grid64") {
|
||||
int l = 12;
|
||||
Grid grid(l);
|
||||
Grid64 grid(l);
|
||||
|
||||
SUBCASE("correct construction") {
|
||||
CHECK_EQ(grid.getDim(), 1);
|
||||
@ -89,9 +89,9 @@ TEST_CASE("1D Grid") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("2D Grid quadratic") {
|
||||
TEST_CASE("2D Grid64 quadratic") {
|
||||
int rc = 12;
|
||||
Grid grid(rc, rc);
|
||||
Grid64 grid(rc, rc);
|
||||
|
||||
SUBCASE("correct construction") {
|
||||
CHECK_EQ(grid.getDim(), 2);
|
||||
@ -170,10 +170,10 @@ TEST_CASE("2D Grid quadratic") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("2D Grid non-quadratic") {
|
||||
TEST_CASE("2D Grid64 non-quadratic") {
|
||||
int r = 12;
|
||||
int c = 15;
|
||||
Grid grid(r, c);
|
||||
Grid64 grid(r, c);
|
||||
|
||||
SUBCASE("correct construction") {
|
||||
CHECK_EQ(grid.getDim(), 2);
|
||||
|
||||
@ -10,16 +10,16 @@
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
using namespace tug;
|
||||
|
||||
static Grid setupSimulation(APPROACH approach, double timestep,
|
||||
int iterations) {
|
||||
Grid64 setupSimulation(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 grid = Grid64(row, col);
|
||||
grid.setDomain(domain_row, domain_col);
|
||||
|
||||
MatrixXd concentrations = MatrixXd::Constant(row, col, 0);
|
||||
@ -44,26 +44,29 @@ static Grid setupSimulation(APPROACH approach, double timestep,
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
constexpr double timestep = 0.001;
|
||||
constexpr double iterations = 7000;
|
||||
|
||||
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);
|
||||
|
||||
Grid grid = setupSimulation(timestep, iterations); // Boundary
|
||||
Boundary bc = Boundary(grid);
|
||||
|
||||
// Simulation
|
||||
|
||||
Simulation sim = Simulation<double, tug::FTCS_APPROACH>(grid, bc);
|
||||
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
|
||||
sim.setTimestep(timestep);
|
||||
sim.setIterations(iterations);
|
||||
sim.run();
|
||||
|
||||
cout << endl;
|
||||
CHECK(checkSimilarity(reference, grid.getConcentrations(), 0.1) == true);
|
||||
}
|
||||
@ -73,24 +76,34 @@ TEST_CASE("equality to reference matrix with BTCS") {
|
||||
string test_path = testSimulationCSVDir;
|
||||
MatrixXd reference = CSV2Eigen(test_path);
|
||||
cout << "BTCS Test: " << endl;
|
||||
Grid grid = setupSimulation(BTCS_APPROACH, 1, 7);
|
||||
|
||||
Grid grid = setupSimulation(timestep, iterations); // Boundary
|
||||
Boundary bc = Boundary(grid);
|
||||
|
||||
// Simulation
|
||||
Simulation sim = Simulation<double, tug::FTCS_APPROACH>(grid, bc);
|
||||
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
|
||||
sim.setTimestep(timestep);
|
||||
sim.setIterations(iterations);
|
||||
sim.run();
|
||||
|
||||
cout << endl;
|
||||
CHECK(checkSimilarityV2(reference, grid.getConcentrations(), 0.01) == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Initialize environment") {
|
||||
int rc = 12;
|
||||
Grid grid(rc, rc);
|
||||
Grid64 grid(rc, rc);
|
||||
Boundary boundary(grid);
|
||||
|
||||
CHECK_NOTHROW(Simulation sim(grid, boundary, BTCS_APPROACH));
|
||||
CHECK_NOTHROW(Simulation sim(grid, boundary));
|
||||
}
|
||||
|
||||
TEST_CASE("Simulation environment") {
|
||||
int rc = 12;
|
||||
Grid grid(rc, rc);
|
||||
Grid64 grid(rc, rc);
|
||||
Boundary boundary(grid);
|
||||
Simulation sim(grid, boundary, FTCS_APPROACH);
|
||||
Simulation<double, tug::FTCS_APPROACH> sim(grid, boundary);
|
||||
|
||||
SUBCASE("default paremeters") { CHECK_EQ(sim.getIterations(), -1); }
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user