Initfile.h 19 KB
Newer Older
1 2 3 4 5 6 7
/******************************************************************************
 *
 * AMDiS - Adaptive multidimensional simulations
 *
 * Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
 * Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
 *
8
 * Authors:
9 10 11 12 13 14 15 16 17
 * Simon Vey, Thomas Witkowski, Andreas Naumann, Simon Praetorius, et al.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *
 * This file is part of AMDiS
 *
 * See also license.opensource.txt in the distribution.
18
 *
19
 ******************************************************************************/
20

Praetorius, Simon's avatar
Praetorius, Simon committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#ifndef INITFILE_H
#define INITFILE_H

#include <fstream>
#include <sstream>
#include <string>
#include <map>
#include <list>
#include <set>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <typeinfo>
#include "FixVec.h"

36 37
#include "traits/size.hpp"

Praetorius, Simon's avatar
Praetorius, Simon committed
38
#include <boost/lexical_cast.hpp>
39
#include <boost/numeric/conversion/cast.hpp>
40
#include <boost/thread/shared_mutex.hpp> // requires c++14
41

42
#include <boost/type_traits.hpp>
Praetorius, Simon's avatar
Praetorius, Simon committed
43

44 45
namespace AMDiS {

46
  namespace detail {
47

48 49
    // Exceptions
    //_________________________________________________________________________________________
50

51
    /// Exception for wrong vector size when parsing for concrete vector type
Praetorius, Simon's avatar
Praetorius, Simon committed
52
    struct WrongVectorSize : std::runtime_error {
Praetorius, Simon's avatar
Praetorius, Simon committed
53
      WrongVectorSize(std::string m) : std::runtime_error(m) {}
54 55
    };

56

57
    /// Exception for no delimiter found in vector
Praetorius, Simon's avatar
Praetorius, Simon committed
58
    struct NoDelim : std::runtime_error {
Praetorius, Simon's avatar
Praetorius, Simon committed
59
      NoDelim(std::string m) : std::runtime_error(m) {}
60 61
    };

62

63
    /// Exception for begin and end brackets are different in vector
Praetorius, Simon's avatar
Praetorius, Simon committed
64
    struct WrongVectorFormat : std::runtime_error {
Praetorius, Simon's avatar
Praetorius, Simon committed
65
      WrongVectorFormat(std::string m) : std::runtime_error(m) {}
66
    };
67 68


69
    /// Exception for wrong value format
70
    template<typename T>
71 72
    struct WrongValueFormat : std::runtime_error
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
73 74 75 76 77
      static std::string name(bool) { return "bool"; }
      static std::string name(double) { return "double"; }
      static std::string name(float) { return "float"; }
      static std::string name(int) { return "int"; }
      static std::string name(unsigned int) { return "unsigned int"; }
78 79

      template<typename G>
80 81 82
      static std::string name(G)
      {
        return std::string(typeid(G).name());
83
      }
84

85
      WrongValueFormat(std::string value)
Praetorius, Simon's avatar
Praetorius, Simon committed
86
      : std::runtime_error("cannot convert '" + value + "' into <" + name(T()) + ">")
87 88
      {}
    };
89

Praetorius, Simon's avatar
Praetorius, Simon committed
90

91
    /// Exception for bad arithmetic expression that can not be evaluated
92
    template<typename T>
93
    struct BadArithmeticExpression : std::runtime_error
94
    {
Praetorius, Simon's avatar
Praetorius, Simon committed
95 96 97 98 99
      static std::string name(bool) { return "bool"; }
      static std::string name(double) { return "double"; }
      static std::string name(float) { return "float"; }
      static std::string name(int) { return "int"; }
      static std::string name(unsigned int) { return "unsigned int"; }
100

101
      template<typename G>
102 103
      static std::string name(G)
      {
104
        return std::string(typeid(G).name());
105
      }
106

107
      BadArithmeticExpression(std::string m, std::string value)
Praetorius, Simon's avatar
Praetorius, Simon committed
108 109
      : std::runtime_error("cannot evaluate expression '" + value + "' into <" + name(T()) + ">\n"
			   "Parser message: '" + m + "'")
110 111
      {}
    };
112

113
// _____________________________________________________________________________
114

115
    /// return the delimiter or throw an exception if there is no known
116
    /// delimiter in value
117 118 119
    inline size_t checkDelim(const std::string& value, const std::string& delims)
    {
      size_t pos(std::string::npos);
Praetorius, Simon's avatar
Praetorius, Simon committed
120
      for (size_t i = 0; i < delims.length(); i++) {
121 122 123
        pos = value.find(delims[i]);
        if (pos != std::string::npos)
          return i;
124
      }
Thomas Witkowski's avatar
Thomas Witkowski committed
125
      //      throw NoDelim("cannot detect the delimiter in " + value);
126
      return 0;
127
    }
128 129


130
    double mu_parser_eval(std::string const& valStr);
131 132 133


    /// convert string to string
134
    inline void convert(const std::string valStr, std::string& value)
135 136 137 138 139 140
    {
      value = trim(valStr);
    }


    /// convert string to intrinsic type
141 142 143
    template<typename T> inline
    typename boost::enable_if
	     < boost::mpl::and_
144 145
	       < boost::is_pod<T>,
	         boost::mpl::not_< boost::is_enum<T> > >,
146 147 148
	       void
	     >::type
    convert(const std::string valStr, T& value)
149 150 151 152
    {
      using boost::lexical_cast;
      using boost::numeric_cast;

153
      value = numeric_cast< T >(mu_parser_eval(valStr));
Thomas Witkowski's avatar
Thomas Witkowski committed
154 155 156 157 158 159 160
/*       } catch (boost::bad_lexical_cast e) { */
/*         throw WrongValueFormat< T >(valStr); */
/*       } catch (boost::bad_numeric_cast e) { */
/*         throw WrongValueFormat< T >(valStr); */
/*       } catch (mu::Parser::exception_type &e) { */
/*         throw BadArithmeticExpression<T>(e.GetMsg(), valStr); */
/*       } */
161 162 163
    }


164 165
    template<typename T> inline
    typename boost::enable_if
166
	     < boost::is_enum<T>,
167 168
	       void
	     >::type
169
    convert(const std::string valStr, T& value)
170
    {
171
      int swap = 0;
Thomas Witkowski's avatar
Thomas Witkowski committed
172
      //      try {
173
        swap = boost::lexical_cast<int>(trim(valStr));
Thomas Witkowski's avatar
Thomas Witkowski committed
174 175 176
/*       } catch (boost::bad_lexical_cast e) { */
/*         throw WrongValueFormat< T >(valStr); */
/*       } */
177 178 179 180
      value = static_cast< T >(swap);
    }


181
    /// convert special enums
182
    inline void convert(const std::string valStr, Norm& value)
183 184 185 186 187 188 189 190 191 192
    {
      std::string swapStr = boost::to_upper_copy(valStr);

      if (swapStr == "NO_NORM")
        value = static_cast< Norm >(NO_NORM);
      else if (swapStr == "H1_NORM")
        value = static_cast< Norm >(H1_NORM);
      else if (swapStr == "L2_NORM")
        value = static_cast< Norm >(L2_NORM);
      else {
Praetorius, Simon's avatar
Praetorius, Simon committed
193
        int swap = 0;
194 195 196 197 198 199
        convert(valStr, swap);
        value = static_cast< Norm >(swap);
      }
    }


200
    /// convert value of arbitrary type to string using stringstream and
Naumann, Andreas's avatar
Naumann, Andreas committed
201 202
    /// operator<< for type
    template<typename T>
203
    inline void convert(const T value, std::string& valStr)
Naumann, Andreas's avatar
Naumann, Andreas committed
204 205 206 207 208 209 210 211 212
    {
      std::stringstream ss;
      ss.precision(6);
      ss << value;
      valStr = ss.str();
    }


    /// convert WorldVector to string
213 214
    // TODO: allgemeine Funktion zum Schreiben von Vektoren implementieren
    //       unter Ausnutzung von Type-Traits
Naumann, Andreas's avatar
Naumann, Andreas committed
215 216 217
    template<typename T>
    inline void convert(const WorldVector<T>& c, std::string& valStr)
    {
218 219
      std::vector<T> temp_vec(size(c));
      for (unsigned i = 0; i < size(temp_vec); i++)
Naumann, Andreas's avatar
Naumann, Andreas committed
220 221 222 223
        temp_vec[i] = c[i];
      convert(temp_vec, valStr);
    }

224
    // forward declarations
Thomas Witkowski's avatar
Thomas Witkowski committed
225 226 227 228 229 230 231 232 233
    template< typename T >
    inline void convert(const std::string valStr, WorldVector<T>& c);

    template<typename T>
    inline void convert(const std::string valStr, std::list<T>& value);

    template<typename T>
    inline void convert(const std::string valStr, std::vector<T>& value);

Naumann, Andreas's avatar
Naumann, Andreas committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    /** parse an container from tag tag. The Container must have the properties:
    * 	- type value_type
    * 	- member function push_back
    */
    template< typename Container >
    inline void getContainer(const std::string val_, Container& c)
    {
      // accepted brackets and delimiters for vector input
      std::string begBrackets= "{[(";
      std::string endBrackets= "}])";
      std::string delims= ";,";

      c.clear();
      std::string val = trim(val_);
      bool hasBrackets = true;
      size_t pos = begBrackets.find(val[0]);
      if (pos == std::string::npos)
        hasBrackets = false;
252 253
       if (hasBrackets && val[val.length() - 1] != endBrackets[pos])
        throw WrongVectorFormat("begin and end bracket are different in"
254
            " value '" + val + "'");
Naumann, Andreas's avatar
Naumann, Andreas committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
      size_t oldPos = (hasBrackets ? 1 : 0);
      size_t curDelim = 0;
      typedef typename Container::value_type ValueType;
      ValueType swap;
      try {
        curDelim = checkDelim(val, delims);
        pos = val.find(delims[curDelim], oldPos);
        while (pos != std::string::npos) {
          std::string curWord = val.substr(oldPos, pos - oldPos);
          oldPos = pos + 1;
          convert(curWord, swap);
          c.push_back(swap);
          pos = val.find(delims[curDelim], oldPos);
        }
        //last entry
        std::string curWord = val.substr(oldPos, val.length() - (hasBrackets ? 1 : 0) - oldPos);
        convert(curWord, swap);
        c.push_back(swap);
      } catch (NoDelim nd) {
        std::string curWord = val.substr(oldPos, val.length() - (hasBrackets ? 2 : 0));
        curWord = trim(curWord);
        if (curWord.length() > 0) {
          // container with one entry
          convert(curWord, swap);
          c.push_back(swap);
        }
      }
    }

284 285
    // TODO: Verallgemeinerung der convert-method für bel. Vektoren und
    //       hinzunahme der Möglichkeit Matrizen anzugeben
Naumann, Andreas's avatar
Naumann, Andreas committed
286

287 288
    /// convert string to WorldVector
    template< typename T >
289
    inline void convert(const std::string valStr, WorldVector<T>& c)
290 291 292
    {
      std::vector<T> temp_vec;
      getContainer(valStr, temp_vec);
293
       if (static_cast<int>(temp_vec.size()) != c.getSize())
294 295
        throw WrongVectorSize("wrong number of entries for WorldVector");

Praetorius, Simon's avatar
Praetorius, Simon committed
296
      for (size_t i = 0; i < temp_vec.size(); i++)
297
        c[i] = temp_vec[i];
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
    }

    /// convert string to std::list using begBrackets, endBrackets and delims
    template<typename T>
    inline void convert(const std::string valStr, std::list<T>& value)
    {
      getContainer(valStr, value);
    }


    /// convert string to std::vector using begBrackets, endBrackets and delims
    template<typename T>
    inline void convert(const std::string valStr, std::vector<T>& value)
    {
      getContainer(valStr, value);
    }


316
  } // end namespace detail
Praetorius, Simon's avatar
Praetorius, Simon committed
317

318
// _____________________________________________________________________________
Praetorius, Simon's avatar
Praetorius, Simon committed
319

320
  /** The entry in an initfile. This helper class was constructed to allow calls
321 322 323
  *  like val = data.get(tag) for arbitrary types of val. At current stage, only
  *  double and bool is supported
  */
324 325 326 327 328 329
  struct InitEntry {
    ///the value as string
    std::string valStr;

    /// initialize with value as string
    InitEntry(std::string v = "")
330
    : valStr(v)
331 332 333 334
    {}

    /// cast string to type T
    template<typename T>
335 336 337 338
    operator T() const
    {
      T t;
      detail::convert(valStr, t);
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
      return t;
    }
  };


  /// output-stream for std::list
  template<typename T>
  std::ostream& operator<<(std::ostream& o, const std::list< T >& l)
  {
    typename std::list< T >::const_iterator it = l.begin();
    o << "[";
    for (unsigned i = 0; it != l.end() && i < l.size(); i++) {
      o << *it << (i < l.size() - 1 ? ", " : "");
      ++it;
    }
    o << "]";
    return o;
  }


  /// output-stream for std::vector
  template<typename T>
  std::ostream& operator<<(std::ostream& o, const std::vector<T>& l)
  {
    typename std::vector<T>::const_iterator it = l.begin();
    o << "[";
    for (unsigned i = 0; it != l.end() && i < l.size(); i++) {
      o << *it << (i < l.size() - 1 ? ", " : "");
      ++it;
    }
    o << "]";
    return o;
  }

373
// _____________________________________________________________________________
374

375
  /** Basis data container as a map of tag on a value as strings. The container
376 377
  *  throws an exception, if the tag was not found.
  */
378
  struct Initfile : public std::map<std::string, std::string>
379 380 381
  {
    typedef std::map< std::string, std::string > super;

382 383
    static const int TAG_NOT_FOUND = 1;
    static const int TAG_NOT_FOUND_BREAK = 2;
384

385
    /// Exceptions
Praetorius, Simon's avatar
Praetorius, Simon committed
386
    struct TagNotFound : std::invalid_argument {
387 388
      TagNotFound(std::string m)
      : std::invalid_argument(m)
389 390 391
      {}
    };

392 393 394

    struct TagNotFoundBreak : std::invalid_argument {
    // print 'tag not found' and exit
395
      TagNotFoundBreak(std::string m)
396
      : std::invalid_argument(m)
397 398
      {}
    };
399 400


401
    /** initialize init-file from file with filename in, read data and save it
402 403 404
    *  to singleton-map
    *  @param in: filename string
    */
405 406
    static void init(std::string in);

407
    static void init(int print, std::string filename, const char *flags = NULL)
408
    {
409 410 411
        WARNING("Parameters::init(int,std::string,const char*) is depreciated. "
          "Use Parameters::init(std::string) instead!\n");
        init(filename);
412
    }
413 414


415
    /** Static get routine for getting parameter-values from init-file
416 417 418 419 420 421 422
    *  initialized in init()-method.
    *  Cast the value to the desired type using std::stringstream.
    *  @param tag: The tag to look for
    *  @param value: The result.
    *  @param debugInfo: msgInfo for current parameter. (0..no printing,
    *    1..print missing parameter info, 2..print parameter value) [optional]
    */
423 424 425
    template<typename T>
    static void get(const std::string tag, T& value, int debugInfo = -1)
    {
426 427
//       boost::shared_lock<boost::shared_mutex> lock(singlett().mutex_);
      
428
      if (debugInfo == -1)
429
        debugInfo = singlett().getMsgInfo();
430 431
      else {
	int swap(debugInfo);
432 433
	debugInfo = singlett().getMsgInfo();
	singlett().msgInfo=swap;
434
      }
435

436
      std::string valStr;
437
      try {
438
      int error_code = singlett().checkedGet(tag, valStr);
439 440
      if (error_code == 0) {
	valStr = trim(valStr);
441
	detail::convert(valStr, value);
442
      }
443
      else if(error_code == TAG_NOT_FOUND_BREAK)
444
	throw TagNotFoundBreak("required tag '" + tag + "' not found");
445 446 447 448
      else if (error_code == TAG_NOT_FOUND) {
	if (debugInfo == 2)
	  std::cout << "there is no tag '" + tag + "'" << std::endl;
      } else
Praetorius, Simon's avatar
Praetorius, Simon committed
449
	throw std::runtime_error("unknown error_code (" + boost::lexical_cast<std::string>(error_code) + ") returned for tag '" + tag + "'");
Thomas Witkowski's avatar
Thomas Witkowski committed
450

451
      } catch(...) {
452 453 454
	std::string parser_error = "Could not parse: " + tag;
	throw std::runtime_error(parser_error);
      }
455 456 457
      if (debugInfo == 2) {
	std::cout << "Parameter '" << tag << "'"
		  << " initialized with: " << value << std::endl;
458
      }
459
      singlett().msgInfo = debugInfo;
460 461
    }

462

463
    /** Static get routine for getting a parameter-map from init-file
464
    *  initialized in init()-method.
465
    *  Idea:
466 467
    *     line in Initfile: <tag><key>: <value>
    *     is extracted in am map [key]->[value].
468
    *  @param tag: The tag withch labels the parameter map
469 470 471
    *  @param debugInfo: msgInfo for current parameter. (0..no printing,
    *    2..print parameter value) [optional]
    */
472
    static void getParameterMap(const std::string tag,
473 474 475
				std::map<std::string,std::string> &pm,
				int debugInfo = -1)
    {
476
	for (Initfile::iterator it = singlett().begin(); it != singlett().end(); it++){
477 478
	    std::string longTag= (*it).first ;
	    std::string value=(*it).second;
479
	    if(longTag.length()>tag.length() &&
480
	       longTag.compare(0,tag.length(),tag)==0){
481
		if (debugInfo == 2){
482
		  std::cout <<"tag "<< tag <<"\n";
483
		  std::cout <<"Extract Parameter map from "<<(*it).first
484
			    << " => " <<(*it).second  << std::endl;
485
		}
486

487
		std::string key=trim(longTag.substr(tag.length()));
488

489
		if (debugInfo == 2){
490
		  std::cout <<"Parameter map "<< key << " => "
491
			    << value << std::endl;
492 493 494
		}
		pm[key]=value;
	    }
495
	}
496
    }
497

498
    /// return InitEntry object for tag tag
499
    static InitEntry get(const std::string tag)
500
    {
501 502
//       boost::shared_lock<boost::shared_mutex> lock(singlett().mutex_);
      
503
      InitEntry result;
504

505
      std::string valStr;
506
      int error_code = singlett().checkedGet(tag, valStr);
507 508 509
      if (error_code == 0) {
	valStr = trim(valStr);
	result = InitEntry(valStr);
510
      }
Thomas Witkowski's avatar
Thomas Witkowski committed
511 512 513

#if 0
else if(error_code == TAG_NOT_FOUND_BREAK)
514
	throw TagNotFoundBreak("get(): required tag '" + tag + "' not found");
515
      else if (error_code == TAG_NOT_FOUND)
516
	throw TagNotFound("get(): there is no tag '" + tag + "'");
Praetorius, Simon's avatar
Praetorius, Simon committed
517
	// exception must be thrown, because an empty object would be return otherwise
518
      else
519
	throw std::runtime_error("get(): unknown error_code returned for tag '" + tag + "'");
Thomas Witkowski's avatar
Thomas Witkowski committed
520 521
#endif

522 523
      return result;
    }
524

525 526 527

    /// update map tag->value_old to tag->value in singleton
    template<typename T>
528
    static void set(const std::string tag, T& value, int debugInfo=  -1)
529
    {
530 531
//       boost::unique_lock<boost::shared_mutex> lock(singlett().mutex_);
      
532
      if (debugInfo == -1)
533
        debugInfo = singlett().getMsgInfo();
534

535
      std::string swap = "";
536
      detail::convert(value, swap);
537
      singlett()[trim(tag)] = swap;
538
      // update msg parameters msgInfo, msgWait, paramInfo
539
      singlett().getInternalParameters();
540
      if (debugInfo == 2)
541 542
        std::cout << "Parameter '" << tag << "'"
                  << " set to: " << value << std::endl;
543
    }
544 545


546 547
    /// add map tag->value to data in singleton
    template< typename T >
548
    static void add(const std::string tag, T& value, int debugInfo = -1)
549 550 551 552
    {
      set(tag, value, debugInfo);
    }

553

554
    /// rescheduling parameter
Praetorius, Simon's avatar
Praetorius, Simon committed
555
    static void readArgv(std::string parameters, int debugInfo = 2);
556

557

558
    /// Returns specified info level
559 560
    static int getMsgInfo()
    {
561
      return singlett().msgInfo;
562 563
    }

564

565
    /// Returns specified wait value
566 567
    static int getMsgWait()
    {
568
      return singlett().msgWait;
569
    }
570 571


572 573 574
    /// print all data in singleton to std::cout
    static void printParameters();

575

576 577 578
    /// clear data in singleton
    static void clearData()
    {
579
      singlett().clear();
580
    }
581
    
582

583 584 585
    /// save singlett-data to file with filename fn
    static void save(std::string fn)
    {
586
      singlett().write(fn);
587
    }
588
    
589

590 591 592 593 594
    // list of included filenames
    static std::set< std::string >& getIncludeList()
    {
      static std::set< std::string > include_list;
      return include_list;
595
    }
596
    
597 598 599 600 601 602
protected:
    Initfile()
    : msgInfo(0),
      msgWait(1),
      paramInfo(1),
      breakOnMissingTag(0)
603
    {}
604

605 606
    /// return reference to singleton
    static Initfile& singlett()
607
    {
608 609
      static Initfile singlett_{};
      return singlett_;
610
    }
Praetorius, Simon's avatar
Praetorius, Simon committed
611

612

613
    /// return the value of the given tag or throws an exception if the tag
614
    /// does not exist
615
    int checkedGet(const std::string& tag, std::string &valStr) const
616 617 618
    {
      super::const_iterator it = find(tag);
      if (it == end()) {
619
        if (breakOnMissingTag == 0 || msgInfo <= 2)
620
	  return TAG_NOT_FOUND;
621
        else
622
	  return TAG_NOT_FOUND_BREAK;
623
      }
624 625
      valStr = it->second;
      return 0;
626 627
    }

628 629 630 631 632
    /// replace variables by its value defined as parameter previousely
    /// variable definition is simple parameter definition
    /// variable evaluation by ${variablename} or $variablename
    /// the last version only for variablenames without whitespaces
    std::string variableReplacement(const std::string& input) const;
633

Praetorius, Simon's avatar
Praetorius, Simon committed
634
    std::string variableEvaluation(const std::string& input) const;
635

636
    /** Fill the initfile from an input stream.
637 638 639 640 641
    * @param in: the stream to fill the data from.
    * Current dataformat: tag:value
    * Comment char: percent '%'
    * Include files: #include "filename" or #include <filename>
    */
642 643 644
    void read(std::istream& in);

    /// Fill the initfile from a file with filename fn
645
    void read(std::string fn, bool force = false);
646

647
    /** Write data-map to initfile
648 649
    * @param out: the stream to fill the data in.
    */
650
    void write(std::ostream& out);
Praetorius, Simon's avatar
Praetorius, Simon committed
651

652 653
    /// Write data-map to initfile with filename fn
    void write(std::string fn);
654

655 656
    /// read parameters for msgInfo, msgWait, paramInfo
    void getInternalParameters();
657

658
    int msgInfo, msgWait, paramInfo, breakOnMissingTag;
659 660
    
//     mutable boost::shared_mutex mutex_;
661
  };
662

663
  typedef Initfile Parameters;
664

665
} // end namespace AMDiS
Praetorius, Simon's avatar
Praetorius, Simon committed
666
#endif