18 #include "TObjString.h"
21 #include "yaml-cpp/yaml.h"
46 template<
typename T,
typename... Args>
58 template<
typename... Args>
75 template<
typename T,
typename... Args>
83 return FindFromManagerHelper<T>(node[key], args...);
87 template<
typename T,
typename... Args>
90 return FindFromManagerHelper<T>(node, args...);
99 return YAML::Load(yaml_string);
100 }
catch (
const YAML::ParserException& e) {
113 YAML::Emitter emitter;
115 return emitter.c_str();
124 std::stringstream ss;
127 TList* linesList = macro.GetListOfLines();
133 TIter nextLine(linesList);
134 TObject *obj =
nullptr;
135 while ((obj = nextLine())) {
136 TObjString* line =
dynamic_cast<TObjString*
>(obj);
141 ss << line->GetString() << std::endl;
166 inline TMacro
YAMLtoTMacro(
const YAML::Node& yaml_node,
const std::string& name) {
172 TMacro macro(name.c_str(), name.c_str());
173 macro.AddLine(macro_string.c_str());
185 inline bool compareYAMLNodes(
const YAML::Node& node1,
const YAML::Node& node2,
bool Mute =
false) {
188 if (node1.Type() != node2.Type()) {
193 if (node1.IsScalar() && node2.IsScalar()) {
194 auto v1 = node1.as<std::string>();
195 auto v2 = node2.as<std::string>();
197 if (!Mute)
MACH3LOG_WARN(
"Scalar value mismatch: '{}' != '{}'", v1, v2);
204 if (node1.IsSequence() && node2.IsSequence()) {
205 if (node1.size() != node2.size()) {
208 for (std::size_t i = 0; i < node1.size(); ++i) {
217 if (node1.IsMap() && node2.IsMap()) {
218 if (node1.size() != node2.size()) {
220 node1.size(), node2.size());
223 for (
auto it1 = node1.begin(); it1 != node1.end(); ++it1) {
224 auto key = it1->first.as<std::string>();
238 if (!Mute)
MACH3LOG_WARN(
"Unsupported YAML node type encountered");
247 template <
typename TValue>
267 template <
typename... Args>
278 char* demangledName = abi::__cxa_demangle(mangledName.c_str(),
nullptr,
nullptr, &status);
279 std::string result = (status == 0) ? demangledName : mangledName;
289 template<
typename Type>
290 Type
Get(
const YAML::Node& node,
const std::string& File,
const int Line) {
294 return node.as<Type>();
295 }
catch (
const YAML::BadConversion& e) {
298 MACH3LOG_ERROR(
"While trying to access variable \"{}\"", nodeAsString);
299 if (!node || node.IsNull()) {
300 MACH3LOG_ERROR(
"Requested node does not exist (null or undefined)");
303 }
catch (
const YAML::InvalidNode& e) {
304 std::string key =
"<unknown>";
305 const std::string msg = e.what();
306 const std::string search =
"first invalid key: \"";
307 auto start = msg.find(search);
308 if (start != std::string::npos) {
309 start += search.length();
310 auto end = msg.find(
"\"", start);
311 if (end != std::string::npos) {
312 key = msg.substr(start, end - start);
327 template<
typename Type>
328 Type
GetFromManager(
const YAML::Node& node,
const Type defval,
const std::string& File,
const int Line) {
335 return node.as<Type>();
336 }
catch (
const YAML::BadConversion& e) {
339 MACH3LOG_ERROR(
"While trying to access variable {}", nodeAsString);
355 inline YAML::Node
LoadYamlConfig(
const std::string& filename,
const std::string& File,
const int Line) {
358 if (!(filename.length() >= 5 && filename.compare(filename.length() - 5, 5,
".yaml") == 0) &&
359 !(filename.length() >= 4 && filename.compare(filename.length() - 4, 4,
".yml") == 0)) {
366 YAML::Node yamlNode = YAML::LoadFile(filename);
369 std::ifstream fileStream(filename);
370 std::stringstream buffer;
371 buffer << fileStream.rdbuf();
372 std::string fileContent = buffer.str();
376 std::regex linePattern(R
"(^\s*-(?![\d\s\.]).*)");
378 std::istringstream contentStream(fileContent);
379 std::string lineconfig;
383 int flowListDepth = 0;
385 while (std::getline(contentStream, lineconfig)) {
387 if (lineconfig.find(
"---") != std::string::npos) {
393 for (
char c : lineconfig) {
394 if (c ==
'[') flowListDepth++;
395 else if (c ==
']') flowListDepth = std::max(0, flowListDepth - 1);
399 if (flowListDepth == 0 && std::regex_match(lineconfig, linePattern)) {
400 MACH3LOG_ERROR(
"Warning: Missing space or tab after '-' at line {}: {}\n", lineNumber, lineconfig);
407 }
catch (
const std::exception& e) {
418 inline const YAML::Node &
Cnode(
const YAML::Node &n) {
434 inline YAML::Node
MergeNodes(
const YAML::Node& a,
const YAML::Node& b) {
438 return b.IsNull() ? a : b;
449 auto c = YAML::Node(YAML::NodeType::Map);
451 if (n.first.IsScalar()) {
452 const std::string & key = n.first.Scalar();
453 auto t = (
Cnode(b)[key]);
459 c[n.first] = n.second;
463 if (!n.first.IsScalar() || !
Cnode(c)[n.first.Scalar()]) {
464 c[n.first] = n.second;
483 inline std::vector<double>
ParseBounds(
const YAML::Node& node,
const std::string& File,
const int Line) {
485 std::vector<double> bounds;
487 if (!node || !node.IsSequence() || (node.size() != 1 && node.size() != 2)) {
488 MACH3LOG_ERROR(
"Bounds must be a sequence of 1 or 2 elements, got size {}",
static_cast<int>(node.size()));
492 if (node.size() == 1) {
493 const YAML::Node& val = node[0];
495 if (!val || val.IsNull() || (val.IsScalar() && val.Scalar().empty())) {
496 MACH3LOG_ERROR(
"Single-element bounds must contain a valid numeric value.");
498 }
else if (val.IsScalar()) {
500 const double center = val.as<
double>();
501 if (std::floor(center) != center) {
502 MACH3LOG_ERROR(
"Invalid center value in Bounds[0]: '{}'. Expected an integer (e.g., 0 or 1).",
static_cast<std::string
>(val.Scalar()));
505 bounds = {center - 0.5, center + 0.5};
506 }
catch (
const YAML::BadConversion& e) {
507 MACH3LOG_ERROR(
"Invalid value in Bounds[0]: '{}'. Expected a number.",
static_cast<std::string
>(val.Scalar()));
515 for (std::size_t i = 0; i < 2; ++i) {
516 const YAML::Node& val = node[i];
518 if (!val || val.IsNull() || (val.IsScalar() && val.Scalar().empty())) {
520 }
else if (val.IsScalar()) {
522 bounds.push_back(val.as<
double>());
523 }
catch (
const YAML::BadConversion& e) {
524 MACH3LOG_ERROR(
"Invalid value in Bounds[{}]: '{}'. Expected a number or empty string.",
525 static_cast<int>(i),
static_cast<std::string
>(val.Scalar()));
529 MACH3LOG_ERROR(
"Invalid type in Bounds[{}]",
static_cast<int>(i));
567 inline std::vector<std::vector<double>>
Parse2DBounds(
const YAML::Node& node,
const std::string& File,
const int Line) {
569 std::vector<std::vector<double>> bounds;
570 if (!node || !node.IsSequence()) {
576 if (node.size() == 2 && node[0].IsScalar()) {
579 for (std::size_t i = 0; i < node.size(); ++i) {
580 const YAML::Node& subnode = node[i];
581 bounds.push_back(
ParseBounds(subnode, File, Line));
589 #define M3OpenConfig(filename) LoadYamlConfig((filename), __FILE__, __LINE__)
590 #define GetBounds(filename) ParseBounds((filename), __FILE__, __LINE__)
591 #define Get2DBounds(filename) Parse2DBounds((filename), __FILE__, __LINE__)
KS: Core MaCh3 definitions and compile-time configuration utilities.
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
Defines the custom exception class used throughout MaCh3.
void OverrideConfig(YAML::Node node, std::string const &key, TValue val)
Overrides the configuration settings based on provided arguments.
T FindFromManagerHelper(const YAML::Node &node)
T FindFromManager(const YAML::Node &node, Args... args)
Wrapper function to call the recursive helper.
TMacro YAMLtoTMacro(const YAML::Node &yaml_node, const std::string &name)
Convert a YAML node to a ROOT TMacro object.
bool CheckNodeExistsHelper([[maybe_unused]] const T &node)
Use this like this CheckNodeExists(config, "LikelihoodOptions", "TestStatistic"); KS: Base case for r...
YAML::Node STRINGtoYAML(const std::string &yaml_string)
Function to convert a YAML string to a YAML node.
std::string DemangleTypeName(const std::string &mangledName)
Function to demangle type names.
std::vector< double > ParseBounds(const YAML::Node &node, const std::string &File, const int Line)
KS: Get bounds from YAML for example for selection cuts.
YAML::Node LoadYamlConfig(const std::string &filename, const std::string &File, const int Line)
Open YAML file.
YAML::Node TMacroToYAML(const TMacro ¯o)
KS: Convert a ROOT TMacro object to a YAML node.
std::vector< std::vector< double > > Parse2DBounds(const YAML::Node &node, const std::string &File, const int Line)
KS: Get 2D bounds from YAML for example for selection cuts.
std::string TMacroToString(const TMacro ¯o)
KS: Convert a ROOT TMacro object to a string representation.
Type GetFromManager(const YAML::Node &node, const Type defval, const std::string &File, const int Line)
Get content of config file if node is not found take default value specified.
YAML::Node MergeNodes(const YAML::Node &a, const YAML::Node &b)
KS: Recursively merges two YAML nodes.
std::string YAMLtoSTRING(const YAML::Node &node)
KS: Convert a YAML node to a string representation.
bool compareYAMLNodes(const YAML::Node &node1, const YAML::Node &node2, bool Mute=false)
Compare if yaml nodes are identical.
Type Get(const YAML::Node &node, const std::string &File, const int Line)
Get content of config file.
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
const YAML::Node & Cnode(const YAML::Node &n)
KS: Convenience wrapper to return a YAML node as-is.
Custom exception class used throughout MaCh3.
constexpr static const double KinematicLowBound
When parameter has no bound this serves as it. Lowest possible value the system.
constexpr static const double KinematicUpBound
When parameter has no bound this serves as it. Highest possible value the system.