596 std::cout <<
"=== Umbrella Sampling Z-Factor Solver ===" << std::endl;
599 std::cout <<
"Debugging OpenMP availability..." << std::endl;
602 std::cout <<
"_OPENMP is defined with value: " << _OPENMP << std::endl;
603 std::cout <<
"OpenMP version: " << _OPENMP << std::endl;
604 std::cout <<
"Max threads available: " << omp_get_max_threads() << std::endl;
606 std::cout <<
"_OPENMP is NOT defined - OpenMP not available" << std::endl;
609 std::cout <<
"Loading configuration from: " << config_file << std::endl;
615 std::cerr <<
"Error: No windows defined in configuration and dynamic file loading is disabled." << std::endl;
619 std::cout <<
"Variable of interest: " <<
config.variable_of_interest << std::endl;
620 std::cout <<
"Output file: " <<
config.output_file << std::endl;
624 bool openmp_available =
true;
625 std::cout <<
"OpenMP: AVAILABLE" << std::endl;
627 std::cout <<
"OpenMP: ENABLED (using " << omp_get_max_threads() <<
" threads)" << std::endl;
630 bool openmp_available =
false;
631 std::cout <<
"OpenMP: NOT AVAILABLE" << std::endl;
633 std::cout <<
"OpenMP: NOT AVAILABLE - falling back to single-threaded execution" << std::endl;
634 std::cout <<
"Note: For OpenMP support, try compiling with: g++ -fopenmp ..." << std::endl;
635 std::cout <<
"Or ensure OpenMP library is properly loaded in ROOT" << std::endl;
636 config.use_openmp =
false;
638 std::cout <<
"OpenMP: DISABLED (single-threaded execution)" << std::endl;
643 std::vector<std::vector<double>> samples;
645 if (!
config.dynamic_files) {
646 samples.resize(
config.windows.size());
648 samples.resize(
config.dynamic_n_windows);
650 std::vector<TFile *> input_files;
651 std::vector<TTree *> input_trees;
653 if (!
config.dynamic_files) {
654 std::cout <<
"Using static input files from configuration." << std::endl;
655 for (
size_t i = 0; i <
config.windows.size(); i++) {
656 std::cout <<
"Loading file: " <<
config.windows[i].input_file << std::endl;
659 if (!file || file->IsZombie()) {
660 std::cerr <<
"Error: Cannot open file " <<
config.windows[i].input_file << std::endl;
664 TTree *tree = (TTree *)file->Get(
"posteriors");
666 std::cerr <<
"Error: Cannot find 'posteriors' tree in " <<
config.windows[i].input_file << std::endl;
671 input_files.push_back(file);
672 input_trees.push_back(tree);
675 std::cout <<
"Dynamic file loading enabled. Searching for files in directory: " <<
config.dynamic_pattern << std::endl;
677 TSystemDirectory dir(
"",
config.dynamic_pattern.c_str());
678 TList *files = dir.GetListOfFiles();
683 while ((file = (TSystemFile *)next())) {
684 std::string filename = file->GetName();
685 if (filename.find(
".root") == std::string::npos) {
686 std::cout <<
"Skipping non-root file: " << filename << std::endl;
690 std::string full_path =
config.dynamic_pattern +
"/" + filename;
691 std::cout <<
"Found file: " << full_path << std::endl;
693 TFile *root_file =
TFile::Open(full_path.c_str(),
"READ");
694 if (!root_file || root_file->IsZombie()) {
695 std::cerr <<
"Error: Cannot open file " << full_path << std::endl;
696 throw std::runtime_error(
"Cannot open file: " + full_path);
699 TTree *tree = (TTree *)root_file->Get(
"posteriors");
701 std::cerr <<
"Error: Cannot find 'posteriors' tree in " << full_path << std::endl;
703 throw std::runtime_error(
"Missing 'posteriors' tree in file: " + full_path);
708 input_files.push_back(root_file);
709 input_trees.push_back(tree);
710 std::cout <<
"Loaded tree 'posteriors' from file: " << full_path << std::endl;
713 std::cerr <<
"Error: No files found matching pattern: " <<
config.dynamic_pattern << std::endl;
714 throw std::runtime_error(
"No files found matching pattern: " +
config.dynamic_pattern);
717 if (file_count !=
config.dynamic_n_windows) {
718 std::cerr <<
"Error: Number of files found (" << file_count <<
") does not match expected dynamic_n_windows (" <<
config.dynamic_n_windows <<
")." << std::endl;
719 throw std::runtime_error(
"File count mismatch for dynamic loading.");
723 for (
size_t i = 0; i < input_trees.size(); i++) {
724 TTree *tree = input_trees[i];
725 TFile *file = input_files[i];
727 std::cout <<
"\nProcessing file " << i + 1 <<
"/" << input_trees.size() <<
": " << file->GetName() << std::endl;
729 TMacro *macro = (TMacro *)file->Get(
"MaCh3_Config");
731 std::cout <<
"Found MaCh3_Config macro in file." << std::endl;
734 std::stringstream yaml_text;
735 TList *lines = macro->GetListOfLines();
736 for (
int iline = 0; iline < lines->GetEntries(); ++iline) {
737 TObjString *line = (TObjString *)lines->At(iline);
738 yaml_text << line->GetString().Data() <<
"\n";
742 YAML::Node macro_yaml = YAML::Load(yaml_text.str());
743 YAML::Node umbrellaConfig =
744 macro_yaml[
"General"][
"MCMC"][
"Multicanonical"];
746 config.windows[i].center = umbrellaConfig[
"Umbrella"][
"UmbrellaMean"].as<
double>();
747 std::cout <<
"Window " << i <<
" center updated to " <<
config.windows[i].center << std::endl;
750 std::string biasString = umbrellaConfig[
"Umbrella"][
"UmbrellaBiasFunction"].as<std::string>();
752 if (biasString ==
"gaussian") {
754 std::cout <<
"Window wieghted with gaussian" << std::endl;
755 }
else if (biasString ==
"generalisedGaussian") {
757 std::cout <<
"Window weighted with generalised gaussian" << std::endl;
758 }
else if (biasString ==
"vonMises") {
760 std::cout <<
"Window weighted with vonMises" << std::endl;
762 std::cout <<
"Unrecognised Bias" << std::endl;
764 config.windows[i].umbrellaBiasFunction = biasMode;
768 double vonMises_sigma = umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"].as<
double>();
769 config.windows[i].vonMises_kappa = 1.0 / (vonMises_sigma * vonMises_sigma);
770 config.windows[i].width = vonMises_sigma;
771 std::cout <<
"Window " << i <<
" using von Mises: sigma = " << vonMises_sigma <<
", kappa = " <<
config.windows[i].vonMises_kappa << std::endl;
774 config.windows[i].width = umbrellaConfig[
"Umbrella"][
"UmbrellaWidth"].as<
double>();
775 config.windows[i].vonMises_kappa = -1.0;
776 std::cout <<
"Window " << i <<
" using Gaussian: width = " <<
config.windows[i].width << std::endl;
779 }
catch (
const std::exception &e) {
780 std::cerr <<
"Warning: Could not parse macro as YAML: " << e.what() << std::endl;
786 tree->SetBranchAddress(
config.variable_of_interest.c_str(), &var_value);
787 tree->SetBranchAddress(
"LogL", &logL_value);
789 Long64_t nentries = tree->GetEntries();
790 Long64_t filtered_entries = 0;
791 std::cout <<
"Window " << i <<
": " << nentries <<
" entries" << std::endl;
793 for (Long64_t entry = 0; entry < nentries; entry++) {
794 tree->GetEntry(entry);
800 samples[i].push_back(var_value);
811 for (
size_t i = 0; i <
config.windows.size(); i++) {
812 for (
size_t j = i + 1; j <
config.windows.size(); j++) {
813 if (
config.windows[i].center >
config.windows[j].center) {
815 std::swap(samples[i], samples[j]);
816 std::swap(input_trees[i], input_trees[j]);
817 std::swap(input_files[i], input_files[j]);
823 std::cout <<
"\nFinal window configurations after sorting:" << std::endl;
824 for (
size_t i = 0; i <
config.windows.size(); i++) {
825 std::cout <<
"Window " << i <<
": center = " <<
config.windows[i].center
826 <<
", width = " <<
config.windows[i].width
828 <<
", vonMises_kappa = " <<
config.windows[i].vonMises_kappa
829 <<
", samples = " << samples[i].size() << std::endl;
833 std::vector<double> z_current(
config.windows.size(), 1.0);
834 std::vector<double> z_prev(
config.windows.size(), 1.0);
837 bool hacky_start =
false;
847 0.023968629123202176, 0.024927005713178161, 0.026030888791054529,
848 0.036203405237770721, 0.04004944137212621, 0.055993616350153479,
849 0.079251266929094608, 0.065860139904686643, 0.067944181615205768,
850 0.055237804689517243, 0.054778778031073304, 0.066196102148964917,
851 0.059298667596959342, 0.049864361722134341, 0.048890393249559315,
852 0.05284144211204099, 0.050606183191239794, 0.037329936801427918,
853 0.025159187577401387, 0.01487697249782439, 0.0074395236463036573,
854 0.0040535024095969992, 0.0025347709967512371, 0.0015590961074484638,
855 0.00083136243905723782, 0.00047599269828616062, 0.0004454414833514952,
856 0.00045205403592812914, 0.00062098732584897902, 0.00091722187629238541,
857 0.0012407301891732216, 0.0023494590898020503, 0.0041640866720417773,
858 0.0080599626292932776, 0.011429554834689368, 0.018117848911520615};
861 std::cout <<
"!!!!!!!starting from hacky start vector!!!!!!" << std::endl;
864 std::vector<std::vector<double>> z_evolution;
866 std::cout <<
"\nStarting iterative z-solver..." << std::endl;
869 bool openmp_works =
false;
871 std::cout <<
"Testing OpenMP parallelization..." << std::endl;
873 int max_threads = omp_get_max_threads();
878 std::cout <<
"Max threads reported: " << max_threads << std::endl;
881 int actual_threads = 1;
891 actual_threads = omp_get_num_threads();
895 std::cout <<
"Actual threads in parallel region: " << actual_threads << std::endl;
899 if (actual_threads > 1) {
900 std::cout <<
"OpenMP is working correctly with " << actual_threads <<
" threads" << std::endl;
902 }
else if (max_threads > 1) {
903 std::cout <<
"WARNING: OpenMP pragmas not working in CLING - falling back to single-threaded" << std::endl;
904 openmp_works =
false;
906 openmp_works =
false;
910 std::cout <<
"Precomputing window cache..." << std::endl;
913 std::vector<std::vector<std::vector<double>>> window_cache =
buildWindowCache(
config.windows, samples, openmp_works);
916 bool save_matrix =
true;
918 TFile *F_file =
nullptr;
920 size_t pos =
config.output_file.find(
".root");
921 std::string base_name = (pos != std::string::npos) ?
config.output_file.substr(0, pos) :
config.output_file;
922 F_file =
TFile::Open((base_name +
"_matrix_evolution.root").c_str(),
"RECREATE");
923 if (!F_file || F_file->IsZombie()) {
924 std::cerr <<
"Error: Cannot create file " << base_name +
"_matrix_evolution.root" << std::endl;
928 std::vector<std::vector<double>> initial_F =
calcFmatrix(z_current,
config.windows, samples, window_cache, openmp_works);
929 int n_windows =
config.windows.size();
930 TH2D initial_F_TH2D(
"F_matrix_initial",
"Initial F matrix;Window j;Window i", n_windows, 0, n_windows, n_windows, 0, n_windows);
931 for (
int i = 0; i < n_windows; i++) {
932 for (
int j = 0; j < n_windows; j++) {initial_F_TH2D.SetBinContent(j + 1, i + 1, initial_F[i][j]);
936 std::cout <<
"Saving initial F matrix to file..." << std::endl;
937 initial_F_TH2D.Write();
941 auto start_time = std::chrono::high_resolution_clock::now();
942 auto last_print_time = start_time;
944 bool converged_robustness_check =
false;
948 if (!converged_robustness_check) {
949 std::cout <<
"\nStarting iterative solver with convergence checks..." << std::endl;
953 int total_output_lines = 0;
954 for (
int iteration = 0; iteration <
config.max_iterations; iteration++) {
955 if (iteration %
config.print_frequency == 0 || iteration == 1) {
956 auto current_time = std::chrono::high_resolution_clock::now();
961 std::cout <<
"\033[" << total_output_lines <<
"A";
962 std::cout <<
"\033[J";
966 double avg_relative_change = 0.0;
968 for (
size_t i = 0; i < z_current.size(); i++) {
969 double rel_change = std::abs(z_current[i] - z_prev[i]) / std::max(std::abs(z_current[i]), 1e-10);
970 avg_relative_change += rel_change;
972 avg_relative_change /= z_current.size();
976 std::cout <<
"Iteration " << std::setw(6) << iteration <<
", z values: [";
977 for (
size_t i = 0; i < z_current.size(); i++) {
978 std::cout << std::setw(10) << std::fixed << std::setprecision(5) << z_current[i];
979 if (i < z_current.size() - 1)
983 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_print_time);
984 double avg_time_per_iteration = duration.count() / double(
config.print_frequency);
985 std::cout <<
"] (avg: " << std::setw(6) << std::setprecision(1) << avg_time_per_iteration <<
" ms/iter)" << std::endl;
986 std::cout <<
"Avg relative change: " << std::scientific << std::setprecision(3) << avg_relative_change <<
" (target: " <<
config.tolerance <<
")" << std::endl;
987 total_output_lines = 2;
989 std::cout <<
"]" << std::endl;
990 total_output_lines = 1;
993 last_print_time = current_time;
997 z_current =
zSolver(z_current,
config.windows, samples, window_cache, openmp_works, iteration %
config.print_frequency == 0, &total_output_lines);
998 z_evolution.push_back(z_current);
1004 if (save_matrix && (iteration < 15 || iteration %
config.print_frequency == 0)) {
1005 std::vector<std::vector<double>> F_matrix =
calcFmatrix(z_current,
config.windows, samples, window_cache, openmp_works);
1007 int n_windows =
config.windows.size();
1008 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);
1009 for (
int i = 0; i < n_windows; i++) {
1010 for (
int j = 0; j < n_windows; j++) {
1011 F_TH2D.SetBinContent(j + 1, i + 1, F_matrix[i][j]);
1016 TTree *z_tree =
new TTree(Form(
"z_saved_iter_%02d", iteration), Form(
"Z vector at iteration %02d", iteration));
1017 z_tree->Branch(
"z_saved", &z_current);
1020 std::cout <<
"Saving F matrix for iteration " << iteration <<
" to file..." << std::endl;
1032 bool apply_robustness_check =
true;
1033 if (iteration % 100 == 0 &&
1035 if (!converged_robustness_check && apply_robustness_check) {
1036 std::cout <<
"\nConvergence check passed at iteration " << iteration <<
". Starting robustness check with random perturbation..." << std::endl;
1037 converged_robustness_check =
true;
1040 std::vector<double> z_perturbed = z_current;
1041 for (
size_t i = 0; i < z_perturbed.size(); i++) {
1042 double perturbation = (rand() / RAND_MAX - 0.5) * z_perturbed[i];
1043 std::cout <<
"Applying perturbation of " << std::scientific << std::setprecision(6) << perturbation <<
" to z[" << i <<
"] = " << std::scientific << std::setprecision(6) << z_perturbed[i] << std::endl;
1044 z_perturbed[i] += perturbation;
1045 if (z_perturbed[i] < 0)
1046 z_perturbed[i] = abs(z_perturbed[i]);
1048 z_current = z_perturbed;
1049 std::cout <<
"\nApplied random perturbation to z values for robustness "
1050 "check.\n" << std::endl;
1054 std::cout <<
"\nConvergence achieved at iteration " << iteration << std::endl;
1056 std::cout <<
"\nConvergence appears to be stalled at iteration " << iteration << std::endl;
1059 if (iteration ==
config.max_iterations - 1) {
1060 std::cout <<
"Reached maximum iterations without convergence." << std::endl;
1063 auto end_time = std::chrono::high_resolution_clock::now();
1064 auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
1065 double avg_time_total = total_duration.count() / double(iteration + 1);
1069 std::cout <<
"\nTerminating at iteration " << iteration << std::endl;
1070 std::cout <<
"Average time per iteration: " << avg_time_total <<
" ms" << std::endl;
1076 std::cout <<
"\nFinal z values: [";
1077 for (
size_t i = 0; i < z_current.size(); i++) {
1078 std::cout << std::fixed << std::setprecision(5) << z_current[i];
1079 if (i < z_current.size() - 1)
1082 std::cout <<
"]" << std::endl;
1086 if (!output_file || output_file->IsZombie()) {
1087 throw std::runtime_error(
"Cannot create output file: " +
config.output_file);
1092 TTree *combined_tree =
new TTree(
"posteriors",
"Combined Posterior Distributions");
1095 double sin2th_12, sin2th_23, sin2th_13, delm2_12, delm2_23, delta_cp;
1096 double baseline, density, LogL, accProb, stepTime;
1098 std::vector<double> LogL_samples(6);
1099 double LogL_systematic_osc_cov;
1100 double umbrella_weight;
1105 combined_tree->Branch(
"sin2th_12", &sin2th_12,
"sin2th_12/D");
1106 combined_tree->Branch(
"sin2th_23", &sin2th_23,
"sin2th_23/D");
1107 combined_tree->Branch(
"sin2th_13", &sin2th_13,
"sin2th_13/D");
1108 combined_tree->Branch(
"delm2_12", &delm2_12,
"delm2_12/D");
1109 combined_tree->Branch(
"delm2_23", &delm2_23,
"delm2_23/D");
1110 combined_tree->Branch(
"delta_cp", &delta_cp,
"delta_cp/D");
1111 combined_tree->Branch(
"baseline", &baseline,
"baseline/D");
1112 combined_tree->Branch(
"density", &density,
"density/D");
1113 combined_tree->Branch(
"LogL", &LogL,
"LogL/D");
1114 combined_tree->Branch(
"accProb", &accProb,
"accProb/D");
1115 combined_tree->Branch(
"step", &step,
"step/I");
1116 combined_tree->Branch(
"stepTime", &stepTime,
"stepTime/D");
1117 for (
int i = 0; i < 6; i++) {
1118 combined_tree->Branch(Form(
"LogL_sample_%d", i), &LogL_samples[i], Form(
"LogL_sample_%d/D", i));
1120 combined_tree->Branch(
"LogL_systematic_osc_cov", &LogL_systematic_osc_cov,
"LogL_systematic_osc_cov/D");
1121 combined_tree->Branch(
"umbrella_weight", &umbrella_weight,
"umbrella_weight/D");
1122 combined_tree->Branch(
"window_id", &window_id,
"window_id/I");
1125 for (
size_t i = 0; i < input_trees.size(); i++) {
1126 TTree *tree = input_trees[i];
1129 tree->SetBranchAddress(
"sin2th_12", &sin2th_12);
1130 tree->SetBranchAddress(
"sin2th_23", &sin2th_23);
1131 tree->SetBranchAddress(
"sin2th_13", &sin2th_13);
1132 tree->SetBranchAddress(
"delm2_12", &delm2_12);
1133 tree->SetBranchAddress(
"delm2_23", &delm2_23);
1134 tree->SetBranchAddress(
"delta_cp", &delta_cp);
1135 tree->SetBranchAddress(
"baseline", &baseline);
1136 tree->SetBranchAddress(
"density", &density);
1137 tree->SetBranchAddress(
"LogL", &LogL);
1138 tree->SetBranchAddress(
"accProb", &accProb);
1139 tree->SetBranchAddress(
"step", &step);
1140 tree->SetBranchAddress(
"stepTime", &stepTime);
1141 for (
int j = 0; j < 6; j++) {
1142 tree->SetBranchAddress(Form(
"LogL_sample_%d", j), &LogL_samples[j]);
1144 tree->SetBranchAddress(
"LogL_systematic_osc_cov", &LogL_systematic_osc_cov);
1146 Long64_t nentries = tree->GetEntries();
1148 if (z_current[i] == 0) {
1149 std::cout <<
"WARNING: Z value for window " << i <<
" is zero, skipping weighting for this window to avoid division by zero." << std::endl;
1152 for (Long64_t entry = 0; entry < nentries; entry++) {
1153 tree->GetEntry(entry);
1155 if (z_current[i] == 0) {
1156 umbrella_weight = 0.0;
1163 umbrella_weight = denominator;
1165 if (combined_tree->Fill() < 0) {
1166 throw std::runtime_error(
"Failed writing output tree. Check disk quota/space and write permissions for: " +
config.output_file);
1173 TCanvas *c1 =
new TCanvas(
"c1",
"Z Evolution", 800, 600);
1175 std::vector<TGraph*> z_graphs(
config.windows.size());
1176 TLegend *legend =
new TLegend(0.7, 0.7, 0.9, 0.9);
1179 double ymin = std::numeric_limits<double>::max();
1181 for (
size_t i = 0; i <
config.windows.size(); i++) {
1182 std::vector<double> iterations, z_vals;
1183 for (
size_t j = 0; j < z_evolution.size(); j++) {
1184 iterations.push_back(j);
1185 z_vals.push_back(z_evolution[j][i]);
1188 for (
double val : z_vals) {
1195 z_graphs[i] =
new TGraph(iterations.size(), &iterations[0], &z_vals[0]);
1196 z_graphs[i]->SetLineColor(i + 1);
1197 z_graphs[i]->SetLineWidth(2);
1198 z_graphs[i]->SetName(Form(
"z_evolution_window_%lu", i));
1199 z_graphs[i]->SetTitle(
"Evolution of Z Values");
1202 z_graphs[i]->GetXaxis()->SetTitle(
"Iteration");
1203 z_graphs[i]->GetYaxis()->SetTitle(
"Z Value");
1204 z_graphs[i]->Draw(
"AL");
1206 z_graphs[i]->Draw(
"L SAME");
1209 legend->AddEntry(z_graphs[i], Form(
"Window %lu", i),
"l");
1210 z_graphs[i]->Write();
1214 z_graphs[0]->SetMaximum(ymax);
1215 z_graphs[0]->SetMinimum(ymin);
1221 combined_tree->Write();
1224 TH1D *h_delta_cp =
new TH1D(
"h_delta_cp_weighted",
"Weighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1225 TH1D *h_delta_cp_unweighted =
new TH1D(
"h_delta_cp_unweighted",
"Unweighted Delta CP Distribution", 100, -TMath::Pi(), TMath::Pi());
1227 combined_tree->Draw(
"delta_cp>>h_delta_cp_weighted",
"umbrella_weight",
"goff");
1228 combined_tree->Draw(
"delta_cp>>h_delta_cp_unweighted",
"",
"goff");
1230 h_delta_cp->Write();
1231 h_delta_cp_unweighted->Write();
1234 Long64_t total_entries = combined_tree->GetEntries();
1236 output_file->Close();
1239 for (TFile *file : input_files) {
1243 std::cout <<
"\nUmbrella sampling outputs created (make sure to check for convergence issues)!" << std::endl;
1244 std::cout <<
"Output written to: " <<
config.output_file << std::endl;
1245 std::cout <<
"Combined tree contains " << total_entries <<
" entries with umbrella weights." << std::endl;
double summedWindowsWeighted(double x, const std::vector< WindowConfig > &windows, const std::vector< double > &z_values)
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, bool use_openmp=true)
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)
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, int *total_lines=nullptr)
bool checkConvergenceStalled(const std::vector< double > &z_current, const std::vector< double > &z_prev, double tolerance)
std::vector< std::vector< std::vector< double > > > buildWindowCache(const std::vector< WindowConfig > &windows, const std::vector< std::vector< double >> &samples, bool use_openmp=true)