5bool is_ignored_metadata_block(std::string_view name) {
9 return name ==
"SPINFO" ||
name ==
"spinfo" ||
10 name ==
"DCINFO" ||
name ==
"dcinfo";
15void LhaParser::addBlock(std::map<
BlockName, std::shared_ptr<LhaBlock>>& blocks,
const BlockName&
id,
const std::vector<std::vector<std::string>>& lines)
const {
16 auto block = std::make_shared<LhaBlock>(findPrototype(
id));
18 block->readData(lines);
19 BlockName id_ci = block->getPrototype().blockName;
21 blocks.insert(std::pair(id_ci, std::move(block)));
24std::vector<Token> LhaParser::tokenize(
const std::string &src)
const {
28 auto rit = std::sregex_iterator(src.begin(), src.end(),
analyzer_rx);
29 auto rend = std::sregex_iterator();
31 std::vector<Token> tokens;
34 size_t group_index = m.size();
36 for (
size_t idx = 1; idx < m.size(); ++idx) {
38 group_index = idx - 1;
43 auto tokenType =
static_cast<TokenType>(group_index);
44 auto value = m[group_index + 1].str();
50 tokens.emplace_back(
Token{tokenType, value, cLine, cCol});
61std::map<BlockName, std::vector<std::vector<std::string>>>
62LhaParser::parse_tokens(std::vector<Token> tokens,
bool comments)
const
64 bool newBlock =
false;
65 bool hasGlobalScale =
false;
67 bool skipBlock =
false;
74 std::map<BlockName, std::vector<std::vector<std::string>>> rawBlocks;
75 auto current_block_it = rawBlocks.end();
77 for (
const Token& t : tokens) {
79 auto prototype = this->findPrototype(t.value);
80 if (prototype.blockName !=
"") {
81 LOG_DEBUG(
"LHA reader: Block ", prototype.blockName,
" found.");
83 cBlock = prototype.blockName;
84 hasGlobalScale= prototype.globalScale;
91 rawBlocks.emplace(cBlock, std::vector<std::vector<std::string>>{});
92 current_block_it = it;
95 LOG_DEBUG(
"LHA reader: Decay block found. Skipping.");
97 hasGlobalScale =
false;
98 current_block_it = rawBlocks.end();
102 if (is_ignored_metadata_block(t.value)) {
103 LOG_DEBUG(
"LHA reader: Metadata block ", t.value,
" skipped.");
105 LOG_WARN(
"LHA reader: Unknown block " + t.value +
" encountered. Skipping.");
108 hasGlobalScale =
false;
109 current_block_it = rawBlocks.end();
124 isQ = (t.value ==
"Q=" || t.value ==
"q=");
126 else if (!skipBlock && current_block_it != rawBlocks.end()) {
132 LOG_VERBOSE(
"Token : [",
static_cast<int>(t.type),
", ", t.value,
"]");
133 auto& rows = current_block_it->second;
136 rows.emplace_back(std::vector<std::string>{});
138 rows.back().emplace_back(!globalQ.empty() ? globalQ :
"-1");
140 rows.back().emplace_back(t.value);
150 for (
const auto& p : blockPrototypes) {
152 if (p.blockName == name)
159 std::ifstream file(input_file.data());
160 std::stringstream buffer;
161 buffer << file.rdbuf();
162 return this->
parse(buffer.str());
166 auto rawBlocks = this->parse_tokens(this->tokenize(src));
167 std::map<BlockName, std::shared_ptr<LhaBlock>> blocks;
168 for (
auto &[
id, lines] : rawBlocks) {
169 addBlock(blocks,
id, lines);
173 return this->toDBNode(blocks);
179 this->blockPrototypes = prototypes;
183std::shared_ptr<DBNode> LhaParser::toDBNode(std::map<
BlockName, std::shared_ptr<LhaBlock>> blocks)
const {
186 for (
const auto& [blockName, blockPtr] : blocks) {
187 auto block_node = blockPtr->toDBNode();
189 auto group = block_node->
getGroup({blockName});
192 if (block_node->contains(
"scale")) {
193 auto sval = block_node->get(
"scale");
194 if (std::holds_alternative<double>(sval)) {
195 root.
set(std::get<double>(sval), blockName,
"scale");
196 }
else if (std::holds_alternative<int>(sval)) {
197 root.
set(
static_cast<double>(std::get<int>(sval)), blockName,
"scale");
199 LOG_WARN(
"Expected numeric 'scale' for block ", blockName,
" but got variant index ", sval.index());
204 return std::make_shared<DBNode>(root);
207static double value_to_double(
const DBNode::Value& v,
double fallback = 0.0)
209 if (std::holds_alternative<double>(v))
return std::get<double>(v);
210 if (std::holds_alternative<int>(v))
return static_cast<double>(std::get<int>(v));
216 return std::holds_alternative<std::shared_ptr<DBNode>>(
v);
219static std::shared_ptr<DBNode> value_to_node_ptr(
const DBNode::Value& v)
221 if (!value_is_node_ptr(v))
return nullptr;
222 return std::get<std::shared_ptr<DBNode>>(
v);
225static std::vector<std::string> split_tokens(
const std::string& s)
228 std::replace(tmp.begin(), tmp.end(),
'_',
' ');
229 std::istringstream iss(tmp);
231 std::vector<std::string> out;
233 while (iss >> tok) out.push_back(tok);
237static std::vector<std::string> split_ws(
const std::string& s)
239 std::istringstream iss(s);
240 std::vector<std::string> out;
242 while (iss >> tok) out.push_back(tok);
246static bool read_ew_scale_Q(
const std::shared_ptr<DBNode>& root,
double& Q_out)
248 if (!root || !root->contains(
BlockName(
"EW_SCALE")))
return false;
250 auto g = root->getGroup({
BlockName(
"EW_SCALE")});
254 auto node = value_to_node_ptr(it->second);
255 if (node && node->contains(
"central_value")) {
256 Q_out = value_to_double(node->get(
"central_value"), 0.0);
261 for (
const auto& kv :
g) {
262 auto node = value_to_node_ptr(kv.second);
263 if (node && node->contains(
"central_value")) {
264 Q_out = value_to_double(node->get(
"central_value"), 0.0);
272static bool starts_with(
const std::string& s,
const std::string& pre)
274 return s.size() >= pre.size() &&
s.compare(0, pre.size(), pre) == 0;
277static std::map<BlockName, DBNode::Value>
278build_fwcoef_group_from_ewscale_blocks(
const std::shared_ptr<DBNode>& root,
279 const std::vector<std::string>& prefixes,
280 bool& usedExternalBlocks)
282 usedExternalBlocks =
false;
283 std::map<BlockName, DBNode::Value> out;
285 if (!root)
return out;
287 for (
const auto& k : root->
get_keys()) {
288 std::ostringstream oss; oss << k;
289 const std::string ks = oss.str();
291 if (!
ends_with(ks,
"_EW_SCALE"))
continue;
293 bool okPrefix =
false;
294 for (
const auto& pre : prefixes) {
295 if (starts_with(ks, pre)) { okPrefix =
true;
break; }
297 if (!okPrefix)
continue;
299 usedExternalBlocks =
true;
301 auto g = root->getGroup({k});
302 for (
const auto& kv :
g) {
303 if (kv.first ==
BlockName(
"scale"))
continue;
304 out[kv.first] = kv.second;
311static std::string blockname_primary(
const BlockName& bn)
313 std::ostringstream oss;
318static const Prototype* find_proto(
const std::unordered_set<Prototype>& protos,
const BlockName& name)
320 for (
const auto& p : protos) {
321 if (p.blockName == name)
return &p;
326static void write_block_header(std::ostream& os,
const Prototype& proto,
double Q,
bool hasQ)
328 os <<
"BLOCK " << blockname_primary(proto.
blockName);
330 os <<
" Q= " << std::setprecision(8) << std::scientific << Q;
335static void write_value(std::ostream& os,
double v)
337 os << std::setprecision(8) << std::scientific <<
v;
340static std::string pad_left_zeros(std::string s,
size_t width)
342 if (!
s.empty() && std::all_of(
s.begin(),
s.end(), [](
unsigned char c){ return std::isdigit(c); })) {
343 if (
s.size() < width)
s = std::string(width -
s.size(),
'0') +
s;
348static void write_fwcoef_like(std::ostream& os,
350 const std::map<BlockName, DBNode::Value>& group,
351 double Q_from_ewscale,
352 bool hasQ_from_ewscale)
354 write_block_header(os, proto, Q_from_ewscale, hasQ_from_ewscale);
355 os <<
"#id order M value comment\n";
357 for (
const auto& kv : group) {
358 if (kv.first ==
BlockName(
"scale"))
continue;
360 auto node = value_to_node_ptr(kv.second);
361 if (!node || !node->contains(
"central_value"))
continue;
363 const double val = value_to_double(node->get(
"central_value"), 0.0);
365 const auto toks = split_tokens(blockname_primary(kv.first));
366 if (toks.size() < 4)
continue;
368 std::string id1 = toks[0];
369 std::string id2 = toks[1];
370 std::string ord = toks[2];
371 std::string cty = toks[3];
373 if (id1.size() == 7) id1 = pad_left_zeros(id1, 8);
376 try { ord_i = std::stoi(ord); }
catch (...) { ord_i = 0; }
378 os << std::setw(8) << id1 <<
" "
379 << std::setw(6) << id2 <<
" "
380 << std::setfill(
'0') << std::setw(2) << ord_i << std::setfill(
' ') <<
" "
381 << std::setw(2) << cty <<
" ";
382 write_value(os, val);
390static void write_fobs_like(std::ostream& os,
392 const std::map<BlockName, DBNode::Value>& group)
394 write_block_header(os, proto, 0.0,
false);
395 os <<
"# ParentPDG type value q bin_low bin_high NDA ID1 ID2 ID3 ... comment\n";
397 for (
const auto& kv : group) {
398 if (kv.first ==
BlockName(
"scale"))
continue;
400 auto node = value_to_node_ptr(kv.second);
401 if (!node || !node->contains(
"central_value"))
continue;
403 const double val = value_to_double(node->get(
"central_value"), 0.0);
404 const double q = node->contains(
"scale") ? value_to_double(node->get(
"scale"), 0.0) : 0.0;
406 const double bin_low = node->contains(
"bin_low") ? value_to_double(node->get(
"bin_low"), 0.0) : 0.0;
407 const double bin_high = node->contains(
"bin_high") ? value_to_double(node->get(
"bin_high"), 0.0) : 0.0;
409 const auto toks = split_tokens(blockname_primary(kv.first));
410 if (toks.size() < 3)
continue;
412 os << std::setw(6) << toks[0] <<
" "
413 << std::setw(4) << toks[1] <<
" ";
414 write_value(os, val);
416 os <<
" " << std::setw(2) <<
static_cast<int>(q)
417 <<
" " << std::setw(7) << std::fixed << std::setprecision(0) << bin_low
418 <<
" " << std::setw(8) << std::fixed << std::setprecision(0) << bin_high
421 os <<
" " << std::setw(3) << toks[2];
422 for (
size_t i = 3; i < toks.size(); ++i) {
423 os <<
" " << std::setw(4) << toks[i];
431static void write_fmass_like(std::ostream& os,
433 const std::map<BlockName, DBNode::Value>& group)
435 write_block_header(os, proto, 0.0,
false);
436 os <<
"# PDG_code mass scheme Q particle\n";
437 for (
const auto& kv : group) {
438 const auto& key = kv.first;
441 auto node = value_to_node_ptr(kv.second);
442 if (!node || !node->contains(
"central_value"))
continue;
444 const double val = value_to_double(node->get(
"central_value"), 0.0);
445 const double q = node->contains(
"scale") ? value_to_double(node->get(
"scale"), 0.0) : 0.0;
447 auto toks = split_tokens(blockname_primary(key));
448 std::string pdg = toks.size() > 0 ? toks[0] :
"0";
449 std::string scheme = toks.size() > 1 ? toks[1] :
"0";
451 os << std::setw(6) << pdg <<
" ";
452 write_value(os, val);
453 os <<
" " << std::setw(2) << scheme
454 <<
" " << std::setw(2) <<
static_cast<int>(q)
461static void write_fconst_like(std::ostream& os,
463 const std::map<BlockName, DBNode::Value>& group)
465 write_block_header(os, proto, 0.0,
false);
466 os <<
"# PDG_code number decay_constant scheme scale particle\n";
468 for (
const auto& kv : group) {
469 const auto& key = kv.first;
472 auto node = value_to_node_ptr(kv.second);
473 if (!node || !node->contains(
"central_value"))
continue;
475 const double val = value_to_double(node->get(
"central_value"), 0.0);
476 const double scale = node->contains(
"scale") ? value_to_double(node->get(
"scale"), 1.0) : 1.0;
478 auto toks = split_tokens(blockname_primary(key));
479 std::string pdg = toks.size() > 0 ? toks[0] :
"0";
480 std::string number = toks.size() > 1 ? toks[1] :
"1";
481 std::string scheme = toks.size() > 2 ? toks[2] :
"0";
483 os << std::setw(6) << pdg <<
" "
484 << std::setw(3) << number <<
" ";
485 write_value(os, val);
486 os <<
" " << std::setw(2) << scheme <<
" "
487 << std::fixed << std::setprecision(1) << scale
488 << std::scientific <<
"\n";
494static void write_fconstratio_like(std::ostream& os,
496 const std::map<BlockName, DBNode::Value>& group)
498 write_block_header(os, proto, 0.0,
false);
499 os <<
"# PDG_code1 code2 nb1 nb2 ratio scheme scale comment\n";
501 for (
const auto& kv : group) {
502 const auto& key = kv.first;
505 auto node = value_to_node_ptr(kv.second);
506 if (!node || !node->contains(
"central_value"))
continue;
508 const double val = value_to_double(node->get(
"central_value"), 0.0);
509 const double scale = node->contains(
"scale") ? value_to_double(node->get(
"scale"), 1.0) : 1.0;
511 auto toks = split_tokens(blockname_primary(key));
512 if (toks.size() < 4)
continue;
514 std::string scheme = toks.size() > 4 ? toks[4] :
"0";
516 os << std::setw(6) << toks[0] <<
" "
517 << std::setw(6) << toks[1] <<
" "
518 << std::setw(2) << toks[2] <<
" "
519 << std::setw(2) << toks[3] <<
" ";
520 write_value(os, val);
521 os <<
" " << std::setw(2) << scheme <<
" "
522 << std::fixed << std::setprecision(1) << scale
523 << std::scientific <<
"\n";
529static void write_fbag_like(std::ostream& os,
531 const std::map<BlockName, DBNode::Value>& group)
533 write_fconst_like(os, proto, group);
536static void write_generic_block(std::ostream& os,
538 const std::map<BlockName, DBNode::Value>& group)
543 auto itScale = group.find(
BlockName(
"scale"));
544 if (itScale != group.end()) {
545 Q = value_to_double(itScale->second, 0.0);
549 write_block_header(os, proto, Q, hasQ);
551 for (
const auto& kv : group) {
552 if (kv.first ==
BlockName(
"scale"))
continue;
554 auto node = value_to_node_ptr(kv.second);
555 if (!node || !node->contains(
"central_value"))
continue;
557 const double val = value_to_double(node->get(
"central_value"), 0.0);
559 auto toks = split_tokens(blockname_primary(kv.first));
561 for (
const auto& t : toks) {
562 os << std::setw(6) << t <<
" ";
564 write_value(os, val);
571static std::map<BlockName, DBNode::Value> build_imaginary_group(
572 const std::map<BlockName, DBNode::Value>& group
575 std::map<BlockName, DBNode::Value> imaginary_group;
576 bool has_imaginary_entries =
false;
578 for (
const auto& [key, value] : group) {
583 auto node = value_to_node_ptr(value);
584 if (!node || !node->contains(
"imaginary_value")) {
588 const double imaginary = value_to_double(node->get(
"imaginary_value"), 0.0);
589 if (imaginary == 0.0) {
593 auto imaginary_node = std::make_shared<DBNode>();
594 imaginary_node->set(imaginary,
"central_value");
595 for (
const auto& metadata_key : {
600 if (node->contains(metadata_key)) {
601 imaginary_node->set(node->get(metadata_key), metadata_key);
605 imaginary_group[key] = imaginary_node;
606 has_imaginary_entries =
true;
609 if (has_imaginary_entries) {
610 const auto scale = group.find(
BlockName(
"scale"));
611 if (scale != group.end()) {
612 imaginary_group[
BlockName(
"scale")] = scale->second;
616 return imaginary_group;
619static void write_flife_like(std::ostream& os,
621 const std::map<BlockName, DBNode::Value>& group)
623 write_block_header(os, proto, 0.0,
false);
624 os <<
"# PDG_code lifetime particle\n";
626 for (
const auto& kv : group) {
627 if (kv.first ==
BlockName(
"scale"))
continue;
629 auto node = value_to_node_ptr(kv.second);
630 if (!node || !node->contains(
"central_value"))
continue;
632 const double val = value_to_double(node->get(
"central_value"), 0.0);
634 auto toks = split_tokens(blockname_primary(kv.first));
635 if (toks.empty())
continue;
637 os << std::setw(6) << toks[0] <<
" ";
638 write_value(os, val);
645 const std::shared_ptr<DBNode> &root)
const
647 if (!root) {
LOG_ERROR(
"LhaParser",
"writeToFile: root is null");
return; }
649 std::unordered_set<Prototype> allowed =
LHA_BLOCKS;
650 if (filename.size() >= 5 && filename.substr(filename.size() - 5) ==
".flha") {
651 allowed.merge(std::unordered_set<Prototype>(
FLHA_BLOCKS));
652 }
else if (filename.size() >= 5 && filename.substr(filename.size() - 5) ==
".slha") {
653 allowed.merge(std::unordered_set<Prototype>(
SLHA_BLOCKS));
656 std::ofstream out(filename);
657 if (!out) {
LOG_ERROR(
"LhaParser",
"Cannot open output file:", filename);
return; }
660 bool hasQew = read_ew_scale_Q(root, Qew);
662 bool usedExternal =
false;
663 std::map<BlockName, DBNode::Value> fwcoef_from_external;
666 fwcoef_from_external = build_fwcoef_group_from_ewscale_blocks(root, prefixes, usedExternal);
669 std::vector<BlockName> written_blocks;
671 for (
const auto& proto : allowed) {
674 bool present =
false;
675 for (
const auto& k : root->get_keys()) {
676 if (
BlockName(k) == bn) { present =
true;
break; }
678 if (!present)
continue;
680 written_blocks.push_back(bn);
682 auto group = root->getGroup({bn});
684 const std::string name = blockname_primary(bn);
686 if (name ==
"FWCOEF" || name ==
"IMFWCOEF") {
687 if (usedExternal) write_fwcoef_like(out, proto, fwcoef_from_external, Qew, hasQew);
688 else write_fwcoef_like(out, proto, group, Qew, hasQew);
689 }
else if (name ==
"FOBS" || name ==
"FOBSSM" || name ==
"FOBSERR") {
690 write_fobs_like(out, proto, group);
691 }
else if (name ==
"FMASS") {
692 write_fmass_like(out, proto, group);
693 }
else if (name ==
"FCONST") {
694 write_fconst_like(out, proto, group);
695 }
else if (name ==
"FCONSTRATIO") {
696 write_fconstratio_like(out, proto, group);
697 }
else if (name ==
"FBAG") {
698 write_fbag_like(out, proto, group);
699 }
else if (name ==
"FLIFE") {
700 write_flife_like(out, proto, group);
702 write_generic_block(out, proto, group);
710 auto root_keys = root->get_keys();
711 std::sort(root_keys.begin(), root_keys.end(), [](
const BlockName& lhs,
const BlockName& rhs) {
712 return lhs.canonical() < rhs.canonical();
715 for (
const auto& block_name : root_keys) {
716 const bool already_written = std::any_of(
717 written_blocks.begin(),
718 written_blocks.end(),
719 [&](
const BlockName& written) { return written == block_name; }
721 if (already_written) {
725 auto group = root->getGroup({block_name});
728 write_generic_block(out, generic, group);
734 for (
const auto& block_name : root_keys) {
735 const std::string name = blockname_primary(block_name);
736 if (starts_with(name,
"IM")) {
740 const BlockName imaginary_name(
"IM" + name);
741 const bool explicit_companion = std::any_of(
744 [&](
const BlockName& candidate) { return candidate == imaginary_name; }
746 if (explicit_companion) {
750 const auto imaginary_group = build_imaginary_group(root->getGroup({block_name}));
751 if (imaginary_group.empty()) {
755 Prototype imaginary_proto{imaginary_name};
756 if (
const Prototype* known = find_proto(allowed, imaginary_name)) {
757 imaginary_proto = *known;
759 imaginary_proto.globalScale =
760 imaginary_group.contains(
BlockName(
"scale"));
763 if (blockname_primary(imaginary_name) ==
"IMFWCOEF") {
764 write_fwcoef_like(out, imaginary_proto, imaginary_group, Qew, hasQew);
766 write_generic_block(out, imaginary_proto, imaginary_group);
const std::unordered_set< Prototype > SLHA_BLOCKS
const std::unordered_set< Prototype > FLHA_BLOCKS
const std::unordered_set< Prototype > LHA_BLOCKS
Parser for LHA / FLHA-style files into LhaBlock structures and DBNode trees.
const std::regex analyzer_rx(R"x(((?:[+-])?(?:\d+\.\d*|\.\d+)(?:[eEdD][+-]\d+)?|(\d+(?:[eEdD][+-]\d+)?))|(?:[+-]?\d+(?!\.))|(block)|(decay)|(\n)|([ \t]+)|(#.*)|([\w\=\.]+)|([^#]*))x", std::regex_constants::icase)
TokenType
Token categories used during LHA lexical analysis.
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
#define LOG_DEBUG(...)
Macro for logging debug messages.
#define LOG_VERBOSE(...)
Macro for logging verbose messages.
#define LOG_WARN(...)
Macro for logging warning messages.
bool ends_with(const std::string &str, const std::string &suffix)
Tests whether a string ends with a given suffix.
std::unordered_set< T > get_keys(const std::map< T, U > &map)
Extracts the key set from a std::map into an unordered_set.
Block identifier with alias support.
void to_upper()
Converts all aliases to upper-case in-place.
void setGroup(const std::vector< BlockName > &keys, const std::map< BlockName, Value > &groupData)
Sets the content of a whole sub-tree from a map of values.
std::map< BlockName, Value > getGroup(const std::vector< BlockName > &keys) const
Retrieves a whole sub-tree as a map from a path of keys.
void set(T value, Key &&key, Rest &&... rest)
Sets a value in the node using a sequence of keys.
std::variant< BlockName, int, double, bool, std::shared_ptr< DBNode >, std::vector< std::shared_ptr< DBNode > > > Value
Variant type used to store values in the tree.
static std::vector< std::string > get_str()
Returns the list of builtin string names from MapFn().
Mapper for WGroup <-> WGroupId <-> optional external string.
std::shared_ptr< DBNode > readFromFile(const std::string &input_file) const override
Parses an LHA/FLHA file from disk and returns its DBNode representation.
void writeToFile(const std::string &filename, const std::shared_ptr< DBNode > &root) const
Serializes a DBNode structure back to an LHA-like file.
std::shared_ptr< DBNode > parse(const std::string &src) const override
Parses LHA/FLHA content from an in-memory string.
void set_prototypes(const std::unordered_set< Prototype > &prototypes)
Sets the set of block prototypes used by the parser.
Represents the structure of an LHA block, specifying columns for values, scales, and renormalization ...
Single lexical token produced from an LHA file.