Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
MartyInterface.cpp
Go to the documentation of this file.
1#include "MartyInterface.h"
2#include "ModelAPI.h"
6#include "ParamWriter.h"
7
8#include <algorithm>
9#include <atomic>
10#include <chrono>
11#include <cmath>
12#include <cctype>
13#include <cstdint>
14#include <fstream>
15#include <iomanip>
16#include <map>
17#include <mutex>
18#include <optional>
19#include <random>
20#include <regex>
21#include <shared_mutex>
22#include <sstream>
23#include <thread>
24#include <unordered_map>
25#include <vector>
26
27namespace fs = std::filesystem;
28
29namespace {
30std::shared_mutex marty_artifact_mutex;
31std::mutex marty_legacy_csv_mutex;
32std::atomic<std::uint64_t> marty_run_counter {0};
33
34constexpr const char* kMartyCacheAbi = "HYPERISO_MARTY_CACHE_ABI: pyhyperiso-1.0.3-v5";
35
36std::string sanitize_path_component(std::string value) {
37 for (char& c : value) {
38 const auto uc = static_cast<unsigned char>(c);
39 if (!std::isalnum(uc) && c != '-' && c != '_') {
40 c = '_';
41 }
42 }
43 return value.empty() ? std::string("unnamed") : value;
44}
45
46fs::path make_invocation_directory(const std::shared_ptr<FileNameManager>& files,
47 const std::string& wilson,
48 const std::string& model) {
49 static const std::uint64_t process_nonce = [] {
50 std::random_device random;
51 return (static_cast<std::uint64_t>(random()) << 32)
52 ^ static_cast<std::uint64_t>(random());
53 }();
54 const auto counter = marty_run_counter.fetch_add(1, std::memory_order_relaxed);
55 const auto now = std::chrono::steady_clock::now().time_since_epoch().count();
56 const auto thread_hash = std::hash<std::thread::id>{}(std::this_thread::get_id());
57
58 std::ostringstream name;
59 name << sanitize_path_component(model) << "_"
60 << sanitize_path_component(wilson) << "_"
61 << std::hex << process_nonce << "_" << now << "_"
62 << thread_hash << "_" << counter;
63
64 const fs::path dir = fs::path(files->getOutputDir()) / "runs" / name.str();
65 std::error_code ec;
66 fs::create_directories(dir, ec);
67 if (ec) {
68 throw std::runtime_error(
69 "Cannot create invocation-local MARTY directory: " + dir.string()
70 + " (" + ec.message() + ")"
71 );
72 }
73 return dir;
74}
75
76void write_parameter_snapshot(const fs::path& path,
77 const std::unordered_map<std::string, double>& params) {
78 std::ofstream output(path, std::ios::trunc);
79 if (!output) {
80 throw std::runtime_error("Cannot write MARTY parameter snapshot: " + path.string());
81 }
82
83 // Reuse the exact legacy serializer instead of changing the numerical
84 // representation merely because the file is invocation-local. The path
85 // isolation provides thread safety; parameter rounding must remain
86 // backward-compatible with the pre-thread-safe implementation.
87 ParamWriter parameter_writer;
88 parameter_writer.writeParams(output, params);
89 output.flush();
90 if (!output) {
91 throw std::runtime_error("Failed while writing MARTY parameter snapshot: " + path.string());
92 }
93}
94
95void publish_legacy_csv(const fs::path& isolated, const fs::path& legacy) {
96 std::lock_guard<std::mutex> lock(marty_legacy_csv_mutex);
97
98 const auto split_csv_line = [](const std::string& line) {
99 std::vector<std::string> cells;
100 std::stringstream stream(line);
101 std::string cell;
102 while (std::getline(stream, cell, ',')) {
103 cells.push_back(cell);
104 }
105 return cells;
106 };
107
108 struct CsvTable {
109 std::vector<std::string> headers;
110 std::vector<std::vector<std::string>> rows;
111 };
112
113 const auto read_table = [&](const fs::path& path, bool required) {
114 CsvTable table;
115 std::ifstream input(path);
116 if (!input) {
117 if (required) {
118 throw std::runtime_error("Cannot read MARTY CSV: " + path.string());
119 }
120 return table;
121 }
122 std::string line;
123 if (std::getline(input, line)) {
124 table.headers = split_csv_line(line);
125 }
126 while (std::getline(input, line)) {
127 if (!line.empty()) {
128 table.rows.push_back(split_csv_line(line));
129 }
130 }
131 return table;
132 };
133
134 CsvTable incoming = read_table(isolated, true);
135 if (incoming.headers.empty() || incoming.headers.front() != "Q_match") {
136 throw std::runtime_error("Invalid invocation-local MARTY CSV: " + isolated.string());
137 }
138
139 CsvTable merged = read_table(legacy, false);
140 if (merged.headers.empty()) {
141 merged.headers = {"Q_match"};
142 }
143 if (merged.headers.front() != "Q_match") {
144 throw std::runtime_error("Invalid legacy MARTY CSV: " + legacy.string());
145 }
146
147 std::unordered_map<std::string, std::size_t> merged_columns;
148 for (std::size_t i = 0; i < merged.headers.size(); ++i) {
149 merged_columns.emplace(merged.headers[i], i);
150 }
151 for (std::size_t i = 1; i < incoming.headers.size(); ++i) {
152 if (!merged_columns.contains(incoming.headers[i])) {
153 merged_columns.emplace(incoming.headers[i], merged.headers.size());
154 merged.headers.push_back(incoming.headers[i]);
155 for (auto& row : merged.rows) {
156 row.resize(merged.headers.size(), "NaN");
157 }
158 }
159 }
160
161 std::map<double, std::size_t> merged_rows;
162 for (std::size_t i = 0; i < merged.rows.size(); ++i) {
163 merged.rows[i].resize(merged.headers.size(), "NaN");
164 if (!merged.rows[i].empty()) {
165 merged_rows[std::stod(merged.rows[i][0])] = i;
166 }
167 }
168
169 for (const auto& incoming_row : incoming.rows) {
170 if (incoming_row.empty()) {
171 continue;
172 }
173 const double q_match = std::stod(incoming_row[0]);
174 std::size_t row_index = 0;
175 const auto existing = merged_rows.find(q_match);
176 if (existing == merged_rows.end()) {
177 row_index = merged.rows.size();
178 merged.rows.emplace_back(merged.headers.size(), "NaN");
179 merged.rows.back()[0] = incoming_row[0];
180 merged_rows.emplace(q_match, row_index);
181 } else {
182 row_index = existing->second;
183 }
184
185 for (std::size_t i = 1; i < incoming.headers.size() && i < incoming_row.size(); ++i) {
186 merged.rows[row_index][merged_columns.at(incoming.headers[i])] = incoming_row[i];
187 }
188 }
189
190 std::error_code ec;
191 fs::create_directories(legacy.parent_path(), ec);
192 if (ec) {
193 throw std::runtime_error("Cannot create MARTY CSV directory: " + ec.message());
194 }
195
196 const fs::path tmp = legacy.string() + ".tmp." + std::to_string(
197 marty_run_counter.fetch_add(1, std::memory_order_relaxed)
198 );
199 {
200 std::ofstream output(tmp, std::ios::trunc);
201 if (!output) {
202 throw std::runtime_error("Cannot write temporary MARTY CSV: " + tmp.string());
203 }
204 for (std::size_t i = 0; i < merged.headers.size(); ++i) {
205 output << merged.headers[i] << (i + 1 == merged.headers.size() ? '\n' : ',');
206 }
207 for (const auto& row : merged.rows) {
208 for (std::size_t i = 0; i < merged.headers.size(); ++i) {
209 output << (i < row.size() ? row[i] : "NaN")
210 << (i + 1 == merged.headers.size() ? '\n' : ',');
211 }
212 }
213 output.flush();
214 if (!output) {
215 std::error_code cleanup_ec;
216 fs::remove(tmp, cleanup_ec);
217 throw std::runtime_error("Failed while writing temporary MARTY CSV: " + tmp.string());
218 }
219 }
220
221 fs::rename(tmp, legacy, ec);
222 if (ec) {
223 std::error_code cleanup_ec;
224 fs::remove(tmp, cleanup_ec);
225 throw std::runtime_error("Cannot atomically publish MARTY CSV: " + ec.message());
226 }
227}
228
229std::string template_signature(const std::string& wilson,
230 const std::shared_ptr<FileNameManager>& files);
231std::string generation_mode_marker(const std::string& wilson,
232 bool sm_like_filter,
233 bool bsm_only_generation,
234 bool full_target_generation);
235void append_cache_metadata_if_missing(const fs::path& generated_file,
236 const std::string& model_signature,
237 const std::string& template_signature_value,
238 const std::string& mode_marker);
239bool template_needs_generic_tree_first(const std::string& wilson,
240 const std::shared_ptr<FileNameManager>& files);
241} // namespace
242
243
245 core_api = std::make_shared<ModelAPI>();
246 param_proxy_sm = std::make_shared<MartyParameterProxy>(ParameterType::SM);
247 param_proxy_bsm = std::make_shared<MartyParameterProxy>(ParameterType::BSM);
248 ports = std::make_shared<DefaultInterpreterPortsFactory>();
249}
250
251
252void MartyInterface::compile_run(std::string wilson, std::string model) {
253 if (!MartyRuntimeConfig::require_available("MartyInterface::compile_run").valid) {
254 return;
255 }
256
257 GppCompilerStrategy compiler(model, wilson);
258 const auto files = FileNameManager::getInstance(wilson, model);
259 if (!this->already_run(files->getExecutableFileName())) {
260 compiler.compile_run(files->getGeneratedFileName(), files->getExecutableFileName());
261 }
262}
263
264void MartyInterface::generate(std::string wilson, std::string model, std::string model_path) {
265 generate(std::move(wilson), model, model, std::move(model_path), false, false, false);
266}
267
268void MartyInterface::generate(std::string wilson,
269 std::string output_model,
270 std::string target_model,
271 std::string model_path,
272 bool sm_like_filter,
273 bool bsm_split_generation,
274 bool full_target_generation) {
275 if (!MartyRuntimeConfig::require_available("MartyInterface::generate").valid) {
276 return;
277 }
278
279 const auto model_template_index = resolve_model_template_index(target_model);
280 const auto files = FileNameManager::getInstance(wilson, output_model);
281 const bool tree_first_fallback = template_needs_generic_tree_first(wilson, files);
282 invalidate_template_model_cache_if_needed(
283 wilson, output_model, target_model, model_path, model_template_index,
284 sm_like_filter, bsm_split_generation, full_target_generation
285 );
286
287 std::unique_ptr<ModelModifier> smModifier;
288 smModifier = std::make_unique<GeneralModelModifier>(
289 wilson, output_model, target_model, model_path, model_template_index,
290 sm_like_filter, bsm_split_generation, full_target_generation,
291 tree_first_fallback
292 );
293
294 std::unique_ptr<TemplateManagerBase> templateManager = std::make_unique<NonNumericTemplateManager>(files->getTemplateDir());
295 templateManager->setModelAndWilson(output_model, wilson);
296 templateManager->setModelModifier(std::move(smModifier));
297
298 CodeGenerator codeGenerator(std::move(templateManager));
299
300 codeGenerator.generate(wilson, files->getGeneratedFileName());
301 append_cache_metadata_if_missing(
302 files->getGeneratedFileName(),
303 GeneralModelModifier::modelSignature(target_model, model_path, model_template_index),
304 template_signature(wilson, files),
305 generation_mode_marker(wilson, sm_like_filter, bsm_split_generation, full_target_generation)
306 );
307}
308
309void MartyInterface::generate_numlib(std::string wilson, std::string model) {
310 generate_numlib(std::move(wilson), model, model, false, false);
311}
312
313void MartyInterface::generate_numlib(std::string wilson,
314 std::string output_model,
315 std::string target_model,
316 bool bsm_split_generation,
317 bool full_target_generation) {
318 if (!MartyRuntimeConfig::require_available("MartyInterface::generate_numlib").valid) {
319 return;
320 }
321
322 bool forceMode = false;
323 auto file_names = FileNameManager::getInstance(wilson, output_model);
324 const std::string cinematic_template = file_names->getGeneratedFileName();
325
326 std::unique_ptr<SMParamSetter> sm_p_setter = std::make_unique<SMParamSetter>(
327 target_model,
328 specials_block,
329 param_proxy_sm,
330 param_proxy_bsm,
331 cinematic_template
332 );
333
334 std::unique_ptr<GeneralNumModelModifier> ModelModifier = std::make_unique<GeneralNumModelModifier>(
335 wilson,
336 output_model,
337 target_model,
338 std::move(sm_p_setter),
339 core_api,
340 ports,
341 forceMode,
342 bsm_split_generation,
343 full_target_generation
344 );
345
346 std::unique_ptr<TemplateManagerBase> templateManager = std::make_unique<NumericTemplateManager>(file_names->getLibDir());
347 templateManager->setModelAndWilson(output_model, wilson);
348 templateManager->setNumModelModifier(std::move(ModelModifier));
349 const auto discovered_dependencies = templateManager->get_dependencies();
350 auto& cached_dependencies = this->dependencies[wilson];
351 cached_dependencies.insert(discovered_dependencies.begin(), discovered_dependencies.end());
352 CodeGenerator codeGenerator(std::move(templateManager));
353 std::string file_path = file_names->getNumGeneratedFileName();
354 codeGenerator.generate(file_path, file_path);
355}
356
357void MartyInterface::compile_run_libs(std::string wilson, std::string model, double Q_match) {
358 if (!MartyRuntimeConfig::require_available("MartyInterface::compile_run_libs").valid) {
359 return;
360 }
361
362 MakeCompilerStrategy compiler(model, wilson);
363 compiler.set_Q_match(Q_match);
364 compiler.compile_run(FileNameManager::getInstance(wilson, model)->getLibDir(), FileNameManager::getInstance(wilson,model)->getNumExecutableFileName());
365}
366
367void MartyInterface::calculate(std::string wilson, std::string model, double Q_match, std::string model_path) {
368 calculate(std::move(wilson), model, model, Q_match, std::move(model_path), false, false, false);
369}
370
371void MartyInterface::calculate(std::string wilson,
372 std::string output_model,
373 std::string target_model,
374 double Q_match,
375 std::string model_path,
376 bool sm_like_filter,
377 bool bsm_split_generation,
378 bool full_target_generation) {
379 if (!MartyRuntimeConfig::require_available("MartyInterface::calculate").valid) {
380 return;
381 }
382
383 const fs::path isolated_csv = calculate_isolated(
384 wilson,
385 output_model,
386 target_model,
387 Q_match,
388 model_path,
389 sm_like_filter,
390 bsm_split_generation,
391 full_target_generation
392 );
393 if (isolated_csv.empty()) {
394 return;
395 }
396
397 const fs::path legacy_csv = FileNameManager::getInstance(wilson, output_model)->getCsvWilsonFileName();
398 try {
399 publish_legacy_csv(isolated_csv, legacy_csv);
400 } catch (...) {
401 std::error_code cleanup_ec;
402 fs::remove_all(isolated_csv.parent_path(), cleanup_ec);
403 throw;
404 }
405
406 std::error_code cleanup_ec;
407 fs::remove_all(isolated_csv.parent_path(), cleanup_ec);
408}
409
410void MartyInterface::compile_numlib(const std::string& wilson, const std::string& model) {
411 const auto files = FileNameManager::getInstance(wilson, model);
412 MakeCompilerStrategy compiler(model, wilson);
413 if (!compiler.check_if_compile(files->getNumExecutableFileName())) {
414 compiler.compile(files->getLibDir(), files->getNumExecutableFileName());
415 }
416}
417
418std::unordered_map<std::string, double> MartyInterface::snapshot_numeric_params(
419 const std::string& wilson,
420 const std::string& output_model,
421 const std::string& target_model,
422 bool bsm_split_generation,
423 bool full_target_generation
424) {
425 const auto files = FileNameManager::getInstance(wilson, output_model);
426 auto setter = std::make_unique<SMParamSetter>(
427 target_model,
428 specials_block,
429 param_proxy_sm,
430 param_proxy_bsm,
431 files->getGeneratedFileName()
432 );
434 wilson,
435 output_model,
436 target_model,
437 std::move(setter),
438 core_api,
439 ports,
440 false,
441 bsm_split_generation,
442 full_target_generation
443 );
444 return modifier.get_params();
445}
446
447void MartyInterface::ensure_built(const std::string& wilson,
448 const std::string& output_model,
449 const std::string& target_model,
450 const std::string& model_path,
451 bool sm_like_filter,
452 bool bsm_split_generation,
453 bool full_target_generation) {
454 generate(
455 wilson,
456 output_model,
457 target_model,
458 model_path,
459 sm_like_filter,
460 bsm_split_generation,
461 full_target_generation
462 );
463 compile_run(wilson, output_model);
465 wilson,
466 output_model,
467 target_model,
468 bsm_split_generation,
469 full_target_generation
470 );
471 compile_numlib(wilson, output_model);
472}
473
474bool MartyInterface::artifacts_ready(const std::string& wilson,
475 const std::string& output_model,
476 const std::string& target_model,
477 const std::string& model_path,
478 bool sm_like_filter,
479 bool bsm_split_generation,
480 bool full_target_generation) const {
481 const auto files = FileNameManager::getInstance(wilson, output_model);
482 const auto model_template_index = resolve_model_template_index(target_model);
483 const std::string expected_model_signature = GeneralModelModifier::modelSignature(
484 target_model,
485 model_path,
486 model_template_index
487 );
488 const std::string expected_template_signature = template_signature(wilson, files);
489 const std::string expected_mode = generation_mode_marker(
490 wilson,
491 sm_like_filter,
492 bsm_split_generation,
493 full_target_generation
494 );
495
496 std::ifstream generated(files->getGeneratedFileName());
497 if (!generated) {
498 return false;
499 }
500
501 bool has_cache_abi = false;
502 bool has_model_signature = false;
503 bool has_template_signature = false;
504 bool has_generation_mode = false;
505 std::string line;
506 while (std::getline(generated, line)) {
507 has_cache_abi = has_cache_abi || line.find(kMartyCacheAbi) != std::string::npos;
508 has_model_signature = has_model_signature || line.find(expected_model_signature) != std::string::npos;
509 has_template_signature = has_template_signature || line.find(expected_template_signature) != std::string::npos;
510 has_generation_mode = has_generation_mode || line.find(expected_mode) != std::string::npos;
511 }
512
513 const auto non_empty_file = [](const fs::path& path) {
514 std::error_code ec;
515 return fs::is_regular_file(path, ec) && !ec && fs::file_size(path, ec) > 0 && !ec;
516 };
517 const auto has_generation_marker = [](const fs::path& path) {
518 std::ifstream input(path);
519 std::string header;
520 for (int i = 0; i < 16 && std::getline(input, header); ++i) {
521 if (header.find("//42") != std::string::npos) {
522 return true;
523 }
524 }
525 return false;
526 };
527
528 return has_cache_abi
529 && has_model_signature
530 && has_template_signature
531 && has_generation_mode
532 && non_empty_file(files->getExecutableFileName())
533 && non_empty_file(files->getNumGeneratedFileName())
534 && has_generation_marker(files->getNumGeneratedFileName())
535 && non_empty_file(files->getNumExecutableFileName())
536 && this->dependencies.contains(wilson);
537}
538
539std::string MartyInterface::calculate_isolated(std::string wilson,
540 std::string output_model,
541 std::string target_model,
542 double Q_match,
543 std::string model_path,
544 bool sm_like_filter,
545 bool bsm_split_generation,
546 bool full_target_generation) {
547 if (!MartyRuntimeConfig::require_available("MartyInterface::calculate_isolated").valid) {
548 return {};
549 }
550
551 const auto execute_isolated = [&]() -> std::string {
552 const auto files = FileNameManager::getInstance(wilson, output_model);
553 const fs::path run_dir = make_invocation_directory(files, wilson, output_model);
554 const fs::path param_file = run_dir / "paramlist.csv";
555 const fs::path output_file = run_dir / "wilson.csv";
556
557 try {
558 write_parameter_snapshot(
559 param_file,
560 snapshot_numeric_params(
561 wilson,
562 output_model,
563 target_model,
564 bsm_split_generation,
565 full_target_generation
566 )
567 );
568
569 MakeCompilerStrategy compiler(output_model, wilson);
570 compiler.set_Q_match(Q_match);
571 compiler.set_param_file(param_file);
572 compiler.set_output_file(output_file);
573 compiler.compile_run(files->getLibDir(), files->getNumExecutableFileName());
574
575 std::error_code ec;
576 if (!fs::is_regular_file(output_file, ec) || ec || fs::file_size(output_file, ec) == 0 || ec) {
577 throw std::runtime_error(
578 "MARTY numeric execution did not create a non-empty invocation-local CSV: "
579 + output_file.string()
580 );
581 }
582 return output_file.string();
583 } catch (...) {
584 std::error_code cleanup_ec;
585 fs::remove_all(run_dir, cleanup_ec);
586 throw;
587 }
588 };
589
590 {
591 std::shared_lock<std::shared_mutex> read_lock(marty_artifact_mutex);
592 if (artifacts_ready(
593 wilson,
594 output_model,
595 target_model,
596 model_path,
597 sm_like_filter,
598 bsm_split_generation,
599 full_target_generation
600 )) {
601 return execute_isolated();
602 }
603 }
604
605 std::unique_lock<std::shared_mutex> build_lock(marty_artifact_mutex);
606 if (!artifacts_ready(
607 wilson,
608 output_model,
609 target_model,
610 model_path,
611 sm_like_filter,
612 bsm_split_generation,
613 full_target_generation
614 )) {
615 ensure_built(
616 wilson,
617 output_model,
618 target_model,
619 model_path,
620 sm_like_filter,
621 bsm_split_generation,
622 full_target_generation
623 );
624 }
625 return execute_isolated();
626}
627
628
629std::optional<int> MartyInterface::resolve_model_template_index(const std::string& model) const {
630 std::string model_upper = model;
631 std::transform(model_upper.begin(), model_upper.end(), model_upper.begin(), [](unsigned char c) {
632 return static_cast<char>(std::toupper(c));
633 });
634
635 if (model_upper != "THDM") {
636 return std::nullopt;
637 }
638
639 if (!param_proxy_bsm) {
640 LOG_ERROR("MartyConfigError", "Cannot instantiate the templated THDM MARTY model: no BSM parameter proxy is available to read MINPAR(24). ",
641 "Set the THDM Yukawa type in the LHA card or provide a BSM parameter provider before MARTY generation.");
642 }
643
644 const double raw_type = (*param_proxy_bsm)("MINPAR", LhaID(24));
645 const int type = static_cast<int>(std::lround(raw_type));
646
647 if (std::abs(raw_type - static_cast<double>(type)) > 1e-9 || type < 1 || type > 4) {
648 LOG_ERROR("MartyConfigError", "Invalid THDM Yukawa type MINPAR(24)=", raw_type,
649 ". MARTY THDM generation expects an integer type in {1,2,3,4}.");
650 }
651
652 LOG_INFO("MartyInterface", "Using THDM Yukawa type ", type, " from MINPAR(24) for MARTY generation.");
653 return type;
654}
655
656namespace {
657
658std::string stable_file_fingerprint(const fs::path& path) {
659 std::ifstream input(path, std::ios::binary);
660 if (!input) {
661 throw std::runtime_error("Cannot fingerprint MARTY template file: " + path.string());
662 }
663
664 std::uint64_t hash = 14695981039346656037ULL;
665 char buffer[8192];
666 while (input.read(buffer, sizeof(buffer)) || input.gcount() > 0) {
667 const auto count = input.gcount();
668 for (std::streamsize i = 0; i < count; ++i) {
669 hash ^= static_cast<unsigned char>(buffer[i]);
670 hash *= 1099511628211ULL;
671 }
672 }
673
674 std::ostringstream result;
675 result << std::hex << std::setw(16) << std::setfill('0') << hash;
676 return result.str();
677}
678
679std::string normalized_path(const fs::path& path) {
680 std::error_code ec;
681 fs::path normalized = fs::weakly_canonical(path, ec);
682 if (ec) {
683 ec.clear();
684 normalized = fs::absolute(path, ec);
685 }
686 return normalized.lexically_normal().string();
687}
688
689std::string template_signature(const std::string& wilson,
690 const std::shared_ptr<FileNameManager>& files) {
691 const fs::path path = fs::path(files->getTemplateDir()) / (wilson + ".cpp");
692 return "HYPERISO_MARTY_TEMPLATE_SIGNATURE: path=" + normalized_path(path)
693 + "; fnv1a64=" + stable_file_fingerprint(path);
694}
695
696bool uses_split_regprop_policy(const std::string& wilson) {
697 return wilson == "C9" || wilson == "CP9" || wilson == "CP10";
698}
699
700bool template_needs_generic_tree_first(const std::string& wilson,
701 const std::shared_ptr<FileNameManager>& files) {
702 if (uses_split_regprop_policy(wilson)) {
703 return false;
704 }
705
706 const fs::path path = fs::path(files->getTemplateDir()) / (wilson + ".cpp");
707 std::ifstream input(path);
708 if (!input) {
709 throw std::runtime_error("Cannot inspect MARTY template order policy: " + path.string());
710 }
711
712 const std::string source(
713 (std::istreambuf_iterator<char>(input)),
714 std::istreambuf_iterator<char>()
715 );
716 static const std::regex tree_call(
717 R"(computeWilsonCoefficients\s*\‍(\s*(?:mty::Order::)?TreeLevel)"
718 );
719 static const std::regex loop_call(
720 R"(computeWilsonCoefficients\s*\‍(\s*(?:mty::Order::)?OneLoop)"
721 );
722
723 return std::regex_search(source, loop_call) && !std::regex_search(source, tree_call);
724}
725
726std::string generation_mode(const std::string& wilson,
727 bool sm_like_filter,
728 bool bsm_only_generation,
729 bool full_target_generation) {
730 if (sm_like_filter) {
731 return "sm-like";
732 }
733 if (full_target_generation && bsm_only_generation && uses_split_regprop_policy(wilson)) {
734 return "target-regprop-split";
735 }
736 if (full_target_generation) {
737 return "target-full";
738 }
739 if (bsm_only_generation && uses_split_regprop_policy(wilson)) {
740 return "bsm-regprop-split";
741 }
742 if (bsm_only_generation) {
743 return "bsm-only";
744 }
745 return "full";
746}
747
748std::string generation_mode_marker(const std::string& wilson,
749 bool sm_like_filter,
750 bool bsm_only_generation,
751 bool full_target_generation) {
752 return "HYPERISO_MARTY_GENERATION_MODE: "
753 + generation_mode(
754 wilson,
755 sm_like_filter,
756 bsm_only_generation,
757 full_target_generation
758 );
759}
760
761void append_cache_metadata_if_missing(const fs::path& generated_file,
762 const std::string& model_signature,
763 const std::string& template_signature_value,
764 const std::string& mode_marker) {
765 std::ifstream input(generated_file);
766 if (!input) {
767 throw std::runtime_error(
768 "MARTY source generation did not create the expected file: " + generated_file.string()
769 );
770 }
771
772 bool has_cache_abi = false;
773 std::string line;
774 while (std::getline(input, line)) {
775 if (line.find(kMartyCacheAbi) != std::string::npos) {
776 has_cache_abi = true;
777 break;
778 }
779 }
780 if (has_cache_abi) {
781 return;
782 }
783
784 std::ofstream output(generated_file, std::ios::app);
785 if (!output) {
786 throw std::runtime_error(
787 "Cannot append MARTY cache metadata to: " + generated_file.string()
788 );
789 }
790 output << "\n// " << kMartyCacheAbi << "\n";
791 output << "// " << model_signature << "\n";
792 output << "// " << template_signature_value << "\n";
793 output << "// " << mode_marker << "\n";
794}
795
796} // namespace
797
798void MartyInterface::invalidate_template_model_cache_if_needed(const std::string& wilson,
799 const std::string& output_model,
800 const std::string& target_model,
801 const std::string& model_path,
802 std::optional<int> model_template_index,
803 bool sm_like_filter,
804 bool bsm_split_generation,
805 bool full_target_generation) const {
806 const auto files = FileNameManager::getInstance(wilson, output_model);
807 const std::string expected_model_signature = GeneralModelModifier::modelSignature(
808 target_model,
809 model_path,
810 model_template_index
811 );
812 const std::string expected_template_signature = template_signature(wilson, files);
813 const std::string expected_mode = generation_mode_marker(
814 wilson,
815 sm_like_filter,
816 bsm_split_generation,
817 full_target_generation
818 );
819
820 bool file_present = false;
821 bool has_cache_abi = false;
822 bool has_model_signature = false;
823 bool has_template_signature = false;
824 bool has_generation_mode = false;
825
826 {
827 std::ifstream in(files->getGeneratedFileName());
828 file_present = static_cast<bool>(in);
829 std::string line;
830 while (std::getline(in, line)) {
831 has_cache_abi = has_cache_abi || line.find(kMartyCacheAbi) != std::string::npos;
832 has_model_signature = has_model_signature || line.find(expected_model_signature) != std::string::npos;
833 has_template_signature = has_template_signature || line.find(expected_template_signature) != std::string::npos;
834 has_generation_mode = has_generation_mode || line.find(expected_mode) != std::string::npos;
835 }
836 }
837
838 const bool stale = !file_present
839 || !has_cache_abi
840 || !has_model_signature
841 || !has_template_signature
842 || !has_generation_mode;
843
844 if (!stale) {
845 return;
846 }
847
848 std::error_code ec;
849 fs::remove(files->getGeneratedFileName(), ec);
850 ec.clear();
851 fs::remove(files->getExecutableFileName(), ec);
852 ec.clear();
853 fs::remove_all(files->getLibDir(), ec);
854 ec.clear();
855 fs::remove(files->getCsvWilsonFileName(), ec);
856
857 std::string reason;
858 if (!file_present) {
859 reason = "generated file is missing";
860 } else if (!has_cache_abi) {
861 reason = "cache ABI mismatch";
862 } else if (!has_model_signature) {
863 reason = "model path/content signature mismatch";
864 } else if (!has_template_signature) {
865 reason = "template content signature mismatch";
866 } else if (!has_generation_mode) {
867 reason = "generation mode mismatch";
868 } else {
869 reason = "cache metadata mismatch";
870 }
871
872 LOG_INFO("MartyInterface", "Invalidated stale MARTY cache for ", wilson, " / ", output_model,
873 " because ", reason, ". Expected mode: ", expected_mode,
874 "; expected model signature: ", expected_model_signature);
875}
876
877std::unordered_set<InterpretedParam> MartyInterface::get_dependencies(std::string wilson) {
878 std::shared_lock<std::shared_mutex> read_lock(marty_artifact_mutex);
879 if (!this->dependencies.contains(wilson)) {
880 LOG_ERROR("KeyError", "Trying to access dependencies for unknown wilson coefficient", wilson, "in WilsonInterface.");
881 }
882
883 return this->dependencies.at(wilson);
884}
885
886bool MartyInterface::already_run(std::string&& outputBinary) {
887 struct stat buffer;
888 if (stat(outputBinary.c_str(), &buffer) != 0) {
889 return false;
890 }
891 if (buffer.st_size == 0) {
892 return false;
893 }
894 LOG_DEBUG("Already run !");
895 return true;
896}
897
898std::string MartyInterface::output_binary_name(std::string& wilson, std::string& model) {
899 return "generated_" + wilson+"_" + model + ".cpp";
900 }
901
902std::set<std::string> MartyInterface::get_special_blocks() {
903 return this->specials_block;
904}
Declares the default factory for interpreter ports.
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
Definition Logger.h:41
#define LOG_INFO(...)
Macro for logging informational messages.
Definition Logger.h:39
#define LOG_DEBUG(...)
Macro for logging debug messages.
Definition Logger.h:45
High-level façade around MARTY-based code generation and compilation.
Concrete parameter proxy that reads values from Hyperiso Parameters.
Declares a small helper to write parameter maps to CSV files.
A class that generates code using a template manager.
void generate(const std::string &templateName, const std::string &outputPath)
Generates a code file based on a specified template.
static std::shared_ptr< FileNameManager > getInstance(const std::string &wilson="", const std::string &model="")
Retrieves a FileNameManager for a given (wilson, model) pair.
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.
High-level orchestrator for numeric MARTY model generation.
Compilation strategy using the g++ compiler.
void compile_run(const std::string &sourceFile, const std::string &outputBinary) override
Compiles if necessary and then runs the resulting binary.
Compilation strategy using a make-based build system.
void compile_run(const std::string &sourceFile, const std::string &outputBinary) override
Compiles if necessary and then runs the resulting binary.
void set_output_file(std::filesystem::path path)
void set_param_file(std::filesystem::path path)
void set_Q_match(double Q_match)
Sets the matching scale passed to the binary.
void compile_run_libs(std::string wilson, std::string model, double Q_match)
Compiles and runs the numeric libraries.
std::set< std::string > get_special_blocks()
Returns the special blocks handled with custom logic.
std::string calculate_isolated(std::string wilson, std::string output_model, std::string target_model, double Q_match, std::string model_path, bool sm_like_filter, bool bsm_split_generation=false, bool full_target_generation=false)
Thread-safe calculation using invocation-local parameter and CSV files.
void generate(std::string wilson, std::string model, std::string model_path)
Generates a non-numeric MARTY-powered C++ template.
std::unordered_set< InterpretedParam > get_dependencies(std::string wilson)
Retrieves the set of parameter dependencies for a given Wilson basis.
void compile_run(std::string wilson, std::string model)
Compiles and runs the non-numeric generated code.
MartyInterface()
Default constructor wiring default implementations.
void calculate(std::string wilson, std::string model, double Q_match, std::string model_path)
Convenience shortcut to generate, compile, and run the full pipeline.
void generate_numlib(std::string wilson, std::string model)
Generates the numeric library wrapper for a given (Wilson, model) pair.
static InstallInfo require_available(const std::string &context)
Validate and return the active MARTY installation.
Abstract base class for model source modifiers.
Helper class to serialize parameter values to a text stream.
Definition ParamWriter.h:29
void writeParams(std::ofstream &outputFile, const std::unordered_map< std::string, double > &params)
Writes a map of parameters to an output stream.
Represents an identifier of a LHA element, possibly containing several sub-ids.
Definition LhaID.h:56