18 #include "TObjString.h"
21 #include "yaml-cpp/yaml.h"
47 template<
typename T,
typename... Args>
59 template<
typename... Args>
76 template<
typename T,
typename... Args>
84 return FindFromManagerHelper<T>(node[key], args...);
88 template<
typename T,
typename... Args>
91 return FindFromManagerHelper<T>(node, args...);
100 return YAML::Load(yaml_string);
101 }
catch (
const YAML::ParserException& e) {
114 YAML::Emitter emitter;
116 return emitter.c_str();
125 std::stringstream ss;
128 TList* linesList = macro.GetListOfLines();
134 TIter nextLine(linesList);
135 TObject *obj =
nullptr;
136 while ((obj = nextLine())) {
137 TObjString* line =
dynamic_cast<TObjString*
>(obj);
142 ss << line->GetString() << std::endl;
167 inline TMacro
YAMLtoTMacro(
const YAML::Node& yaml_node,
const std::string& name) {
173 TMacro macro(name.c_str(), name.c_str());
174 macro.AddLine(macro_string.c_str());
186 inline bool compareYAMLNodes(
const YAML::Node& node1,
const YAML::Node& node2,
bool Mute =
false) {
189 if (node1.Type() != node2.Type()) {
194 if (node1.IsScalar() && node2.IsScalar()) {
195 auto v1 = node1.as<std::string>();
196 auto v2 = node2.as<std::string>();
198 if (!Mute)
MACH3LOG_WARN(
"Scalar value mismatch: '{}' != '{}'", v1, v2);
205 if (node1.IsSequence() && node2.IsSequence()) {
206 if (node1.size() != node2.size()) {
209 for (std::size_t i = 0; i < node1.size(); ++i) {
218 if (node1.IsMap() && node2.IsMap()) {
219 if (node1.size() != node2.size()) {
221 node1.size(), node2.size());
224 for (
auto it1 = node1.begin(); it1 != node1.end(); ++it1) {
225 auto key = it1->first.as<std::string>();
239 if (!Mute)
MACH3LOG_WARN(
"Unsupported YAML node type encountered");
248 template <
typename TValue>
268 template <
typename... Args>
279 char* demangledName = abi::__cxa_demangle(mangledName.c_str(),
nullptr,
nullptr, &status);
280 std::string result = (status == 0) ? demangledName : mangledName;
290 template<
typename Type>
291 Type
Get(
const YAML::Node& node,
const std::string& File,
const int Line) {
295 return node.as<Type>();
296 }
catch (
const YAML::BadConversion& e) {
299 MACH3LOG_ERROR(
"While trying to access variable \"{}\"", nodeAsString);
300 if (!node || node.IsNull()) {
301 MACH3LOG_ERROR(
"Requested node does not exist (null or undefined)");
304 }
catch (
const YAML::InvalidNode& e) {
305 std::string key =
"<unknown>";
306 const std::string msg = e.what();
307 const std::string search =
"first invalid key: \"";
308 auto start = msg.find(search);
309 if (start != std::string::npos) {
310 start += search.length();
311 auto end = msg.find(
"\"", start);
312 if (end != std::string::npos) {
313 key = msg.substr(start, end - start);
328 template<
typename Type>
329 Type
GetFromManager(
const YAML::Node& node,
const Type defval,
const std::string& File,
const int Line) {
336 return node.as<Type>();
337 }
catch (
const YAML::BadConversion& e) {
340 MACH3LOG_ERROR(
"While trying to access variable {}", nodeAsString);
356 inline YAML::Node
LoadYamlConfig(
const std::string& filename,
const std::string& File,
const int Line) {
359 if (!(filename.length() >= 5 && filename.compare(filename.length() - 5, 5,
".yaml") == 0) &&
360 !(filename.length() >= 4 && filename.compare(filename.length() - 4, 4,
".yml") == 0)) {
367 YAML::Node yamlNode = YAML::LoadFile(filename);
370 std::ifstream fileStream(filename);
371 std::stringstream buffer;
372 buffer << fileStream.rdbuf();
373 std::string fileContent = buffer.str();
377 std::regex linePattern(R
"(^\s*-(?![\d\s\.]).*)");
379 std::istringstream contentStream(fileContent);
380 std::string lineconfig;
384 int flowListDepth = 0;
386 while (std::getline(contentStream, lineconfig)) {
388 if (lineconfig.find(
"---") != std::string::npos) {
394 for (
char c : lineconfig) {
395 if (c ==
'[') flowListDepth++;
396 else if (c ==
']') flowListDepth = std::max(0, flowListDepth - 1);
400 if (flowListDepth == 0 && std::regex_match(lineconfig, linePattern)) {
401 MACH3LOG_ERROR(
"Warning: Missing space or tab after '-' at line {}: {}\n", lineNumber, lineconfig);
408 }
catch (
const std::exception& e) {
419 inline const YAML::Node &
Cnode(
const YAML::Node &n) {
435 inline YAML::Node
MergeNodes(
const YAML::Node& a,
const YAML::Node& b) {
439 return b.IsNull() ? a : b;
450 auto c = YAML::Node(YAML::NodeType::Map);
452 if (n.first.IsScalar()) {
453 const std::string & key = n.first.Scalar();
454 auto t = (
Cnode(b)[key]);
460 c[n.first] = n.second;
464 if (!n.first.IsScalar() || !
Cnode(c)[n.first.Scalar()]) {
465 c[n.first] = n.second;
484 inline std::vector<double>
ParseBounds(
const YAML::Node& node,
const std::string& File,
const int Line) {
486 std::vector<double> bounds;
488 if (!node || !node.IsSequence() || (node.size() != 1 && node.size() != 2)) {
489 MACH3LOG_ERROR(
"Bounds must be a sequence of 1 or 2 elements, got size {}",
static_cast<int>(node.size()));
493 if (node.size() == 1) {
494 const YAML::Node& val = node[0];
496 if (!val || val.IsNull() || (val.IsScalar() && val.Scalar().empty())) {
497 MACH3LOG_ERROR(
"Single-element bounds must contain a valid numeric value.");
499 }
else if (val.IsScalar()) {
501 const double center = val.as<
double>();
502 if (std::floor(center) != center) {
503 MACH3LOG_ERROR(
"Invalid center value in Bounds[0]: '{}'. Expected an integer (e.g., 0 or 1).",
static_cast<std::string
>(val.Scalar()));
506 bounds = {center - 0.5, center + 0.5};
507 }
catch (
const YAML::BadConversion& e) {
508 MACH3LOG_ERROR(
"Invalid value in Bounds[0]: '{}'. Expected a number.",
static_cast<std::string
>(val.Scalar()));
516 for (std::size_t i = 0; i < 2; ++i) {
517 const YAML::Node& val = node[i];
519 if (!val || val.IsNull() || (val.IsScalar() && val.Scalar().empty())) {
521 }
else if (val.IsScalar()) {
523 bounds.push_back(val.as<
double>());
524 }
catch (
const YAML::BadConversion& e) {
525 MACH3LOG_ERROR(
"Invalid value in Bounds[{}]: '{}'. Expected a number or empty string.",
526 static_cast<int>(i),
static_cast<std::string
>(val.Scalar()));
530 MACH3LOG_ERROR(
"Invalid type in Bounds[{}]",
static_cast<int>(i));
568 inline std::vector<std::vector<double>>
Parse2DBounds(
const YAML::Node& node,
const std::string& File,
const int Line) {
570 std::vector<std::vector<double>> bounds;
571 if (!node || !node.IsSequence()) {
577 if (node.size() == 2 && node[0].IsScalar()) {
580 for (std::size_t i = 0; i < node.size(); ++i) {
581 const YAML::Node& subnode = node[i];
582 bounds.push_back(
ParseBounds(subnode, File, Line));
590 #define M3OpenConfig(filename) LoadYamlConfig((filename), __FILE__, __LINE__)
591 #define GetBounds(filename) ParseBounds((filename), __FILE__, __LINE__)
592 #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(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.