Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
GeneralModelModifier.cpp
Go to the documentation of this file.
3
4#include <cstddef>
5#include <cstdint>
6#include <filesystem>
7#include <fstream>
8#include <iomanip>
9#include <sstream>
10#include <utility>
11
12namespace fs = std::filesystem;
13
14namespace {
15
16std::string stable_file_fingerprint(const fs::path& path) {
17 std::ifstream input(path, std::ios::binary);
18 if (!input) {
19 throw std::runtime_error("Cannot fingerprint MARTY model file: " + path.string());
20 }
21
22 // FNV-1a is intentionally simple and deterministic. This is a cache key,
23 // not a cryptographic integrity check.
24 std::uint64_t hash = 14695981039346656037ULL;
25 char buffer[8192];
26 while (input.read(buffer, sizeof(buffer)) || input.gcount() > 0) {
27 const auto count = input.gcount();
28 for (std::streamsize i = 0; i < count; ++i) {
29 hash ^= static_cast<unsigned char>(buffer[i]);
30 hash *= 1099511628211ULL;
31 }
32 }
33
34 std::ostringstream result;
35 result << std::hex << std::setw(16) << std::setfill('0') << hash;
36 return result.str();
37}
38
39std::string normalized_path(const fs::path& path) {
40 std::error_code ec;
41 fs::path normalized = fs::weakly_canonical(path, ec);
42 if (ec) {
43 ec.clear();
44 normalized = fs::absolute(path, ec);
45 }
46 return normalized.lexically_normal().string();
47}
48
49} // namespace
50
52 std::string model,
53 std::string model_path,
54 std::optional<int> model_template_index)
56 std::move(wilson),
57 model,
58 model,
59 std::move(model_path),
60 model_template_index,
61 false,
62 false,
63 false,
64 false
65 ) {}
66
68 std::string output_model,
69 std::string target_model,
70 std::string model_path,
71 std::optional<int> model_template_index,
72 bool disable_non_sm_particles,
73 bool bsm_split_generation,
74 bool full_target_generation,
75 bool tree_first_fallback) {
76 this->wilson = std::move(wilson);
77 this->output_model = std::move(output_model);
78 this->target_model = std::move(target_model);
79 this->model_path = std::move(model_path);
80 this->model_template_index = model_template_index;
81 this->disable_non_sm_particles = disable_non_sm_particles;
82 this->bsm_split_generation = bsm_split_generation;
83 this->full_target_generation = full_target_generation;
84 this->tree_first_fallback = tree_first_fallback;
85
86 ModelFileChecker checker(this->model_path);
87 this->model_class = checker.resolveModelClass(this->target_model);
88 this->model_instantiation = resolveModelInstantiation(
89 this->target_model,
90 this->model_path,
91 this->model_template_index
92 );
93
94 const auto marty = MartyRuntimeConfig::require_available("GeneralModelModifier");
95 if (marty.valid) {
96 this->marty_path = marty.marty_header.string();
97 }
98 }
99
100std::string GeneralModelModifier::resolveModelInstantiation(const std::string& model,
101 const std::string& model_path,
102 std::optional<int> model_template_index) {
103 ModelFileChecker checker(model_path);
104 const ModelClassInfo info = checker.resolveModelClass(model);
105
106 if (!info.is_template) {
107 return info.class_name;
108 }
109
110 if (!model_template_index.has_value()) {
111 throw std::runtime_error(
112 "MARTY model class '" + info.class_name + "' resolved from mty_model_name='" + model +
113 "' is a template class, but no template index was provided."
114 );
115 }
116
117 return info.class_name + "<" + std::to_string(*model_template_index) + ">";
118}
119
120std::string GeneralModelModifier::modelSignature(const std::string& model,
121 const std::string& model_path,
122 std::optional<int> model_template_index) {
123 return "HYPERISO_MARTY_MODEL_SIGNATURE: "
124 + resolveModelInstantiation(model, model_path, model_template_index)
125 + "; path=" + normalized_path(model_path)
126 + "; fnv1a64=" + stable_file_fingerprint(model_path);
127}
128
129bool GeneralModelModifier::usesRegPropSplit() const {
130 return this->bsm_split_generation
131 && (this->wilson == "C9" || this->wilson == "CP9" || this->wilson == "CP10");
132}
133
134bool GeneralModelModifier::usesGenericTreeFirst() const {
135 return this->tree_first_fallback && !this->usesRegPropSplit();
136}
137
138void GeneralModelModifier::replaceWilsonOrderArgument(std::string& line) {
139 const auto call = line.find("computeWilsonCoefficients");
140 if (call != std::string::npos) {
141 const auto open = line.find('(', call);
142 if (open != std::string::npos) {
143 const std::pair<const char*, const char*> candidates[] = {
144 {"mty::Order::TreeLevel", "hyperiso_marty_order"},
145 {"mty::Order::OneLoop", "hyperiso_marty_order"},
146 {"TreeLevel", "hyperiso_marty_order"},
147 {"OneLoop", "hyperiso_marty_order"},
148 };
149 for (const auto& [needle, replacement] : candidates) {
150 const auto pos = line.find(needle, open + 1);
151 if (pos != std::string::npos) {
152 line.replace(pos, std::string(needle).size(), replacement);
153 return;
154 }
155 }
156 }
157 }
158
159 // Multi-line calls put the order on its own line.
160 const auto first = line.find_first_not_of(" \t");
161 const auto last = line.find_last_not_of(" \t");
162 const std::string trimmed = first == std::string::npos
163 ? std::string{}
164 : line.substr(first, last - first + 1);
165 if (trimmed == "mty::Order::TreeLevel,"
166 || trimmed == "mty::Order::OneLoop,"
167 || trimmed == "TreeLevel,"
168 || trimmed == "OneLoop,") {
169 line.replace(first, last - first + 1, "hyperiso_marty_order,");
170 }
171}
172
173std::string GeneralModelModifier::makeSmFilterHelper() {
174 return R"cpp(
175namespace {
176std::unordered_set<std::string> hyperiso_marty_sm_particle_names() {
177 // Do not instantiate an mty::SM_Model inside a generated BSM executable.
178 // MARTY keeps global model state internally; constructing a reference SM
179 // model while a THDM/SUSY model is active can corrupt that state and has
180 // been observed to segfault during fermion embedding. The split filter
181 // only needs a stable name list, so keep it explicit and local.
182 return {
183 "A", "Z", "W", "Wp", "Wm", "G", "Gp", "Gm", "G0",
184 "h",
185 "u", "c", "t", "d", "s", "b",
186 "u_L", "c_L", "t_L", "d_L", "s_L", "b_L",
187 "u_R", "c_R", "t_R", "d_R", "s_R", "b_R",
188 "e", "mu", "tau",
189 "e_L", "mu_L", "tau_L",
190 "e_R", "mu_R", "tau_R",
191 "nu_e", "nu_mu", "nu_tau",
192 "ve", "vmu", "vtau"
193 };
194}
195
196std::vector<mty::Particle> hyperiso_marty_non_sm_particles(mty::Model& model) {
197 mty::Model::current = &model;
198 const auto sm_particle_names = hyperiso_marty_sm_particle_names();
199
200 std::vector<mty::Particle> non_sm_particles;
201 for (const auto& particle : model.getParticles()) {
202 const std::string name = std::string(particle->getName());
203 if (sm_particle_names.find(name) == sm_particle_names.end()) {
204 non_sm_particles.push_back(particle);
205 }
206 }
207
208 return non_sm_particles;
209}
210
211void hyperiso_marty_disable_non_sm_particles(mty::FeynOptions& opts, mty::Model& model) {
212 auto non_sm_particles = hyperiso_marty_non_sm_particles(model);
213 if (!non_sm_particles.empty()) {
214 opts.addFilter(mty::filter::disableParticles(non_sm_particles));
215 }
216}
217
218bool hyperiso_marty_is_non_sm_particle_name(const std::string& name,
219 const std::unordered_set<std::string>& sm_particle_names) {
220 return sm_particle_names.find(name) == sm_particle_names.end();
221}
222
223bool hyperiso_marty_has_non_sm_diagram_particle(mty::FeynmanDiagram const& diag,
224 const std::unordered_set<std::string>& sm_particle_names) {
225 for (const auto& particle : diag.getParticles(mty::FeynmanDiagram::DiagramParticleType::Loop)) {
226 if (hyperiso_marty_is_non_sm_particle_name(std::string(particle->getName()), sm_particle_names)) {
227 return true;
228 }
229 }
230 for (const auto& particle : diag.getParticles(mty::FeynmanDiagram::DiagramParticleType::Mediator)) {
231 if (hyperiso_marty_is_non_sm_particle_name(std::string(particle->getName()), sm_particle_names)) {
232 return true;
233 }
234 }
235 // MARTY may classify a penguin linker (for example Z_X in b -> s l l)
236 // as an External diagram particle even though it is not one of the physical
237 // process legs. Omitting this category silently removes the Z' diagrams.
238 for (const auto& particle : diag.getParticles(mty::FeynmanDiagram::DiagramParticleType::External)) {
239 if (hyperiso_marty_is_non_sm_particle_name(std::string(particle->getName()), sm_particle_names)) {
240 return true;
241 }
242 }
243 return false;
244}
245
246void hyperiso_marty_require_non_sm_diagram_particle(mty::FeynOptions& opts) {
247 const auto sm_particle_names = hyperiso_marty_sm_particle_names();
248 opts.addFilter([sm_particle_names](mty::FeynmanDiagram const& diag) {
249 return hyperiso_marty_has_non_sm_diagram_particle(diag, sm_particle_names);
250 });
251}
252} // namespace
253)cpp";
254}
255
256bool GeneralModelModifier::consumeTreeSafeWilsonCall(std::ofstream& outputFile,
257 const std::string& currentLine,
258 bool pair_return,
259 bool count_graphs) {
260 const bool starts_call = currentLine.find("model.computeWilsonCoefficients") != std::string::npos;
261 if (!this->buffering_tree_safe_wilson_call && !starts_call) {
262 return false;
263 }
264
265 if (!this->buffering_tree_safe_wilson_call) {
266 this->buffering_tree_safe_wilson_call = true;
267 this->tree_safe_wilson_call_lines.clear();
268 }
269 this->tree_safe_wilson_call_lines.push_back(currentLine);
270
271 if (currentLine.find(");") == std::string::npos) {
272 return true;
273 }
274
275 emitTreeSafeWilsonCall(outputFile, pair_return, count_graphs);
276 this->tree_safe_wilson_call_lines.clear();
277 this->buffering_tree_safe_wilson_call = false;
278 return true;
279}
280
281void GeneralModelModifier::emitTreeSafeWilsonCall(std::ofstream& outputFile,
282 bool pair_return,
283 bool count_graphs) {
284 if (this->tree_safe_wilson_call_lines.empty()) {
285 throw std::runtime_error("Cannot emit an empty MARTY Wilson call");
286 }
287
288 const std::string& first_line = this->tree_safe_wilson_call_lines.front();
289 const auto auto_pos = first_line.find("auto ");
290 const auto eq_pos = first_line.find('=', auto_pos == std::string::npos ? 0 : auto_pos + 5);
291 const auto call_pos = first_line.find("model.computeWilsonCoefficients", eq_pos);
292 if (auto_pos == std::string::npos || eq_pos == std::string::npos
293 || call_pos == std::string::npos || eq_pos <= auto_pos + 5) {
294 throw std::runtime_error(
295 "Cannot rewrite MARTY computeWilsonCoefficients call for a safe tree probe: "
296 + first_line
297 );
298 }
299
300 std::string variable = first_line.substr(auto_pos + 5, eq_pos - (auto_pos + 5));
301 const auto variable_first = variable.find_first_not_of(" \t");
302 const auto variable_last = variable.find_last_not_of(" \t");
303 if (variable_first == std::string::npos || variable_last == std::string::npos) {
304 throw std::runtime_error("Cannot resolve MARTY WilsonSet variable from: " + first_line);
305 }
306 variable = variable.substr(variable_first, variable_last - variable_first + 1);
307 const std::string indent = first_line.substr(0, auto_pos);
308
309 auto replace_token = [](std::string& line,
310 const std::string& token,
311 const std::string& replacement) {
312 std::size_t pos = 0;
313 while ((pos = line.find(token, pos)) != std::string::npos) {
314 const bool left_ok = pos == 0
315 || !(std::isalnum(static_cast<unsigned char>(line[pos - 1])) || line[pos - 1] == '_');
316 const std::size_t right = pos + token.size();
317 const bool right_ok = right >= line.size()
318 || !(std::isalnum(static_cast<unsigned char>(line[right])) || line[right] == '_');
319 if (left_ok && right_ok) {
320 line.replace(pos, token.size(), replacement);
321 pos += replacement.size();
322 } else {
323 pos += token.size();
324 }
325 }
326 };
327
328 std::vector<std::string> probe_lines = this->tree_safe_wilson_call_lines;
329 {
330 std::string& line = probe_lines.front();
331 line = indent + "auto hyperiso_marty_tree_probe =" + line.substr(eq_pos + 1);
332 const auto method = line.find("computeWilsonCoefficients");
333 if (method == std::string::npos) {
334 throw std::runtime_error("Cannot locate MARTY Wilson method in buffered call");
335 }
336 line.replace(method,
337 std::string("computeWilsonCoefficients").size(),
338 "computeAmplitude");
339 }
340 for (auto& line : probe_lines) {
341 replace_token(line, "opts", "hyperiso_marty_tree_options");
342 }
343
344 std::vector<std::string> loop_lines = this->tree_safe_wilson_call_lines;
345 loop_lines.front() = indent + variable + " =" + loop_lines.front().substr(eq_pos + 1);
346
347 outputFile << indent << "mty::WilsonSet " << variable << ";\n";
348 outputFile << indent << "if (hyperiso_marty_order == mty::Order::TreeLevel) {\n";
349 outputFile << indent << " auto hyperiso_marty_tree_options = opts;\n";
350 outputFile << indent << " hyperiso_marty_tree_options.orderExternalFermions = true;\n";
351 for (const auto& line : probe_lines) {
352 outputFile << " " << line << "\n";
353 }
354 outputFile << indent << " if (hyperiso_marty_tree_probe.empty()) {\n";
355 if (pair_return) {
356 outputFile << indent
357 << " return std::make_pair(CSL_0, std::size_t{0});\n";
358 } else {
359 outputFile << indent << " return CSL_0;\n";
360 }
361 outputFile << indent << " }\n";
362 outputFile << indent << " " << variable
363 << " = model.getWilsonCoefficients(hyperiso_marty_tree_probe, "
364 << "hyperiso_marty_tree_options);\n";
365 outputFile << indent << "} else {\n";
366 for (const auto& line : loop_lines) {
367 outputFile << " " << line << "\n";
368 }
369 outputFile << indent << "}\n";
370 if (count_graphs) {
371 outputFile << indent << "hyperiso_marty_graph_count += "
372 << variable << ".graphs.size();\n";
373 }
374}
375
376void GeneralModelModifier::modifyLine(std::string& line) {
377 if (this->usesRegPropSplit()) {
378 // The semileptonic templates contain an order literal as the standalone
379 // first argument of computeWilsonCoefficients(). Replace only that
380 // argument. A broad textual replacement also rewrites helper logic such
381 // as `order == mty::Order::TreeLevel` into `order == order`, causing all
382 // one-loop calls to be treated as tree-level and disabling the C9 linker
383 // policy.
384 const auto first = line.find_first_not_of(" \t");
385 const auto last = line.find_last_not_of(" \t");
386 const std::string trimmed = first == std::string::npos
387 ? std::string{}
388 : line.substr(first, last - first + 1);
389 if (trimmed == "mty::Order::TreeLevel,"
390 || trimmed == "mty::Order::OneLoop,"
391 || trimmed == "TreeLevel,"
392 || trimmed == "OneLoop,") {
393 line.replace(first, last - first + 1, "hyperiso_marty_order,");
394 }
395 return;
396 }
397
398 if (this->usesGenericTreeFirst()) {
399 replaceWilsonOrderArgument(line);
400 return;
401 }
402
403 if (line.find("SM_Model sm;") != std::string::npos) {
404 line.replace(line.find("SM_Model"), 8, this->model_instantiation);
405 }
406 else if (line.find("_SM") != std::string::npos) {
407 line.replace(line.find("SM"), 2, this->output_model);
408 }
409}
410
411void GeneralModelModifier::addLine(std::ofstream& outputFile, const std::string& currentLine) {
412 auto is_comment_line = [](const std::string& value) {
413 const auto first = value.find_first_not_of(" \t");
414 return first != std::string::npos
415 && value.compare(first, 2, "//") == 0;
416 };
417
418 if (this->skip_old_main) {
419 return;
420 }
421
422 if (this->usesGenericTreeFirst()) {
423 if (currentLine.find("<iostream>") != std::string::npos) {
424 outputFile << currentLine << "\n";
425 if (this->disable_non_sm_particles || this->bsm_split_generation) {
426 outputFile << "#include <string>\n";
427 outputFile << "#include <unordered_set>\n";
428 outputFile << "#include <vector>\n";
429 }
430 outputFile << "#include \"" + this->model_path + "\"\n";
431 outputFile << "#include \"" + this->marty_path + "\"\n";
432 outputFile << "// " << modelSignature(this->target_model, this->model_path, this->model_template_index) << "\n";
433 outputFile << "// HYPERISO_MARTY_TREE_FIRST: TreeLevel then OneLoop fallback\n";
434 return;
435 }
436
437 if ((this->disable_non_sm_particles || this->bsm_split_generation)
438 && currentLine.find("using namespace sm_input;") != std::string::npos) {
439 outputFile << currentLine << "\n";
440 outputFile << makeSmFilterHelper() << "\n";
441 return;
442 }
443
444 if (currentLine.find("int calculate") != std::string::npos) {
445 std::string signature = currentLine;
446 const auto int_pos = signature.find("int ");
447 const auto open = signature.find('(', int_pos == std::string::npos ? 0 : int_pos + 4);
448 const auto close = signature.rfind(')');
449 if (int_pos == std::string::npos || open == std::string::npos
450 || close == std::string::npos || close <= open) {
451 throw std::runtime_error(
452 "Cannot rewrite MARTY calculate signature for tree-first mode: " + currentLine
453 );
454 }
455 this->generic_builder_name = signature.substr(int_pos + 4, open - (int_pos + 4));
456 // Insert the new argument before changing the return type. Replacing
457 // `int` by the one-character-longer `Expr` first invalidates the
458 // previously computed closing-parenthesis index and used to turn
459 // `gauge` / `hyperiso_marty_order` into `gaug` /
460 // `hyperiso_marty_ordere` in every generic TreeLevel-first wrapper.
461 signature.insert(close, ", mty::Order hyperiso_marty_order");
462 signature.replace(int_pos, 3, "Expr");
463 outputFile << signature << "\n";
464 this->inside_calculate_function = true;
465 this->expression_returned = false;
466 return;
467 }
468
469 if (this->inside_calculate_function
470 && currentLine.find("FeynOptions opts;") != std::string::npos) {
471 outputFile << currentLine << "\n";
472 if (this->disable_non_sm_particles) {
473 outputFile << " hyperiso_marty_disable_non_sm_particles(opts, model);\n";
474 } else if (this->bsm_split_generation && !this->full_target_generation) {
475 outputFile << " hyperiso_marty_require_non_sm_diagram_particle(opts);\n";
476 }
477 return;
478 }
479
480 if (this->inside_calculate_function
481 && consumeTreeSafeWilsonCall(outputFile, currentLine, false, false)) {
482 return;
483 }
484
485 if (this->inside_calculate_function
486 && (currentLine.find("[[maybe_unused]] int sysres") != std::string::npos
487 || currentLine.find("mty::Library wilsonLib") != std::string::npos
488 || currentLine.find("wilsonLib.cleanExistingSources") != std::string::npos
489 || currentLine.find("defineLibPath(wilsonLib)") != std::string::npos
490 || currentLine.find("wilsonLib.print") != std::string::npos)) {
491 return;
492 }
493
494 if (this->inside_calculate_function
495 && currentLine.find("wilsonLib.addFunction") != std::string::npos) {
496 if (!this->expression_returned) {
497 const auto comma = currentLine.find_last_of(',');
498 const auto close = currentLine.rfind(')');
499 if (comma == std::string::npos || close == std::string::npos || close <= comma) {
500 throw std::runtime_error(
501 "Cannot rewrite MARTY addFunction line for tree-first mode: " + currentLine
502 );
503 }
504 const std::string expr = currentLine.substr(comma + 1, close - comma - 1);
505 outputFile << " return " << expr << ";\n";
506 this->expression_returned = true;
507 }
508 return;
509 }
510
511 if (this->inside_calculate_function
512 && currentLine.find("return 0;") != std::string::npos) {
513 if (!this->expression_returned) {
514 outputFile << " return CSL_0;\n";
515 this->expression_returned = true;
516 }
517 return;
518 }
519
520 if (this->inside_calculate_function && currentLine == "}") {
521 this->inside_calculate_function = false;
522 this->expression_returned = false;
523 outputFile << currentLine << "\n";
524 return;
525 }
526
527 if (currentLine.find("int main") != std::string::npos) {
528 if (this->generic_builder_name.empty()) {
529 throw std::runtime_error(
530 "MARTY tree-first generation reached main before calculate function"
531 );
532 }
533 outputFile << "int main() {\n";
534 outputFile << " " << this->model_instantiation << " model;\n";
535 outputFile << " Expr hyperiso_marty_tree = " << this->generic_builder_name
536 << "(model, gauge::Type::Feynman, mty::Order::TreeLevel);\n";
537 outputFile << " const bool hyperiso_marty_use_tree = hyperiso_marty_tree != CSL_0;\n";
538 outputFile << " Expr hyperiso_marty_selected = hyperiso_marty_tree;\n";
539 outputFile << " if (!hyperiso_marty_use_tree) {\n";
540 outputFile << " hyperiso_marty_selected = " << this->generic_builder_name
541 << "(model, gauge::Type::Feynman, mty::Order::OneLoop);\n";
542 outputFile << " }\n";
543 outputFile << " std::cout << \"[MARTY " << this->wilson
544 << "] selected order=\" << (hyperiso_marty_use_tree ? \"TreeLevel\" : \"OneLoop\")"
545 << " << std::endl;\n";
546 outputFile << " [[maybe_unused]] int sysres = system(\"rm -rf libs/"
547 << this->wilson << "_" << this->output_model << "\");\n";
548 outputFile << " mty::Library wilsonLib(\"" << this->wilson << "_"
549 << this->output_model << "\", \"libs\");\n";
550 outputFile << " wilsonLib.cleanExistingSources();\n";
551 outputFile << " wilsonLib.addFunction(\"" << this->wilson
552 << "\", hyperiso_marty_selected);\n";
553 outputFile << " defineLibPath(wilsonLib);\n";
554 outputFile << " wilsonLib.print();\n";
555 outputFile << " return 0;\n";
556 outputFile << "}\n";
557 this->skip_old_main = true;
558 return;
559 }
560
561 outputFile << currentLine << "\n";
562 return;
563 }
564
565 if (this->usesRegPropSplit()) {
566 if (currentLine.find("<iostream>") != std::string::npos) {
567 outputFile << currentLine << "\n";
568 outputFile << "#include <string>\n";
569 outputFile << "#include <unordered_set>\n";
570 outputFile << "#include <vector>\n";
571 outputFile << "#include <cstddef>\n";
572 outputFile << "#include <utility>\n";
573 outputFile << "#include \"" + this->model_path + "\"" << "\n";
574 outputFile << "#include \"" + this->marty_path + "\"" << "\n";
575 outputFile << "// " << modelSignature(this->target_model, this->model_path, this->model_template_index) << "\n";
576 if (this->full_target_generation) {
577 outputFile << "// HYPERISO_MARTY_TARGET_SPLIT: complete target-model diagrams in "
578 << this->model_instantiation << "\n";
579 } else {
580 outputFile << "// HYPERISO_MARTY_BSM_SPLIT: diagrams with at least one non-SM diagram particle in "
581 << this->model_instantiation << "\n";
582 }
583 outputFile << "// HYPERISO_MARTY_BSM_SPLIT_ABI: model-split-v26\n";
584 return;
585 }
586
587 if (currentLine.find("using namespace sm_input;") != std::string::npos) {
588 outputFile << currentLine << "\n";
589 outputFile << makeSmFilterHelper() << "\n";
590 return;
591 }
592
593 if (currentLine.find("int calculate") != std::string::npos) {
594 outputFile << "std::pair<Expr, std::size_t> hyperiso_marty_build_" << this->wilson
595 << "(Model &model, gauge::Type gauge, mty::Order hyperiso_marty_order, "
596 << "bool hyperiso_marty_sm_like_filter = false) {\n";
597 outputFile << " std::size_t hyperiso_marty_graph_count = 0;\n";
598 outputFile << " hyperiso_marty_set_semileptonic_order(hyperiso_marty_order);\n";
599 this->inside_calculate_function = true;
600 this->expression_returned = false;
601 return;
602 }
603
604 if (this->inside_calculate_function && currentLine.find("FeynOptions opts;") != std::string::npos) {
605 outputFile << currentLine << "\n";
606 outputFile << " if (hyperiso_marty_sm_like_filter) {\n";
607 outputFile << " hyperiso_marty_disable_non_sm_particles(opts, model);\n";
608 if (!this->full_target_generation) {
609 outputFile << " } else {\n";
610 }
611 if (!this->full_target_generation) {
612 outputFile << " hyperiso_marty_require_non_sm_diagram_particle(opts);\n";
613 }
614 outputFile << " }\n";
615 return;
616 }
617
618 if (this->inside_calculate_function
619 && consumeTreeSafeWilsonCall(outputFile, currentLine, true, true)) {
620 return;
621 }
622
623 if (is_comment_line(currentLine)) {
624 outputFile << currentLine << "\n";
625 return;
626 }
627
628 if (this->inside_calculate_function && currentLine.find("[[maybe_unused]] int sysres") != std::string::npos) {
629 return;
630 }
631 if (this->inside_calculate_function && currentLine.find("mty::Library wilsonLib") != std::string::npos) {
632 return;
633 }
634 if (this->inside_calculate_function && currentLine.find("wilsonLib.cleanExistingSources") != std::string::npos) {
635 return;
636 }
637 if (this->inside_calculate_function && currentLine.find("defineLibPath(wilsonLib)") != std::string::npos) {
638 return;
639 }
640 if (this->inside_calculate_function && currentLine.find("wilsonLib.print") != std::string::npos) {
641 return;
642 }
643 if (this->inside_calculate_function && currentLine.find("wilsonLib.addFunction") != std::string::npos) {
644 if (!this->expression_returned) {
645 const auto comma = currentLine.find_last_of(',');
646 const auto close = currentLine.rfind(')');
647 if (comma == std::string::npos || close == std::string::npos || close <= comma) {
648 throw std::runtime_error("Cannot rewrite MARTY addFunction line for BSM split: " + currentLine);
649 }
650 std::string expr = currentLine.substr(comma + 1, close - comma - 1);
651 outputFile << " return std::make_pair(" << expr << ", hyperiso_marty_graph_count);\n";
652 this->expression_returned = true;
653 }
654 return;
655 }
656 if (this->inside_calculate_function && currentLine.find("return 0;") != std::string::npos) {
657 if (!this->expression_returned) {
658 outputFile << " return std::make_pair(CSL_0, hyperiso_marty_graph_count);\n";
659 this->expression_returned = true;
660 }
661 return;
662 }
663 if (this->inside_calculate_function && currentLine == "}") {
664 this->inside_calculate_function = false;
665 this->expression_returned = false;
666 outputFile << currentLine << "\n";
667 return;
668 }
669
670 if (currentLine.find("int main") != std::string::npos) {
671 const bool split_sm_components = false;
672 const bool split_linker_components = (this->wilson == "CP10");
673 outputFile << "int main() {\n";
674 const bool isolate_c9_tree_probe = (this->wilson == "C9");
675 const std::string tree_model_name = isolate_c9_tree_probe ? "tree_model" : "model";
676 const std::string loop_model_name = isolate_c9_tree_probe ? "loop_model" : "model";
677 if (isolate_c9_tree_probe) {
678 outputFile << " // C9 is sensitive to MARTY state accumulated while probing TreeLevel.\n";
679 outputFile << " // Use a fresh target model for the OneLoop fallback so loop-only models\n";
680 outputFile << " // reproduce the historical one-loop-only result exactly.\n";
681 outputFile << " " << this->model_instantiation << " tree_model;\n";
682 outputFile << " " << this->model_instantiation << " loop_model;\n";
683 } else {
684 outputFile << " " << this->model_instantiation << " model;\n";
685 }
686 outputFile << " // Semileptonic BSM matching is tree-first. A non-zero tree-level\n";
687 outputFile << " // coefficient suppresses the one-loop calculation entirely; otherwise\n";
688 outputFile << " // MARTY falls back to the one-loop split-reg_prop path.\n";
689 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::NonPhotonVector);\n";
690 outputFile << " auto hyperiso_marty_bsm_tree = hyperiso_marty_build_" << this->wilson
691 << "(" << tree_model_name
692 << ", gauge::Type::Feynman, mty::Order::TreeLevel, false);\n";
693 outputFile << " const bool hyperiso_marty_use_tree_level = hyperiso_marty_bsm_tree.first != CSL_0;\n";
694 outputFile << " const char* hyperiso_marty_selected_order = hyperiso_marty_use_tree_level ? \"TreeLevel\" : \"OneLoop\";\n";
695 outputFile << " Expr hyperiso_marty_bsm = hyperiso_marty_bsm_tree.first;\n";
696 outputFile << " Expr hyperiso_marty_bsm_photon = CSL_0;\n";
697 outputFile << " Expr hyperiso_marty_bsm_scalar = CSL_0;\n";
698 outputFile << " Expr hyperiso_marty_bsm_vector = hyperiso_marty_bsm_tree.first;\n";
699 outputFile << " std::size_t hyperiso_marty_non_photon_graph_count = hyperiso_marty_bsm_tree.second;\n";
700 outputFile << " std::size_t hyperiso_marty_photon_graph_count = 0;\n";
701 outputFile << " std::size_t hyperiso_marty_scalar_graph_count = 0;\n";
702 outputFile << " std::size_t hyperiso_marty_vector_graph_count = hyperiso_marty_bsm_tree.second;\n";
703 outputFile << " if (!hyperiso_marty_use_tree_level) {\n";
704 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::NonPhotonVector);\n";
705 outputFile << " auto hyperiso_marty_bsm_loop = hyperiso_marty_build_" << this->wilson
706 << "(" << loop_model_name
707 << ", gauge::Type::Feynman, mty::Order::OneLoop, false);\n";
708 outputFile << " hyperiso_marty_bsm = hyperiso_marty_bsm_loop.first;\n";
709 outputFile << " hyperiso_marty_non_photon_graph_count = hyperiso_marty_bsm_loop.second;\n";
710 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::PhotonOnly);\n";
711 outputFile << " auto hyperiso_marty_bsm_photon_loop = hyperiso_marty_build_" << this->wilson
712 << "(" << loop_model_name
713 << ", gauge::Type::Feynman, mty::Order::OneLoop, false);\n";
714 outputFile << " hyperiso_marty_bsm_photon = hyperiso_marty_bsm_photon_loop.first;\n";
715 outputFile << " hyperiso_marty_photon_graph_count = hyperiso_marty_bsm_photon_loop.second;\n";
716 if (split_linker_components) {
717 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::ScalarOnly);\n";
718 outputFile << " auto hyperiso_marty_bsm_scalar_loop = hyperiso_marty_build_" << this->wilson
719 << "(" << loop_model_name
720 << ", gauge::Type::Feynman, mty::Order::OneLoop, false);\n";
721 outputFile << " hyperiso_marty_bsm_scalar = hyperiso_marty_bsm_scalar_loop.first;\n";
722 outputFile << " hyperiso_marty_scalar_graph_count = hyperiso_marty_bsm_scalar_loop.second;\n";
723 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::VectorOnly);\n";
724 outputFile << " auto hyperiso_marty_bsm_vector_loop = hyperiso_marty_build_" << this->wilson
725 << "(" << loop_model_name
726 << ", gauge::Type::Feynman, mty::Order::OneLoop, false);\n";
727 outputFile << " hyperiso_marty_bsm_vector = hyperiso_marty_bsm_vector_loop.first;\n";
728 outputFile << " hyperiso_marty_vector_graph_count = hyperiso_marty_bsm_vector_loop.second;\n";
729 } else {
730 outputFile << " hyperiso_marty_bsm_vector = hyperiso_marty_bsm;\n";
731 outputFile << " hyperiso_marty_vector_graph_count = hyperiso_marty_non_photon_graph_count;\n";
732 }
733 outputFile << " }\n";
734 if (split_linker_components) {
735 outputFile << " else {\n";
736 outputFile << " // At tree level the penguin linker split is disabled. Expose the\n";
737 outputFile << " // complete tree coefficient as VECTOR for diagnostics and do not\n";
738 outputFile << " // trigger additional tree or one-loop calculations.\n";
739 outputFile << " hyperiso_marty_bsm_vector = hyperiso_marty_bsm;\n";
740 outputFile << " hyperiso_marty_vector_graph_count = hyperiso_marty_bsm_tree.second;\n";
741 outputFile << " }\n";
742 }
743 outputFile << " std::cout << \"[MARTY " << this->wilson << "] "
744 << (this->full_target_generation ? "target" : "BSM")
745 << " selected order=\" << hyperiso_marty_selected_order"
746 << " << \", tree=\" << hyperiso_marty_bsm_tree.second"
747 << " << \", non-photon=\" << hyperiso_marty_non_photon_graph_count"
748 << " << \", photon=\" << hyperiso_marty_photon_graph_count";
749 if (split_linker_components) {
750 outputFile << " << \", scalar=\" << hyperiso_marty_scalar_graph_count"
751 << " << \", vector=\" << hyperiso_marty_vector_graph_count";
752 }
753 outputFile << " << std::endl;\n";
754 if (split_sm_components) {
755 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::NonPhotonVector);\n";
756 outputFile << " auto hyperiso_marty_sm_loop = hyperiso_marty_build_" << this->wilson
757 << "(model, gauge::Type::Feynman, mty::Order::OneLoop, true);\n";
758 outputFile << " Expr hyperiso_marty_sm = hyperiso_marty_sm_loop.first;\n";
759 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::PhotonOnly);\n";
760 outputFile << " auto hyperiso_marty_sm_photon_loop = hyperiso_marty_build_" << this->wilson
761 << "(model, gauge::Type::Feynman, mty::Order::OneLoop, true);\n";
762 outputFile << " Expr hyperiso_marty_sm_photon = hyperiso_marty_sm_photon_loop.first;\n";
763 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::ScalarOnly);\n";
764 outputFile << " auto hyperiso_marty_sm_scalar_loop = hyperiso_marty_build_" << this->wilson
765 << "(model, gauge::Type::Feynman, mty::Order::OneLoop, true);\n";
766 outputFile << " Expr hyperiso_marty_sm_scalar = hyperiso_marty_sm_scalar_loop.first;\n";
767 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::VectorOnly);\n";
768 outputFile << " auto hyperiso_marty_sm_vector_loop = hyperiso_marty_build_" << this->wilson
769 << "(model, gauge::Type::Feynman, mty::Order::OneLoop, true);\n";
770 outputFile << " Expr hyperiso_marty_sm_vector = hyperiso_marty_sm_vector_loop.first;\n";
771 } else {
772 outputFile << " Expr hyperiso_marty_sm = CSL_0;\n";
773 outputFile << " Expr hyperiso_marty_sm_photon = CSL_0;\n";
774 outputFile << " Expr hyperiso_marty_sm_scalar = CSL_0;\n";
775 outputFile << " Expr hyperiso_marty_sm_vector = CSL_0;\n";
776 }
777 outputFile << " hyperiso_marty_set_c9_linker_selection(HyperisoMartyC9LinkerSelection::NonPhotonVector);\n";
778 outputFile << " [[maybe_unused]] int sysres = system(\"rm -rf libs/" << this->wilson << "_" << this->output_model << "\");\n";
779 outputFile << " mty::Library wilsonLib(\"" << this->wilson << "_" << this->output_model << "\", \"libs\");\n";
780 outputFile << " wilsonLib.cleanExistingSources();\n";
781 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "\", hyperiso_marty_bsm);\n";
782 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_A\", hyperiso_marty_bsm_photon);\n";
783 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_SCALAR\", hyperiso_marty_bsm_scalar);\n";
784 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_VECTOR\", hyperiso_marty_bsm_vector);\n";
785 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_SM\", hyperiso_marty_sm);\n";
786 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_SM_A\", hyperiso_marty_sm_photon);\n";
787 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_SM_SCALAR\", hyperiso_marty_sm_scalar);\n";
788 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_SM_VECTOR\", hyperiso_marty_sm_vector);\n";
789 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_TOT\", hyperiso_marty_sm + hyperiso_marty_bsm);\n";
790 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_TOT_A\", hyperiso_marty_sm_photon + hyperiso_marty_bsm_photon);\n";
791 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_TOT_SCALAR\", hyperiso_marty_sm_scalar + hyperiso_marty_bsm_scalar);\n";
792 outputFile << " wilsonLib.addFunction(\"" << this->wilson << "_TOT_VECTOR\", hyperiso_marty_sm_vector + hyperiso_marty_bsm_vector);\n";
793 outputFile << " defineLibPath(wilsonLib);\n";
794 outputFile << " wilsonLib.print();\n";
795 outputFile << " return 0;\n";
796 outputFile << "}\n";
797 this->skip_old_main = true;
798 return;
799 }
800
801 outputFile << currentLine << "\n";
802 return;
803 }
804
805 if (currentLine.find("<iostream>") != std::string::npos) {
806 outputFile << currentLine << "\n";
807 if (this->disable_non_sm_particles || this->bsm_split_generation) {
808 outputFile << "#include <string>\n";
809 outputFile << "#include <unordered_set>\n";
810 outputFile << "#include <vector>\n";
811 }
812 outputFile << "#include \"" + this->model_path + "\"" << "\n";
813 outputFile << "#include \"" + this->marty_path + "\"" << "\n";
814 outputFile << "// " << modelSignature(this->target_model, this->model_path, this->model_template_index) << "\n";
815 if (this->disable_non_sm_particles) {
816 outputFile << "// HYPERISO_MARTY_SM_LIKE_FILTER: disable non-SM particles in "
817 << this->model_instantiation << "\n";
818 } else if (this->bsm_split_generation) {
819 outputFile << "// HYPERISO_MARTY_BSM_ONLY_FILTER: require a non-SM diagram particle in "
820 << this->model_instantiation << "\n";
821 }
822 }
823 else if ((this->disable_non_sm_particles || this->bsm_split_generation)
824 && currentLine.find("using namespace sm_input;") != std::string::npos) {
825 outputFile << currentLine << "\n";
826 outputFile << makeSmFilterHelper() << "\n";
827 }
828 else if (this->disable_non_sm_particles
829 && currentLine.find("FeynOptions opts;") != std::string::npos) {
830 outputFile << currentLine << "\n";
831 outputFile << " hyperiso_marty_disable_non_sm_particles(opts, model);\n";
832 }
833 else if (this->bsm_split_generation
834 && currentLine.find("FeynOptions opts;") != std::string::npos) {
835 outputFile << currentLine << "\n";
836 outputFile << " hyperiso_marty_require_non_sm_diagram_particle(opts);\n";
837 }
838 else {
839 outputFile << currentLine << "\n";
840 }
841
842}
Declares a general-purpose ModelModifier for non-numeric templates.
ModelModifier that rewrites SM templates into a target model.
static std::string modelSignature(const std::string &model, const std::string &model_path, std::optional< int > model_template_index=std::nullopt)
Build the cache signature written in generated analytical files.
void addLine(std::ofstream &outputFile, const std::string &currentLine) override
Writes a (possibly modified) line to the output file.
void modifyLine(std::string &line) override
Modifies a single line of source code in place.
GeneralModelModifier(std::string wilson, std::string model, std::string model_path, std::optional< int > model_template_index=std::nullopt)
Constructs a modifier for a given (Wilson, model) pair and model header.
static std::string resolveModelInstantiation(const std::string &model, const std::string &model_path, std::optional< int > model_template_index=std::nullopt)
Resolve the C++ model instantiation used by generated MARTY code.
static InstallInfo require_available(const std::string &context)
Validate and return the active MARTY installation.
Checks and resolves MARTY model classes in a model header.
ModelClassInfo resolveModelClass(const std::string &model) const
Resolve the concrete C++ class name for a user-provided model name.
std::string wilson
Name of the Wilson operator basis used in the model.
Hash specialization for SymbolId<Tag>.
Definition BlockName.h:353
std::string class_name