From dcbe7ac9830802cd770bc41978817554ba2437f0 Mon Sep 17 00:00:00 2001 From: Alexander Richards Date: Wed, 4 Mar 2026 17:26:35 +0000 Subject: [PATCH 01/26] Initial unified binary work --- CMakeLists.txt | 4 +- Diagnostics/CMakeLists.txt | 26 +- Diagnostics/ProcessMCMC.cpp | 829 +---------------------------- Diagnostics/ProcessMCMCPlugin.cpp | 850 ++++++++++++++++++++++++++++++ Diagnostics/ProcessMCMCPlugin.hpp | 59 +++ api/CMakeLists.txt | 35 ++ api/argparse.hpp | 29 + api/plugin.hpp | 20 + cli/CMakeLists.txt | 4 + cli/mach3.cpp | 197 +++++++ 10 files changed, 1235 insertions(+), 818 deletions(-) create mode 100644 Diagnostics/ProcessMCMCPlugin.cpp create mode 100644 Diagnostics/ProcessMCMCPlugin.hpp create mode 100644 api/CMakeLists.txt create mode 100644 api/argparse.hpp create mode 100644 api/plugin.hpp create mode 100644 cli/CMakeLists.txt create mode 100644 cli/mach3.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 762554943..74f4fcf77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,6 +281,8 @@ endforeach() ################################# Build MaCh3 ################################## # Build components +add_subdirectory(api) +add_subdirectory(cli) add_subdirectory(Manager) add_subdirectory(Parameters) add_subdirectory(Splines) @@ -305,7 +307,7 @@ add_library(MaCh3 INTERFACE) if (MaCh3_NUDOCK_ENABLED) target_link_libraries(MaCh3 INTERFACE MaCh3NuDock) endif() -target_link_libraries(MaCh3 INTERFACE Fitters Samples Parameters Splines Manager MaCh3CompilerOptions MaCh3GPUCompilerOptions Plotting) +target_link_libraries(MaCh3 INTERFACE api Fitters Samples Parameters Splines Manager MaCh3CompilerOptions MaCh3GPUCompilerOptions Plotting) set_target_properties(MaCh3 PROPERTIES EXPORT_NAME All) install(TARGETS MaCh3 diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index 0b6d187ae..f83a49db7 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -1,3 +1,27 @@ +set(HEADERS + ProcessMCMCPlugin.hpp +) + + +add_library(Plugins STATIC + ProcessMCMCPlugin.cpp +) + +target_link_libraries(Plugins PRIVATE MaCh3::All) +target_include_directories(Plugins PUBLIC $) + +# set_target_properties(Plugins PROPERTIES +# PUBLIC_HEADER "${HEADERS}" +# EXPORT_NAME Plugins) + + +# install(TARGETS Plugins +# EXPORT MaCh3-targets +# LIBRARY DESTINATION lib/ +# PUBLIC_HEADER DESTINATION include/Plugins) + +add_library(MaCh3::Plugins ALIAS Plugins) + add_custom_target(DiagApps) foreach(app @@ -14,7 +38,7 @@ foreach(app ) add_executable(${app} ${app}.cpp) add_dependencies(DiagApps ${app}) - target_link_libraries(${app} MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) + target_link_libraries(${app} MaCh3::All MaCh3::Plugins MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either install(TARGETS ${app} DESTINATION bin) endforeach(app) diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index 9eadd7897..de2ad19ae 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -1,817 +1,14 @@ -//MaCh3 includes -#include "Fitters/OscProcessor.h" -#include "Manager/Manager.h" - -/// @file ProcessMCMC.cpp -/// @brief Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implantation of methods is in MCMCProcessor -/// @ingroup MaCh3DiagnosticProcessing -/// -/// @author Kamil Skwarczynski - -/// @brief Main function processing MCMC and Producing plots -inline void ProcessMCMC(const std::string& inputFile); -/// @brief Function producing comparison of posterior and more betwen a few MCMC chains -inline void MultipleProcessMCMC(); -inline void CalcBayesFactor(MCMCProcessor* Processor); -inline void CalcSavageDickey(MCMCProcessor* Processor); -inline void CalcBipolarPlot(MCMCProcessor* Processor); -inline void CalcParameterEvolution(MCMCProcessor* Processor); -inline void GetTrianglePlot(MCMCProcessor* Processor); -inline void DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile); -/// @brief KS: Convert TMatrix to TH2D, mostly useful for making fancy plots -inline TH2D* TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title); -/// @brief KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution -inline void KolmogorovSmirnovTest(const std::vector>& Processor, - const std::unique_ptr& Posterior, - const TString& canvasname); -int nFiles; -std::vector FileNames; -std::vector TitleNames; -std::string config; - -int main(int argc, char *argv[]) -{ - SetMaCh3LoggerFormat(); - nFiles = 0; - if (argc != 3 && argc !=6 && argc != 8) - { - MACH3LOG_ERROR("How to use: "); - MACH3LOG_ERROR(" single chain: {} ", argv[0]); - MACH3LOG_ERROR(" two chain: {} <MCMC_ND_Output_2.root> <Title 2>", argv[0]); - MACH3LOG_ERROR(" three chain: {} <Config> <MCMM_ND_Output_1.root> <Title 1> <MCMC_ND_Output_2.root> <Title 2> <MCMC_ND_Output_3.root> <Title 3>", argv[0]); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - - config = argv[1]; - YAML::Node card_yaml = M3OpenConfig(config); - if (!CheckNodeExists(card_yaml, "ProcessMCMC")) { - MACH3LOG_ERROR("The 'ProcessMCMC' node is not defined in the YAML configuration."); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - - if (argc == 3) - { - MACH3LOG_INFO("Producing single fit output"); - std::string filename = argv[2]; - ProcessMCMC(filename); - } - // If we want to compare two or more fits (e.g. binning changes or introducing new params/priors) - else if (argc == 6 || argc == 8) - { - MACH3LOG_INFO("Producing two fit comparison"); - FileNames.push_back(argv[2]); - TitleNames.push_back(argv[3]); - - FileNames.push_back(argv[4]); - TitleNames.push_back(argv[5]); - //KS: If there is third file add it - if(argc == 8) - { - FileNames.push_back(argv[6]); - TitleNames.push_back(argv[7]); - } - - MultipleProcessMCMC(); - } - return 0; -} - -/// @brief Parse custom binning edges from a YAML configuration node. -/// -/// This function reads the `CustomBinEdges` section of the YAML config and -/// returns a mapping of parameter names to their lower and upper edges. -/// -/// The expected YAML syntax is: -/// @code{.yaml} -/// CustomBinEdges: -/// delta_cp: [-3.141592, 3.141592] -/// another_param: [min, max] -/// @endcode -/// -/// @param Settings YAML configuration node containing the optional -/// `CustomBinEdges` section. -std::map<std::string, std::pair<double, double>> GetCustomBinning(const YAML::Node& Settings) -{ - std::map<std::string, std::pair<double, double>> CustomBinning; - if (Settings["CustomBinEdges"]) { - const YAML::Node& edges = Settings["CustomBinEdges"]; - - for (const auto& node : edges) { - std::string key = node.first.as<std::string>(); - auto values = node.second.as<std::vector<double>>(); - - if (values.size() == 2) { - CustomBinning[key] = std::make_pair(values[0], values[1]); - MACH3LOG_DEBUG("Adding custom binning {} with {:.4f}, {:.4f}", key, values[0], values[1]); - } else { - MACH3LOG_ERROR("Invalid number of values for key: {}", key); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - } - } - return CustomBinning; -} - -void ProcessMCMC(const std::string& inputFile) -{ - MACH3LOG_INFO("File for study: {} with config {}", inputFile, config); - // Make the processor) - auto Processor = std::make_unique<OscProcessor>(inputFile); - - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - const bool PlotCorr = GetFromManager<bool>(Settings["PlotCorr"], false); - - Processor->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["ExcludedTypes"], {})); - Processor->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["ExcludedNames"], {})); - Processor->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["ExcludedGroups"], {})); - - //Apply additional cuts to 1D posterior - Processor->SetPosterior1DCut(GetFromManager<std::string>(Settings["Posterior1DCut"], "")); - - if(PlotCorr) Processor->SetOutputSuffix("_drawCorr"); - //KS:Turn off plotting detector and some other setting, should be via some config - Processor->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["PlotRelativeToPrior"], false)); - Processor->SetPrintToPDF(GetFromManager<bool>(Settings["PrintToPDF"], true)); - - //KS: Whether you want prior error bands for parameters with flat prior or not - Processor->SetPlotErrorForFlatPrior(GetFromManager<bool>(Settings["PlotErrorForFlatPrior"], true)); - Processor->SetFancyNames(GetFromManager<bool>(Settings["FancyNames"], true)); - Processor->SetPlotBinValue(GetFromManager<bool>(Settings["PlotBinValue"], false)); - //KS: Plot only 2D posteriors with correlations greater than 0.2 - Processor->SetPost2DPlotThreshold(GetFromManager<double>(Settings["Post2DPlotThreshold"], 0.2)); - - Processor->Initialise(); - - if(Settings["BurnInSteps"]) { - Processor->SetStepCut(Settings["BurnInSteps"].as<int>()); - } else { - MACH3LOG_WARN("BurnInSteps not set, defaulting to 20%"); - Processor->SetStepCut(static_cast<int>(Processor->GetnSteps()/5)); - } - if(Settings["MaxEntries"]) { - Processor->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); - } - if(Settings["NBins"]) { - Processor->SetNBins(Get<int>(Settings["NBins"], __FILE__, __LINE__)); - } - if(Settings["Thinning"]) - { - if(Settings["Thinning"][0].as<bool>()){ - Processor->ThinMCMC(Settings["Thinning"][1].as<int>()); - } - } - // Make the postfit - Processor->MakePostfit(GetCustomBinning(Settings)); - Processor->DrawPostfit(); - //KS: Should set via config whether you want below or not - if(GetFromManager<bool>(Settings["MakeCredibleIntervals"], true)) { - Processor->MakeCredibleIntervals(GetFromManager<std::vector<double>>(Settings["CredibleIntervals"], {0.99, 0.90, 0.68}), - GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), - GetFromManager<bool>(Settings["CredibleInSigmas"], false)); - } - if(GetFromManager<bool>(Settings["CalcBayesFactor"], true)) CalcBayesFactor(Processor.get()); - if(GetFromManager<bool>(Settings["CalcSavageDickey"], true)) CalcSavageDickey(Processor.get()); - if(GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) CalcBipolarPlot(Processor.get()); - if(GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) CalcParameterEvolution(Processor.get()); - - if(PlotCorr) - { - Processor->SetSmoothing(GetFromManager<bool>(Settings["Smoothing"], true)); - // Make the covariance matrix - //We have different treatment for multithread - Processor->CacheSteps(); - //KS: Since we cached let's make fancy violins :) - if(GetFromManager<bool>(Settings["MakeViolin"], true)) Processor->MakeViolin(); - Processor->MakeCovariance_MP(); - - Processor->DrawCovariance(); - if(GetFromManager<bool>(Settings["MakeCovarianceYAML"], true)) Processor->MakeCovarianceYAML(GetFromManager<std::string>(Settings["CovarianceYAMLOutName"], "UpdatedCorrelationMatrix.yaml"), GetFromManager<std::string>(Settings["CovarianceYAMLMeansMethod"], "HPD")); - - auto const &MakeSubOptimality = Settings["MakeSubOptimality"]; - if(MakeSubOptimality[0].as<bool>()) Processor->MakeSubOptimality(MakeSubOptimality[1].as<int>()); - - if(GetFromManager<bool>(Settings["MakeCredibleRegions"], false)) { - Processor->MakeCredibleRegions(GetFromManager<std::vector<double>>(Settings["CredibleRegions"], {0.99, 0.90, 0.68}), - GetFromManager<std::vector<short int>>(Settings["CredibleRegionStyle"], {2, 1, 3}), - GetFromManager<std::vector<short int>>(Settings["CredibleRegionColor"], {413, 406, 416}), - GetFromManager<bool>(Settings["CredibleInSigmas"], false), - GetFromManager<bool>(Settings["Draw2DPosterior"], true), - GetFromManager<bool>(Settings["DrawBestFit"], true)); - } - if(GetFromManager<bool>(Settings["GetTrianglePlot"], true)) GetTrianglePlot(Processor.get()); - - //KS: When creating covariance matrix longest time is spend on caching every step, since we already cached we can run some fancy covariance stability diagnostic - if(GetFromManager<bool>(Settings["DiagnoseCovarianceMatrix"], false)) DiagnoseCovarianceMatrix(Processor.get(), inputFile); - } - if(GetFromManager<bool>(Settings["JarlskogAnalysis"], true)) Processor->PerformJarlskogAnalysis(); - if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); -} - -void MultipleProcessMCMC() -{ - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - constexpr Color_t PosteriorColor[] = {kBlue-1, kRed, kGreen+2}; - //constexpr Style_t PosteriorStyle[] = {kSolid, kDashed, kDotted}; - nFiles = int(FileNames.size()); - std::vector<std::unique_ptr<MCMCProcessor>> Processor(nFiles); - - if(!Settings["BurnInSteps"]) { - MACH3LOG_WARN("BurnInSteps not set, defaulting to 20%"); - } - - for (int ik = 0; ik < nFiles; ik++) - { - MACH3LOG_INFO("File for study: {}", FileNames[ik]); - // Make the processor - Processor[ik] = std::make_unique<MCMCProcessor>(FileNames[ik]); - Processor[ik]->SetOutputSuffix(("_" + std::to_string(ik)).c_str()); - - Processor[ik]->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["ExcludedTypes"], {})); - Processor[ik]->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["ExcludedNames"], {})); - Processor[ik]->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["ExcludedGroups"], {})); - - //Apply additional cuts to 1D posterior - Processor[ik]->SetPosterior1DCut(GetFromManager<std::string>(Settings["Posterior1DCut"], "")); - - Processor[ik]->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["PlotRelativeToPrior"], false)); - Processor[ik]->SetFancyNames(GetFromManager<bool>(Settings["FancyNames"], true)); - Processor[ik]->Initialise(); - - if(Settings["BurnInSteps"]) { - Processor[ik]->SetStepCut(Settings["BurnInSteps"].as<int>()); - }else { - Processor[ik]->SetStepCut(static_cast<int>(Processor[ik]->GetnSteps()/5)); - } - - if(Settings["MaxEntries"]) { - Processor[ik]->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); - } - if(Settings["NBins"]) { - Processor[ik]->SetNBins(Get<int>(Settings["NBins"], __FILE__, __LINE__)); - } - } - - Processor[0]->MakePostfit(GetCustomBinning(Settings)); - Processor[0]->DrawPostfit(); - // Get edges from first histogram to ensure all params use same binning - std::map<std::string, std::pair<double, double>> ParamEdges; - for(int i = 0; i < Processor[0]->GetNParams(); ++i) { - // Get the histogram for the i-th parameter - TH1D* hist = Processor[0]->GetHpost(i); - if (!hist) { - MACH3LOG_DEBUG("Histogram for parameter {} is null.", i); - continue; - } - - // Get the parameter name (title of the histogram) - std::string paramName = hist->GetTitle(); - - // Get the axis limits (edges) - TAxis* axis = hist->GetXaxis(); - double xmin = axis->GetXmin(); - double xmax = axis->GetXmax(); - - MACH3LOG_DEBUG("Adding bin edges for {} equal to {:.4f}, {:.4f}",paramName, xmin, xmax); - // Insert into the map - ParamEdges[paramName] = std::make_pair(xmin, xmax); - } - - //KS: Multithreading here is very tempting but there are some issues with root that need to be resovled :( - for (int ik = 1; ik < nFiles; ik++) - { - // Make the postfit - Processor[ik]->MakePostfit(ParamEdges); - Processor[ik]->DrawPostfit(); - } - - // Open a TCanvas to write the posterior onto - auto Posterior = std::make_unique<TCanvas>("PosteriorMulti", "PosteriorMulti", 0, 0, 1024, 1024); - gStyle->SetOptStat(0); - gStyle->SetOptTitle(0); - Posterior->SetGrid(); - Posterior->SetBottomMargin(0.1f); - Posterior->SetTopMargin(0.05f); - Posterior->SetRightMargin(0.03f); - Posterior->SetLeftMargin(0.15f); - - // First filename: keep path, just remove ".root" - // Would be nice to specify outpath in a later update - size_t pos = FileNames[0].rfind(".root"); - std::string base = (pos == std::string::npos) ? FileNames[0] : FileNames[0].substr(0, pos); - TString canvasname = base; - - // Remaining filenames: strip path and ".root" - // So if you have /path/to/file1.root and /path/to/file2.root or /another/path/to/file2.root, canvasname = /path/to/file1_file2.root - for (int ik = 1; ik < nFiles; ik++) { - pos = FileNames[ik].find_last_of('/'); - base = (pos == std::string::npos) ? FileNames[ik] : FileNames[ik].substr(pos + 1); - pos = base.rfind(".root"); - if (pos != std::string::npos) base = base.substr(0, pos); - canvasname += "_" + TString(base); - } - - canvasname = canvasname +".pdf["; - - Posterior->Print(canvasname); - // Once the pdf file is open no longer need to bracket - canvasname.ReplaceAll("[",""); - - for(int i = 0; i < Processor[0]->GetNParams(); ++i) - { - // This holds the posterior density - std::vector<std::unique_ptr<TH1D>> hpost(nFiles); - std::vector<std::unique_ptr<TLine>> hpd(nFiles); - hpost[0] = M3::Clone(Processor[0]->GetHpost(i)); - hpost[0]->GetYaxis()->SetTitle("Posterior Density"); - bool Skip = false; - for (int ik = 1 ; ik < nFiles; ik++) - { - // KS: If somehow this chain doesn't given params we skip it - const int Index = Processor[ik]->GetParamIndexFromName(hpost[0]->GetTitle()); - if(Index == M3::_BAD_INT_) - { - Skip = true; - break; - } - hpost[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); - } - - // Don't plot if this is a fixed histogram (i.e. the peak is the whole integral) - if(hpost[0]->GetMaximum() == hpost[0]->Integral()*1.5 || Skip) { - continue; - } - for (int ik = 0; ik < nFiles; ik++) - { - RemoveFitter(hpost[ik].get(), "Gauss"); - - // Set some nice colours - hpost[ik]->SetLineColor(PosteriorColor[ik]); - //hpost[ik]->SetLineStyle(PosteriorStyle[ik]); - hpost[ik]->SetLineWidth(2); - - // Area normalise the distributions - hpost[ik]->Scale(1./hpost[ik]->Integral()); - } - TString Title; - double Prior = 1.0; - double PriorError = 1.0; - - Processor[0]->GetNthParameter(i, Prior, PriorError, Title); - - // Now make the TLine for the Asimov - auto Asimov = std::make_unique<TLine>(Prior, hpost[0]->GetMinimum(), Prior, hpost[0]->GetMaximum()); - Asimov->SetLineColor(kRed-3); - Asimov->SetLineWidth(2); - Asimov->SetLineStyle(kDashed); - - // Make a nice little TLegend - auto leg = std::make_unique<TLegend>(0.20, 0.7, 0.6, 0.97); - leg->SetTextSize(0.03f); - leg->SetFillColor(0); - leg->SetFillStyle(0); - leg->SetLineColor(0); - leg->SetLineStyle(0); - TString asimovLeg = Form("#splitline{Prior}{x = %.2f , #sigma = %.2f}", Prior, PriorError); - leg->AddEntry(Asimov.get(), asimovLeg, "l"); - - for (int ik = 0; ik < nFiles; ik++) - { - TString rebinLeg = Form("#splitline{%s}{#mu = %.2f, #sigma = %.2f}", TitleNames[ik].c_str(), hpost[ik]->GetMean(), hpost[ik]->GetRMS()); - leg->AddEntry(hpost[ik].get(), rebinLeg, "l"); - - hpd[ik] = std::make_unique<TLine>(hpost[ik]->GetBinCenter(hpost[ik]->GetMaximumBin()), hpost[ik]->GetMinimum(), - hpost[ik]->GetBinCenter(hpost[ik]->GetMaximumBin()), hpost[ik]->GetMaximum()); - hpd[ik]->SetLineColor(hpost[ik]->GetLineColor()); - hpd[ik]->SetLineWidth(2); - hpd[ik]->SetLineStyle(kSolid); - } - - // Find the maximum value to nicely resize hist - double maximum = 0; - for (int ik = 0; ik < nFiles; ik++) maximum = std::max(maximum, hpost[ik]->GetMaximum()); - for (int ik = 0; ik < nFiles; ik++) hpost[ik]->SetMaximum(1.3*maximum); - - hpost[0]->Draw("hist"); - for (int ik = 1; ik < nFiles; ik++) hpost[ik]->Draw("hist same"); - Asimov->Draw("same"); - for (int ik = 0; ik < nFiles; ik++) hpd[ik]->Draw("same"); - leg->Draw("same"); - Posterior->cd(); - Posterior->Print(canvasname); - }//End loop over parameters - - // Finally draw the parameter plot onto the PDF - // Close the .pdf file with all the posteriors - Posterior->cd(); - Posterior->Clear(); - - if(GetFromManager<bool>(Settings["PerformKStest"], true)) KolmogorovSmirnovTest(Processor, Posterior, canvasname); - - // Close the pdf file - MACH3LOG_INFO("Closing pdf {}", canvasname); - canvasname+="]"; - Posterior->Print(canvasname); -} - -/// @brief KS: Calculate Bayes factor for a given hypothesis, most informative are those related to osc params. However, it make relative easy interpretation for switch dials -void CalcBayesFactor(MCMCProcessor* Processor) -{ - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - std::vector<std::string> ParNames; - std::vector<std::vector<double>> Model1Bounds; - std::vector<std::vector<double>> Model2Bounds; - std::vector<std::vector<std::string>> ModelNames; - for (const auto& dg : Settings["BayesFactor"]) - { - ParNames.push_back(dg[0].as<std::string>()); - ModelNames.push_back(dg[1].as<std::vector<std::string>>()); - Model1Bounds.push_back(dg[2].as<std::vector<double>>()); - Model2Bounds.push_back(dg[3].as<std::vector<double>>()); - } - - Processor->GetBayesFactor(ParNames, Model1Bounds, Model2Bounds, ModelNames); -} - -void CalcSavageDickey(MCMCProcessor* Processor) -{ - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - std::vector<std::string> ParNames; - std::vector<double> EvaluationPoint; - std::vector<std::vector<double>> Bounds; - - for (const auto& d : Settings["SavageDickey"]) - { - ParNames.push_back(d[0].as<std::string>()); - EvaluationPoint.push_back(d[1].as<double>()); - Bounds.push_back(d[2].as<std::vector<double>>()); - } - Processor->GetSavageDickey(ParNames, EvaluationPoint, Bounds); -} - -void CalcParameterEvolution(MCMCProcessor* Processor) -{ - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - std::vector<std::string> ParNames; - std::vector<int> Intervals; - for (const auto& d : Settings["ParameterEvolution"]) - { - ParNames.push_back(d[0].as<std::string>()); - Intervals.push_back(d[1].as<int>()); - } - Processor->ParameterEvolution(ParNames, Intervals); -} - -void CalcBipolarPlot(MCMCProcessor* Processor) -{ - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - std::vector<std::string> ParNames; - for (const auto& d : Settings["BipolarPlot"]) - { - ParNames.push_back(d[0].as<std::string>()); - } - Processor->GetPolarPlot(ParNames); -} - -void GetTrianglePlot(MCMCProcessor* Processor) { - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - for (const auto& dg : Settings["TrianglePlot"]) - { - std::string ParName = dg[0].as<std::string>(); - - std::vector<std::string> NameVec = dg[1].as<std::vector<std::string>>(); - Processor->MakeTrianglePlot(NameVec, - GetFromManager<std::vector<double>>(Settings["CredibleIntervals"], {0.99, 0.90, 0.68}), - GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), - GetFromManager<std::vector<double>>(Settings["CredibleRegions"], {0.99, 0.90, 0.68}), - GetFromManager<std::vector<short int>>(Settings["CredibleRegionStyle"], {2, 1, 3}), - GetFromManager<std::vector<short int>>(Settings["CredibleRegionColor"], {413, 406, 416}), - GetFromManager<bool>(Settings["CredibleInSigmas"], false)); - } -} - -/// @brief KS: You validate stability of posterior covariance matrix, you set burn calc cov matrix increase burn calc again and compare. By performing such operation several hundred times we can check when matrix becomes stable -void DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) -{ - //Turn of plots from Processor - Processor->SetPrintToPDF(false); - // Open a TCanvas to write the posterior onto - auto Canvas = std::make_unique<TCanvas>("Canvas", "Canvas", 0, 0, 1024, 1024); - Canvas->SetGrid(); - gStyle->SetOptStat(0); - gStyle->SetOptTitle(0); - Canvas->SetTickx(); - Canvas->SetTicky(); - Canvas->SetBottomMargin(0.1f); - Canvas->SetTopMargin(0.05f); - Canvas->SetRightMargin(0.15f); - Canvas->SetLeftMargin(0.10f); - - //KS: Fancy colours - const int NRGBs = 10; - TColor::InitializeColors(); - Double_t stops[NRGBs] = { 0.00, 0.10, 0.25, 0.35, 0.50, 0.60, 0.65, 0.75, 0.90, 1.00 }; - Double_t red[NRGBs] = { 0.50, 1.00, 1.00, 0.25, 0.00, 0.10, 0.50, 1.00, 0.75, 0.55 }; - Double_t green[NRGBs] = { 0.00, 0.25, 1.00, 0.25, 0.00, 0.60, 0.90, 1.00, 0.75, 0.75 }; - Double_t blue[NRGBs] = { 0.00, 0.25, 1.00, 1.00, 0.50, 0.60, 0.90, 1.00, 0.05, 0.05 }; - TColor::CreateGradientColorTable(NRGBs, stops, red, green, blue, 255); - gStyle->SetNumberContours(255); - - std::string OutName = inputFile; - OutName = OutName.substr(0, OutName.find(".root")); - Canvas->Print(Form("Correlation_%s.pdf[", OutName.c_str()), "pdf"); - Canvas->Print(Form("Covariance_%s.pdf[", OutName.c_str()), "pdf"); - - YAML::Node card_yaml = M3OpenConfig(config.c_str()); - YAML::Node Settings = card_yaml["ProcessMCMC"]; - - const int entries = int(Processor->GetnSteps()); - const int NIntervals = GetFromManager<int>(Settings["NIntervals"], 5); - const int IntervalsSize = entries/NIntervals; - //We start with burn from 0 (no burn in at all) - int BurnIn = 0; - MACH3LOG_INFO("Diagnosing matrices with entries={}, NIntervals={} and IntervalsSize={}", entries, NIntervals, IntervalsSize); - - TMatrixDSym *Covariance = nullptr; - TMatrixDSym *Correlation = nullptr; - - TH2D *CovariancePreviousHist = nullptr; - TH2D *CorrelationPreviousHist = nullptr; - - TH2D *CovarianceHist = nullptr; - TH2D *CorrelationHist = nullptr; - - //KS: Get first covariances, we need two for comparison... - Processor->SetStepCut(BurnIn); - Processor->GetCovariance(Covariance, Correlation); - - CovariancePreviousHist = TMatrixIntoTH2D(Covariance, "Covariance"); - CorrelationPreviousHist = TMatrixIntoTH2D(Correlation, "Correlation"); - - delete Covariance; - Covariance = nullptr; - delete Correlation; - Correlation = nullptr; - - //KS: Loop over all desired cuts - for(int k = 1; k < NIntervals; ++k) - { - BurnIn = k*IntervalsSize; - Processor->SetStepCut(BurnIn); - Processor->GetCovariance(Covariance, Correlation); - Processor->Reset2DPosteriors(); - - CovarianceHist = TMatrixIntoTH2D(Covariance, "Covariance"); - CorrelationHist = TMatrixIntoTH2D(Correlation, "Correlation"); - - TH2D *CovarianceDiff = static_cast<TH2D*>(CovarianceHist->Clone("Covariance_Ratio")); - TH2D *CorrelationDiff = static_cast<TH2D*>(CorrelationHist->Clone("Correlation_Ratio")); - - //KS: Bit messy but quite often covariance is 0 is divided by 0 is problematic so - #ifdef MULTITHREAD - #pragma omp parallel for - #endif - for (int j = 1; j < CovarianceDiff->GetXaxis()->GetNbins()+1; ++j) - { - for (int i = 1; i < CovarianceDiff->GetYaxis()->GetNbins()+1; ++i) - { - if( std::fabs (CovarianceDiff->GetBinContent(j, i)) < 1.e-5 && std::fabs (CovariancePreviousHist->GetBinContent(j, i)) < 1.e-5) - { - CovarianceDiff->SetBinContent(j, i, M3::_BAD_DOUBLE_); - CovariancePreviousHist->SetBinContent(j, i, M3::_BAD_DOUBLE_); - } - if( std::fabs (CorrelationDiff->GetBinContent(j, i)) < 1.e-5 && std::fabs (CorrelationPreviousHist->GetBinContent(j, i)) < 1.e-5) - { - CorrelationDiff->SetBinContent(j, i, M3::_BAD_DOUBLE_); - CorrelationPreviousHist->SetBinContent(j, i, M3::_BAD_DOUBLE_); - } - } - } - //Divide matrices - CovarianceDiff->Divide(CovariancePreviousHist); - CorrelationDiff->Divide(CorrelationPreviousHist); - - //Now it is time for fancy names etc. - for (int j = 0; j < CovarianceDiff->GetXaxis()->GetNbins(); ++j) - { - TString Title = ""; - double Prior = 1.0; - double PriorError = 1.0; - - Processor->GetNthParameter(j, Prior, PriorError, Title); - - CovarianceDiff->GetXaxis()->SetBinLabel(j+1, Title); - CovarianceDiff->GetYaxis()->SetBinLabel(j+1, Title); - CorrelationDiff->GetXaxis()->SetBinLabel(j+1, Title); - CorrelationDiff->GetYaxis()->SetBinLabel(j+1, Title); - } - CovarianceDiff->GetXaxis()->SetLabelSize(0.015f); - CovarianceDiff->GetYaxis()->SetLabelSize(0.015f); - CorrelationDiff->GetXaxis()->SetLabelSize(0.015f); - CorrelationDiff->GetYaxis()->SetLabelSize(0.015f); - - std::stringstream ss; - ss << "BCut_"; - ss << BurnIn; - ss << "/"; - ss << "BCut_"; - ss << (k-1)*IntervalsSize; - std::string str = ss.str(); - - TString Title = "Cov " + str; - CovarianceDiff->GetZaxis()->SetTitle( Title ); - Title = "Corr " + str; - CorrelationDiff->GetZaxis()->SetTitle(Title); - - CovarianceDiff->SetMinimum(-2); - CovarianceDiff->SetMaximum(2); - CorrelationDiff->SetMinimum(-2); - CorrelationDiff->SetMaximum(2); - - Canvas->cd(); - CovarianceDiff->Draw("colz"); - Canvas->Print(Form("Covariance_%s.pdf", OutName.c_str()), "pdf"); - - CorrelationDiff->Draw("colz"); - Canvas->Print(Form("Correlation_%s.pdf", OutName.c_str()), "pdf"); - - //KS: Current hist become previous as we need it for further comparison - delete CovariancePreviousHist; - CovariancePreviousHist = static_cast<TH2D*>(CovarianceHist->Clone()); - delete CorrelationPreviousHist; - CorrelationPreviousHist = static_cast<TH2D*>(CorrelationHist->Clone()); - - delete CovarianceHist; - CovarianceHist = nullptr; - delete CorrelationHist; - CorrelationHist = nullptr; - - delete CovarianceDiff; - delete CorrelationDiff; - delete Covariance; - Covariance = nullptr; - delete Correlation; - Correlation = nullptr; - } - Canvas->cd(); - Canvas->Print(Form("Covariance_%s.pdf]", OutName.c_str()), "pdf"); - Canvas->Print(Form("Correlation_%s.pdf]", OutName.c_str()), "pdf"); - - Processor->SetPrintToPDF(true); - if(Covariance != nullptr) delete Covariance; - if(Correlation != nullptr) delete Correlation; - if(CovariancePreviousHist != nullptr) delete CovariancePreviousHist; - if(CorrelationPreviousHist != nullptr) delete CorrelationPreviousHist; - if(CovarianceHist != nullptr) delete CovarianceHist; - if(CorrelationHist != nullptr) delete CorrelationHist; -} - -//KS: Convert TMatrix to TH2D, mostly useful for making fancy plots -TH2D* TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) -{ - TH2D* hMatrix = new TH2D(title.c_str(), title.c_str(), Matrix->GetNrows(), 0.0, Matrix->GetNrows(), Matrix->GetNcols(), 0.0, Matrix->GetNcols()); - for(int i = 0; i < Matrix->GetNrows(); i++) - { - for(int j = 0; j < Matrix->GetNcols(); j++) - { - //KS: +1 because there is offset in histogram relative to TMatrix - hMatrix->SetBinContent(i+1,j+1, (*Matrix)(i,j)); - } - } - return hMatrix; -} - -// KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution -void KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, - const std::unique_ptr<TCanvas>& Posterior, - const TString& canvasname) -{ - constexpr Color_t CumulativeColor[] = {kBlue-1, kRed, kGreen+2}; - constexpr Style_t CumulativeStyle[] = {kSolid, kDashed, kDotted}; - - for(int i = 0; i < Processor[0]->GetNParams(); ++i) - { - // This holds the posterior density - std::vector<std::unique_ptr<TH1D>> hpost(nFiles); - std::vector<std::unique_ptr<TH1D>> CumulativeDistribution(nFiles); - - TString Title; - double Prior = 1.0; - double PriorError = 1.0; - - Processor[0]->GetNthParameter(i, Prior, PriorError, Title); - bool Skip = false; - for (int ik = 0 ; ik < nFiles; ik++) - { - int Index = 0; - if(ik == 0 ) Index = i; - else - { - // KS: If somehow this chain doesn't given params we skip it - Index = Processor[ik]->GetParamIndexFromName(hpost[0]->GetTitle()); - if(Index == M3::_BAD_INT_) - { - Skip = true; - break; - } - } - hpost[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); - CumulativeDistribution[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); - CumulativeDistribution[ik]->Fill(0., 0.); - CumulativeDistribution[ik]->Reset(); - CumulativeDistribution[ik]->SetMaximum(1.); - TString TempTitle = Title+" Kolmogorov Smirnov"; - CumulativeDistribution[ik]->SetTitle(TempTitle); - - TempTitle = Title+" Value"; - CumulativeDistribution[ik]->GetXaxis()->SetTitle(TempTitle); - CumulativeDistribution[ik]->GetYaxis()->SetTitle("Cumulative Probability"); - - CumulativeDistribution[ik]->SetLineWidth(2); - CumulativeDistribution[ik]->SetLineColor(CumulativeColor[ik]); - CumulativeDistribution[ik]->SetLineStyle(CumulativeStyle[ik]); - } - - // Don't plot if this is a fixed histogram (i.e. the peak is the whole integral) - if(hpost[0]->GetMaximum() == hpost[0]->Integral()*1.5 || Skip) { - continue; - } - - for (int ik = 0 ; ik < nFiles; ik++) - { - const int NumberOfBins = hpost[ik]->GetXaxis()->GetNbins(); - double Cumulative = 0; - const double Integral = hpost[ik]->Integral(); - for (int j = 1; j < NumberOfBins+1; ++j) - { - Cumulative += hpost[ik]->GetBinContent(j)/Integral; - CumulativeDistribution[ik]->SetBinContent(j, Cumulative); - } - //KS: Set overflow to 1 just in case - CumulativeDistribution[ik]->SetBinContent(NumberOfBins+1, 1.); - } - - std::vector<int> TestStatBin(nFiles, 0); - std::vector<double> TestStatD(nFiles, -999); - std::vector<std::unique_ptr<TLine>> LineD(nFiles); - //Find KS statistic - for (int ik = 1 ; ik < nFiles; ik++) - { - const int NumberOfBins = CumulativeDistribution[0]->GetXaxis()->GetNbins(); - for (int j = 1; j < NumberOfBins+1; ++j) - { - const double BinValue = CumulativeDistribution[0]->GetBinCenter(j); - const int BinNumber = CumulativeDistribution[ik]->FindBin(BinValue); - //KS: Calculate D statistic for this bin, only save it if it's bigger than previously found value - double TempDstat = std::fabs(CumulativeDistribution[0]->GetBinContent(j) - CumulativeDistribution[ik]->GetBinContent(BinNumber)); - if(TempDstat > TestStatD[ik]) - { - TestStatD[ik] = TempDstat; - TestStatBin[ik] = j; - } - } - } - - for (int ik = 0 ; ik < nFiles; ik++) - { - LineD[ik] = std::make_unique<TLine>(CumulativeDistribution[0]->GetBinCenter(TestStatBin[ik]), 0, CumulativeDistribution[0]->GetBinCenter(TestStatBin[ik]), CumulativeDistribution[0]->GetBinContent(TestStatBin[ik])); - LineD[ik]->SetLineColor(CumulativeColor[ik]); - LineD[ik]->SetLineWidth(2.0); - } - CumulativeDistribution[0]->Draw(); - for (int ik = 0 ; ik < nFiles; ik++) - CumulativeDistribution[ik]->Draw("SAME"); - - auto leg = std::make_unique<TLegend>(0.15, 0.7, 0.5, 0.90); - leg->SetTextSize(0.04f); - for (int ik = 0; ik < nFiles; ik++) - leg->AddEntry(CumulativeDistribution[ik].get(), TitleNames[ik].c_str(), "l"); - for (int ik = 1; ik < nFiles; ik++) - leg->AddEntry(LineD[ik].get(), Form("#Delta D = %.4f", TestStatD[ik]), "l"); - - leg->SetLineColor(0); - leg->SetLineStyle(0); - leg->SetFillColor(0); - leg->SetFillStyle(0); - leg->Draw("SAME"); - - for (int ik = 1; ik < nFiles; ik++) - LineD[ik]->Draw("sam"); - - Posterior->cd(); - Posterior->Print(canvasname); - } //End loop over parameter +#include "Diagnostics/ProcessMCMCPlugin.hpp" + +int main(int argc, char const* argv[]){ + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + argv[0] = "process"; + mach3::ProcessMCMCPlugin proc; + ArgumentParser* parser = proc.get_parser(); + parser->parse_args(argc, argv); + proc.run(); + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + return 0; } diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp new file mode 100644 index 000000000..3aef17ea9 --- /dev/null +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -0,0 +1,850 @@ +//MaCh3 includes +#include "api/plugin.hpp" +#include "Fitters/OscProcessor.h" +#include "Manager/Manager.h" +#include "Diagnostics/ProcessMCMCPlugin.hpp" + + +extern "C" mach3::IPlugin* create_plugin() { + return new mach3::ProcessMCMCPlugin(); +} + +extern "C" void destroy_plugin(mach3::IPlugin* p) { + delete p; +} + +namespace mach3{ + + MaCh3ArgumentParser* ProcessMCMCPlugin::get_parser(){ + m_parser = new MaCh3ArgumentParser("process", "1.0", argparse::default_arguments::help); + m_parser->add_description("Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc."); + m_parser->add_epilog("""\ + ProcessMCMC The main application for analysing the ND280 chain.\n\ + It prints posterior distributions after the burn-in cut and allows comparison of two or three different chains.\n\ + Several options can be configured directly in the app, such as selection, burn-in cut, and whether to plot xsec+flux or only flux.\n\ + \n\ + Additional functionality includes:\n\ + Produce a covariance matrix with multithreading (RAM intensive due to caching)\n\ + Violin plots\n\ + Credible intervals and regions\n\ + Calculation of Bayes factors with significance based on the Jeffreys scale\n\ + Triangle plots\n\ + Study of covariance matrix stability\n"""); + m_parser->add_argument("--corr") + .help("plot correlation - Same as PlotCorr option in config.") + .flag(); + m_parser->add_argument("--MakeCredibleIntervals") + .help("Same as MakeCredibleIntervals option in config.") + .flag(); + m_parser->add_argument("--CalcBayesFactor") + .help("Same as CalcBayesFactor option in config.") + .flag(); + m_parser->add_argument("--CalcSavageDickey") + .help("Same as CalcSavageDickey option in config.") + .flag(); + m_parser->add_argument("--CalcBipolarPlot") + .help("Same as CalcBipolarPlot option in config.") + .flag(); + m_parser->add_argument("--CalcParameterEvolution") + .help("Same as CalcParameterEvolution option in config.") + .flag(); + m_parser->add_argument("config") + .help("Config file.") + .metavar("CONFIG") + .required(); + m_parser->add_argument("mcmc-chain") + .help("MCMC chain root file.") + .metavar("MCMC_CHAIN") + .nargs(1, 3) + .required(); + return m_parser; + } + + + int ProcessMCMCPlugin::run() { + SetMaCh3LoggerFormat(); + nFiles = 0; + config = m_parser->get<std::string>("config"); + auto files = m_parser->get<std::vector<std::string>>("mcmc-chain"); + // if (argc != 3 && argc !=6 && argc != 8) + // { + // MACH3LOG_ERROR("How to use: "); + // MACH3LOG_ERROR(" single chain: {} <Config> <MCMM_ND_Output.root>", m_parser->name()); + // MACH3LOG_ERROR(" two chain: {} <Config> <MCMM_ND_Output_1.root> <Title 1> <MCMC_ND_Output_2.root> <Title 2>", m_parser->name()); + // MACH3LOG_ERROR(" three chain: {} <Config> <MCMM_ND_Output_1.root> <Title 1> <MCMC_ND_Output_2.root> <Title 2> <MCMC_ND_Output_3.root> <Title 3>", m_parser->name()); + // throw MaCh3Exception(__FILE__ , __LINE__ ); + // } + + YAML::Node card_yaml = M3OpenConfig(config); + if (!CheckNodeExists(card_yaml, "ProcessMCMC")) { + MACH3LOG_ERROR("The 'ProcessMCMC' node is not defined in the YAML configuration."); + throw MaCh3Exception(__FILE__ , __LINE__ ); + } + + if (files.size() == 1) + { + MACH3LOG_INFO("Producing single fit output"); + std::string filename = files[0]; + ProcessMCMC(filename); + } + // If we want to compare two or more fits (e.g. binning changes or introducing new params/priors) + else if (files.size() > 1) + { + MACH3LOG_INFO("Producing two fit comparison"); + FileNames.push_back(files[0]); + TitleNames.push_back("ONE"); // todo fix + + FileNames.push_back(files[1]); + TitleNames.push_back("TWO"); + //KS: If there is third file add it + if(files.size() == 3) + { + FileNames.push_back(files[2]); + TitleNames.push_back("THREE"); + } + + MultipleProcessMCMC(); + } + return 0; + } + + /// @brief Parse custom binning edges from a YAML configuration node. + /// + /// This function reads the `CustomBinEdges` section of the YAML config and + /// returns a mapping of parameter names to their lower and upper edges. + /// + /// The expected YAML syntax is: + /// @code{.yaml} + /// CustomBinEdges: + /// delta_cp: [-3.141592, 3.141592] + /// another_param: [min, max] + /// @endcode + /// + /// @param Settings YAML configuration node containing the optional + /// `CustomBinEdges` section. + std::map<std::string, std::pair<double, double>> ProcessMCMCPlugin::GetCustomBinning(const YAML::Node& Settings) + { + std::map<std::string, std::pair<double, double>> CustomBinning; + if (Settings["CustomBinEdges"]) { + const YAML::Node& edges = Settings["CustomBinEdges"]; + + for (const auto& node : edges) { + std::string key = node.first.as<std::string>(); + auto values = node.second.as<std::vector<double>>(); + + if (values.size() == 2) { + CustomBinning[key] = std::make_pair(values[0], values[1]); + MACH3LOG_DEBUG("Adding custom binning {} with {:.4f}, {:.4f}", key, values[0], values[1]); + } else { + MACH3LOG_ERROR("Invalid number of values for key: {}", key); + throw MaCh3Exception(__FILE__ , __LINE__ ); + } + } + } + return CustomBinning; + } + + void ProcessMCMCPlugin::ProcessMCMC(const std::string& inputFile) + { + MACH3LOG_INFO("File for study: {} with config {}", inputFile, config); + // Make the processor) + auto Processor = std::make_unique<OscProcessor>(inputFile); + + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + const bool PlotCorr = GetFromManager<bool>(Settings["PlotCorr"], false); + + Processor->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["ExcludedTypes"], {})); + Processor->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["ExcludedNames"], {})); + Processor->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["ExcludedGroups"], {})); + + //Apply additional cuts to 1D posterior + Processor->SetPosterior1DCut(GetFromManager<std::string>(Settings["Posterior1DCut"], "")); + + if(PlotCorr) Processor->SetOutputSuffix("_drawCorr"); + //KS:Turn off plotting detector and some other setting, should be via some config + Processor->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["PlotRelativeToPrior"], false)); + Processor->SetPrintToPDF(GetFromManager<bool>(Settings["PrintToPDF"], true)); + + //KS: Whether you want prior error bands for parameters with flat prior or not + Processor->SetPlotErrorForFlatPrior(GetFromManager<bool>(Settings["PlotErrorForFlatPrior"], true)); + Processor->SetFancyNames(GetFromManager<bool>(Settings["FancyNames"], true)); + Processor->SetPlotBinValue(GetFromManager<bool>(Settings["PlotBinValue"], false)); + //KS: Plot only 2D posteriors with correlations greater than 0.2 + Processor->SetPost2DPlotThreshold(GetFromManager<double>(Settings["Post2DPlotThreshold"], 0.2)); + + Processor->Initialise(); + + if(Settings["BurnInSteps"]) { + Processor->SetStepCut(Settings["BurnInSteps"].as<int>()); + } else { + MACH3LOG_WARN("BurnInSteps not set, defaulting to 20%"); + Processor->SetStepCut(static_cast<int>(Processor->GetnSteps()/5)); + } + if(Settings["MaxEntries"]) { + Processor->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); + } + if(Settings["NBins"]) { + Processor->SetNBins(Get<int>(Settings["NBins"], __FILE__, __LINE__)); + } + if(Settings["Thinning"]) + { + if(Settings["Thinning"][0].as<bool>()){ + Processor->ThinMCMC(Settings["Thinning"][1].as<int>()); + } + } + // Make the postfit + Processor->MakePostfit(GetCustomBinning(Settings)); + Processor->DrawPostfit(); + //KS: Should set via config whether you want below or not + if(GetFromManager<bool>(Settings["MakeCredibleIntervals"], true)) { + Processor->MakeCredibleIntervals(GetFromManager<std::vector<double>>(Settings["CredibleIntervals"], {0.99, 0.90, 0.68}), + GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), + GetFromManager<bool>(Settings["CredibleInSigmas"], false)); + } + if(GetFromManager<bool>(Settings["CalcBayesFactor"], true)) CalcBayesFactor(Processor.get()); + if(GetFromManager<bool>(Settings["CalcSavageDickey"], true)) CalcSavageDickey(Processor.get()); + if(GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) CalcBipolarPlot(Processor.get()); + if(GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) CalcParameterEvolution(Processor.get()); + + if(PlotCorr) + { + Processor->SetSmoothing(GetFromManager<bool>(Settings["Smoothing"], true)); + // Make the covariance matrix + //We have different treatment for multithread + Processor->CacheSteps(); + //KS: Since we cached let's make fancy violins :) + if(GetFromManager<bool>(Settings["MakeViolin"], true)) Processor->MakeViolin(); + Processor->MakeCovariance_MP(); + + Processor->DrawCovariance(); + if(GetFromManager<bool>(Settings["MakeCovarianceYAML"], true)) Processor->MakeCovarianceYAML(GetFromManager<std::string>(Settings["CovarianceYAMLOutName"], "UpdatedCorrelationMatrix.yaml"), GetFromManager<std::string>(Settings["CovarianceYAMLMeansMethod"], "HPD")); + + auto const &MakeSubOptimality = Settings["MakeSubOptimality"]; + if(MakeSubOptimality[0].as<bool>()) Processor->MakeSubOptimality(MakeSubOptimality[1].as<int>()); + + if(GetFromManager<bool>(Settings["MakeCredibleRegions"], false)) { + Processor->MakeCredibleRegions(GetFromManager<std::vector<double>>(Settings["CredibleRegions"], {0.99, 0.90, 0.68}), + GetFromManager<std::vector<short int>>(Settings["CredibleRegionStyle"], {2, 1, 3}), + GetFromManager<std::vector<short int>>(Settings["CredibleRegionColor"], {413, 406, 416}), + GetFromManager<bool>(Settings["CredibleInSigmas"], false), + GetFromManager<bool>(Settings["Draw2DPosterior"], true), + GetFromManager<bool>(Settings["DrawBestFit"], true)); + } + if(GetFromManager<bool>(Settings["GetTrianglePlot"], true)) GetTrianglePlot(Processor.get()); + + //KS: When creating covariance matrix longest time is spend on caching every step, since we already cached we can run some fancy covariance stability diagnostic + if(GetFromManager<bool>(Settings["DiagnoseCovarianceMatrix"], false)) DiagnoseCovarianceMatrix(Processor.get(), inputFile); + } + if(GetFromManager<bool>(Settings["JarlskogAnalysis"], true)) Processor->PerformJarlskogAnalysis(); + if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); + } + + void ProcessMCMCPlugin::MultipleProcessMCMC() + { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + constexpr Color_t PosteriorColor[] = {kBlue-1, kRed, kGreen+2}; + //constexpr Style_t PosteriorStyle[] = {kSolid, kDashed, kDotted}; + nFiles = int(FileNames.size()); + std::vector<std::unique_ptr<MCMCProcessor>> Processor(nFiles); + + if(!Settings["BurnInSteps"]) { + MACH3LOG_WARN("BurnInSteps not set, defaulting to 20%"); + } + + for (int ik = 0; ik < nFiles; ik++) + { + MACH3LOG_INFO("File for study: {}", FileNames[ik]); + // Make the processor + Processor[ik] = std::make_unique<MCMCProcessor>(FileNames[ik]); + Processor[ik]->SetOutputSuffix(("_" + std::to_string(ik)).c_str()); + + Processor[ik]->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["ExcludedTypes"], {})); + Processor[ik]->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["ExcludedNames"], {})); + Processor[ik]->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["ExcludedGroups"], {})); + + //Apply additional cuts to 1D posterior + Processor[ik]->SetPosterior1DCut(GetFromManager<std::string>(Settings["Posterior1DCut"], "")); + + Processor[ik]->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["PlotRelativeToPrior"], false)); + Processor[ik]->SetFancyNames(GetFromManager<bool>(Settings["FancyNames"], true)); + Processor[ik]->Initialise(); + + if(Settings["BurnInSteps"]) { + Processor[ik]->SetStepCut(Settings["BurnInSteps"].as<int>()); + }else { + Processor[ik]->SetStepCut(static_cast<int>(Processor[ik]->GetnSteps()/5)); + } + + if(Settings["MaxEntries"]) { + Processor[ik]->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); + } + if(Settings["NBins"]) { + Processor[ik]->SetNBins(Get<int>(Settings["NBins"], __FILE__, __LINE__)); + } + } + + Processor[0]->MakePostfit(GetCustomBinning(Settings)); + Processor[0]->DrawPostfit(); + // Get edges from first histogram to ensure all params use same binning + std::map<std::string, std::pair<double, double>> ParamEdges; + for(int i = 0; i < Processor[0]->GetNParams(); ++i) { + // Get the histogram for the i-th parameter + TH1D* hist = Processor[0]->GetHpost(i); + if (!hist) { + MACH3LOG_DEBUG("Histogram for parameter {} is null.", i); + continue; + } + + // Get the parameter name (title of the histogram) + std::string paramName = hist->GetTitle(); + + // Get the axis limits (edges) + TAxis* axis = hist->GetXaxis(); + double xmin = axis->GetXmin(); + double xmax = axis->GetXmax(); + + MACH3LOG_DEBUG("Adding bin edges for {} equal to {:.4f}, {:.4f}",paramName, xmin, xmax); + // Insert into the map + ParamEdges[paramName] = std::make_pair(xmin, xmax); + } + + //KS: Multithreading here is very tempting but there are some issues with root that need to be resovled :( + for (int ik = 1; ik < nFiles; ik++) + { + // Make the postfit + Processor[ik]->MakePostfit(ParamEdges); + Processor[ik]->DrawPostfit(); + } + + // Open a TCanvas to write the posterior onto + auto Posterior = std::make_unique<TCanvas>("PosteriorMulti", "PosteriorMulti", 0, 0, 1024, 1024); + gStyle->SetOptStat(0); + gStyle->SetOptTitle(0); + Posterior->SetGrid(); + Posterior->SetBottomMargin(0.1f); + Posterior->SetTopMargin(0.05f); + Posterior->SetRightMargin(0.03f); + Posterior->SetLeftMargin(0.15f); + + // First filename: keep path, just remove ".root" + // Would be nice to specify outpath in a later update + size_t pos = FileNames[0].rfind(".root"); + std::string base = (pos == std::string::npos) ? FileNames[0] : FileNames[0].substr(0, pos); + TString canvasname = base; + + // Remaining filenames: strip path and ".root" + // So if you have /path/to/file1.root and /path/to/file2.root or /another/path/to/file2.root, canvasname = /path/to/file1_file2.root + for (int ik = 1; ik < nFiles; ik++) { + pos = FileNames[ik].find_last_of('/'); + base = (pos == std::string::npos) ? FileNames[ik] : FileNames[ik].substr(pos + 1); + pos = base.rfind(".root"); + if (pos != std::string::npos) base = base.substr(0, pos); + canvasname += "_" + TString(base); + } + + canvasname = canvasname +".pdf["; + + Posterior->Print(canvasname); + // Once the pdf file is open no longer need to bracket + canvasname.ReplaceAll("[",""); + + for(int i = 0; i < Processor[0]->GetNParams(); ++i) + { + // This holds the posterior density + std::vector<std::unique_ptr<TH1D>> hpost(nFiles); + std::vector<std::unique_ptr<TLine>> hpd(nFiles); + hpost[0] = M3::Clone(Processor[0]->GetHpost(i)); + hpost[0]->GetYaxis()->SetTitle("Posterior Density"); + bool Skip = false; + for (int ik = 1 ; ik < nFiles; ik++) + { + // KS: If somehow this chain doesn't given params we skip it + const int Index = Processor[ik]->GetParamIndexFromName(hpost[0]->GetTitle()); + if(Index == M3::_BAD_INT_) + { + Skip = true; + break; + } + hpost[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); + } + + // Don't plot if this is a fixed histogram (i.e. the peak is the whole integral) + if(hpost[0]->GetMaximum() == hpost[0]->Integral()*1.5 || Skip) { + continue; + } + for (int ik = 0; ik < nFiles; ik++) + { + RemoveFitter(hpost[ik].get(), "Gauss"); + + // Set some nice colours + hpost[ik]->SetLineColor(PosteriorColor[ik]); + //hpost[ik]->SetLineStyle(PosteriorStyle[ik]); + hpost[ik]->SetLineWidth(2); + + // Area normalise the distributions + hpost[ik]->Scale(1./hpost[ik]->Integral()); + } + TString Title; + double Prior = 1.0; + double PriorError = 1.0; + + Processor[0]->GetNthParameter(i, Prior, PriorError, Title); + + // Now make the TLine for the Asimov + auto Asimov = std::make_unique<TLine>(Prior, hpost[0]->GetMinimum(), Prior, hpost[0]->GetMaximum()); + Asimov->SetLineColor(kRed-3); + Asimov->SetLineWidth(2); + Asimov->SetLineStyle(kDashed); + + // Make a nice little TLegend + auto leg = std::make_unique<TLegend>(0.20, 0.7, 0.6, 0.97); + leg->SetTextSize(0.03f); + leg->SetFillColor(0); + leg->SetFillStyle(0); + leg->SetLineColor(0); + leg->SetLineStyle(0); + TString asimovLeg = Form("#splitline{Prior}{x = %.2f , #sigma = %.2f}", Prior, PriorError); + leg->AddEntry(Asimov.get(), asimovLeg, "l"); + + for (int ik = 0; ik < nFiles; ik++) + { + TString rebinLeg = Form("#splitline{%s}{#mu = %.2f, #sigma = %.2f}", TitleNames[ik].c_str(), hpost[ik]->GetMean(), hpost[ik]->GetRMS()); + leg->AddEntry(hpost[ik].get(), rebinLeg, "l"); + + hpd[ik] = std::make_unique<TLine>(hpost[ik]->GetBinCenter(hpost[ik]->GetMaximumBin()), hpost[ik]->GetMinimum(), + hpost[ik]->GetBinCenter(hpost[ik]->GetMaximumBin()), hpost[ik]->GetMaximum()); + hpd[ik]->SetLineColor(hpost[ik]->GetLineColor()); + hpd[ik]->SetLineWidth(2); + hpd[ik]->SetLineStyle(kSolid); + } + + // Find the maximum value to nicely resize hist + double maximum = 0; + for (int ik = 0; ik < nFiles; ik++) maximum = std::max(maximum, hpost[ik]->GetMaximum()); + for (int ik = 0; ik < nFiles; ik++) hpost[ik]->SetMaximum(1.3*maximum); + + hpost[0]->Draw("hist"); + for (int ik = 1; ik < nFiles; ik++) hpost[ik]->Draw("hist same"); + Asimov->Draw("same"); + for (int ik = 0; ik < nFiles; ik++) hpd[ik]->Draw("same"); + leg->Draw("same"); + Posterior->cd(); + Posterior->Print(canvasname); + }//End loop over parameters + + // Finally draw the parameter plot onto the PDF + // Close the .pdf file with all the posteriors + Posterior->cd(); + Posterior->Clear(); + + if(GetFromManager<bool>(Settings["PerformKStest"], true)) KolmogorovSmirnovTest(Processor, Posterior, canvasname); + + // Close the pdf file + MACH3LOG_INFO("Closing pdf {}", canvasname); + canvasname+="]"; + Posterior->Print(canvasname); + } + + /// @brief KS: Calculate Bayes factor for a given hypothesis, most informative are those related to osc params. However, it make relative easy interpretation for switch dials + void ProcessMCMCPlugin::CalcBayesFactor(MCMCProcessor* Processor) + { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + std::vector<std::string> ParNames; + std::vector<std::vector<double>> Model1Bounds; + std::vector<std::vector<double>> Model2Bounds; + std::vector<std::vector<std::string>> ModelNames; + for (const auto& dg : Settings["BayesFactor"]) + { + ParNames.push_back(dg[0].as<std::string>()); + ModelNames.push_back(dg[1].as<std::vector<std::string>>()); + Model1Bounds.push_back(dg[2].as<std::vector<double>>()); + Model2Bounds.push_back(dg[3].as<std::vector<double>>()); + } + + Processor->GetBayesFactor(ParNames, Model1Bounds, Model2Bounds, ModelNames); + } + + void ProcessMCMCPlugin::CalcSavageDickey(MCMCProcessor* Processor) + { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + std::vector<std::string> ParNames; + std::vector<double> EvaluationPoint; + std::vector<std::vector<double>> Bounds; + + for (const auto& d : Settings["SavageDickey"]) + { + ParNames.push_back(d[0].as<std::string>()); + EvaluationPoint.push_back(d[1].as<double>()); + Bounds.push_back(d[2].as<std::vector<double>>()); + } + Processor->GetSavageDickey(ParNames, EvaluationPoint, Bounds); + } + + void ProcessMCMCPlugin::CalcParameterEvolution(MCMCProcessor* Processor) + { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + std::vector<std::string> ParNames; + std::vector<int> Intervals; + for (const auto& d : Settings["ParameterEvolution"]) + { + ParNames.push_back(d[0].as<std::string>()); + Intervals.push_back(d[1].as<int>()); + } + Processor->ParameterEvolution(ParNames, Intervals); + } + + void ProcessMCMCPlugin::CalcBipolarPlot(MCMCProcessor* Processor) + { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + std::vector<std::string> ParNames; + for (const auto& d : Settings["BipolarPlot"]) + { + ParNames.push_back(d[0].as<std::string>()); + } + Processor->GetPolarPlot(ParNames); + } + + void ProcessMCMCPlugin::GetTrianglePlot(MCMCProcessor* Processor) { + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + for (const auto& dg : Settings["TrianglePlot"]) + { + std::string ParName = dg[0].as<std::string>(); + + std::vector<std::string> NameVec = dg[1].as<std::vector<std::string>>(); + Processor->MakeTrianglePlot(NameVec, + GetFromManager<std::vector<double>>(Settings["CredibleIntervals"], {0.99, 0.90, 0.68}), + GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), + GetFromManager<std::vector<double>>(Settings["CredibleRegions"], {0.99, 0.90, 0.68}), + GetFromManager<std::vector<short int>>(Settings["CredibleRegionStyle"], {2, 1, 3}), + GetFromManager<std::vector<short int>>(Settings["CredibleRegionColor"], {413, 406, 416}), + GetFromManager<bool>(Settings["CredibleInSigmas"], false)); + } + } + + /// @brief KS: You validate stability of posterior covariance matrix, you set burn calc cov matrix increase burn calc again and compare. By performing such operation several hundred times we can check when matrix becomes stable + void ProcessMCMCPlugin::DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) + { + //Turn of plots from Processor + Processor->SetPrintToPDF(false); + // Open a TCanvas to write the posterior onto + auto Canvas = std::make_unique<TCanvas>("Canvas", "Canvas", 0, 0, 1024, 1024); + Canvas->SetGrid(); + gStyle->SetOptStat(0); + gStyle->SetOptTitle(0); + Canvas->SetTickx(); + Canvas->SetTicky(); + Canvas->SetBottomMargin(0.1f); + Canvas->SetTopMargin(0.05f); + Canvas->SetRightMargin(0.15f); + Canvas->SetLeftMargin(0.10f); + + //KS: Fancy colours + const int NRGBs = 10; + TColor::InitializeColors(); + Double_t stops[NRGBs] = { 0.00, 0.10, 0.25, 0.35, 0.50, 0.60, 0.65, 0.75, 0.90, 1.00 }; + Double_t red[NRGBs] = { 0.50, 1.00, 1.00, 0.25, 0.00, 0.10, 0.50, 1.00, 0.75, 0.55 }; + Double_t green[NRGBs] = { 0.00, 0.25, 1.00, 0.25, 0.00, 0.60, 0.90, 1.00, 0.75, 0.75 }; + Double_t blue[NRGBs] = { 0.00, 0.25, 1.00, 1.00, 0.50, 0.60, 0.90, 1.00, 0.05, 0.05 }; + TColor::CreateGradientColorTable(NRGBs, stops, red, green, blue, 255); + gStyle->SetNumberContours(255); + + std::string OutName = inputFile; + OutName = OutName.substr(0, OutName.find(".root")); + Canvas->Print(Form("Correlation_%s.pdf[", OutName.c_str()), "pdf"); + Canvas->Print(Form("Covariance_%s.pdf[", OutName.c_str()), "pdf"); + + YAML::Node card_yaml = M3OpenConfig(config.c_str()); + YAML::Node Settings = card_yaml["ProcessMCMC"]; + + const int entries = int(Processor->GetnSteps()); + const int NIntervals = GetFromManager<int>(Settings["NIntervals"], 5); + const int IntervalsSize = entries/NIntervals; + //We start with burn from 0 (no burn in at all) + int BurnIn = 0; + MACH3LOG_INFO("Diagnosing matrices with entries={}, NIntervals={} and IntervalsSize={}", entries, NIntervals, IntervalsSize); + + TMatrixDSym *Covariance = nullptr; + TMatrixDSym *Correlation = nullptr; + + TH2D *CovariancePreviousHist = nullptr; + TH2D *CorrelationPreviousHist = nullptr; + + TH2D *CovarianceHist = nullptr; + TH2D *CorrelationHist = nullptr; + + //KS: Get first covariances, we need two for comparison... + Processor->SetStepCut(BurnIn); + Processor->GetCovariance(Covariance, Correlation); + + CovariancePreviousHist = TMatrixIntoTH2D(Covariance, "Covariance"); + CorrelationPreviousHist = TMatrixIntoTH2D(Correlation, "Correlation"); + + delete Covariance; + Covariance = nullptr; + delete Correlation; + Correlation = nullptr; + + //KS: Loop over all desired cuts + for(int k = 1; k < NIntervals; ++k) + { + BurnIn = k*IntervalsSize; + Processor->SetStepCut(BurnIn); + Processor->GetCovariance(Covariance, Correlation); + Processor->Reset2DPosteriors(); + + CovarianceHist = TMatrixIntoTH2D(Covariance, "Covariance"); + CorrelationHist = TMatrixIntoTH2D(Correlation, "Correlation"); + + TH2D *CovarianceDiff = static_cast<TH2D*>(CovarianceHist->Clone("Covariance_Ratio")); + TH2D *CorrelationDiff = static_cast<TH2D*>(CorrelationHist->Clone("Correlation_Ratio")); + + //KS: Bit messy but quite often covariance is 0 is divided by 0 is problematic so + #ifdef MULTITHREAD + #pragma omp parallel for + #endif + for (int j = 1; j < CovarianceDiff->GetXaxis()->GetNbins()+1; ++j) + { + for (int i = 1; i < CovarianceDiff->GetYaxis()->GetNbins()+1; ++i) + { + if( std::fabs (CovarianceDiff->GetBinContent(j, i)) < 1.e-5 && std::fabs (CovariancePreviousHist->GetBinContent(j, i)) < 1.e-5) + { + CovarianceDiff->SetBinContent(j, i, M3::_BAD_DOUBLE_); + CovariancePreviousHist->SetBinContent(j, i, M3::_BAD_DOUBLE_); + } + if( std::fabs (CorrelationDiff->GetBinContent(j, i)) < 1.e-5 && std::fabs (CorrelationPreviousHist->GetBinContent(j, i)) < 1.e-5) + { + CorrelationDiff->SetBinContent(j, i, M3::_BAD_DOUBLE_); + CorrelationPreviousHist->SetBinContent(j, i, M3::_BAD_DOUBLE_); + } + } + } + //Divide matrices + CovarianceDiff->Divide(CovariancePreviousHist); + CorrelationDiff->Divide(CorrelationPreviousHist); + + //Now it is time for fancy names etc. + for (int j = 0; j < CovarianceDiff->GetXaxis()->GetNbins(); ++j) + { + TString Title = ""; + double Prior = 1.0; + double PriorError = 1.0; + + Processor->GetNthParameter(j, Prior, PriorError, Title); + + CovarianceDiff->GetXaxis()->SetBinLabel(j+1, Title); + CovarianceDiff->GetYaxis()->SetBinLabel(j+1, Title); + CorrelationDiff->GetXaxis()->SetBinLabel(j+1, Title); + CorrelationDiff->GetYaxis()->SetBinLabel(j+1, Title); + } + CovarianceDiff->GetXaxis()->SetLabelSize(0.015f); + CovarianceDiff->GetYaxis()->SetLabelSize(0.015f); + CorrelationDiff->GetXaxis()->SetLabelSize(0.015f); + CorrelationDiff->GetYaxis()->SetLabelSize(0.015f); + + std::stringstream ss; + ss << "BCut_"; + ss << BurnIn; + ss << "/"; + ss << "BCut_"; + ss << (k-1)*IntervalsSize; + std::string str = ss.str(); + + TString Title = "Cov " + str; + CovarianceDiff->GetZaxis()->SetTitle( Title ); + Title = "Corr " + str; + CorrelationDiff->GetZaxis()->SetTitle(Title); + + CovarianceDiff->SetMinimum(-2); + CovarianceDiff->SetMaximum(2); + CorrelationDiff->SetMinimum(-2); + CorrelationDiff->SetMaximum(2); + + Canvas->cd(); + CovarianceDiff->Draw("colz"); + Canvas->Print(Form("Covariance_%s.pdf", OutName.c_str()), "pdf"); + + CorrelationDiff->Draw("colz"); + Canvas->Print(Form("Correlation_%s.pdf", OutName.c_str()), "pdf"); + + //KS: Current hist become previous as we need it for further comparison + delete CovariancePreviousHist; + CovariancePreviousHist = static_cast<TH2D*>(CovarianceHist->Clone()); + delete CorrelationPreviousHist; + CorrelationPreviousHist = static_cast<TH2D*>(CorrelationHist->Clone()); + + delete CovarianceHist; + CovarianceHist = nullptr; + delete CorrelationHist; + CorrelationHist = nullptr; + + delete CovarianceDiff; + delete CorrelationDiff; + delete Covariance; + Covariance = nullptr; + delete Correlation; + Correlation = nullptr; + } + Canvas->cd(); + Canvas->Print(Form("Covariance_%s.pdf]", OutName.c_str()), "pdf"); + Canvas->Print(Form("Correlation_%s.pdf]", OutName.c_str()), "pdf"); + + Processor->SetPrintToPDF(true); + if(Covariance != nullptr) delete Covariance; + if(Correlation != nullptr) delete Correlation; + if(CovariancePreviousHist != nullptr) delete CovariancePreviousHist; + if(CorrelationPreviousHist != nullptr) delete CorrelationPreviousHist; + if(CovarianceHist != nullptr) delete CovarianceHist; + if(CorrelationHist != nullptr) delete CorrelationHist; + } + + //KS: Convert TMatrix to TH2D, mostly useful for making fancy plots + TH2D* ProcessMCMCPlugin::TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) + { + TH2D* hMatrix = new TH2D(title.c_str(), title.c_str(), Matrix->GetNrows(), 0.0, Matrix->GetNrows(), Matrix->GetNcols(), 0.0, Matrix->GetNcols()); + for(int i = 0; i < Matrix->GetNrows(); i++) + { + for(int j = 0; j < Matrix->GetNcols(); j++) + { + //KS: +1 because there is offset in histogram relative to TMatrix + hMatrix->SetBinContent(i+1,j+1, (*Matrix)(i,j)); + } + } + return hMatrix; + } + + // KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution + void ProcessMCMCPlugin::KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, + const std::unique_ptr<TCanvas>& Posterior, + const TString& canvasname) + { + constexpr Color_t CumulativeColor[] = {kBlue-1, kRed, kGreen+2}; + constexpr Style_t CumulativeStyle[] = {kSolid, kDashed, kDotted}; + + for(int i = 0; i < Processor[0]->GetNParams(); ++i) + { + // This holds the posterior density + std::vector<std::unique_ptr<TH1D>> hpost(nFiles); + std::vector<std::unique_ptr<TH1D>> CumulativeDistribution(nFiles); + + TString Title; + double Prior = 1.0; + double PriorError = 1.0; + + Processor[0]->GetNthParameter(i, Prior, PriorError, Title); + bool Skip = false; + for (int ik = 0 ; ik < nFiles; ik++) + { + int Index = 0; + if(ik == 0 ) Index = i; + else + { + // KS: If somehow this chain doesn't given params we skip it + Index = Processor[ik]->GetParamIndexFromName(hpost[0]->GetTitle()); + if(Index == M3::_BAD_INT_) + { + Skip = true; + break; + } + } + hpost[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); + CumulativeDistribution[ik] = M3::Clone(Processor[ik]->GetHpost(Index)); + CumulativeDistribution[ik]->Fill(0., 0.); + CumulativeDistribution[ik]->Reset(); + CumulativeDistribution[ik]->SetMaximum(1.); + TString TempTitle = Title+" Kolmogorov Smirnov"; + CumulativeDistribution[ik]->SetTitle(TempTitle); + + TempTitle = Title+" Value"; + CumulativeDistribution[ik]->GetXaxis()->SetTitle(TempTitle); + CumulativeDistribution[ik]->GetYaxis()->SetTitle("Cumulative Probability"); + + CumulativeDistribution[ik]->SetLineWidth(2); + CumulativeDistribution[ik]->SetLineColor(CumulativeColor[ik]); + CumulativeDistribution[ik]->SetLineStyle(CumulativeStyle[ik]); + } + + // Don't plot if this is a fixed histogram (i.e. the peak is the whole integral) + if(hpost[0]->GetMaximum() == hpost[0]->Integral()*1.5 || Skip) { + continue; + } + + for (int ik = 0 ; ik < nFiles; ik++) + { + const int NumberOfBins = hpost[ik]->GetXaxis()->GetNbins(); + double Cumulative = 0; + const double Integral = hpost[ik]->Integral(); + for (int j = 1; j < NumberOfBins+1; ++j) + { + Cumulative += hpost[ik]->GetBinContent(j)/Integral; + CumulativeDistribution[ik]->SetBinContent(j, Cumulative); + } + //KS: Set overflow to 1 just in case + CumulativeDistribution[ik]->SetBinContent(NumberOfBins+1, 1.); + } + + std::vector<int> TestStatBin(nFiles, 0); + std::vector<double> TestStatD(nFiles, -999); + std::vector<std::unique_ptr<TLine>> LineD(nFiles); + //Find KS statistic + for (int ik = 1 ; ik < nFiles; ik++) + { + const int NumberOfBins = CumulativeDistribution[0]->GetXaxis()->GetNbins(); + for (int j = 1; j < NumberOfBins+1; ++j) + { + const double BinValue = CumulativeDistribution[0]->GetBinCenter(j); + const int BinNumber = CumulativeDistribution[ik]->FindBin(BinValue); + //KS: Calculate D statistic for this bin, only save it if it's bigger than previously found value + double TempDstat = std::fabs(CumulativeDistribution[0]->GetBinContent(j) - CumulativeDistribution[ik]->GetBinContent(BinNumber)); + if(TempDstat > TestStatD[ik]) + { + TestStatD[ik] = TempDstat; + TestStatBin[ik] = j; + } + } + } + + for (int ik = 0 ; ik < nFiles; ik++) + { + LineD[ik] = std::make_unique<TLine>(CumulativeDistribution[0]->GetBinCenter(TestStatBin[ik]), 0, CumulativeDistribution[0]->GetBinCenter(TestStatBin[ik]), CumulativeDistribution[0]->GetBinContent(TestStatBin[ik])); + LineD[ik]->SetLineColor(CumulativeColor[ik]); + LineD[ik]->SetLineWidth(2.0); + } + CumulativeDistribution[0]->Draw(); + for (int ik = 0 ; ik < nFiles; ik++) + CumulativeDistribution[ik]->Draw("SAME"); + + auto leg = std::make_unique<TLegend>(0.15, 0.7, 0.5, 0.90); + leg->SetTextSize(0.04f); + for (int ik = 0; ik < nFiles; ik++) + leg->AddEntry(CumulativeDistribution[ik].get(), TitleNames[ik].c_str(), "l"); + for (int ik = 1; ik < nFiles; ik++) + leg->AddEntry(LineD[ik].get(), Form("#Delta D = %.4f", TestStatD[ik]), "l"); + + leg->SetLineColor(0); + leg->SetLineStyle(0); + leg->SetFillColor(0); + leg->SetFillStyle(0); + leg->Draw("SAME"); + + for (int ik = 1; ik < nFiles; ik++) + LineD[ik]->Draw("sam"); + + Posterior->cd(); + Posterior->Print(canvasname); + } //End loop over parameter + } +}; \ No newline at end of file diff --git a/Diagnostics/ProcessMCMCPlugin.hpp b/Diagnostics/ProcessMCMCPlugin.hpp new file mode 100644 index 000000000..f0e84392a --- /dev/null +++ b/Diagnostics/ProcessMCMCPlugin.hpp @@ -0,0 +1,59 @@ +//MaCh3 includes +#pragma once +#include "api/plugin.hpp" +#include "Fitters/OscProcessor.h" +#include "Manager/Manager.h" + +using argparse::ArgumentParser; + +namespace mach3{ + + + +/// @file ProcessMCMC.cpp +/// @brief Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implantation of methods is in MCMCProcessor +/// @ingroup MaCh3DiagnosticProcessing +/// +/// @author Kamil Skwarczynski + +/// @brief Main function processing MCMC and Producing plots + + class ProcessMCMCPlugin: public IPlugin{ + + public: + virtual ~ProcessMCMCPlugin(){ + if (m_parser) { delete m_parser; } + } + MaCh3ArgumentParser* get_parser() override; + int run() override; + + + private: + std::map<std::string, std::pair<double, double>> GetCustomBinning(const YAML::Node& Settings); + void ProcessMCMC(const std::string& inputFile); + /// @brief Function producing comparison of posterior and more betwen a few MCMC chains + void MultipleProcessMCMC(); + void CalcBayesFactor(MCMCProcessor* Processor); + void CalcSavageDickey(MCMCProcessor* Processor); + void CalcParameterEvolution(MCMCProcessor* Processor); + void CalcBipolarPlot(MCMCProcessor* Processor); + void GetTrianglePlot(MCMCProcessor* Processor); + void DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile); + /// @brief KS: Convert TMatrix to TH2D, mostly useful for making fancy plots + TH2D* TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title); + /// @brief KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution + void KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, + const std::unique_ptr<TCanvas>& Posterior, + const TString& canvasname); + int nFiles; + std::vector <std::string> FileNames; + std::vector <std::string> TitleNames; + std::string config; + MaCh3ArgumentParser* m_parser; + }; +}; + +extern "C" { + typedef mach3::IPlugin* (*create_plugin_t)(); + typedef void (*destroy_plugin_t)(mach3::IPlugin*); +} diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt new file mode 100644 index 000000000..65b196032 --- /dev/null +++ b/api/CMakeLists.txt @@ -0,0 +1,35 @@ +include(FetchContent) +FetchContent_Declare( + argparse + GIT_REPOSITORY https://github.com/p-ranav/argparse.git +) +FetchContent_MakeAvailable(argparse) + +set(HEADERS + plugin.hpp +) + + +add_library(api INTERFACE) + +target_include_directories(api INTERFACE ${argparse_SOURCE_DIR}/include) + +target_include_directories(api + INTERFACE + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> + $<INSTALL_INTERFACE:include/api> +) + + +set_target_properties(api PROPERTIES + PUBLIC_HEADER "${HEADERS}" + EXPORT_NAME api) + + +install(TARGETS api + EXPORT MaCh3-targets + LIBRARY DESTINATION lib/ + PUBLIC_HEADER DESTINATION include/api) + +#install(FILES ${HEADERS} DESTINATION include/api) +add_library(MaCh3::api ALIAS api) \ No newline at end of file diff --git a/api/argparse.hpp b/api/argparse.hpp new file mode 100644 index 000000000..daa765ea4 --- /dev/null +++ b/api/argparse.hpp @@ -0,0 +1,29 @@ +#pragma once +#include <argparse/argparse.hpp> + +using argparse::ArgumentParser; + +namespace mach3 { + + class MaCh3ArgumentParser: public ArgumentParser{ + public: + using ArgumentParser::ArgumentParser; + virtual ~MaCh3ArgumentParser() = default; + + const std::string name() const{ + return this->m_program_name; + } + const std::list<std::reference_wrapper<ArgumentParser>>& subparsers() const{ + return this->m_subparsers; + } + + const MaCh3ArgumentParser& get_subcommand_used(){ + for (const std::reference_wrapper<ArgumentParser>& subparser : this->m_subparsers) { + if (this->is_subcommand_used(subparser.get())) { + return static_cast<MaCh3ArgumentParser*>(&(subparser.get()))->get_subcommand_used(); + } + } + return (*this); + } + }; +}; \ No newline at end of file diff --git a/api/plugin.hpp b/api/plugin.hpp new file mode 100644 index 000000000..fb43d5149 --- /dev/null +++ b/api/plugin.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "api/argparse.hpp" + + +namespace mach3 { + + class IPlugin { + public: + virtual ~IPlugin() = default; + virtual int run() = 0; + virtual MaCh3ArgumentParser* get_parser() = 0; + }; + +}; + +// Factory function signature +extern "C" { + typedef mach3::IPlugin* (*create_plugin_t)(); + typedef void (*destroy_plugin_t)(mach3::IPlugin*); +} diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 000000000..b0064fd6e --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,4 @@ + add_executable(mach3 mach3.cpp) + target_link_libraries(mach3 MaCh3::All MaCh3::Plugins) + + install(TARGETS mach3 DESTINATION bin) diff --git a/cli/mach3.cpp b/cli/mach3.cpp new file mode 100644 index 000000000..b07a02c95 --- /dev/null +++ b/cli/mach3.cpp @@ -0,0 +1,197 @@ +#include <dlfcn.h> +#include <cstdlib> +#include <iostream> +#include <sstream> +#include <vector> +#include "api/plugin.hpp" +#include "Diagnostics/ProcessMCMCPlugin.hpp" + +using namespace std; + +namespace mach3{ + class DynamicPlugin: public IPlugin{ + public: + DynamicPlugin(void* handle, + IPlugin* plugin, + destroy_plugin_t destroy_func):m_handle(handle), + m_plugin(plugin), + m_destroy_func(destroy_func){} + + int run() { + return m_plugin->run(); + } + MaCh3ArgumentParser* get_parser(){ + return m_plugin->get_parser(); + } + + void destroy(){ + m_destroy_func(m_plugin); + m_plugin = 0; + dlclose(m_handle); + m_handle = 0; + m_destroy_func = 0; + } + + private: + void* m_handle; + IPlugin* m_plugin; + destroy_plugin_t m_destroy_func; + }; + + class NoArgsException: public std::exception{}; + + + class MaCh3Program: public MaCh3ArgumentParser{ + public: + using MaCh3ArgumentParser::MaCh3ArgumentParser; + virtual ~MaCh3Program(){ + //std::cout<<"UNLOADED"<< std::endl; + this->unload_dynamic_plugins(); + } + + + void parse_args(int argc, const char *const argv[]) { + try{ + MaCh3ArgumentParser::parse_args(argc, argv); + } + catch (const std::exception& err) { + std::cerr << err.what() << std::endl; + std::cerr << this->get_subcommand_used(); + throw; + } + + if (!this->get_subcommand_used()){ + std::cerr << this->get_subcommand_used(); + throw NoArgsException(); + } + } + + void add_core_plugin(IPlugin& plugin){ + MaCh3ArgumentParser* parser = plugin.get_parser(); + this->add_subparser(*parser); + m_plugin_map[parser] = &plugin; + } + + void load_dynamic_plugins(){ + const char* env = std::getenv("MACH3_PLUGINS"); + if (!env) { + //std::cerr << "No plugins specified\n"; + return; + } + + std::stringstream ss(env); + std::string path; + + while (std::getline(ss, path, ':')) { + void* handle = dlopen(path.c_str(), RTLD_NOW); + if (!handle) { + std::cerr << "dlopen failed - shared library not loaded: " << dlerror() << "\n"; + continue; + } + + auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); + auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); + + if (!create || !destroy) { + std::cerr << "Invalid plugin: " << path << "\n"; + dlclose(handle); + continue; + } + + IPlugin* plugin = nullptr; + try{ + plugin = create(); + if (!plugin){ + throw std::runtime_error("null pointer"); + } + } + catch (...){ + std::cerr << "Error instantiating plugin: " << path << "\n"; + dlclose(handle); + continue; + } + + MaCh3ArgumentParser* parser = nullptr; + try{ + parser = plugin->get_parser(); + if (!parser){ + throw std::runtime_error("null pointer"); + } + } + catch(...){ + std::cerr<< "Error retrieving parser" << "\n"; + destroy(plugin); + dlclose(handle); + continue; + } + + + try{ + this->add_subparser(*parser); + m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); + } + catch(...){ + std::cerr<< "Error finalising loading of plugin." << "\n"; + destroy(plugin); + dlclose(handle); + continue; + } + + } + } + + void unload_dynamic_plugins(){ + for (auto& [_, plugin] : m_dynamic_plugin_map){ + plugin->destroy(); + } + m_dynamic_plugin_map.clear(); + } + + int run(){ + + const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); + + if (sub_parser){ + auto plugin_itr = m_plugin_map.find(&sub_parser); + if (plugin_itr != m_plugin_map.end()){ + return plugin_itr->second->run(); + } + auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); + if (dplugin_itr != m_dynamic_plugin_map.end()){ + return dplugin_itr->second->run(); + } + } + return 0; + } + + private: + std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; + std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; + }; +}; + + +int main(int argc, char *argv[]) { + mach3::MaCh3Program program; + + mach3::ProcessMCMCPlugin proc; + + program.add_core_plugin(proc); + program.load_dynamic_plugins(); + + try { + program.parse_args(argc, argv); + } + catch(...){ + return 1; + } + + try{ + program.run(); + } + catch(...){ + return 2; + } + + return 0; +} \ No newline at end of file From 626dcca01919618337a09ee846a0aa799e69233b Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 21 Apr 2026 14:33:40 +0100 Subject: [PATCH 02/26] Fixup --- Diagnostics/ProcessMCMCPlugin.cpp | 2 +- api/argparse.hpp | 2 +- cli/mach3.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp index 3aef17ea9..baefe284c 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -847,4 +847,4 @@ namespace mach3{ Posterior->Print(canvasname); } //End loop over parameter } -}; \ No newline at end of file +}; diff --git a/api/argparse.hpp b/api/argparse.hpp index daa765ea4..f0eceb9cb 100644 --- a/api/argparse.hpp +++ b/api/argparse.hpp @@ -26,4 +26,4 @@ namespace mach3 { return (*this); } }; -}; \ No newline at end of file +}; diff --git a/cli/mach3.cpp b/cli/mach3.cpp index b07a02c95..ad112cca6 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -194,4 +194,4 @@ int main(int argc, char *argv[]) { } return 0; -} \ No newline at end of file +} From 56c798e091e4d63a7cb429b9280ae954a63c1012 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 28 Apr 2026 19:48:45 +0100 Subject: [PATCH 03/26] Adding Diag --- Diagnostics/CMakeLists.txt | 17 ++++---- Diagnostics/DiagMCMC.cpp | 66 +++++++------------------------ Diagnostics/DiagMCMCPlugin.cpp | 61 ++++++++++++++++++++++++++++ Diagnostics/DiagMCMCPlugin.hpp | 19 +++++++++ Diagnostics/ProcessMCMC.cpp | 1 + Diagnostics/ProcessMCMCPlugin.cpp | 17 ++++---- Diagnostics/ProcessMCMCPlugin.hpp | 14 ++----- api/plugin.hpp | 11 +++++- cli/CMakeLists.txt | 2 +- cli/mach3.cpp | 3 ++ 10 files changed, 130 insertions(+), 81 deletions(-) create mode 100644 Diagnostics/DiagMCMCPlugin.cpp create mode 100644 Diagnostics/DiagMCMCPlugin.hpp diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index f83a49db7..a5d6aad40 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -1,14 +1,15 @@ -set(HEADERS - ProcessMCMCPlugin.hpp -) +# set(HEADERS +# ProcessMCMCPlugin.hpp +# ) -add_library(Plugins STATIC +add_library(CorePlugins STATIC ProcessMCMCPlugin.cpp + DiagMCMCPlugin.cpp ) -target_link_libraries(Plugins PRIVATE MaCh3::All) -target_include_directories(Plugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) +target_link_libraries(CorePlugins PRIVATE MaCh3::All) +target_include_directories(CorePlugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) # set_target_properties(Plugins PROPERTIES # PUBLIC_HEADER "${HEADERS}" @@ -20,7 +21,7 @@ target_include_directories(Plugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/ # LIBRARY DESTINATION lib/ # PUBLIC_HEADER DESTINATION include/Plugins) -add_library(MaCh3::Plugins ALIAS Plugins) +add_library(MaCh3::CorePlugins ALIAS CorePlugins) add_custom_target(DiagApps) @@ -38,7 +39,7 @@ foreach(app ) add_executable(${app} ${app}.cpp) add_dependencies(DiagApps ${app}) - target_link_libraries(${app} MaCh3::All MaCh3::Plugins MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either + target_link_libraries(${app} MaCh3::CorePlugins MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either install(TARGETS ${app} DESTINATION bin) endforeach(app) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index 88802c903..d211d9efb 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -1,53 +1,15 @@ -#include "Fitters/MCMCProcessor.h" - -#include "Manager/Manager.h" - -/// @file DiagMCMC.cpp -/// @ingroup MaCh3DiagnosticProcessing -/// -/// @author Clarence Wret -/// @author Kamil Skwarczynski - -/// @brief Main function creating MCMCProcessor and calling MCMC Diagnostic -/// @param inputFile MCMC Chain -/// @param config Config file with settings -void DiagMCMC(const std::string& inputFile, const std::string& config) -{ - MACH3LOG_INFO("File for study: {}", inputFile); - - YAML::Node Settings = M3OpenConfig(config); - - // Make the processor - auto Processor = std::make_unique<MCMCProcessor>(inputFile); - Processor->SetOutputSuffix("_MCMC_Diag"); - //KS:Turn off plotting detector and some other setting - Processor->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedTypes"], {})); - Processor->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedNames"], {})); - Processor->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedGroups"], {})); - Processor->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["DiagMCMC"]["PlotRelativeToPrior"], false)); - //KS: Use 20 batches for batched means - Processor->SetnBatches(GetFromManager<int>(Settings["DiagMCMC"]["nBatches"], 20)); - Processor->SetnLags(GetFromManager<int>(Settings["DiagMCMC"]["nLags"], 25000)); - Processor->SetPrintToPDF(GetFromManager<bool>(Settings["PrintToPDF"], true)); - Processor->Initialise(); - if(Settings["MaxEntries"]) { - Processor->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); - } - //KS: finally call main method - Processor->DiagMCMC(); -} - -int main(int argc, char *argv[]) { - SetMaCh3LoggerFormat(); - if (argc != 3) - { - MACH3LOG_ERROR("How to use: DiagMCMC MCMC_Output.root config"); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - MACH3LOG_INFO("Producing single fit output"); - std::string filename = argv[1]; - std::string config = argv[2]; - DiagMCMC(filename, config); - - return 0; +#include "Manager/MaCh3Logger.h" +#include "Diagnostics/DiagMCMCPlugin.hpp" + +int main(int argc, char const* argv[]){ + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + argv[0] = "diag"; + mach3::DiagMCMCPlugin proc; + ArgumentParser* parser = proc.get_parser(); + parser->parse_args(argc, argv); + proc.run(); + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + return 0; } diff --git a/Diagnostics/DiagMCMCPlugin.cpp b/Diagnostics/DiagMCMCPlugin.cpp new file mode 100644 index 000000000..3382fb880 --- /dev/null +++ b/Diagnostics/DiagMCMCPlugin.cpp @@ -0,0 +1,61 @@ +#include "Fitters/MCMCProcessor.h" +#include "Manager/Manager.h" +#include "Diagnostics/DiagMCMCPlugin.hpp" + +// Not needed for Core plugins as not dynamically loaded +// extern "C" mach3::IPlugin* create_plugin() { +// return new mach3::DiagMCMCPlugin(); +// } + +// extern "C" void destroy_plugin(mach3::IPlugin* p) { +// delete p; +// } + +namespace mach3{ + + MaCh3ArgumentParser* DiagMCMCPlugin::get_parser(){ + m_parser = new MaCh3ArgumentParser("diag", "1.0", argparse::default_arguments::help); + m_parser->add_argument("mcmc-output") + .help("MCMC chain root file.") + .metavar("MCMC_CHAIN") + .required(); + m_parser->add_argument("config") + .help("Config file.") + .metavar("CONFIG") + .required(); + return m_parser; + } + + int DiagMCMCPlugin::run() { + SetMaCh3LoggerFormat(); + MACH3LOG_INFO("Producing single fit output"); + std::string inputFile = m_parser->get<std::string>("mcmc-chain"); + std::string config = m_parser->get<std::string>("config"); + + MACH3LOG_INFO("File for study: {}", inputFile); + + YAML::Node Settings = M3OpenConfig(config); + + // Make the processor + auto Processor = std::make_unique<MCMCProcessor>(inputFile); + Processor->SetOutputSuffix("_MCMC_Diag"); + //KS:Turn off plotting detector and some other setting + Processor->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedTypes"], {})); + Processor->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedNames"], {})); + Processor->SetExcludedGroups(GetFromManager<std::vector<std::string>>(Settings["DiagMCMC"]["ExcludedGroups"], {})); + Processor->SetPlotRelativeToPrior(GetFromManager<bool>(Settings["DiagMCMC"]["PlotRelativeToPrior"], false)); + //KS: Use 20 batches for batched means + Processor->SetnBatches(GetFromManager<int>(Settings["DiagMCMC"]["nBatches"], 20)); + Processor->SetnLags(GetFromManager<int>(Settings["DiagMCMC"]["nLags"], 25000)); + Processor->SetPrintToPDF(GetFromManager<bool>(Settings["PrintToPDF"], true)); + Processor->Initialise(); + if(Settings["MaxEntries"]) { + Processor->SetEntries(Get<int>(Settings["MaxEntries"], __FILE__, __LINE__)); + } + //KS: finally call main method + Processor->DiagMCMC(); + + return 0; + } + +}; \ No newline at end of file diff --git a/Diagnostics/DiagMCMCPlugin.hpp b/Diagnostics/DiagMCMCPlugin.hpp new file mode 100644 index 000000000..9196d8089 --- /dev/null +++ b/Diagnostics/DiagMCMCPlugin.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "api/plugin.hpp" + +namespace mach3{ + + class DiagMCMCPlugin: public IPlugin{ + + public: + virtual ~DiagMCMCPlugin(){ + if (m_parser) { delete m_parser; } + } + MaCh3ArgumentParser* get_parser() override; + int run() override; + + + private: + MaCh3ArgumentParser* m_parser; + }; +}; diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index de2ad19ae..b2e7c255e 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -1,3 +1,4 @@ +#include "Manager/MaCh3Logger.h" #include "Diagnostics/ProcessMCMCPlugin.hpp" int main(int argc, char const* argv[]){ diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp index baefe284c..2f42d5068 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -1,17 +1,18 @@ //MaCh3 includes -#include "api/plugin.hpp" #include "Fitters/OscProcessor.h" #include "Manager/Manager.h" #include "Diagnostics/ProcessMCMCPlugin.hpp" +// Not needed for Core plugins as not dynamically loaded +// extern "C" mach3::IPlugin* create_plugin() { +// return new mach3::ProcessMCMCPlugin(); +// } -extern "C" mach3::IPlugin* create_plugin() { - return new mach3::ProcessMCMCPlugin(); -} - -extern "C" void destroy_plugin(mach3::IPlugin* p) { - delete p; -} +// extern "C" void destroy_plugin(mach3::IPlugin* p) { +// delete p; +// } +// if was a dynamic plugin, could now use the convienience macro to generate these +// MACH3_REGISTER_PLUGIN(mach3::ProcessMCMCPlugin) namespace mach3{ diff --git a/Diagnostics/ProcessMCMCPlugin.hpp b/Diagnostics/ProcessMCMCPlugin.hpp index f0e84392a..e26f0fcfe 100644 --- a/Diagnostics/ProcessMCMCPlugin.hpp +++ b/Diagnostics/ProcessMCMCPlugin.hpp @@ -1,15 +1,12 @@ //MaCh3 includes #pragma once +// yaml Includes +#include "yaml-cpp/yaml.h" +#include "Fitters/MCMCProcessor.h" #include "api/plugin.hpp" -#include "Fitters/OscProcessor.h" -#include "Manager/Manager.h" - -using argparse::ArgumentParser; namespace mach3{ - - /// @file ProcessMCMC.cpp /// @brief Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implantation of methods is in MCMCProcessor /// @ingroup MaCh3DiagnosticProcessing @@ -52,8 +49,3 @@ namespace mach3{ MaCh3ArgumentParser* m_parser; }; }; - -extern "C" { - typedef mach3::IPlugin* (*create_plugin_t)(); - typedef void (*destroy_plugin_t)(mach3::IPlugin*); -} diff --git a/api/plugin.hpp b/api/plugin.hpp index fb43d5149..df9fed95a 100644 --- a/api/plugin.hpp +++ b/api/plugin.hpp @@ -13,8 +13,17 @@ namespace mach3 { }; -// Factory function signature +// Factory function typedefs extern "C" { typedef mach3::IPlugin* (*create_plugin_t)(); typedef void (*destroy_plugin_t)(mach3::IPlugin*); } + +#define MACH3_REGISTER_PLUGIN(PluginClass) \ +static_assert(std::is_base_of<mach3::IPlugin, PluginClass>::value, "PluginClass must derive from mach3::IPlugin"); \ +extern "C" mach3::IPlugin* create_plugin() { \ + return new PluginClass(); \ +} \ +extern "C" void destroy_plugin(mach3::IPlugin* p) { \ + delete p; \ +} diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index b0064fd6e..c35139314 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(mach3 mach3.cpp) - target_link_libraries(mach3 MaCh3::All MaCh3::Plugins) + target_link_libraries(mach3 PRIVATE MaCh3::CorePlugins MaCh3::All) install(TARGETS mach3 DESTINATION bin) diff --git a/cli/mach3.cpp b/cli/mach3.cpp index ad112cca6..31c84256f 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -5,6 +5,7 @@ #include <vector> #include "api/plugin.hpp" #include "Diagnostics/ProcessMCMCPlugin.hpp" +#include "Diagnostics/DiagMCMCPlugin.hpp" using namespace std; @@ -175,8 +176,10 @@ int main(int argc, char *argv[]) { mach3::MaCh3Program program; mach3::ProcessMCMCPlugin proc; + mach3::DiagMCMCPlugin diag; program.add_core_plugin(proc); + program.add_core_plugin(diag); program.load_dynamic_plugins(); try { From c74285cc35ea8ec8ea23c1d20f49675b20720a99 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 28 Apr 2026 18:52:53 +0000 Subject: [PATCH 04/26] fixup --- Diagnostics/DiagMCMC.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index d211d9efb..c4bb77323 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -3,13 +3,13 @@ int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); - MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + MACH3LOG_WARN(" : you can use 'mach3 diag' as a direct replacement instead."); argv[0] = "diag"; mach3::DiagMCMCPlugin proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); - MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); + MACH3LOG_WARN(" : you can use 'mach3 diag' as a direct replacement instead."); return 0; } From 9949b938ebcfa823a4df8a6a902bdf319b3a768e Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Fri, 1 May 2026 11:00:08 +0000 Subject: [PATCH 05/26] Implement the GetPenaltyTerm binary --- Diagnostics/CMakeLists.txt | 1 + Diagnostics/GetPenaltyTerm.cpp | 409 +-------------------------- Diagnostics/GetPenaltyTermPlugin.cpp | 406 ++++++++++++++++++++++++++ Diagnostics/GetPenaltyTermPlugin.hpp | 34 +++ Diagnostics/ProcessMCMCPlugin.cpp | 30 +- cli/mach3.cpp | 3 + 6 files changed, 473 insertions(+), 410 deletions(-) create mode 100644 Diagnostics/GetPenaltyTermPlugin.cpp create mode 100644 Diagnostics/GetPenaltyTermPlugin.hpp diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index a5d6aad40..04def5804 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(CorePlugins STATIC ProcessMCMCPlugin.cpp DiagMCMCPlugin.cpp + GetPenaltyTermPlugin.cpp ) target_link_libraries(CorePlugins PRIVATE MaCh3::All) diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index a971a1003..0ef8ec527 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -1,396 +1,15 @@ -// MaCh3 includes -#include "Manager/Manager.h" -#include "Samples/SampleStructs.h" -#include "Samples/HistogramUtils.h" -#include "Parameters/ParameterHandlerUtils.h" - -_MaCh3_Safe_Include_Start_ //{ -// ROOT includes -#include "TFile.h" -#include "TBranch.h" -#include "TCanvas.h" -#include "TLine.h" -#include "TLegend.h" -#include "TString.h" -#include "TStyle.h" -#include "TMatrixT.h" -#include "TMatrixDSym.h" -#include "TVectorD.h" -#include "TObject.h" -#include "TChain.h" -#include "TH1.h" -#include "TColor.h" -#include "TObjString.h" -#include "TROOT.h" -_MaCh3_Safe_Include_End_ //} - -/// @file GetPenaltyTerm.cpp -/// @brief KS: This file contains the implementation of the function to extract specific penalty terms from systematic chains. -/// -/// This script is designed to retrieve penalty terms from various sources, such as flux and cross-section systematic chains. -/// Since flux and cross-section uncertainties are handled systematically, the penalty term cannot be taken directly from the chain. -/// @todo KS: This should really be moved to MCMC Processor -/// -/// @ingroup MaCh3DiagnosticProcessing -/// -/// @author Kamil Skwarczynski - -void ReadCovFile(const std::string& inputFile, - std::vector <double>& Prior, - std::vector <bool>& isFlat, - std::vector<std::string>& ParamNames, - std::vector<std::vector<double>>& invCovMatrix, - int& nParams) -{ - // Now read the MCMC file - TFile *TempFile = M3::Open(inputFile, "open", __FILE__, __LINE__); - - // Get the matrix - TDirectory* CovarianceFolder = TempFile->Get<TDirectory>("CovarianceFolder"); - TMatrixDSym *CovMatrix = M3::GetCovMatrixFromChain(CovarianceFolder); - - // Get the settings for the MCMC - TMacro *Config = TempFile->Get<TMacro>("MaCh3_Config"); - if (Config == nullptr) { - MACH3LOG_ERROR("Didn't find MaCh3_Config tree in MCMC file! {}", inputFile); - TempFile->ls(); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - - YAML::Node Settings = TMacroToYAML(*Config); - - //CW: Get the Covariance matrix - std::vector<std::string> CovPos = GetFromManager<std::vector<std::string>>(Settings["General"]["Systematics"]["XsecCovFile"], {"none"}); - if(CovPos.back() == "none") - { - MACH3LOG_WARN("Couldn't find Cov branch in output"); - M3::Utils::PrintConfig(Settings); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - - //KS:Most inputs are in ${MACH3}/inputs/blarb.root - if (std::getenv("MACH3") != nullptr) { - MACH3LOG_INFO("Found MACH3 environment variable: {}", std::getenv("MACH3")); - for(unsigned int i = 0; i < CovPos.size(); i++) - CovPos[i].insert(0, std::string(std::getenv("MACH3"))+"/"); - } - - YAML::Node CovFile; - CovFile["Systematics"] = YAML::Node(YAML::NodeType::Sequence); - for(unsigned int i = 0; i < CovPos.size(); i++) - { - YAML::Node YAMLDocTemp = M3OpenConfig(CovPos[i]); - for (const auto& item : YAMLDocTemp["Systematics"]) { - CovFile["Systematics"].push_back(item); - } - } - - nParams = CovMatrix->GetNrows(); - - auto systematics = CovFile["Systematics"]; - for (auto it = systematics.begin(); it != systematics.end(); ++it) - { - auto const ¶m = *it; - - ParamNames.push_back(param["Systematic"]["Names"]["FancyName"].as<std::string>()); - Prior.push_back( param["Systematic"]["ParameterValues"]["PreFitValue"].as<double>() ); - - bool flat = false; - if (param["Systematic"]["FlatPrior"]) { flat = param["Systematic"]["FlatPrior"].as<bool>(); } - isFlat.push_back( flat ); - } - - CovMatrix->Invert(); - //KS: Let's use double as it is faster than TMatrix - invCovMatrix.resize(nParams, std::vector<double>(nParams, -999)); - - #ifdef MULTITHREAD - #pragma omp parallel for collapse(2) - #endif - for (int i = 0; i < nParams; i++) - { - for (int j = 0; j < nParams; ++j) - { - invCovMatrix[i][j] = (*CovMatrix)(i,j); - } - } - - TempFile->Close(); - delete TempFile; -} - -void LoadSettings(YAML::Node& Settings, - std::vector<std::string>& SetsNames, - std::vector<std::string>& FancyTitle, - std::vector<std::vector<bool>>& isRelevantParam, - const std::vector<std::string>& ParamNames, - const int nParams) -{ - std::vector<std::string> node = Settings["GetPenaltyTerm"]["PenaltySets"].as<std::vector<std::string>>(); - std::vector<std::vector<std::string>> RemoveNames; - std::vector<bool> Exclude; - - for (unsigned int i = 0; i < node.size(); i++) - { - std::string ParName = node[i]; - SetsNames.push_back(ParName); - - const auto& Set = Settings["GetPenaltyTerm"][ParName]; - - RemoveNames.push_back(Set[0].as<std::vector<std::string>>()); - Exclude.push_back(Set[1].as<bool>()); - FancyTitle.push_back(Set[2].as<std::string>()); - } - - const int NSets = int(SetsNames.size()); - - isRelevantParam.resize(NSets); - //Loop over sets in the config - for(int i = 0; i < NSets; i++) - { - isRelevantParam[i].resize(nParams); - int counter = 0; - //Loop over parameters in the Covariance object - for (int j = 0; j < nParams; j++) - { - isRelevantParam[i][j] = false; - - //KS: Here we loop over all names and if parameters wasn't matched then we set it is relevant. - if(Exclude[i]) - { - bool found = false; - for (unsigned int k = 0; k < RemoveNames[i].size(); k++) - { - if (ParamNames[j].rfind(RemoveNames[i][k], 0) == 0) - { - found = true; - } - } - if(!found) - { - isRelevantParam[i][j] = true; - counter++; - } - } - //KS: Here is much simpler, if parameter matched then it is relevant - else - { - for (unsigned int k = 0; k < RemoveNames[i].size(); k++) - { - if (ParamNames[j].rfind(RemoveNames[i][k], 0) == 0) - { - isRelevantParam[i][j] = true; - counter++; - break; - } - } - } - } - MACH3LOG_INFO(" Found {} params for set {}", counter, SetsNames[i]); - } -} - -void GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) -{ - auto canvas = std::make_unique<TCanvas>("canvas", "canvas", 0, 0, 1024, 1024); - canvas->SetGrid(); - canvas->SetTickx(); - canvas->SetTicky(); - - canvas->SetBottomMargin(0.1f); - canvas->SetTopMargin(0.02f); - canvas->SetRightMargin(0.08f); - canvas->SetLeftMargin(0.15f); - - gStyle->SetOptTitle(0); - gStyle->SetOptStat(0); - gStyle->SetPalette(51); - - std::vector <double> Prior; - std::vector <bool> isFlat; - std::vector<std::string> ParamNames; - std::vector<std::vector<double>> invCovMatrix; - int nParams; - ReadCovFile(inputFile, Prior, isFlat, ParamNames, invCovMatrix, nParams); - - std::vector<TString> BranchNames; - - // Open the Chain - TChain* Chain = new TChain("posteriors",""); - Chain->Add(inputFile.c_str()); - - // Get the list of branches - TObjArray* brlis = Chain->GetListOfBranches(); - - // Get the number of branches - int nBranches = brlis->GetEntries(); - int RelevantBranches = 0; - for (int i = 0; i < nBranches; i++) - { - // Get the TBranch and its name - TBranch* br = static_cast<TBranch*>(brlis->At(i)); - if(!br){ - MACH3LOG_ERROR("Invalid branch at position {}", i); - throw MaCh3Exception(__FILE__,__LINE__); - } - TString bname = br->GetName(); - - // If we're on beam systematics - if(bname.BeginsWith("param_")) - { - BranchNames.push_back(bname); - RelevantBranches++; - } - } - - // Set all the branches to off - Chain->SetBranchStatus("*", false); - - std::vector<double> fParProp(RelevantBranches); - // Turn on the branches which we want for parameters - for (int i = 0; i < RelevantBranches; ++i) - { - Chain->SetBranchStatus(BranchNames[i].Data(), true); - Chain->SetBranchAddress(BranchNames[i].Data(), &fParProp[i]); - } - - YAML::Node Settings = M3OpenConfig(configFile); - std::vector<std::string> SetsNames; - std::vector<std::string> FancyTitle; - std::vector<std::vector<bool>> isRelevantParam; - - LoadSettings(Settings, SetsNames, FancyTitle, isRelevantParam, ParamNames, nParams); - - const int NSets = int(SetsNames.size()); - int AllEvents = int(Chain->GetEntries()); - std::vector<std::unique_ptr<TH1D>> hLogL(NSets); - for (int i = 0; i < NSets; i++) { - std::string NameTemp = "LogL_" + SetsNames[i]; - hLogL[i] = std::make_unique<TH1D>(NameTemp.c_str(), NameTemp.c_str(), AllEvents, 0, AllEvents); - hLogL[i]->SetLineColor(kBlue); - } - std::vector<double> logL(NSets, 0.0); - for(int n = 0; n < AllEvents; ++n) - { - if(n%10000 == 0) M3::Utils::PrintProgressBar(n, AllEvents); - - Chain->GetEntry(n); - - for(int k = 0; k < NSets; ++k) logL[k] = 0.; -#ifdef MULTITHREAD - // The per-thread array - double *logL_private = nullptr; - - // Declare the omp parallel region - // The parallel region needs to stretch beyond the for loop! - #pragma omp parallel private(logL_private) - { - logL_private = new double[NSets]; - for(int k = 0; k < NSets; ++k) logL_private[k] = 0.; - - #pragma omp for - for (int i = 0; i < nParams; i++) - { - for (int j = 0; j <= i; ++j) - { - //check if flat prior - if (!isFlat[i] && !isFlat[j]) - { - for(int k = 0; k < NSets; ++k) - { - //Check if parameter is relevant for this set - if (isRelevantParam[k][i] && isRelevantParam[k][j]) - { - //KS: Since matrix is symmetric we can calculate non diagonal elements only once and multiply by 2, can bring up to factor speed decrease. - int scale = 1; - if(i != j) scale = 2; - logL_private[k] += scale * 0.5*(fParProp[i] - Prior[i])*(fParProp[j] - Prior[j])*invCovMatrix[i][j]; - } - } - } - } - } - // Now we can write the individual arrays from each thread to the main array - for(int k = 0; k < NSets; ++k) - { - #pragma omp atomic - logL[k] += logL_private[k]; - } - //Delete private arrays - delete[] logL_private; - }//End omp range - -#else - for (int i = 0; i < nParams; i++) - { - for (int j = 0; j <= i; ++j) - { - //check if flat prior - if (!isFlat[i] && !isFlat[j]) - { - for(int k = 0; k < NSets; ++k) - { - //Check if parameter is relevant for this set - if (isRelevantParam[k][i] && isRelevantParam[k][j]) - { - //KS: Since matrix is symmetric we can calculate non diagonal elements only once and multiply by 2, can bring up to factor speed decrease. - int scale = 1; - if(i != j) scale = 2; - logL[k] += scale * 0.5*(fParProp[i] - Prior[i])*(fParProp[j] - Prior[j])*invCovMatrix[i][j]; - } - } - } - } - } -#endif // end MULTITHREAD - for(int k = 0; k < NSets; ++k) - { - hLogL[k]->SetBinContent(n, logL[k]); - } - }//End loop over steps - - // Directory for posteriors - std::string OutputName = inputFile + "_PenaltyTerm" +".root"; - TFile *OutputFile = M3::Open(OutputName, "recreate", __FILE__, __LINE__); - TDirectory *PenaltyTermDir = OutputFile->mkdir("PenaltyTerm"); - - canvas->Print(Form("%s_PenaltyTerm.pdf[",inputFile.c_str()), "pdf"); - for(int i = 0; i < NSets; i++) - { - const double Maximum = hLogL[i]->GetMaximum(); - hLogL[i]->GetYaxis()->SetRangeUser(0., Maximum*1.2); - hLogL[i]->SetTitle(FancyTitle[i].c_str()); - hLogL[i]->GetXaxis()->SetTitle("Step"); - hLogL[i]->GetYaxis()->SetTitle(FancyTitle[i].c_str()); - hLogL[i]->GetYaxis()->SetTitleOffset(1.4f); - - hLogL[i]->Draw(""); - - PenaltyTermDir->cd(); - hLogL[i]->Write(); - - canvas->Print(Form("%s_PenaltyTerm.pdf",inputFile.c_str()), "pdf"); - } - canvas->Print(Form("%s_PenaltyTerm.pdf]",inputFile.c_str()), "pdf"); - delete Chain; - - OutputFile->Close(); - delete OutputFile; -} - -int main(int argc, char *argv[]) -{ - SetMaCh3LoggerFormat(); - M3::Utils::MaCh3Welcome(); - if (argc != 3 ) - { - MACH3LOG_WARN("Something went wrong "); - MACH3LOG_WARN("{} root_file_to_analyse.root", argv[1]); - throw MaCh3Exception(__FILE__ , __LINE__ ); - } - std::string filename = argv[1]; - std::string config = argv[2]; - GetPenaltyTerm(filename, config); - - return 0; +#include "Manager/MaCh3Logger.h" +#include "Diagnostics/GetPenaltyTermPlugin.hpp" + +int main(int argc, char const* argv[]){ + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 penterm' as a direct replacement instead."); + argv[0] = "penterm"; + mach3::GetPenaltyTermPlugin proc; + ArgumentParser* parser = proc.get_parser(); + parser->parse_args(argc, argv); + proc.run(); + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 penterm' as a direct replacement instead."); + return 0; } diff --git a/Diagnostics/GetPenaltyTermPlugin.cpp b/Diagnostics/GetPenaltyTermPlugin.cpp new file mode 100644 index 000000000..0122fc74e --- /dev/null +++ b/Diagnostics/GetPenaltyTermPlugin.cpp @@ -0,0 +1,406 @@ +// MaCh3 includes +#include "Manager/Manager.h" +#include "Samples/SampleStructs.h" +#include "Samples/HistogramUtils.h" +#include "Parameters/ParameterHandlerUtils.h" +#include "Diagnostics/GetPenaltyTermPlugin.hpp" + +_MaCh3_Safe_Include_Start_ //{ +// ROOT includes +#include "TFile.h" +#include "TBranch.h" +#include "TCanvas.h" +#include "TLine.h" +#include "TLegend.h" +#include "TString.h" +#include "TStyle.h" +#include "TMatrixT.h" +#include "TMatrixDSym.h" +#include "TVectorD.h" +#include "TObject.h" +#include "TChain.h" +#include "TH1.h" +#include "TColor.h" +#include "TObjString.h" +#include "TROOT.h" +_MaCh3_Safe_Include_End_ //} + +/// @file GetPenaltyTerm.cpp +/// @brief KS: This file contains the implementation of the function to extract specific penalty terms from systematic chains. +/// +/// This script is designed to retrieve penalty terms from various sources, such as flux and cross-section systematic chains. +/// Since flux and cross-section uncertainties are handled systematically, the penalty term cannot be taken directly from the chain. +/// @todo KS: This should really be moved to MCMC Processor +/// +/// @ingroup MaCh3DiagnosticProcessing +/// +/// @author Kamil Skwarczynski +namespace mach3{ + + MaCh3ArgumentParser* GetPenaltyTermPlugin::get_parser(){ + m_parser = new MaCh3ArgumentParser("penterm", "1.0", argparse::default_arguments::help); + m_parser->add_argument("inputfile") + .help("Root file to analyse.") + .metavar("INPUTFILE") + .required(); + m_parser->add_argument("config") + .help("Config file.") + .metavar("CONFIG") + .required(); + return m_parser; + } + + int GetPenaltyTermPlugin::run()//int argc, char *argv[]) + { + SetMaCh3LoggerFormat(); + M3::Utils::MaCh3Welcome(); + std::string inputFile = m_parser->get<std::string>("inputfile"); + std::string config = m_parser->get<std::string>("config"); + this->GetPenaltyTerm(inputFile, config); + + return 0; + } + + void GetPenaltyTermPlugin::ReadCovFile(const std::string& inputFile, + std::vector <double>& Prior, + std::vector <bool>& isFlat, + std::vector<std::string>& ParamNames, + std::vector<std::vector<double>>& invCovMatrix, + int& nParams) + { + // Now read the MCMC file + TFile *TempFile = M3::Open(inputFile, "open", __FILE__, __LINE__); + + // Get the matrix + TDirectory* CovarianceFolder = TempFile->Get<TDirectory>("CovarianceFolder"); + TMatrixDSym *CovMatrix = M3::GetCovMatrixFromChain(CovarianceFolder); + + // Get the settings for the MCMC + TMacro *Config = TempFile->Get<TMacro>("MaCh3_Config"); + if (Config == nullptr) { + MACH3LOG_ERROR("Didn't find MaCh3_Config tree in MCMC file! {}", inputFile); + TempFile->ls(); + throw MaCh3Exception(__FILE__ , __LINE__ ); + } + + YAML::Node Settings = TMacroToYAML(*Config); + + //CW: Get the Covariance matrix + std::vector<std::string> CovPos = GetFromManager<std::vector<std::string>>(Settings["General"]["Systematics"]["XsecCovFile"], {"none"}); + if(CovPos.back() == "none") + { + MACH3LOG_WARN("Couldn't find Cov branch in output"); + M3::Utils::PrintConfig(Settings); + throw MaCh3Exception(__FILE__ , __LINE__ ); + } + + //KS:Most inputs are in ${MACH3}/inputs/blarb.root + if (std::getenv("MACH3") != nullptr) { + MACH3LOG_INFO("Found MACH3 environment variable: {}", std::getenv("MACH3")); + for(unsigned int i = 0; i < CovPos.size(); i++) + CovPos[i].insert(0, std::string(std::getenv("MACH3"))+"/"); + } + + YAML::Node CovFile; + CovFile["Systematics"] = YAML::Node(YAML::NodeType::Sequence); + for(unsigned int i = 0; i < CovPos.size(); i++) + { + YAML::Node YAMLDocTemp = M3OpenConfig(CovPos[i]); + for (const auto& item : YAMLDocTemp["Systematics"]) { + CovFile["Systematics"].push_back(item); + } + } + + nParams = CovMatrix->GetNrows(); + + auto systematics = CovFile["Systematics"]; + for (auto it = systematics.begin(); it != systematics.end(); ++it) + { + auto const ¶m = *it; + + ParamNames.push_back(param["Systematic"]["Names"]["FancyName"].as<std::string>()); + Prior.push_back( param["Systematic"]["ParameterValues"]["PreFitValue"].as<double>() ); + + bool flat = false; + if (param["Systematic"]["FlatPrior"]) { flat = param["Systematic"]["FlatPrior"].as<bool>(); } + isFlat.push_back( flat ); + } + + CovMatrix->Invert(); + //KS: Let's use double as it is faster than TMatrix + invCovMatrix.resize(nParams, std::vector<double>(nParams, -999)); + + #ifdef MULTITHREAD + #pragma omp parallel for collapse(2) + #endif + for (int i = 0; i < nParams; i++) + { + for (int j = 0; j < nParams; ++j) + { + invCovMatrix[i][j] = (*CovMatrix)(i,j); + } + } + + TempFile->Close(); + delete TempFile; + } + + void GetPenaltyTermPlugin::LoadSettings(YAML::Node& Settings, + std::vector<std::string>& SetsNames, + std::vector<std::string>& FancyTitle, + std::vector<std::vector<bool>>& isRelevantParam, + const std::vector<std::string>& ParamNames, + const int nParams) + { + std::vector<std::string> node = Settings["GetPenaltyTerm"]["PenaltySets"].as<std::vector<std::string>>(); + std::vector<std::vector<std::string>> RemoveNames; + std::vector<bool> Exclude; + + for (unsigned int i = 0; i < node.size(); i++) + { + std::string ParName = node[i]; + SetsNames.push_back(ParName); + + const auto& Set = Settings["GetPenaltyTerm"][ParName]; + + RemoveNames.push_back(Set[0].as<std::vector<std::string>>()); + Exclude.push_back(Set[1].as<bool>()); + FancyTitle.push_back(Set[2].as<std::string>()); + } + + const int NSets = int(SetsNames.size()); + + isRelevantParam.resize(NSets); + //Loop over sets in the config + for(int i = 0; i < NSets; i++) + { + isRelevantParam[i].resize(nParams); + int counter = 0; + //Loop over parameters in the Covariance object + for (int j = 0; j < nParams; j++) + { + isRelevantParam[i][j] = false; + + //KS: Here we loop over all names and if parameters wasn't matched then we set it is relevant. + if(Exclude[i]) + { + bool found = false; + for (unsigned int k = 0; k < RemoveNames[i].size(); k++) + { + if (ParamNames[j].rfind(RemoveNames[i][k], 0) == 0) + { + found = true; + } + } + if(!found) + { + isRelevantParam[i][j] = true; + counter++; + } + } + //KS: Here is much simpler, if parameter matched then it is relevant + else + { + for (unsigned int k = 0; k < RemoveNames[i].size(); k++) + { + if (ParamNames[j].rfind(RemoveNames[i][k], 0) == 0) + { + isRelevantParam[i][j] = true; + counter++; + break; + } + } + } + } + MACH3LOG_INFO(" Found {} params for set {}", counter, SetsNames[i]); + } + } + + void GetPenaltyTermPlugin::GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) + { + auto canvas = std::make_unique<TCanvas>("canvas", "canvas", 0, 0, 1024, 1024); + canvas->SetGrid(); + canvas->SetTickx(); + canvas->SetTicky(); + + canvas->SetBottomMargin(0.1f); + canvas->SetTopMargin(0.02f); + canvas->SetRightMargin(0.08f); + canvas->SetLeftMargin(0.15f); + + gStyle->SetOptTitle(0); + gStyle->SetOptStat(0); + gStyle->SetPalette(51); + + std::vector <double> Prior; + std::vector <bool> isFlat; + std::vector<std::string> ParamNames; + std::vector<std::vector<double>> invCovMatrix; + int nParams; + this->ReadCovFile(inputFile, Prior, isFlat, ParamNames, invCovMatrix, nParams); + + std::vector<TString> BranchNames; + + // Open the Chain + TChain* Chain = new TChain("posteriors",""); + Chain->Add(inputFile.c_str()); + + // Get the list of branches + TObjArray* brlis = Chain->GetListOfBranches(); + + // Get the number of branches + int nBranches = brlis->GetEntries(); + int RelevantBranches = 0; + for (int i = 0; i < nBranches; i++) + { + // Get the TBranch and its name + TBranch* br = static_cast<TBranch*>(brlis->At(i)); + if(!br){ + MACH3LOG_ERROR("Invalid branch at position {}", i); + throw MaCh3Exception(__FILE__,__LINE__); + } + TString bname = br->GetName(); + + // If we're on beam systematics + if(bname.BeginsWith("param_")) + { + BranchNames.push_back(bname); + RelevantBranches++; + } + } + + // Set all the branches to off + Chain->SetBranchStatus("*", false); + + std::vector<double> fParProp(RelevantBranches); + // Turn on the branches which we want for parameters + for (int i = 0; i < RelevantBranches; ++i) + { + Chain->SetBranchStatus(BranchNames[i].Data(), true); + Chain->SetBranchAddress(BranchNames[i].Data(), &fParProp[i]); + } + + YAML::Node Settings = M3OpenConfig(configFile); + std::vector<std::string> SetsNames; + std::vector<std::string> FancyTitle; + std::vector<std::vector<bool>> isRelevantParam; + + this->LoadSettings(Settings, SetsNames, FancyTitle, isRelevantParam, ParamNames, nParams); + + const int NSets = int(SetsNames.size()); + int AllEvents = int(Chain->GetEntries()); + std::vector<std::unique_ptr<TH1D>> hLogL(NSets); + for (int i = 0; i < NSets; i++) { + std::string NameTemp = "LogL_" + SetsNames[i]; + hLogL[i] = std::make_unique<TH1D>(NameTemp.c_str(), NameTemp.c_str(), AllEvents, 0, AllEvents); + hLogL[i]->SetLineColor(kBlue); + } + std::vector<double> logL(NSets, 0.0); + for(int n = 0; n < AllEvents; ++n) + { + if(n%10000 == 0) M3::Utils::PrintProgressBar(n, AllEvents); + + Chain->GetEntry(n); + + for(int k = 0; k < NSets; ++k) logL[k] = 0.; + #ifdef MULTITHREAD + // The per-thread array + double *logL_private = nullptr; + + // Declare the omp parallel region + // The parallel region needs to stretch beyond the for loop! + #pragma omp parallel private(logL_private) + { + logL_private = new double[NSets]; + for(int k = 0; k < NSets; ++k) logL_private[k] = 0.; + + #pragma omp for + for (int i = 0; i < nParams; i++) + { + for (int j = 0; j <= i; ++j) + { + //check if flat prior + if (!isFlat[i] && !isFlat[j]) + { + for(int k = 0; k < NSets; ++k) + { + //Check if parameter is relevant for this set + if (isRelevantParam[k][i] && isRelevantParam[k][j]) + { + //KS: Since matrix is symmetric we can calculate non diagonal elements only once and multiply by 2, can bring up to factor speed decrease. + int scale = 1; + if(i != j) scale = 2; + logL_private[k] += scale * 0.5*(fParProp[i] - Prior[i])*(fParProp[j] - Prior[j])*invCovMatrix[i][j]; + } + } + } + } + } + // Now we can write the individual arrays from each thread to the main array + for(int k = 0; k < NSets; ++k) + { + #pragma omp atomic + logL[k] += logL_private[k]; + } + //Delete private arrays + delete[] logL_private; + }//End omp range + + #else + for (int i = 0; i < nParams; i++) + { + for (int j = 0; j <= i; ++j) + { + //check if flat prior + if (!isFlat[i] && !isFlat[j]) + { + for(int k = 0; k < NSets; ++k) + { + //Check if parameter is relevant for this set + if (isRelevantParam[k][i] && isRelevantParam[k][j]) + { + //KS: Since matrix is symmetric we can calculate non diagonal elements only once and multiply by 2, can bring up to factor speed decrease. + int scale = 1; + if(i != j) scale = 2; + logL[k] += scale * 0.5*(fParProp[i] - Prior[i])*(fParProp[j] - Prior[j])*invCovMatrix[i][j]; + } + } + } + } + } + #endif // end MULTITHREAD + for(int k = 0; k < NSets; ++k) + { + hLogL[k]->SetBinContent(n, logL[k]); + } + }//End loop over steps + + // Directory for posteriors + std::string OutputName = inputFile + "_PenaltyTerm" +".root"; + TFile *OutputFile = M3::Open(OutputName, "recreate", __FILE__, __LINE__); + TDirectory *PenaltyTermDir = OutputFile->mkdir("PenaltyTerm"); + + canvas->Print(Form("%s_PenaltyTerm.pdf[",inputFile.c_str()), "pdf"); + for(int i = 0; i < NSets; i++) + { + const double Maximum = hLogL[i]->GetMaximum(); + hLogL[i]->GetYaxis()->SetRangeUser(0., Maximum*1.2); + hLogL[i]->SetTitle(FancyTitle[i].c_str()); + hLogL[i]->GetXaxis()->SetTitle("Step"); + hLogL[i]->GetYaxis()->SetTitle(FancyTitle[i].c_str()); + hLogL[i]->GetYaxis()->SetTitleOffset(1.4f); + + hLogL[i]->Draw(""); + + PenaltyTermDir->cd(); + hLogL[i]->Write(); + + canvas->Print(Form("%s_PenaltyTerm.pdf",inputFile.c_str()), "pdf"); + } + canvas->Print(Form("%s_PenaltyTerm.pdf]",inputFile.c_str()), "pdf"); + delete Chain; + + OutputFile->Close(); + delete OutputFile; + } +}; \ No newline at end of file diff --git a/Diagnostics/GetPenaltyTermPlugin.hpp b/Diagnostics/GetPenaltyTermPlugin.hpp new file mode 100644 index 000000000..a44e9c598 --- /dev/null +++ b/Diagnostics/GetPenaltyTermPlugin.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "yaml-cpp/yaml.h" +#include "api/plugin.hpp" + +namespace mach3{ + + class GetPenaltyTermPlugin: public IPlugin{ + + public: + virtual ~GetPenaltyTermPlugin(){ + if (m_parser) { delete m_parser; } + } + MaCh3ArgumentParser* get_parser() override; + int run() override; + + private: + void ReadCovFile(const std::string& inputFile, + std::vector <double>& Prior, + std::vector <bool>& isFlat, + std::vector<std::string>& ParamNames, + std::vector<std::vector<double>>& invCovMatrix, + int& nParams); + void LoadSettings(YAML::Node& Settings, + std::vector<std::string>& SetsNames, + std::vector<std::string>& FancyTitle, + std::vector<std::vector<bool>>& isRelevantParam, + const std::vector<std::string>& ParamNames, + const int nParams); + void GetPenaltyTerm(const std::string& inputFile, const std::string& configFile); + + private: + MaCh3ArgumentParser* m_parser; + }; +}; diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp index 2f42d5068..4bc9459b6 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -86,7 +86,7 @@ namespace mach3{ { MACH3LOG_INFO("Producing single fit output"); std::string filename = files[0]; - ProcessMCMC(filename); + this->ProcessMCMC(filename); } // If we want to compare two or more fits (e.g. binning changes or introducing new params/priors) else if (files.size() > 1) @@ -104,7 +104,7 @@ namespace mach3{ TitleNames.push_back("THREE"); } - MultipleProcessMCMC(); + this->MultipleProcessMCMC(); } return 0; } @@ -196,7 +196,7 @@ namespace mach3{ } } // Make the postfit - Processor->MakePostfit(GetCustomBinning(Settings)); + Processor->MakePostfit(this->GetCustomBinning(Settings)); Processor->DrawPostfit(); //KS: Should set via config whether you want below or not if(GetFromManager<bool>(Settings["MakeCredibleIntervals"], true)) { @@ -204,10 +204,10 @@ namespace mach3{ GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), GetFromManager<bool>(Settings["CredibleInSigmas"], false)); } - if(GetFromManager<bool>(Settings["CalcBayesFactor"], true)) CalcBayesFactor(Processor.get()); - if(GetFromManager<bool>(Settings["CalcSavageDickey"], true)) CalcSavageDickey(Processor.get()); - if(GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) CalcBipolarPlot(Processor.get()); - if(GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) CalcParameterEvolution(Processor.get()); + if(GetFromManager<bool>(Settings["CalcBayesFactor"], true)) this->CalcBayesFactor(Processor.get()); + if(GetFromManager<bool>(Settings["CalcSavageDickey"], true)) this->CalcSavageDickey(Processor.get()); + if(GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) this->CalcBipolarPlot(Processor.get()); + if(GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) this->CalcParameterEvolution(Processor.get()); if(PlotCorr) { @@ -233,10 +233,10 @@ namespace mach3{ GetFromManager<bool>(Settings["Draw2DPosterior"], true), GetFromManager<bool>(Settings["DrawBestFit"], true)); } - if(GetFromManager<bool>(Settings["GetTrianglePlot"], true)) GetTrianglePlot(Processor.get()); + if(GetFromManager<bool>(Settings["GetTrianglePlot"], true)) this->GetTrianglePlot(Processor.get()); //KS: When creating covariance matrix longest time is spend on caching every step, since we already cached we can run some fancy covariance stability diagnostic - if(GetFromManager<bool>(Settings["DiagnoseCovarianceMatrix"], false)) DiagnoseCovarianceMatrix(Processor.get(), inputFile); + if(GetFromManager<bool>(Settings["DiagnoseCovarianceMatrix"], false)) this->DiagnoseCovarianceMatrix(Processor.get(), inputFile); } if(GetFromManager<bool>(Settings["JarlskogAnalysis"], true)) Processor->PerformJarlskogAnalysis(); if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); @@ -288,7 +288,7 @@ namespace mach3{ } } - Processor[0]->MakePostfit(GetCustomBinning(Settings)); + Processor[0]->MakePostfit(this->GetCustomBinning(Settings)); Processor[0]->DrawPostfit(); // Get edges from first histogram to ensure all params use same binning std::map<std::string, std::pair<double, double>> ParamEdges; @@ -442,7 +442,7 @@ namespace mach3{ Posterior->cd(); Posterior->Clear(); - if(GetFromManager<bool>(Settings["PerformKStest"], true)) KolmogorovSmirnovTest(Processor, Posterior, canvasname); + if(GetFromManager<bool>(Settings["PerformKStest"], true)) this->KolmogorovSmirnovTest(Processor, Posterior, canvasname); // Close the pdf file MACH3LOG_INFO("Closing pdf {}", canvasname); @@ -591,8 +591,8 @@ namespace mach3{ Processor->SetStepCut(BurnIn); Processor->GetCovariance(Covariance, Correlation); - CovariancePreviousHist = TMatrixIntoTH2D(Covariance, "Covariance"); - CorrelationPreviousHist = TMatrixIntoTH2D(Correlation, "Correlation"); + CovariancePreviousHist = this->TMatrixIntoTH2D(Covariance, "Covariance"); + CorrelationPreviousHist = this->TMatrixIntoTH2D(Correlation, "Correlation"); delete Covariance; Covariance = nullptr; @@ -607,8 +607,8 @@ namespace mach3{ Processor->GetCovariance(Covariance, Correlation); Processor->Reset2DPosteriors(); - CovarianceHist = TMatrixIntoTH2D(Covariance, "Covariance"); - CorrelationHist = TMatrixIntoTH2D(Correlation, "Correlation"); + CovarianceHist = this->TMatrixIntoTH2D(Covariance, "Covariance"); + CorrelationHist = this->TMatrixIntoTH2D(Correlation, "Correlation"); TH2D *CovarianceDiff = static_cast<TH2D*>(CovarianceHist->Clone("Covariance_Ratio")); TH2D *CorrelationDiff = static_cast<TH2D*>(CorrelationHist->Clone("Correlation_Ratio")); diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 31c84256f..9c4dbb07b 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -6,6 +6,7 @@ #include "api/plugin.hpp" #include "Diagnostics/ProcessMCMCPlugin.hpp" #include "Diagnostics/DiagMCMCPlugin.hpp" +#include "Diagnostics/GetPenaltyTermPlugin.hpp" using namespace std; @@ -177,9 +178,11 @@ int main(int argc, char *argv[]) { mach3::ProcessMCMCPlugin proc; mach3::DiagMCMCPlugin diag; + mach3::GetPenaltyTermPlugin penterm; program.add_core_plugin(proc); program.add_core_plugin(diag); + program.add_core_plugin(penterm); program.load_dynamic_plugins(); try { From 0de2a2dee72435f311039965ff232c2f4d649540 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 5 May 2026 10:55:18 +0000 Subject: [PATCH 06/26] adding MACH3_ERR --- Diagnostics/ProcessMCMCPlugin.cpp | 60 +++++++++++++++++-------------- cli/mach3.cpp | 1 + 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp index 4bc9459b6..304a4d477 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -54,9 +54,12 @@ namespace mach3{ .metavar("CONFIG") .required(); m_parser->add_argument("mcmc-chain") - .help("MCMC chain root file.") - .metavar("MCMC_CHAIN") - .nargs(1, 3) + .help("MCMC chain root files and titles.\n" + "single chain mode: MCMC_CHAIN1\n" + "two chain mode : MCMC_CHAIN1 TITLE1 MCMC_CHAIN2 TITLE2\n" + "three chain mode : MCMC_CHAIN1 TITLE1 MCMC_CHAIN2 TITLE2 MCMC_CHAIN3 TITLE3") + .metavar("MCMC_CHAIN1 [TITLE1 MCMC_CHAIN2 TITLE2 [MCMC_CHAIN3 TITLE3]]") + .nargs(1, 6) .required(); return m_parser; } @@ -66,15 +69,16 @@ namespace mach3{ SetMaCh3LoggerFormat(); nFiles = 0; config = m_parser->get<std::string>("config"); - auto files = m_parser->get<std::vector<std::string>>("mcmc-chain"); - // if (argc != 3 && argc !=6 && argc != 8) - // { - // MACH3LOG_ERROR("How to use: "); - // MACH3LOG_ERROR(" single chain: {} <Config> <MCMM_ND_Output.root>", m_parser->name()); - // MACH3LOG_ERROR(" two chain: {} <Config> <MCMM_ND_Output_1.root> <Title 1> <MCMC_ND_Output_2.root> <Title 2>", m_parser->name()); - // MACH3LOG_ERROR(" three chain: {} <Config> <MCMM_ND_Output_1.root> <Title 1> <MCMC_ND_Output_2.root> <Title 2> <MCMC_ND_Output_3.root> <Title 3>", m_parser->name()); - // throw MaCh3Exception(__FILE__ , __LINE__ ); - // } + auto mcmc_chain_args = m_parser->get<std::vector<std::string>>("mcmc-chain"); + + int nargs = mcmc_chain_args.size(); + if (nargs != 1 && nargs !=4 && nargs != 6) + { + MACH3LOG_ERROR("invalid number of arguments: {}", nargs); + std::cerr << *m_parser; + MACH3LOG_ERROR("invalid number of arguments: {}", nargs); + throw MaCh3Exception(__FILE__ , __LINE__ ); + } YAML::Node card_yaml = M3OpenConfig(config); if (!CheckNodeExists(card_yaml, "ProcessMCMC")) { @@ -82,27 +86,31 @@ namespace mach3{ throw MaCh3Exception(__FILE__ , __LINE__ ); } - if (files.size() == 1) + if (mcmc_chain_args.size() == 1) { MACH3LOG_INFO("Producing single fit output"); - std::string filename = files[0]; + std::string filename = mcmc_chain_args[0]; this->ProcessMCMC(filename); } // If we want to compare two or more fits (e.g. binning changes or introducing new params/priors) - else if (files.size() > 1) + else if (mcmc_chain_args.size() > 1) { - MACH3LOG_INFO("Producing two fit comparison"); - FileNames.push_back(files[0]); - TitleNames.push_back("ONE"); // todo fix - - FileNames.push_back(files[1]); - TitleNames.push_back("TWO"); - //KS: If there is third file add it - if(files.size() == 3) - { - FileNames.push_back(files[2]); - TitleNames.push_back("THREE"); + for (std::size_t i = 0; i < mcmc_chain_args.size(); i += 2) { + FileNames.push_back(mcmc_chain_args[i]); + TitleNames.push_back(mcmc_chain_args[i + 1]); } + // MACH3LOG_INFO("Producing two fit comparison"); + // FileNames.push_back(files[0]); + // TitleNames.push_back("ONE"); // todo fix + + // FileNames.push_back(files[1]); + // TitleNames.push_back("TWO"); + // //KS: If there is third file add it + // if(files.size() == 3) + // { + // FileNames.push_back(files[2]); + // TitleNames.push_back("THREE"); + // } this->MultipleProcessMCMC(); } diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 9c4dbb07b..90cfd72c4 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -59,6 +59,7 @@ namespace mach3{ catch (const std::exception& err) { std::cerr << err.what() << std::endl; std::cerr << this->get_subcommand_used(); + MACH3LOG_ERROR(err.what()); throw; } From 8435352615fbd55b514c9bcdcf318786b0d2e64c Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 6 May 2026 12:38:27 +0100 Subject: [PATCH 07/26] allow MACH3_PLUGINS directory --- api/argparse.hpp | 2 +- cli/mach3.cpp | 123 ++++++++++++++++++++++++++++++----------------- 2 files changed, 79 insertions(+), 46 deletions(-) diff --git a/api/argparse.hpp b/api/argparse.hpp index f0eceb9cb..af58293c4 100644 --- a/api/argparse.hpp +++ b/api/argparse.hpp @@ -17,7 +17,7 @@ namespace mach3 { return this->m_subparsers; } - const MaCh3ArgumentParser& get_subcommand_used(){ + const MaCh3ArgumentParser& get_subcommand_used() const{ for (const std::reference_wrapper<ArgumentParser>& subparser : this->m_subparsers) { if (this->is_subcommand_used(subparser.get())) { return static_cast<MaCh3ArgumentParser*>(&(subparser.get()))->get_subcommand_used(); diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 90cfd72c4..c476df5fe 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -2,6 +2,7 @@ #include <cstdlib> #include <iostream> #include <sstream> +#include <filesystem> #include <vector> #include "api/plugin.hpp" #include "Diagnostics/ProcessMCMCPlugin.hpp" @@ -9,6 +10,7 @@ #include "Diagnostics/GetPenaltyTermPlugin.hpp" using namespace std; +namespace fs = std::filesystem; namespace mach3{ class DynamicPlugin: public IPlugin{ @@ -86,60 +88,61 @@ namespace mach3{ std::string path; while (std::getline(ss, path, ':')) { - void* handle = dlopen(path.c_str(), RTLD_NOW); - if (!handle) { - std::cerr << "dlopen failed - shared library not loaded: " << dlerror() << "\n"; - continue; - } + for (const auto& sofile : this->expand_plugin_path(path)) { + void* handle = dlopen(sofile.c_str(), RTLD_NOW); + if (!handle) { + std::cerr << "dlopen failed - shared library '"<< sofile << "' not loaded: " << dlerror() << "\n"; + continue; + } - auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); - auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); + auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); + auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); - if (!create || !destroy) { - std::cerr << "Invalid plugin: " << path << "\n"; - dlclose(handle); - continue; - } + if (!create || !destroy) { + std::cerr << "Invalid plugin: " << sofile << "\n"; + dlclose(handle); + continue; + } - IPlugin* plugin = nullptr; - try{ - plugin = create(); - if (!plugin){ - throw std::runtime_error("null pointer"); + IPlugin* plugin = nullptr; + try{ + plugin = create(); + if (!plugin){ + throw std::runtime_error("null pointer"); + } + } + catch (...){ + std::cerr << "Error instantiating plugin: " << sofile << "\n"; + dlclose(handle); + continue; } - } - catch (...){ - std::cerr << "Error instantiating plugin: " << path << "\n"; - dlclose(handle); - continue; - } - MaCh3ArgumentParser* parser = nullptr; - try{ - parser = plugin->get_parser(); - if (!parser){ - throw std::runtime_error("null pointer"); + MaCh3ArgumentParser* parser = nullptr; + try{ + parser = plugin->get_parser(); + if (!parser){ + throw std::runtime_error("null pointer"); + } + } + catch(...){ + std::cerr<< "Error retrieving parser" << "\n"; + destroy(plugin); + dlclose(handle); + continue; } - } - catch(...){ - std::cerr<< "Error retrieving parser" << "\n"; - destroy(plugin); - dlclose(handle); - continue; - } - try{ - this->add_subparser(*parser); - m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); - } - catch(...){ - std::cerr<< "Error finalising loading of plugin." << "\n"; - destroy(plugin); - dlclose(handle); - continue; + try{ + this->add_subparser(*parser); + m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); + } + catch(...){ + std::cerr<< "Error finalising loading of plugin." << "\n"; + destroy(plugin); + dlclose(handle); + continue; + } } - } } @@ -167,6 +170,36 @@ namespace mach3{ return 0; } + private: + std::vector<std::string> expand_plugin_path(const std::string& path) const { + std::vector<std::string> result; + + fs::path p(path); + + if (!fs::exists(p)) { + std::cerr << "Plugin path does not exist: " << path << "\n"; + return result; + } + + if (fs::is_regular_file(p)) { + // Single .so file + result.push_back(path); + } + else if (fs::is_directory(p)) { + // Load all .so files in directory + for (auto& entry : fs::directory_iterator(p)) { + if (entry.is_regular_file() && entry.path().extension() == ".so") { + result.push_back(entry.path().string()); + } + } + } + else { + std::cerr << "Invalid plugin path (not file or directory): " << path << "\n"; + } + + return result; + } + private: std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; From 6399a50cb1f964daf0c81a8086aeb121ec8573b9 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Thu, 7 May 2026 11:24:32 +0000 Subject: [PATCH 08/26] fix link time optimisation removing vtable --- Diagnostics/DiagMCMCPlugin.cpp | 4 ++++ Diagnostics/DiagMCMCPlugin.hpp | 4 +--- Diagnostics/GetPenaltyTermPlugin.cpp | 4 ++++ Diagnostics/GetPenaltyTermPlugin.hpp | 4 +--- Diagnostics/ProcessMCMCPlugin.cpp | 4 ++++ Diagnostics/ProcessMCMCPlugin.hpp | 4 +--- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Diagnostics/DiagMCMCPlugin.cpp b/Diagnostics/DiagMCMCPlugin.cpp index 3382fb880..84281d0d2 100644 --- a/Diagnostics/DiagMCMCPlugin.cpp +++ b/Diagnostics/DiagMCMCPlugin.cpp @@ -12,6 +12,10 @@ // } namespace mach3{ + + DiagMCMCPlugin::~DiagMCMCPlugin(){ + if (this->m_parser) { delete this->m_parser; } + } MaCh3ArgumentParser* DiagMCMCPlugin::get_parser(){ m_parser = new MaCh3ArgumentParser("diag", "1.0", argparse::default_arguments::help); diff --git a/Diagnostics/DiagMCMCPlugin.hpp b/Diagnostics/DiagMCMCPlugin.hpp index 9196d8089..122739b1a 100644 --- a/Diagnostics/DiagMCMCPlugin.hpp +++ b/Diagnostics/DiagMCMCPlugin.hpp @@ -6,9 +6,7 @@ namespace mach3{ class DiagMCMCPlugin: public IPlugin{ public: - virtual ~DiagMCMCPlugin(){ - if (m_parser) { delete m_parser; } - } + virtual ~DiagMCMCPlugin(); MaCh3ArgumentParser* get_parser() override; int run() override; diff --git a/Diagnostics/GetPenaltyTermPlugin.cpp b/Diagnostics/GetPenaltyTermPlugin.cpp index 0122fc74e..6c233cae5 100644 --- a/Diagnostics/GetPenaltyTermPlugin.cpp +++ b/Diagnostics/GetPenaltyTermPlugin.cpp @@ -37,6 +37,10 @@ _MaCh3_Safe_Include_End_ //} /// @author Kamil Skwarczynski namespace mach3{ + GetPenaltyTermPlugin::~GetPenaltyTermPlugin(){ + if (this->m_parser) { delete this->m_parser; } + } + MaCh3ArgumentParser* GetPenaltyTermPlugin::get_parser(){ m_parser = new MaCh3ArgumentParser("penterm", "1.0", argparse::default_arguments::help); m_parser->add_argument("inputfile") diff --git a/Diagnostics/GetPenaltyTermPlugin.hpp b/Diagnostics/GetPenaltyTermPlugin.hpp index a44e9c598..6789056cf 100644 --- a/Diagnostics/GetPenaltyTermPlugin.hpp +++ b/Diagnostics/GetPenaltyTermPlugin.hpp @@ -7,9 +7,7 @@ namespace mach3{ class GetPenaltyTermPlugin: public IPlugin{ public: - virtual ~GetPenaltyTermPlugin(){ - if (m_parser) { delete m_parser; } - } + virtual ~GetPenaltyTermPlugin(); MaCh3ArgumentParser* get_parser() override; int run() override; diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/Diagnostics/ProcessMCMCPlugin.cpp index 304a4d477..f78738354 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/Diagnostics/ProcessMCMCPlugin.cpp @@ -16,6 +16,10 @@ namespace mach3{ + ProcessMCMCPlugin::~ProcessMCMCPlugin(){ + if (this->m_parser) { delete this->m_parser; } + } + MaCh3ArgumentParser* ProcessMCMCPlugin::get_parser(){ m_parser = new MaCh3ArgumentParser("process", "1.0", argparse::default_arguments::help); m_parser->add_description("Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc."); diff --git a/Diagnostics/ProcessMCMCPlugin.hpp b/Diagnostics/ProcessMCMCPlugin.hpp index e26f0fcfe..86724dde8 100644 --- a/Diagnostics/ProcessMCMCPlugin.hpp +++ b/Diagnostics/ProcessMCMCPlugin.hpp @@ -18,9 +18,7 @@ namespace mach3{ class ProcessMCMCPlugin: public IPlugin{ public: - virtual ~ProcessMCMCPlugin(){ - if (m_parser) { delete m_parser; } - } + virtual ~ProcessMCMCPlugin(); MaCh3ArgumentParser* get_parser() override; int run() override; From 92487440fa4dd2eabb16c642fd1e37df94d6a586 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 13 May 2026 11:53:04 +0000 Subject: [PATCH 09/26] update --- api/CMakeLists.txt | 1 + cli/CMakeLists.txt | 2 +- cli/DynamicPlugin.hpp | 34 ++++ cli/MaCh3Program.cpp | 160 +++++++++++++++++ cli/MaCh3Program.hpp | 33 ++++ cli/mach3.cpp | 399 +++++++++++++++++++++--------------------- 6 files changed, 429 insertions(+), 200 deletions(-) create mode 100644 cli/DynamicPlugin.hpp create mode 100644 cli/MaCh3Program.cpp create mode 100644 cli/MaCh3Program.hpp diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 65b196032..3ea9563c8 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -7,6 +7,7 @@ FetchContent_MakeAvailable(argparse) set(HEADERS plugin.hpp + argparse.hpp ) diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index c35139314..ba544cf72 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,4 +1,4 @@ - add_executable(mach3 mach3.cpp) + add_executable(mach3 mach3.cpp MaCh3Program.cpp) target_link_libraries(mach3 PRIVATE MaCh3::CorePlugins MaCh3::All) install(TARGETS mach3 DESTINATION bin) diff --git a/cli/DynamicPlugin.hpp b/cli/DynamicPlugin.hpp new file mode 100644 index 000000000..12338d609 --- /dev/null +++ b/cli/DynamicPlugin.hpp @@ -0,0 +1,34 @@ +#pragma once +#include <dlfcn.h> +#include "api/plugin.hpp" + +namespace mach3{ + class DynamicPlugin: public IPlugin{ + public: + DynamicPlugin(void* handle, + IPlugin* plugin, + destroy_plugin_t destroy_func):m_handle(handle), + m_plugin(plugin), + m_destroy_func(destroy_func){} + + int run() { + return m_plugin->run(); + } + MaCh3ArgumentParser* get_parser(){ + return m_plugin->get_parser(); + } + + void destroy(){ + m_destroy_func(m_plugin); + m_plugin = 0; + dlclose(m_handle); + m_handle = 0; + m_destroy_func = 0; + } + + private: + void* m_handle; + IPlugin* m_plugin; + destroy_plugin_t m_destroy_func; + }; +}; \ No newline at end of file diff --git a/cli/MaCh3Program.cpp b/cli/MaCh3Program.cpp new file mode 100644 index 000000000..287d1a6ab --- /dev/null +++ b/cli/MaCh3Program.cpp @@ -0,0 +1,160 @@ +#include <dlfcn.h> +#include <filesystem> +#include <iostream> +#include <sstream> +#include "Manager/MaCh3Logger.h" +#include "cli/MaCh3Program.hpp" + +using std::vector, std::string, std::map; +namespace fs = std::filesystem; + +namespace mach3{ + + + void MaCh3Program::parse_args(int argc, const char *const argv[]) { + try{ + MaCh3ArgumentParser::parse_args(argc, argv); + } + catch (const std::exception& err) { + std::cerr << err.what() << std::endl; + std::cerr << this->get_subcommand_used(); + MACH3LOG_ERROR(err.what()); + throw; + } + + if (!this->get_subcommand_used()){ + std::cerr << this->get_subcommand_used(); + throw NoArgsException(); + } + } + + void MaCh3Program::add_core_plugin(IPlugin& plugin){ + MaCh3ArgumentParser* parser = plugin.get_parser(); + this->add_subparser(*parser); + m_plugin_map[parser] = &plugin; + } + + void MaCh3Program::load_dynamic_plugins(){ + const char* env = std::getenv("MACH3_PLUGINS"); + if (!env) { + //std::cerr << "No plugins specified\n"; + return; + } + + std::stringstream ss(env); + std::string path; + + while (std::getline(ss, path, ':')) { + for (const auto& sofile : this->expand_plugin_path(path)) { + void* handle = dlopen(sofile.c_str(), RTLD_NOW); + if (!handle) { + std::cerr << "dlopen failed - shared library '"<< sofile << "' not loaded: " << dlerror() << "\n"; + continue; + } + + auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); + auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); + + if (!create || !destroy) { + std::cerr << "Invalid plugin: " << sofile << "\n"; + dlclose(handle); + continue; + } + + IPlugin* plugin = nullptr; + try{ + plugin = create(); + if (!plugin){ + throw std::runtime_error("null pointer"); + } + } + catch (...){ + std::cerr << "Error instantiating plugin: " << sofile << "\n"; + dlclose(handle); + continue; + } + + MaCh3ArgumentParser* parser = nullptr; + try{ + parser = plugin->get_parser(); + if (!parser){ + throw std::runtime_error("null pointer"); + } + } + catch(...){ + std::cerr<< "Error retrieving parser" << "\n"; + destroy(plugin); + dlclose(handle); + continue; + } + + + try{ + this->add_subparser(*parser); + m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); + } + catch(...){ + std::cerr<< "Error finalising loading of plugin." << "\n"; + destroy(plugin); + dlclose(handle); + continue; + } + } + } + } + + void MaCh3Program::unload_dynamic_plugins(){ + for (auto& [_, plugin] : m_dynamic_plugin_map){ + plugin->destroy(); + } + m_dynamic_plugin_map.clear(); + } + + int MaCh3Program::run(){ + + const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); + + if (sub_parser){ + auto plugin_itr = m_plugin_map.find(&sub_parser); + if (plugin_itr != m_plugin_map.end()){ + return plugin_itr->second->run(); + } + auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); + if (dplugin_itr != m_dynamic_plugin_map.end()){ + return dplugin_itr->second->run(); + } + } + return 0; + } + + + std::vector<std::string> MaCh3Program::expand_plugin_path(const std::string& path) const { + std::vector<std::string> result; + + fs::path p(path); + + if (!fs::exists(p)) { + std::cerr << "Plugin path does not exist: " << path << "\n"; + return result; + } + + if (fs::is_regular_file(p)) { + // Single .so file + result.push_back(path); + } + else if (fs::is_directory(p)) { + // Load all .so files in directory + for (auto& entry : fs::directory_iterator(p)) { + if (entry.is_regular_file() && entry.path().extension() == ".so") { + result.push_back(entry.path().string()); + } + } + } + else { + std::cerr << "Invalid plugin path (not file or directory): " << path << "\n"; + } + + return result; + } + +}; \ No newline at end of file diff --git a/cli/MaCh3Program.hpp b/cli/MaCh3Program.hpp new file mode 100644 index 000000000..8dff7ee33 --- /dev/null +++ b/cli/MaCh3Program.hpp @@ -0,0 +1,33 @@ +#pragma once +#include <map> +#include <string> +#include <vector> +#include "api/plugin.hpp" +#include "cli/DynamicPlugin.hpp" + + +namespace mach3{ + + class NoArgsException: public std::exception{}; + + class MaCh3Program: public MaCh3ArgumentParser{ + public: + using MaCh3ArgumentParser::MaCh3ArgumentParser; + virtual ~MaCh3Program(){ + //std::cout<<"UNLOADED"<< std::endl; + this->unload_dynamic_plugins(); + } + void parse_args(int argc, const char *const argv[]); + void add_core_plugin(IPlugin& plugin); + void load_dynamic_plugins(); + void unload_dynamic_plugins(); + int run(); + + private: + std::vector<std::string> expand_plugin_path(const std::string& path) const; + + private: + std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; + std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; + }; +}; \ No newline at end of file diff --git a/cli/mach3.cpp b/cli/mach3.cpp index c476df5fe..f7b5a14cb 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -1,210 +1,211 @@ -#include <dlfcn.h> -#include <cstdlib> -#include <iostream> -#include <sstream> -#include <filesystem> -#include <vector> -#include "api/plugin.hpp" +// #include <dlfcn.h> +// #include <cstdlib> +// #include <iostream> +// #include <sstream> +// #include <filesystem> +// #include <vector> +// #include "api/plugin.hpp" +#include "cli/MaCh3Program.hpp" #include "Diagnostics/ProcessMCMCPlugin.hpp" #include "Diagnostics/DiagMCMCPlugin.hpp" #include "Diagnostics/GetPenaltyTermPlugin.hpp" -using namespace std; -namespace fs = std::filesystem; - -namespace mach3{ - class DynamicPlugin: public IPlugin{ - public: - DynamicPlugin(void* handle, - IPlugin* plugin, - destroy_plugin_t destroy_func):m_handle(handle), - m_plugin(plugin), - m_destroy_func(destroy_func){} - - int run() { - return m_plugin->run(); - } - MaCh3ArgumentParser* get_parser(){ - return m_plugin->get_parser(); - } - - void destroy(){ - m_destroy_func(m_plugin); - m_plugin = 0; - dlclose(m_handle); - m_handle = 0; - m_destroy_func = 0; - } - - private: - void* m_handle; - IPlugin* m_plugin; - destroy_plugin_t m_destroy_func; - }; - - class NoArgsException: public std::exception{}; - - - class MaCh3Program: public MaCh3ArgumentParser{ - public: - using MaCh3ArgumentParser::MaCh3ArgumentParser; - virtual ~MaCh3Program(){ - //std::cout<<"UNLOADED"<< std::endl; - this->unload_dynamic_plugins(); - } +// using namespace std; +// namespace fs = std::filesystem; + +// namespace mach3{ + // class DynamicPlugin: public IPlugin{ + // public: + // DynamicPlugin(void* handle, + // IPlugin* plugin, + // destroy_plugin_t destroy_func):m_handle(handle), + // m_plugin(plugin), + // m_destroy_func(destroy_func){} + + // int run() { + // return m_plugin->run(); + // } + // MaCh3ArgumentParser* get_parser(){ + // return m_plugin->get_parser(); + // } + + // void destroy(){ + // m_destroy_func(m_plugin); + // m_plugin = 0; + // dlclose(m_handle); + // m_handle = 0; + // m_destroy_func = 0; + // } + + // private: + // void* m_handle; + // IPlugin* m_plugin; + // destroy_plugin_t m_destroy_func; + // }; + + // class NoArgsException: public std::exception{}; + + + // class MaCh3Program: public MaCh3ArgumentParser{ + // public: + // using MaCh3ArgumentParser::MaCh3ArgumentParser; + // virtual ~MaCh3Program(){ + // //std::cout<<"UNLOADED"<< std::endl; + // this->unload_dynamic_plugins(); + // } - void parse_args(int argc, const char *const argv[]) { - try{ - MaCh3ArgumentParser::parse_args(argc, argv); - } - catch (const std::exception& err) { - std::cerr << err.what() << std::endl; - std::cerr << this->get_subcommand_used(); - MACH3LOG_ERROR(err.what()); - throw; - } - - if (!this->get_subcommand_used()){ - std::cerr << this->get_subcommand_used(); - throw NoArgsException(); - } - } - - void add_core_plugin(IPlugin& plugin){ - MaCh3ArgumentParser* parser = plugin.get_parser(); - this->add_subparser(*parser); - m_plugin_map[parser] = &plugin; - } - - void load_dynamic_plugins(){ - const char* env = std::getenv("MACH3_PLUGINS"); - if (!env) { - //std::cerr << "No plugins specified\n"; - return; - } - - std::stringstream ss(env); - std::string path; - - while (std::getline(ss, path, ':')) { - for (const auto& sofile : this->expand_plugin_path(path)) { - void* handle = dlopen(sofile.c_str(), RTLD_NOW); - if (!handle) { - std::cerr << "dlopen failed - shared library '"<< sofile << "' not loaded: " << dlerror() << "\n"; - continue; - } - - auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); - auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); - - if (!create || !destroy) { - std::cerr << "Invalid plugin: " << sofile << "\n"; - dlclose(handle); - continue; - } - - IPlugin* plugin = nullptr; - try{ - plugin = create(); - if (!plugin){ - throw std::runtime_error("null pointer"); - } - } - catch (...){ - std::cerr << "Error instantiating plugin: " << sofile << "\n"; - dlclose(handle); - continue; - } - - MaCh3ArgumentParser* parser = nullptr; - try{ - parser = plugin->get_parser(); - if (!parser){ - throw std::runtime_error("null pointer"); - } - } - catch(...){ - std::cerr<< "Error retrieving parser" << "\n"; - destroy(plugin); - dlclose(handle); - continue; - } - - - try{ - this->add_subparser(*parser); - m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); - } - catch(...){ - std::cerr<< "Error finalising loading of plugin." << "\n"; - destroy(plugin); - dlclose(handle); - continue; - } - } - } - } - - void unload_dynamic_plugins(){ - for (auto& [_, plugin] : m_dynamic_plugin_map){ - plugin->destroy(); - } - m_dynamic_plugin_map.clear(); - } - - int run(){ + // void parse_args(int argc, const char *const argv[]) { + // try{ + // MaCh3ArgumentParser::parse_args(argc, argv); + // } + // catch (const std::exception& err) { + // std::cerr << err.what() << std::endl; + // std::cerr << this->get_subcommand_used(); + // MACH3LOG_ERROR(err.what()); + // throw; + // } + + // if (!this->get_subcommand_used()){ + // std::cerr << this->get_subcommand_used(); + // throw NoArgsException(); + // } + // } + + // void add_core_plugin(IPlugin& plugin){ + // MaCh3ArgumentParser* parser = plugin.get_parser(); + // this->add_subparser(*parser); + // m_plugin_map[parser] = &plugin; + // } + + // void load_dynamic_plugins(){ + // const char* env = std::getenv("MACH3_PLUGINS"); + // if (!env) { + // //std::cerr << "No plugins specified\n"; + // return; + // } + + // std::stringstream ss(env); + // std::string path; + + // while (std::getline(ss, path, ':')) { + // for (const auto& sofile : this->expand_plugin_path(path)) { + // void* handle = dlopen(sofile.c_str(), RTLD_NOW); + // if (!handle) { + // std::cerr << "dlopen failed - shared library '"<< sofile << "' not loaded: " << dlerror() << "\n"; + // continue; + // } + + // auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); + // auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); + + // if (!create || !destroy) { + // std::cerr << "Invalid plugin: " << sofile << "\n"; + // dlclose(handle); + // continue; + // } + + // IPlugin* plugin = nullptr; + // try{ + // plugin = create(); + // if (!plugin){ + // throw std::runtime_error("null pointer"); + // } + // } + // catch (...){ + // std::cerr << "Error instantiating plugin: " << sofile << "\n"; + // dlclose(handle); + // continue; + // } + + // MaCh3ArgumentParser* parser = nullptr; + // try{ + // parser = plugin->get_parser(); + // if (!parser){ + // throw std::runtime_error("null pointer"); + // } + // } + // catch(...){ + // std::cerr<< "Error retrieving parser" << "\n"; + // destroy(plugin); + // dlclose(handle); + // continue; + // } + + + // try{ + // this->add_subparser(*parser); + // m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); + // } + // catch(...){ + // std::cerr<< "Error finalising loading of plugin." << "\n"; + // destroy(plugin); + // dlclose(handle); + // continue; + // } + // } + // } + // } + + // void unload_dynamic_plugins(){ + // for (auto& [_, plugin] : m_dynamic_plugin_map){ + // plugin->destroy(); + // } + // m_dynamic_plugin_map.clear(); + // } + + // int run(){ - const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); + // const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); - if (sub_parser){ - auto plugin_itr = m_plugin_map.find(&sub_parser); - if (plugin_itr != m_plugin_map.end()){ - return plugin_itr->second->run(); - } - auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); - if (dplugin_itr != m_dynamic_plugin_map.end()){ - return dplugin_itr->second->run(); - } - } - return 0; - } - - private: - std::vector<std::string> expand_plugin_path(const std::string& path) const { - std::vector<std::string> result; - - fs::path p(path); - - if (!fs::exists(p)) { - std::cerr << "Plugin path does not exist: " << path << "\n"; - return result; - } - - if (fs::is_regular_file(p)) { - // Single .so file - result.push_back(path); - } - else if (fs::is_directory(p)) { - // Load all .so files in directory - for (auto& entry : fs::directory_iterator(p)) { - if (entry.is_regular_file() && entry.path().extension() == ".so") { - result.push_back(entry.path().string()); - } - } - } - else { - std::cerr << "Invalid plugin path (not file or directory): " << path << "\n"; - } - - return result; - } - - private: - std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; - std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; - }; -}; + // if (sub_parser){ + // auto plugin_itr = m_plugin_map.find(&sub_parser); + // if (plugin_itr != m_plugin_map.end()){ + // return plugin_itr->second->run(); + // } + // auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); + // if (dplugin_itr != m_dynamic_plugin_map.end()){ + // return dplugin_itr->second->run(); + // } + // } + // return 0; + // } + + // private: + // std::vector<std::string> expand_plugin_path(const std::string& path) const { + // std::vector<std::string> result; + + // fs::path p(path); + + // if (!fs::exists(p)) { + // std::cerr << "Plugin path does not exist: " << path << "\n"; + // return result; + // } + + // if (fs::is_regular_file(p)) { + // // Single .so file + // result.push_back(path); + // } + // else if (fs::is_directory(p)) { + // // Load all .so files in directory + // for (auto& entry : fs::directory_iterator(p)) { + // if (entry.is_regular_file() && entry.path().extension() == ".so") { + // result.push_back(entry.path().string()); + // } + // } + // } + // else { + // std::cerr << "Invalid plugin path (not file or directory): " << path << "\n"; + // } + + // return result; + // } + + // private: + // std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; + // std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; + // }; +// }; int main(int argc, char *argv[]) { From c8648ec3af614e6a0e6283d4dc56b55e05bddd6e Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 13 May 2026 14:58:45 +0000 Subject: [PATCH 10/26] refactoring --- Diagnostics/CMakeLists.txt | 26 ++- Diagnostics/DiagMCMC.cpp | 2 +- Diagnostics/GetPenaltyTerm.cpp | 2 +- Diagnostics/ProcessMCMC.cpp | 2 +- cli/CMakeLists.txt | 22 +- cli/mach3.cpp | 210 +----------------- cli/modules/CMakeLists.txt | 25 +++ .../modules}/DiagMCMCPlugin.cpp | 2 +- .../modules}/DiagMCMCPlugin.hpp | 0 .../modules}/GetPenaltyTermPlugin.cpp | 2 +- .../modules}/GetPenaltyTermPlugin.hpp | 0 .../modules}/ProcessMCMCPlugin.cpp | 2 +- .../modules}/ProcessMCMCPlugin.hpp | 0 13 files changed, 70 insertions(+), 225 deletions(-) create mode 100644 cli/modules/CMakeLists.txt rename {Diagnostics => cli/modules}/DiagMCMCPlugin.cpp (98%) rename {Diagnostics => cli/modules}/DiagMCMCPlugin.hpp (100%) rename {Diagnostics => cli/modules}/GetPenaltyTermPlugin.cpp (99%) rename {Diagnostics => cli/modules}/GetPenaltyTermPlugin.hpp (100%) rename {Diagnostics => cli/modules}/ProcessMCMCPlugin.cpp (99%) rename {Diagnostics => cli/modules}/ProcessMCMCPlugin.hpp (100%) diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index 04def5804..c51b23979 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -3,14 +3,14 @@ # ) -add_library(CorePlugins STATIC - ProcessMCMCPlugin.cpp - DiagMCMCPlugin.cpp - GetPenaltyTermPlugin.cpp -) +# add_library(CorePlugins STATIC +# ProcessMCMCPlugin.cpp +# DiagMCMCPlugin.cpp +# GetPenaltyTermPlugin.cpp +# ) -target_link_libraries(CorePlugins PRIVATE MaCh3::All) -target_include_directories(CorePlugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) +# target_link_libraries(CorePlugins PRIVATE MaCh3::All) +# target_include_directories(CorePlugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) # set_target_properties(Plugins PROPERTIES # PUBLIC_HEADER "${HEADERS}" @@ -22,7 +22,15 @@ target_include_directories(CorePlugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_D # LIBRARY DESTINATION lib/ # PUBLIC_HEADER DESTINATION include/Plugins) -add_library(MaCh3::CorePlugins ALIAS CorePlugins) +# add_library(MaCh3::CorePlugins ALIAS CorePlugins) + +# set(CLI_MODULES +# ProcessMCMCPlugin.cpp +# DiagMCMCPlugin.cpp +# GetPenaltyTermPlugin.cpp +# ) + +# list(TRANSFORM CLI_MODULES PREPEND "../cli/modules/") add_custom_target(DiagApps) @@ -40,7 +48,7 @@ foreach(app ) add_executable(${app} ${app}.cpp) add_dependencies(DiagApps ${app}) - target_link_libraries(${app} MaCh3::CorePlugins MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either + target_link_libraries(${app} MaCh3::CoreBinaryModules MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either install(TARGETS ${app} DESTINATION bin) endforeach(app) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index c4bb77323..acc2b9100 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "Diagnostics/DiagMCMCPlugin.hpp" +#include "cli/modules/DiagMCMCPlugin.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index 0ef8ec527..8310660c0 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "Diagnostics/GetPenaltyTermPlugin.hpp" +#include "cli/modules/GetPenaltyTermPlugin.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index b2e7c255e..96a5bd851 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "Diagnostics/ProcessMCMCPlugin.hpp" +#include "cli/modules/ProcessMCMCPlugin.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index ba544cf72..2764b4a38 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,4 +1,20 @@ - add_executable(mach3 mach3.cpp MaCh3Program.cpp) - target_link_libraries(mach3 PRIVATE MaCh3::CorePlugins MaCh3::All) +add_subdirectory(modules) +# set(CLI_MODULES +# ProcessMCMCPlugin.cpp +# DiagMCMCPlugin.cpp +# GetPenaltyTermPlugin.cpp +# ) - install(TARGETS mach3 DESTINATION bin) +# list(TRANSFORM CLI_MODULES PREPEND "modules/") + + + +add_executable(mach3 + mach3.cpp + MaCh3Program.cpp + # ${CLI_MODULES} +) + +target_link_libraries(mach3 PRIVATE MaCh3::CoreBinaryModules MaCh3::All) + +install(TARGETS mach3 DESTINATION bin) diff --git a/cli/mach3.cpp b/cli/mach3.cpp index f7b5a14cb..8c65a2f55 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -1,211 +1,7 @@ -// #include <dlfcn.h> -// #include <cstdlib> -// #include <iostream> -// #include <sstream> -// #include <filesystem> -// #include <vector> -// #include "api/plugin.hpp" #include "cli/MaCh3Program.hpp" -#include "Diagnostics/ProcessMCMCPlugin.hpp" -#include "Diagnostics/DiagMCMCPlugin.hpp" -#include "Diagnostics/GetPenaltyTermPlugin.hpp" - -// using namespace std; -// namespace fs = std::filesystem; - -// namespace mach3{ - // class DynamicPlugin: public IPlugin{ - // public: - // DynamicPlugin(void* handle, - // IPlugin* plugin, - // destroy_plugin_t destroy_func):m_handle(handle), - // m_plugin(plugin), - // m_destroy_func(destroy_func){} - - // int run() { - // return m_plugin->run(); - // } - // MaCh3ArgumentParser* get_parser(){ - // return m_plugin->get_parser(); - // } - - // void destroy(){ - // m_destroy_func(m_plugin); - // m_plugin = 0; - // dlclose(m_handle); - // m_handle = 0; - // m_destroy_func = 0; - // } - - // private: - // void* m_handle; - // IPlugin* m_plugin; - // destroy_plugin_t m_destroy_func; - // }; - - // class NoArgsException: public std::exception{}; - - - // class MaCh3Program: public MaCh3ArgumentParser{ - // public: - // using MaCh3ArgumentParser::MaCh3ArgumentParser; - // virtual ~MaCh3Program(){ - // //std::cout<<"UNLOADED"<< std::endl; - // this->unload_dynamic_plugins(); - // } - - - // void parse_args(int argc, const char *const argv[]) { - // try{ - // MaCh3ArgumentParser::parse_args(argc, argv); - // } - // catch (const std::exception& err) { - // std::cerr << err.what() << std::endl; - // std::cerr << this->get_subcommand_used(); - // MACH3LOG_ERROR(err.what()); - // throw; - // } - - // if (!this->get_subcommand_used()){ - // std::cerr << this->get_subcommand_used(); - // throw NoArgsException(); - // } - // } - - // void add_core_plugin(IPlugin& plugin){ - // MaCh3ArgumentParser* parser = plugin.get_parser(); - // this->add_subparser(*parser); - // m_plugin_map[parser] = &plugin; - // } - - // void load_dynamic_plugins(){ - // const char* env = std::getenv("MACH3_PLUGINS"); - // if (!env) { - // //std::cerr << "No plugins specified\n"; - // return; - // } - - // std::stringstream ss(env); - // std::string path; - - // while (std::getline(ss, path, ':')) { - // for (const auto& sofile : this->expand_plugin_path(path)) { - // void* handle = dlopen(sofile.c_str(), RTLD_NOW); - // if (!handle) { - // std::cerr << "dlopen failed - shared library '"<< sofile << "' not loaded: " << dlerror() << "\n"; - // continue; - // } - - // auto create = reinterpret_cast<create_plugin_t>(dlsym(handle, "create_plugin")); - // auto destroy = reinterpret_cast<destroy_plugin_t>(dlsym(handle, "destroy_plugin")); - - // if (!create || !destroy) { - // std::cerr << "Invalid plugin: " << sofile << "\n"; - // dlclose(handle); - // continue; - // } - - // IPlugin* plugin = nullptr; - // try{ - // plugin = create(); - // if (!plugin){ - // throw std::runtime_error("null pointer"); - // } - // } - // catch (...){ - // std::cerr << "Error instantiating plugin: " << sofile << "\n"; - // dlclose(handle); - // continue; - // } - - // MaCh3ArgumentParser* parser = nullptr; - // try{ - // parser = plugin->get_parser(); - // if (!parser){ - // throw std::runtime_error("null pointer"); - // } - // } - // catch(...){ - // std::cerr<< "Error retrieving parser" << "\n"; - // destroy(plugin); - // dlclose(handle); - // continue; - // } - - - // try{ - // this->add_subparser(*parser); - // m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); - // } - // catch(...){ - // std::cerr<< "Error finalising loading of plugin." << "\n"; - // destroy(plugin); - // dlclose(handle); - // continue; - // } - // } - // } - // } - - // void unload_dynamic_plugins(){ - // for (auto& [_, plugin] : m_dynamic_plugin_map){ - // plugin->destroy(); - // } - // m_dynamic_plugin_map.clear(); - // } - - // int run(){ - - // const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); - - // if (sub_parser){ - // auto plugin_itr = m_plugin_map.find(&sub_parser); - // if (plugin_itr != m_plugin_map.end()){ - // return plugin_itr->second->run(); - // } - // auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); - // if (dplugin_itr != m_dynamic_plugin_map.end()){ - // return dplugin_itr->second->run(); - // } - // } - // return 0; - // } - - // private: - // std::vector<std::string> expand_plugin_path(const std::string& path) const { - // std::vector<std::string> result; - - // fs::path p(path); - - // if (!fs::exists(p)) { - // std::cerr << "Plugin path does not exist: " << path << "\n"; - // return result; - // } - - // if (fs::is_regular_file(p)) { - // // Single .so file - // result.push_back(path); - // } - // else if (fs::is_directory(p)) { - // // Load all .so files in directory - // for (auto& entry : fs::directory_iterator(p)) { - // if (entry.is_regular_file() && entry.path().extension() == ".so") { - // result.push_back(entry.path().string()); - // } - // } - // } - // else { - // std::cerr << "Invalid plugin path (not file or directory): " << path << "\n"; - // } - - // return result; - // } - - // private: - // std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; - // std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; - // }; -// }; +#include "cli/modules/ProcessMCMCPlugin.hpp" +#include "cli/modules/DiagMCMCPlugin.hpp" +#include "cli/modules/GetPenaltyTermPlugin.hpp" int main(int argc, char *argv[]) { diff --git a/cli/modules/CMakeLists.txt b/cli/modules/CMakeLists.txt new file mode 100644 index 000000000..cdc51e242 --- /dev/null +++ b/cli/modules/CMakeLists.txt @@ -0,0 +1,25 @@ +# set(HEADERS +# ProcessMCMCPlugin.hpp +# ) + + +add_library(CoreBinaryModules SHARED + ProcessMCMCPlugin.cpp + DiagMCMCPlugin.cpp + GetPenaltyTermPlugin.cpp +) + +target_link_libraries(CoreBinaryModules PRIVATE MaCh3::All) +target_include_directories(CoreBinaryModules PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) + +# set_target_properties(Plugins PROPERTIES +# PUBLIC_HEADER "${HEADERS}" +# EXPORT_NAME Plugins) + + +# install(TARGETS Plugins +# EXPORT MaCh3-targets +# LIBRARY DESTINATION lib/ +# PUBLIC_HEADER DESTINATION include/Plugins) + +add_library(MaCh3::CoreBinaryModules ALIAS CoreBinaryModules) \ No newline at end of file diff --git a/Diagnostics/DiagMCMCPlugin.cpp b/cli/modules/DiagMCMCPlugin.cpp similarity index 98% rename from Diagnostics/DiagMCMCPlugin.cpp rename to cli/modules/DiagMCMCPlugin.cpp index 84281d0d2..fa5491229 100644 --- a/Diagnostics/DiagMCMCPlugin.cpp +++ b/cli/modules/DiagMCMCPlugin.cpp @@ -1,6 +1,6 @@ #include "Fitters/MCMCProcessor.h" #include "Manager/Manager.h" -#include "Diagnostics/DiagMCMCPlugin.hpp" +#include "cli/modules/DiagMCMCPlugin.hpp" // Not needed for Core plugins as not dynamically loaded // extern "C" mach3::IPlugin* create_plugin() { diff --git a/Diagnostics/DiagMCMCPlugin.hpp b/cli/modules/DiagMCMCPlugin.hpp similarity index 100% rename from Diagnostics/DiagMCMCPlugin.hpp rename to cli/modules/DiagMCMCPlugin.hpp diff --git a/Diagnostics/GetPenaltyTermPlugin.cpp b/cli/modules/GetPenaltyTermPlugin.cpp similarity index 99% rename from Diagnostics/GetPenaltyTermPlugin.cpp rename to cli/modules/GetPenaltyTermPlugin.cpp index 6c233cae5..4595d1600 100644 --- a/Diagnostics/GetPenaltyTermPlugin.cpp +++ b/cli/modules/GetPenaltyTermPlugin.cpp @@ -3,7 +3,7 @@ #include "Samples/SampleStructs.h" #include "Samples/HistogramUtils.h" #include "Parameters/ParameterHandlerUtils.h" -#include "Diagnostics/GetPenaltyTermPlugin.hpp" +#include "cli/modules/GetPenaltyTermPlugin.hpp" _MaCh3_Safe_Include_Start_ //{ // ROOT includes diff --git a/Diagnostics/GetPenaltyTermPlugin.hpp b/cli/modules/GetPenaltyTermPlugin.hpp similarity index 100% rename from Diagnostics/GetPenaltyTermPlugin.hpp rename to cli/modules/GetPenaltyTermPlugin.hpp diff --git a/Diagnostics/ProcessMCMCPlugin.cpp b/cli/modules/ProcessMCMCPlugin.cpp similarity index 99% rename from Diagnostics/ProcessMCMCPlugin.cpp rename to cli/modules/ProcessMCMCPlugin.cpp index f78738354..014ece757 100644 --- a/Diagnostics/ProcessMCMCPlugin.cpp +++ b/cli/modules/ProcessMCMCPlugin.cpp @@ -1,7 +1,7 @@ //MaCh3 includes #include "Fitters/OscProcessor.h" #include "Manager/Manager.h" -#include "Diagnostics/ProcessMCMCPlugin.hpp" +#include "cli/modules/ProcessMCMCPlugin.hpp" // Not needed for Core plugins as not dynamically loaded // extern "C" mach3::IPlugin* create_plugin() { diff --git a/Diagnostics/ProcessMCMCPlugin.hpp b/cli/modules/ProcessMCMCPlugin.hpp similarity index 100% rename from Diagnostics/ProcessMCMCPlugin.hpp rename to cli/modules/ProcessMCMCPlugin.hpp From f8e52cb73cfc98c512da374c11ac6c40d3fc782c Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 13 May 2026 15:26:15 +0000 Subject: [PATCH 11/26] remane core library --- Diagnostics/CMakeLists.txt | 2 +- cli/CMakeLists.txt | 2 +- cli/modules/CMakeLists.txt | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index c51b23979..e0ecdf56e 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -48,7 +48,7 @@ foreach(app ) add_executable(${app} ${app}.cpp) add_dependencies(DiagApps ${app}) - target_link_libraries(${app} MaCh3::CoreBinaryModules MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either + target_link_libraries(${app} MaCh3::CLIModules MaCh3::All MaCh3Warnings ROOT::ROOTDataFrame) # dont need plugins in MaCh3::All at root scope as only used here. Once all transferred to plugins wont need MaCh3::All either install(TARGETS ${app} DESTINATION bin) endforeach(app) diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 2764b4a38..46cb3787f 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -15,6 +15,6 @@ add_executable(mach3 # ${CLI_MODULES} ) -target_link_libraries(mach3 PRIVATE MaCh3::CoreBinaryModules MaCh3::All) +target_link_libraries(mach3 PRIVATE MaCh3::CLIModules MaCh3::All) install(TARGETS mach3 DESTINATION bin) diff --git a/cli/modules/CMakeLists.txt b/cli/modules/CMakeLists.txt index cdc51e242..f7f9a3db4 100644 --- a/cli/modules/CMakeLists.txt +++ b/cli/modules/CMakeLists.txt @@ -3,14 +3,14 @@ # ) -add_library(CoreBinaryModules SHARED +add_library(CLIModules SHARED ProcessMCMCPlugin.cpp DiagMCMCPlugin.cpp GetPenaltyTermPlugin.cpp ) -target_link_libraries(CoreBinaryModules PRIVATE MaCh3::All) -target_include_directories(CoreBinaryModules PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) +target_link_libraries(CLIModules PRIVATE MaCh3::All) +target_include_directories(CLIModules PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) # set_target_properties(Plugins PROPERTIES # PUBLIC_HEADER "${HEADERS}" @@ -22,4 +22,4 @@ target_include_directories(CoreBinaryModules PUBLIC $<BUILD_INTERFACE:${CMAKE_BI # LIBRARY DESTINATION lib/ # PUBLIC_HEADER DESTINATION include/Plugins) -add_library(MaCh3::CoreBinaryModules ALIAS CoreBinaryModules) \ No newline at end of file +add_library(MaCh3::CLIModules ALIAS CLIModules) \ No newline at end of file From 702889753e23cefb0052f50d7af87d033b183c2d Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Fri, 15 May 2026 15:08:21 +0100 Subject: [PATCH 12/26] Fix namespace and shared library install --- Diagnostics/DiagMCMC.cpp | 2 +- Diagnostics/GetPenaltyTerm.cpp | 2 +- Diagnostics/ProcessMCMC.cpp | 2 +- api/argparse.hpp | 2 +- api/plugin.hpp | 12 ++++++------ cli/DynamicPlugin.hpp | 2 +- cli/MaCh3Program.cpp | 2 +- cli/MaCh3Program.hpp | 2 +- cli/mach3.cpp | 8 ++++---- cli/modules/CMakeLists.txt | 2 ++ cli/modules/DiagMCMCPlugin.cpp | 2 +- cli/modules/DiagMCMCPlugin.hpp | 2 +- cli/modules/GetPenaltyTermPlugin.cpp | 2 +- cli/modules/GetPenaltyTermPlugin.hpp | 2 +- cli/modules/ProcessMCMCPlugin.cpp | 2 +- cli/modules/ProcessMCMCPlugin.hpp | 2 +- 16 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index acc2b9100..c40c92f86 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -5,7 +5,7 @@ int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 diag' as a direct replacement instead."); argv[0] = "diag"; - mach3::DiagMCMCPlugin proc; + M3::DiagMCMCPlugin proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index 8310660c0..7f27e4c7f 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -5,7 +5,7 @@ int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 penterm' as a direct replacement instead."); argv[0] = "penterm"; - mach3::GetPenaltyTermPlugin proc; + M3::GetPenaltyTermPlugin proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index 96a5bd851..3a531aea0 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -5,7 +5,7 @@ int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); argv[0] = "process"; - mach3::ProcessMCMCPlugin proc; + M3::ProcessMCMCPlugin proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/api/argparse.hpp b/api/argparse.hpp index af58293c4..377d3a3a5 100644 --- a/api/argparse.hpp +++ b/api/argparse.hpp @@ -3,7 +3,7 @@ using argparse::ArgumentParser; -namespace mach3 { +namespace M3 { class MaCh3ArgumentParser: public ArgumentParser{ public: diff --git a/api/plugin.hpp b/api/plugin.hpp index df9fed95a..2b9299cc2 100644 --- a/api/plugin.hpp +++ b/api/plugin.hpp @@ -2,7 +2,7 @@ #include "api/argparse.hpp" -namespace mach3 { +namespace M3 { class IPlugin { public: @@ -15,15 +15,15 @@ namespace mach3 { // Factory function typedefs extern "C" { - typedef mach3::IPlugin* (*create_plugin_t)(); - typedef void (*destroy_plugin_t)(mach3::IPlugin*); + typedef M3::IPlugin* (*create_plugin_t)(); + typedef void (*destroy_plugin_t)(M3::IPlugin*); } #define MACH3_REGISTER_PLUGIN(PluginClass) \ -static_assert(std::is_base_of<mach3::IPlugin, PluginClass>::value, "PluginClass must derive from mach3::IPlugin"); \ -extern "C" mach3::IPlugin* create_plugin() { \ +static_assert(std::is_base_of<M3::IPlugin, PluginClass>::value, "PluginClass must derive from M3::IPlugin"); \ +extern "C" M3::IPlugin* create_plugin() { \ return new PluginClass(); \ } \ -extern "C" void destroy_plugin(mach3::IPlugin* p) { \ +extern "C" void destroy_plugin(M3::IPlugin* p) { \ delete p; \ } diff --git a/cli/DynamicPlugin.hpp b/cli/DynamicPlugin.hpp index 12338d609..7ede0814c 100644 --- a/cli/DynamicPlugin.hpp +++ b/cli/DynamicPlugin.hpp @@ -2,7 +2,7 @@ #include <dlfcn.h> #include "api/plugin.hpp" -namespace mach3{ +namespace M3{ class DynamicPlugin: public IPlugin{ public: DynamicPlugin(void* handle, diff --git a/cli/MaCh3Program.cpp b/cli/MaCh3Program.cpp index 287d1a6ab..f3dcff87d 100644 --- a/cli/MaCh3Program.cpp +++ b/cli/MaCh3Program.cpp @@ -8,7 +8,7 @@ using std::vector, std::string, std::map; namespace fs = std::filesystem; -namespace mach3{ +namespace M3{ void MaCh3Program::parse_args(int argc, const char *const argv[]) { diff --git a/cli/MaCh3Program.hpp b/cli/MaCh3Program.hpp index 8dff7ee33..887e7a330 100644 --- a/cli/MaCh3Program.hpp +++ b/cli/MaCh3Program.hpp @@ -6,7 +6,7 @@ #include "cli/DynamicPlugin.hpp" -namespace mach3{ +namespace M3{ class NoArgsException: public std::exception{}; diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 8c65a2f55..84f1778c1 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -5,11 +5,11 @@ int main(int argc, char *argv[]) { - mach3::MaCh3Program program; + M3::MaCh3Program program; - mach3::ProcessMCMCPlugin proc; - mach3::DiagMCMCPlugin diag; - mach3::GetPenaltyTermPlugin penterm; + M3::ProcessMCMCPlugin proc; + M3::DiagMCMCPlugin diag; + M3::GetPenaltyTermPlugin penterm; program.add_core_plugin(proc); program.add_core_plugin(diag); diff --git a/cli/modules/CMakeLists.txt b/cli/modules/CMakeLists.txt index f7f9a3db4..bc6738956 100644 --- a/cli/modules/CMakeLists.txt +++ b/cli/modules/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(CLIModules SHARED target_link_libraries(CLIModules PRIVATE MaCh3::All) target_include_directories(CLIModules PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) +install(TARGETS CLIModules LIBRARY DESTINATION lib) + # set_target_properties(Plugins PROPERTIES # PUBLIC_HEADER "${HEADERS}" # EXPORT_NAME Plugins) diff --git a/cli/modules/DiagMCMCPlugin.cpp b/cli/modules/DiagMCMCPlugin.cpp index fa5491229..76271ed1c 100644 --- a/cli/modules/DiagMCMCPlugin.cpp +++ b/cli/modules/DiagMCMCPlugin.cpp @@ -11,7 +11,7 @@ // delete p; // } -namespace mach3{ +namespace M3{ DiagMCMCPlugin::~DiagMCMCPlugin(){ if (this->m_parser) { delete this->m_parser; } diff --git a/cli/modules/DiagMCMCPlugin.hpp b/cli/modules/DiagMCMCPlugin.hpp index 122739b1a..f0be96dd9 100644 --- a/cli/modules/DiagMCMCPlugin.hpp +++ b/cli/modules/DiagMCMCPlugin.hpp @@ -1,7 +1,7 @@ #pragma once #include "api/plugin.hpp" -namespace mach3{ +namespace M3{ class DiagMCMCPlugin: public IPlugin{ diff --git a/cli/modules/GetPenaltyTermPlugin.cpp b/cli/modules/GetPenaltyTermPlugin.cpp index 4595d1600..270af07d4 100644 --- a/cli/modules/GetPenaltyTermPlugin.cpp +++ b/cli/modules/GetPenaltyTermPlugin.cpp @@ -35,7 +35,7 @@ _MaCh3_Safe_Include_End_ //} /// @ingroup MaCh3DiagnosticProcessing /// /// @author Kamil Skwarczynski -namespace mach3{ +namespace M3{ GetPenaltyTermPlugin::~GetPenaltyTermPlugin(){ if (this->m_parser) { delete this->m_parser; } diff --git a/cli/modules/GetPenaltyTermPlugin.hpp b/cli/modules/GetPenaltyTermPlugin.hpp index 6789056cf..6c4571155 100644 --- a/cli/modules/GetPenaltyTermPlugin.hpp +++ b/cli/modules/GetPenaltyTermPlugin.hpp @@ -2,7 +2,7 @@ #include "yaml-cpp/yaml.h" #include "api/plugin.hpp" -namespace mach3{ +namespace M3{ class GetPenaltyTermPlugin: public IPlugin{ diff --git a/cli/modules/ProcessMCMCPlugin.cpp b/cli/modules/ProcessMCMCPlugin.cpp index 014ece757..6856ca4b8 100644 --- a/cli/modules/ProcessMCMCPlugin.cpp +++ b/cli/modules/ProcessMCMCPlugin.cpp @@ -14,7 +14,7 @@ // if was a dynamic plugin, could now use the convienience macro to generate these // MACH3_REGISTER_PLUGIN(mach3::ProcessMCMCPlugin) -namespace mach3{ +namespace M3{ ProcessMCMCPlugin::~ProcessMCMCPlugin(){ if (this->m_parser) { delete this->m_parser; } diff --git a/cli/modules/ProcessMCMCPlugin.hpp b/cli/modules/ProcessMCMCPlugin.hpp index 86724dde8..21b7855d8 100644 --- a/cli/modules/ProcessMCMCPlugin.hpp +++ b/cli/modules/ProcessMCMCPlugin.hpp @@ -5,7 +5,7 @@ #include "Fitters/MCMCProcessor.h" #include "api/plugin.hpp" -namespace mach3{ +namespace M3{ /// @file ProcessMCMC.cpp /// @brief Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implantation of methods is in MCMCProcessor From 7c9a7413ecb080cf615dfc341649eec21631a96c Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Mon, 18 May 2026 15:40:22 +0000 Subject: [PATCH 13/26] implement the explicit options flags --- cli/modules/ProcessMCMCPlugin.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/modules/ProcessMCMCPlugin.cpp b/cli/modules/ProcessMCMCPlugin.cpp index 6856ca4b8..f97531723 100644 --- a/cli/modules/ProcessMCMCPlugin.cpp +++ b/cli/modules/ProcessMCMCPlugin.cpp @@ -166,7 +166,7 @@ namespace M3{ YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; - const bool PlotCorr = GetFromManager<bool>(Settings["PlotCorr"], false); + const bool PlotCorr = m_parser->get<bool>("--corr") || GetFromManager<bool>(Settings["PlotCorr"], false); Processor->SetExcludedTypes(GetFromManager<std::vector<std::string>>(Settings["ExcludedTypes"], {})); Processor->SetExcludedNames(GetFromManager<std::vector<std::string>>(Settings["ExcludedNames"], {})); @@ -211,15 +211,15 @@ namespace M3{ Processor->MakePostfit(this->GetCustomBinning(Settings)); Processor->DrawPostfit(); //KS: Should set via config whether you want below or not - if(GetFromManager<bool>(Settings["MakeCredibleIntervals"], true)) { + if(m_parser->get<bool>("--MakeCredibleIntervals") || GetFromManager<bool>(Settings["MakeCredibleIntervals"], true)) { Processor->MakeCredibleIntervals(GetFromManager<std::vector<double>>(Settings["CredibleIntervals"], {0.99, 0.90, 0.68}), GetFromManager<std::vector<short int>>(Settings["CredibleIntervalsColours"], {436, 430, 422}), GetFromManager<bool>(Settings["CredibleInSigmas"], false)); } - if(GetFromManager<bool>(Settings["CalcBayesFactor"], true)) this->CalcBayesFactor(Processor.get()); - if(GetFromManager<bool>(Settings["CalcSavageDickey"], true)) this->CalcSavageDickey(Processor.get()); - if(GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) this->CalcBipolarPlot(Processor.get()); - if(GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) this->CalcParameterEvolution(Processor.get()); + if(m_parser->get<bool>("--CalcBayesFactor") || GetFromManager<bool>(Settings["CalcBayesFactor"], true)) this->CalcBayesFactor(Processor.get()); + if(m_parser->get<bool>("--CalcSavageDickey") || GetFromManager<bool>(Settings["CalcSavageDickey"], true)) this->CalcSavageDickey(Processor.get()); + if(m_parser->get<bool>("--CalcBipolarPlot") || GetFromManager<bool>(Settings["CalcBipolarPlot"], false)) this->CalcBipolarPlot(Processor.get()); + if(m_parser->get<bool>("--CalcParameterEvolution") || GetFromManager<bool>(Settings["CalcParameterEvolution"], false)) this->CalcParameterEvolution(Processor.get()); if(PlotCorr) { From 353c4a6785fce7774b0b98aa2b55e7b3fc9b58e5 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 27 May 2026 10:12:08 +0000 Subject: [PATCH 14/26] fixing the diag app --- cli/modules/DiagMCMCPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/modules/DiagMCMCPlugin.cpp b/cli/modules/DiagMCMCPlugin.cpp index 76271ed1c..da4b4ca89 100644 --- a/cli/modules/DiagMCMCPlugin.cpp +++ b/cli/modules/DiagMCMCPlugin.cpp @@ -33,7 +33,7 @@ namespace M3{ int DiagMCMCPlugin::run() { SetMaCh3LoggerFormat(); MACH3LOG_INFO("Producing single fit output"); - std::string inputFile = m_parser->get<std::string>("mcmc-chain"); + std::string inputFile = m_parser->get<std::string>("mcmc-output"); std::string config = m_parser->get<std::string>("config"); MACH3LOG_INFO("File for study: {}", inputFile); From 06fbd7cd94b25d79b2da40e9c202618584a77b70 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Mon, 1 Jun 2026 15:33:08 +0000 Subject: [PATCH 15/26] Adding tab completion --- cli/MaCh3Program.cpp | 96 ++++++++++++++++++++++++++++++++++++++------ cli/MaCh3Program.hpp | 26 +++++++++++- cli/mach3.cpp | 39 +++++++++++++++++- 3 files changed, 146 insertions(+), 15 deletions(-) diff --git a/cli/MaCh3Program.cpp b/cli/MaCh3Program.cpp index f3dcff87d..abe71cd31 100644 --- a/cli/MaCh3Program.cpp +++ b/cli/MaCh3Program.cpp @@ -10,28 +10,99 @@ namespace fs = std::filesystem; namespace M3{ - - void MaCh3Program::parse_args(int argc, const char *const argv[]) { - try{ - MaCh3ArgumentParser::parse_args(argc, argv); + const bool MaCh3Program::write_file(const fs::path& path, std::string_view content) const{ + try { + fs::create_directories(path.parent_path()); + std::ofstream out(path); + if (!out) return false; + out << content; + return true; + } catch (...) { + return false; } - catch (const std::exception& err) { - std::cerr << err.what() << std::endl; - std::cerr << this->get_subcommand_used(); - MACH3LOG_ERROR(err.what()); - throw; + } + + const std::string MaCh3Program::detect_shell() const{ + const char* shell = std::getenv("SHELL"); + if (!shell) return ""; + + std::string s(shell); + if (s.find("bash") != std::string::npos) return "bash"; + if (s.find("zsh") != std::string::npos) return "zsh"; + + return ""; + } + + const void MaCh3Program::install_completions() const{ + const char* home = std::getenv("HOME"); + if (!home) { + std::cerr << "Cannot determine HOME directory\n"; + return; } - if (!this->get_subcommand_used()){ - std::cerr << this->get_subcommand_used(); - throw NoArgsException(); + std::string shell = detect_shell(); + if (shell.empty()) { + std::cerr << "Could not detect your shell. Supported: bash, zsh\n"; + return; } + + fs::path home_path(home); + + if (shell == "bash") { + fs::path path = home_path / ".local/share/bash-completion/completions/mach3"; + if (write_file(path, MaCh3Program::BASH_COMPLETION)) + std::cout << "Installed bash completions → " << path << "\n"; + return; + } + + if (shell == "zsh") { + fs::path path = home_path / ".local/share/zsh/site-functions/_mach3"; + if (write_file(path, MaCh3Program::ZSH_COMPLETION)) + std::cout << "Installed zsh completions → " << path << "\n"; + return; + } + } + + const void MaCh3Program::completions(const std::string& prefix) const{ + for (const std::string& cmd : m_subcommands) { + if (cmd.rfind(prefix, 0) == 0) + std::cout << cmd << "\n"; + } + } + + // void MaCh3Program::parse_args(int argc, const char *const argv[]) { + // try{ + // MaCh3ArgumentParser::parse_args(argc, argv); + + // } + // catch (const std::exception& err) { + // if (auto prefix = this->present<std::string>("--complete")) { + // this->completions(*prefix); + // exit(0); + // } + + // if (this->get<bool>("--install-completions")) { + // this->install_completions(); + // exit(0); + // } + // std::cerr << err.what() << std::endl; + // std::cerr << this->get_subcommand_used(); + // MACH3LOG_ERROR(err.what()); + // throw; + // } + + // if (!this->get_subcommand_used()){ + // std::cerr << this->get_subcommand_used(); + // throw NoArgsException(); + // } + // } void MaCh3Program::add_core_plugin(IPlugin& plugin){ MaCh3ArgumentParser* parser = plugin.get_parser(); this->add_subparser(*parser); m_plugin_map[parser] = &plugin; + m_subcommands.push_back(parser->name()); } void MaCh3Program::load_dynamic_plugins(){ @@ -92,6 +163,7 @@ namespace M3{ try{ this->add_subparser(*parser); m_dynamic_plugin_map[parser] = new DynamicPlugin(handle, plugin, destroy); + m_subcommands.push_back(parser->name()); } catch(...){ std::cerr<< "Error finalising loading of plugin." << "\n"; diff --git a/cli/MaCh3Program.hpp b/cli/MaCh3Program.hpp index 887e7a330..e84d2be21 100644 --- a/cli/MaCh3Program.hpp +++ b/cli/MaCh3Program.hpp @@ -2,9 +2,11 @@ #include <map> #include <string> #include <vector> +#include <filesystem> #include "api/plugin.hpp" #include "cli/DynamicPlugin.hpp" +namespace fs = std::filesystem; namespace M3{ @@ -17,17 +19,39 @@ namespace M3{ //std::cout<<"UNLOADED"<< std::endl; this->unload_dynamic_plugins(); } - void parse_args(int argc, const char *const argv[]); + // void parse_args(int argc, const char *const argv[]); void add_core_plugin(IPlugin& plugin); void load_dynamic_plugins(); + const void install_completions() const; + const void completions(const std::string& prefix) const; void unload_dynamic_plugins(); int run(); private: + const std::string detect_shell() const; + const bool write_file(const fs::path& path, std::string_view content) const; std::vector<std::string> expand_plugin_path(const std::string& path) const; private: + std::vector<std::string> m_subcommands; std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; + + static constexpr std::string_view BASH_COMPLETION = R"( +_mach3_complete() { + local cur prev words cword + _init_completion || return + COMPREPLY=( $(mach3 --complete "$cur" "${words[@]:1:$cword}") ) +} +complete -F _mach3_complete mach3 +)"; + static constexpr std::string_view ZSH_COMPLETION = R"( +#compdef mach3 + +local -a completions +local cur="$words[-1]" +completions=("${(@f)$(mach3 --complete "$cur" "${words[@]:1}")}") +compadd -a completions +)"; }; }; \ No newline at end of file diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 84f1778c1..225448fab 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -5,7 +5,17 @@ int main(int argc, char *argv[]) { - M3::MaCh3Program program; + M3::MaCh3Program program("mach3"); + + // Hidden completion option + program.add_argument("--complete") + .hidden() + .nargs(1); + + // Hidden installer option + program.add_argument("--install-completions") + .help("") + .flag(); M3::ProcessMCMCPlugin proc; M3::DiagMCMCPlugin diag; @@ -19,15 +29,40 @@ int main(int argc, char *argv[]) { try { program.parse_args(argc, argv); } + catch (const std::exception& err) { + // completions count as parsing error + if (auto prefix = program.present<std::string>("--complete")) { + program.completions(*prefix); + return 0; + } + + std::cerr << err.what() << std::endl; + std::cerr << program.get_subcommand_used(); + MACH3LOG_ERROR(err.what()); + return 1; + } catch(...){ + MACH3LOG_ERROR("Unknown parsing error."); return 1; } + // no subcommand args + if (!program.get_subcommand_used()){ + std::cerr << program.get_subcommand_used(); + return 2; + } + + + if (program.get<bool>("--install-completions")) { + program.install_completions(); + return 0; + } + try{ program.run(); } catch(...){ - return 2; + return 3; } return 0; From cd4bb7d47ee9a0ecbfa5f9c3df79ff0eeee414af Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 3 Jun 2026 13:29:17 +0000 Subject: [PATCH 16/26] Moving api into cli --- CMakeLists.txt | 9 +++++++-- cli/CMakeLists.txt | 1 + cli/DynamicPlugin.hpp | 2 +- cli/MaCh3Program.cpp | 10 +++++----- cli/MaCh3Program.hpp | 8 ++++---- {api => cli/api}/CMakeLists.txt | 19 +++++++++---------- {api => cli/api}/argparse.hpp | 0 {api => cli/api}/plugin.hpp | 3 ++- cli/mach3.cpp | 6 +++--- cli/modules/DiagMCMCPlugin.hpp | 4 ++-- cli/modules/GetPenaltyTermPlugin.hpp | 4 ++-- cli/modules/ProcessMCMCPlugin.hpp | 4 ++-- 12 files changed, 38 insertions(+), 32 deletions(-) rename {api => cli/api}/CMakeLists.txt (54%) rename {api => cli/api}/argparse.hpp (100%) rename {api => cli/api}/plugin.hpp (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74f4fcf77..31a7d2003 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,7 +281,7 @@ endforeach() ################################# Build MaCh3 ################################## # Build components -add_subdirectory(api) +# add_subdirectory(api) add_subdirectory(cli) add_subdirectory(Manager) add_subdirectory(Parameters) @@ -307,7 +307,12 @@ add_library(MaCh3 INTERFACE) if (MaCh3_NUDOCK_ENABLED) target_link_libraries(MaCh3 INTERFACE MaCh3NuDock) endif() -target_link_libraries(MaCh3 INTERFACE api Fitters Samples Parameters Splines Manager MaCh3CompilerOptions MaCh3GPUCompilerOptions Plotting) +target_link_libraries(MaCh3 INTERFACE CLIApi Fitters Samples Parameters Splines Manager MaCh3CompilerOptions MaCh3GPUCompilerOptions Plotting) +# Ensure API headers propagate to all consumers of MaCh3::All +# target_include_directories(MaCh3 INTERFACE +# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/api> +# $<INSTALL_INTERFACE:include/api> +# ) set_target_properties(MaCh3 PROPERTIES EXPORT_NAME All) install(TARGETS MaCh3 diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 46cb3787f..71a49afa6 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(api) add_subdirectory(modules) # set(CLI_MODULES # ProcessMCMCPlugin.cpp diff --git a/cli/DynamicPlugin.hpp b/cli/DynamicPlugin.hpp index 7ede0814c..9c962d067 100644 --- a/cli/DynamicPlugin.hpp +++ b/cli/DynamicPlugin.hpp @@ -1,6 +1,6 @@ #pragma once #include <dlfcn.h> -#include "api/plugin.hpp" +#include "cli/api/plugin.hpp" namespace M3{ class DynamicPlugin: public IPlugin{ diff --git a/cli/MaCh3Program.cpp b/cli/MaCh3Program.cpp index abe71cd31..4323bf8d8 100644 --- a/cli/MaCh3Program.cpp +++ b/cli/MaCh3Program.cpp @@ -98,10 +98,10 @@ namespace M3{ // } // } - void MaCh3Program::add_core_plugin(IPlugin& plugin){ - MaCh3ArgumentParser* parser = plugin.get_parser(); + void MaCh3Program::add_core_module(IModule& module){ + MaCh3ArgumentParser* parser = module.get_parser(); this->add_subparser(*parser); - m_plugin_map[parser] = &plugin; + m_module_map[parser] = &module; m_subcommands.push_back(parser->name()); } @@ -187,8 +187,8 @@ namespace M3{ const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); if (sub_parser){ - auto plugin_itr = m_plugin_map.find(&sub_parser); - if (plugin_itr != m_plugin_map.end()){ + auto plugin_itr = m_module_map.find(&sub_parser); + if (plugin_itr != m_module_map.end()){ return plugin_itr->second->run(); } auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); diff --git a/cli/MaCh3Program.hpp b/cli/MaCh3Program.hpp index e84d2be21..5e39c302d 100644 --- a/cli/MaCh3Program.hpp +++ b/cli/MaCh3Program.hpp @@ -3,14 +3,14 @@ #include <string> #include <vector> #include <filesystem> -#include "api/plugin.hpp" +#include "cli/api/plugin.hpp" #include "cli/DynamicPlugin.hpp" namespace fs = std::filesystem; namespace M3{ - class NoArgsException: public std::exception{}; + // class NoArgsException: public std::exception{}; class MaCh3Program: public MaCh3ArgumentParser{ public: @@ -20,7 +20,7 @@ namespace M3{ this->unload_dynamic_plugins(); } // void parse_args(int argc, const char *const argv[]); - void add_core_plugin(IPlugin& plugin); + void add_core_module(IModule& module); void load_dynamic_plugins(); const void install_completions() const; const void completions(const std::string& prefix) const; @@ -34,7 +34,7 @@ namespace M3{ private: std::vector<std::string> m_subcommands; - std::map<const MaCh3ArgumentParser*, IPlugin*> m_plugin_map; + std::map<const MaCh3ArgumentParser*, IModule*> m_module_map; std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; static constexpr std::string_view BASH_COMPLETION = R"( diff --git a/api/CMakeLists.txt b/cli/api/CMakeLists.txt similarity index 54% rename from api/CMakeLists.txt rename to cli/api/CMakeLists.txt index 3ea9563c8..5af71c036 100644 --- a/api/CMakeLists.txt +++ b/cli/api/CMakeLists.txt @@ -11,26 +11,25 @@ set(HEADERS ) -add_library(api INTERFACE) +add_library(CLIApi INTERFACE) -target_include_directories(api INTERFACE ${argparse_SOURCE_DIR}/include) +target_include_directories(CLIApi INTERFACE ${argparse_SOURCE_DIR}/include) -target_include_directories(api +target_include_directories(CLIApi INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> - $<INSTALL_INTERFACE:include/api> + $<INSTALL_INTERFACE:include/cli/api> ) - -set_target_properties(api PROPERTIES +set_target_properties(CLIApi PROPERTIES PUBLIC_HEADER "${HEADERS}" - EXPORT_NAME api) + EXPORT_NAME CLIApi) -install(TARGETS api +install(TARGETS CLIApi EXPORT MaCh3-targets LIBRARY DESTINATION lib/ - PUBLIC_HEADER DESTINATION include/api) + PUBLIC_HEADER DESTINATION include/cli/api) #install(FILES ${HEADERS} DESTINATION include/api) -add_library(MaCh3::api ALIAS api) \ No newline at end of file +add_library(MaCh3::CLIApi ALIAS CLIApi) \ No newline at end of file diff --git a/api/argparse.hpp b/cli/api/argparse.hpp similarity index 100% rename from api/argparse.hpp rename to cli/api/argparse.hpp diff --git a/api/plugin.hpp b/cli/api/plugin.hpp similarity index 93% rename from api/plugin.hpp rename to cli/api/plugin.hpp index 2b9299cc2..b5e55b69b 100644 --- a/api/plugin.hpp +++ b/cli/api/plugin.hpp @@ -1,5 +1,5 @@ #pragma once -#include "api/argparse.hpp" +#include "cli/api/argparse.hpp" namespace M3 { @@ -11,6 +11,7 @@ namespace M3 { virtual MaCh3ArgumentParser* get_parser() = 0; }; + typedef IPlugin IModule; }; // Factory function typedefs diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 225448fab..811222186 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -21,9 +21,9 @@ int main(int argc, char *argv[]) { M3::DiagMCMCPlugin diag; M3::GetPenaltyTermPlugin penterm; - program.add_core_plugin(proc); - program.add_core_plugin(diag); - program.add_core_plugin(penterm); + program.add_core_module(proc); + program.add_core_module(diag); + program.add_core_module(penterm); program.load_dynamic_plugins(); try { diff --git a/cli/modules/DiagMCMCPlugin.hpp b/cli/modules/DiagMCMCPlugin.hpp index f0be96dd9..1637b947f 100644 --- a/cli/modules/DiagMCMCPlugin.hpp +++ b/cli/modules/DiagMCMCPlugin.hpp @@ -1,9 +1,9 @@ #pragma once -#include "api/plugin.hpp" +#include "cli/api/plugin.hpp" namespace M3{ - class DiagMCMCPlugin: public IPlugin{ + class DiagMCMCPlugin: public IModule{ public: virtual ~DiagMCMCPlugin(); diff --git a/cli/modules/GetPenaltyTermPlugin.hpp b/cli/modules/GetPenaltyTermPlugin.hpp index 6c4571155..2c609fe30 100644 --- a/cli/modules/GetPenaltyTermPlugin.hpp +++ b/cli/modules/GetPenaltyTermPlugin.hpp @@ -1,10 +1,10 @@ #pragma once #include "yaml-cpp/yaml.h" -#include "api/plugin.hpp" +#include "cli/api/plugin.hpp" namespace M3{ - class GetPenaltyTermPlugin: public IPlugin{ + class GetPenaltyTermPlugin: public IModule{ public: virtual ~GetPenaltyTermPlugin(); diff --git a/cli/modules/ProcessMCMCPlugin.hpp b/cli/modules/ProcessMCMCPlugin.hpp index 21b7855d8..4d9b274b1 100644 --- a/cli/modules/ProcessMCMCPlugin.hpp +++ b/cli/modules/ProcessMCMCPlugin.hpp @@ -3,7 +3,7 @@ // yaml Includes #include "yaml-cpp/yaml.h" #include "Fitters/MCMCProcessor.h" -#include "api/plugin.hpp" +#include "cli/api/plugin.hpp" namespace M3{ @@ -15,7 +15,7 @@ namespace M3{ /// @brief Main function processing MCMC and Producing plots - class ProcessMCMCPlugin: public IPlugin{ + class ProcessMCMCPlugin: public IModule{ public: virtual ~ProcessMCMCPlugin(); From 5ddbade21d93b502df73ad30397b9ad9090c0b62 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 3 Jun 2026 13:33:02 +0000 Subject: [PATCH 17/26] rename core plugins to modules --- cli/modules/{DiagMCMCPlugin.cpp => DiagMCMCModule.cpp} | 0 cli/modules/{DiagMCMCPlugin.hpp => DiagMCMCModule.hpp} | 0 .../{GetPenaltyTermPlugin.cpp => GetPenaltyTermModule.cpp} | 0 .../{GetPenaltyTermPlugin.hpp => GetPenaltyTermModule.hpp} | 0 cli/modules/{ProcessMCMCPlugin.cpp => ProcessMCMCModule.cpp} | 0 cli/modules/{ProcessMCMCPlugin.hpp => ProcessMCMCModule.hpp} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename cli/modules/{DiagMCMCPlugin.cpp => DiagMCMCModule.cpp} (100%) rename cli/modules/{DiagMCMCPlugin.hpp => DiagMCMCModule.hpp} (100%) rename cli/modules/{GetPenaltyTermPlugin.cpp => GetPenaltyTermModule.cpp} (100%) rename cli/modules/{GetPenaltyTermPlugin.hpp => GetPenaltyTermModule.hpp} (100%) rename cli/modules/{ProcessMCMCPlugin.cpp => ProcessMCMCModule.cpp} (100%) rename cli/modules/{ProcessMCMCPlugin.hpp => ProcessMCMCModule.hpp} (100%) diff --git a/cli/modules/DiagMCMCPlugin.cpp b/cli/modules/DiagMCMCModule.cpp similarity index 100% rename from cli/modules/DiagMCMCPlugin.cpp rename to cli/modules/DiagMCMCModule.cpp diff --git a/cli/modules/DiagMCMCPlugin.hpp b/cli/modules/DiagMCMCModule.hpp similarity index 100% rename from cli/modules/DiagMCMCPlugin.hpp rename to cli/modules/DiagMCMCModule.hpp diff --git a/cli/modules/GetPenaltyTermPlugin.cpp b/cli/modules/GetPenaltyTermModule.cpp similarity index 100% rename from cli/modules/GetPenaltyTermPlugin.cpp rename to cli/modules/GetPenaltyTermModule.cpp diff --git a/cli/modules/GetPenaltyTermPlugin.hpp b/cli/modules/GetPenaltyTermModule.hpp similarity index 100% rename from cli/modules/GetPenaltyTermPlugin.hpp rename to cli/modules/GetPenaltyTermModule.hpp diff --git a/cli/modules/ProcessMCMCPlugin.cpp b/cli/modules/ProcessMCMCModule.cpp similarity index 100% rename from cli/modules/ProcessMCMCPlugin.cpp rename to cli/modules/ProcessMCMCModule.cpp diff --git a/cli/modules/ProcessMCMCPlugin.hpp b/cli/modules/ProcessMCMCModule.hpp similarity index 100% rename from cli/modules/ProcessMCMCPlugin.hpp rename to cli/modules/ProcessMCMCModule.hpp From 603bbd61f10c97a3b24687d5c7623f41b01f7844 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Wed, 3 Jun 2026 15:00:55 +0100 Subject: [PATCH 18/26] Move core binaries from plugin to module naming convention --- Diagnostics/DiagMCMC.cpp | 4 +-- Diagnostics/GetPenaltyTerm.cpp | 4 +-- Diagnostics/ProcessMCMC.cpp | 4 +-- cli/mach3.cpp | 12 ++++----- cli/modules/CMakeLists.txt | 6 ++--- cli/modules/DiagMCMCModule.cpp | 16 +++-------- cli/modules/DiagMCMCModule.hpp | 4 +-- cli/modules/GetPenaltyTermModule.cpp | 14 +++++----- cli/modules/GetPenaltyTermModule.hpp | 4 +-- cli/modules/ProcessMCMCModule.cpp | 40 +++++++++++----------------- cli/modules/ProcessMCMCModule.hpp | 4 +-- 11 files changed, 47 insertions(+), 65 deletions(-) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index c40c92f86..cf30e1031 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -1,11 +1,11 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/DiagMCMCPlugin.hpp" +#include "cli/modules/DiagMCMCModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 diag' as a direct replacement instead."); argv[0] = "diag"; - M3::DiagMCMCPlugin proc; + M3::DiagMCMCModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index 7f27e4c7f..d6f9b6dbd 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -1,11 +1,11 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/GetPenaltyTermPlugin.hpp" +#include "cli/modules/GetPenaltyTermModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 penterm' as a direct replacement instead."); argv[0] = "penterm"; - M3::GetPenaltyTermPlugin proc; + M3::GetPenaltyTermModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index 3a531aea0..92bd54f03 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -1,11 +1,11 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/ProcessMCMCPlugin.hpp" +#include "cli/modules/ProcessMCMCModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); argv[0] = "process"; - M3::ProcessMCMCPlugin proc; + M3::ProcessMCMCModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); proc.run(); diff --git a/cli/mach3.cpp b/cli/mach3.cpp index 811222186..e8c4c7d3e 100644 --- a/cli/mach3.cpp +++ b/cli/mach3.cpp @@ -1,7 +1,7 @@ #include "cli/MaCh3Program.hpp" -#include "cli/modules/ProcessMCMCPlugin.hpp" -#include "cli/modules/DiagMCMCPlugin.hpp" -#include "cli/modules/GetPenaltyTermPlugin.hpp" +#include "cli/modules/ProcessMCMCModule.hpp" +#include "cli/modules/DiagMCMCModule.hpp" +#include "cli/modules/GetPenaltyTermModule.hpp" int main(int argc, char *argv[]) { @@ -17,9 +17,9 @@ int main(int argc, char *argv[]) { .help("") .flag(); - M3::ProcessMCMCPlugin proc; - M3::DiagMCMCPlugin diag; - M3::GetPenaltyTermPlugin penterm; + M3::ProcessMCMCModule proc; + M3::DiagMCMCModule diag; + M3::GetPenaltyTermModule penterm; program.add_core_module(proc); program.add_core_module(diag); diff --git a/cli/modules/CMakeLists.txt b/cli/modules/CMakeLists.txt index bc6738956..b0bff1776 100644 --- a/cli/modules/CMakeLists.txt +++ b/cli/modules/CMakeLists.txt @@ -4,9 +4,9 @@ add_library(CLIModules SHARED - ProcessMCMCPlugin.cpp - DiagMCMCPlugin.cpp - GetPenaltyTermPlugin.cpp + ProcessMCMCModule.cpp + DiagMCMCModule.cpp + GetPenaltyTermModule.cpp ) target_link_libraries(CLIModules PRIVATE MaCh3::All) diff --git a/cli/modules/DiagMCMCModule.cpp b/cli/modules/DiagMCMCModule.cpp index da4b4ca89..e77348cc7 100644 --- a/cli/modules/DiagMCMCModule.cpp +++ b/cli/modules/DiagMCMCModule.cpp @@ -1,23 +1,15 @@ #include "Fitters/MCMCProcessor.h" #include "Manager/Manager.h" -#include "cli/modules/DiagMCMCPlugin.hpp" +#include "cli/modules/DiagMCMCModule.hpp" -// Not needed for Core plugins as not dynamically loaded -// extern "C" mach3::IPlugin* create_plugin() { -// return new mach3::DiagMCMCPlugin(); -// } - -// extern "C" void destroy_plugin(mach3::IPlugin* p) { -// delete p; -// } namespace M3{ - DiagMCMCPlugin::~DiagMCMCPlugin(){ + DiagMCMCModule::~DiagMCMCModule(){ if (this->m_parser) { delete this->m_parser; } } - MaCh3ArgumentParser* DiagMCMCPlugin::get_parser(){ + MaCh3ArgumentParser* DiagMCMCModule::get_parser(){ m_parser = new MaCh3ArgumentParser("diag", "1.0", argparse::default_arguments::help); m_parser->add_argument("mcmc-output") .help("MCMC chain root file.") @@ -30,7 +22,7 @@ namespace M3{ return m_parser; } - int DiagMCMCPlugin::run() { + int DiagMCMCModule::run() { SetMaCh3LoggerFormat(); MACH3LOG_INFO("Producing single fit output"); std::string inputFile = m_parser->get<std::string>("mcmc-output"); diff --git a/cli/modules/DiagMCMCModule.hpp b/cli/modules/DiagMCMCModule.hpp index 1637b947f..9f4f65dcd 100644 --- a/cli/modules/DiagMCMCModule.hpp +++ b/cli/modules/DiagMCMCModule.hpp @@ -3,10 +3,10 @@ namespace M3{ - class DiagMCMCPlugin: public IModule{ + class DiagMCMCModule: public IModule{ public: - virtual ~DiagMCMCPlugin(); + virtual ~DiagMCMCModule(); MaCh3ArgumentParser* get_parser() override; int run() override; diff --git a/cli/modules/GetPenaltyTermModule.cpp b/cli/modules/GetPenaltyTermModule.cpp index 270af07d4..b3d0b90e0 100644 --- a/cli/modules/GetPenaltyTermModule.cpp +++ b/cli/modules/GetPenaltyTermModule.cpp @@ -3,7 +3,7 @@ #include "Samples/SampleStructs.h" #include "Samples/HistogramUtils.h" #include "Parameters/ParameterHandlerUtils.h" -#include "cli/modules/GetPenaltyTermPlugin.hpp" +#include "cli/modules/GetPenaltyTermModule.hpp" _MaCh3_Safe_Include_Start_ //{ // ROOT includes @@ -37,11 +37,11 @@ _MaCh3_Safe_Include_End_ //} /// @author Kamil Skwarczynski namespace M3{ - GetPenaltyTermPlugin::~GetPenaltyTermPlugin(){ + GetPenaltyTermModule::~GetPenaltyTermModule(){ if (this->m_parser) { delete this->m_parser; } } - MaCh3ArgumentParser* GetPenaltyTermPlugin::get_parser(){ + MaCh3ArgumentParser* GetPenaltyTermModule::get_parser(){ m_parser = new MaCh3ArgumentParser("penterm", "1.0", argparse::default_arguments::help); m_parser->add_argument("inputfile") .help("Root file to analyse.") @@ -54,7 +54,7 @@ namespace M3{ return m_parser; } - int GetPenaltyTermPlugin::run()//int argc, char *argv[]) + int GetPenaltyTermModule::run()//int argc, char *argv[]) { SetMaCh3LoggerFormat(); M3::Utils::MaCh3Welcome(); @@ -65,7 +65,7 @@ namespace M3{ return 0; } - void GetPenaltyTermPlugin::ReadCovFile(const std::string& inputFile, + void GetPenaltyTermModule::ReadCovFile(const std::string& inputFile, std::vector <double>& Prior, std::vector <bool>& isFlat, std::vector<std::string>& ParamNames, @@ -149,7 +149,7 @@ namespace M3{ delete TempFile; } - void GetPenaltyTermPlugin::LoadSettings(YAML::Node& Settings, + void GetPenaltyTermModule::LoadSettings(YAML::Node& Settings, std::vector<std::string>& SetsNames, std::vector<std::string>& FancyTitle, std::vector<std::vector<bool>>& isRelevantParam, @@ -220,7 +220,7 @@ namespace M3{ } } - void GetPenaltyTermPlugin::GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) + void GetPenaltyTermModule::GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) { auto canvas = std::make_unique<TCanvas>("canvas", "canvas", 0, 0, 1024, 1024); canvas->SetGrid(); diff --git a/cli/modules/GetPenaltyTermModule.hpp b/cli/modules/GetPenaltyTermModule.hpp index 2c609fe30..4d64417d0 100644 --- a/cli/modules/GetPenaltyTermModule.hpp +++ b/cli/modules/GetPenaltyTermModule.hpp @@ -4,10 +4,10 @@ namespace M3{ - class GetPenaltyTermPlugin: public IModule{ + class GetPenaltyTermModule: public IModule{ public: - virtual ~GetPenaltyTermPlugin(); + virtual ~GetPenaltyTermModule(); MaCh3ArgumentParser* get_parser() override; int run() override; diff --git a/cli/modules/ProcessMCMCModule.cpp b/cli/modules/ProcessMCMCModule.cpp index f97531723..5d3a869c9 100644 --- a/cli/modules/ProcessMCMCModule.cpp +++ b/cli/modules/ProcessMCMCModule.cpp @@ -1,26 +1,16 @@ //MaCh3 includes #include "Fitters/OscProcessor.h" #include "Manager/Manager.h" -#include "cli/modules/ProcessMCMCPlugin.hpp" +#include "cli/modules/ProcessMCMCModule.hpp" -// Not needed for Core plugins as not dynamically loaded -// extern "C" mach3::IPlugin* create_plugin() { -// return new mach3::ProcessMCMCPlugin(); -// } - -// extern "C" void destroy_plugin(mach3::IPlugin* p) { -// delete p; -// } -// if was a dynamic plugin, could now use the convienience macro to generate these -// MACH3_REGISTER_PLUGIN(mach3::ProcessMCMCPlugin) namespace M3{ - ProcessMCMCPlugin::~ProcessMCMCPlugin(){ + ProcessMCMCModule::~ProcessMCMCModule(){ if (this->m_parser) { delete this->m_parser; } } - MaCh3ArgumentParser* ProcessMCMCPlugin::get_parser(){ + MaCh3ArgumentParser* ProcessMCMCModule::get_parser(){ m_parser = new MaCh3ArgumentParser("process", "1.0", argparse::default_arguments::help); m_parser->add_description("Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc."); m_parser->add_epilog("""\ @@ -69,7 +59,7 @@ namespace M3{ } - int ProcessMCMCPlugin::run() { + int ProcessMCMCModule::run() { SetMaCh3LoggerFormat(); nFiles = 0; config = m_parser->get<std::string>("config"); @@ -135,7 +125,7 @@ namespace M3{ /// /// @param Settings YAML configuration node containing the optional /// `CustomBinEdges` section. - std::map<std::string, std::pair<double, double>> ProcessMCMCPlugin::GetCustomBinning(const YAML::Node& Settings) + std::map<std::string, std::pair<double, double>> ProcessMCMCModule::GetCustomBinning(const YAML::Node& Settings) { std::map<std::string, std::pair<double, double>> CustomBinning; if (Settings["CustomBinEdges"]) { @@ -157,7 +147,7 @@ namespace M3{ return CustomBinning; } - void ProcessMCMCPlugin::ProcessMCMC(const std::string& inputFile) + void ProcessMCMCModule::ProcessMCMC(const std::string& inputFile) { MACH3LOG_INFO("File for study: {} with config {}", inputFile, config); // Make the processor) @@ -254,7 +244,7 @@ namespace M3{ if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); } - void ProcessMCMCPlugin::MultipleProcessMCMC() + void ProcessMCMCModule::MultipleProcessMCMC() { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -463,7 +453,7 @@ namespace M3{ } /// @brief KS: Calculate Bayes factor for a given hypothesis, most informative are those related to osc params. However, it make relative easy interpretation for switch dials - void ProcessMCMCPlugin::CalcBayesFactor(MCMCProcessor* Processor) + void ProcessMCMCModule::CalcBayesFactor(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -483,7 +473,7 @@ namespace M3{ Processor->GetBayesFactor(ParNames, Model1Bounds, Model2Bounds, ModelNames); } - void ProcessMCMCPlugin::CalcSavageDickey(MCMCProcessor* Processor) + void ProcessMCMCModule::CalcSavageDickey(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -501,7 +491,7 @@ namespace M3{ Processor->GetSavageDickey(ParNames, EvaluationPoint, Bounds); } - void ProcessMCMCPlugin::CalcParameterEvolution(MCMCProcessor* Processor) + void ProcessMCMCModule::CalcParameterEvolution(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -516,7 +506,7 @@ namespace M3{ Processor->ParameterEvolution(ParNames, Intervals); } - void ProcessMCMCPlugin::CalcBipolarPlot(MCMCProcessor* Processor) + void ProcessMCMCModule::CalcBipolarPlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -529,7 +519,7 @@ namespace M3{ Processor->GetPolarPlot(ParNames); } - void ProcessMCMCPlugin::GetTrianglePlot(MCMCProcessor* Processor) { + void ProcessMCMCModule::GetTrianglePlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -549,7 +539,7 @@ namespace M3{ } /// @brief KS: You validate stability of posterior covariance matrix, you set burn calc cov matrix increase burn calc again and compare. By performing such operation several hundred times we can check when matrix becomes stable - void ProcessMCMCPlugin::DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) + void ProcessMCMCModule::DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) { //Turn of plots from Processor Processor->SetPrintToPDF(false); @@ -725,7 +715,7 @@ namespace M3{ } //KS: Convert TMatrix to TH2D, mostly useful for making fancy plots - TH2D* ProcessMCMCPlugin::TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) + TH2D* ProcessMCMCModule::TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) { TH2D* hMatrix = new TH2D(title.c_str(), title.c_str(), Matrix->GetNrows(), 0.0, Matrix->GetNrows(), Matrix->GetNcols(), 0.0, Matrix->GetNcols()); for(int i = 0; i < Matrix->GetNrows(); i++) @@ -740,7 +730,7 @@ namespace M3{ } // KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution - void ProcessMCMCPlugin::KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, + void ProcessMCMCModule::KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, const std::unique_ptr<TCanvas>& Posterior, const TString& canvasname) { diff --git a/cli/modules/ProcessMCMCModule.hpp b/cli/modules/ProcessMCMCModule.hpp index 4d9b274b1..1e2e6c771 100644 --- a/cli/modules/ProcessMCMCModule.hpp +++ b/cli/modules/ProcessMCMCModule.hpp @@ -15,10 +15,10 @@ namespace M3{ /// @brief Main function processing MCMC and Producing plots - class ProcessMCMCPlugin: public IModule{ + class ProcessMCMCModule: public IModule{ public: - virtual ~ProcessMCMCPlugin(); + virtual ~ProcessMCMCModule(); MaCh3ArgumentParser* get_parser() override; int run() override; From c110f98d924725514f68381c2eb361bd35b1b400 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 16 Jun 2026 08:52:47 +0000 Subject: [PATCH 19/26] Add specific argparse tag --- cli/api/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/api/CMakeLists.txt b/cli/api/CMakeLists.txt index 5af71c036..eb490c1ac 100644 --- a/cli/api/CMakeLists.txt +++ b/cli/api/CMakeLists.txt @@ -2,6 +2,7 @@ include(FetchContent) FetchContent_Declare( argparse GIT_REPOSITORY https://github.com/p-ranav/argparse.git + GIT_TAG d924b84eba1f0f0adf38b20b7b4829f6f65b6570 ) FetchContent_MakeAvailable(argparse) From 39592fa220e1e9b0f851b02caf07eae00ad7e62d Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 16 Jun 2026 09:36:49 +0000 Subject: [PATCH 20/26] moving to upppercase directories --- {cli/api => CLI/API}/CMakeLists.txt | 4 +-- {cli/api => CLI/API}/argparse.hpp | 0 {cli/api => CLI/API}/plugin.hpp | 2 +- CLI/CMakeLists.txt | 12 +++++++ {cli => CLI}/DynamicPlugin.hpp | 2 +- {cli => CLI}/MaCh3Program.cpp | 2 +- {cli => CLI}/MaCh3Program.hpp | 4 +-- {cli/modules => CLI/Modules}/CMakeLists.txt | 0 .../Modules}/DiagMCMCModule.cpp | 2 +- .../Modules}/DiagMCMCModule.hpp | 2 +- .../Modules}/GetPenaltyTermModule.cpp | 2 +- .../Modules}/GetPenaltyTermModule.hpp | 2 +- .../Modules}/ProcessMCMCModule.cpp | 2 +- .../Modules}/ProcessMCMCModule.hpp | 2 +- {cli => CLI}/mach3.cpp | 8 ++--- CMakeLists.txt | 9 ++--- Diagnostics/CMakeLists.txt | 33 ------------------- Diagnostics/DiagMCMC.cpp | 2 +- Diagnostics/GetPenaltyTerm.cpp | 2 +- Diagnostics/ProcessMCMC.cpp | 2 +- cli/CMakeLists.txt | 21 ------------ 21 files changed, 34 insertions(+), 81 deletions(-) rename {cli/api => CLI/API}/CMakeLists.txt (88%) rename {cli/api => CLI/API}/argparse.hpp (100%) rename {cli/api => CLI/API}/plugin.hpp (96%) create mode 100644 CLI/CMakeLists.txt rename {cli => CLI}/DynamicPlugin.hpp (96%) rename {cli => CLI}/MaCh3Program.cpp (99%) rename {cli => CLI}/MaCh3Program.hpp (96%) rename {cli/modules => CLI/Modules}/CMakeLists.txt (100%) rename {cli/modules => CLI/Modules}/DiagMCMCModule.cpp (98%) rename {cli/modules => CLI/Modules}/DiagMCMCModule.hpp (89%) rename {cli/modules => CLI/Modules}/GetPenaltyTermModule.cpp (99%) rename {cli/modules => CLI/Modules}/GetPenaltyTermModule.hpp (97%) rename {cli/modules => CLI/Modules}/ProcessMCMCModule.cpp (99%) rename {cli/modules => CLI/Modules}/ProcessMCMCModule.hpp (98%) rename {cli => CLI}/mach3.cpp (89%) delete mode 100644 cli/CMakeLists.txt diff --git a/cli/api/CMakeLists.txt b/CLI/API/CMakeLists.txt similarity index 88% rename from cli/api/CMakeLists.txt rename to CLI/API/CMakeLists.txt index eb490c1ac..1dfc9f42e 100644 --- a/cli/api/CMakeLists.txt +++ b/CLI/API/CMakeLists.txt @@ -19,7 +19,7 @@ target_include_directories(CLIApi INTERFACE ${argparse_SOURCE_DIR}/include) target_include_directories(CLIApi INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> - $<INSTALL_INTERFACE:include/cli/api> + $<INSTALL_INTERFACE:include/CLI/API> ) set_target_properties(CLIApi PROPERTIES @@ -30,7 +30,7 @@ set_target_properties(CLIApi PROPERTIES install(TARGETS CLIApi EXPORT MaCh3-targets LIBRARY DESTINATION lib/ - PUBLIC_HEADER DESTINATION include/cli/api) + PUBLIC_HEADER DESTINATION include/CLI/API) #install(FILES ${HEADERS} DESTINATION include/api) add_library(MaCh3::CLIApi ALIAS CLIApi) \ No newline at end of file diff --git a/cli/api/argparse.hpp b/CLI/API/argparse.hpp similarity index 100% rename from cli/api/argparse.hpp rename to CLI/API/argparse.hpp diff --git a/cli/api/plugin.hpp b/CLI/API/plugin.hpp similarity index 96% rename from cli/api/plugin.hpp rename to CLI/API/plugin.hpp index b5e55b69b..018af1757 100644 --- a/cli/api/plugin.hpp +++ b/CLI/API/plugin.hpp @@ -1,5 +1,5 @@ #pragma once -#include "cli/api/argparse.hpp" +#include "CLI/API/argparse.hpp" namespace M3 { diff --git a/CLI/CMakeLists.txt b/CLI/CMakeLists.txt new file mode 100644 index 000000000..a10db45ae --- /dev/null +++ b/CLI/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(API) +add_subdirectory(Modules) + + +add_executable(mach3 + mach3.cpp + MaCh3Program.cpp +) + +target_link_libraries(mach3 PRIVATE MaCh3::CLIModules MaCh3::All) + +install(TARGETS mach3 DESTINATION bin) diff --git a/cli/DynamicPlugin.hpp b/CLI/DynamicPlugin.hpp similarity index 96% rename from cli/DynamicPlugin.hpp rename to CLI/DynamicPlugin.hpp index 9c962d067..f52b7a668 100644 --- a/cli/DynamicPlugin.hpp +++ b/CLI/DynamicPlugin.hpp @@ -1,6 +1,6 @@ #pragma once #include <dlfcn.h> -#include "cli/api/plugin.hpp" +#include "CLI/API/plugin.hpp" namespace M3{ class DynamicPlugin: public IPlugin{ diff --git a/cli/MaCh3Program.cpp b/CLI/MaCh3Program.cpp similarity index 99% rename from cli/MaCh3Program.cpp rename to CLI/MaCh3Program.cpp index 4323bf8d8..9d1f341d3 100644 --- a/cli/MaCh3Program.cpp +++ b/CLI/MaCh3Program.cpp @@ -3,7 +3,7 @@ #include <iostream> #include <sstream> #include "Manager/MaCh3Logger.h" -#include "cli/MaCh3Program.hpp" +#include "CLI/MaCh3Program.hpp" using std::vector, std::string, std::map; namespace fs = std::filesystem; diff --git a/cli/MaCh3Program.hpp b/CLI/MaCh3Program.hpp similarity index 96% rename from cli/MaCh3Program.hpp rename to CLI/MaCh3Program.hpp index 5e39c302d..c0cba5806 100644 --- a/cli/MaCh3Program.hpp +++ b/CLI/MaCh3Program.hpp @@ -3,8 +3,8 @@ #include <string> #include <vector> #include <filesystem> -#include "cli/api/plugin.hpp" -#include "cli/DynamicPlugin.hpp" +#include "CLI/API/plugin.hpp" +#include "CLI/DynamicPlugin.hpp" namespace fs = std::filesystem; diff --git a/cli/modules/CMakeLists.txt b/CLI/Modules/CMakeLists.txt similarity index 100% rename from cli/modules/CMakeLists.txt rename to CLI/Modules/CMakeLists.txt diff --git a/cli/modules/DiagMCMCModule.cpp b/CLI/Modules/DiagMCMCModule.cpp similarity index 98% rename from cli/modules/DiagMCMCModule.cpp rename to CLI/Modules/DiagMCMCModule.cpp index e77348cc7..2175c44b8 100644 --- a/cli/modules/DiagMCMCModule.cpp +++ b/CLI/Modules/DiagMCMCModule.cpp @@ -1,6 +1,6 @@ #include "Fitters/MCMCProcessor.h" #include "Manager/Manager.h" -#include "cli/modules/DiagMCMCModule.hpp" +#include "CLI/Modules/DiagMCMCModule.hpp" namespace M3{ diff --git a/cli/modules/DiagMCMCModule.hpp b/CLI/Modules/DiagMCMCModule.hpp similarity index 89% rename from cli/modules/DiagMCMCModule.hpp rename to CLI/Modules/DiagMCMCModule.hpp index 9f4f65dcd..f4b8abe50 100644 --- a/cli/modules/DiagMCMCModule.hpp +++ b/CLI/Modules/DiagMCMCModule.hpp @@ -1,5 +1,5 @@ #pragma once -#include "cli/api/plugin.hpp" +#include "CLI/API/plugin.hpp" namespace M3{ diff --git a/cli/modules/GetPenaltyTermModule.cpp b/CLI/Modules/GetPenaltyTermModule.cpp similarity index 99% rename from cli/modules/GetPenaltyTermModule.cpp rename to CLI/Modules/GetPenaltyTermModule.cpp index b3d0b90e0..64a0420da 100644 --- a/cli/modules/GetPenaltyTermModule.cpp +++ b/CLI/Modules/GetPenaltyTermModule.cpp @@ -3,7 +3,7 @@ #include "Samples/SampleStructs.h" #include "Samples/HistogramUtils.h" #include "Parameters/ParameterHandlerUtils.h" -#include "cli/modules/GetPenaltyTermModule.hpp" +#include "CLI/Modules/GetPenaltyTermModule.hpp" _MaCh3_Safe_Include_Start_ //{ // ROOT includes diff --git a/cli/modules/GetPenaltyTermModule.hpp b/CLI/Modules/GetPenaltyTermModule.hpp similarity index 97% rename from cli/modules/GetPenaltyTermModule.hpp rename to CLI/Modules/GetPenaltyTermModule.hpp index 4d64417d0..2b1684e8a 100644 --- a/cli/modules/GetPenaltyTermModule.hpp +++ b/CLI/Modules/GetPenaltyTermModule.hpp @@ -1,6 +1,6 @@ #pragma once #include "yaml-cpp/yaml.h" -#include "cli/api/plugin.hpp" +#include "CLI/API/plugin.hpp" namespace M3{ diff --git a/cli/modules/ProcessMCMCModule.cpp b/CLI/Modules/ProcessMCMCModule.cpp similarity index 99% rename from cli/modules/ProcessMCMCModule.cpp rename to CLI/Modules/ProcessMCMCModule.cpp index 5d3a869c9..f72be903c 100644 --- a/cli/modules/ProcessMCMCModule.cpp +++ b/CLI/Modules/ProcessMCMCModule.cpp @@ -1,7 +1,7 @@ //MaCh3 includes #include "Fitters/OscProcessor.h" #include "Manager/Manager.h" -#include "cli/modules/ProcessMCMCModule.hpp" +#include "CLI/Modules/ProcessMCMCModule.hpp" namespace M3{ diff --git a/cli/modules/ProcessMCMCModule.hpp b/CLI/Modules/ProcessMCMCModule.hpp similarity index 98% rename from cli/modules/ProcessMCMCModule.hpp rename to CLI/Modules/ProcessMCMCModule.hpp index 1e2e6c771..690f11cfe 100644 --- a/cli/modules/ProcessMCMCModule.hpp +++ b/CLI/Modules/ProcessMCMCModule.hpp @@ -3,7 +3,7 @@ // yaml Includes #include "yaml-cpp/yaml.h" #include "Fitters/MCMCProcessor.h" -#include "cli/api/plugin.hpp" +#include "CLI/API/plugin.hpp" namespace M3{ diff --git a/cli/mach3.cpp b/CLI/mach3.cpp similarity index 89% rename from cli/mach3.cpp rename to CLI/mach3.cpp index e8c4c7d3e..c930e6224 100644 --- a/cli/mach3.cpp +++ b/CLI/mach3.cpp @@ -1,7 +1,7 @@ -#include "cli/MaCh3Program.hpp" -#include "cli/modules/ProcessMCMCModule.hpp" -#include "cli/modules/DiagMCMCModule.hpp" -#include "cli/modules/GetPenaltyTermModule.hpp" +#include "CLI/Modules/ProcessMCMCModule.hpp" +#include "CLI/Modules/DiagMCMCModule.hpp" +#include "CLI/Modules/GetPenaltyTermModule.hpp" +#include "CLI/MaCh3Program.hpp" int main(int argc, char *argv[]) { diff --git a/CMakeLists.txt b/CMakeLists.txt index 31a7d2003..039a6e987 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,8 +281,7 @@ endforeach() ################################# Build MaCh3 ################################## # Build components -# add_subdirectory(api) -add_subdirectory(cli) +add_subdirectory(CLI) add_subdirectory(Manager) add_subdirectory(Parameters) add_subdirectory(Splines) @@ -308,11 +307,7 @@ if (MaCh3_NUDOCK_ENABLED) target_link_libraries(MaCh3 INTERFACE MaCh3NuDock) endif() target_link_libraries(MaCh3 INTERFACE CLIApi Fitters Samples Parameters Splines Manager MaCh3CompilerOptions MaCh3GPUCompilerOptions Plotting) -# Ensure API headers propagate to all consumers of MaCh3::All -# target_include_directories(MaCh3 INTERFACE -# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/api> -# $<INSTALL_INTERFACE:include/api> -# ) + set_target_properties(MaCh3 PROPERTIES EXPORT_NAME All) install(TARGETS MaCh3 diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index e0ecdf56e..52f7bf901 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -1,36 +1,3 @@ -# set(HEADERS -# ProcessMCMCPlugin.hpp -# ) - - -# add_library(CorePlugins STATIC -# ProcessMCMCPlugin.cpp -# DiagMCMCPlugin.cpp -# GetPenaltyTermPlugin.cpp -# ) - -# target_link_libraries(CorePlugins PRIVATE MaCh3::All) -# target_include_directories(CorePlugins PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>) - -# set_target_properties(Plugins PROPERTIES -# PUBLIC_HEADER "${HEADERS}" -# EXPORT_NAME Plugins) - - -# install(TARGETS Plugins -# EXPORT MaCh3-targets -# LIBRARY DESTINATION lib/ -# PUBLIC_HEADER DESTINATION include/Plugins) - -# add_library(MaCh3::CorePlugins ALIAS CorePlugins) - -# set(CLI_MODULES -# ProcessMCMCPlugin.cpp -# DiagMCMCPlugin.cpp -# GetPenaltyTermPlugin.cpp -# ) - -# list(TRANSFORM CLI_MODULES PREPEND "../cli/modules/") add_custom_target(DiagApps) diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index cf30e1031..26bf4f0c4 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/DiagMCMCModule.hpp" +#include "CLI/Modules/DiagMCMCModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index d6f9b6dbd..5f33ec1fd 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/GetPenaltyTermModule.hpp" +#include "CLI/Modules/GetPenaltyTermModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index 92bd54f03..92b62b556 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -1,5 +1,5 @@ #include "Manager/MaCh3Logger.h" -#include "cli/modules/ProcessMCMCModule.hpp" +#include "CLI/Modules/ProcessMCMCModule.hpp" int main(int argc, char const* argv[]){ MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt deleted file mode 100644 index 71a49afa6..000000000 --- a/cli/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_subdirectory(api) -add_subdirectory(modules) -# set(CLI_MODULES -# ProcessMCMCPlugin.cpp -# DiagMCMCPlugin.cpp -# GetPenaltyTermPlugin.cpp -# ) - -# list(TRANSFORM CLI_MODULES PREPEND "modules/") - - - -add_executable(mach3 - mach3.cpp - MaCh3Program.cpp - # ${CLI_MODULES} -) - -target_link_libraries(mach3 PRIVATE MaCh3::CLIModules MaCh3::All) - -install(TARGETS mach3 DESTINATION bin) From 84cc8a7c2cdd4a4daab21ecc53dfb2e77390a1e3 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Thu, 18 Jun 2026 08:56:47 +0000 Subject: [PATCH 21/26] Adding Doxygen comments --- CLI/API/argparse.hpp | 30 +++++++ CLI/API/plugin.hpp | 53 +++++++++++- CLI/DynamicPlugin.hpp | 38 ++++++++- CLI/MaCh3Program.cpp | 33 ++++++++ CLI/MaCh3Program.hpp | 73 +++++++++++++++-- CLI/Modules/DiagMCMCModule.cpp | 7 +- CLI/Modules/DiagMCMCModule.hpp | 27 ++++++- CLI/Modules/GetPenaltyTermModule.cpp | 60 +++++++++++--- CLI/Modules/GetPenaltyTermModule.hpp | 53 +++++++++++- CLI/Modules/ProcessMCMCModule.cpp | 101 ++++++++++++++++++++++- CLI/Modules/ProcessMCMCModule.hpp | 117 +++++++++++++++++++++++---- CLI/mach3.cpp | 19 ++++- 12 files changed, 567 insertions(+), 44 deletions(-) diff --git a/CLI/API/argparse.hpp b/CLI/API/argparse.hpp index 377d3a3a5..a0930cb8b 100644 --- a/CLI/API/argparse.hpp +++ b/CLI/API/argparse.hpp @@ -1,3 +1,8 @@ +/** + * @file argparse.hpp + * @brief MaCh3 wrapper for argparse library with additional functionality + */ + #pragma once #include <argparse/argparse.hpp> @@ -5,18 +10,43 @@ using argparse::ArgumentParser; namespace M3 { + /** + * @class MaCh3ArgumentParser + * @brief Extended ArgumentParser with MaCh3-specific functionality + * + * This class extends the standard ArgumentParser to provide additional + * methods for accessing parser name, subparsers, and tracking which + * subcommand was used. + */ class MaCh3ArgumentParser: public ArgumentParser{ public: using ArgumentParser::ArgumentParser; virtual ~MaCh3ArgumentParser() = default; + /** + * @brief Get the name of this parser/subcommand + * @return The program or subcommand name + */ const std::string name() const{ return this->m_program_name; } + + /** + * @brief Get the list of registered subparsers + * @return Reference to the list of subparsers + */ const std::list<std::reference_wrapper<ArgumentParser>>& subparsers() const{ return this->m_subparsers; } + /** + * @brief Get the subcommand that was used in parsing + * + * Recursively traverses the subparser hierarchy to find the + * deepest subcommand that was actually invoked. + * + * @return Reference to the MaCh3ArgumentParser of the used subcommand + */ const MaCh3ArgumentParser& get_subcommand_used() const{ for (const std::reference_wrapper<ArgumentParser>& subparser : this->m_subparsers) { if (this->is_subcommand_used(subparser.get())) { diff --git a/CLI/API/plugin.hpp b/CLI/API/plugin.hpp index 018af1757..3facabe9a 100644 --- a/CLI/API/plugin.hpp +++ b/CLI/API/plugin.hpp @@ -1,25 +1,76 @@ +/** + * @file plugin.hpp + * @brief Plugin interface and registration macros for MaCh3 + */ + #pragma once #include "CLI/API/argparse.hpp" namespace M3 { + /** + * @class IPlugin + * @brief Interface for MaCh3 plugins and modules + * + * All plugins and modules must implement this interface to provide + * argument parsing and execution functionality. + */ class IPlugin { public: virtual ~IPlugin() = default; + + /** + * @brief Execute the plugin's main functionality + * @return Exit code (0 on success, non-zero on error) + */ virtual int run() = 0; + + /** + * @brief Get the argument parser for this plugin + * @return Pointer to the plugin's MaCh3ArgumentParser + */ virtual MaCh3ArgumentParser* get_parser() = 0; }; + /** + * @typedef IModule + * @brief Alias for IPlugin, used for core modules + */ typedef IPlugin IModule; }; -// Factory function typedefs +/** + * @typedef create_plugin_t + * @brief Function pointer type for plugin factory function + */ extern "C" { typedef M3::IPlugin* (*create_plugin_t)(); + + /** + * @typedef destroy_plugin_t + * @brief Function pointer type for plugin destructor function + */ typedef void (*destroy_plugin_t)(M3::IPlugin*); } +/** + * @def MACH3_REGISTER_PLUGIN + * @brief Macro to register a plugin class with MaCh3 + * + * This macro generates the required factory and destructor functions + * for a plugin class. Use it in your plugin implementation file. + * + * @param PluginClass The plugin class that implements M3::IPlugin + * + * Example usage: + * @code + * class MyPlugin : public M3::IPlugin { + * // ... implementation + * }; + * MACH3_REGISTER_PLUGIN(MyPlugin) + * @endcode + */ #define MACH3_REGISTER_PLUGIN(PluginClass) \ static_assert(std::is_base_of<M3::IPlugin, PluginClass>::value, "PluginClass must derive from M3::IPlugin"); \ extern "C" M3::IPlugin* create_plugin() { \ diff --git a/CLI/DynamicPlugin.hpp b/CLI/DynamicPlugin.hpp index f52b7a668..167a9a742 100644 --- a/CLI/DynamicPlugin.hpp +++ b/CLI/DynamicPlugin.hpp @@ -1,23 +1,55 @@ +/** + * @file DynamicPlugin.hpp + * @brief Wrapper class for dynamically loaded plugins + */ + #pragma once #include <dlfcn.h> #include "CLI/API/plugin.hpp" namespace M3{ + /** + * @class DynamicPlugin + * @brief Manages the lifecycle of a dynamically loaded plugin + * + * This class wraps a plugin loaded from a shared library, maintaining the + * library handle and providing cleanup functionality. + */ class DynamicPlugin: public IPlugin{ public: + /** + * @brief Constructor for a dynamic plugin + * @param handle Handle to the loaded shared library + * @param plugin Pointer to the instantiated plugin + * @param destroy_func Function pointer to destroy the plugin + */ DynamicPlugin(void* handle, IPlugin* plugin, destroy_plugin_t destroy_func):m_handle(handle), m_plugin(plugin), m_destroy_func(destroy_func){} + /** + * @brief Run the plugin's main functionality + * @return Exit code from the plugin + */ int run() { return m_plugin->run(); } + + /** + * @brief Get the argument parser for this plugin + * @return Pointer to the plugin's argument parser + */ MaCh3ArgumentParser* get_parser(){ return m_plugin->get_parser(); } + /** + * @brief Destroy the plugin and close the shared library + * + * Calls the plugin's destroy function and closes the library handle. + */ void destroy(){ m_destroy_func(m_plugin); m_plugin = 0; @@ -27,8 +59,8 @@ namespace M3{ } private: - void* m_handle; - IPlugin* m_plugin; - destroy_plugin_t m_destroy_func; + void* m_handle; ///< Handle to the loaded shared library + IPlugin* m_plugin; ///< Pointer to the plugin instance + destroy_plugin_t m_destroy_func; ///< Function pointer to destroy the plugin }; }; \ No newline at end of file diff --git a/CLI/MaCh3Program.cpp b/CLI/MaCh3Program.cpp index 9d1f341d3..a254ac6ea 100644 --- a/CLI/MaCh3Program.cpp +++ b/CLI/MaCh3Program.cpp @@ -1,3 +1,8 @@ +/** + * @file MaCh3Program.cpp + * @brief Implementation of the MaCh3Program class + */ + #include <dlfcn.h> #include <filesystem> #include <iostream> @@ -105,6 +110,13 @@ namespace M3{ m_subcommands.push_back(parser->name()); } + /** + * @brief Load dynamic plugins from paths specified in MACH3_PLUGINS environment variable + * + * Reads the MACH3_PLUGINS environment variable (colon-separated paths), loads each .so file, + * and registers the plugins with the program. Each plugin must export create_plugin and + * destroy_plugin functions. + */ void MaCh3Program::load_dynamic_plugins(){ const char* env = std::getenv("MACH3_PLUGINS"); if (!env) { @@ -175,6 +187,11 @@ namespace M3{ } } + /** + * @brief Unload all dynamic plugins and clean up resources + * + * Calls the destroy function for each loaded plugin and closes the dynamic library handles. + */ void MaCh3Program::unload_dynamic_plugins(){ for (auto& [_, plugin] : m_dynamic_plugin_map){ plugin->destroy(); @@ -182,6 +199,13 @@ namespace M3{ m_dynamic_plugin_map.clear(); } + /** + * @brief Execute the selected subcommand + * + * Determines which subcommand was invoked and runs the corresponding module or plugin. + * + * @return Exit code from the executed module (0 on success) + */ int MaCh3Program::run(){ const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); @@ -200,6 +224,15 @@ namespace M3{ } + /** + * @brief Expand a plugin path to a list of shared library files + * + * If the path is a file, returns it directly. If it's a directory, + * returns all .so files found in that directory. + * + * @param path Path to a plugin file or directory + * @return Vector of absolute paths to .so files + */ std::vector<std::string> MaCh3Program::expand_plugin_path(const std::string& path) const { std::vector<std::string> result; diff --git a/CLI/MaCh3Program.hpp b/CLI/MaCh3Program.hpp index c0cba5806..47ccb5853 100644 --- a/CLI/MaCh3Program.hpp +++ b/CLI/MaCh3Program.hpp @@ -1,3 +1,8 @@ +/** + * @file MaCh3Program.hpp + * @brief Core program class for the MaCh3 command-line interface + */ + #pragma once #include <map> #include <string> @@ -10,32 +15,86 @@ namespace fs = std::filesystem; namespace M3{ - // class NoArgsException: public std::exception{}; - + /** + * @class MaCh3Program + * @brief Main program class that manages modules, plugins, and command-line parsing + * + * This class extends MaCh3ArgumentParser to provide functionality for: + * - Managing core modules and dynamic plugins + * - Installing and generating shell completions + * - Running selected subcommands + */ class MaCh3Program: public MaCh3ArgumentParser{ public: using MaCh3ArgumentParser::MaCh3ArgumentParser; + + /** + * @brief Destructor that unloads all dynamic plugins + */ virtual ~MaCh3Program(){ //std::cout<<"UNLOADED"<< std::endl; this->unload_dynamic_plugins(); - } - // void parse_args(int argc, const char *const argv[]); + } + + /** + * @brief Add a core module to the program + * @param module Reference to the module to be added + */ void add_core_module(IModule& module); + + /** + * @brief Load dynamic plugins from the MACH3_PLUGINS environment variable + */ void load_dynamic_plugins(); + + /** + * @brief Install shell completion scripts for bash and zsh + */ const void install_completions() const; + + /** + * @brief Generate completion suggestions for the given prefix + * @param prefix The prefix string to match against available subcommands + */ const void completions(const std::string& prefix) const; + + /** + * @brief Unload all dynamically loaded plugins + */ void unload_dynamic_plugins(); + + /** + * @brief Run the selected subcommand + * @return Exit code from the executed module + */ int run(); private: + /** + * @brief Detect the user's shell from the SHELL environment variable + * @return Shell name ("bash", "zsh", or empty string if not detected) + */ const std::string detect_shell() const; + + /** + * @brief Write content to a file, creating parent directories if needed + * @param path Filesystem path to write to + * @param content Content to write to the file + * @return true if successful, false otherwise + */ const bool write_file(const fs::path& path, std::string_view content) const; + + /** + * @brief Expand a plugin path (file or directory) into a list of .so files + * @param path Path to a plugin file or directory containing plugins + * @return Vector of plugin file paths + */ std::vector<std::string> expand_plugin_path(const std::string& path) const; private: - std::vector<std::string> m_subcommands; - std::map<const MaCh3ArgumentParser*, IModule*> m_module_map; - std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; + std::vector<std::string> m_subcommands; ///< List of registered subcommand names + std::map<const MaCh3ArgumentParser*, IModule*> m_module_map; ///< Map of parsers to core modules + std::map<const MaCh3ArgumentParser*, DynamicPlugin*> m_dynamic_plugin_map; ///< Map of parsers to dynamic plugins static constexpr std::string_view BASH_COMPLETION = R"( _mach3_complete() { diff --git a/CLI/Modules/DiagMCMCModule.cpp b/CLI/Modules/DiagMCMCModule.cpp index 2175c44b8..62bc4ff6b 100644 --- a/CLI/Modules/DiagMCMCModule.cpp +++ b/CLI/Modules/DiagMCMCModule.cpp @@ -1,10 +1,15 @@ +/** + * @file DiagMCMCModule.cpp + * @brief Implementation of the DiagMCMCModule class + */ + #include "Fitters/MCMCProcessor.h" #include "Manager/Manager.h" #include "CLI/Modules/DiagMCMCModule.hpp" namespace M3{ - + DiagMCMCModule::~DiagMCMCModule(){ if (this->m_parser) { delete this->m_parser; } } diff --git a/CLI/Modules/DiagMCMCModule.hpp b/CLI/Modules/DiagMCMCModule.hpp index f4b8abe50..c70b6e315 100644 --- a/CLI/Modules/DiagMCMCModule.hpp +++ b/CLI/Modules/DiagMCMCModule.hpp @@ -1,17 +1,42 @@ +/** + * @file DiagMCMCModule.hpp + * @brief Module for MCMC diagnostic analysis + */ + #pragma once #include "CLI/API/plugin.hpp" namespace M3{ + /** + * @class DiagMCMCModule + * @brief Module for performing diagnostic analysis on MCMC chains + * + * This module provides diagnostics such as convergence tests, autocorrelation + * analysis, and batch means calculations to assess the quality of MCMC samples. + */ class DiagMCMCModule: public IModule{ public: + /** + * @brief Destructor + */ virtual ~DiagMCMCModule(); + + /** + * @brief Get the argument parser for this module + * @return Pointer to the configured MaCh3ArgumentParser + */ MaCh3ArgumentParser* get_parser() override; + + /** + * @brief Execute the MCMC diagnostics + * @return Exit code (0 on success) + */ int run() override; private: - MaCh3ArgumentParser* m_parser; + MaCh3ArgumentParser* m_parser; ///< Argument parser for this module }; }; diff --git a/CLI/Modules/GetPenaltyTermModule.cpp b/CLI/Modules/GetPenaltyTermModule.cpp index 64a0420da..6c66d5cfe 100644 --- a/CLI/Modules/GetPenaltyTermModule.cpp +++ b/CLI/Modules/GetPenaltyTermModule.cpp @@ -1,3 +1,17 @@ +/** + * @file GetPenaltyTerm.cpp + * @brief Implementation of penalty term extraction from systematic chains + * + * This script is designed to retrieve penalty terms from various sources, + * such as flux and cross-section systematic chains. Since flux and cross-section + * uncertainties are handled systematically, the penalty term cannot be taken + * directly from the chain. + * + * @todo This should really be moved to MCMC Processor + * @ingroup MaCh3DiagnosticProcessing + * @author Kamil Skwarczynski + */ + // MaCh3 includes #include "Manager/Manager.h" #include "Samples/SampleStructs.h" @@ -25,16 +39,6 @@ _MaCh3_Safe_Include_Start_ //{ #include "TROOT.h" _MaCh3_Safe_Include_End_ //} -/// @file GetPenaltyTerm.cpp -/// @brief KS: This file contains the implementation of the function to extract specific penalty terms from systematic chains. -/// -/// This script is designed to retrieve penalty terms from various sources, such as flux and cross-section systematic chains. -/// Since flux and cross-section uncertainties are handled systematically, the penalty term cannot be taken directly from the chain. -/// @todo KS: This should really be moved to MCMC Processor -/// -/// @ingroup MaCh3DiagnosticProcessing -/// -/// @author Kamil Skwarczynski namespace M3{ GetPenaltyTermModule::~GetPenaltyTermModule(){ @@ -65,6 +69,19 @@ namespace M3{ return 0; } + /** + * @brief Read covariance matrix and prior information from ROOT file + * + * Loads the covariance matrix, inverts it, and extracts parameter priors + * and flat prior flags from the MCMC output file. + * + * @param inputFile Path to the ROOT file containing MCMC output + * @param Prior Vector to be filled with prior parameter values + * @param isFlat Vector to be filled with flat prior flags + * @param ParamNames Vector to be filled with parameter names + * @param invCovMatrix 2D vector to be filled with inverted covariance matrix + * @param nParams Reference to store the number of parameters + */ void GetPenaltyTermModule::ReadCovFile(const std::string& inputFile, std::vector <double>& Prior, std::vector <bool>& isFlat, @@ -149,6 +166,19 @@ namespace M3{ delete TempFile; } + /** + * @brief Load penalty term set definitions from YAML configuration + * + * Parses the configuration to determine which parameters belong to each + * penalty term set, based on either inclusion or exclusion patterns. + * + * @param Settings YAML configuration node + * @param SetsNames Vector to be filled with set names + * @param FancyTitle Vector to be filled with fancy titles for plotting + * @param isRelevantParam 2D vector to be filled with parameter relevance flags + * @param ParamNames Vector of all parameter names from the covariance + * @param nParams Total number of parameters + */ void GetPenaltyTermModule::LoadSettings(YAML::Node& Settings, std::vector<std::string>& SetsNames, std::vector<std::string>& FancyTitle, @@ -220,6 +250,16 @@ namespace M3{ } } + /** + * @brief Calculate and plot penalty terms for each parameter set + * + * Main function that reads the covariance, loads configuration, iterates + * through MCMC steps, and calculates penalty terms for each defined set. + * Results are saved to ROOT file and PDF plots. + * + * @param inputFile Path to the MCMC chain ROOT file + * @param configFile Path to the YAML configuration file + */ void GetPenaltyTermModule::GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) { auto canvas = std::make_unique<TCanvas>("canvas", "canvas", 0, 0, 1024, 1024); diff --git a/CLI/Modules/GetPenaltyTermModule.hpp b/CLI/Modules/GetPenaltyTermModule.hpp index 2b1684e8a..4a940a713 100644 --- a/CLI/Modules/GetPenaltyTermModule.hpp +++ b/CLI/Modules/GetPenaltyTermModule.hpp @@ -1,32 +1,83 @@ +/** + * @file GetPenaltyTermModule.hpp + * @brief Module for extracting penalty terms from systematic chains + */ + #pragma once #include "yaml-cpp/yaml.h" #include "CLI/API/plugin.hpp" namespace M3{ + /** + * @class GetPenaltyTermModule + * @brief Module for extracting specific penalty terms from MCMC chains + * + * This module retrieves penalty terms from flux and cross-section systematic + * chains, since these systematic uncertainties are handled separately and the + * penalty term cannot be taken directly from the chain. + */ class GetPenaltyTermModule: public IModule{ public: + /** + * @brief Destructor + */ virtual ~GetPenaltyTermModule(); + + /** + * @brief Get the argument parser for this module + * @return Pointer to the configured MaCh3ArgumentParser + */ MaCh3ArgumentParser* get_parser() override; + + /** + * @brief Execute the penalty term extraction + * @return Exit code (0 on success) + */ int run() override; private: + /** + * @brief Read covariance matrix and parameter information from file + * @param inputFile Path to the ROOT file containing the covariance + * @param Prior Vector to store prior parameter values + * @param isFlat Vector to store flat prior flags + * @param ParamNames Vector to store parameter names + * @param invCovMatrix 2D vector to store inverted covariance matrix + * @param nParams Reference to store the number of parameters + */ void ReadCovFile(const std::string& inputFile, std::vector <double>& Prior, std::vector <bool>& isFlat, std::vector<std::string>& ParamNames, std::vector<std::vector<double>>& invCovMatrix, int& nParams); + + /** + * @brief Load penalty term sets from YAML configuration + * @param Settings YAML configuration node + * @param SetsNames Vector to store set names + * @param FancyTitle Vector to store fancy titles for each set + * @param isRelevantParam 2D vector indicating which parameters belong to each set + * @param ParamNames Vector of all parameter names + * @param nParams Total number of parameters + */ void LoadSettings(YAML::Node& Settings, std::vector<std::string>& SetsNames, std::vector<std::string>& FancyTitle, std::vector<std::vector<bool>>& isRelevantParam, const std::vector<std::string>& ParamNames, const int nParams); + + /** + * @brief Calculate and plot penalty terms for parameter sets + * @param inputFile Path to the MCMC chain ROOT file + * @param configFile Path to the YAML configuration file + */ void GetPenaltyTerm(const std::string& inputFile, const std::string& configFile); private: - MaCh3ArgumentParser* m_parser; + MaCh3ArgumentParser* m_parser; ///< Argument parser for this module }; }; diff --git a/CLI/Modules/ProcessMCMCModule.cpp b/CLI/Modules/ProcessMCMCModule.cpp index f72be903c..86e2a3215 100644 --- a/CLI/Modules/ProcessMCMCModule.cpp +++ b/CLI/Modules/ProcessMCMCModule.cpp @@ -1,3 +1,8 @@ +/** + * @file ProcessMCMCModule.cpp + * @brief Implementation of the ProcessMCMCModule class + */ + //MaCh3 includes #include "Fitters/OscProcessor.h" #include "Manager/Manager.h" @@ -147,6 +152,15 @@ namespace M3{ return CustomBinning; } + /** + * @brief Process a single MCMC chain file + * + * Loads the MCMC chain, applies configuration settings, and produces + * various diagnostic plots and analyses including posteriors, correlations, + * credible intervals, and Bayes factors. + * + * @param inputFile Path to the MCMC chain ROOT file + */ void ProcessMCMCModule::ProcessMCMC(const std::string& inputFile) { MACH3LOG_INFO("File for study: {} with config {}", inputFile, config); @@ -244,6 +258,13 @@ namespace M3{ if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); } + /** + * @brief Compare multiple MCMC chains + * + * Processes multiple MCMC chains simultaneously, compares their posterior + * distributions, and performs Kolmogorov-Smirnov tests to check if posteriors + * are consistent across chains. + */ void ProcessMCMCModule::MultipleProcessMCMC() { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -452,7 +473,15 @@ namespace M3{ Posterior->Print(canvasname); } - /// @brief KS: Calculate Bayes factor for a given hypothesis, most informative are those related to osc params. However, it make relative easy interpretation for switch dials + /** + * @brief Calculate Bayes factors for hypothesis testing + * + * Computes Bayes factors to compare different models or hypotheses, + * particularly useful for oscillation parameters and switch parameters. + * Configuration is read from the YAML file under BayesFactor section. + * + * @param Processor Pointer to the MCMCProcessor instance + */ void ProcessMCMCModule::CalcBayesFactor(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -473,6 +502,15 @@ namespace M3{ Processor->GetBayesFactor(ParNames, Model1Bounds, Model2Bounds, ModelNames); } + /** + * @brief Calculate Savage-Dickey ratios + * + * Computes Savage-Dickey ratios for Bayes factor estimation at specific + * parameter values. Configuration is read from the YAML file under + * SavageDickey section. + * + * @param Processor Pointer to the MCMCProcessor instance + */ void ProcessMCMCModule::CalcSavageDickey(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -491,6 +529,15 @@ namespace M3{ Processor->GetSavageDickey(ParNames, EvaluationPoint, Bounds); } + /** + * @brief Calculate parameter evolution over MCMC steps + * + * Tracks how parameters evolve during the MCMC chain, useful for + * diagnosing convergence and burn-in. Configuration is read from + * the YAML file under ParameterEvolution section. + * + * @param Processor Pointer to the MCMCProcessor instance + */ void ProcessMCMCModule::CalcParameterEvolution(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -506,6 +553,15 @@ namespace M3{ Processor->ParameterEvolution(ParNames, Intervals); } + /** + * @brief Create bipolar plots for parameter visualization + * + * Generates bipolar plots to visualize parameter distributions in a + * circular representation. Configuration is read from the YAML file + * under BipolarPlot section. + * + * @param Processor Pointer to the MCMCProcessor instance + */ void ProcessMCMCModule::CalcBipolarPlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -519,6 +575,15 @@ namespace M3{ Processor->GetPolarPlot(ParNames); } + /** + * @brief Generate triangle plots showing parameter correlations + * + * Creates triangle plots displaying 1D and 2D posterior distributions + * for sets of correlated parameters. Configuration is read from the + * YAML file under TrianglePlot section. + * + * @param Processor Pointer to the MCMCProcessor instance + */ void ProcessMCMCModule::GetTrianglePlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -538,7 +603,16 @@ namespace M3{ } } - /// @brief KS: You validate stability of posterior covariance matrix, you set burn calc cov matrix increase burn calc again and compare. By performing such operation several hundred times we can check when matrix becomes stable + /** + * @brief Diagnose covariance matrix stability + * + * Validates the stability of the posterior covariance matrix by computing + * it at different burn-in cuts and comparing successive matrices. This helps + * determine when the matrix becomes stable and the appropriate burn-in length. + * + * @param Processor Pointer to the MCMCProcessor instance + * @param inputFile Path to the input file for naming output files + */ void ProcessMCMCModule::DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) { //Turn of plots from Processor @@ -714,7 +788,16 @@ namespace M3{ if(CorrelationHist != nullptr) delete CorrelationHist; } - //KS: Convert TMatrix to TH2D, mostly useful for making fancy plots + /** + * @brief Convert TMatrixDSym to TH2D histogram + * + * Converts a ROOT symmetric matrix to a 2D histogram for visualization + * and easier manipulation with ROOT plotting tools. + * + * @param Matrix Pointer to the symmetric matrix + * @param title Title for the resulting histogram + * @return Pointer to the newly created TH2D histogram + */ TH2D* ProcessMCMCModule::TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) { TH2D* hMatrix = new TH2D(title.c_str(), title.c_str(), Matrix->GetNrows(), 0.0, Matrix->GetNrows(), Matrix->GetNcols(), 0.0, Matrix->GetNcols()); @@ -729,7 +812,17 @@ namespace M3{ return hMatrix; } - // KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution + /** + * @brief Perform Kolmogorov-Smirnov test between posterior distributions + * + * Tests whether posterior distributions from different MCMC chains are + * consistent by computing the KS test statistic for each parameter. + * Results are visualized showing cumulative distributions and D-statistic. + * + * @param Processor Vector of MCMCProcessor instances (one per chain) + * @param Posterior Canvas for drawing the comparison plots + * @param canvasname Name for the output PDF file + */ void ProcessMCMCModule::KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, const std::unique_ptr<TCanvas>& Posterior, const TString& canvasname) diff --git a/CLI/Modules/ProcessMCMCModule.hpp b/CLI/Modules/ProcessMCMCModule.hpp index 690f11cfe..22662cfdc 100644 --- a/CLI/Modules/ProcessMCMCModule.hpp +++ b/CLI/Modules/ProcessMCMCModule.hpp @@ -1,3 +1,8 @@ +/** + * @file ProcessMCMCModule.hpp + * @brief Module for processing MCMC chains and producing diagnostic plots + */ + //MaCh3 includes #pragma once // yaml Includes @@ -7,43 +12,125 @@ namespace M3{ -/// @file ProcessMCMC.cpp -/// @brief Main exectable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implantation of methods is in MCMCProcessor -/// @ingroup MaCh3DiagnosticProcessing -/// -/// @author Kamil Skwarczynski - -/// @brief Main function processing MCMC and Producing plots +/** + * @file ProcessMCMC.cpp + * @brief Main executable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implementation of methods is in MCMCProcessor + * @ingroup MaCh3DiagnosticProcessing + * + * @author Kamil Skwarczynski + */ +/** + * @class ProcessMCMCModule + * @brief Module for processing MCMC chains and producing various diagnostic plots + * + * This module provides functionality for: + * - Drawing posterior distributions + * - Creating triangle plots + * - Computing Bayes factors + * - Performing Savage-Dickey tests + * - Calculating parameter evolution + * - Comparing multiple MCMC chains + */ class ProcessMCMCModule: public IModule{ public: + /** + * @brief Destructor + */ virtual ~ProcessMCMCModule(); + + /** + * @brief Get the argument parser for this module + * @return Pointer to the configured MaCh3ArgumentParser + */ MaCh3ArgumentParser* get_parser() override; + + /** + * @brief Execute the MCMC processing + * @return Exit code (0 on success) + */ int run() override; private: + /** + * @brief Parse custom binning edges from YAML configuration + * @param Settings YAML node containing CustomBinEdges section + * @return Map of parameter names to their (min, max) bin edges + */ std::map<std::string, std::pair<double, double>> GetCustomBinning(const YAML::Node& Settings); + + /** + * @brief Process a single MCMC chain + * @param inputFile Path to the MCMC chain ROOT file + */ void ProcessMCMC(const std::string& inputFile); - /// @brief Function producing comparison of posterior and more betwen a few MCMC chains + + /** + * @brief Compare and process multiple MCMC chains + */ void MultipleProcessMCMC(); + + /** + * @brief Calculate Bayes factors for hypothesis testing + * @param Processor Pointer to the MCMCProcessor instance + */ void CalcBayesFactor(MCMCProcessor* Processor); + + /** + * @brief Calculate Savage-Dickey ratios for Bayes factor estimation + * @param Processor Pointer to the MCMCProcessor instance + */ void CalcSavageDickey(MCMCProcessor* Processor); + + /** + * @brief Calculate parameter evolution over MCMC steps + * @param Processor Pointer to the MCMCProcessor instance + */ void CalcParameterEvolution(MCMCProcessor* Processor); + + /** + * @brief Create bipolar plots for parameter visualization + * @param Processor Pointer to the MCMCProcessor instance + */ void CalcBipolarPlot(MCMCProcessor* Processor); + + /** + * @brief Generate triangle plots showing parameter correlations + * @param Processor Pointer to the MCMCProcessor instance + */ void GetTrianglePlot(MCMCProcessor* Processor); + + /** + * @brief Diagnose covariance matrix stability across burn-in cuts + * @param Processor Pointer to the MCMCProcessor instance + * @param inputFile Path to the input file for naming output + */ void DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile); - /// @brief KS: Convert TMatrix to TH2D, mostly useful for making fancy plots + + /** + * @brief Convert TMatrixDSym to TH2D histogram for plotting + * @param Matrix Pointer to the symmetric matrix + * @param title Title for the resulting histogram + * @return Pointer to the created TH2D histogram + */ TH2D* TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title); - /// @brief KS: Perform KS test to check if two posteriors for the same parameter came from the same distribution + + /** + * @brief Perform Kolmogorov-Smirnov test between posterior distributions + * @param Processor Vector of MCMCProcessor instances + * @param Posterior Canvas for drawing results + * @param canvasname Name for the output PDF + */ void KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, const std::unique_ptr<TCanvas>& Posterior, const TString& canvasname); - int nFiles; - std::vector <std::string> FileNames; - std::vector <std::string> TitleNames; - std::string config; - MaCh3ArgumentParser* m_parser; + + int nFiles; ///< Number of MCMC files being processed + std::vector <std::string> FileNames; ///< List of MCMC chain file paths + std::vector <std::string> TitleNames; ///< List of titles for each chain + std::string config; ///< Path to the configuration file + MaCh3ArgumentParser* m_parser; ///< Argument parser for this module }; }; diff --git a/CLI/mach3.cpp b/CLI/mach3.cpp index c930e6224..e9d33bf0e 100644 --- a/CLI/mach3.cpp +++ b/CLI/mach3.cpp @@ -1,9 +1,26 @@ +/** + * @file mach3.cpp + * @brief Main entry point for the MaCh3 command-line interface + * + * This file contains the main function that initializes the MaCh3 program, + * registers core modules, loads dynamic plugins, and processes command-line arguments. + */ + #include "CLI/Modules/ProcessMCMCModule.hpp" #include "CLI/Modules/DiagMCMCModule.hpp" #include "CLI/Modules/GetPenaltyTermModule.hpp" #include "CLI/MaCh3Program.hpp" - +/** + * @brief Main entry point for the MaCh3 application + * + * Initializes the MaCh3 program with core modules and dynamic plugins, + * parses command-line arguments, and executes the selected subcommand. + * + * @param argc Number of command-line arguments + * @param argv Array of command-line argument strings + * @return Exit code (0 on success, non-zero on error) + */ int main(int argc, char *argv[]) { M3::MaCh3Program program("mach3"); From d8121f80902cf5a8de5bab6c181ddf995518dfc5 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Fri, 19 Jun 2026 13:54:59 +0000 Subject: [PATCH 22/26] Pull completions for now --- CLI/mach3.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/CLI/mach3.cpp b/CLI/mach3.cpp index e9d33bf0e..ac2cde462 100644 --- a/CLI/mach3.cpp +++ b/CLI/mach3.cpp @@ -24,15 +24,15 @@ int main(int argc, char *argv[]) { M3::MaCh3Program program("mach3"); - // Hidden completion option - program.add_argument("--complete") - .hidden() - .nargs(1); + // // Hidden completion option + // program.add_argument("--complete") + // .hidden() + // .nargs(1); - // Hidden installer option - program.add_argument("--install-completions") - .help("") - .flag(); + // // Hidden installer option + // program.add_argument("--install-completions") + // .help("") + // .flag(); M3::ProcessMCMCModule proc; M3::DiagMCMCModule diag; @@ -47,11 +47,11 @@ int main(int argc, char *argv[]) { program.parse_args(argc, argv); } catch (const std::exception& err) { - // completions count as parsing error - if (auto prefix = program.present<std::string>("--complete")) { - program.completions(*prefix); - return 0; - } + // // completions count as parsing error + // if (auto prefix = program.present<std::string>("--complete")) { + // program.completions(*prefix); + // return 0; + // } std::cerr << err.what() << std::endl; std::cerr << program.get_subcommand_used(); @@ -70,10 +70,10 @@ int main(int argc, char *argv[]) { } - if (program.get<bool>("--install-completions")) { - program.install_completions(); - return 0; - } + // if (program.get<bool>("--install-completions")) { + // program.install_completions(); + // return 0; + // } try{ program.run(); From c539201cfbf8b2eef5f33e13a3ceebff6d69f397 Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 23 Jun 2026 11:37:48 +0000 Subject: [PATCH 23/26] migrate run -> Run as per Kamils suggestion --- CLI/API/plugin.hpp | 2 +- CLI/DynamicPlugin.hpp | 4 ++-- CLI/MaCh3Program.cpp | 6 +++--- CLI/MaCh3Program.hpp | 2 +- CLI/Modules/DiagMCMCModule.cpp | 2 +- CLI/Modules/DiagMCMCModule.hpp | 2 +- CLI/Modules/GetPenaltyTermModule.cpp | 2 +- CLI/Modules/GetPenaltyTermModule.hpp | 2 +- CLI/Modules/ProcessMCMCModule.cpp | 2 +- CLI/Modules/ProcessMCMCModule.hpp | 2 +- CLI/mach3.cpp | 2 +- Diagnostics/DiagMCMC.cpp | 2 +- Diagnostics/GetPenaltyTerm.cpp | 2 +- Diagnostics/ProcessMCMC.cpp | 2 +- 14 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CLI/API/plugin.hpp b/CLI/API/plugin.hpp index 3facabe9a..d43e95c79 100644 --- a/CLI/API/plugin.hpp +++ b/CLI/API/plugin.hpp @@ -24,7 +24,7 @@ namespace M3 { * @brief Execute the plugin's main functionality * @return Exit code (0 on success, non-zero on error) */ - virtual int run() = 0; + virtual int Run() = 0; /** * @brief Get the argument parser for this plugin diff --git a/CLI/DynamicPlugin.hpp b/CLI/DynamicPlugin.hpp index 167a9a742..d7c232217 100644 --- a/CLI/DynamicPlugin.hpp +++ b/CLI/DynamicPlugin.hpp @@ -33,8 +33,8 @@ namespace M3{ * @brief Run the plugin's main functionality * @return Exit code from the plugin */ - int run() { - return m_plugin->run(); + int Run() { + return m_plugin->Run(); } /** diff --git a/CLI/MaCh3Program.cpp b/CLI/MaCh3Program.cpp index a254ac6ea..98489ad6a 100644 --- a/CLI/MaCh3Program.cpp +++ b/CLI/MaCh3Program.cpp @@ -206,18 +206,18 @@ namespace M3{ * * @return Exit code from the executed module (0 on success) */ - int MaCh3Program::run(){ + int MaCh3Program::Run(){ const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); if (sub_parser){ auto plugin_itr = m_module_map.find(&sub_parser); if (plugin_itr != m_module_map.end()){ - return plugin_itr->second->run(); + return plugin_itr->second->Run(); } auto dplugin_itr = m_dynamic_plugin_map.find(&sub_parser); if (dplugin_itr != m_dynamic_plugin_map.end()){ - return dplugin_itr->second->run(); + return dplugin_itr->second->Run(); } } return 0; diff --git a/CLI/MaCh3Program.hpp b/CLI/MaCh3Program.hpp index 47ccb5853..93a160604 100644 --- a/CLI/MaCh3Program.hpp +++ b/CLI/MaCh3Program.hpp @@ -67,7 +67,7 @@ namespace M3{ * @brief Run the selected subcommand * @return Exit code from the executed module */ - int run(); + int Run(); private: /** diff --git a/CLI/Modules/DiagMCMCModule.cpp b/CLI/Modules/DiagMCMCModule.cpp index 62bc4ff6b..a7950170d 100644 --- a/CLI/Modules/DiagMCMCModule.cpp +++ b/CLI/Modules/DiagMCMCModule.cpp @@ -27,7 +27,7 @@ namespace M3{ return m_parser; } - int DiagMCMCModule::run() { + int DiagMCMCModule::Run() { SetMaCh3LoggerFormat(); MACH3LOG_INFO("Producing single fit output"); std::string inputFile = m_parser->get<std::string>("mcmc-output"); diff --git a/CLI/Modules/DiagMCMCModule.hpp b/CLI/Modules/DiagMCMCModule.hpp index c70b6e315..ac6b781e2 100644 --- a/CLI/Modules/DiagMCMCModule.hpp +++ b/CLI/Modules/DiagMCMCModule.hpp @@ -33,7 +33,7 @@ namespace M3{ * @brief Execute the MCMC diagnostics * @return Exit code (0 on success) */ - int run() override; + int Run() override; private: diff --git a/CLI/Modules/GetPenaltyTermModule.cpp b/CLI/Modules/GetPenaltyTermModule.cpp index 6c66d5cfe..3a77cfb12 100644 --- a/CLI/Modules/GetPenaltyTermModule.cpp +++ b/CLI/Modules/GetPenaltyTermModule.cpp @@ -58,7 +58,7 @@ namespace M3{ return m_parser; } - int GetPenaltyTermModule::run()//int argc, char *argv[]) + int GetPenaltyTermModule::Run()//int argc, char *argv[]) { SetMaCh3LoggerFormat(); M3::Utils::MaCh3Welcome(); diff --git a/CLI/Modules/GetPenaltyTermModule.hpp b/CLI/Modules/GetPenaltyTermModule.hpp index 4a940a713..81d76241d 100644 --- a/CLI/Modules/GetPenaltyTermModule.hpp +++ b/CLI/Modules/GetPenaltyTermModule.hpp @@ -35,7 +35,7 @@ namespace M3{ * @brief Execute the penalty term extraction * @return Exit code (0 on success) */ - int run() override; + int Run() override; private: /** diff --git a/CLI/Modules/ProcessMCMCModule.cpp b/CLI/Modules/ProcessMCMCModule.cpp index 86e2a3215..b6d8ab766 100644 --- a/CLI/Modules/ProcessMCMCModule.cpp +++ b/CLI/Modules/ProcessMCMCModule.cpp @@ -64,7 +64,7 @@ namespace M3{ } - int ProcessMCMCModule::run() { + int ProcessMCMCModule::Run() { SetMaCh3LoggerFormat(); nFiles = 0; config = m_parser->get<std::string>("config"); diff --git a/CLI/Modules/ProcessMCMCModule.hpp b/CLI/Modules/ProcessMCMCModule.hpp index 22662cfdc..2b44476f3 100644 --- a/CLI/Modules/ProcessMCMCModule.hpp +++ b/CLI/Modules/ProcessMCMCModule.hpp @@ -50,7 +50,7 @@ namespace M3{ * @brief Execute the MCMC processing * @return Exit code (0 on success) */ - int run() override; + int Run() override; private: diff --git a/CLI/mach3.cpp b/CLI/mach3.cpp index ac2cde462..1609ca212 100644 --- a/CLI/mach3.cpp +++ b/CLI/mach3.cpp @@ -76,7 +76,7 @@ int main(int argc, char *argv[]) { // } try{ - program.run(); + program.Run(); } catch(...){ return 3; diff --git a/Diagnostics/DiagMCMC.cpp b/Diagnostics/DiagMCMC.cpp index 26bf4f0c4..9c149bbaa 100644 --- a/Diagnostics/DiagMCMC.cpp +++ b/Diagnostics/DiagMCMC.cpp @@ -8,7 +8,7 @@ int main(int argc, char const* argv[]){ M3::DiagMCMCModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); - proc.run(); + proc.Run(); MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 diag' as a direct replacement instead."); return 0; diff --git a/Diagnostics/GetPenaltyTerm.cpp b/Diagnostics/GetPenaltyTerm.cpp index 5f33ec1fd..2c95453c7 100644 --- a/Diagnostics/GetPenaltyTerm.cpp +++ b/Diagnostics/GetPenaltyTerm.cpp @@ -8,7 +8,7 @@ int main(int argc, char const* argv[]){ M3::GetPenaltyTermModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); - proc.run(); + proc.Run(); MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 penterm' as a direct replacement instead."); return 0; diff --git a/Diagnostics/ProcessMCMC.cpp b/Diagnostics/ProcessMCMC.cpp index 92b62b556..f31eb0962 100644 --- a/Diagnostics/ProcessMCMC.cpp +++ b/Diagnostics/ProcessMCMC.cpp @@ -8,7 +8,7 @@ int main(int argc, char const* argv[]){ M3::ProcessMCMCModule proc; ArgumentParser* parser = proc.get_parser(); parser->parse_args(argc, argv); - proc.run(); + proc.Run(); MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); MACH3LOG_WARN(" : you can use 'mach3 process' as a direct replacement instead."); return 0; From 5b97a69bbd48ef8619a558b3adae7af37dfc6ace Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Tue, 23 Jun 2026 11:45:52 +0000 Subject: [PATCH 24/26] reinstating missing comments --- CLI/Modules/GetPenaltyTermModule.cpp | 14 -------------- CLI/Modules/GetPenaltyTermModule.hpp | 9 +++++++++ CLI/Modules/ProcessMCMCModule.hpp | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/CLI/Modules/GetPenaltyTermModule.cpp b/CLI/Modules/GetPenaltyTermModule.cpp index 3a77cfb12..c929b0dd7 100644 --- a/CLI/Modules/GetPenaltyTermModule.cpp +++ b/CLI/Modules/GetPenaltyTermModule.cpp @@ -1,17 +1,3 @@ -/** - * @file GetPenaltyTerm.cpp - * @brief Implementation of penalty term extraction from systematic chains - * - * This script is designed to retrieve penalty terms from various sources, - * such as flux and cross-section systematic chains. Since flux and cross-section - * uncertainties are handled systematically, the penalty term cannot be taken - * directly from the chain. - * - * @todo This should really be moved to MCMC Processor - * @ingroup MaCh3DiagnosticProcessing - * @author Kamil Skwarczynski - */ - // MaCh3 includes #include "Manager/Manager.h" #include "Samples/SampleStructs.h" diff --git a/CLI/Modules/GetPenaltyTermModule.hpp b/CLI/Modules/GetPenaltyTermModule.hpp index 81d76241d..bf037f78a 100644 --- a/CLI/Modules/GetPenaltyTermModule.hpp +++ b/CLI/Modules/GetPenaltyTermModule.hpp @@ -1,6 +1,15 @@ /** * @file GetPenaltyTermModule.hpp * @brief Module for extracting penalty terms from systematic chains + * + * This module is designed to retrieve penalty terms from various sources, + * such as flux and cross-section systematic chains. Since flux and cross-section + * uncertainties are handled systematically, the penalty term cannot be taken + * directly from the chain. + * + * @todo This should really be moved to MCMC Processor + * @ingroup MaCh3DiagnosticProcessing + * @author Kamil Skwarczynski */ #pragma once diff --git a/CLI/Modules/ProcessMCMCModule.hpp b/CLI/Modules/ProcessMCMCModule.hpp index 2b44476f3..d3f0cd782 100644 --- a/CLI/Modules/ProcessMCMCModule.hpp +++ b/CLI/Modules/ProcessMCMCModule.hpp @@ -13,7 +13,7 @@ namespace M3{ /** - * @file ProcessMCMC.cpp + * @file ProcessMCMCModule.hpp * @brief Main executable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implementation of methods is in MCMCProcessor * @ingroup MaCh3DiagnosticProcessing * From 24fe017ec90f4db33856efb8b3e1fbb98169833c Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Thu, 25 Jun 2026 11:15:18 +0000 Subject: [PATCH 25/26] restore author info --- CLI/Modules/DiagMCMCModule.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CLI/Modules/DiagMCMCModule.cpp b/CLI/Modules/DiagMCMCModule.cpp index a7950170d..f17f1fc27 100644 --- a/CLI/Modules/DiagMCMCModule.cpp +++ b/CLI/Modules/DiagMCMCModule.cpp @@ -1,8 +1,9 @@ -/** - * @file DiagMCMCModule.cpp - * @brief Implementation of the DiagMCMCModule class - */ - +/// @file DiagMCMCModule.cpp +/// @ingroup MaCh3DiagnosticProcessing +/// @brief Implementation of the DiagMCMCModule class +/// +/// @author Clarence Wret +/// @author Kamil Skwarczynski #include "Fitters/MCMCProcessor.h" #include "Manager/Manager.h" #include "CLI/Modules/DiagMCMCModule.hpp" @@ -27,6 +28,10 @@ namespace M3{ return m_parser; } + + /// @brief Main function creating MCMCProcessor and calling MCMC Diagnostic + /// @param inputFile MCMC Chain + /// @param config Config file with settings int DiagMCMCModule::Run() { SetMaCh3LoggerFormat(); MACH3LOG_INFO("Producing single fit output"); From d8b72e8995db987afae4cc1ad67e6cd9c5ee22df Mon Sep 17 00:00:00 2001 From: Alexander Richards <a.richards@imperial.ac.uk> Date: Thu, 25 Jun 2026 14:34:47 +0000 Subject: [PATCH 26/26] Move Doxygen comments to triple slash format --- CLI/API/argparse.hpp | 46 +++----- CLI/API/plugin.hpp | 80 +++++-------- CLI/DynamicPlugin.hpp | 50 +++----- CLI/MaCh3Program.cpp | 54 ++++----- CLI/MaCh3Program.hpp | 82 +++++-------- CLI/Modules/DiagMCMCModule.hpp | 34 ++---- CLI/Modules/GetPenaltyTermModule.cpp | 66 +++++------ CLI/Modules/GetPenaltyTermModule.hpp | 94 ++++++--------- CLI/Modules/ProcessMCMCModule.cpp | 170 ++++++++++++--------------- CLI/Modules/ProcessMCMCModule.hpp | 134 ++++++++------------- CLI/mach3.cpp | 30 ++--- 11 files changed, 338 insertions(+), 502 deletions(-) diff --git a/CLI/API/argparse.hpp b/CLI/API/argparse.hpp index a0930cb8b..be5bbde41 100644 --- a/CLI/API/argparse.hpp +++ b/CLI/API/argparse.hpp @@ -1,7 +1,5 @@ -/** - * @file argparse.hpp - * @brief MaCh3 wrapper for argparse library with additional functionality - */ +/// @file argparse.hpp +/// @brief MaCh3 wrapper for argparse library with additional functionality #pragma once #include <argparse/argparse.hpp> @@ -10,43 +8,35 @@ using argparse::ArgumentParser; namespace M3 { - /** - * @class MaCh3ArgumentParser - * @brief Extended ArgumentParser with MaCh3-specific functionality - * - * This class extends the standard ArgumentParser to provide additional - * methods for accessing parser name, subparsers, and tracking which - * subcommand was used. - */ + /// @class MaCh3ArgumentParser + /// @brief Extended ArgumentParser with MaCh3-specific functionality + /// + /// This class extends the standard ArgumentParser to provide additional + /// methods for accessing parser name, subparsers, and tracking which + /// subcommand was used. class MaCh3ArgumentParser: public ArgumentParser{ public: using ArgumentParser::ArgumentParser; virtual ~MaCh3ArgumentParser() = default; - /** - * @brief Get the name of this parser/subcommand - * @return The program or subcommand name - */ + /// @brief Get the name of this parser/subcommand + /// @return The program or subcommand name const std::string name() const{ return this->m_program_name; } - /** - * @brief Get the list of registered subparsers - * @return Reference to the list of subparsers - */ + /// @brief Get the list of registered subparsers + /// @return Reference to the list of subparsers const std::list<std::reference_wrapper<ArgumentParser>>& subparsers() const{ return this->m_subparsers; } - /** - * @brief Get the subcommand that was used in parsing - * - * Recursively traverses the subparser hierarchy to find the - * deepest subcommand that was actually invoked. - * - * @return Reference to the MaCh3ArgumentParser of the used subcommand - */ + /// @brief Get the subcommand that was used in parsing + /// + /// Recursively traverses the subparser hierarchy to find the + /// deepest subcommand that was actually invoked. + /// + /// @return Reference to the MaCh3ArgumentParser of the used subcommand const MaCh3ArgumentParser& get_subcommand_used() const{ for (const std::reference_wrapper<ArgumentParser>& subparser : this->m_subparsers) { if (this->is_subcommand_used(subparser.get())) { diff --git a/CLI/API/plugin.hpp b/CLI/API/plugin.hpp index d43e95c79..c4751b6ba 100644 --- a/CLI/API/plugin.hpp +++ b/CLI/API/plugin.hpp @@ -1,7 +1,5 @@ -/** - * @file plugin.hpp - * @brief Plugin interface and registration macros for MaCh3 - */ +/// @file plugin.hpp +/// @brief Plugin interface and registration macros for MaCh3 #pragma once #include "CLI/API/argparse.hpp" @@ -9,68 +7,54 @@ namespace M3 { - /** - * @class IPlugin - * @brief Interface for MaCh3 plugins and modules - * - * All plugins and modules must implement this interface to provide - * argument parsing and execution functionality. - */ + /// @class IPlugin + /// @brief Interface for MaCh3 plugins and modules + /// + /// All plugins and modules must implement this interface to provide + /// argument parsing and execution functionality. class IPlugin { public: virtual ~IPlugin() = default; - /** - * @brief Execute the plugin's main functionality - * @return Exit code (0 on success, non-zero on error) - */ + /// @brief Execute the plugin's main functionality + /// @return Exit code (0 on success, non-zero on error) virtual int Run() = 0; - /** - * @brief Get the argument parser for this plugin - * @return Pointer to the plugin's MaCh3ArgumentParser - */ + /// @brief Get the argument parser for this plugin + /// @return Pointer to the plugin's MaCh3ArgumentParser virtual MaCh3ArgumentParser* get_parser() = 0; }; - /** - * @typedef IModule - * @brief Alias for IPlugin, used for core modules - */ + /// @typedef IModule + /// @brief Alias for IPlugin, used for core modules typedef IPlugin IModule; }; -/** - * @typedef create_plugin_t - * @brief Function pointer type for plugin factory function - */ +/// @typedef create_plugin_t +/// @brief Function pointer type for plugin factory function extern "C" { typedef M3::IPlugin* (*create_plugin_t)(); - /** - * @typedef destroy_plugin_t - * @brief Function pointer type for plugin destructor function - */ + /// @typedef destroy_plugin_t + /// @brief Function pointer type for plugin destructor function typedef void (*destroy_plugin_t)(M3::IPlugin*); } -/** - * @def MACH3_REGISTER_PLUGIN - * @brief Macro to register a plugin class with MaCh3 - * - * This macro generates the required factory and destructor functions - * for a plugin class. Use it in your plugin implementation file. - * - * @param PluginClass The plugin class that implements M3::IPlugin - * - * Example usage: - * @code - * class MyPlugin : public M3::IPlugin { - * // ... implementation - * }; - * MACH3_REGISTER_PLUGIN(MyPlugin) - * @endcode - */ +/// @def MACH3_REGISTER_PLUGIN +/// @brief Macro to register a plugin class with MaCh3 +/// +/// This macro generates the required factory and destructor functions +/// for a plugin class. Use it in your plugin implementation file. +/// +/// @param PluginClass The plugin class that implements M3::IPlugin +/// +/// Example usage: +/// @code +/// class MyPlugin : public M3::IPlugin { +/// // ... implementation +/// }; +/// MACH3_REGISTER_PLUGIN(MyPlugin) +/// @endcode #define MACH3_REGISTER_PLUGIN(PluginClass) \ static_assert(std::is_base_of<M3::IPlugin, PluginClass>::value, "PluginClass must derive from M3::IPlugin"); \ extern "C" M3::IPlugin* create_plugin() { \ diff --git a/CLI/DynamicPlugin.hpp b/CLI/DynamicPlugin.hpp index d7c232217..725e35b7a 100644 --- a/CLI/DynamicPlugin.hpp +++ b/CLI/DynamicPlugin.hpp @@ -1,55 +1,43 @@ -/** - * @file DynamicPlugin.hpp - * @brief Wrapper class for dynamically loaded plugins - */ +/// @file DynamicPlugin.hpp +/// @brief Wrapper class for dynamically loaded plugins #pragma once #include <dlfcn.h> #include "CLI/API/plugin.hpp" namespace M3{ - /** - * @class DynamicPlugin - * @brief Manages the lifecycle of a dynamically loaded plugin - * - * This class wraps a plugin loaded from a shared library, maintaining the - * library handle and providing cleanup functionality. - */ + /// @class DynamicPlugin + /// @brief Manages the lifecycle of a dynamically loaded plugin + /// + /// This class wraps a plugin loaded from a shared library, maintaining the + /// library handle and providing cleanup functionality. class DynamicPlugin: public IPlugin{ public: - /** - * @brief Constructor for a dynamic plugin - * @param handle Handle to the loaded shared library - * @param plugin Pointer to the instantiated plugin - * @param destroy_func Function pointer to destroy the plugin - */ + /// @brief Constructor for a dynamic plugin + /// @param handle Handle to the loaded shared library + /// @param plugin Pointer to the instantiated plugin + /// @param destroy_func Function pointer to destroy the plugin DynamicPlugin(void* handle, IPlugin* plugin, destroy_plugin_t destroy_func):m_handle(handle), m_plugin(plugin), m_destroy_func(destroy_func){} - /** - * @brief Run the plugin's main functionality - * @return Exit code from the plugin - */ + /// @brief Run the plugin's main functionality + /// @return Exit code from the plugin int Run() { return m_plugin->Run(); } - /** - * @brief Get the argument parser for this plugin - * @return Pointer to the plugin's argument parser - */ + /// @brief Get the argument parser for this plugin + /// @return Pointer to the plugin's argument parser MaCh3ArgumentParser* get_parser(){ return m_plugin->get_parser(); } - /** - * @brief Destroy the plugin and close the shared library - * - * Calls the plugin's destroy function and closes the library handle. - */ + /// @brief Destroy the plugin and close the shared library + /// + /// Calls the plugin's destroy function and closes the library handle. void destroy(){ m_destroy_func(m_plugin); m_plugin = 0; @@ -63,4 +51,4 @@ namespace M3{ IPlugin* m_plugin; ///< Pointer to the plugin instance destroy_plugin_t m_destroy_func; ///< Function pointer to destroy the plugin }; -}; \ No newline at end of file +}; diff --git a/CLI/MaCh3Program.cpp b/CLI/MaCh3Program.cpp index 98489ad6a..fcd853bc3 100644 --- a/CLI/MaCh3Program.cpp +++ b/CLI/MaCh3Program.cpp @@ -1,7 +1,5 @@ -/** - * @file MaCh3Program.cpp - * @brief Implementation of the MaCh3Program class - */ +/// @file MaCh3Program.cpp +/// @brief Implementation of the MaCh3Program class #include <dlfcn.h> #include <filesystem> @@ -110,13 +108,11 @@ namespace M3{ m_subcommands.push_back(parser->name()); } - /** - * @brief Load dynamic plugins from paths specified in MACH3_PLUGINS environment variable - * - * Reads the MACH3_PLUGINS environment variable (colon-separated paths), loads each .so file, - * and registers the plugins with the program. Each plugin must export create_plugin and - * destroy_plugin functions. - */ + /// @brief Load dynamic plugins from paths specified in MACH3_PLUGINS environment variable + /// + /// Reads the MACH3_PLUGINS environment variable (colon-separated paths), loads each .so file, + /// and registers the plugins with the program. Each plugin must export create_plugin and + /// destroy_plugin functions. void MaCh3Program::load_dynamic_plugins(){ const char* env = std::getenv("MACH3_PLUGINS"); if (!env) { @@ -187,11 +183,9 @@ namespace M3{ } } - /** - * @brief Unload all dynamic plugins and clean up resources - * - * Calls the destroy function for each loaded plugin and closes the dynamic library handles. - */ + /// @brief Unload all dynamic plugins and clean up resources + /// + /// Calls the destroy function for each loaded plugin and closes the dynamic library handles. void MaCh3Program::unload_dynamic_plugins(){ for (auto& [_, plugin] : m_dynamic_plugin_map){ plugin->destroy(); @@ -199,13 +193,11 @@ namespace M3{ m_dynamic_plugin_map.clear(); } - /** - * @brief Execute the selected subcommand - * - * Determines which subcommand was invoked and runs the corresponding module or plugin. - * - * @return Exit code from the executed module (0 on success) - */ + /// @brief Execute the selected subcommand + /// + /// Determines which subcommand was invoked and runs the corresponding module or plugin. + /// + /// @return Exit code from the executed module (0 on success) int MaCh3Program::Run(){ const MaCh3ArgumentParser& sub_parser = this->get_subcommand_used(); @@ -224,15 +216,13 @@ namespace M3{ } - /** - * @brief Expand a plugin path to a list of shared library files - * - * If the path is a file, returns it directly. If it's a directory, - * returns all .so files found in that directory. - * - * @param path Path to a plugin file or directory - * @return Vector of absolute paths to .so files - */ + /// @brief Expand a plugin path to a list of shared library files + /// + /// If the path is a file, returns it directly. If it's a directory, + /// returns all .so files found in that directory. + /// + /// @param path Path to a plugin file or directory + /// @return Vector of absolute paths to .so files std::vector<std::string> MaCh3Program::expand_plugin_path(const std::string& path) const { std::vector<std::string> result; diff --git a/CLI/MaCh3Program.hpp b/CLI/MaCh3Program.hpp index 93a160604..03cbe7e5d 100644 --- a/CLI/MaCh3Program.hpp +++ b/CLI/MaCh3Program.hpp @@ -1,7 +1,5 @@ -/** - * @file MaCh3Program.hpp - * @brief Core program class for the MaCh3 command-line interface - */ +/// @file MaCh3Program.hpp +/// @brief Core program class for the MaCh3 command-line interface #pragma once #include <map> @@ -15,80 +13,58 @@ namespace fs = std::filesystem; namespace M3{ - /** - * @class MaCh3Program - * @brief Main program class that manages modules, plugins, and command-line parsing - * - * This class extends MaCh3ArgumentParser to provide functionality for: - * - Managing core modules and dynamic plugins - * - Installing and generating shell completions - * - Running selected subcommands - */ + /// @class MaCh3Program + /// @brief Main program class that manages modules, plugins, and command-line parsing + /// + /// This class extends MaCh3ArgumentParser to provide functionality for: + /// - Managing core modules and dynamic plugins + /// - Installing and generating shell completions + /// - Running selected subcommands class MaCh3Program: public MaCh3ArgumentParser{ public: using MaCh3ArgumentParser::MaCh3ArgumentParser; - /** - * @brief Destructor that unloads all dynamic plugins - */ + /// @brief Destructor that unloads all dynamic plugins virtual ~MaCh3Program(){ //std::cout<<"UNLOADED"<< std::endl; this->unload_dynamic_plugins(); } - /** - * @brief Add a core module to the program - * @param module Reference to the module to be added - */ + /// @brief Add a core module to the program + /// @param module Reference to the module to be added void add_core_module(IModule& module); - /** - * @brief Load dynamic plugins from the MACH3_PLUGINS environment variable - */ + /// @brief Load dynamic plugins from the MACH3_PLUGINS environment variable void load_dynamic_plugins(); - /** - * @brief Install shell completion scripts for bash and zsh - */ + /// @brief Install shell completion scripts for bash and zsh const void install_completions() const; - /** - * @brief Generate completion suggestions for the given prefix - * @param prefix The prefix string to match against available subcommands - */ + /// @brief Generate completion suggestions for the given prefix + /// @param prefix The prefix string to match against available subcommands const void completions(const std::string& prefix) const; - /** - * @brief Unload all dynamically loaded plugins - */ + /// @brief Unload all dynamically loaded plugins void unload_dynamic_plugins(); - /** - * @brief Run the selected subcommand - * @return Exit code from the executed module - */ + /// @brief Run the selected subcommand + /// @return Exit code from the executed module int Run(); private: - /** - * @brief Detect the user's shell from the SHELL environment variable - * @return Shell name ("bash", "zsh", or empty string if not detected) - */ + /// @brief Detect the user's shell from the SHELL environment variable + /// @return Shell name ("bash", "zsh", or empty string if not detected) const std::string detect_shell() const; - /** - * @brief Write content to a file, creating parent directories if needed - * @param path Filesystem path to write to - * @param content Content to write to the file - * @return true if successful, false otherwise - */ + /// @brief Write content to a file, creating parent directories if needed + /// @param path Filesystem path to write to + /// @param content Content to write to the file + /// @return true if successful, false otherwise const bool write_file(const fs::path& path, std::string_view content) const; - /** - * @brief Expand a plugin path (file or directory) into a list of .so files - * @param path Path to a plugin file or directory containing plugins - * @return Vector of plugin file paths - */ + /// @brief Expand a plugin path (file or directory) into a list of .so files + /// @param path Path to a plugin file or directory containing plugins + /// @return Vector of plugin file paths std::vector<std::string> expand_plugin_path(const std::string& path) const; private: @@ -113,4 +89,4 @@ completions=("${(@f)$(mach3 --complete "$cur" "${words[@]:1}")}") compadd -a completions )"; }; -}; \ No newline at end of file +}; diff --git a/CLI/Modules/DiagMCMCModule.hpp b/CLI/Modules/DiagMCMCModule.hpp index ac6b781e2..f444dcb7d 100644 --- a/CLI/Modules/DiagMCMCModule.hpp +++ b/CLI/Modules/DiagMCMCModule.hpp @@ -1,38 +1,28 @@ -/** - * @file DiagMCMCModule.hpp - * @brief Module for MCMC diagnostic analysis - */ +/// @file DiagMCMCModule.hpp +/// @brief Module for MCMC diagnostic analysis #pragma once #include "CLI/API/plugin.hpp" namespace M3{ - /** - * @class DiagMCMCModule - * @brief Module for performing diagnostic analysis on MCMC chains - * - * This module provides diagnostics such as convergence tests, autocorrelation - * analysis, and batch means calculations to assess the quality of MCMC samples. - */ + /// @class DiagMCMCModule + /// @brief Module for performing diagnostic analysis on MCMC chains + /// + /// This module provides diagnostics such as convergence tests, autocorrelation + /// analysis, and batch means calculations to assess the quality of MCMC samples. class DiagMCMCModule: public IModule{ public: - /** - * @brief Destructor - */ + /// @brief Destructor virtual ~DiagMCMCModule(); - /** - * @brief Get the argument parser for this module - * @return Pointer to the configured MaCh3ArgumentParser - */ + /// @brief Get the argument parser for this module + /// @return Pointer to the configured MaCh3ArgumentParser MaCh3ArgumentParser* get_parser() override; - /** - * @brief Execute the MCMC diagnostics - * @return Exit code (0 on success) - */ + /// @brief Execute the MCMC diagnostics + /// @return Exit code (0 on success) int Run() override; diff --git a/CLI/Modules/GetPenaltyTermModule.cpp b/CLI/Modules/GetPenaltyTermModule.cpp index c929b0dd7..407fcc24c 100644 --- a/CLI/Modules/GetPenaltyTermModule.cpp +++ b/CLI/Modules/GetPenaltyTermModule.cpp @@ -55,19 +55,17 @@ namespace M3{ return 0; } - /** - * @brief Read covariance matrix and prior information from ROOT file - * - * Loads the covariance matrix, inverts it, and extracts parameter priors - * and flat prior flags from the MCMC output file. - * - * @param inputFile Path to the ROOT file containing MCMC output - * @param Prior Vector to be filled with prior parameter values - * @param isFlat Vector to be filled with flat prior flags - * @param ParamNames Vector to be filled with parameter names - * @param invCovMatrix 2D vector to be filled with inverted covariance matrix - * @param nParams Reference to store the number of parameters - */ + /// @brief Read covariance matrix and prior information from ROOT file + /// + /// Loads the covariance matrix, inverts it, and extracts parameter priors + /// and flat prior flags from the MCMC output file. + /// + /// @param inputFile Path to the ROOT file containing MCMC output + /// @param Prior Vector to be filled with prior parameter values + /// @param isFlat Vector to be filled with flat prior flags + /// @param ParamNames Vector to be filled with parameter names + /// @param invCovMatrix 2D vector to be filled with inverted covariance matrix + /// @param nParams Reference to store the number of parameters void GetPenaltyTermModule::ReadCovFile(const std::string& inputFile, std::vector <double>& Prior, std::vector <bool>& isFlat, @@ -152,19 +150,17 @@ namespace M3{ delete TempFile; } - /** - * @brief Load penalty term set definitions from YAML configuration - * - * Parses the configuration to determine which parameters belong to each - * penalty term set, based on either inclusion or exclusion patterns. - * - * @param Settings YAML configuration node - * @param SetsNames Vector to be filled with set names - * @param FancyTitle Vector to be filled with fancy titles for plotting - * @param isRelevantParam 2D vector to be filled with parameter relevance flags - * @param ParamNames Vector of all parameter names from the covariance - * @param nParams Total number of parameters - */ + /// @brief Load penalty term set definitions from YAML configuration + /// + /// Parses the configuration to determine which parameters belong to each + /// penalty term set, based on either inclusion or exclusion patterns. + /// + /// @param Settings YAML configuration node + /// @param SetsNames Vector to be filled with set names + /// @param FancyTitle Vector to be filled with fancy titles for plotting + /// @param isRelevantParam 2D vector to be filled with parameter relevance flags + /// @param ParamNames Vector of all parameter names from the covariance + /// @param nParams Total number of parameters void GetPenaltyTermModule::LoadSettings(YAML::Node& Settings, std::vector<std::string>& SetsNames, std::vector<std::string>& FancyTitle, @@ -236,16 +232,14 @@ namespace M3{ } } - /** - * @brief Calculate and plot penalty terms for each parameter set - * - * Main function that reads the covariance, loads configuration, iterates - * through MCMC steps, and calculates penalty terms for each defined set. - * Results are saved to ROOT file and PDF plots. - * - * @param inputFile Path to the MCMC chain ROOT file - * @param configFile Path to the YAML configuration file - */ + /// @brief Calculate and plot penalty terms for each parameter set + /// + /// Main function that reads the covariance, loads configuration, iterates + /// through MCMC steps, and calculates penalty terms for each defined set. + /// Results are saved to ROOT file and PDF plots. + /// + /// @param inputFile Path to the MCMC chain ROOT file + /// @param configFile Path to the YAML configuration file void GetPenaltyTermModule::GetPenaltyTerm(const std::string& inputFile, const std::string& configFile) { auto canvas = std::make_unique<TCanvas>("canvas", "canvas", 0, 0, 1024, 1024); diff --git a/CLI/Modules/GetPenaltyTermModule.hpp b/CLI/Modules/GetPenaltyTermModule.hpp index bf037f78a..37eccddad 100644 --- a/CLI/Modules/GetPenaltyTermModule.hpp +++ b/CLI/Modules/GetPenaltyTermModule.hpp @@ -1,16 +1,14 @@ -/** - * @file GetPenaltyTermModule.hpp - * @brief Module for extracting penalty terms from systematic chains - * - * This module is designed to retrieve penalty terms from various sources, - * such as flux and cross-section systematic chains. Since flux and cross-section - * uncertainties are handled systematically, the penalty term cannot be taken - * directly from the chain. - * - * @todo This should really be moved to MCMC Processor - * @ingroup MaCh3DiagnosticProcessing - * @author Kamil Skwarczynski - */ +/// @file GetPenaltyTermModule.hpp +/// @brief Module for extracting penalty terms from systematic chains +/// +/// This module is designed to retrieve penalty terms from various sources, +/// such as flux and cross-section systematic chains. Since flux and cross-section +/// uncertainties are handled systematically, the penalty term cannot be taken +/// directly from the chain. +/// +/// @todo This should really be moved to MCMC Processor +/// @ingroup MaCh3DiagnosticProcessing +/// @author Kamil Skwarczynski #pragma once #include "yaml-cpp/yaml.h" @@ -18,44 +16,34 @@ namespace M3{ - /** - * @class GetPenaltyTermModule - * @brief Module for extracting specific penalty terms from MCMC chains - * - * This module retrieves penalty terms from flux and cross-section systematic - * chains, since these systematic uncertainties are handled separately and the - * penalty term cannot be taken directly from the chain. - */ + /// @class GetPenaltyTermModule + /// @brief Module for extracting specific penalty terms from MCMC chains + /// + /// This module retrieves penalty terms from flux and cross-section systematic + /// chains, since these systematic uncertainties are handled separately and the + /// penalty term cannot be taken directly from the chain. class GetPenaltyTermModule: public IModule{ public: - /** - * @brief Destructor - */ + /// @brief Destructor virtual ~GetPenaltyTermModule(); - /** - * @brief Get the argument parser for this module - * @return Pointer to the configured MaCh3ArgumentParser - */ + /// @brief Get the argument parser for this module + /// @return Pointer to the configured MaCh3ArgumentParser MaCh3ArgumentParser* get_parser() override; - /** - * @brief Execute the penalty term extraction - * @return Exit code (0 on success) - */ + /// @brief Execute the penalty term extraction + /// @return Exit code (0 on success) int Run() override; private: - /** - * @brief Read covariance matrix and parameter information from file - * @param inputFile Path to the ROOT file containing the covariance - * @param Prior Vector to store prior parameter values - * @param isFlat Vector to store flat prior flags - * @param ParamNames Vector to store parameter names - * @param invCovMatrix 2D vector to store inverted covariance matrix - * @param nParams Reference to store the number of parameters - */ + /// @brief Read covariance matrix and parameter information from file + /// @param inputFile Path to the ROOT file containing the covariance + /// @param Prior Vector to store prior parameter values + /// @param isFlat Vector to store flat prior flags + /// @param ParamNames Vector to store parameter names + /// @param invCovMatrix 2D vector to store inverted covariance matrix + /// @param nParams Reference to store the number of parameters void ReadCovFile(const std::string& inputFile, std::vector <double>& Prior, std::vector <bool>& isFlat, @@ -63,15 +51,13 @@ namespace M3{ std::vector<std::vector<double>>& invCovMatrix, int& nParams); - /** - * @brief Load penalty term sets from YAML configuration - * @param Settings YAML configuration node - * @param SetsNames Vector to store set names - * @param FancyTitle Vector to store fancy titles for each set - * @param isRelevantParam 2D vector indicating which parameters belong to each set - * @param ParamNames Vector of all parameter names - * @param nParams Total number of parameters - */ + /// @brief Load penalty term sets from YAML configuration + /// @param Settings YAML configuration node + /// @param SetsNames Vector to store set names + /// @param FancyTitle Vector to store fancy titles for each set + /// @param isRelevantParam 2D vector indicating which parameters belong to each set + /// @param ParamNames Vector of all parameter names + /// @param nParams Total number of parameters void LoadSettings(YAML::Node& Settings, std::vector<std::string>& SetsNames, std::vector<std::string>& FancyTitle, @@ -79,11 +65,9 @@ namespace M3{ const std::vector<std::string>& ParamNames, const int nParams); - /** - * @brief Calculate and plot penalty terms for parameter sets - * @param inputFile Path to the MCMC chain ROOT file - * @param configFile Path to the YAML configuration file - */ + /// @brief Calculate and plot penalty terms for parameter sets + /// @param inputFile Path to the MCMC chain ROOT file + /// @param configFile Path to the YAML configuration file void GetPenaltyTerm(const std::string& inputFile, const std::string& configFile); private: diff --git a/CLI/Modules/ProcessMCMCModule.cpp b/CLI/Modules/ProcessMCMCModule.cpp index b6d8ab766..42b404378 100644 --- a/CLI/Modules/ProcessMCMCModule.cpp +++ b/CLI/Modules/ProcessMCMCModule.cpp @@ -1,7 +1,5 @@ -/** - * @file ProcessMCMCModule.cpp - * @brief Implementation of the ProcessMCMCModule class - */ +/// @file ProcessMCMCModule.cpp +/// @brief Implementation of the ProcessMCMCModule class //MaCh3 includes #include "Fitters/OscProcessor.h" @@ -152,15 +150,13 @@ namespace M3{ return CustomBinning; } - /** - * @brief Process a single MCMC chain file - * - * Loads the MCMC chain, applies configuration settings, and produces - * various diagnostic plots and analyses including posteriors, correlations, - * credible intervals, and Bayes factors. - * - * @param inputFile Path to the MCMC chain ROOT file - */ + /// @brief Process a single MCMC chain file + /// + /// Loads the MCMC chain, applies configuration settings, and produces + /// various diagnostic plots and analyses including posteriors, correlations, + /// credible intervals, and Bayes factors. + /// + /// @param inputFile Path to the MCMC chain ROOT file void ProcessMCMCModule::ProcessMCMC(const std::string& inputFile) { MACH3LOG_INFO("File for study: {} with config {}", inputFile, config); @@ -258,13 +254,11 @@ namespace M3{ if(GetFromManager<bool>(Settings["MakePiePlot"], true)) Processor->MakePiePlot(); } - /** - * @brief Compare multiple MCMC chains - * - * Processes multiple MCMC chains simultaneously, compares their posterior - * distributions, and performs Kolmogorov-Smirnov tests to check if posteriors - * are consistent across chains. - */ + /// @brief Compare multiple MCMC chains + /// + /// Processes multiple MCMC chains simultaneously, compares their posterior + /// distributions, and performs Kolmogorov-Smirnov tests to check if posteriors + /// are consistent across chains. void ProcessMCMCModule::MultipleProcessMCMC() { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -473,15 +467,13 @@ namespace M3{ Posterior->Print(canvasname); } - /** - * @brief Calculate Bayes factors for hypothesis testing - * - * Computes Bayes factors to compare different models or hypotheses, - * particularly useful for oscillation parameters and switch parameters. - * Configuration is read from the YAML file under BayesFactor section. - * - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate Bayes factors for hypothesis testing + /// + /// Computes Bayes factors to compare different models or hypotheses, + /// particularly useful for oscillation parameters and switch parameters. + /// Configuration is read from the YAML file under BayesFactor section. + /// + /// @param Processor Pointer to the MCMCProcessor instance void ProcessMCMCModule::CalcBayesFactor(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -502,15 +494,13 @@ namespace M3{ Processor->GetBayesFactor(ParNames, Model1Bounds, Model2Bounds, ModelNames); } - /** - * @brief Calculate Savage-Dickey ratios - * - * Computes Savage-Dickey ratios for Bayes factor estimation at specific - * parameter values. Configuration is read from the YAML file under - * SavageDickey section. - * - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate Savage-Dickey ratios + /// + /// Computes Savage-Dickey ratios for Bayes factor estimation at specific + /// parameter values. Configuration is read from the YAML file under + /// SavageDickey section. + /// + /// @param Processor Pointer to the MCMCProcessor instance void ProcessMCMCModule::CalcSavageDickey(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -529,15 +519,13 @@ namespace M3{ Processor->GetSavageDickey(ParNames, EvaluationPoint, Bounds); } - /** - * @brief Calculate parameter evolution over MCMC steps - * - * Tracks how parameters evolve during the MCMC chain, useful for - * diagnosing convergence and burn-in. Configuration is read from - * the YAML file under ParameterEvolution section. - * - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate parameter evolution over MCMC steps + /// + /// Tracks how parameters evolve during the MCMC chain, useful for + /// diagnosing convergence and burn-in. Configuration is read from + /// the YAML file under ParameterEvolution section. + /// + /// @param Processor Pointer to the MCMCProcessor instance void ProcessMCMCModule::CalcParameterEvolution(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -553,15 +541,13 @@ namespace M3{ Processor->ParameterEvolution(ParNames, Intervals); } - /** - * @brief Create bipolar plots for parameter visualization - * - * Generates bipolar plots to visualize parameter distributions in a - * circular representation. Configuration is read from the YAML file - * under BipolarPlot section. - * - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Create bipolar plots for parameter visualization + /// + /// Generates bipolar plots to visualize parameter distributions in a + /// circular representation. Configuration is read from the YAML file + /// under BipolarPlot section. + /// + /// @param Processor Pointer to the MCMCProcessor instance void ProcessMCMCModule::CalcBipolarPlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); @@ -575,15 +561,13 @@ namespace M3{ Processor->GetPolarPlot(ParNames); } - /** - * @brief Generate triangle plots showing parameter correlations - * - * Creates triangle plots displaying 1D and 2D posterior distributions - * for sets of correlated parameters. Configuration is read from the - * YAML file under TrianglePlot section. - * - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Generate triangle plots showing parameter correlations + /// + /// Creates triangle plots displaying 1D and 2D posterior distributions + /// for sets of correlated parameters. Configuration is read from the + /// YAML file under TrianglePlot section. + /// + /// @param Processor Pointer to the MCMCProcessor instance void ProcessMCMCModule::GetTrianglePlot(MCMCProcessor* Processor) { YAML::Node card_yaml = M3OpenConfig(config.c_str()); YAML::Node Settings = card_yaml["ProcessMCMC"]; @@ -603,16 +587,14 @@ namespace M3{ } } - /** - * @brief Diagnose covariance matrix stability - * - * Validates the stability of the posterior covariance matrix by computing - * it at different burn-in cuts and comparing successive matrices. This helps - * determine when the matrix becomes stable and the appropriate burn-in length. - * - * @param Processor Pointer to the MCMCProcessor instance - * @param inputFile Path to the input file for naming output files - */ + /// @brief Diagnose covariance matrix stability + /// + /// Validates the stability of the posterior covariance matrix by computing + /// it at different burn-in cuts and comparing successive matrices. This helps + /// determine when the matrix becomes stable and the appropriate burn-in length. + /// + /// @param Processor Pointer to the MCMCProcessor instance + /// @param inputFile Path to the input file for naming output files void ProcessMCMCModule::DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile) { //Turn of plots from Processor @@ -788,16 +770,14 @@ namespace M3{ if(CorrelationHist != nullptr) delete CorrelationHist; } - /** - * @brief Convert TMatrixDSym to TH2D histogram - * - * Converts a ROOT symmetric matrix to a 2D histogram for visualization - * and easier manipulation with ROOT plotting tools. - * - * @param Matrix Pointer to the symmetric matrix - * @param title Title for the resulting histogram - * @return Pointer to the newly created TH2D histogram - */ + /// @brief Convert TMatrixDSym to TH2D histogram + /// + /// Converts a ROOT symmetric matrix to a 2D histogram for visualization + /// and easier manipulation with ROOT plotting tools. + /// + /// @param Matrix Pointer to the symmetric matrix + /// @param title Title for the resulting histogram + /// @return Pointer to the newly created TH2D histogram TH2D* ProcessMCMCModule::TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title) { TH2D* hMatrix = new TH2D(title.c_str(), title.c_str(), Matrix->GetNrows(), 0.0, Matrix->GetNrows(), Matrix->GetNcols(), 0.0, Matrix->GetNcols()); @@ -812,17 +792,15 @@ namespace M3{ return hMatrix; } - /** - * @brief Perform Kolmogorov-Smirnov test between posterior distributions - * - * Tests whether posterior distributions from different MCMC chains are - * consistent by computing the KS test statistic for each parameter. - * Results are visualized showing cumulative distributions and D-statistic. - * - * @param Processor Vector of MCMCProcessor instances (one per chain) - * @param Posterior Canvas for drawing the comparison plots - * @param canvasname Name for the output PDF file - */ + /// @brief Perform Kolmogorov-Smirnov test between posterior distributions + /// + /// Tests whether posterior distributions from different MCMC chains are + /// consistent by computing the KS test statistic for each parameter. + /// Results are visualized showing cumulative distributions and D-statistic. + /// + /// @param Processor Vector of MCMCProcessor instances (one per chain) + /// @param Posterior Canvas for drawing the comparison plots + /// @param canvasname Name for the output PDF file void ProcessMCMCModule::KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, const std::unique_ptr<TCanvas>& Posterior, const TString& canvasname) diff --git a/CLI/Modules/ProcessMCMCModule.hpp b/CLI/Modules/ProcessMCMCModule.hpp index d3f0cd782..67deedfab 100644 --- a/CLI/Modules/ProcessMCMCModule.hpp +++ b/CLI/Modules/ProcessMCMCModule.hpp @@ -1,7 +1,5 @@ -/** - * @file ProcessMCMCModule.hpp - * @brief Module for processing MCMC chains and producing diagnostic plots - */ +/// @file ProcessMCMCModule.hpp +/// @brief Module for processing MCMC chains and producing diagnostic plots //MaCh3 includes #pragma once @@ -12,117 +10,85 @@ namespace M3{ -/** - * @file ProcessMCMCModule.hpp - * @brief Main executable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implementation of methods is in MCMCProcessor - * @ingroup MaCh3DiagnosticProcessing - * - * @author Kamil Skwarczynski - */ - -/** - * @class ProcessMCMCModule - * @brief Module for processing MCMC chains and producing various diagnostic plots - * - * This module provides functionality for: - * - Drawing posterior distributions - * - Creating triangle plots - * - Computing Bayes factors - * - Performing Savage-Dickey tests - * - Calculating parameter evolution - * - Comparing multiple MCMC chains - */ +/// @file ProcessMCMCModule.hpp +/// @brief Main executable responsible for different types of MCMC processing like drawing posteriors, triangle plots etc. Actual implementation of methods is in MCMCProcessor +/// @ingroup MaCh3DiagnosticProcessing +/// +/// @author Kamil Skwarczynski + +/// @class ProcessMCMCModule +/// @brief Module for processing MCMC chains and producing various diagnostic plots +/// +/// This module provides functionality for: +/// - Drawing posterior distributions +/// - Creating triangle plots +/// - Computing Bayes factors +/// - Performing Savage-Dickey tests +/// - Calculating parameter evolution +/// - Comparing multiple MCMC chains class ProcessMCMCModule: public IModule{ public: - /** - * @brief Destructor - */ + /// @brief Destructor virtual ~ProcessMCMCModule(); - /** - * @brief Get the argument parser for this module - * @return Pointer to the configured MaCh3ArgumentParser - */ + /// @brief Get the argument parser for this module + /// @return Pointer to the configured MaCh3ArgumentParser MaCh3ArgumentParser* get_parser() override; - /** - * @brief Execute the MCMC processing - * @return Exit code (0 on success) - */ + /// @brief Execute the MCMC processing + /// @return Exit code (0 on success) int Run() override; private: - /** - * @brief Parse custom binning edges from YAML configuration - * @param Settings YAML node containing CustomBinEdges section - * @return Map of parameter names to their (min, max) bin edges - */ + /// @brief Parse custom binning edges from YAML configuration + /// @param Settings YAML node containing CustomBinEdges section + /// @return Map of parameter names to their (min, max) bin edges std::map<std::string, std::pair<double, double>> GetCustomBinning(const YAML::Node& Settings); - /** - * @brief Process a single MCMC chain - * @param inputFile Path to the MCMC chain ROOT file - */ + /// @brief Process a single MCMC chain + /// @param inputFile Path to the MCMC chain ROOT file void ProcessMCMC(const std::string& inputFile); - /** - * @brief Compare and process multiple MCMC chains - */ + /// @brief Compare and process multiple MCMC chains void MultipleProcessMCMC(); - /** - * @brief Calculate Bayes factors for hypothesis testing - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate Bayes factors for hypothesis testing + /// @param Processor Pointer to the MCMCProcessor instance void CalcBayesFactor(MCMCProcessor* Processor); - /** - * @brief Calculate Savage-Dickey ratios for Bayes factor estimation - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate Savage-Dickey ratios for Bayes factor estimation + /// @param Processor Pointer to the MCMCProcessor instance void CalcSavageDickey(MCMCProcessor* Processor); - /** - * @brief Calculate parameter evolution over MCMC steps - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Calculate parameter evolution over MCMC steps + /// @param Processor Pointer to the MCMCProcessor instance void CalcParameterEvolution(MCMCProcessor* Processor); - /** - * @brief Create bipolar plots for parameter visualization - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Create bipolar plots for parameter visualization + /// @param Processor Pointer to the MCMCProcessor instance void CalcBipolarPlot(MCMCProcessor* Processor); - /** - * @brief Generate triangle plots showing parameter correlations - * @param Processor Pointer to the MCMCProcessor instance - */ + /// @brief Generate triangle plots showing parameter correlations + /// @param Processor Pointer to the MCMCProcessor instance void GetTrianglePlot(MCMCProcessor* Processor); - /** - * @brief Diagnose covariance matrix stability across burn-in cuts - * @param Processor Pointer to the MCMCProcessor instance - * @param inputFile Path to the input file for naming output - */ + /// @brief Diagnose covariance matrix stability across burn-in cuts + /// @param Processor Pointer to the MCMCProcessor instance + /// @param inputFile Path to the input file for naming output void DiagnoseCovarianceMatrix(MCMCProcessor* Processor, const std::string& inputFile); - /** - * @brief Convert TMatrixDSym to TH2D histogram for plotting - * @param Matrix Pointer to the symmetric matrix - * @param title Title for the resulting histogram - * @return Pointer to the created TH2D histogram - */ + /// @brief Convert TMatrixDSym to TH2D histogram for plotting + /// @param Matrix Pointer to the symmetric matrix + /// @param title Title for the resulting histogram + /// @return Pointer to the created TH2D histogram TH2D* TMatrixIntoTH2D(TMatrixDSym* Matrix, const std::string& title); - /** - * @brief Perform Kolmogorov-Smirnov test between posterior distributions - * @param Processor Vector of MCMCProcessor instances - * @param Posterior Canvas for drawing results - * @param canvasname Name for the output PDF - */ + /// @brief Perform Kolmogorov-Smirnov test between posterior distributions + /// @param Processor Vector of MCMCProcessor instances + /// @param Posterior Canvas for drawing results + /// @param canvasname Name for the output PDF void KolmogorovSmirnovTest(const std::vector<std::unique_ptr<MCMCProcessor>>& Processor, const std::unique_ptr<TCanvas>& Posterior, const TString& canvasname); diff --git a/CLI/mach3.cpp b/CLI/mach3.cpp index 1609ca212..bfcd3dee7 100644 --- a/CLI/mach3.cpp +++ b/CLI/mach3.cpp @@ -1,26 +1,22 @@ -/** - * @file mach3.cpp - * @brief Main entry point for the MaCh3 command-line interface - * - * This file contains the main function that initializes the MaCh3 program, - * registers core modules, loads dynamic plugins, and processes command-line arguments. - */ +/// @file mach3.cpp +/// @brief Main entry point for the MaCh3 command-line interface +/// +/// This file contains the main function that initializes the MaCh3 program, +/// registers core modules, loads dynamic plugins, and processes command-line arguments. #include "CLI/Modules/ProcessMCMCModule.hpp" #include "CLI/Modules/DiagMCMCModule.hpp" #include "CLI/Modules/GetPenaltyTermModule.hpp" #include "CLI/MaCh3Program.hpp" -/** - * @brief Main entry point for the MaCh3 application - * - * Initializes the MaCh3 program with core modules and dynamic plugins, - * parses command-line arguments, and executes the selected subcommand. - * - * @param argc Number of command-line arguments - * @param argv Array of command-line argument strings - * @return Exit code (0 on success, non-zero on error) - */ +/// @brief Main entry point for the MaCh3 application +/// +/// Initializes the MaCh3 program with core modules and dynamic plugins, +/// parses command-line arguments, and executes the selected subcommand. +/// +/// @param argc Number of command-line arguments +/// @param argv Array of command-line argument strings +/// @return Exit code (0 on success, non-zero on error) int main(int argc, char *argv[]) { M3::MaCh3Program program("mach3");