Commit a5a5b102 authored by Jannick Wolters's avatar Jannick Wolters
Browse files

added basic classes for settings, mesh and io along with simple functionalities

parent 048e360f
......@@ -10,11 +10,12 @@ TBD
## Build
### Required dependencies
- Compiler with C++17 support (GCC >= v6, clang >= v3.8)
- Compiler with C++20 support
- cmake >= v3.5
- LAPACK
- OpenMP
- MPI
- VTK
- git
- ninja or make
......
cmake_minimum_required( VERSION 3.5 )
project( RTSN LANGUAGES CXX VERSION 0.0.1 )
set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD 20 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -march=native -pg -no-pie" )
......@@ -24,8 +25,10 @@ include_directories( ${MPI_INCLUDE_PATH} )
find_package( LAPACK REQUIRED )
include_directories( ${LAPACK_INCLUDE_DIR} )
include_directories( ${CMAKE_SOURCE_DIR}/../ext/cpptoml/include )
include_directories( ${CMAKE_SOURCE_DIR}/../ext/spdlog/include )
find_package( VTK REQUIRED COMPONENTS vtkIOGeometry vtkFiltersCore )
include_directories( ${CMAKE_SOURCE_DIR}/ext/cpptoml/include )
include_directories( ${CMAKE_SOURCE_DIR}/ext/spdlog/include )
execute_process(
COMMAND git rev-parse HEAD
......@@ -35,7 +38,7 @@ execute_process(
)
add_definitions( '-DGIT_HASH="${GIT_HASH}"' )
set( CORE_LIBRARIES ${LAPACK_LIBRARIES} ${MPI_LIBRARIES} -lstdc++fs )
set( CORE_LIBRARIES ${LAPACK_LIBRARIES} ${MPI_LIBRARIES} ${VTK_LIBRARIES} -lstdc++fs )
target_link_libraries( ${CMAKE_PROJECT_NAME} ${CORE_LIBRARIES} )
......
#ifndef IO_H
#define IO_H
#include <chrono>
#include <filesystem>
#include <iostream>
#include <string>
#include "cpptoml.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/spdlog.h"
#include <mpi.h>
#include <omp.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCellDataToPointData.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkPointDataToCellData.h>
#include <vtkQuad.h>
#include <vtkSmartPointer.h>
#include <vtkTriangle.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGridReader.h>
#include <vtkUnstructuredGridWriter.h>
#include "mesh.h"
#include "settings.h"
using vtkPointsSP = vtkSmartPointer<vtkPoints>;
using vtkUnstructuredGridSP = vtkSmartPointer<vtkUnstructuredGrid>;
using vtkTriangleSP = vtkSmartPointer<vtkTriangle>;
using vtkCellArraySP = vtkSmartPointer<vtkCellArray>;
using vtkDoubleArraySP = vtkSmartPointer<vtkDoubleArray>;
using vtkUnstructuredGridWriterSP = vtkSmartPointer<vtkUnstructuredGridWriter>;
using vtkUnstructuredGridReaderSP = vtkSmartPointer<vtkUnstructuredGridReader>;
using vtkCellDataToPointDataSP = vtkSmartPointer<vtkCellDataToPointData>;
using vtkPointDataToCellDataSP = vtkSmartPointer<vtkPointDataToCellData>;
void ExportVTK( const std::string fileName,
const std::vector<std::vector<std::vector<double>>>& results,
const std::vector<std::string> fieldNames,
const Mesh* mesh );
void InitLogger( std::string logDir, spdlog::level::level_enum terminalLogLvl, spdlog::level::level_enum fileLogLvl );
std::string ParseArguments( int argc, char* argv[] );
Settings* ReadInputFile( std::string inputFile );
void PrintLogHeader( std::string inputFile );
#endif // IO_H
#ifndef MESH_H
#define MESH_H
#include <vector>
class Mesh
{
protected:
unsigned _dim;
unsigned _numCells;
unsigned _numNodes;
unsigned _numNodesPerCell;
std::vector<std::vector<double>> _nodes;
std::vector<std::vector<double>> _cells;
public:
unsigned GetDim() const;
unsigned GetNumCells() const;
unsigned GetNumNodes() const;
unsigned GetNumNodesPerCell() const;
const std::vector<std::vector<double>>& GetNodes() const;
const std::vector<std::vector<double>>& GetCells() const;
};
#endif // MESH_H
#ifndef SETTINGS_H
#define SETTINGS_H
#include <filesystem>
#include <string>
class Settings
{
private:
// IO
std::filesystem::path _inputFile;
std::filesystem::path _inputDir;
std::filesystem::path _outputFile;
std::filesystem::path _outputDir;
std::filesystem::path _logDir;
std::filesystem::path _meshFile;
// Mesh
unsigned _meshDimension;
// Solver
unsigned _quadOrder;
double _CFL;
// MPI
int _comm_rank;
int _comm_size;
public:
Settings();
std::string GetInputFile() const;
std::string GetInputDir() const;
std::string GetOutputFile() const;
std::string GetOutputDir() const;
std::string GetLogDir() const;
friend Settings* ReadInputFile( std::string fileName );
};
#endif // SETTINGS_H
#include "io.h"
Settings* ReadInputFile( std::string inputFile ) {
bool validConfig = true;
Settings* settings = new Settings;
MPI_Comm_rank( MPI_COMM_WORLD, &settings->_comm_rank );
MPI_Comm_size( MPI_COMM_WORLD, &settings->_comm_size );
try {
auto file = cpptoml::parse_file( inputFile );
settings->_inputFile = std::filesystem::path( inputFile );
auto cwd = std::filesystem::current_path();
std::string tmp = std::filesystem::path( inputFile ).parent_path().string();
if( !tmp.ends_with( "/" ) ) tmp.append( "/" );
settings->_inputDir = tmp;
// section IO
auto io = file->get_table( "io" );
auto meshFile = io->get_as<std::string>( "meshFile" );
if( meshFile ) {
settings->_meshFile = std::filesystem::path( *meshFile );
}
else {
spdlog::error( "[inputfile] [io] 'meshFile' not set!" );
validConfig = false;
}
auto outputDir = io->get_as<std::string>( "outputDir" );
if( outputDir ) {
std::string tmp = *outputDir;
if( !tmp.ends_with( "/" ) ) tmp.append( "/" );
settings->_outputDir = std::filesystem::path( tmp );
}
else {
spdlog::error( "[inputfile] [io] 'outputDir' not set!" );
validConfig = false;
}
auto outputFile = io->get_as<std::string>( "outputFile" );
if( outputFile ) {
settings->_outputFile = std::filesystem::path( settings->_inputDir.string() + settings->_outputDir.string() + *outputFile );
}
else {
spdlog::error( "[inputfile] [io] 'outputFile' not set!" );
validConfig = false;
}
auto logDir = io->get_as<std::string>( "logDir" );
if( logDir ) {
std::string tmp = *logDir;
if( !tmp.ends_with( "/" ) ) tmp.append( "/" );
settings->_logDir = std::filesystem::path( tmp );
}
else {
spdlog::error( "[inputfile] [io] 'logDir' not set!" );
validConfig = false;
}
// section solver
auto solver = file->get_table( "solver" );
auto CFL = solver->get_as<double>( "CFL" );
if( CFL ) {
settings->_CFL = *CFL;
}
else {
spdlog::error( "[inputfile] [solver] 'CFL' not set!" );
validConfig = false;
}
auto quadType = solver->get_as<std::string>( "quadType" );
if( quadType ) {
// TODO
}
else {
spdlog::error( "[inputfile] [solver] 'quadType' not set!" );
validConfig = false;
}
auto quadOrder = solver->get_as<unsigned>( "quadOrder" );
if( quadOrder ) {
settings->_quadOrder = *quadOrder;
}
else {
spdlog::error( "[inputfile] [solver] 'quadOrder' not set!" );
validConfig = false;
}
} catch( const cpptoml::parse_exception& e ) {
spdlog::error( "Failed to parse {0}: {1}", inputFile.c_str(), e.what() );
exit( EXIT_FAILURE );
}
if( !validConfig ) {
exit( EXIT_FAILURE );
}
return settings;
}
void ExportVTK( const std::string fileName,
const std::vector<std::vector<std::vector<double>>>& results,
const std::vector<std::string> fieldNames,
const Mesh* mesh ) {
unsigned numCells = mesh->GetNumCells();
unsigned numNodes = mesh->GetNumNodes();
auto nodes = mesh->GetNodes();
auto cells = mesh->GetCells();
unsigned numNodesPerCell = mesh->GetNumNodesPerCell();
auto writer = vtkUnstructuredGridWriterSP::New();
std::string fileNameWithExt = fileName;
if( !fileNameWithExt.ends_with( ".vtk" ) ) {
fileNameWithExt.append( ".vtk" );
}
writer->SetFileName( fileNameWithExt.c_str() );
auto grid = vtkUnstructuredGridSP::New();
auto pts = vtkPointsSP::New();
pts->SetDataTypeToDouble();
pts->SetNumberOfPoints( static_cast<int>( numNodes ) );
unsigned nodeID = 0;
for( const auto& node : nodes ) {
pts->SetPoint( nodeID++, node[0], node[1], node[2] );
}
vtkCellArraySP cellArray = vtkCellArraySP::New();
for( unsigned i = 0; i < numCells; ++i ) {
if( numNodesPerCell == 3 ) {
auto tri = vtkTriangleSP::New();
for( unsigned j = 0; j < numNodesPerCell; ++j ) {
tri->GetPointIds()->SetId( j, cells[i][j] );
}
cellArray->InsertNextCell( tri );
}
if( numNodesPerCell == 4 ) {
auto quad = vtkQuad::New();
for( unsigned j = 0; j < numNodesPerCell; ++j ) {
quad->GetPointIds()->SetId( j, cells[i][j] );
}
cellArray->InsertNextCell( quad );
}
}
if( numNodesPerCell == 3 ) {
grid->SetCells( VTK_TRIANGLE, cellArray );
}
else if( numNodesPerCell == 4 ) {
grid->SetCells( VTK_QUAD, cellArray );
}
for( unsigned i = 0; i < results.size(); i++ ) {
auto cellData = vtkDoubleArraySP::New();
cellData->SetName( fieldNames[i].c_str() );
switch( results[i].size() ) {
case 1:
for( unsigned j = 0; j < numCells; j++ ) {
cellData->InsertNextValue( results[i][0][j] );
}
break;
case 2:
cellData = vtkDoubleArraySP::New();
cellData->SetName( "E(ρU)" );
cellData->SetNumberOfComponents( 3 );
cellData->SetComponentName( 0, "x" );
cellData->SetComponentName( 1, "y" );
cellData->SetComponentName( 2, "z" );
cellData->SetNumberOfTuples( numCells );
for( unsigned j = 0; j < numCells; j++ ) {
cellData->SetTuple3( i, results[i][0][j], results[i][1][j], 0.0 );
}
break;
case 3:
cellData = vtkDoubleArraySP::New();
cellData->SetName( "E(ρU)" );
cellData->SetNumberOfComponents( 3 );
cellData->SetComponentName( 0, "x" );
cellData->SetComponentName( 1, "y" );
cellData->SetComponentName( 2, "z" );
cellData->SetNumberOfTuples( numCells );
for( unsigned j = 0; j < numCells; j++ ) {
cellData->SetTuple3( i, results[i][0][j], results[i][1][j], results[i][2][j] );
}
break;
default: std::cout << "[ERROR][IO::ExportVTK] Invalid dimension" << std::endl;
}
grid->GetCellData()->AddArray( cellData );
}
grid->SetPoints( pts );
grid->Squeeze();
auto converter = vtkCellDataToPointDataSP::New();
converter->AddInputDataObject( grid );
converter->PassCellDataOn();
converter->Update();
auto conv_grid = converter->GetOutput();
writer->SetInputData( conv_grid );
writer->Write();
}
void InitLogger( std::string logDir, spdlog::level::level_enum terminalLogLvl, spdlog::level::level_enum fileLogLvl ) {
// create log dir if not existent
if( !std::filesystem::exists( logDir ) ) {
std::filesystem::create_directory( logDir );
}
// create sinks if level is not off
std::vector<spdlog::sink_ptr> sinks;
if( terminalLogLvl != spdlog::level::off ) {
// create spdlog terminal sink
auto terminalSink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
terminalSink->set_level( terminalLogLvl );
terminalSink->set_pattern( "%v" );
sinks.push_back( terminalSink );
}
if( fileLogLvl != spdlog::level::off ) {
// define filename on root
int pe;
MPI_Comm_rank( MPI_COMM_WORLD, &pe );
char cfilename[1024];
if( pe == 0 ) {
// get date and time
time_t now = time( nullptr );
struct tm tstruct;
char buf[80];
tstruct = *localtime( &now );
strftime( buf, sizeof( buf ), "%Y-%m-%d_%X", &tstruct );
// set filename to date and time
std::string filename = buf;
// in case of existing files append '_#'
int ctr = 0;
if( std::filesystem::exists( logDir + filename ) ) {
filename += "_" + std::to_string( ++ctr );
}
while( std::filesystem::exists( logDir + filename ) ) {
filename.pop_back();
filename += std::to_string( ++ctr );
}
strncpy( cfilename, filename.c_str(), sizeof( cfilename ) );
cfilename[sizeof( cfilename ) - 1] = 0;
}
MPI_Bcast( &cfilename, sizeof( cfilename ), MPI_CHAR, 0, MPI_COMM_WORLD );
MPI_Barrier( MPI_COMM_WORLD );
// create spdlog file sink
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>( logDir + cfilename );
fileSink->set_level( fileLogLvl );
fileSink->set_pattern( "%Y-%m-%d %H:%M:%S.%f | %v" );
sinks.push_back( fileSink );
}
// register all sinks
auto event_logger = std::make_shared<spdlog::logger>( "event", begin( sinks ), end( sinks ) );
spdlog::register_logger( event_logger );
spdlog::flush_every( std::chrono::seconds( 5 ) );
}
std::string ParseArguments( int argc, char* argv[] ) {
std::string inputFile;
std::string usage_help = "\n"
"Usage: " +
std::string( argv[0] ) + " inputfile\n";
if( argc < 2 ) {
std::cout << usage_help;
exit( EXIT_FAILURE );
}
for( int i = 1; i < argc; i++ ) {
std::string arg = argv[i];
if( arg == "-h" ) {
std::cout << usage_help;
exit( EXIT_SUCCESS );
}
else {
inputFile = std::string( argv[i] );
std::ifstream f( inputFile );
if( !f.is_open() ) {
std::cerr << "[ERROR] Unable to open inputfile '" << inputFile << "'!" << std::endl;
exit( EXIT_FAILURE );
}
}
}
return inputFile;
}
void PrintLogHeader( std::string inputFile ) {
int nprocs;
MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
auto log = spdlog::get( "event" );
log->info( "RTSN" );
log->info( "================================================================" );
log->info( "Git commit :\t{0}", GIT_HASH );
log->info( "Config file:\t{0}", inputFile );
log->info( "MPI Threads:\t{0}", nprocs );
log->info( "OMP Threads:\t{0}", omp_get_max_threads() );
log->info( "================================================================" );
// print file content while omitting comments
std::ifstream ifs( inputFile );
if( ifs.is_open() ) {
std::string line;
while( !ifs.eof() ) {
std::getline( ifs, line );
if( line[0] != '#' ) log->info( " {0}", line );
}
}
log->info( "================================================================" );
log->info( "" );
}
#include <iostream>
#include <mpi.h>
#include "io.h"
int main( int argc, char** argv ) {
std::cout << "Hello world!" << std::endl;
MPI_Init( &argc, &argv );
std::string inputFile = ParseArguments( argc, argv );
Settings* settings = ReadInputFile( inputFile );
InitLogger( settings->GetLogDir(), spdlog::level::info, spdlog::level::info );
PrintLogHeader( settings->GetInputFile() );
return EXIT_SUCCESS;
}
#include "mesh.h"
unsigned Mesh::GetDim() const { return _dim; }
unsigned Mesh::GetNumCells() const { return _numCells; }
unsigned Mesh::GetNumNodes() const { return _numNodes; }
unsigned Mesh::GetNumNodesPerCell() const { return _numNodesPerCell; }
const std::vector<std::vector<double>>& Mesh::GetNodes() const { return _nodes; }
const std::vector<std::vector<double>>& Mesh::GetCells() const { return _cells; }
#include "settings.h"
Settings::Settings() {}
std::string Settings::GetInputFile() const { return _inputFile.string(); }
std::string Settings::GetInputDir() const { return _inputDir.string(); }
std::string Settings::GetOutputFile() const { return _outputFile.string(); }
std::string Settings::GetOutputDir() const { return _outputDir.string(); }
std::string Settings::GetLogDir() const { return _logDir.string(); }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment