improved commentary, refactored TugUtils into .cpp, and added CRNI example

This commit is contained in:
philippun 2023-09-08 15:30:27 +02:00
parent 8fcc8812e7
commit d2e3ef23de
12 changed files with 171 additions and 72 deletions

View File

@ -2,6 +2,7 @@ add_executable(FTCS_1D_proto_example FTCS_1D_proto_example.cpp)
add_executable(FTCS_2D_proto_example FTCS_2D_proto_example.cpp)
add_executable(BTCS_1D_proto_example BTCS_1D_proto_example.cpp)
add_executable(BTCS_2D_proto_example BTCS_2D_proto_example.cpp)
add_executable(CRNI_2D_proto_example CRNI_2D_proto_example.cpp)
add_executable(reference-FTCS_2D_closed reference-FTCS_2D_closed.cpp)
add_executable(profiling_openmp profiling_openmp.cpp)
@ -9,6 +10,7 @@ target_link_libraries(FTCS_1D_proto_example tug)
target_link_libraries(FTCS_2D_proto_example tug)
target_link_libraries(BTCS_1D_proto_example tug)
target_link_libraries(BTCS_2D_proto_example tug)
target_link_libraries(CRNI_2D_proto_example tug)
target_link_libraries(reference-FTCS_2D_closed tug)
target_link_libraries(profiling_openmp tug)

View File

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

View File

@ -44,7 +44,8 @@ class BoundaryElement {
/**
* @brief Construct a new Boundary Element object for the closed case.
* The boundary type is here automatically set to the type
* BC_TYPE_CLOSED, where the value takes NaN.
* BC_TYPE_CLOSED, where the value takes -1 and does not hold any
* physical meaning.
*/
BoundaryElement();
@ -107,7 +108,8 @@ class Boundary {
* @brief Creates a boundary object based on the passed grid object and
* initializes the boundaries as closed.
*
* @param grid Grid object on the basis of which the simulation is to take place.
* @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);
@ -158,7 +160,7 @@ class Boundary {
* @param side Boundary side from which the boundary conditions are to be returned.
* @return vector<BoundaryElement> Contains the boundary conditions as BoundaryElement objects.
*/
vector<BoundaryElement> getBoundarySide(BC_SIDE side);
const vector<BoundaryElement> getBoundarySide(BC_SIDE side);
/**
* @brief Get thes Boundary Side Values as a vector. Value is -1 in case some specific

View File

@ -10,13 +10,14 @@
using namespace Eigen;
// TODO document default values and perhaps adjust them
class Grid {
public:
/**
* @brief Constructs a new 1D-Grid object of a given length, which holds a matrix
* with concentrations and a respective matrix of alpha coefficients.
* The domain length is per default the same as the length. The concentrations
* are all 20 by default and the alpha coefficients are 1.
*
* @param length Length of the 1D-Grid. Must be greater than 3.
*/
@ -25,7 +26,10 @@ class Grid {
/**
* @brief Constructs a new 2D-Grid object of given dimensions, which holds a matrix
* with concentrations and the respective matrices of alpha coefficient for
* each direction.
* each direction. The domain in x- and y-direction is per default equal to
* the col length and row length, respectively.
* The concentrations are all 20 by default across the entire grid and the
* alpha coefficients 1 in both directions.
*
* @param row Length of the 2D-Grid in y-direction. Must be greater than 3.
* @param col Length of the 2D-Grid in x-direction. Must be greater than 3.
@ -36,8 +40,8 @@ class Grid {
* @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,
* respectively.
* have correct dimensions as defined in row and col. (Or length,
* in 1D case).
*/
void setConcentrations(MatrixXd concentrations);
@ -47,7 +51,7 @@ class Grid {
* @return MatrixXd An Eigen3 matrix holding the concentrations and having the
* same dimensions as the grid.
*/
MatrixXd getConcentrations();
const MatrixXd getConcentrations();
/**
* @brief Set the alpha coefficients of a 1D-Grid. Grid must be one dimensional.
@ -72,7 +76,7 @@ class Grid {
*
* @return MatrixXd A matrix with 1 row holding the alpha coefficients.
*/
MatrixXd getAlpha();
const MatrixXd getAlpha();
/**
* @brief Gets the matrix of alpha coefficients in x-direction of a 2D-Grid. Grid must be
@ -80,7 +84,7 @@ class Grid {
*
* @return MatrixXd A matrix holding the alpha coefficients in x-direction.
*/
MatrixXd getAlphaX();
const MatrixXd getAlphaX();
/**
* @brief Gets the matrix of alpha coefficients in y-direction of a 2D-Grid. Grid must be
@ -88,7 +92,7 @@ class Grid {
*
* @return MatrixXd A matrix holding the alpha coefficients in y-direction.
*/
MatrixXd getAlphaY();
const MatrixXd getAlphaY();
/**
* @brief Gets the dimensions of the grid.

View File

@ -72,6 +72,7 @@ class Simulation {
* must be set. For the BTCS approach, the Thomas algorithm is used as
* the default linear equation solver as this is faster for tridiagonal
* matrices. CSV output, console output and time measure are off by default.
* Also, the number of cores is set to the maximum number of cores by default.
*
* @param grid Valid grid object
* @param bc Valid boundary condition object
@ -106,11 +107,12 @@ class Simulation {
*/
void setOutputConsole(CONSOLE_OUTPUT console_output);
// TODO document method
/**
* @brief Set the Time Measure object. Off by default.
* @brief Set the Time Measure option. Off by default.
*
* @param time_measure
* @param time_measure The following options are allowed:
* - TIME_MEASURE_OFF: Time of simulation is not printed to console
* - TIME_MEASURE_ON: Time of simulation run is printed to console
*/
void setTimeMeasure(TIME_MEASURE time_measure);
@ -151,7 +153,7 @@ class Simulation {
*
* @param num_threads Number of desired threads. Must have a value between
* 1 and the maximum available number of processors. The maximum number of
* processors is set as the default case.
* processors is set as the default case during Simulation construction.
*/
void setNumberThreads(int num_threads);
@ -168,14 +170,13 @@ class Simulation {
*/
void printConcentrationsConsole();
// TODO move create CSVfile to TugUtils
/**
* @brief Creates a CSV file with a name containing the current simulation
* parameters. If the data name already exists, an additional counter is
* appended to the name. The name of the file is built up as follows:
* <approach> + <number rows> + <number columns> + <number of iterations>+<counter>.csv
*
* @return string Filename with given simulation parameter.
* @return string Filename with configured simulation parameters.
*/
string createCSVfile();

View File

@ -1,4 +1,4 @@
#include "TugUtils.hpp"
#include "TugUtils.cpp"
#include <iostream>
#include <omp.h>
#include <tug/Boundary.hpp>
@ -9,7 +9,7 @@ using namespace std;
BoundaryElement::BoundaryElement() {
this->type = BC_TYPE_CLOSED;
this->value = NAN;
this->value = -1; // without meaning in closed case
}
BoundaryElement::BoundaryElement(double value) {
@ -42,10 +42,8 @@ double BoundaryElement::getValue() {
}
Boundary::Boundary(Grid grid) : grid(grid) {
//probably to DEBUG assignment grid
if (grid.getDim() == 1) {
this->boundaries = vector<vector<BoundaryElement>>(2);
this->boundaries = vector<vector<BoundaryElement>>(2); // in 1D only left and right boundary
this->boundaries[BC_SIDE_LEFT].push_back(BoundaryElement());
this->boundaries[BC_SIDE_RIGHT].push_back(BoundaryElement());
@ -112,7 +110,7 @@ void Boundary::setBoundaryElementConstant(BC_SIDE side, int index, double value)
this->boundaries[side][index].setValue(value);
}
vector<BoundaryElement> Boundary::getBoundarySide(BC_SIDE side) {
const vector<BoundaryElement> Boundary::getBoundarySide(BC_SIDE side) {
if(grid.getDim() == 1){
if((side == BC_SIDE_BOTTOM) || (side == BC_SIDE_TOP)){
throw_invalid_argument(

View File

@ -5,7 +5,7 @@
*
*/
#include "TugUtils.hpp"
#include "TugUtils.cpp"
#include <cstddef>
#include <tug/Boundary.hpp>
#include <iostream>

View File

@ -1,4 +1,4 @@
#include "TugUtils.hpp"
#include "TugUtils.cpp"
#include <tug/Grid.hpp>
#include <iostream>
@ -43,7 +43,7 @@ void Grid::setConcentrations(MatrixXd concentrations) {
this->concentrations = concentrations;
}
MatrixXd Grid::getConcentrations() {
const MatrixXd Grid::getConcentrations() {
return this->concentrations;
}
@ -73,7 +73,7 @@ void Grid::setAlpha(MatrixXd alphaX, MatrixXd alphaY) {
this->alphaY = alphaY;
}
MatrixXd Grid::getAlpha() {
const MatrixXd Grid::getAlpha() {
if (dim != 1) {
throw_invalid_argument("Grid is not one dimensional, you should probably use either getAlphaX() or getAlphaY()!");
}
@ -81,7 +81,7 @@ MatrixXd Grid::getAlpha() {
return this->alphaX;
}
MatrixXd Grid::getAlphaX() {
const MatrixXd Grid::getAlphaX() {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use getAlpha()!");
}
@ -89,7 +89,7 @@ MatrixXd Grid::getAlphaX() {
return this->alphaX;
}
MatrixXd Grid::getAlphaY() {
const MatrixXd Grid::getAlphaY() {
if (dim != 2) {
throw_invalid_argument("Grid is not two dimensional, you should probably use getAlpha()!");
}

View File

@ -20,7 +20,7 @@ Simulation::Simulation(Grid &grid, Boundary &bc, APPROACH approach) : grid(grid)
this->approach = approach;
this->solver = THOMAS_ALGORITHM_SOLVER;
this->timestep = -1; // error per default
this->timestep = -1; // error per default; needs to be set
this->iterations = -1;
this->innerIterations = 1;
this->numThreads = omp_get_num_procs();
@ -59,7 +59,7 @@ void Simulation::setTimestep(double timestep) {
throw_invalid_argument("Timestep has to be greater than zero.");
}
if (approach == FTCS_APPROACH) {
if (approach == FTCS_APPROACH || approach == CRANK_NICOLSON_APPROACH) {
double deltaRowSquare;
double deltaColSquare = grid.getDeltaCol() * grid.getDeltaCol();
@ -91,8 +91,9 @@ void Simulation::setTimestep(double timestep) {
// 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)));
cout << "FTCS_" << dim << " :: CFL condition MDL: " << cfl << endl;
cout << "FTCS_" << dim << " :: required dt=" << timestep << endl;
string approachPrefix = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
cout << approachPrefix << "_" << dim << " :: CFL condition: " << cfl << endl;
cout << approachPrefix << "_" << dim << " :: required dt=" << timestep << endl;
if (timestep > cfl) {
@ -103,13 +104,13 @@ void Simulation::setTimestep(double timestep) {
"conditions. Time duration was approximately preserved by "
"adjusting internal number of iterations."
<< endl;
cout << "FTCS_" << dim << " :: Required " << this->innerIterations
cout << approachPrefix << "_" << dim << " :: Required " << this->innerIterations
<< " inner iterations with dt=" << this->timestep << endl;
} else {
this->timestep = timestep;
cout << "FTCS_" << dim << " :: No inner iterations required, dt=" << timestep << endl;
cout << approachPrefix << "_" << dim << " :: No inner iterations required, dt=" << timestep << endl;
}
@ -146,8 +147,10 @@ void Simulation::setNumberThreads(int numThreads){
}
else{
int maxThreadNumber = omp_get_num_procs();
string outputMessage = "Number of threads exceeds the number of processor cores ("
+ to_string(maxThreadNumber) + ") or is less than 1.";
throw_invalid_argument("Number of threads exceeds the number of processor cores or is less than 1.");
throw_invalid_argument(outputMessage);
}
}
@ -165,7 +168,8 @@ string Simulation::createCSVfile() {
int appendIdent = 0;
string appendIdentString;
string approachString = (approach == 0) ? "FTCS" : "BTCS";
// string approachString = (approach == 0) ? "FTCS" : "BTCS";
string approachString = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
string row = to_string(grid.getRow());
string col = to_string(grid.getCol());
string numIterations = to_string(iterations);
@ -281,7 +285,29 @@ void Simulation::run() {
} else if (approach == CRANK_NICOLSON_APPROACH) { // Crank-Nicolson case
// TODO
double beta = 0.5;
// TODO this implementation is very inefficient!
// a separate implementation that sets up a specific tridiagonal matrix for Crank-Nicolson would be better
MatrixXd concentrations;
MatrixXd concentrationsFTCS;
MatrixXd concentrationsResult;
for (int i = 0; i < iterations * innerIterations; i++) {
if (console_output == CONSOLE_OUTPUT_VERBOSE && i > 0) {
printConcentrationsConsole();
}
if (csv_output >= CSV_OUTPUT_VERBOSE) {
printConcentrationsCSV(filename);
}
concentrations = grid.getConcentrations();
FTCS(this->grid, this->bc, this->timestep, this->numThreads);
concentrationsFTCS = grid.getConcentrations();
grid.setConcentrations(concentrations);
BTCS_Thomas(this->grid, this->bc, this->timestep, this->numThreads);
concentrationsResult = beta * concentrationsFTCS + (1-beta) * grid.getConcentrations();
grid.setConcentrations(concentrationsResult);
}
}
@ -295,7 +321,7 @@ void Simulation::run() {
printConcentrationsCSV(filename);
}
if (this->time_measure > TIME_MEASURE_OFF) {
string approachString = (approach == 0) ? "FTCS" : "BTCS";
string approachString = (approach == 0) ? "FTCS" : ((approach == 1) ? "BTCS" : "CRNI");
string dimString = (grid.getDim() == 1) ? "-1D" : "-2D";
cout << approachString << dimString << ":: run() finished in " << milliseconds.count() << "ms" << endl;
}

38
src/TugUtils.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <chrono>
#include <stdexcept>
#include <string>
#include <fstream>
using namespace std;
// used for throwing an invalid argument message
#define throw_invalid_argument(msg) \
throw std::invalid_argument(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + \
std::string(msg))
// used for throwing an out of range message
#define throw_out_of_range(msg) \
throw std::out_of_range(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + std::string(msg))
// get current time
#define time_marker() std::chrono::high_resolution_clock::now()
// calculates difference between two time points
#define diff_time(start, end) \
({ \
std::chrono::duration<double> duration = \
std::chrono::duration_cast<std::chrono::duration<double>>(end - \
start); \
duration.count(); \
})
// calculates arithmetic or harmonic mean of alpha between two cells
static double calcAlphaIntercell(const double &alpha1, const double &alpha2, bool useHarmonic = true) {
if (useHarmonic) {
return double(2) / ((double(1)/alpha1) + (double(1)/alpha2));
} else {
return 0.5 * (alpha1 + alpha2);
}
}

View File

@ -1,35 +1,35 @@
#ifndef BTCSUTILS_H_
#define BTCSUTILS_H_
// #ifndef BTCSUTILS_H_
// #define BTCSUTILS_H_
#include <chrono>
#include <stdexcept>
#include <string>
// #include <chrono>
// #include <stdexcept>
// #include <string>
#define throw_invalid_argument(msg) \
throw std::invalid_argument(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + \
std::string(msg))
// #define throw_invalid_argument(msg) \
// throw std::invalid_argument(std::string(__FILE__) + ":" + \
// std::to_string(__LINE__) + ":" + \
// std::string(msg))
#define throw_out_of_range(msg) \
throw std::out_of_range(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ":" + std::string(msg))
// #define throw_out_of_range(msg) \
// throw std::out_of_range(std::string(__FILE__) + ":" + \
// std::to_string(__LINE__) + ":" + std::string(msg))
#define time_marker() std::chrono::high_resolution_clock::now()
// #define time_marker() std::chrono::high_resolution_clock::now()
#define diff_time(start, end) \
({ \
std::chrono::duration<double> duration = \
std::chrono::duration_cast<std::chrono::duration<double>>(end - \
start); \
duration.count(); \
})
#endif // BTCSUTILS_H_
// #define diff_time(start, end) \
// ({ \
// std::chrono::duration<double> duration = \
// std::chrono::duration_cast<std::chrono::duration<double>>(end - \
// start); \
// duration.count(); \
// })
// #endif // BTCSUTILS_H_
// calculates arithmetic or harmonic mean of alpha between two cells
static double calcAlphaIntercell(double &alpha1, double &alpha2, bool useHarmonic = true) {
if (useHarmonic) {
return double(2) / ((double(1)/alpha1) + (double(1)/alpha2));
} else {
return 0.5 * (alpha1 + alpha2);
}
}
// // calculates arithmetic or harmonic mean of alpha between two cells
// static double calcAlphaIntercell(double &alpha1, double &alpha2, bool useHarmonic = true) {
// if (useHarmonic) {
// return double(2) / ((double(1)/alpha1) + (double(1)/alpha2));
// } else {
// return 0.5 * (alpha1 + alpha2);
// }
// }

View File

@ -47,7 +47,7 @@ static Grid setupSimulation(APPROACH approach, double timestep, int iterations)
// Simulation
Simulation sim = Simulation(grid, bc, approach);
sim.setOutputConsole(CONSOLE_OUTPUT_ON);
// sim.setOutputConsole(CONSOLE_OUTPUT_ON);
sim.setTimestep(timestep);
sim.setIterations(iterations);
sim.run();
@ -61,7 +61,9 @@ TEST_CASE("equality to reference matrix with FTCS") {
// set string from the header file
string test_path = testSimulationCSVDir;
MatrixXd reference = CSV2Eigen(test_path);
cout << "FTCS Test: " << endl;
Grid grid = setupSimulation(FTCS_APPROACH, 0.001, 7000);
cout << endl;
CHECK(checkSimilarity(reference, grid.getConcentrations(), 0.1) == true);
}
@ -69,7 +71,9 @@ TEST_CASE("equality to reference matrix with BTCS") {
// set string from the header file
string test_path = testSimulationCSVDir;
MatrixXd reference = CSV2Eigen(test_path);
cout << "BTCS Test: " << endl;
Grid grid = setupSimulation(BTCS_APPROACH, 1, 7);
cout << endl;
CHECK(checkSimilarityV2(reference, grid.getConcentrations(), 0.01) == true);
}
@ -87,17 +91,17 @@ TEST_CASE("Simulation environment"){
Boundary boundary(grid);
Simulation sim(grid, boundary, FTCS_APPROACH);
SUBCASE("default paremeters"){
SUBCASE("default paremeters") {
CHECK_EQ(sim.getIterations(), -1);
}
SUBCASE("set iterations"){
SUBCASE("set iterations") {
CHECK_NOTHROW(sim.setIterations(2000));
CHECK_EQ(sim.getIterations(), 2000);
CHECK_THROWS(sim.setIterations(-300));
}
SUBCASE("set timestep"){
SUBCASE("set timestep") {
CHECK_NOTHROW(sim.setTimestep(0.1));
CHECK_EQ(sim.getTimestep(), 0.1);
CHECK_THROWS(sim.setTimestep(-0.3));