14 #include "TSystemDirectory.h"
49 YAML::Node yaml_diag_config = YAML::LoadFile(filename);
50 YAML::Node yaml_config = yaml_diag_config[
"UmbrellaSolver"];
53 config.
output_file = Get<std::string>(yaml_config[
"output_file"], __FILE__ , __LINE__);
54 config.
variable_of_interest = Get<std::string>(yaml_config[
"variable_of_interest"], __FILE__ , __LINE__);
55 config.
max_iterations = Get<int>(yaml_config[
"max_iterations"], __FILE__ , __LINE__);
56 config.
tolerance = Get<double>(yaml_config[
"tolerance"], __FILE__ , __LINE__);
57 config.
print_frequency = GetFromManager<int>(yaml_config[
"print_frequency"], 0, __FILE__ , __LINE__);
58 config.
dynamic_files = GetFromManager<bool>(yaml_config[
"dynamic_files"],
false, __FILE__ , __LINE__);
59 config.
dynamic_pattern = Get<std::string>(yaml_config[
"dynamic_pattern"], __FILE__ , __LINE__);
60 config.
dynamic_n_windows = Get<int>(yaml_config[
"dynamic_n_windows"], __FILE__ , __LINE__);
61 config.
use_openmp = GetFromManager<bool>(yaml_config[
"use_openmp"],
true, __FILE__ , __LINE__);
67 if (yaml_config[
"windows"]) {
68 const YAML::Node &windows = yaml_config[
"windows"];
69 for (
size_t i = 0; i < windows.size(); i++) {
71 window.
name = Get<std::string>(windows[i][
"name"], __FILE__, __LINE__);
72 window.
center = Get<double>(windows[i][
"center"], __FILE__, __LINE__);
73 window.
width = Get<double>(windows[i][
"width"], __FILE__, __LINE__);
74 config.
windows.push_back(window);
79 if (yaml_config[
"input_files"]) {
80 const YAML::Node &input_files = yaml_config[
"input_files"];
81 for (
size_t i = 0; i < input_files.size() && i < config.
windows.size(); i++) {
82 config.
windows[i].input_file = Get<std::string>(input_files[i], __FILE__, __LINE__);
89 window.
name =
"Window_" + std::to_string(i);
94 config.
windows.push_back(window);
98 }
catch (
const YAML::Exception &e) {
99 MACH3LOG_ERROR(
"Error parsing YAML file {}: {}", filename, e.what());
108 return exp(-0.5 * pow((x - center) / width, 2)) / (width * sqrt(2 * TMath::Pi()));
119 double log_I0 = kappa - 0.5 * log(2 * TMath::Pi() * kappa);
120 return exp(kappa * cos(x - center) - log_I0 - log(2 * TMath::Pi()));
122 I0_kappa = TMath::BesselI0(kappa);
123 return exp(kappa * cos(x - center)) / (2 * TMath::Pi() * I0_kappa);
133 double likelihood = normFactor * std::exp(-std::pow((std::pow(x - mean, 2) / (2 * std::pow(width, 2))), n));
145 double multicanonicalBeta = 1.0;
146 return (g0 + g1 + g2) * (multicanonicalBeta);
151 double summedWindowsWeighted(
double x,
const std::vector<WindowConfig> &windows,
const std::vector<double> &z_values) {
153 for (
size_t k = 0; k < windows.size(); k++) {
156 window_val =
vonMisesWindow(x, windows[k].center, windows[k].vonMises_kappa);
158 window_val =
gaussianWindow(x, windows[k].center, windows[k].width);
165 sum += window_val / z_values[k];
173 std::vector<std::vector<std::vector<double>>>
buildWindowCache(
const std::vector<WindowConfig> &windows,
174 const std::vector<std::vector<double>> &samples,
bool use_openmp =
true) {
175 int n_windows =
static_cast<int>(windows.size());
176 std::vector<std::vector<std::vector<double>>> cache(n_windows);
178 for (
int i = 0; i < n_windows; i++) {
179 cache[i].resize(n_windows);
180 for (
int j = 0; j < n_windows; j++) {
181 cache[i][j].resize(samples[i].size());
187 #pragma omp parallel for collapse(2) schedule(dynamic)
189 for (
int i = 0; i < n_windows; i++) {
190 for (
int j = 0; j < n_windows; j++) {
191 for (
size_t s = 0; s < samples[i].size(); s++) {
193 cache[i][j][s] =
vonMisesWindow(samples[i][s], windows[j].center, windows[j].vonMises_kappa);
195 cache[i][j][s] =
gaussianWindow(samples[i][s], windows[j].center, windows[j].width);
206 for (
int i = 0; i < n_windows; i++) {
207 for (
int j = 0; j < n_windows; j++) {
208 for (
size_t s = 0; s < samples[i].size(); s++) {
210 cache[i][j][s] =
vonMisesWindow(samples[i][s], windows[j].center, windows[j].vonMises_kappa);
212 cache[i][j][s] =
gaussianWindow(samples[i][s], windows[j].center, windows[j].width);
231 size_t cache_size_bytes = 0;
232 for (
int i = 0; i < n_windows; i++) {
233 for (
int j = 0; j < n_windows; j++) {
234 cache_size_bytes += cache[i][j].size() *
sizeof(double);
237 MACH3LOG_INFO(
"Window cache size: {:.2f} MB",
static_cast<double>(cache_size_bytes) / (1024.0 * 1024.0));
239 TFile *cache_file =
TFile::Open(
"window_cache_debug_histograms.root",
"RECREATE");
240 for (
int i = 0; i < n_windows; i++) {
241 for (
int j = 0; j < n_windows; j++) {
242 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j);
243 TH1D *window_cache_hist =
new TH1D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415);
244 for (
size_t s = 0; s < cache[i][j].size(); s++) {
245 window_cache_hist->AddBinContent(window_cache_hist->FindBin(samples[i][s]));
247 window_cache_hist->Write();
254 for (
int i = 0; i < n_windows; i++) {
255 for (
int j = 0; j < n_windows; j++) {
256 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j) +
"_cache_2D";
257 TH2D *window_cache_2D_hist =
new TH2D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415, 100, -1000, 10);
258 for (
size_t s = 0; s < cache[i][j].size(); s++) {
260 window_cache_2D_hist->Fill(samples[i][s], log(cache[i][j][s]));
263 window_cache_2D_hist->SetStats(1);
264 window_cache_2D_hist->Write();
275 std::vector<std::vector<double>>
calcFmatrix(std::vector<double> &z_current,
276 const std::vector<WindowConfig> &windows,
277 const std::vector<std::vector<double>> &samples,
278 const std::vector<std::vector<std::vector<double>>> &window_cache) {
279 int n_windows =
static_cast<int>(windows.size());
280 std::vector<std::vector<double>> F(n_windows, std::vector<double>(n_windows, 0.0));
282 std::vector<double> z_inv = z_current;
283 for (
size_t i = 0; i < z_current.size(); i++) {
284 if (z_current[i] > 0) {
285 z_inv[i] = 1.0 / z_current[i];
289 MACH3LOG_WARN(
"Warning: z_current[{}] is non-positive ({}). Setting its inverse to 0 in F matrix calculation.", i, z_current[i]);
294 #pragma omp parallel for schedule(dynamic)
296 for (
int i = 0; i < n_windows; i++) {
297 std::vector<double> denominator_cache(samples[i].size(), 0.0);
298 for (
size_t s = 0; s < samples[i].size(); s++) {
299 double denominator = 0.0;
300 for (
int k = 0; k < n_windows; k++) {
301 denominator += window_cache[i][k][s] * z_inv[k];
303 denominator_cache[s] = 1 / denominator;
306 for (
int j = 0; j < n_windows; j++) {
310 for (
size_t s = 0; s < samples[i].size(); s++) {
311 double sample = samples[i][s];
312 double window_j = window_cache[i][j][s];
313 double denominator = denominator_cache[s];
315 if (denominator > 0) {
316 double integrand = (window_j * z_inv[i]) * denominator;
320 MACH3LOG_WARN(
"Denominator is zero for sample {} in window {}, skipping...", sample, i);
325 MACH3LOG_INFO(
"F[{}][{}] sum: {}, count: {}", i, j, sum, count);
329 F[i][j] = sum / count;
338 std::vector<double>
zSolver(
const std::vector<double> &z_current,
339 const std::vector<WindowConfig> &windows,
340 const std::vector<std::vector<double>> &samples,
341 const std::vector<std::vector<std::vector<double>>> &window_cache,
342 bool use_openmp =
true,
bool verbose =
false,
343 [[maybe_unused]]
int *total_lines =
nullptr) {
344 int n_windows =
static_cast<int>(windows.size());
345 if (verbose && !use_openmp) {
346 MACH3LOG_INFO(
"Using single-threaded computation for F matrix...");
350 std::vector<double> z_working = z_current;
351 std::vector<std::vector<double>> F =
352 calcFmatrix(z_working, windows, samples, window_cache);
372 std::vector<double> z_new(n_windows, 0.0);
373 for (
int i = 0; i < n_windows; i++) {
374 for (
int j = 0; j < n_windows; j++) {
375 z_new[i] += z_current[j] * F[j][i];
394 double z_magnitude = 0.0;
395 for (
int i = 0; i < n_windows; i++) {
396 z_magnitude += z_new[i] * z_new[i];
398 z_magnitude = sqrt(z_magnitude);
399 if (z_magnitude > 0) {
400 for (
int i = 0; i < n_windows; i++) {
401 z_new[i] /= z_magnitude;
410 bool checkConvergence(
const std::vector<double> &z_current,
const std::vector<double> &z_prev,
double tolerance) {
411 double sum_diffs = 0.0;
412 for (
size_t i = 0; i < z_current.size(); i++) {
421 sum_diffs += std::abs(z_current[i] - z_prev[i]);
427 if (sum_diffs /
static_cast<double>(z_current.size()) > tolerance) {
434 std::vector<double>
getZDiffs(
const std::vector<double> &z_current,
const std::vector<double> &z_prev) {
435 std::vector<double> diffs(z_current.size(), 0.0);
436 for (
size_t i = 0; i < z_current.size(); i++) {
437 diffs[i] = std::abs(z_current[i] - z_prev[i]);
447 static std::deque<std::vector<double>> z_history;
448 static std::vector<double> previous_moving_average;
449 static int stagnant_iterations = 0;
451 constexpr
int moving_average_window = 500;
452 constexpr
int stagnant_required = 500;
453 const double bound = tolerance;
455 if (z_current.empty()) {
460 if (!z_history.empty() && z_history.front().size() != z_current.size()) {
462 previous_moving_average.clear();
463 stagnant_iterations = 0;
466 z_history.push_back(z_current);
467 if (
static_cast<int>(z_history.size()) > moving_average_window) {
468 z_history.pop_front();
472 if (
static_cast<int>(z_history.size()) < moving_average_window) {
476 std::vector<double> moving_average(z_current.size(), 0.0);
477 for (
const auto &z_vec : z_history) {
478 for (
size_t i = 0; i < z_vec.size(); i++) {
479 moving_average[i] += z_vec[i];
482 for (
size_t i = 0; i < moving_average.size(); i++) {
483 moving_average[i] /= moving_average_window;
486 if (previous_moving_average.empty()) {
487 previous_moving_average = moving_average;
491 bool all_within_bound =
true;
492 for (
size_t i = 0; i < moving_average.size(); i++) {
493 if (std::abs(moving_average[i] - previous_moving_average[i]) > bound) {
494 all_within_bound =
false;
499 if (all_within_bound) {
500 stagnant_iterations++;
502 stagnant_iterations = 0;
505 previous_moving_average = moving_average;
507 if (stagnant_iterations == stagnant_required) {
508 MACH3LOG_WARN(
"Convergence appears stalled: moving-average change stayed within {} for {} iterations.",
509 bound, stagnant_required);
512 return stagnant_iterations >= stagnant_required;
522 MACH3LOG_INFO(
"Max threads available: {}", omp_get_max_threads());
524 MACH3LOG_WARN(
"_OPENMP is NOT defined - OpenMP not available");
526 MACH3LOG_INFO(
"Loading configuration from: {}", config_file);
532 MACH3LOG_ERROR(
"No windows defined in configuration and dynamic file loading is disabled.");
543 MACH3LOG_INFO(
"OpenMP: ENABLED (using {} threads)", omp_get_max_threads());
548 MACH3LOG_WARN(
"OpenMP: NOT AVAILABLE - falling back to single-threaded execution");
549 MACH3LOG_INFO(
"Note: For OpenMP support, try compiling with: g++ -fopenmp ...");
550 MACH3LOG_INFO(
"Or ensure OpenMP library is properly loaded in ROOT");
553 MACH3LOG_INFO(
"OpenMP: DISABLED (single-threaded execution)");
558 std::vector<std::vector<double>> samples;
561 samples.resize(config.
windows.size());
565 std::vector<TFile *> input_files;
566 std::vector<TTree *> input_trees;
569 MACH3LOG_INFO(
"Using static input files from configuration.");
570 for (
size_t i = 0; i < config.
windows.size(); i++) {
572 TFile *file =
M3::Open(config.
windows[i].input_file.c_str(),
"READ", __FILE__, __LINE__);
574 TTree *tree =
static_cast<TTree*
>(file->Get(
"posteriors"));
581 input_files.push_back(file);
582 input_trees.push_back(tree);
588 TList *files = dir.GetListOfFiles();
593 while ((file =
static_cast<TSystemFile*
>(next()))) {
594 std::string filename = file->GetName();
595 if (filename.find(
".root") == std::string::npos) {
603 TFile *root_file =
M3::Open(full_path,
"READ", __FILE__, __LINE__);
605 TTree *tree =
static_cast<TTree*
>(root_file->Get(
"posteriors"));
609 throw MaCh3Exception(__FILE__, __LINE__,
"Missing 'posteriors' tree in file: " + full_path);
613 input_files.push_back(root_file);
614 input_trees.push_back(tree);
615 MACH3LOG_INFO(
"Loaded tree 'posteriors' from file: {}", full_path);
623 MACH3LOG_ERROR(
"Number of files found ({}) does not match expected dynamic_n_windows ({}).",
625 throw MaCh3Exception(__FILE__, __LINE__,
"File count mismatch for dynamic loading.");
629 for (
size_t i = 0; i < input_trees.size(); i++) {
630 TTree *tree = input_trees[i];
631 TFile *file = input_files[i];
633 MACH3LOG_INFO(
"Processing file {}/{}: {}", i + 1, input_trees.size(), file->GetName());
634 TMacro *macro =
static_cast<TMacro*
>(file->Get(
"MaCh3_Config"));
639 std::stringstream yaml_text;
640 TList *lines = macro->GetListOfLines();
641 for (
int iline = 0; iline < lines->GetEntries(); ++iline) {
642 TObjString *line =
static_cast<TObjString*
>(lines->At(iline));
643 yaml_text << line->GetString().Data() <<
"\n";
647 YAML::Node macro_yaml = YAML::Load(yaml_text.str());
648 YAML::Node umbrellaConfig =
649 macro_yaml[
"General"][
"MCMC"][
"Multicanonical"];
651 config.
windows[i].center = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaMean"], __FILE__, __LINE__);
655 auto biasString = Get<std::string>(umbrellaConfig[
"Umbrella"][
"UmbrellaBiasFunction"], __FILE__, __LINE__);
657 if (biasString ==
"gaussian") {
660 }
else if (biasString ==
"generalisedGaussian") {
663 }
else if (biasString ==
"vonMises") {
670 config.
windows[i].umbrellaBiasFunction = biasMode;
674 double vonMises_sigma = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"], __FILE__, __LINE__);
675 config.
windows[i].vonMises_kappa = 1.0 / (vonMises_sigma * vonMises_sigma);
676 config.
windows[i].width = vonMises_sigma;
677 MACH3LOG_INFO(
"Window {} using von Mises: sigma = {}, kappa = {}",
678 i, vonMises_sigma, config.
windows[i].vonMises_kappa);
681 config.
windows[i].width = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"], __FILE__, __LINE__);
682 config.
windows[i].vonMises_kappa = -1.0;
685 }
catch (
const std::exception &e) {
686 MACH3LOG_WARN(
"Could not parse macro as YAML: {}", e.what());
693 tree->SetBranchAddress(
"LogL", &logL_value);
695 Long64_t nentries = tree->GetEntries();
699 for (Long64_t entry = 0; entry < nentries; entry++) {
700 tree->GetEntry(entry);
706 samples[i].push_back(var_value);
717 for (
size_t i = 0; i < config.
windows.size(); i++) {
718 for (
size_t j = i + 1; j < config.
windows.size(); j++) {
721 std::swap(samples[i], samples[j]);
722 std::swap(input_trees[i], input_trees[j]);
723 std::swap(input_files[i], input_files[j]);
730 for (
size_t i = 0; i < config.
windows.size(); i++) {
731 MACH3LOG_INFO(
"Window {}: center = {}, width = {}, vonMises_mode = {}, vonMises_kappa = {}, samples = {}",
734 config.
windows[i].vonMises_kappa, samples[i].size());
738 std::vector<double> z_current(config.
windows.size(), 1.0);
739 std::vector<double> z_prev(config.
windows.size(), 1.0);
742 bool hacky_start =
false;
752 0.023968629123202176, 0.024927005713178161, 0.026030888791054529,
753 0.036203405237770721, 0.04004944137212621, 0.055993616350153479,
754 0.079251266929094608, 0.065860139904686643, 0.067944181615205768,
755 0.055237804689517243, 0.054778778031073304, 0.066196102148964917,
756 0.059298667596959342, 0.049864361722134341, 0.048890393249559315,
757 0.05284144211204099, 0.050606183191239794, 0.037329936801427918,
758 0.025159187577401387, 0.01487697249782439, 0.0074395236463036573,
759 0.0040535024095969992, 0.0025347709967512371, 0.0015590961074484638,
760 0.00083136243905723782, 0.00047599269828616062, 0.0004454414833514952,
761 0.00045205403592812914, 0.00062098732584897902, 0.00091722187629238541,
762 0.0012407301891732216, 0.0023494590898020503, 0.0041640866720417773,
763 0.0080599626292932776, 0.011429554834689368, 0.018117848911520615};
766 MACH3LOG_WARN(
"!!!!!!!starting from hacky start vector!!!!!!");
769 std::vector<std::vector<double>> z_evolution;
773 bool openmp_works =
false;
777 int max_threads = omp_get_max_threads();
785 int actual_threads = 1;
795 actual_threads = omp_get_num_threads();
799 MACH3LOG_INFO(
"Actual threads in parallel region: {}", actual_threads);
803 if (actual_threads > 1) {
804 MACH3LOG_INFO(
"OpenMP is working correctly with {} threads", actual_threads);
807 openmp_works =
false;
814 std::vector<std::vector<std::vector<double>>> window_cache =
buildWindowCache(config.
windows, samples, openmp_works);
817 bool save_matrix =
true;
819 TFile *F_file =
nullptr;
822 std::string base_name = (pos != std::string::npos) ? config.
output_file.substr(0, pos) : config.
output_file;
823 F_file =
TFile::Open((base_name +
"_matrix_evolution.root").c_str(),
"RECREATE");
824 if (!F_file || F_file->IsZombie()) {
825 MACH3LOG_ERROR(
"Cannot create file {}", base_name +
"_matrix_evolution.root");
829 std::vector<std::vector<double>> initial_F =
calcFmatrix(z_current, config.
windows, samples, window_cache);
830 int n_windows =
static_cast<int>(config.
windows.size());
831 TH2D initial_F_TH2D(
"F_matrix_initial",
"Initial F matrix;Window j;Window i", n_windows, 0, n_windows, n_windows, 0, n_windows);
832 for (
int i = 0; i < n_windows; i++) {
833 for (
int j = 0; j < n_windows; j++) {initial_F_TH2D.SetBinContent(j + 1, i + 1, initial_F[i][j]);
838 initial_F_TH2D.Write();
842 auto start_time = std::chrono::high_resolution_clock::now();
843 auto last_print_time = start_time;
845 bool converged_robustness_check =
false;
849 if (!converged_robustness_check) {
850 MACH3LOG_INFO(
"Starting iterative solver with convergence checks...");
854 int total_output_lines = 0;
855 for (
int iteration = 0; iteration < config.
max_iterations; iteration++) {
857 auto current_time = std::chrono::high_resolution_clock::now();
862 std::cout <<
"\033[" << total_output_lines <<
"A";
863 std::cout <<
"\033[J";
867 double avg_relative_change = 0.0;
869 for (
size_t i = 0; i < z_current.size(); i++) {
870 double rel_change = std::abs(z_current[i] - z_prev[i]) / std::max(std::abs(z_current[i]), 1e-10);
871 avg_relative_change += rel_change;
873 avg_relative_change /=
static_cast<double>(z_current.size());
877 std::cout <<
"Iteration " << std::setw(6) << iteration <<
", z values: [";
878 for (
size_t i = 0; i < z_current.size(); i++) {
879 std::cout << std::setw(10) << std::fixed << std::setprecision(5) << z_current[i];
880 if (i < z_current.size() - 1)
884 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_print_time);
885 double avg_time_per_iteration =
static_cast<double>(duration.count()) /
887 std::cout <<
"] (avg: " << std::setw(6) << std::setprecision(1) << avg_time_per_iteration <<
" ms/iter)" << std::endl;
888 std::cout <<
"Avg relative change: " << std::scientific << std::setprecision(3) << avg_relative_change <<
" (target: " << config.
tolerance <<
")" << std::endl;
889 total_output_lines = 2;
891 std::cout <<
"]" << std::endl;
892 total_output_lines = 1;
894 last_print_time = current_time;
898 z_current =
zSolver(z_current, config.
windows, samples, window_cache, openmp_works, iteration % config.
print_frequency == 0, &total_output_lines);
899 z_evolution.push_back(z_current);
905 if (save_matrix && (iteration < 15 || iteration % config.
print_frequency == 0)) {
906 std::vector<std::vector<double>> F_matrix =
calcFmatrix(z_current, config.
windows, samples, window_cache);
908 int n_windows =
static_cast<int>(config.
windows.size());
909 TH2D F_TH2D(Form(
"F_matrix_iter_%02d", iteration),Form(
"F matrix at iteration %02d;Window j;Window i", iteration), n_windows, 0, n_windows, n_windows, 0, n_windows);
910 for (
int i = 0; i < n_windows; i++) {
911 for (
int j = 0; j < n_windows; j++) {
912 F_TH2D.SetBinContent(j + 1, i + 1, F_matrix[i][j]);
917 TTree *z_tree =
new TTree(Form(
"z_saved_iter_%02d", iteration), Form(
"Z vector at iteration %02d", iteration));
918 z_tree->Branch(
"z_saved", &z_current);
921 MACH3LOG_INFO(
"Saving F matrix for iteration {} to file...", iteration);
933 bool apply_robustness_check =
true;
934 if (iteration % 100 == 0 &&
936 if (!converged_robustness_check && apply_robustness_check) {
937 MACH3LOG_INFO(
"Convergence check passed at iteration {}. Starting robustness check with random perturbation...", iteration);
938 converged_robustness_check =
true;
941 std::vector<double> z_perturbed = z_current;
942 for (
size_t i = 0; i < z_perturbed.size(); i++) {
944 double perturbation = gRandom3.Uniform(-0.5, 0.5) * z_perturbed[i];
945 MACH3LOG_INFO(
"Applying perturbation of {:.6e} to z[{}] = {:.6e}", perturbation, i, z_perturbed[i]);
946 z_perturbed[i] += perturbation;
947 if (z_perturbed[i] < 0)
948 z_perturbed[i] = abs(z_perturbed[i]);
950 z_current = z_perturbed;
951 MACH3LOG_INFO(
"Applied random perturbation to z values for robustness check.");
954 MACH3LOG_INFO(
"Convergence achieved at iteration {}", iteration);
956 MACH3LOG_WARN(
"Convergence appears to be stalled at iteration {}", iteration);
960 MACH3LOG_WARN(
"Reached maximum iterations without convergence.");
963 auto end_time = std::chrono::high_resolution_clock::now();
964 auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
965 double avg_time_total =
static_cast<double>(total_duration.count()) /
966 static_cast<double>(iteration + 1);
971 MACH3LOG_INFO(
"Average time per iteration: {} ms", avg_time_total);
977 std::ostringstream oss;
978 oss <<
"Final z values: [";
979 for (
size_t i = 0; i < z_current.size(); i++) {
980 oss << std::fixed << std::setprecision(5) << z_current[i];
981 if (i < z_current.size() - 1)
987 std::filesystem::copy_file(input_files[0]->GetName(), config.
output_file,
988 std::filesystem::copy_options::overwrite_existing);
993 TTree *input_tree =
dynamic_cast<TTree*
>(output_file->Get(
"posteriors"));
995 TTree *combined_tree = input_tree->CloneTree(0);
998 double umbrella_weight;
1002 combined_tree->Branch(
"umbrella_weight", &umbrella_weight,
"umbrella_weight/D");
1003 combined_tree->Branch(
"window_id", &window_id,
"window_id/I");
1006 for (
size_t i = 0; i < input_trees.size(); i++) {
1007 TTree *tree = input_trees[i];
1009 Long64_t nentries = tree->GetEntries();
1012 int oldLevel = gErrorIgnoreLevel;
1013 gErrorIgnoreLevel = kError;
1014 combined_tree->CopyAddresses(tree);
1015 gErrorIgnoreLevel = oldLevel;
1018 tree->SetBranchAddress(
"delta_cp", &delta_cp);
1021 combined_tree->GetBranch(
"delta_cp")->SetAddress(&delta_cp);
1022 if (z_current[i] == 0) {
1023 MACH3LOG_WARN(
"Z value for window {} is zero, skipping weighting for this window to avoid division by zero.", i);
1025 window_id =
static_cast<int>(i);
1027 for (Long64_t entry = 0; entry < nentries; entry++) {
1028 tree->GetEntry(entry);
1030 if (z_current[i] == 0) {
1031 umbrella_weight = 0.0;
1038 umbrella_weight = denominator;
1041 if (combined_tree->Fill() < 0) {
1042 MACH3LOG_WARN(
"Failed writing output tree. Check disk quota/space and write permissions for: {}", config.
output_file);
1048 combined_tree->Write(input_tree->GetName(), TObject::kOverwrite);
1050 TDirectory* UmbreallaDir = output_file->mkdir(
"Umbrealla");
1053 TCanvas c1(
"c1",
"Z Evolution", 800, 600);
1054 std::vector<TGraph*> z_graphs(config.
windows.size());
1055 TLegend legend(0.7, 0.7, 0.9, 0.9);
1058 double ymin = std::numeric_limits<double>::max();
1060 for (
size_t i = 0; i < config.
windows.size(); i++) {
1061 std::vector<double> iterations, z_vals;
1062 for (
size_t j = 0; j < z_evolution.size(); j++) {
1063 iterations.push_back(
static_cast<int>(j));
1064 z_vals.push_back(z_evolution[j][i]);
1067 for (
double val : z_vals) {
1074 z_graphs[i] =
new TGraph(
static_cast<int>(iterations.size()), &iterations[0], &z_vals[0]);
1075 z_graphs[i]->SetLineColor(
static_cast<Color_t
>(i + 1));
1076 z_graphs[i]->SetLineWidth(2);
1077 z_graphs[i]->SetName(Form(
"z_evolution_window_%lu", i));
1078 z_graphs[i]->SetTitle(
"Evolution of Z Values");
1081 z_graphs[i]->GetXaxis()->SetTitle(
"Iteration");
1082 z_graphs[i]->GetYaxis()->SetTitle(
"Z Value");
1083 z_graphs[i]->Draw(
"AL");
1085 z_graphs[i]->Draw(
"L SAME");
1088 legend.AddEntry(z_graphs[i], Form(
"Window %lu", i),
"l");
1089 z_graphs[i]->Write();
1093 z_graphs[0]->SetMaximum(ymax);
1094 z_graphs[0]->SetMinimum(ymin);
1100 TH1D *h_delta_cp =
new TH1D(
"h_delta_cp_weighted",
"Weighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1101 TH1D *h_delta_cp_unweighted =
new TH1D(
"h_delta_cp_unweighted",
"Unweighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1103 combined_tree->Draw(
"delta_cp>>h_delta_cp_weighted",
"umbrella_weight",
"goff");
1104 combined_tree->Draw(
"delta_cp>>h_delta_cp_unweighted",
"",
"goff");
1106 h_delta_cp->Write();
1107 h_delta_cp_unweighted->Write();
1109 UmbreallaDir->Close();
1110 delete UmbreallaDir;
1113 Long64_t total_entries = combined_tree->GetEntries();
1118 YAML::Node umbrella_config;
1119 umbrella_config[
"UmbrellaSolver"] = yaml_config[
"UmbrellaSolver"];
1122 TMacro UmbrellaHeader =
YAMLtoTMacro(umbrella_config,
"Umbrella_Config");
1123 UmbrellaHeader.Write();
1125 output_file->Close();
1128 for (TFile *file : input_files) {
1132 MACH3LOG_INFO(
"Umbrella sampling outputs created (make sure to check for convergence issues)!");
1134 MACH3LOG_INFO(
"Combined tree contains {} entries with umbrella weights.", total_entries);
1140 std::string config_file =
"umbrella_config.yaml";
1142 config_file = argv[1];
1145 MACH3LOG_INFO(
"Running compiled version with OpenMP support");
1148 }
catch (
const std::exception &e) {
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
std::vector< double > getZDiffs(const std::vector< double > &z_current, const std::vector< double > &z_prev)
int main(int argc, char *argv[])
double summedWindowsWeighted(double x, const std::vector< WindowConfig > &windows, const std::vector< double > &z_values)
double vonMisesWindow(double x, double center, double kappa)
double generalisedGaussian2(double x, double mean, double width)
void UmbrellaSolver(const std::string &config_file)
bool checkConvergence(const std::vector< double > &z_current, const std::vector< double > &z_prev, double tolerance)
A few different convergence checks.
UmbrellaConfig parseYAMLConfig(const std::string &filename)
YAML-based config parser using yaml-cpp library.
bool checkConvergenceStalled(const std::vector< double > &z_current, const std::vector< double > &z_prev, double tolerance)
double GetMulticanonicalWeightGenGaussian(double deltacp, double mean, double width)
std::vector< std::vector< std::vector< double > > > buildWindowCache(const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, bool use_openmp=true)
double gaussianWindow(double x, double center, double width)
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ bool debug_mode
std::vector< double > zSolver(const std::vector< double > &z_current, const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, const std::vector< std::vector< std::vector< double >>> &window_cache, bool use_openmp=true, bool verbose=false, [[maybe_unused]] int *total_lines=nullptr)
std::vector< std::vector< double > > calcFmatrix(std::vector< double > &z_current, const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, const std::vector< std::vector< std::vector< double >>> &window_cache)
TMacro YAMLtoTMacro(const YAML::Node &yaml_node, const std::string &name)
Convert a YAML node to a ROOT TMacro object.
#define M3OpenConfig(filename)
Macro to simplify calling LoadYaml with file and line info.
Custom exception class used throughout MaCh3.
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.
constexpr double UmbrellaGaussianNormFactor
@ kGaussian
Assumes gaussian prior.
std::string variable_of_interest
std::vector< WindowConfig > windows
std::string dynamic_pattern
M3::BiasFunction umbrellaBiasFunction