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.
max_iterations = Get<int>(yaml_config[
"max_iterations"], __FILE__ , __LINE__);
55 config.
tolerance = Get<double>(yaml_config[
"tolerance"], __FILE__ , __LINE__);
56 config.
print_frequency = GetFromManager<int>(yaml_config[
"print_frequency"], 0, __FILE__ , __LINE__);
57 config.
dynamic_files = GetFromManager<bool>(yaml_config[
"dynamic_files"],
false, __FILE__ , __LINE__);
58 config.
dynamic_pattern = Get<std::string>(yaml_config[
"dynamic_pattern"], __FILE__ , __LINE__);
59 config.
dynamic_n_windows = Get<int>(yaml_config[
"dynamic_n_windows"], __FILE__ , __LINE__);
65 if (yaml_config[
"windows"]) {
66 const YAML::Node &windows = yaml_config[
"windows"];
67 for (
size_t i = 0; i < windows.size(); i++) {
69 window.
name = Get<std::string>(windows[i][
"name"], __FILE__, __LINE__);
70 window.
center = Get<double>(windows[i][
"center"], __FILE__, __LINE__);
71 window.
width = Get<double>(windows[i][
"width"], __FILE__, __LINE__);
72 config.
windows.push_back(window);
77 if (yaml_config[
"input_files"]) {
78 const YAML::Node &input_files = yaml_config[
"input_files"];
79 for (
size_t i = 0; i < input_files.size() && i < config.
windows.size(); i++) {
80 config.
windows[i].input_file = Get<std::string>(input_files[i], __FILE__, __LINE__);
87 window.
name =
"Window_" + std::to_string(i);
90 config.
windows.push_back(window);
93 }
catch (
const YAML::Exception &e) {
94 MACH3LOG_ERROR(
"Error parsing YAML file {}: {}", filename, e.what());
103 return exp(-0.5 * pow((x - center) / width, 2)) / (width * sqrt(2 * TMath::Pi()));
113 double log_I0 = kappa - 0.5 * log(2 * TMath::Pi() * kappa);
114 return exp(kappa * cos(x - center) - log_I0 - log(2 * TMath::Pi()));
116 double I0_kappa = TMath::BesselI0(kappa);
117 return exp(kappa * cos(x - center)) / (2 * TMath::Pi() * I0_kappa);
127 double likelihood = normFactor * std::exp(-std::pow((std::pow(x - mean, 2) / (2 * std::pow(width, 2))), n));
139 return (g0 + g1 + g2) * (multicanonicalBeta);
144 double summedWindowsWeighted(
double x,
const std::vector<WindowConfig> &windows,
const std::vector<double> &z_values) {
146 for (
size_t k = 0; k < windows.size(); k++) {
149 window_val =
vonMisesWindow(x, windows[k].center, windows[k].vonMises_kappa);
151 window_val =
gaussianWindow(x, windows[k].center, windows[k].width);
158 sum += window_val / z_values[k];
164 const size_t n_windows = windows.size();
165 size_t total_samples = 0;
166 for (
const auto &sample_set : samples) {
167 total_samples += sample_set.size();
169 return n_windows * total_samples *
sizeof(double);
175 std::vector<std::vector<std::vector<double>>>
buildWindowCache(
const std::vector<WindowConfig> &windows,
176 const std::vector<std::vector<double>> &samples) {
179 int n_windows =
static_cast<int>(windows.size());
181 const double cache_size_gb =
static_cast<double>(cache_size_bytes) / (1024.0 * 1024.0 * 1024.0);
182 if (cache_size_gb > 10.0) {
183 MACH3LOG_WARN(
"Window cache size: {:.2f} GB", cache_size_gb);
185 MACH3LOG_INFO(
"Window cache size: {:.2f} GB", cache_size_gb);
188 std::vector<std::vector<std::vector<double>>> cache(n_windows);
189 for (
int i = 0; i < n_windows; i++) {
190 cache[i].resize(n_windows);
191 for (
int j = 0; j < n_windows; j++) {
192 cache[i][j].resize(samples[i].size());
198 #pragma omp parallel for collapse(2) schedule(dynamic)
200 for (
int i = 0; i < n_windows; i++) {
201 for (
int j = 0; j < n_windows; j++) {
202 for (
size_t s = 0; s < samples[i].size(); s++) {
204 cache[i][j][s] =
vonMisesWindow(samples[i][s], windows[j].center, windows[j].vonMises_kappa);
206 cache[i][j][s] =
gaussianWindow(samples[i][s], windows[j].center, windows[j].width);
224 TFile *cache_file =
TFile::Open(
"window_cache_debug_histograms.root",
"RECREATE");
225 for (
int i = 0; i < n_windows; i++) {
226 for (
int j = 0; j < n_windows; j++) {
227 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j);
228 TH1D *window_cache_hist =
new TH1D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415);
229 for (
size_t s = 0; s < cache[i][j].size(); s++) {
230 window_cache_hist->AddBinContent(window_cache_hist->FindBin(samples[i][s]));
232 window_cache_hist->Write();
239 for (
int i = 0; i < n_windows; i++) {
240 for (
int j = 0; j < n_windows; j++) {
241 std::string hist_name =
"Sample" + std::to_string(i) +
"_window" + std::to_string(j) +
"_cache_2D";
242 TH2D *window_cache_2D_hist =
new TH2D(hist_name.c_str(), hist_name.c_str(), 100, -3.1415, 3.1415, 100, -1000, 10);
243 for (
size_t s = 0; s < cache[i][j].size(); s++) {
245 window_cache_2D_hist->Fill(samples[i][s], log(cache[i][j][s]));
248 window_cache_2D_hist->SetStats(1);
249 window_cache_2D_hist->Write();
257 MACH3LOG_INFO(
"Finished building window cache, it took {:.2f}s to finish", clock.RealTime());
262 std::vector<std::vector<double>>
calcFmatrix(
const std::vector<double> &z_current,
263 const std::vector<WindowConfig> &windows,
264 const std::vector<std::vector<double>> &samples,
265 const std::vector<std::vector<std::vector<double>>> &window_cache) {
266 int n_windows =
static_cast<int>(windows.size());
267 std::vector<std::vector<double>> F(n_windows, std::vector<double>(n_windows, 0.0));
269 std::vector<double> z_inv = z_current;
270 for (
size_t i = 0; i < z_current.size(); i++) {
271 if (z_current[i] > 0) {
272 z_inv[i] = 1.0 / z_current[i];
276 MACH3LOG_WARN(
"Warning: z_current[{}] is non-positive ({}). Setting its inverse to 0 in F matrix calculation.", i, z_current[i]);
281 #pragma omp parallel for schedule(dynamic)
283 for (
int i = 0; i < n_windows; i++) {
284 std::vector<double> denominator_cache(samples[i].size(), 0.0);
285 for (
size_t s = 0; s < samples[i].size(); s++) {
286 double denominator = 0.0;
287 for (
int k = 0; k < n_windows; k++) {
288 denominator += window_cache[i][k][s] * z_inv[k];
290 denominator_cache[s] = 1 / denominator;
293 for (
int j = 0; j < n_windows; j++) {
297 for (
size_t s = 0; s < samples[i].size(); s++) {
298 double sample = samples[i][s];
299 double window_j = window_cache[i][j][s];
300 double denominator = denominator_cache[s];
302 if (denominator > 0) {
303 double integrand = (window_j * z_inv[i]) * denominator;
307 MACH3LOG_WARN(
"Denominator is zero for sample {} in window {}, skipping...", sample, i);
312 MACH3LOG_INFO(
"F[{}][{}] sum: {}, count: {}", i, j, sum, count);
316 F[i][j] = sum / count;
325 std::vector<double>
zSolver(
const std::vector<double> &z_current,
326 const std::vector<WindowConfig> &windows,
327 const std::vector<std::vector<double>> &samples,
328 const std::vector<std::vector<std::vector<double>>> &window_cache,
329 [[maybe_unused]]
bool verbose =
false,
330 [[maybe_unused]]
int *total_lines =
nullptr) {
331 int n_windows =
static_cast<int>(windows.size());
334 std::vector<std::vector<double>> F =
calcFmatrix(z_current, windows, samples, window_cache);
337 std::vector<double> z_new(n_windows, 0.0);
338 for (
int i = 0; i < n_windows; i++) {
339 for (
int j = 0; j < n_windows; j++) {
340 z_new[i] += z_current[j] * F[j][i];
359 double z_magnitude = 0.0;
360 for (
int i = 0; i < n_windows; i++) {
361 z_magnitude += z_new[i] * z_new[i];
363 z_magnitude = sqrt(z_magnitude);
364 if (z_magnitude > 0) {
365 for (
int i = 0; i < n_windows; i++) {
366 z_new[i] /= z_magnitude;
375 bool checkConvergence(
const std::vector<double> &z_current,
const std::vector<double> &z_prev,
double tolerance) {
376 double sum_diffs = 0.0;
377 for (
size_t i = 0; i < z_current.size(); i++) {
386 sum_diffs += std::abs(z_current[i] - z_prev[i]);
392 if (sum_diffs /
static_cast<double>(z_current.size()) > tolerance) {
399 std::vector<double>
getZDiffs(
const std::vector<double> &z_current,
const std::vector<double> &z_prev) {
400 std::vector<double> diffs(z_current.size(), 0.0);
401 for (
size_t i = 0; i < z_current.size(); i++) {
402 diffs[i] = std::abs(z_current[i] - z_prev[i]);
412 static std::deque<std::vector<double>> z_history;
413 static std::vector<double> previous_moving_average;
414 static int stagnant_iterations = 0;
416 constexpr
int moving_average_window = 500;
417 constexpr
int stagnant_required = 500;
418 const double bound = tolerance;
420 if (z_current.empty()) {
425 if (!z_history.empty() && z_history.front().size() != z_current.size()) {
427 previous_moving_average.clear();
428 stagnant_iterations = 0;
431 z_history.push_back(z_current);
432 if (
static_cast<int>(z_history.size()) > moving_average_window) {
433 z_history.pop_front();
437 if (
static_cast<int>(z_history.size()) < moving_average_window) {
441 std::vector<double> moving_average(z_current.size(), 0.0);
442 for (
const auto &z_vec : z_history) {
443 for (
size_t i = 0; i < z_vec.size(); i++) {
444 moving_average[i] += z_vec[i];
447 for (
size_t i = 0; i < moving_average.size(); i++) {
448 moving_average[i] /= moving_average_window;
451 if (previous_moving_average.empty()) {
452 previous_moving_average = moving_average;
456 bool all_within_bound =
true;
457 for (
size_t i = 0; i < moving_average.size(); i++) {
458 if (std::abs(moving_average[i] - previous_moving_average[i]) > bound) {
459 all_within_bound =
false;
464 if (all_within_bound) {
465 stagnant_iterations++;
467 stagnant_iterations = 0;
470 previous_moving_average = moving_average;
472 if (stagnant_iterations == stagnant_required) {
473 MACH3LOG_WARN(
"Convergence appears stalled: moving-average change stayed within {} for {} iterations.",
474 bound, stagnant_required);
477 return stagnant_iterations >= stagnant_required;
480 void LoadFiles(std::vector<std::vector<double>>& samples,
UmbrellaConfig& config, std::vector<TFile *>& input_files, std::vector<TTree *>& input_trees) {
484 MACH3LOG_INFO(
"Using static input files from configuration.");
485 for (
size_t i = 0; i < config.
windows.size(); i++) {
487 TFile *file =
M3::Open(config.
windows[i].input_file.c_str(),
"READ", __FILE__, __LINE__);
489 TTree *tree =
static_cast<TTree*
>(file->Get(
"posteriors"));
496 input_files.push_back(file);
497 input_trees.push_back(tree);
503 TList *files = dir.GetListOfFiles();
508 while ((file =
static_cast<TSystemFile*
>(next()))) {
509 std::string filename = file->GetName();
510 if (filename.find(
".root") == std::string::npos) {
518 TFile *root_file =
M3::Open(full_path,
"READ", __FILE__, __LINE__);
520 TTree *tree =
static_cast<TTree*
>(root_file->Get(
"posteriors"));
524 throw MaCh3Exception(__FILE__, __LINE__,
"Missing 'posteriors' tree in file: " + full_path);
528 input_files.push_back(root_file);
529 input_trees.push_back(tree);
530 MACH3LOG_DEBUG(
"Loaded tree 'posteriors' from file: {}", full_path);
538 MACH3LOG_ERROR(
"Number of files found ({}) does not match expected dynamic_n_windows ({}).",
540 throw MaCh3Exception(__FILE__, __LINE__,
"File count mismatch for dynamic loading.");
544 for (
size_t i = 0; i < input_trees.size(); i++) {
545 TTree *tree = input_trees[i];
546 TFile *file = input_files[i];
548 MACH3LOG_DEBUG(
"Processing file {}/{}: {}", i + 1, input_trees.size(), file->GetName());
549 TMacro *macro =
static_cast<TMacro*
>(file->Get(
"MaCh3_Config"));
554 std::stringstream yaml_text;
555 TList *lines = macro->GetListOfLines();
556 for (
int iline = 0; iline < lines->GetEntries(); ++iline) {
557 TObjString *line =
static_cast<TObjString*
>(lines->At(iline));
558 yaml_text << line->GetString().Data() <<
"\n";
562 YAML::Node macro_yaml = YAML::Load(yaml_text.str());
563 YAML::Node umbrellaConfig =
564 macro_yaml[
"General"][
"MCMC"][
"Multicanonical"];
566 config.
windows[i].center = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaMean"], __FILE__, __LINE__);
569 config.
variable_of_interest = Get<std::string>(umbrellaConfig[
"VariableName"], __FILE__, __LINE__);
570 config.
windows[i].multicanonical_beta = Get<double>(umbrellaConfig[
"Beta"], __FILE__, __LINE__);
573 auto biasString = Get<std::string>(umbrellaConfig[
"Umbrella"][
"UmbrellaBiasFunction"], __FILE__, __LINE__);
575 config.
windows[i].umbrellaBiasFunction = biasMode;
579 double vonMises_sigma = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"], __FILE__, __LINE__);
580 config.
windows[i].vonMises_kappa = 1.0 / (vonMises_sigma * vonMises_sigma);
581 config.
windows[i].width = vonMises_sigma;
582 MACH3LOG_DEBUG(
"Window {} using von Mises: sigma = {}, kappa = {}",
583 i, vonMises_sigma, config.
windows[i].vonMises_kappa);
586 config.
windows[i].width = Get<double>(umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"], __FILE__, __LINE__);
587 config.
windows[i].vonMises_kappa = -1.0;
590 }
catch (
const std::exception &e) {
591 MACH3LOG_WARN(
"Could not parse macro as YAML: {}", e.what());
594 MACH3LOG_ERROR(
"Found MaCh3 chain without embedded config, this can't be right");
600 tree->SetBranchStatus(
"*",
false);
602 if (branch ==
nullptr) {
608 Long64_t nentries = tree->GetEntries();
610 samples[i].resize(nentries);
611 for (Long64_t entry = 0; entry < nentries; entry++) {
612 tree->GetEntry(entry);
613 samples[i][entry] = var_value;
616 tree->SetBranchStatus(
"*",
true);
622 for (
size_t i = 0; i < config.
windows.size(); i++) {
623 for (
size_t j = i + 1; j < config.
windows.size(); j++) {
626 std::swap(samples[i], samples[j]);
627 std::swap(input_trees[i], input_trees[j]);
628 std::swap(input_files[i], input_files[j]);
633 MACH3LOG_INFO(
"Finished loading samples, it took {:.2f}s to finish", clock.RealTime());
647 for (
size_t i = 0; i < config.
windows.size(); ++i) {
648 const auto& window = config.
windows[i];
649 TFile* file = input_files[i];
651 table.AddRow(i, file->GetName(),
658 for (
const auto& line : table.Render()) {
664 TTree *combined_tree) {
665 TDirectory* UmbrellaDir = output_file->mkdir(
"Umbrella");
668 TCanvas c1(
"c1",
"Z Evolution", 800, 600);
669 std::vector<std::unique_ptr<TGraph>> z_graphs(config.
windows.size());
670 TLegend legend(0.7, 0.7, 0.9, 0.9);
673 double ymin = std::numeric_limits<double>::max();
675 for (
size_t i = 0; i < config.
windows.size(); i++) {
676 std::vector<double> iterations, z_vals;
677 for (
size_t j = 0; j < z_evolution.size(); j++) {
678 iterations.push_back(
static_cast<int>(j));
679 z_vals.push_back(z_evolution[j][i]);
682 for (
double val : z_vals) {
689 z_graphs[i] = std::make_unique<TGraph>((iterations.size()), &iterations[0], &z_vals[0]);
690 z_graphs[i]->SetLineColor(
static_cast<Color_t
>(i + 1));
691 z_graphs[i]->SetLineWidth(2);
692 z_graphs[i]->SetName(Form(
"z_evolution_window_%lu", i));
693 z_graphs[i]->SetTitle(
"Evolution of Z Values");
694 z_graphs[i]->GetXaxis()->SetTitle(
"Iteration");
695 z_graphs[i]->GetYaxis()->SetTitle(
"Z Value");
698 z_graphs[i]->Draw(
"AL");
700 z_graphs[i]->Draw(
"L SAME");
703 legend.AddEntry(z_graphs[i].get(), Form(
"Window %lu", i),
"l");
704 z_graphs[i]->Write();
708 z_graphs[0]->SetMaximum(ymax);
709 z_graphs[0]->SetMinimum(ymin);
715 TH1D *h_delta_cp =
new TH1D(
"h_delta_cp_weighted",
"Weighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
716 TH1D *h_delta_cp_unweighted =
new TH1D(
"h_delta_cp_unweighted",
"Unweighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
719 combined_tree->Draw((variable +
">>h_delta_cp_weighted").c_str(),
"umbrella_weight",
"goff");
720 combined_tree->Draw((variable +
">>h_delta_cp_unweighted").c_str(),
"",
"goff");
723 h_delta_cp_unweighted->Write();
725 UmbrellaDir->Close();
734 MACH3LOG_INFO(
"Max threads available: {}", omp_get_max_threads());
738 MACH3LOG_INFO(
"Loading configuration from: {}", config_file);
744 MACH3LOG_ERROR(
"No windows defined in configuration and dynamic file loading is disabled.");
752 std::vector<std::vector<double>> samples;
755 samples.resize(config.
windows.size());
759 std::vector<TFile*> input_files;
760 std::vector<TTree*> input_trees;
762 LoadFiles(samples, config, input_files, input_trees);
765 std::vector<double> z_current(config.
windows.size(), 1.0);
766 std::vector<double> z_prev(config.
windows.size(), 1.0);
767 std::vector<std::vector<double>> z_evolution;
779 bool save_matrix =
true;
781 TFile *F_file =
nullptr;
784 std::string base_name = (pos != std::string::npos) ? config.
output_file.substr(0, pos) : config.
output_file;
785 F_file =
TFile::Open((base_name +
"_matrix_evolution.root").c_str(),
"RECREATE");
786 if (!F_file || F_file->IsZombie()) {
787 MACH3LOG_ERROR(
"Cannot create file {}", base_name +
"_matrix_evolution.root");
791 std::vector<std::vector<double>> initial_F =
calcFmatrix(z_current, config.
windows, samples, window_cache);
792 int n_windows =
static_cast<int>(config.
windows.size());
793 TH2D initial_F_TH2D(
"F_matrix_initial",
"Initial F matrix;Window j;Window i", n_windows, 0, n_windows, n_windows, 0, n_windows);
794 for (
int i = 0; i < n_windows; i++) {
795 for (
int j = 0; j < n_windows; j++) {initial_F_TH2D.SetBinContent(j + 1, i + 1, initial_F[i][j]);
800 initial_F_TH2D.Write();
804 auto start_time = std::chrono::high_resolution_clock::now();
805 auto last_print_time = start_time;
807 bool converged_robustness_check =
false;
811 if (!converged_robustness_check) {
812 MACH3LOG_INFO(
"Starting iterative solver with convergence checks...");
815 int total_output_lines = 0;
816 for (
int iteration = 0; iteration < config.
max_iterations; iteration++) {
818 auto current_time = std::chrono::high_resolution_clock::now();
820 double avg_relative_change = 0.0;
822 for (
size_t i = 0; i < z_current.size(); i++) {
823 double rel_change = std::abs(z_current[i] - z_prev[i]) / std::max(std::abs(z_current[i]), 1e-10);
824 avg_relative_change += rel_change;
826 avg_relative_change /=
static_cast<double>(z_current.size());
830 std::string z_values =
"[";
831 for (
size_t i = 0; i < z_current.size(); i++) {
832 z_values += fmt::format(
"{:10.5f}", z_current[i]);
833 if (i < z_current.size() - 1) {
839 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_print_time);
840 double avg_time_per_iteration =
static_cast<double>(duration.count()) /
842 MACH3LOG_INFO(
"Iteration {:6}: z values: {} (avg: {:6.1f} ms/iter)", iteration, z_values, avg_time_per_iteration);
844 total_output_lines = 2;
846 MACH3LOG_INFO(
"Iteration {:6}: z values: {}", iteration, z_values);
847 total_output_lines = 1;
849 last_print_time = current_time;
854 z_evolution.push_back(z_current);
860 if (save_matrix && (iteration < 15 || iteration % config.
print_frequency == 0)) {
861 std::vector<std::vector<double>> F_matrix =
calcFmatrix(z_current, config.
windows, samples, window_cache);
863 int n_windows =
static_cast<int>(config.
windows.size());
864 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);
865 for (
int i = 0; i < n_windows; i++) {
866 for (
int j = 0; j < n_windows; j++) {
867 F_TH2D.SetBinContent(j + 1, i + 1, F_matrix[i][j]);
872 TTree *z_tree =
new TTree(Form(
"z_saved_iter_%02d", iteration), Form(
"Z vector at iteration %02d", iteration));
873 z_tree->Branch(
"z_saved", &z_current);
876 MACH3LOG_INFO(
"Saving F matrix for iteration {} to file...", iteration);
888 bool apply_robustness_check =
true;
889 if (iteration % 100 == 0 &&
891 if (!converged_robustness_check && apply_robustness_check) {
892 MACH3LOG_INFO(
"Convergence check passed at iteration {}. Starting robustness check with random perturbation...", iteration);
893 converged_robustness_check =
true;
896 std::vector<double> z_perturbed = z_current;
897 for (
size_t i = 0; i < z_perturbed.size(); i++) {
900 z_perturbed[i] += perturbation;
901 if (z_perturbed[i] < 0) {
902 z_perturbed[i] = abs(z_perturbed[i]);
904 MACH3LOG_INFO(
"z[{:2}] | Original: {:.6e} | Perturbation: {:+.6e} | Updated: {:.6e}",
905 i, z_current[i], perturbation, z_perturbed[i]);
907 z_current = z_perturbed;
908 MACH3LOG_INFO(
"Applied random perturbation to z values for robustness check.");
911 MACH3LOG_INFO(
"Convergence achieved at iteration {}", iteration);
913 MACH3LOG_WARN(
"Convergence appears to be stalled at iteration {}", iteration);
917 MACH3LOG_WARN(
"Reached maximum iterations without convergence.");
920 auto end_time = std::chrono::high_resolution_clock::now();
921 auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
922 double avg_time_total =
static_cast<double>(total_duration.count()) /
923 static_cast<double>(iteration + 1);
928 MACH3LOG_INFO(
"Average time per iteration: {:.6f} ms", avg_time_total);
934 std::ostringstream oss;
935 oss <<
"Final z values: [";
936 for (
size_t i = 0; i < z_current.size(); i++) {
937 oss << std::fixed << std::setprecision(5) << z_current[i];
938 if (i < z_current.size() - 1)
944 std::filesystem::copy_file(input_files[0]->GetName(), config.
output_file,
945 std::filesystem::copy_options::overwrite_existing);
950 TTree *input_tree =
dynamic_cast<TTree*
>(output_file->Get(
"posteriors"));
952 TTree *combined_tree = input_tree->CloneTree(0);
955 double umbrella_weight;
957 double umbrella_variable;
959 combined_tree->Branch(
"umbrella_weight", &umbrella_weight,
"umbrella_weight/D");
960 combined_tree->Branch(
"window_id", &window_id,
"window_id/I");
963 for (
size_t i = 0; i < input_trees.size(); i++) {
964 TTree *tree = input_trees[i];
966 Long64_t nentries = tree->GetEntries();
969 int oldLevel = gErrorIgnoreLevel;
970 gErrorIgnoreLevel = kError;
971 combined_tree->CopyAddresses(tree);
972 gErrorIgnoreLevel = oldLevel;
978 if (z_current[i] == 0) {
979 MACH3LOG_WARN(
"Z value for window {} is zero, skipping weighting for this window to avoid division by zero.", i);
981 window_id =
static_cast<int>(i);
983 for (Long64_t entry = 0; entry < nentries; entry++) {
984 tree->GetEntry(entry);
986 if (z_current[i] == 0) {
987 umbrella_weight = 0.0;
994 umbrella_weight = denominator;
997 if (combined_tree->Fill() < 0) {
998 MACH3LOG_WARN(
"Failed writing output tree. Check disk quota/space and write permissions for: {}", config.
output_file);
1004 combined_tree->Write(input_tree->GetName(), TObject::kOverwrite);
1010 Long64_t total_entries = combined_tree->GetEntries();
1015 YAML::Node umbrella_config;
1016 umbrella_config[
"UmbrellaSolver"] = yaml_config[
"UmbrellaSolver"];
1019 TMacro UmbrellaHeader =
YAMLtoTMacro(umbrella_config,
"Umbrella_Config");
1020 UmbrellaHeader.Write();
1022 output_file->Close();
1025 for (TFile *file : input_files) {
1029 MACH3LOG_INFO(
"Umbrella sampling outputs created (make sure to check for convergence issues)!");
1031 MACH3LOG_INFO(
"Combined tree contains {} entries with umbrella weights.", total_entries);
1037 std::string config_file =
"umbrella_config.yaml";
1039 config_file = argv[1];
1044 }
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[])
void LoadFiles(std::vector< std::vector< double >> &samples, UmbrellaConfig &config, std::vector< TFile * > &input_files, std::vector< TTree * > &input_trees)
void DiagnoseUmbrella(const UmbrellaConfig &config, TFile *output_file, const std::vector< std::vector< double >> &z_evolution, TTree *combined_tree)
double summedWindowsWeighted(double x, const std::vector< WindowConfig > &windows, const std::vector< double > &z_values)
double vonMisesWindow(double x, double center, double kappa)
double GetMulticanonicalWeightGenGaussian(double deltacp, double mean, double width, double multicanonicalBeta)
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 gaussianWindow(double x, double center, double width)
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ bool debug_mode
std::vector< std::vector< std::vector< double > > > buildWindowCache(const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples)
std::vector< std::vector< double > > calcFmatrix(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)
size_t calculateWindowCacheSizeBytes(const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples)
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, [[maybe_unused]] bool verbose=false, [[maybe_unused]] int *total_lines=nullptr)
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.
Creates fancy formatted text tables for terminal output.
Custom exception class used throughout MaCh3.
static std::string Cell(T &&value, std::string_view format)
Format a value using a runtime fmt format string.
@ Right
Align text to the right.
@ Left
Align text to the left.
double Uniform(const double min=0.0, const double max=1.0)
Generate a uniform random number in [min, max).
M3::BiasFunction ParseBiasFunction(const std::string &biasFunctionName)
std::string BiasFunction_ToString(const M3::BiasFunction &biasFunction)
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.
int GetNThreads()
number of threads which we need for example for TRandom3
constexpr double UmbrellaGaussianNormFactor
@ kGaussian
Assumes gaussian prior.
std::string variable_of_interest
std::vector< WindowConfig > windows
std::string dynamic_pattern
double multicanonical_beta
M3::BiasFunction umbrellaBiasFunction