file src/yaml_parser_base.cpp
[No description available] More…
Namespaces
Name |
---|
Gambit TODO: see if we can use this one: |
Gambit::IniParser |
Detailed Description
Author:
- Christoph Weniger (c.weniger@uva.nl)
- Pat Scott (patscott@physics.mcgill.ca)
- Tomas Gonzalo (tomas.gonzalo@monash.edu)
Date:
- 2013 May, June, July
- 2014 Mar
- 2020 June
Base class for ini-file parsers using yaml-cpp
Authors (add name and date if you modify):
Source code
// GAMBIT: Global and Modular BSM Inference Tool
// *********************************************
/// \file
///
/// Base class for ini-file parsers using yaml-cpp
///
/// *********************************************
///
/// Authors (add name and date if you modify):
///
/// \author Christoph Weniger
/// (c.weniger@uva.nl)
/// \date 2013 May, June, July
///
/// \author Pat Scott
/// (patscott@physics.mcgill.ca)
/// \date 2014 Mar
///
/// \author Tomas Gonzalo
/// (tomas.gonzalo@monash.edu)
/// \date 2020 June
///
/// *********************************************
#include <iostream>
#include "gambit/Utils/yaml_parser_base.hpp"
#include "gambit/Utils/util_functions.hpp"
#include "gambit/Utils/mpiwrapper.hpp"
#include "gambit/Logs/logger.hpp"
#include "gambit/Logs/logmaster.hpp"
namespace Gambit
{
namespace IniParser
{
/// Recursive import
int importRound(YAML::Node node, const std::string& filename)
{
int counter = 0;
if (node.IsScalar())
{
if ( node.Tag() == "!import" )
{
#ifdef WITH_MPI
int rank = GMPI::Comm().Get_rank();
#else
int rank = 0;
#endif
YAML::Node import;
std::string new_filename = node.as<std::string>();
if (rank == 0) std::cout << "Importing: " << new_filename << std::endl;
try
{
// We want to do the import relative to the path in which the YAML file
// sits, unless it has a forward slash at the beginning (in which case we
// will interpret it as an absolute path)
std::string file_location = Utils::dir_name(filename); // "outer" file location
if(new_filename.at(0)=='/') // POSIX
{
// Use the absolute path as given
import = YAML::LoadFile(new_filename);
}
else
{
// Append the path of the outer file
new_filename = file_location+"/"+new_filename;
import = YAML::LoadFile(new_filename);
}
}
catch (YAML::Exception &e)
{
std::ostringstream msg;
msg << "Error importing \""<<new_filename<<"\"! ";
msg << "Please check that file exists! Error occurred during parsing of YAML file '"<<filename<<"'" << endl;
msg << "(yaml-cpp error: "<<e.what()<<" )";
inifile_error().raise(LOCAL_INFO,msg.str());
}
node = import;
return 1;
}
return 0;
}
if (node.IsSequence())
{
for (unsigned int i = 0; i<node.size(); ++i)
{
counter += importRound(node[i],filename);
}
return counter;
}
if (node.IsMap())
{
for (YAML::const_iterator it = node.begin(); it != node.end(); ++it)
{
counter += importRound(it->second,filename); // Only values are processed
}
return counter;
}
return 0;
}
void recursiveImport(const YAML::Node& node, const std::string& filename)
{
int last_import_counter = 0;
for ( int i = 0; i < 10; ++i)
{
last_import_counter = importRound(node,filename);
}
if (last_import_counter > 0)
{
#ifdef WITH_MPI
int rank = GMPI::Comm().Get_rank();
#else
int rank = 0;
#endif
if (rank == 0)
{
std::cout << last_import_counter << std::endl;
std::cout << "WARNING: YAML imports were truncated after 10 recursions." << std::endl;
}
}
}
// Implementations of main inifile class
void Parser::readFile(std::string filename)
{
YAML::Node root = filename_to_node(filename);
basicParse(root,filename);
}
YAML::Node Parser::filename_to_node(std::string filename)
{
YAML::Node root;
// Read inifile file
try
{
root = YAML::LoadFile(filename);
}
catch (YAML::Exception &e)
{
std::ostringstream msg;
msg << "Error reading Inifile \""<<filename<<"\"! ";
msg << "Please check that file exist!" << endl;
msg << "(yaml-cpp error: "<<e.what()<<" )";
inifile_error().raise(LOCAL_INFO,msg.str());
}
return root;
}
void Parser::basicParse(YAML::Node root, std::string filename)
{
recursiveImport(root,filename);
YAMLNode = root;
parametersNode = root["Parameters"];
priorsNode = root["Priors"];
printerNode = root["Printer"];
scannerNode = root["Scanner"];
logNode = root["Logger"];
keyValuePairNode = root["KeyValues"];
// Set default output path
std::string defpath;
if(hasKey("default_output_path"))
{
defpath = getValue<std::string>("default_output_path");
}
else
{
// Assign a default default (;)) path based on the yaml file name
// Ridiculously we have to parse manually in C++ since no
// standard library tools for doing this exist...
// Assumes that file extension has only one dot, or that
// there is no file extension. Should work anyway if more
// dots, will just get a directory name with a dot in it.
size_t fname_start = filename.find_last_of("/\\");
size_t fname_end = filename.find_last_of(".");
str fname = filename.substr(fname_start+1,fname_end);
defpath = "runs/" + fname + "/";
}
scannerNode["default_output_path"] = Utils::ensure_path_exists(defpath+"/scanner_plugins/");
logNode ["default_output_path"] = Utils::ensure_path_exists(defpath+"/logs/");
printerNode["options"]["default_output_path"] = Utils::ensure_path_exists(defpath+"/samples/");
// Make a copy of yaml file in output dir
str new_filename = defpath+'/'+Utils::base_name(filename);
bool replace_yaml_file = getValueOrDef<bool>(true, "replace_yaml_file");
printNode(root,new_filename,replace_yaml_file);
// Postprocessor is currently incompatible with 'print_timing_data', so need to pass this option on for checking
scannerNode["print_timing_data"] = getValueOrDef<bool>(false,"print_timing_data");
// Pass on minimum recognised lnlike and offset to Scanner
scannerNode["model_invalid_for_lnlike_below"] = getValueOrDef<double>(0.9*std::numeric_limits<double>::lowest(), "likelihood", "model_invalid_for_lnlike_below");
if (hasKey("likelihood", "lnlike_offset"))
scannerNode["lnlike_offset"] = getValue<double>("likelihood", "lnlike_offset");
// Set fatality of exceptions
if (hasKey("exceptions"))
{
// Iterate over the map of all recognised exception objects
std::map<const char*,exception*>::const_iterator iter;
for (iter = exception::all_exceptions().begin(); iter != exception::all_exceptions().end(); ++iter)
{
// Check if the exception has an entry in the YAML file
if (hasKey("exceptions",iter->first))
{
// Retrieve the entry and set the exception's 'fatal' flag accordingly.
str value = getValue<str>("exceptions",iter->first);
if (value == "fatal")
{
iter->second->set_fatal(true);
}
else if (value == "non-fatal")
{
iter->second->set_fatal(false);
}
else
{
str error_msg = "Unrecognised entry \"" + value + "\" for exceptions key \"" + iter->first + "\" in input file.";
inifile_error().raise(LOCAL_INFO,error_msg);
}
}
}
}
// Parse the logging setup node, and initialise the LogMaster object
std::string prefix;
if(logNode["prefix"])
{
prefix = logNode["prefix"].as<std::string>();
}
else
{
prefix = logNode["default_output_path"].as<std::string>()+"/";
}
// map storing info used to set up logger objects
std::map<std::set<std::string>,std::string> loggerinfo;
if(logNode["redirection"])
{
YAML::Node redir = logNode["redirection"];
for(YAML::const_iterator it=redir.begin(); it!=redir.end(); ++it)
{
std::set<std::string> tags;
std::string filename;
// Iterate through tags and add them to the set
YAML::Node yamltags = it->first;
for(YAML::const_iterator it2=yamltags.begin();it2!=yamltags.end();++it2)
{
tags.insert( it2->as<std::string>() );
}
filename = (it->second).as<std::string>();
// Add entry to the loggerinfo map
if((filename=="stdout") or (filename=="stderr"))
{
// Special cases to trigger redirection to standard output streams
loggerinfo[tags] = filename;
}
else
{
// The logger won't be able to create the log files if the prefix
// directory doesn't exist, so let us now make sure that it does
loggerinfo[tags] = Utils::ensure_path_exists(prefix + filename);
}
}
}
else
{
// Use default log file only
std::set<std::string> tags;
std::string filename;
tags.insert("Default");
filename = "default.log";
loggerinfo[tags] = Utils::ensure_path_exists(prefix + filename);
}
// Initialise global LogMaster object
bool master_debug = (keyValuePairNode["debug"]) ? keyValuePairNode["debug"].as<bool>() : false;
bool logger_debug = (logNode["debug"]) ? logNode["debug"].as<bool>() : false;
logger().set_log_debug_messages(master_debug or logger_debug);
logger().initialise(loggerinfo);
// Parse the Parameters node and expand out some shorthand syntax
// e.g.
// model1
// param1: 5.678
// expands to
// model1
// param1:
// fixed_value: 5.678
// Parameter must have no entries besides the value for this syntax to be valid
}
/// Print a yaml node to file
void Parser::printNode(YAML::Node node, str filename, bool replace_yaml_file)
{
#ifdef WITH_MPI
int rank = GMPI::Comm().Get_rank();
#else
int rank = 0;
#endif
if (rank == 0)
{
if(not Utils::file_exists(filename) or replace_yaml_file)
{
std::ofstream fout(filename);
fout << node;
}
}
}
/// Getter for the full YAML Node
YAML::Node Parser::getYAMLNode() const {return YAMLNode;}
/// Getters for key/value section
/// @{
YAML::Node Parser::getParametersNode() const {return parametersNode;}
YAML::Node Parser::getPriorsNode() const {return priorsNode;}
YAML::Node Parser::getPrinterNode() const {return printerNode;}
YAML::Node Parser::getScannerNode() const {return scannerNode;}
YAML::Node Parser::getLoggerNode() const {return logNode;}
YAML::Node Parser::getKeyValuePairNode() const {return keyValuePairNode;}
/// @}
/// Getters for model/parameter section
/// @{
bool Parser::hasModelParameterEntry(std::string model, std::string param, std::string key) const
{
return parametersNode[model][param][key];
}
/// Return list of model names (without "adhoc" model!)
const std::set<str> Parser::getModelNames() const
{
std::set<str> result;
for (YAML::const_iterator it = parametersNode.begin(); it!=parametersNode.end(); ++it)
{
if (it->first.as<std::string>() != "adhoc")
result.insert( it->first.as<std::string>() );
}
return result;
}
const std::vector<std::string> Parser::getModelParameters(std::string model) const
{
std::vector<std::string> result;
if (parametersNode[model])
{
for (YAML::const_iterator it = parametersNode[model].begin(); it!=parametersNode[model].end(); ++it)
{
result.push_back( it->first.as<std::string>() );
}
}
return result;
}
/// Getter for options
const Options Parser::getOptions(std::string key) const
{
if (hasKey(key, "options"))
{
return Options(keyValuePairNode[key]["options"]);
}
else
{
return Options(keyValuePairNode[key]);
}
}
/// @}
}
}
Updated on 2024-07-18 at 13:53:32 +0000