DUNE-FEM (unstable)

container.hh
1#ifndef DUNE_FEM_IO_PARAMETER_CONTAINER_HH
2#define DUNE_FEM_IO_PARAMETER_CONTAINER_HH
3
4#include <cassert>
5#include <cstddef>
6
7#include <fstream>
8#include <iostream>
9#include <map>
10#include <queue>
11#include <set>
12#include <string>
13#include <utility>
14
15#include <dune/grid/io/file/dgfparser/dgfparser.hh>
16
17#include <dune/fem/io/io.hh>
18#include <dune/fem/io/parameter/exceptions.hh>
19#include <dune/fem/io/parameter/reader.hh>
20#include <dune/fem/misc/mpimanager.hh>
21
22namespace Dune
23{
24
25 namespace Fem
26 {
27
28 // ParameterContainerData
29 // ----------------------
30
31 struct ParameterContainerData
32 {
34 static const int solverStatistics = 1; // this is the new default level
36 static const int extendedStatistics = 2;
38 static const int parameterOutput = 3;
40 static const int diagnosticsOutput = 4;
42 static const int debugOutput = 5;
43
44 // default verbosity level used when verbose()
45 // without specifying a level is called (old behavior)
46 static const int defaultVerbosityLevel = parameterOutput;
47
48 struct Value
49 {
50 enum ShadowStatus { unresolved, resolved, resolving };
51
52 Value () = default;
53
54 Value ( std::string v, std::string fn ) : value( std::move( v ) ), fileName( std::move( fn ) ) {}
55
56 std::string value, fileName, defaultValue;
57 bool used = false, hasDefault = false;
58 ShadowStatus shadowStatus = unresolved;
59 };
60
61 const std::string *operator() ( const std::string &key, const std::string *defaultValue ) const;
62
63 static std::string trim ( const std::string &s )
64 {
65 const std::size_t first = s.find_first_not_of( " \t\n" );
66 return (first != s.npos ? s.substr( first, s.find_last_not_of( " \t\n" ) + 1 - first ) : std::string());
67 }
68
69 std::string resolveEscape ( const std::string &key, std::string &value ) const;
70 void resolveShadows ( const std::string &key, Value &val ) const;
71 std::string getShadowKey ( const std::string key, const char delimter, std::string &value ) const;
72
73 bool verbose ( const int level = defaultVerbosityLevel ) const
74 {
75 // return true if verboserank is the current rank and if
76 // the activated verbosity level is higher or equal to the given level
77 return (verboseRank == MPIManager::rank() && level <= verbosityLevel);
78 }
79
80 mutable std::map< std::string, Value > map;
81 std::set< std::string > deprecated;
82 int verboseRank = 0; // default is to output on rank 0
83 int verbosityLevel = 1; // default verbosity level is 1
84 bool verbosityLevelPresent = false; // this is parameter was provided
85 bool verbosityChangedByVerboseRank = false; // this is true if verboserank was provided, but not verbositylevel
86 };
87
88
89
90 // ParameterContainer
91 // ------------------
92
93 class ParameterContainer
94 : public BasicParameterReader< ParameterContainerData >
95 {
96 typedef ParameterContainerData::Value Value;
97
98 struct DGFBlock;
99
100 static std::string stripComment ( const std::string &line );
101
102 const std::string &insert ( const std::string &key, const std::string &value, bool force );
103 bool insert ( const std::string &s, std::queue< std::string > &includes );
104
105 void processFile ( const std::string &filename );
106 void processIncludes( std::queue< std::string > &includes );
107
108 public:
109
111 operator ParameterReader () const { return ParameterReader( std::ref( parameter_ ) ); }
112
123 void append ( int &argc, char **argv );
124
130 void append ( const std::string &filename )
131 {
132 processFile( filename );
133 }
134
142 void append ( const std::string &key, const std::string &value, bool force = false )
143 {
144 if( key != "paramfile" )
145 {
146 curFileName_ = "program code";
147 insert( key, value, force );
148 }
149 else
150 append( value );
151 }
152
153
159 template <class T>
160 std::string toString( const T& value )
161 {
162 std::stringstream str;
163 str << std::scientific;
164 str << value;
165 return str.str();
166 }
167
175 template<class NumberType, std::enable_if_t< std::is_floating_point< NumberType >::value || std::is_integral< NumberType >::value, int> = 0 >
176 void append ( const std::string &key, NumberType value, bool force = false )
177 {
178 assert( key != "paramfile" );
179 curFileName_ = "program code";
180 std::string valueString = toString( value );
181 insert( key, valueString, force );
182 }
183
192 void appendDGF ( const std::string &filename );
193
195 void clear () { parameter_.map.clear(); }
196
198 bool verbose ( const int level = ParameterContainerData::defaultVerbosityLevel ) const
199 {
200 return parameter_.verbose( level );
201 }
202
203 std::string commonInputPath () const
204 {
205 return getValue( "fem.prefix.input", std::string( "." ) );
206 }
207
208 std::string commonOutputPath () const
209 {
210 return getValue( "fem.prefix", std::string( "." ) );
211 }
212
227 void write ( std::ostream &out, bool writeAll = true ) const;
228 auto write ( ) const;
229
230 std::map<std::string,std::set<std::pair<std::string,std::string>>> localParameterLog_;
231
232 private:
233 std::string curFileName_;
234 int curLineNumber_;
235 };
236
237
238
239 // ParameterContainer::DGFBlock
240 // ----------------------------
241
242 struct ParameterContainer::DGFBlock
243 : dgf::BasicBlock
244 {
245 explicit DGFBlock ( std::istream &in ) : BasicBlock( in, "FemParameter" ) {}
246
247 bool advance () { return getnextline(); }
248 std::string getLine () const { return line.str(); }
249 };
250
251
252
253 // Implementation of ParameterContainerData
254 // ----------------------------------------
255
256 inline const std::string *ParameterContainerData::operator() ( const std::string &key, const std::string *defaultValue ) const
257 {
258 if( deprecated.find( key ) != deprecated.end() )
259 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' deprecated" );
260
261 std::map< std::string, Value >::iterator pos;
262 if( defaultValue )
263 {
264 const std::string& defaultValueStr = *defaultValue;
265 // only check existence, do not check default values and the like
266 // when the default string has the value of checkParameterExistsString
267 // this is to avoid problems with default and non-default parameters
268 if( defaultValueStr == checkParameterExistsString() )
269 {
270 pos = map.find( key );
271 if( pos == map.end() )
272 return nullptr;
273 else
274 {
275 Value &val = pos->second;
276 return &val.value ;
277 }
278 }
279
280 auto info = map.insert( std::make_pair( key, Value( *defaultValue, "default" ) ) );
281 if( info.second && verbose() )
282 std::cout << "Adding default: " << key << ": " << *defaultValue << std::endl;
283 pos = info.first;
284 }
285 else
286 pos = map.find( key );
287
288 if( pos == map.end() )
289 return nullptr;
290 Value &val = pos->second;
291
292 if( val.used )
293 {
294 if( val.hasDefault != static_cast< bool >( defaultValue ) )
295 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' used with and without default" );
296 if( defaultValue && (val.defaultValue != *defaultValue) )
297 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' used with different default values" );
298 }
299 else
300 {
301 val.used = true;
302 val.hasDefault = static_cast< bool >( defaultValue );
303 if( defaultValue )
304 val.defaultValue = *defaultValue;
305 }
306
307 resolveShadows( key, val );
308 return &val.value;
309 }
310
311
312 inline std::string ParameterContainerData::resolveEscape ( const std::string &key, std::string &value ) const
313 {
314 if( value.empty() )
315 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' contains trailing '$'." );
316
317 const char escapedChar = value[ 0 ];
318 value.replace( 0, 1, "" );
319
320 switch( escapedChar )
321 {
322 case '$':
323 case '%':
324 case '#':
325 return std::string( "" ) + escapedChar;
326
327 case '(':
328 {
329 auto pos = map.find( getShadowKey( key, ')', value ) );
330 if( pos == map.end() )
331 DUNE_THROW( ParameterNotFound, "Parameter '" << key << "' not found" );
332 resolveShadows( pos->first, pos->second );
333 return pos->second.value;
334 }
335
336 case '[':
337 return trim( executeCommand( getShadowKey( key, ']', value ) ) );
338
339 default:
340 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' invalid." );
341 }
342 }
343
344
345 inline void ParameterContainerData::resolveShadows ( const std::string &key, Value &val ) const
346 {
347 std::string &realValue = val.value;
348 if( val.shadowStatus == Value::resolved )
349 return;
350
351 if ( val.shadowStatus == Value::resolving )
352 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' invalid, contains infinite loop" );
353
354 val.shadowStatus = Value::resolving;
355 std::string realValueHelper;
356 realValue.swap( realValueHelper );
357
358 while( !realValueHelper.empty() )
359 {
360 std::size_t startPoint = realValueHelper.find_first_of( '$' );
361 realValue += realValueHelper.substr( 0, startPoint );
362
363 if( startPoint == std::string::npos )
364 break;
365
366 realValueHelper.replace( 0, startPoint+1, "" );
367
368 realValue += resolveEscape( key, realValueHelper );
369 }
370 val.shadowStatus = Value::resolved;
371 }
372
373
374 inline std::string ParameterContainerData::getShadowKey ( const std::string key, const char delimiter, std::string &value ) const
375 {
376 std::string shadowKey;
377
378 while( true )
379 {
380 std::size_t startPoint = value.find_first_of( std::string( "$" ) + delimiter );
381
382 if( startPoint == std::string::npos )
383 DUNE_THROW( ParameterInvalid, "Parameter '" << key << "' invalid." );
384
385 shadowKey += value.substr( 0, startPoint );
386 const char startChar = value[ startPoint ];
387
388 value.replace( 0, startPoint+1, "" );
389
390 if( startChar == delimiter )
391 return shadowKey;
392 assert( startChar == '$' );
393
394 shadowKey += resolveEscape( key, value );
395 }
396 }
397
398
399
400 // Implementation of ParameterContainer
401 // ------------------------------------
402
403 inline const std::string &ParameterContainer::insert ( const std::string &key, const std::string &value, bool force = false)
404 {
405 auto pos = parameter_.map.find( key );
406 bool paramExists = ( pos != parameter_.map.end() );
407 std::string paramValue;
408 if( force && paramExists )
409 {
410 paramValue = pos->second.value;
411 if( paramValue == value )
412 return value;
413 parameter_.map.erase( key );
414 }
415 auto info = parameter_.map.insert( std::make_pair( key, Value( value, curFileName_ ) ) );
416 Value &val = info.first->second;
417 if( key == "fem.verboserank" )
418 {
419 ParameterParser< int >::parse( val.value, parameter_.verboseRank );
420 if( (parameter_.verboseRank < -1) || (parameter_.verboseRank >= MPIManager::size() ) )
421 std::cout << "Warning: Parameter 'fem.verboserank' is neither a " << "valid rank nor -1." << std::endl;
422
423 // Restore default behavior:
424 // If fem.verboserank is provided, then we set the verbosityLevel to 3
425 // to restore the old behavior. Otherwise the level is 1.
426 if( ! parameter_.verbosityLevelPresent &&
427 parameter_.verbosityLevel < ParameterContainerData::defaultVerbosityLevel )
428 {
429 parameter_.verbosityLevel = ParameterContainerData::defaultVerbosityLevel;
430 parameter_.verbosityChangedByVerboseRank = true;
431 }
432 }
433
434 if( key == "fem.verbositylevel" )
435 {
436 // if verbositylevel is provided undo the changed by verboserank
437 if( parameter_.verbosityChangedByVerboseRank )
438 parameter_.verbosityLevel = 1;
439
440 parameter_.verbosityLevelPresent = true;
441
442 ParameterParser< int >::parse( val.value, parameter_.verbosityLevel );
443 if( (parameter_.verbosityLevel < 0) || (parameter_.verbosityLevel >= 10 ) )
444 std::cout << "Warning: Parameter 'fem.verbositylevel' is neither a " << "valid level nor 0." << std::endl;
445 }
446
447 if( verbose() )
448 {
449 std::cout << curFileName_ << "[" << curLineNumber_ << "]: ";
450 if( !paramExists )
451 std::cout << "Adding " << key << " = " << value << std::endl;
452 else if ( !force )
453 std::cout << "Ignored " << key << " = " << value << ", using " << val.value << std::endl;
454 else
455 std::cout << "Replacing " << key << " = " << paramValue << " by " << value << std::endl;
456 }
457
458 return force ? value : val.value;
459 }
460
461
462 inline std::string ParameterContainer::stripComment ( const std::string &line )
463 {
464 std::size_t size = line.size();
465 std::size_t end = line.find_first_of ( "%#$" );
466
467 while( (end != std::string::npos) && (line[end] =='$') )
468 {
469 if( end+2 < size )
470 end = line.find_first_of ( "%#$", end+2 );
471 else
472 end = std::string::npos;
473 }
474
475 return ParameterContainerData::trim( line.substr( 0, end ) );
476 }
477
478
479 inline bool ParameterContainer::insert ( const std::string &s, std::queue< std::string > &includes )
480 {
481 const std::size_t size = s.size();
482
483 std::size_t key_start = 0;
484 for( ; key_start < size; ++key_start )
485 {
486 if( (s[ key_start ] != ' ') && (s[ key_start ] != '\t') )
487 break;
488 }
489
490 std::size_t key_end = key_start;
491 for( ; key_end < size; ++key_end )
492 {
493 const char &c = s[ key_end ];
494 if( (c == ' ') || (c == '\t') || (c == ':') )
495 break;
496 }
497
498 std::size_t value_start = key_end;
499 for( ; value_start < size ; ++value_start )
500 {
501 if( s[ value_start ] == ':' )
502 break;
503 }
504 ++value_start;
505
506 for( ; value_start < size; ++value_start )
507 {
508 if( (s[ value_start ] != ' ') && (s[ value_start ] != '\t') )
509 break;
510 }
511
512 std::size_t value_end = value_start;
513 for( std::size_t i = 0; i < size; ++i )
514 {
515 if( (s[ i ] != ' ') && (s[ i ] != '\t') )
516 value_end = i+1;
517 }
518
519 if( value_start >= size )
520 return false;
521
522 std::string key = s.substr( key_start, key_end - key_start );
523 std::string value = s.substr( value_start, value_end - value_start );
524
525 if( key == "paramfile" )
526 includes.push( commonInputPath() + "/" + value );
527 else if( key == "deprecated" )
528 parameter_.deprecated.insert( value );
529 else
530 insert( key, value );
531 return true;
532 }
533
534
535 inline void ParameterContainer::processFile ( const std::string &filename )
536 {
537 if( verbose() )
538 std::cout << "Parameter: Processing '" << filename << "'..." << std::endl;
539
540 std::ifstream file( filename );
541 if( !file.is_open() )
542 {
543 std::cerr << "Warning: Unable to read parameter file '" << filename << "'" << std::endl;
544 return;
545 }
546
547 curFileName_ = filename;
548 curLineNumber_ = 0;
549 std::queue< std::string > includes;
550
551 while( !file.eof() )
552 {
553 std::string line;
554 std::getline( file, line );
555 curLineNumber_++;
556 line = stripComment( line );
557 if( !line.empty() )
558 insert( line, includes );
559 }
560 file.close();
561
562 processIncludes( includes );
563 }
564
565
566 inline void ParameterContainer::processIncludes( std::queue< std::string > &includes )
567 {
568 while( !includes.empty() )
569 {
570 Value val;
571 val.value = includes.front();
572 includes.pop();
573 parameter_.resolveShadows( "paramfile", val );
574 processFile( val.value );
575 }
576 }
577
578
579 inline void ParameterContainer::append ( int &argc, char **argv )
580 {
581 std::queue< std::string > includes;
582 curFileName_ = "program arguments";
583 curLineNumber_ = 0;
584 for( int i = 1 ; i < argc; ++i )
585 {
586 ++curLineNumber_;
587 if( !insert( std::string( argv[ i ] ), includes ) )
588 continue;
589
590 std::copy( argv + (i+1), argv + argc, argv + i );
591 --i;
592 --argc;
593 }
594
595 processIncludes( includes );
596 }
597
598
599 inline void ParameterContainer::appendDGF ( const std::string &filename )
600 {
601 if( verbose() )
602 std::cout << "Parameter: Processing DGF '" << filename << "'..." << std::endl;
603
604 std::ifstream file( filename );
605 if( !file.is_open() )
606 {
607 std::cerr << "Warning: Unable to read DGF file '" << filename << "'" << std::endl;
608 return;
609 }
610
612 return;
613
614 DGFBlock block( file );
615 if( !block.isactive() )
616 return;
617
618 curFileName_ = filename;
619 curLineNumber_ = 0;
620 std::queue< std::string > includes;
621
622 while( block.advance() )
623 {
624 ++curLineNumber_;
625 const std::string line = stripComment( block.getLine() );
626 if( !line.empty() )
627 insert( line, includes );
628 }
629
630 processIncludes( includes );
631 }
632
633
634 inline void ParameterContainer::write ( std::ostream &out, bool writeAll ) const
635 {
636 std::map< std::string, std::map<std::string, std::string> > writeMap;
637 for( const auto &param : parameter_.map )
638 {
639 const Value &val = param.second;
640 if( writeAll || !val.hasDefault || (val.used && (val.value != val.defaultValue)) )
641 writeMap[ val.fileName ][ (val.used ? "": "# " ) + param.first ] = val.value;
642 }
643
644 for( const auto &source : writeMap )
645 {
646 out << "# from " << source.first << std::endl;
647 for( const auto &param : source.second )
648 out << param.first << ": " << param.second << std::endl;
649 out << std::endl;
650 }
651 }
652 inline auto ParameterContainer::write () const
653 {
654 std::map< std::string, std::set<std::pair<std::string, std::string>> > writeMap;
655 for( const auto &param : parameter_.map )
656 {
657 const Value &val = param.second;
658 writeMap[ val.fileName ].insert( {param.first,val.value} );
659 }
660 return writeMap;
661 }
662
663 } // namespace Fem
664
665} // namespace Dune
666
667#endif // #ifndef DUNE_FEM_IO_PARAMETER_CONTAINER_HH
static bool isDuneGridFormat(std::istream &input)
check whether a stream is in DUNE grid format
Definition: dgfparser.cc:271
std::string toString(Precision p)
map precision to VTK type name
Definition: common.hh:280
#define DUNE_THROW(E, m)
Definition: exceptions.hh:218
constexpr GeometryType line
GeometryType representing a line.
Definition: type.hh:498
Dune namespace.
Definition: alignedallocator.hh:13
constexpr std::integral_constant< std::size_t, sizeof...(II)> size(std::integer_sequence< T, II... >)
Return the size of the sequence.
Definition: integersequence.hh:75
STL namespace.
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Jul 27, 22:29, 2024)