Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
LhaParser.cpp
Go to the documentation of this file.
1#include "LhaParser.h"
2
3namespace {
4
5bool is_ignored_metadata_block(std::string_view name) {
6 // Informational blocks do not contain numerical model parameters and may
7 // have free-form string payloads. Treating them as unsupported numerical
8 // blocks only creates noisy warnings for otherwise valid spectrum files.
9 return name == "SPINFO" || name == "spinfo" ||
10 name == "DCINFO" || name == "dcinfo";
11}
12
13} // namespace
14
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));
17 LOG_DEBUG(id);
18 block->readData(lines);
19 BlockName id_ci = block->getPrototype().blockName;
20 id_ci.to_upper();
21 blocks.insert(std::pair(id_ci, std::move(block)));
22}
23
24std::vector<Token> LhaParser::tokenize(const std::string &src) const {
25 int cLine = 0;
26 int cCol = 0;
27
28 auto rit = std::sregex_iterator(src.begin(), src.end(), analyzer_rx);
29 auto rend = std::sregex_iterator();
30
31 std::vector<Token> tokens;
32 while (rit != rend) {
33 std::smatch m = *rit;
34 size_t group_index = m.size();
35
36 for (size_t idx = 1; idx < m.size(); ++idx) {
37 if (m[idx].matched) {
38 group_index = idx - 1;
39 break;
40 }
41 }
42
43 auto tokenType = static_cast<TokenType>(group_index);
44 auto value = m[group_index + 1].str();
45
46 if (tokenType == TokenType::NEWLINE) {
47 ++cLine;
48 cCol = 0;
49 } else if (tokenType != TokenType::SKIP && value != "") {
50 tokens.emplace_back(Token{tokenType, value, cLine, cCol});
51 ++cCol;
52 }
53
54 ++rit;
55 }
56
57 return tokens;
58}
59
60
61std::map<BlockName, std::vector<std::vector<std::string>>>
62LhaParser::parse_tokens(std::vector<Token> tokens, bool comments) const
63{
64 bool newBlock = false;
65 bool hasGlobalScale = false;
66 bool isQ = false;
67 bool skipBlock = false;
68 bool decay = false;
69
70 std::string globalQ;
71 BlockName cBlock;
72 int cCol = INT_MAX;
73
74 std::map<BlockName, std::vector<std::vector<std::string>>> rawBlocks;
75 auto current_block_it = rawBlocks.end();
76
77 for (const Token& t : tokens) {
78 if (newBlock) {
79 auto prototype = this->findPrototype(t.value);
80 if (prototype.blockName != "") {
81 LOG_DEBUG("LHA reader: Block ", prototype.blockName, " found.");
82
83 cBlock = prototype.blockName;
84 hasGlobalScale= prototype.globalScale;
85 skipBlock = false;
86 decay = false;
87 globalQ.clear();
88 cCol = INT_MAX;
89
90 auto [it, inserted] =
91 rawBlocks.emplace(cBlock, std::vector<std::vector<std::string>>{});
92 current_block_it = it;
93 }
94 else if (decay) {
95 LOG_DEBUG("LHA reader: Decay block found. Skipping.");
96 skipBlock = true;
97 hasGlobalScale = false;
98 current_block_it = rawBlocks.end();
99 decay = false;
100 }
101 else {
102 if (is_ignored_metadata_block(t.value)) {
103 LOG_DEBUG("LHA reader: Metadata block ", t.value, " skipped.");
104 } else {
105 LOG_WARN("LHA reader: Unknown block " + t.value + " encountered. Skipping.");
106 }
107 skipBlock = true;
108 hasGlobalScale = false;
109 current_block_it = rawBlocks.end();
110 }
111 newBlock = false;
112 }
113 else if (t.type == TokenType::BLOCK) {
114 newBlock = true;
115 }
116 else if (t.type == TokenType::DECAY) {
117 decay = true;
118 newBlock = true;
119 }
120 else if (!comments && t.type == TokenType::COMMENT) {
121 continue;
122 }
123 else if (hasGlobalScale && t.type == TokenType::WORD && !skipBlock) {
124 isQ = (t.value == "Q=" || t.value == "q=");
125 }
126 else if (!skipBlock && current_block_it != rawBlocks.end()) {
127 if (isQ) {
128 globalQ = t.value;
129 isQ = false;
130 }
131 else {
132 LOG_VERBOSE("Token : [", static_cast<int>(t.type), ", ", t.value, "]");
133 auto& rows = current_block_it->second;
134
135 if (t.col <= cCol) {
136 rows.emplace_back(std::vector<std::string>{});
137 if (hasGlobalScale)
138 rows.back().emplace_back(!globalQ.empty() ? globalQ : "-1");
139 }
140 rows.back().emplace_back(t.value);
141 cCol = t.col;
142 }
143 }
144 }
145
146 return rawBlocks;
147}
148
149Prototype LhaParser::findPrototype(BlockName name) const {
150 for (const auto& p : blockPrototypes) {
151 name.to_upper();
152 if (p.blockName == name)
153 return p;
154 }
155 return Prototype{""};
156}
157
158std::shared_ptr<DBNode> LhaParser::readFromFile(const std::string& input_file) const {
159 std::ifstream file(input_file.data());
160 std::stringstream buffer;
161 buffer << file.rdbuf();
162 return this->parse(buffer.str());
163}
164
165std::shared_ptr<DBNode> LhaParser::parse(const std::string &src) const {
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);
170 }
171 LOG_DEBUG("LHA file parsed.");
172
173 return this->toDBNode(blocks);
174}
175
176
177void LhaParser::set_prototypes(const std::unordered_set<Prototype> &prototypes)
178{
179 this->blockPrototypes = prototypes;
180}
181
182
183std::shared_ptr<DBNode> LhaParser::toDBNode(std::map<BlockName, std::shared_ptr<LhaBlock>> blocks) const {
184 DBNode root;
185
186 for (const auto& [blockName, blockPtr] : blocks) {
187 auto block_node = blockPtr->toDBNode();
188
189 auto group = block_node->getGroup({blockName});
190 root.setGroup({blockName}, group);
191
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");
198 } else {
199 LOG_WARN("Expected numeric 'scale' for block ", blockName, " but got variant index ", sval.index());
200 }
201 }
202 }
203
204 return std::make_shared<DBNode>(root);
205}
206
207static double value_to_double(const DBNode::Value& v, double fallback = 0.0)
208{
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));
211 return fallback;
212}
213
214static bool value_is_node_ptr(const DBNode::Value& v)
215{
216 return std::holds_alternative<std::shared_ptr<DBNode>>(v);
217}
218
219static std::shared_ptr<DBNode> value_to_node_ptr(const DBNode::Value& v)
220{
221 if (!value_is_node_ptr(v)) return nullptr;
222 return std::get<std::shared_ptr<DBNode>>(v);
223}
224
225static std::vector<std::string> split_tokens(const std::string& s)
226{
227 std::string tmp = s;
228 std::replace(tmp.begin(), tmp.end(), '_', ' ');
229 std::istringstream iss(tmp);
230
231 std::vector<std::string> out;
232 std::string tok;
233 while (iss >> tok) out.push_back(tok);
234 return out;
235}
236
237static std::vector<std::string> split_ws(const std::string& s)
238{
239 std::istringstream iss(s);
240 std::vector<std::string> out;
241 std::string tok;
242 while (iss >> tok) out.push_back(tok);
243 return out;
244}
245
246static bool read_ew_scale_Q(const std::shared_ptr<DBNode>& root, double& Q_out)
247{
248 if (!root || !root->contains(BlockName("EW_SCALE"))) return false;
249
250 auto g = root->getGroup({BlockName("EW_SCALE")});
251
252 auto it = g.find(BlockName("1"));
253 if (it != g.end()) {
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);
257 return true;
258 }
259 }
260
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);
265 return true;
266 }
267 }
268
269 return false;
270}
271
272static bool starts_with(const std::string& s, const std::string& pre)
273{
274 return s.size() >= pre.size() && s.compare(0, pre.size(), pre) == 0;
275}
276
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)
281{
282 usedExternalBlocks = false;
283 std::map<BlockName, DBNode::Value> out;
284
285 if (!root) return out;
286
287 for (const auto& k : root->get_keys()) {
288 std::ostringstream oss; oss << k;
289 const std::string ks = oss.str();
290
291 if (!ends_with(ks, "_EW_SCALE")) continue;
292
293 bool okPrefix = false;
294 for (const auto& pre : prefixes) {
295 if (starts_with(ks, pre)) { okPrefix = true; break; }
296 }
297 if (!okPrefix) continue;
298
299 usedExternalBlocks = true;
300
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;
305 }
306 }
307
308 return out;
309}
310
311static std::string blockname_primary(const BlockName& bn)
312{
313 std::ostringstream oss;
314 oss << bn;
315 return oss.str();
316}
317
318static const Prototype* find_proto(const std::unordered_set<Prototype>& protos, const BlockName& name)
319{
320 for (const auto& p : protos) {
321 if (p.blockName == name) return &p;
322 }
323 return nullptr;
324}
325
326static void write_block_header(std::ostream& os, const Prototype& proto, double Q, bool hasQ)
327{
328 os << "BLOCK " << blockname_primary(proto.blockName);
329 if (proto.globalScale && hasQ) {
330 os << " Q= " << std::setprecision(8) << std::scientific << Q;
331 }
332 os << "\n";
333}
334
335static void write_value(std::ostream& os, double v)
336{
337 os << std::setprecision(8) << std::scientific << v;
338}
339
340static std::string pad_left_zeros(std::string s, size_t width)
341{
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;
344 }
345 return s;
346}
347
348static void write_fwcoef_like(std::ostream& os,
349 const Prototype& proto,
350 const std::map<BlockName, DBNode::Value>& group,
351 double Q_from_ewscale,
352 bool hasQ_from_ewscale)
353{
354 write_block_header(os, proto, Q_from_ewscale, hasQ_from_ewscale);
355 os << "#id order M value comment\n";
356
357 for (const auto& kv : group) {
358 if (kv.first == BlockName("scale")) continue;
359
360 auto node = value_to_node_ptr(kv.second);
361 if (!node || !node->contains("central_value")) continue;
362
363 const double val = value_to_double(node->get("central_value"), 0.0);
364
365 const auto toks = split_tokens(blockname_primary(kv.first));
366 if (toks.size() < 4) continue;
367
368 std::string id1 = toks[0];
369 std::string id2 = toks[1];
370 std::string ord = toks[2];
371 std::string cty = toks[3];
372
373 if (id1.size() == 7) id1 = pad_left_zeros(id1, 8);
374
375 int ord_i = 0;
376 try { ord_i = std::stoi(ord); } catch (...) { ord_i = 0; }
377
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);
383 os << "\n";
384 }
385
386 os << "\n";
387}
388
389
390static void write_fobs_like(std::ostream& os,
391 const Prototype& proto,
392 const std::map<BlockName, DBNode::Value>& group)
393{
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";
396
397 for (const auto& kv : group) {
398 if (kv.first == BlockName("scale")) continue;
399
400 auto node = value_to_node_ptr(kv.second);
401 if (!node || !node->contains("central_value")) continue;
402
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;
405
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;
408
409 const auto toks = split_tokens(blockname_primary(kv.first));
410 if (toks.size() < 3) continue;
411
412 os << std::setw(6) << toks[0] << " "
413 << std::setw(4) << toks[1] << " ";
414 write_value(os, val);
415
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
419 << std::scientific;
420
421 os << " " << std::setw(3) << toks[2];
422 for (size_t i = 3; i < toks.size(); ++i) {
423 os << " " << std::setw(4) << toks[i];
424 }
425 os << "\n";
426 }
427
428 os << "\n";
429}
430
431static void write_fmass_like(std::ostream& os,
432 const Prototype& proto,
433 const std::map<BlockName, DBNode::Value>& group)
434{
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;
439 if (key == BlockName("scale")) continue;
440
441 auto node = value_to_node_ptr(kv.second);
442 if (!node || !node->contains("central_value")) continue;
443
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;
446
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";
450
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)
455 << "\n";
456 }
457
458 os << "\n";
459}
460
461static void write_fconst_like(std::ostream& os,
462 const Prototype& proto,
463 const std::map<BlockName, DBNode::Value>& group)
464{
465 write_block_header(os, proto, 0.0, false);
466 os << "# PDG_code number decay_constant scheme scale particle\n";
467
468 for (const auto& kv : group) {
469 const auto& key = kv.first;
470 if (key == BlockName("scale")) continue;
471
472 auto node = value_to_node_ptr(kv.second);
473 if (!node || !node->contains("central_value")) continue;
474
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;
477
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";
482
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";
489 }
490
491 os << "\n";
492}
493
494static void write_fconstratio_like(std::ostream& os,
495 const Prototype& proto,
496 const std::map<BlockName, DBNode::Value>& group)
497{
498 write_block_header(os, proto, 0.0, false);
499 os << "# PDG_code1 code2 nb1 nb2 ratio scheme scale comment\n";
500
501 for (const auto& kv : group) {
502 const auto& key = kv.first;
503 if (key == BlockName("scale")) continue;
504
505 auto node = value_to_node_ptr(kv.second);
506 if (!node || !node->contains("central_value")) continue;
507
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;
510
511 auto toks = split_tokens(blockname_primary(key));
512 if (toks.size() < 4) continue;
513
514 std::string scheme = toks.size() > 4 ? toks[4] : "0";
515
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";
524 }
525
526 os << "\n";
527}
528
529static void write_fbag_like(std::ostream& os,
530 const Prototype& proto,
531 const std::map<BlockName, DBNode::Value>& group)
532{
533 write_fconst_like(os, proto, group);
534}
535
536static void write_generic_block(std::ostream& os,
537 const Prototype& proto,
538 const std::map<BlockName, DBNode::Value>& group)
539{
540 bool hasQ = false;
541 double Q = 0.0;
542
543 auto itScale = group.find(BlockName("scale"));
544 if (itScale != group.end()) {
545 Q = value_to_double(itScale->second, 0.0);
546 hasQ = true;
547 }
548
549 write_block_header(os, proto, Q, hasQ);
550
551 for (const auto& kv : group) {
552 if (kv.first == BlockName("scale")) continue;
553
554 auto node = value_to_node_ptr(kv.second);
555 if (!node || !node->contains("central_value")) continue;
556
557 const double val = value_to_double(node->get("central_value"), 0.0);
558
559 auto toks = split_tokens(blockname_primary(kv.first));
560
561 for (const auto& t : toks) {
562 os << std::setw(6) << t << " ";
563 }
564 write_value(os, val);
565 os << "\n";
566 }
567
568 os << "\n";
569}
570
571static std::map<BlockName, DBNode::Value> build_imaginary_group(
572 const std::map<BlockName, DBNode::Value>& group
573)
574{
575 std::map<BlockName, DBNode::Value> imaginary_group;
576 bool has_imaginary_entries = false;
577
578 for (const auto& [key, value] : group) {
579 if (key == BlockName("scale")) {
580 continue;
581 }
582
583 auto node = value_to_node_ptr(value);
584 if (!node || !node->contains("imaginary_value")) {
585 continue;
586 }
587
588 const double imaginary = value_to_double(node->get("imaginary_value"), 0.0);
589 if (imaginary == 0.0) {
590 continue;
591 }
592
593 auto imaginary_node = std::make_shared<DBNode>();
594 imaginary_node->set(imaginary, "central_value");
595 for (const auto& metadata_key : {
596 BlockName("scale"),
597 BlockName("bin_low"),
598 BlockName("bin_high")
599 }) {
600 if (node->contains(metadata_key)) {
601 imaginary_node->set(node->get(metadata_key), metadata_key);
602 }
603 }
604
605 imaginary_group[key] = imaginary_node;
606 has_imaginary_entries = true;
607 }
608
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;
613 }
614 }
615
616 return imaginary_group;
617}
618
619static void write_flife_like(std::ostream& os,
620 const Prototype& proto,
621 const std::map<BlockName, DBNode::Value>& group)
622{
623 write_block_header(os, proto, 0.0, false);
624 os << "# PDG_code lifetime particle\n";
625
626 for (const auto& kv : group) {
627 if (kv.first == BlockName("scale")) continue;
628
629 auto node = value_to_node_ptr(kv.second);
630 if (!node || !node->contains("central_value")) continue;
631
632 const double val = value_to_double(node->get("central_value"), 0.0);
633
634 auto toks = split_tokens(blockname_primary(kv.first));
635 if (toks.empty()) continue;
636
637 os << std::setw(6) << toks[0] << " ";
638 write_value(os, val);
639 os << "\n";
640 }
641 os << "\n";
642}
643
644void LhaParser::writeToFile(const std::string &filename,
645 const std::shared_ptr<DBNode> &root) const
646{
647 if (!root) { LOG_ERROR("LhaParser", "writeToFile: root is null"); return; }
648
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));
654 }
655
656 std::ofstream out(filename);
657 if (!out) { LOG_ERROR("LhaParser", "Cannot open output file:", filename); return; }
658
659 double Qew = 0.0;
660 bool hasQew = read_ew_scale_Q(root, Qew);
661
662 bool usedExternal = false;
663 std::map<BlockName, DBNode::Value> fwcoef_from_external;
664 {
665 const auto prefixes = GroupMapper().get_str();
666 fwcoef_from_external = build_fwcoef_group_from_ewscale_blocks(root, prefixes, usedExternal);
667 }
668
669 std::vector<BlockName> written_blocks;
670
671 for (const auto& proto : allowed) {
672 const auto bn = proto.blockName;
673
674 bool present = false;
675 for (const auto& k : root->get_keys()) {
676 if (BlockName(k) == bn) { present = true; break; }
677 }
678 if (!present) continue;
679
680 written_blocks.push_back(bn);
681
682 auto group = root->getGroup({bn});
683
684 const std::string name = blockname_primary(bn);
685
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);
701 } else {
702 write_generic_block(out, proto, group);
703 }
704 }
705
706 // Preserve custom/runtime blocks as generic LHA blocks instead of silently
707 // dropping them because no built-in parsing prototype exists. Their entry
708 // ids are already serialized as underscore-separated DBNode keys by
709 // ParamBlockWriter, which write_generic_block expands into LHA columns.
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();
713 });
714
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; }
720 );
721 if (already_written) {
722 continue;
723 }
724
725 auto group = root->getGroup({block_name});
726 Prototype generic{block_name};
727 generic.globalScale = group.contains(BlockName("scale"));
728 write_generic_block(out, generic, group);
729 }
730
731 // LHA-family formats represent complex entries through companion IM...
732 // blocks. Generate those blocks when a parameter is stored directly as a
733 // complex scalar and no explicit companion block is already present.
734 for (const auto& block_name : root_keys) {
735 const std::string name = blockname_primary(block_name);
736 if (starts_with(name, "IM")) {
737 continue;
738 }
739
740 const BlockName imaginary_name("IM" + name);
741 const bool explicit_companion = std::any_of(
742 root_keys.begin(),
743 root_keys.end(),
744 [&](const BlockName& candidate) { return candidate == imaginary_name; }
745 );
746 if (explicit_companion) {
747 continue;
748 }
749
750 const auto imaginary_group = build_imaginary_group(root->getGroup({block_name}));
751 if (imaginary_group.empty()) {
752 continue;
753 }
754
755 Prototype imaginary_proto{imaginary_name};
756 if (const Prototype* known = find_proto(allowed, imaginary_name)) {
757 imaginary_proto = *known;
758 } else {
759 imaginary_proto.globalScale =
760 imaginary_group.contains(BlockName("scale"));
761 }
762
763 if (blockname_primary(imaginary_name) == "IMFWCOEF") {
764 write_fwcoef_like(out, imaginary_proto, imaginary_group, Qew, hasQew);
765 } else {
766 write_generic_block(out, imaginary_proto, imaginary_group);
767 }
768 }
769}
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.
Definition LhaParser.h:47
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
Definition Logger.h:41
#define LOG_DEBUG(...)
Macro for logging debug messages.
Definition Logger.h:45
#define LOG_VERBOSE(...)
Macro for logging verbose messages.
Definition Logger.h:47
#define LOG_WARN(...)
Macro for logging warning messages.
Definition Logger.h:40
bool ends_with(const std::string &str, const std::string &suffix)
Tests whether a string ends with a given suffix.
Definition Utils.cpp:3
std::unordered_set< T > get_keys(const std::map< T, U > &map)
Extracts the key set from a std::map into an unordered_set.
Definition Utils.h:108
Block identifier with alias support.
Definition BlockName.h:59
void to_upper()
Converts all aliases to upper-case in-place.
Definition BlockName.cpp:92
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.
Definition DBNode.cpp:30
std::map< BlockName, Value > getGroup(const std::vector< BlockName > &keys) const
Retrieves a whole sub-tree as a map from a path of keys.
Definition DBNode.cpp:13
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.
Definition DBNode.h:45
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.
constexpr double g
csl::Expr v
Definition sm.h:110
Represents the structure of an LHA block, specifying columns for values, scales, and renormalization ...
BlockName blockName
Single lexical token produced from an LHA file.
Definition LhaParser.h:92