config.cpp 41.6 KB
Newer Older
1
/*!
2
 * \file Config.cpp
steffen.schotthoefer's avatar
steffen.schotthoefer committed
3
 * \brief Class for different Options in rtsn
4
5
6
7
8
 * \author S. Schotthoefer
 *
 * Disclaimer: This class structure was copied and modifed with open source permission from SU2 v7.0.3 https://su2code.github.io/
 */

9
10
11
#include "common/config.h"
#include "common/globalconstants.h"
#include "common/optionstructure.h"
12
13
#include "toolboxes/errormessages.h"
#include "toolboxes/textprocessingtoolbox.h"
14
15
16
17
18

// externals
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/spdlog.h"
19
20
#include <filesystem>
#include <fstream>
21
#include <mpi.h>
22
23
24

using namespace std;

25
Config::Config( string case_filename ) {
26

27
    /*--- Set the case name to the base config file name without extension ---*/
28

29
    auto cwd     = std::filesystem::current_path();
30
    auto relPath = std::filesystem::relative( std::filesystem::path( case_filename ), cwd );
31
32
    _fileName    = relPath.filename().string();
    _inputDir    = cwd.string() + "/" + relPath.parent_path().string();
33

34
    _baseConfig = true;
35

36
37
    /*--- Store MPI rank and size ---*/
    // TODO with MPI implementation
38

39
    /*--- Initialize pointers to Null---*/
40

41
    SetPointersNull();
42

43
    /*--- Reading config options  ---*/
44

45
    SetConfigOptions();
46

47
    /*--- Parsing the config file  ---*/
48

49
    SetConfigParsing( case_filename );
50

51
    /*--- Set the default values for all of the options that weren't set ---*/
52

53
    SetDefault();
54

55
    /*--- Get the Mesh Value--- */
56

57
58
    // val_nDim = GetnDim(Mesh_FileName, Mesh_FileFormat);
    // TODO
59

60
    /*--- Configuration file postprocessing ---*/
61

62
    SetPostprocessing();
63

64
    /*--- Configuration file boundaries/markers setting ---*/
65

66
    // SetBoundary(); IDK how boundaries are implemented, but i think this should be treated here
67

68
    /*--- Configuration file output ---*/
69

70
    // if ((rank == MASTER_NODE))
71
72
73
    SetOutput();
}

74
75
Config::~Config( void ) {
    // Delete all introduced arrays!
76
77
78
79
80
81

    // delete _option map values proberly
    for( auto const& x : _optionMap ) {
        delete x.second;
        _optionMap.erase( x.first );
    }
82
83
84
85
86
}

// ---- Add Options ----

// Simple Options
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
void Config::AddBoolOption( const string name, bool& option_field, bool default_value ) {
    // Check if the key is already in the map. If this fails, it is coder error
    // and not user error, so throw.
    assert( _optionMap.find( name ) == _optionMap.end() );

    // Add this option to the list of all the options
    _allOptions.insert( pair<string, bool>( name, true ) );

    // Create the parser for a bool option with a reference to the option_field and the desired
    // default value. This will take the string in the config file, convert it to a bool, and
    // place that bool in the memory location specified by the reference.
    OptionBase* val = new OptionBool( name, option_field, default_value );

    // Create an association between the option name ("CFL") and the parser generated above.
    // During configuration, the parsing script will get the option name, and use this map
    // to find how to parse that option.
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
104
105
}

106
107
108
109
110
void Config::AddDoubleOption( const string name, double& option_field, double default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionDouble( name, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
111
112
}

113
114
115
116
117
void Config::AddIntegerOption( const string name, int& option_field, int default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionInt( name, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
118
119
}

120
121
122
123
124
void Config::AddLongOption( const string name, long& option_field, long default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionLong( name, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
125
126
}

127
128
129
130
131
void Config::AddStringOption( const string name, string& option_field, string default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionString( name, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
132
133
}

134
135
136
137
138
void Config::AddUnsignedLongOption( const string name, unsigned long& option_field, unsigned long default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionULong( name, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
139
140
}

141
142
143
void Config::AddUnsignedShortOption( const string name, unsigned short& option_field, unsigned short default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
144
    OptionBase* val = new OptionUShort( name, option_field, default_value );
145
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
146
147
148
149
}

// enum types work differently than all of the others because there are a small number of valid
// string entries for the type. One must also provide a list of all the valid strings of that type.
150
151
152
153
154
155
template <class Tenum> void Config::AddEnumOption( const string name, Tenum& option_field, const map<string, Tenum>& enum_map, Tenum default_value ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionEnum<Tenum>( name, enum_map, option_field, default_value );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
    return;
156
157
158
}

// List Options
159
160
161
162
163
void Config::AddStringListOption( const string name, unsigned short& num_marker, std::vector<std::string>& option_field ) {
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionStringList( name, num_marker, option_field );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
164
165
}

166
167
168
169
170
171
172
173
174
175
176
177
template <class Tenum>
void Config::AddEnumListOption( const std::string name,
                                unsigned short& input_size,
                                std::vector<Tenum>& option_field,
                                const map<std::string, Tenum>& enum_map ) {
    input_size = 0;
    assert( _optionMap.find( name ) == _optionMap.end() );
    _allOptions.insert( pair<string, bool>( name, true ) );
    OptionBase* val = new OptionEnumList<Tenum>( name, enum_map, option_field, input_size );
    _optionMap.insert( pair<string, OptionBase*>( name, val ) );
}

178
179
// ---- Getter Functions ----

180
BOUNDARY_TYPE Config::GetBoundaryType( std::string name ) const {
181
182
183
184
185
186
187
188
    for( unsigned i = 0; i < _boundaries.size(); ++i ) {
        if( name == _boundaries[i].first ) return _boundaries[i].second;
    }
    return BOUNDARY_TYPE::INVALID;
}

// ---- Setter Functions ----

189
190
void Config::SetDefault() {
    /*--- Set the default values for all of the options that weren't set ---*/
191

192
193
194
    for( map<string, bool>::iterator iter = _allOptions.begin(); iter != _allOptions.end(); ++iter ) {
        if( _optionMap[iter->first]->GetValue().size() == 0 ) _optionMap[iter->first]->SetDefault();
    }
195
196
}

197
void Config::SetConfigOptions() {
198

199
200
    /* BEGIN_CONFIG_OPTIONS */

steffen.schotthoefer's avatar
steffen.schotthoefer committed
201
    /*! @par CONFIG_CATEGORY: Problem Definition @ingroup Config */
202
203
204
    /*--- Options related to problem definition and partitioning ---*/

    // File Structure related options
steffen.schotthoefer's avatar
steffen.schotthoefer committed
205
    /*! @brief OUTPUT_DIR \n DESCRIPTION: Relative Directory of output files \n DEFAULT "/out" @ingroup Config.*/
206
    AddStringOption( "OUTPUT_DIR", _outputDir, string( "/out" ) );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
207
    /*! @brief OUTPUT_FILE \n DESCRIPTION: Name of output file \n DEFAULT "output" @ingroup Config.*/
208
    AddStringOption( "OUTPUT_FILE", _outputFile, string( "output" ) );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
209
    /*! @brief LOG_DIR \n DESCRIPTION: Relative Directory of log files \n DEFAULT "/out" @ingroup Config.*/
210
211
212
    AddStringOption( "LOG_DIR", _logDir, string( "/out/logs" ) );
    /*! @brief LOG_DIR \n DESCRIPTION: Name of log files \n DEFAULT "/out" @ingroup Config.*/
    AddStringOption( "LOG_FILE", _logFileName, string( "use_date" ) );
213
    /*! @brief MESH_FILE \n DESCRIPTION: Name of mesh file \n DEFAULT "" \ingroup Config.*/
214
    AddStringOption( "MESH_FILE", _meshFile, string( "mesh.su2" ) );
215
    /*! @brief MESH_FILE \n DESCRIPTION: Name of mesh file \n DEFAULT "" \ingroup Config.*/
216
    AddStringOption( "CT_FILE", _ctFile, string( "../tests/input/phantom.png" ) );
217
218

    // Quadrature relatated options
steffen.schotthoefer's avatar
steffen.schotthoefer committed
219
    /*! @brief QUAD_TYPE \n DESCRIPTION: Type of Quadrature rule \n Options: see @link QUAD_NAME \endlink \n DEFAULT: QUAD_MonteCarlo \ingroup
220
     * Config*/
221
222
    AddEnumOption( "QUAD_TYPE", _quadName, Quadrature_Map, QUAD_MonteCarlo );
    /*!\brief QUAD_ORDER \n DESCRIPTION: Order of Quadrature rule \n DEFAULT 2 \ingroup Config.*/
223
    AddUnsignedShortOption( "QUAD_ORDER", _quadOrder, 1 );
224
225

    // Solver related options
226
    /*! @brief MAX_MOMENT_ORDER \n: DESCRIPTON: Specifies the maximal order of Moments for PN and SN Solver */
227
    AddUnsignedShortOption( "MAX_MOMENT_SOLVER", _maxMomentDegree, 1 );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
228
    /*! @brief CFL \n DESCRIPTION: CFL number \n DEFAULT 1.0 @ingroup Config.*/
229
    AddDoubleOption( "CFL_NUMBER", _CFL, 1.0 );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
230
    /*! @brief TIME_FINAL \n DESCRIPTION: Final time for simulation \n DEFAULT 1.0 @ingroup Config.*/
231
    AddDoubleOption( "TIME_FINAL", _tEnd, 1.0 );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
232
    /*! @brief Problem \n DESCRIPTION: Type of problem setting \n DEFAULT PROBLEM_ElectronRT @ingroup Config.*/
233
    AddEnumOption( "PROBLEM", _problemName, Problem_Map, PROBLEM_ElectronRT );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
234
    /*! @brief Solver \n DESCRIPTION: Solver used for problem \n DEFAULT SN_SOLVER @ingroup Config. */
235
    AddEnumOption( "SOLVER", _solverName, Solver_Map, SN_SOLVER );
236
    /*! @brief RECONS_ORDER \n DESCRIPTION: Reconstruction order for solver (spatial flux) \n DEFAULT 1 \ingroup Config.*/
vavrines's avatar
vavrines committed
237
    AddUnsignedShortOption( "RECONS_ORDER", _reconsOrder, 1 );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
238
239
240
    /*! @brief CleanFluxMatrices \n DESCRIPTION:  If true, very low entries (10^-10 or smaller) of the flux matrices will be set to zero,
     * to improve floating point accuracy \n DEFAULT false \ingroup Config */
    AddBoolOption( "CLEAN_FLUX_MATRICES", _cleanFluxMat, false );
Jonas Kusch's avatar
Jonas Kusch committed
241
242
    /*! @brief ContinuousSlowingDown \n DESCRIPTION: If true, the program uses the continuous slowing down approximation to treat energy dependent
     * problems. \n DEFAULT false \ingroup Config */
243
244
245
246
247
248
    // AddBoolOption( "CONTINUOUS_SLOWING_DOWN", _csd, false );

    // Problem Relateed Options
    /*! @brief MaterialDir \n DESCRIPTION: Relative Path to the data directory (used in the ICRU database class), starting from the directory of the
     * cfg file . \n DEFAULT "../data/material/" \ingroup Config */
    AddStringOption( "DATA_DIR", _dataDir, string( "../data/" ) );
jannick.wolters's avatar
jannick.wolters committed
249
250
251
252
253
254
    /*! @brief HydogenFile \n DESCRIPTION: If the continuous slowing down approximation is used, this referes to the cross section file for hydrogen.
     * . \n DEFAULT "h.dat" \ingroup Config */
    AddStringOption( "HYDROGEN_FILE", _hydrogenFile, string( "ENDL_H.txt" ) );
    /*! @brief OxygenFile \n DESCRIPTION: If the continuous slowing down approximation is used, this referes to the cross section file for oxygen.
     * . \n DEFAULT "o.dat" \ingroup Config */
    AddStringOption( "OXYGEN_FILE", _oxygenFile, string( "ENDL_O.txt" ) );
255
256
    /*! @brief StoppingPowerFile \n DESCRIPTION: Only temporary added. \ingroup Config */
    AddStringOption( "STOPPING_POWER_FILE", _stoppingPowerFile, string( "stopping_power.txt" ) );
257
258
    /*! @brief SN_ALL_GAUSS_PTS \n DESCRIPTION: If true, the SN Solver uses all Gauss Quadrature Points for 2d. \n DEFAULT false \ingroup Config */
    AddBoolOption( "SN_ALL_GAUSS_PTS", _allGaussPts, false );
259

260
261
262
263
    // Linesource Testcase Options
    /*! @brief SCATTER_COEFF \n DESCRIPTION: Sets the scattering coefficient for the Linesource test case. \n DEFAULT 0.0 \ingroup Config */
    AddDoubleOption( "SCATTER_COEFF", _sigmaS, 0.0 );

264
    // Entropy related options
265
    /*! @brief Entropy Functional \n DESCRIPTION: Entropy functional used for the MN_Solver \n DEFAULT QUADRTATIC @ingroup Config. */
266
    AddEnumOption( "ENTROPY_FUNCTIONAL", _entropyName, Entropy_Map, QUADRATIC );
267
268
    /*! @brief Optimizer Name \n DESCRIPTION:  Optimizer used to determine the minimal Entropy reconstruction \n DEFAULT NEWTON \ingroup Config */
    AddEnumOption( "ENTROPY_OPTIMIZER", _entropyOptimizerName, Optimizer_Map, NEWTON );
269
270

    // Newton optimizer related options
271
    /*! @brief Newton Optimizer Epsilon \n DESCRIPTION:  Convergencce Epsilon for Newton Optimizer \n DEFAULT 1e-3 \ingroup Config */
272
    AddDoubleOption( "NEWTON_EPSILON", _optimizerEpsilon, 0.001 );
273
    /*! @brief Max Iter Newton Optmizers \n DESCRIPTION: Max number of newton iterations \n DEFAULT 10 \ingroup Config */
274
    AddUnsignedLongOption( "NEWTON_ITER", _newtonIter, 100 );
275
276
277
278
    /*! @brief Step Size Newton Optmizers \n DESCRIPTION: Step size for Newton optimizer \n DEFAULT 10 \ingroup Config */
    AddDoubleOption( "NEWTON_STEP_SIZE", _newtonStepSize, 0.1 );
    /*! @brief Max Iter for line search in Newton Optmizers \n DESCRIPTION: Max number of line search iter for newton optimizer \n DEFAULT 10 \ingroup
     * Config */
279
    AddUnsignedLongOption( "NEWTON_LINE_SEARCH_ITER", _newtonLineSearchIter, 100 );
280
281
282
    /*! @brief Newton Fast mode \n DESCRIPTION:  If true, we skip the Newton optimizer for Quadratic entropy and set alpha = u \n DEFAULT false
     * \ingroup Config */
    AddBoolOption( "NEWTON_FAST_MODE", _newtonFastMode, false );
283

284
285
286
    // Neural Entropy Closure options
    AddUnsignedShortOption( "NEURAL_MODEL", _neuralModel, 4 );

287
288
289
290
    // Mesh related options
    // Boundary Markers
    /*!\brief BC_DIRICHLET\n DESCRIPTION: Dirichlet wall boundary marker(s) \ingroup Config*/
    AddStringListOption( "BC_DIRICHLET", _nMarkerDirichlet, _MarkerDirichlet );
291
    AddStringListOption( "BC_NEUMANN", _nMarkerNeumann, _MarkerNeumann );
292
    AddUnsignedShortOption( "SPATIAL_DIM", _dim, 3 );
jannick.wolters's avatar
jannick.wolters committed
293

294
    /*! @brief Scattering kernel \n DESCRIPTION: Describes used scattering kernel \n DEFAULT KERNEL_Isotropic \ingroup Config */
jannick.wolters's avatar
jannick.wolters committed
295
    AddEnumOption( "KERNEL", _kernelName, Kernel_Map, KERNEL_Isotropic );
296

297
298
299
300
    /*! @brief Spherical Basis \n DESCRIPTION: Describes the chosen set of basis functions for on the unit sphere (e.g. for Moment methods) \n DEFAULT
     * SPHERICAL_HARMONICS \ingroup Config */
    AddEnumOption( "SPHERICAL_BASIS", _sphericalBasisName, SphericalBasis_Map, SPHERICAL_HARMONICS );

301
    // Output related options
302
    /*! @brief Volume output \n DESCRIPTION: Describes output groups to write to vtk \ingroup Config */
303
    AddEnumListOption( "VOLUME_OUTPUT", _nVolumeOutput, _volumeOutput, VolOutput_Map );
304
305
306
307
308
309
310
311
312
313
    /*! @brief Volume output Frequency \n DESCRIPTION: Describes output write frequency \n DEFAULT 0 ,i.e. only last value \ingroup Config */
    AddUnsignedShortOption( "VOLUME_OUTPUT_FREQUENCY", _volumeOutputFrequency, 0 );
    /*! @brief Screen output \n DESCRIPTION: Describes screen output fields \ingroup Config */
    AddEnumListOption( "SCREEN_OUTPUT", _nScreenOutput, _screenOutput, ScalarOutput_Map );
    /*! @brief Screen output Frequency \n DESCRIPTION: Describes screen output write frequency \n DEFAULT 1 \ingroup Config */
    AddUnsignedShortOption( "SCREEN_OUTPUT_FREQUENCY", _screenOutputFrequency, 1 );
    /*! @brief History output \n DESCRIPTION: Describes history output fields \ingroup Config */
    AddEnumListOption( "HISTORY_OUTPUT", _nHistoryOutput, _historyOutput, ScalarOutput_Map );
    /*! @brief History output Frequency \n DESCRIPTION: Describes history output write frequency \n DEFAULT 1 \ingroup Config */
    AddUnsignedShortOption( "HISTORY_OUTPUT_FREQUENCY", _historyOutputFrequency, 1 );
314
315
316
317
318
319
320
321
322

    // Data generator related options
    /*! @brief Size of training data set \n DESCRIPTION: Size of training data set  \n DEFAULT 1 \ingroup Config */
    AddUnsignedLongOption( "TRAINING_SET_SIZE", _tainingSetSize, 1 );
    /*! @brief Size of training data set \n DESCRIPTION: Size of training data set  \n DEFAULT 10 \ingroup Config */
    AddUnsignedLongOption( "MAX_VALUE_FIRST_MOMENT", _maxValFirstMoment, 10 );
    /*! @brief Data generator mode \n DESCRIPTION: Check, if data generator mode is active. If yes, no solver is called, but instead the data
     *         generator is executed \n DEFAULT false \ingroup Config */
    AddBoolOption( "DATA_GENERATOR_MODE", _dataGeneratorMode, false );
323
324
325
326
327
328
    /*! @brief Distance to 0 of the sampled moments to the boundary of the realizable set  \n DESCRIPTION: Distance to the boundary of the
     * realizable set  \n DEFAULT 0.1 \ingroup Config */
    AddDoubleOption( "REALIZABLE_SET_EPSILON_U0", _RealizableSetEpsilonU0, 0.1 );
    /*! @brief norm(u_1)/u_0 is enforced to be smaller than _RealizableSetEpsilonU1 \n DESCRIPTION: Distance to the boundary of the realizable set  \n
     * DEFAULT 0.1 \ingroup Config */
    AddDoubleOption( "REALIZABLE_SET_EPSILON_U1", _RealizableSetEpsilonU1, 0.9 );
329
330
}

331
void Config::SetConfigParsing( string case_filename ) {
332
333
334
335
336
    string text_line, option_name;
    ifstream case_file;
    vector<string> option_value;

    /*--- Read the configuration file ---*/
337

338
    case_file.open( case_filename, ios::in );
339

340
341
342
    if( case_file.fail() ) {
        ErrorMessages::Error( "The configuration file (.cfg) is missing!!", CURRENT_FUNCTION );
    }
343

344
    string errorString;
345

346
347
    int err_count     = 0;     // How many errors have we found in the config file
    int max_err_count = 30;    // Maximum number of errors to print before stopping
348

349
    map<string, bool> included_options;
350

351
    /*--- Parse the configuration file and set the options ---*/
352

353
    while( getline( case_file, text_line ) ) {
354

355
356
        if( err_count >= max_err_count ) {
            errorString.append( "too many errors. Stopping parse" );
357

358
359
360
            cout << errorString << endl;
            throw( 1 );
        }
361

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
        if( TokenizeString( text_line, option_name, option_value ) ) {

            /*--- See if it's a python option ---*/

            if( _optionMap.find( option_name ) == _optionMap.end() ) {
                string newString;
                newString.append( option_name );
                newString.append( ": invalid option name" );
                newString.append( ". Check current RTSN options in config_template.cfg." );
                newString.append( "\n" );
                errorString.append( newString );
                err_count++;
                continue;
            }

            /*--- Option exists, check if the option has already been in the config file ---*/
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
            if( included_options.find( option_name ) != included_options.end() ) {
                string newString;
                newString.append( option_name );
                newString.append( ": option appears twice" );
                newString.append( "\n" );
                errorString.append( newString );
                err_count++;
                continue;
            }

            /*--- New found option. Add it to the map, and delete from all options ---*/

            included_options.insert( pair<string, bool>( option_name, true ) );
            _allOptions.erase( option_name );

            /*--- Set the value and check error ---*/

            string out = _optionMap[option_name]->SetValue( option_value );
            if( out.compare( "" ) != 0 ) {
                errorString.append( out );
                errorString.append( "\n" );
                err_count++;
            }
        }
403
404
    }

405
    /*--- See if there were any errors parsing the config file ---*/
406

407
    if( errorString.size() != 0 ) {
Steffen Schotthöfer's avatar
Steffen Schotthöfer committed
408
        ErrorMessages::ParsingError( errorString, CURRENT_FUNCTION );
409
    }
410

411
    case_file.close();
412
413
}

414
415
void Config::SetPointersNull( void ) {
    // All pointer valued options should be set to NULL here
416
417
}

418
void Config::SetPostprocessing() {
steffen.schotthoefer's avatar
steffen.schotthoefer committed
419

420
421
422
423
    // append '/' to all dirs to allow for simple path addition
    if( _logDir[_logDir.size() - 1] != '/' ) _logDir.append( "/" );
    if( _outputDir[_outputDir.size() - 1] != '/' ) _outputDir.append( "/" );
    if( _inputDir[_inputDir.size() - 1] != '/' ) _inputDir.append( "/" );
424

425
    // setup relative paths
426
427
428
429
430
431
432
433
434
    _logDir            = std::filesystem::path( _inputDir ).append( _logDir ).lexically_normal();
    _outputDir         = std::filesystem::path( _inputDir ).append( _outputDir ).lexically_normal();
    _meshFile          = std::filesystem::path( _inputDir ).append( _meshFile ).lexically_normal();
    _outputFile        = std::filesystem::path( _outputDir ).append( _outputFile ).lexically_normal();
    _ctFile            = std::filesystem::path( _inputDir ).append( _ctFile ).lexically_normal();
    _hydrogenFile      = std::filesystem::path( _inputDir ).append( _hydrogenFile ).lexically_normal();
    _oxygenFile        = std::filesystem::path( _inputDir ).append( _oxygenFile ).lexically_normal();
    _stoppingPowerFile = std::filesystem::path( _inputDir ).append( _stoppingPowerFile ).lexically_normal();
    _dataDir           = std::filesystem::path( _inputDir ).append( _dataDir ).lexically_normal();
435

436
437
    // create directories if they dont exist
    if( !std::filesystem::exists( _outputDir ) ) std::filesystem::create_directory( _outputDir );
438

Steffen Schotthöfer's avatar
Steffen Schotthöfer committed
439
    // init logger
440
    InitLogger();
441

442
443
444
445
    // Regroup Boundary Conditions to  std::vector<std::pair<std::string, BOUNDARY_TYPE>> _boundaries;
    for( int i = 0; i < _nMarkerDirichlet; i++ ) {
        _boundaries.push_back( std::pair<std::string, BOUNDARY_TYPE>( _MarkerDirichlet[i], DIRICHLET ) );
    }
446
447
448
    for( int i = 0; i < _nMarkerNeumann; i++ ) {
        _boundaries.push_back( std::pair<std::string, BOUNDARY_TYPE>( _MarkerNeumann[i], NEUMANN ) );
    }
449

450
451
452
453
454
455
456
457
458
459
460
461
    // Set option ISCSD
    switch( _solverName ) {
        case CSD_SN_NOTRAFO_SOLVER:                     // Fallthrough
        case CSD_SN_FOKKERPLANCK_SOLVER:                // Fallthrough
        case CSD_SN_FOKKERPLANCK_TRAFO_SOLVER:          // Fallthrough
        case CSD_SN_FOKKERPLANCK_TRAFO_SOLVER_2D:       // Fallthrough
        case CSD_SN_FOKKERPLANCK_TRAFO_SH_SOLVER_2D:    // Fallthrough
        case CSD_SN_SOLVER:                             // Fallthrough
            _csd = true;
            break;
        default: _csd = false;
    }
jannick.wolters's avatar
jannick.wolters committed
462

463
464
    // Check, if mesh file exists
    if( _solverName == CSD_SN_FOKKERPLANCK_TRAFO_SOLVER ) {    // Check if this is neccessary
jannick.wolters's avatar
jannick.wolters committed
465
466
467
468
469
470
471
472
473
        if( !std::filesystem::exists( this->GetHydrogenFile() ) ) {
            ErrorMessages::Error( "Path to mesh file <" + this->GetHydrogenFile() + "> does not exist. Please check your config file.",
                                  CURRENT_FUNCTION );
        }
        if( !std::filesystem::exists( this->GetOxygenFile() ) ) {
            ErrorMessages::Error( "Path to mesh file <" + this->GetOxygenFile() + "> does not exist. Please check your config file.",
                                  CURRENT_FUNCTION );
        }
    }
474

475
476
477
478
479
480
481
482
483
484
    // --- Solver setup ---
    if( GetSolverName() == PN_SOLVER && GetSphericalBasisName() != SPHERICAL_HARMONICS ) {
        ErrorMessages::Error( "PN Solver only works with spherical harmonics basis.\nThis should be the default setting for option SPHERICAL_BASIS.",
                              CURRENT_FUNCTION );
    }

    if( GetSolverName() == MN_SOLVER && GetSphericalBasisName() == SPHERICAL_MONOMIALS && GetMaxMomentDegree() > 1 ) {
        ErrorMessages::Error( "MN Solver only with monomial basis only stable up to degree 1. This is a TODO.", CURRENT_FUNCTION );
    }

485
486
    // --- Output Postprocessing ---

487
488
489
490
491
492
493
494
495
496
497
498
499
    // Volume Output Postprocessing
    {
        // Check for doublicates in VOLUME OUTPUT
        std::map<VOLUME_OUTPUT, int> dublicate_map;

        for( unsigned short idx_volOutput = 0; idx_volOutput < _nVolumeOutput; idx_volOutput++ ) {
            std::map<VOLUME_OUTPUT, int>::iterator it = dublicate_map.find( _volumeOutput[idx_volOutput] );
            if( it == dublicate_map.end() ) {
                dublicate_map.insert( std::pair<VOLUME_OUTPUT, int>( _volumeOutput[idx_volOutput], 0 ) );
            }
            else {
                it->second++;
            }
500
        }
501
502
503
504
505
        for( auto& e : dublicate_map ) {
            if( e.second > 0 ) {
                ErrorMessages::Error( "Each output group for option VOLUME_OUTPUT can only be set once.\nPlease check your .cfg file.",
                                      CURRENT_FUNCTION );
            }
506
        }
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551

        // Check, if the choice of volume output is compatible to the solver
        std::vector<VOLUME_OUTPUT> supportedGroups;

        for( unsigned short idx_volOutput = 0; idx_volOutput < _nVolumeOutput; idx_volOutput++ ) {
            switch( _solverName ) {
                case SN_SOLVER:
                    supportedGroups = { MINIMAL, ANALYTIC };
                    if( supportedGroups.end() == std::find( supportedGroups.begin(), supportedGroups.end(), _volumeOutput[idx_volOutput] ) ) {
                        ErrorMessages::Error( "SN_SOLVER only supports volume output MINIMAL and ANALYTIC.\nPlease check your .cfg file.",
                                              CURRENT_FUNCTION );
                    }
                    if( _volumeOutput[idx_volOutput] == ANALYTIC && _problemName != PROBLEM_LineSource ) {
                        ErrorMessages::Error( "Analytical solution (VOLUME_OUTPUT=ANALYTIC) is only available for the PROBLEM=LINESOURCE.\nPlease "
                                              "check your .cfg file.",
                                              CURRENT_FUNCTION );
                    }
                    break;
                case MN_SOLVER:
                    supportedGroups = { MINIMAL, MOMENTS, DUAL_MOMENTS, ANALYTIC };
                    if( supportedGroups.end() == std::find( supportedGroups.begin(), supportedGroups.end(), _volumeOutput[idx_volOutput] ) ) {

                        ErrorMessages::Error(
                            "MN_SOLVER only supports volume output ANALYTIC, MINIMAL, MOMENTS and DUAL_MOMENTS.\nPlease check your .cfg file.",
                            CURRENT_FUNCTION );
                    }
                    if( _volumeOutput[idx_volOutput] == ANALYTIC && _problemName != PROBLEM_LineSource ) {
                        ErrorMessages::Error( "Analytical solution (VOLUME_OUTPUT=ANALYTIC) is only available for the PROBLEM=LINESOURCE.\nPlease "
                                              "check your .cfg file.",
                                              CURRENT_FUNCTION );
                    }
                    break;
                case PN_SOLVER:
                    supportedGroups = { MINIMAL, MOMENTS, ANALYTIC };
                    if( supportedGroups.end() == std::find( supportedGroups.begin(), supportedGroups.end(), _volumeOutput[idx_volOutput] ) ) {

                        ErrorMessages::Error( "PN_SOLVER only supports volume output ANALYTIC, MINIMAL and MOMENTS.\nPlease check your .cfg file.",
                                              CURRENT_FUNCTION );
                    }
                    if( _volumeOutput[idx_volOutput] == ANALYTIC && _problemName != PROBLEM_LineSource ) {
                        ErrorMessages::Error( "Analytical solution (VOLUME_OUTPUT=ANALYTIC) is only available for the PROBLEM=LINESOURCE.\nPlease "
                                              "check your .cfg file.",
                                              CURRENT_FUNCTION );
                    }
                    break;
552
553
554
555
556
557
558
                case CSD_SN_NOTRAFO_SOLVER:                     // Fallthrough
                case CSD_SN_FOKKERPLANCK_SOLVER:                // Fallthrough
                case CSD_SN_FOKKERPLANCK_TRAFO_SOLVER:          // Fallthrough
                case CSD_SN_FOKKERPLANCK_TRAFO_SOLVER_2D:       // Fallthrough
                case CSD_SN_FOKKERPLANCK_TRAFO_SH_SOLVER_2D:    // Fallthrough
                case CSD_SN_SOLVER:                             // Fallthrough
                    supportedGroups = { MINIMAL, MEDICAL };
559
560
                    if( supportedGroups.end() == std::find( supportedGroups.begin(), supportedGroups.end(), _volumeOutput[idx_volOutput] ) ) {

561
                        ErrorMessages::Error( "CSD_SN_SOLVER types only supports volume output MEDICAL and MINIMAL.\nPlease check your .cfg file.",
562
563
564
565
                                              CURRENT_FUNCTION );
                    }
                    break;
            }
566
567
        }

568
569
570
571
        // Set default volume output
        if( _nVolumeOutput == 0 ) {    // If no specific output is chosen,  use "MINIMAL"
            _nVolumeOutput = 1;
            _volumeOutput.push_back( MINIMAL );
572
573
574
        }
    }

575
576
    // Screen Output Postprocessing
    {
577
578
        // Check for doublicates in SCALAR OUTPUT
        std::map<SCALAR_OUTPUT, int> dublicate_map;
579
580

        for( unsigned short idx_screenOutput = 0; idx_screenOutput < _nScreenOutput; idx_screenOutput++ ) {
581
            std::map<SCALAR_OUTPUT, int>::iterator it = dublicate_map.find( _screenOutput[idx_screenOutput] );
582
            if( it == dublicate_map.end() ) {
583
                dublicate_map.insert( std::pair<SCALAR_OUTPUT, int>( _screenOutput[idx_screenOutput], 0 ) );
584
585
586
587
588
589
590
591
592
593
594
            }
            else {
                it->second++;
            }
        }
        for( auto& e : dublicate_map ) {
            if( e.second > 0 ) {
                ErrorMessages::Error( "Each output field for option SCREEN_OUTPUT can only be set once.\nPlease check your .cfg file.",
                                      CURRENT_FUNCTION );
            }
        }
595

596
        // Set ITER always to index 0 . Assume only one instance of iter is chosen
597
598
599
600
601
602
        if( _nScreenOutput > 0 ) {
            std::vector<SCALAR_OUTPUT>::iterator it;
            it = find( _screenOutput.begin(), _screenOutput.end(), ITER );
            _screenOutput.erase( it );
            _screenOutput.insert( _screenOutput.begin(), ITER );
        }
603
604
        // Set default screen output
        if( _nScreenOutput == 0 ) {
605
            _nScreenOutput = 4;
606
607
608
            _screenOutput.push_back( ITER );
            _screenOutput.push_back( RMS_FLUX );
            _screenOutput.push_back( MASS );
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
            _screenOutput.push_back( VTK_OUTPUT );
        }
    }

    // History Output Postprocessing
    {
        // Check for doublicates in VOLUME OUTPUT
        std::map<SCALAR_OUTPUT, int> dublicate_map;

        for( unsigned short idx_screenOutput = 0; idx_screenOutput < _nHistoryOutput; idx_screenOutput++ ) {
            std::map<SCALAR_OUTPUT, int>::iterator it = dublicate_map.find( _historyOutput[idx_screenOutput] );
            if( it == dublicate_map.end() ) {
                dublicate_map.insert( std::pair<SCALAR_OUTPUT, int>( _historyOutput[idx_screenOutput], 0 ) );
            }
            else {
                it->second++;
            }
        }
        for( auto& e : dublicate_map ) {
            if( e.second > 0 ) {
                ErrorMessages::Error( "Each output field for option SCREEN_OUTPUT can only be set once.\nPlease check your .cfg file.",
                                      CURRENT_FUNCTION );
            }
        }

        // Set ITER always to index 0 . Assume only one instance of iter is chosen
        if( _nHistoryOutput > 0 ) {
            std::vector<SCALAR_OUTPUT>::iterator it;
            it = find( _historyOutput.begin(), _historyOutput.end(), ITER );
            _historyOutput.erase( it );
            _historyOutput.insert( _historyOutput.begin(), ITER );
        }
        // Set default screen output
        if( _nHistoryOutput == 0 ) {
            _nHistoryOutput = 4;
            _historyOutput.push_back( ITER );
            _historyOutput.push_back( RMS_FLUX );
            _historyOutput.push_back( MASS );
            _historyOutput.push_back( VTK_OUTPUT );
648
        }
649
    }
650
651
652
653
654
655
656
657

    // Mesh postprocessing
    {
        if( _dim < (unsigned short)1 || _dim > (unsigned short)3 ) {
            std::string msg = "Dimension " + std::to_string( _dim ) + "not supported.\n";
            ErrorMessages::Error( msg, CURRENT_FUNCTION );
        }
    }
658
659
}

660
661
void Config::SetOutput() {
    // Set Output for settings, i.e. feedback on what has been chosen
662
663
}

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
bool Config::TokenizeString( string& str, string& option_name, vector<string>& option_value ) {
    const string delimiters( " (){}:,\t\n\v\f\r" );
    // check for comments or empty string
    string::size_type pos, last_pos;
    pos = str.find_first_of( "%" );
    if( ( str.length() == 0 ) || ( pos == 0 ) ) {
        // str is empty or a comment line, so no option here
        return false;
    }
    if( pos != string::npos ) {
        // remove comment at end if necessary
        str.erase( pos );
    }

    // look for line composed on only delimiters (usually whitespace)
    pos = str.find_first_not_of( delimiters );
    if( pos == string::npos ) {
        return false;
    }

    // find the equals sign and split string
    string name_part, value_part;
    pos = str.find( "=" );
    if( pos == string::npos ) {
688
689
690
        string errmsg = "Error in Config::TokenizeString(): line in the configuration file with no \"=\" sign.  ";
        errmsg += "\nLook for: \n  str.length() = " + str.length();
        spdlog::error( errmsg );
691
692
693
694
695
696
697
698
699
        throw( -1 );
    }
    name_part  = str.substr( 0, pos );
    value_part = str.substr( pos + 1, string::npos );

    // the first_part should consist of one string with no interior delimiters
    last_pos = name_part.find_first_not_of( delimiters, 0 );
    pos      = name_part.find_first_of( delimiters, last_pos );
    if( ( name_part.length() == 0 ) || ( last_pos == string::npos ) ) {
700
701
702
        string errmsg = "Error in Config::TokenizeString(): ";
        errmsg += "line in the configuration file with no name before the \"=\" sign.\n";
        spdlog::error( errmsg );
703
704
705
706
707
708
        throw( -1 );
    }
    if( pos == string::npos ) pos = name_part.length();
    option_name = name_part.substr( last_pos, pos - last_pos );
    last_pos    = name_part.find_first_not_of( delimiters, pos );
    if( last_pos != string::npos ) {
709
710
711
        string errmsg = "Error in  Config::TokenizeString(): ";
        errmsg += "two or more options before an \"=\" sign in the configuration file.";
        spdlog::error( errmsg );
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
        throw( -1 );
    }
    TextProcessingToolbox::StringToUpperCase( option_name );

    // now fill the option value vector
    option_value.clear();
    last_pos = value_part.find_first_not_of( delimiters, 0 );
    pos      = value_part.find_first_of( delimiters, last_pos );
    while( string::npos != pos || string::npos != last_pos ) {
        // add token to the vector<string>
        option_value.push_back( value_part.substr( last_pos, pos - last_pos ) );
        // skip delimiters
        last_pos = value_part.find_first_not_of( delimiters, pos );
        // find next "non-delimiter"
        pos = value_part.find_first_of( delimiters, last_pos );
    }
    if( option_value.size() == 0 ) {
729
730
731
        string errmsg = "Error in  Config::TokenizeString(): ";
        errmsg += "option " + option_name + " in configuration file with no value assigned.\n";
        spdlog::error( errmsg );
732
733
        throw( -1 );
    }
734

735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    // look for ';' DV delimiters attached to values
    vector<string>::iterator it;
    it = option_value.begin();
    while( it != option_value.end() ) {
        if( it->compare( ";" ) == 0 ) {
            it++;
            continue;
        }

        pos = it->find( ';' );
        if( pos != string::npos ) {
            string before_semi = it->substr( 0, pos );
            string after_semi  = it->substr( pos + 1, string::npos );
            if( before_semi.empty() ) {
                *it = ";";
                it++;
                option_value.insert( it, after_semi );
            }
            else {
                *it = before_semi;
                it++;
                vector<string> to_insert;
                to_insert.push_back( ";" );
                if( !after_semi.empty() ) to_insert.push_back( after_semi );
                option_value.insert( it, to_insert.begin(), to_insert.end() );
            }
            it = option_value.begin();    // go back to beginning; not efficient
            continue;
        }
        else {
            it++;
        }
767
    }
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
    // remove any consecutive ";"
    it                = option_value.begin();
    bool semi_at_prev = false;
    while( it != option_value.end() ) {
        if( semi_at_prev ) {
            if( it->compare( ";" ) == 0 ) {
                option_value.erase( it );
                it           = option_value.begin();
                semi_at_prev = false;
                continue;
            }
        }
        if( it->compare( ";" ) == 0 ) {
            semi_at_prev = true;
        }
        else {
            semi_at_prev = false;
        }
        it++;
788
789
    }

790
    return true;
791
}
792

793
794
795
796
797
798
799
800
801
void Config::InitLogger() {

    // Declare Logger
    spdlog::level::level_enum terminalLogLvl;
    spdlog::level::level_enum fileLogLvl;

    // Choose Logger
#ifdef BUILD_TESTING
    terminalLogLvl = spdlog::level::err;
jannick.wolters's avatar
jannick.wolters committed
802
    fileLogLvl     = spdlog::level::info;
803
#elif NDEBUG
804
805
    terminalLogLvl = spdlog::level::info;
    fileLogLvl     = spdlog::level::info;
806
807
808
#else
    terminalLogLvl = spdlog::level::debug;
    fileLogLvl     = spdlog::level::debug;
809
810
#endif

811
812
813
814
815
    // create log dir if not existent
    if( !std::filesystem::exists( _logDir ) ) {
        std::filesystem::create_directory( _logDir );
    }

jannick.wolters's avatar
jannick.wolters committed
816
    if( !spdlog::get( "event" ) ) {
817
818
819
820
821
822
823
824
825
826
827
828
829
830
        // 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];
831

832
833
834
835
836
837
838
839
            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 );

840
841
                // set filename
                std::string filename;
842
843
844
845
                if( _logFileName.compare( "use_date" ) == 0 ) {
                    _logFileName = buf;    // set filename to date and time
                }
                filename = _logFileName;
846
847
848
849
850
851
852
853
854
855
856
857

                // 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;
858
            }
859
860
861
862
863
864
865
866
            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 );
867
868
        }

869
870
871
872
873
        // 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 ) );
    }
874

jannick.wolters's avatar
jannick.wolters committed
875
    if( !spdlog::get( "tabular" ) ) {
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
        // create sinks if level is not off
        std::vector<spdlog::sink_ptr> sinks;
        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_csv", &tstruct );

892
893
894
895
896
897
                // set filename
                std::string filename;
                if( _logFileName.compare( "use_date" ) == 0 )
                    filename = buf;    // set filename to date and time
                else
                    filename = _logFileName + "_csv";
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916

                // 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 );
917
            fileSink->set_pattern( "%Y-%m-%d %H:%M:%S.%f ,%v" );
918
919
920
921
            sinks.push_back( fileSink );
        }

        // register all sinks
jannick.wolters's avatar
jannick.wolters committed
922
923
        auto tabular_logger = std::make_shared<spdlog::logger>( "tabular", begin( sinks ), end( sinks ) );
        spdlog::register_logger( tabular_logger );
924
925
        spdlog::flush_every( std::chrono::seconds( 5 ) );
    }
926
}