From bed9ae900a5778ea79a314b25bd3d9c0d6c17c7a Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 19 Dec 2022 09:17:31 +0100 Subject: [PATCH 001/174] First upload of prototype classes --- source/framework/CMakeLists.txt | 2 +- .../sensitivity/inc/TRestComponent.h | 70 +++++++++++++ .../sensitivity/inc/TRestExperiment.h | 49 ++++++++++ source/framework/sensitivity/inc/TRestModel.h | 54 ++++++++++ .../framework/sensitivity/inc/TRestResponse.h | 43 ++++++++ .../sensitivity/inc/TRestSensitivity.h | 44 +++++++++ .../sensitivity/src/TRestComponent.cxx | 98 +++++++++++++++++++ .../sensitivity/src/TRestExperiment.cxx | 68 +++++++++++++ .../framework/sensitivity/src/TRestModel.cxx | 92 +++++++++++++++++ .../sensitivity/src/TRestResponse.cxx | 68 +++++++++++++ .../sensitivity/src/TRestSensitivity.cxx | 68 +++++++++++++ 11 files changed, 655 insertions(+), 1 deletion(-) create mode 100644 source/framework/sensitivity/inc/TRestComponent.h create mode 100644 source/framework/sensitivity/inc/TRestExperiment.h create mode 100644 source/framework/sensitivity/inc/TRestModel.h create mode 100644 source/framework/sensitivity/inc/TRestResponse.h create mode 100644 source/framework/sensitivity/inc/TRestSensitivity.h create mode 100644 source/framework/sensitivity/src/TRestComponent.cxx create mode 100644 source/framework/sensitivity/src/TRestExperiment.cxx create mode 100644 source/framework/sensitivity/src/TRestModel.cxx create mode 100644 source/framework/sensitivity/src/TRestResponse.cxx create mode 100644 source/framework/sensitivity/src/TRestSensitivity.cxx diff --git a/source/framework/CMakeLists.txt b/source/framework/CMakeLists.txt index af736aa5b..57cf0fecd 100644 --- a/source/framework/CMakeLists.txt +++ b/source/framework/CMakeLists.txt @@ -5,7 +5,7 @@ link_libraries("-fPIC") add_subdirectory(external) -set(contents external/tinyxml tools core analysis masks) +set(contents external/tinyxml tools core analysis masks sensitivity) file(GLOB_RECURSE addon_src "tiny*cpp" diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h new file mode 100644 index 000000000..962d4ce66 --- /dev/null +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -0,0 +1,70 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestComponent +#define REST_TRestComponent + +//#include "TRestDataSet.h" +#include "TRestMetadata.h" + +/// It defines a background/signal distribution in a given parameter space (tipically x,y,en) +class TRestComponent : public TRestMetadata { + private: + /// It defines how the distribution is initialized (dataset/formula) + std::string fType = "dataset"; + + /// A list (comma separated) with the branches that will be used to create the distribution + std::string fVariables; //< + + /// A list (comma separated) with the branches that will be used to weight the binning + std::string fWeights; //< + + /// The range of each of the variables used to create the distribution + std::vector fRange; //< + + /// The number of bins in which we should divide each variable + std::vector fNbins; //< + + /// The dataset used to initialize the distribution + // TRestDataSet fDataSet; //! + + /// The function used to initialize the distribution + std::string fFunction = ""; //! + + /// The function used to initialize the distribution + TFormula fFormula; //! + + /// A pointer to the component distribution + // THnD* fDistribution = nullptr; //! + + public: + Double_t GetRate(std::vector point); + + void PrintMetadata() override; + + void Initialize() override; + TRestComponent(); + ~TRestComponent(); + + ClassDefOverride(TRestComponent, 1); +}; +#endif diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h new file mode 100644 index 000000000..36fc10992 --- /dev/null +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -0,0 +1,49 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestExperiment +#define REST_TRestExperiment + +#include "TRestDataSet.h" +#include "TRestMetadata.h" +#include "TRestModel.h" + +/// It includes a model definition and experimental data used to obtain a final experimental sensitivity +class TRestExperiment : public TRestMetadata { + private: + /// It contains the model definition, including signal and background + TRestModel* fModel = nullptr; //< + + /// It contains the experimental data to be compared with the model + TRestDataSet fExperimentalData; //< + + public: + void Initialize() override; + + void PrintMetadata() override; + + TRestExperiment(); + ~TRestExperiment(); + + ClassDefOverride(TRestExperiment, 1); +}; +#endif diff --git a/source/framework/sensitivity/inc/TRestModel.h b/source/framework/sensitivity/inc/TRestModel.h new file mode 100644 index 000000000..3aebbd7b0 --- /dev/null +++ b/source/framework/sensitivity/inc/TRestModel.h @@ -0,0 +1,54 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestModel +#define REST_TRestModel + +#include "TRestComponent.h" +#include "TRestMetadata.h" + +/// A combination of signal and background components that build a complete signal and background model +class TRestModel : public TRestMetadata { + private: + // TODO At some point we may want to add here a coupling for each signal component + + /// A vector that includes the signal components in this model + std::vector fSignal; //< + + /// A vector that includes the background components in this model + std::vector fBackground; + + public: + void Initialize() override; + + Double_t GetSignal(std::vector point); + + Double_t GetBackground(std::vector point); + + void PrintMetadata() override; + + TRestModel(); + ~TRestModel(); + + ClassDefOverride(TRestModel, 1); +}; +#endif diff --git a/source/framework/sensitivity/inc/TRestResponse.h b/source/framework/sensitivity/inc/TRestResponse.h new file mode 100644 index 000000000..a47d3d23c --- /dev/null +++ b/source/framework/sensitivity/inc/TRestResponse.h @@ -0,0 +1,43 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestResponse +#define REST_TRestResponse + +#include "TRestMetadata.h" + +/// A response matrix that might be applied to a given signal variable inside TRestResponse +class TRestResponse : public TRestMetadata { + private: + // TODO Add here the response matrix. Probably a TH2D + + public: + void Initialize() override; + + void PrintMetadata() override; + + TRestResponse(); + ~TRestResponse(); + + ClassDefOverride(TRestResponse, 1); +}; +#endif diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h new file mode 100644 index 000000000..c8ad6f0db --- /dev/null +++ b/source/framework/sensitivity/inc/TRestSensitivity.h @@ -0,0 +1,44 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestSensitivity +#define REST_TRestSensitivity + +#include "TRestExperiment.h" + +/// It combines a number of experimental conditions allowing to calculate a combined experimental sensitivity +class TRestSensitivity : public TRestMetadata { + private: + /// A list of experimental conditions included to get a final sensitivity plot + std::vector fExperiment; //< + + public: + void Initialize() override; + + void PrintMetadata() override; + + TRestSensitivity(); + ~TRestSensitivity(); + + ClassDefOverride(TRestSensitivity, 1); +}; +#endif diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx new file mode 100644 index 000000000..ec37d52ec --- /dev/null +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -0,0 +1,98 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// This class allows to make a selection of ROOT data files that fulfill +/// certain metadata conditions allowing to create a group of files that +/// define a particular dataset. The files will be searched in a relative +/// or absolute path that is given together the `filePattern` parameter. +/// +/// ### Basic file selection +/// +/// We will be able to define the dates range where files will be +/// accepted, using `startTime` and `endTime` parameters. The run start +/// time and end time stored inside TRestRun will be evaluated to decide +/// if the file should be considered. +/// +/// A summary of the basic parameters follows: +/// +/// * **filePattern**: A full path glob pattern to the files that will +/// be considered. It is a first filter considering the path and the +/// filename. Usual wild cards such as * or ? will be allowed to target +/// a given range of files. +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2022-December: First implementation of TRestComponent +/// Javier Galan +/// +/// \class TRestComponent +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestComponent.h" + +ClassImp(TRestComponent); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestComponent::TRestComponent() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestComponent::~TRestComponent() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestComponent::Initialize() { SetSectionName(this->ClassName()); } + +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +Double_t TRestComponent::GetRate(std::vector point) { + // we get the rate in seconds at the corresponding bin + return 0.0; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestComponent::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << "----" << RESTendl; +} diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx new file mode 100644 index 000000000..c67c5e3ee --- /dev/null +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -0,0 +1,68 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// Documentation TOBE written +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2022-December: First implementation of TRestExperiment +/// Javier Galan +/// +/// \class TRestExperiment +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestExperiment.h" + +ClassImp(TRestExperiment); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestExperiment::TRestExperiment() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestExperiment::~TRestExperiment() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestExperiment::Initialize() { SetSectionName(this->ClassName()); } + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestExperiment::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << "----" << RESTendl; +} diff --git a/source/framework/sensitivity/src/TRestModel.cxx b/source/framework/sensitivity/src/TRestModel.cxx new file mode 100644 index 000000000..5dab1d460 --- /dev/null +++ b/source/framework/sensitivity/src/TRestModel.cxx @@ -0,0 +1,92 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// Documentation TOBE written +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2022-December: First implementation of TRestModel +/// Javier Galan +/// +/// \class TRestModel +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestModel.h" + +ClassImp(TRestModel); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestModel::TRestModel() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestModel::~TRestModel() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestModel::Initialize() { SetSectionName(this->ClassName()); } + +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// combined background. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +Double_t TRestModel::GetBackground(std::vector point) { + // we get the rate in seconds at the corresponding bin + return 0.0; +} + +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// combined signal. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +Double_t TRestModel::GetSignal(std::vector point) { + // we get the rate in seconds at the corresponding bin + return 0.0; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestModel::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << "----" << RESTendl; +} diff --git a/source/framework/sensitivity/src/TRestResponse.cxx b/source/framework/sensitivity/src/TRestResponse.cxx new file mode 100644 index 000000000..7211db5b8 --- /dev/null +++ b/source/framework/sensitivity/src/TRestResponse.cxx @@ -0,0 +1,68 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// Documentation TOBE written +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2022-December: First implementation of TRestResponse +/// Javier Galan +/// +/// \class TRestResponse +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestResponse.h" + +ClassImp(TRestResponse); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestResponse::TRestResponse() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestResponse::~TRestResponse() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestResponse::Initialize() { SetSectionName(this->ClassName()); } + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestResponse::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << "----" << RESTendl; +} diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx new file mode 100644 index 000000000..84503f12a --- /dev/null +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -0,0 +1,68 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// Documentation TOBE written +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2022-December: First implementation of TRestSensitivity +/// Javier Galan +/// +/// \class TRestSensitivity +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestSensitivity.h" + +ClassImp(TRestSensitivity); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestSensitivity::TRestSensitivity() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestSensitivity::~TRestSensitivity() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestSensitivity::Initialize() { SetSectionName(this->ClassName()); } + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestSensitivity::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << "----" << RESTendl; +} From 711458f69646416ef648b9c734bfee2fa95ae3d9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:09:21 +0000 Subject: [PATCH 002/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 962d4ce66..edf3f5785 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,7 +23,7 @@ #ifndef REST_TRestComponent #define REST_TRestComponent -//#include "TRestDataSet.h" +// #include "TRestDataSet.h" #include "TRestMetadata.h" /// It defines a background/signal distribution in a given parameter space (tipically x,y,en) From 3be2b1923b370b028e2a120b53abb676d713dc17 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 11 Mar 2023 07:09:34 +0100 Subject: [PATCH 003/174] startup.cpp Adding more output --- source/framework/core/src/startup.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/framework/core/src/startup.cpp b/source/framework/core/src/startup.cpp index 259956073..55d360a53 100644 --- a/source/framework/core/src/startup.cpp +++ b/source/framework/core/src/startup.cpp @@ -288,7 +288,8 @@ vector StringToVector(string vec) { } } else { - cout << "illegal format!" << endl; + cout << "Startup. StringToVector. Illegal format!" << endl; + cout << "A vector should be defined using brackets and comma separated elements: {a,b,c,d}" << endl; return vector{}; } From e96b2ff2c01001dc07928dd9272cb737469dd4c5 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 11 Mar 2023 10:43:45 +0100 Subject: [PATCH 004/174] TRestStringHelper::RemoteDelimiters method added --- source/framework/tools/inc/TRestStringHelper.h | 1 + .../framework/tools/src/TRestStringHelper.cxx | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/source/framework/tools/inc/TRestStringHelper.h b/source/framework/tools/inc/TRestStringHelper.h index c49b97225..f33929793 100644 --- a/source/framework/tools/inc/TRestStringHelper.h +++ b/source/framework/tools/inc/TRestStringHelper.h @@ -45,6 +45,7 @@ std::vector Split(std::string in, std::string separator, bool allow std::vector StringToElements(std::string in, std::string separator); std::vector StringToElements(std::string in, std::string headChar, std::string separator, std::string tailChar); +std::string RemoveDelimiters(std::string in); std::string RemoveWhiteSpaces(std::string in); std::string Replace(std::string in, std::string thisString, std::string byThisString, size_t fromPosition = 0, Int_t N = 0); diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 4bfeca413..bd025cc40 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -248,11 +248,13 @@ vector REST_StringHelper::Split(string in, string separator, bool allowB /////////////////////////////////////////////// /// \brief Convert the input string into a vector of double elements /// +/// The method will remove any delimiters found in the string (), [] or {}. +/// /// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.} /// vector REST_StringHelper::StringToElements(string in, string separator) { vector result; - vector vec_str = REST_StringHelper::Split(in, separator); + vector vec_str = REST_StringHelper::Split(RemoveDelimiters(in), separator); for (unsigned int i = 0; i < vec_str.size(); i++) { double temp = REST_StringHelper::StringToDouble(vec_str[i]); result.push_back(temp); @@ -286,6 +288,19 @@ vector REST_StringHelper::StringToElements(string in, string headChar, s return result; } +/////////////////////////////////////////////// +/// \brief Returns the input string removing any delimiters ({[]}) +/// +string REST_StringHelper::RemoveDelimiters(string in) { + string out = in; + size_t pos = out.find_first_of("+-*/e^%"); + while ((pos = out.find_first_of("({[]})")) != string::npos) { + out.erase(pos, 1); + } + + return out; +} + /////////////////////////////////////////////// /// \brief Returns the input string removing all white spaces. /// From 56676517e773d5399a132a2dbe63e028f17635e9 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 11 Mar 2023 22:43:05 +0100 Subject: [PATCH 005/174] TRestComponent. Finalised integration of metadata members --- .../sensitivity/inc/TRestComponent.h | 46 ++++++-- .../sensitivity/src/TRestComponent.cxx | 106 ++++++++++++++---- 2 files changed, 122 insertions(+), 30 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index edf3f5785..3625b24ce 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,45 +23,69 @@ #ifndef REST_TRestComponent #define REST_TRestComponent -// #include "TRestDataSet.h" +#include "TRestDataSet.h" #include "TRestMetadata.h" /// It defines a background/signal distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { private: + //// This will not be necessary the day TRestComponent is a pure abstract class. + //// We will create an instance of TRestDataSetComponent or TRestFormulaComponent /// It defines how the distribution is initialized (dataset/formula) std::string fType = "dataset"; - /// A list (comma separated) with the branches that will be used to create the distribution - std::string fVariables; //< - - /// A list (comma separated) with the branches that will be used to weight the binning - std::string fWeights; //< + /// A list with the branches that will be used to create the distribution space + std::vector fVariables; //< /// The range of each of the variables used to create the distribution - std::vector fRange; //< + std::vector fRanges; //< /// The number of bins in which we should divide each variable std::vector fNbins; //< + /// A list with the branches that will be used to construct the distribution density + std::vector fWeights; //< + + /// It does not contribute to define the density distribution but allows to parametrize a set of + /// distribution densities + std::string fParametricVariable = ""; //< + + /// It defines the nodes of the parametrization + std::vector fParametrizationNodes; + + /// It defines the binning between the parametrization nodes + Int_t fParametrizationBinning = 0; + + ////////// This should be implemented in TRestDataSetComponent + ////////// /// The dataset used to initialize the distribution - // TRestDataSet fDataSet; //! + TRestDataSet fDataSet; //! + ////////// + ////////// This should be implemented in TRestDataSetComponent + ////////// This should be implemented in TRestFormulaComponent + ////////// /// The function used to initialize the distribution - std::string fFunction = ""; //! - + /// std::string fFunction = ""; //! + /// /// The function used to initialize the distribution - TFormula fFormula; //! + /// TFormula fFormula; //! + ////////// + ////////// This should be implemented in TRestFormulaComponent /// A pointer to the component distribution // THnD* fDistribution = nullptr; //! + protected: + void InitFromConfigFile() override; + public: Double_t GetRate(std::vector point); void PrintMetadata() override; void Initialize() override; + TRestComponent(const char* cfgFileName, const std::string& name); TRestComponent(); ~TRestComponent(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index ec37d52ec..94d2e3d7a 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -21,24 +21,7 @@ *************************************************************************/ ///////////////////////////////////////////////////////////////////////// -/// This class allows to make a selection of ROOT data files that fulfill -/// certain metadata conditions allowing to create a group of files that -/// define a particular dataset. The files will be searched in a relative -/// or absolute path that is given together the `filePattern` parameter. -/// -/// ### Basic file selection -/// -/// We will be able to define the dates range where files will be -/// accepted, using `startTime` and `endTime` parameters. The run start -/// time and end time stored inside TRestRun will be evaluated to decide -/// if the file should be considered. -/// -/// A summary of the basic parameters follows: -/// -/// * **filePattern**: A full path glob pattern to the files that will -/// be considered. It is a first filter considering the path and the -/// filename. Usual wild cards such as * or ? will be allowed to target -/// a given range of files. +/// This class allows to ... /// /// ///---------------------------------------------------------------------- @@ -47,7 +30,7 @@ /// /// History of developments: /// -/// 2022-December: First implementation of TRestComponent +/// 2023-December: First implementation of TRestComponent /// Javier Galan /// /// \class TRestComponent @@ -64,6 +47,27 @@ ClassImp(TRestComponent); /// TRestComponent::TRestComponent() { Initialize(); } +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name) + : TRestMetadata(cfgFileName) { + LoadConfigFromFile(fConfigFileName, name); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); +} + /////////////////////////////////////////////// /// \brief Default destructor /// @@ -94,5 +98,69 @@ Double_t TRestComponent::GetRate(std::vector point) { void TRestComponent::PrintMetadata() { TRestMetadata::PrintMetadata(); + if (fVariables.size() != fRanges.size()) + RESTWarning << "The number of variables does not match with the number of defined ranges!" + << RESTendl; + + else if (fVariables.size() != fNbins.size()) + RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; + else { + int n = 0; + RESTMetadata << " === Variables === " << RESTendl; + for (const auto& varName : fVariables) { + RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() + << ") bins: " << fNbins[n] << RESTendl; + n++; + } + } + + RESTMetadata << " " << RESTendl; + RESTMetadata << " === Weights === " << RESTendl; + for (const auto& wName : fWeights) RESTMetadata << "- " << wName << RESTendl; + + if (!fParametricVariable.empty()) { + RESTMetadata << " " << RESTendl; + RESTMetadata << " === Parametrization === " << RESTendl; + RESTMetadata << "- Parametric variable : " << fParametricVariable << RESTendl; + + RESTMetadata << " - Parametric nodes : "; + for (const auto& node : fParametrizationNodes) { + RESTMetadata << node << " "; + } + RESTMetadata << RESTendl; + } + + RESTMetadata << " - Parametrization binning : " << fParametrizationBinning << RESTendl; + RESTMetadata << "----" << RESTendl; } + +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class +/// +void TRestComponent::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + auto ele = GetElement("variable"); + while (ele != nullptr) { + std::string name = GetParameter("name", ele, ""); + TVector2 v = Get2DVectorParameterWithUnits("range", ele); + Int_t bins = StringToInteger(GetParameter("bins", ele, "100")); + + fVariables.push_back(name); + fRanges.push_back(v); + fNbins.push_back(bins); + + ele = GetNextElement(ele); + } + + ele = GetElement("parametricVariable"); + while (ele != nullptr) { + fParametricVariable = GetParameter("name", ele, ""); + std::cout << GetParameter("nodes", ele, "-1") << std::endl; + fParametrizationNodes = StringToElements(GetParameter("nodes", ele, "-1"), ","); + fParametrizationBinning = StringToInteger(GetParameter("bins", ele, "100")); + + ele = GetNextElement(ele); + } +} From d317b7863a80dc38497bcdf59259317fa27b88fc Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 12 Mar 2023 10:33:30 +0100 Subject: [PATCH 006/174] TRestComponent. Removing unwanted output --- source/framework/sensitivity/src/TRestComponent.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 94d2e3d7a..6dc46e504 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -157,7 +157,6 @@ void TRestComponent::InitFromConfigFile() { ele = GetElement("parametricVariable"); while (ele != nullptr) { fParametricVariable = GetParameter("name", ele, ""); - std::cout << GetParameter("nodes", ele, "-1") << std::endl; fParametrizationNodes = StringToElements(GetParameter("nodes", ele, "-1"), ","); fParametrizationBinning = StringToInteger(GetParameter("bins", ele, "100")); From cd79025116c899142d691d1f34700f9dba07b4bd Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 30 Mar 2023 11:02:20 +0200 Subject: [PATCH 007/174] TRestDataSet::GetTree(). Improving error output when fTree is null. --- source/framework/core/inc/TRestDataSet.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index d78fd72ce..93dc7e7ac 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -116,8 +116,8 @@ class TRestDataSet : public TRestMetadata { TTree* GetTree() const { if (fTree == nullptr) { RESTError << "Tree has not been yet initialized" << RESTendl; - RESTError << "You should invoke TRestDataSet::Initialize() before trying to access the tree" - << RESTendl; + RESTError << "You should invoke TRestDataSet::GenerateDataSet() or " << RESTendl; + RESTError << "TRestDataSet::Import( fname ) before trying to access the tree" << RESTendl; } return fTree; } From b54ae46b0332c2e54b49991dcb46550f42af23c2 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 30 Mar 2023 11:03:14 +0200 Subject: [PATCH 008/174] TRestDataSet::GenerateDataSet. Fixing typo --- source/framework/core/src/TRestDataSet.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 9acd42e69..d2d8aff96 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -273,7 +273,8 @@ void TRestDataSet::Initialize() { SetSectionName(this->ClassName()); } /// void TRestDataSet::GenerateDataSet() { if (fTree != nullptr) { - RESTWarning << "Tree has already been loaded. Skipping TRestDataSet::Initialize ... " << RESTendl; + RESTWarning << "Tree has already been loaded. Skipping TRestDataSet::GenerateDataSet ... " + << RESTendl; return; } From 765a0f119cc72a007dfb47b79965bd385c684766 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 25 May 2023 11:43:36 +0200 Subject: [PATCH 009/174] TRestComponent. Loading dataset --- .../sensitivity/inc/TRestComponent.h | 29 +++++++-- .../sensitivity/src/TRestComponent.cxx | 64 +++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 3625b24ce..fd44be9ed 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -26,7 +26,7 @@ #include "TRestDataSet.h" #include "TRestMetadata.h" -/// It defines a background/signal distribution in a given parameter space (tipically x,y,en) +/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { private: //// This will not be necessary the day TRestComponent is a pure abstract class. @@ -37,7 +37,7 @@ class TRestComponent : public TRestMetadata { /// A list with the branches that will be used to create the distribution space std::vector fVariables; //< - /// The range of each of the variables used to create the distribution + /// The range of each of the variables used to create the PDF distribution std::vector fRanges; //< /// The number of bins in which we should divide each variable @@ -51,17 +51,25 @@ class TRestComponent : public TRestMetadata { std::string fParametricVariable = ""; //< /// It defines the nodes of the parametrization - std::vector fParametrizationNodes; + std::vector fParametrizationNodes; //< /// It defines the binning between the parametrization nodes - Int_t fParametrizationBinning = 0; + Int_t fParametrizationBinning = 0; //< ////////// This should be implemented in TRestDataSetComponent ////////// + + /// The filename of the dataset used + std::string fDataSetFileName = ""; //< + /// The dataset used to initialize the distribution TRestDataSet fDataSet; //! - ////////// - ////////// This should be implemented in TRestDataSetComponent + + /// It is true of the dataset was loaded without issues + Bool_t fDataSetLoaded = false; + + ////////// + ////////// This should be implemented in TRestDataSetComponent ////////// This should be implemented in TRestFormulaComponent ////////// @@ -76,15 +84,24 @@ class TRestComponent : public TRestMetadata { /// A pointer to the component distribution // THnD* fDistribution = nullptr; //! + Bool_t VariablesOk(); + Bool_t WeightsOk(); + protected: void InitFromConfigFile() override; public: + Bool_t LoadDataSet(std::string fname); + + /// This method should go to TRestDataSetComponent + Bool_t IsDataSetLoaded() { return fDataSetLoaded; } + Double_t GetRate(std::vector point); void PrintMetadata() override; void Initialize() override; + TRestComponent(const char* configFilename); TRestComponent(const char* cfgFileName, const std::string& name); TRestComponent(); ~TRestComponent(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 6dc46e504..888fc2e37 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -40,6 +40,8 @@ /// #include "TRestComponent.h" +#include "TKey.h" + ClassImp(TRestComponent); /////////////////////////////////////////////// @@ -47,6 +49,12 @@ ClassImp(TRestComponent); /// TRestComponent::TRestComponent() { Initialize(); } +TRestComponent::TRestComponent(const char* configFilename) : TRestMetadata(configFilename) { + Initialize(); + + LoadConfigFromFile(fConfigFileName); +} + ///////////////////////////////////////////// /// \brief Constructor loading data from a config file /// @@ -162,4 +170,60 @@ void TRestComponent::InitFromConfigFile() { ele = GetNextElement(ele); } + + if (fDataSetFileName != "") { + fDataSetLoaded = LoadDataSet(fDataSetFileName); + } else { + RESTWarning + << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" + << RESTendl; + } +} + +Bool_t TRestComponent::LoadDataSet(std::string fname) { + std::string fullFileName = SearchFile(fDataSetFileName); + if (fullFileName.empty()) { + RESTError << "TRestComponent::LoadDataSet. Error loading file : " << fDataSetFileName << RESTendl; + RESTError << "Does the file exist?" << RESTendl; + RESTError << "You may use ` = TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); + + return (VariablesOk() && WeightsOk()); +} + +///////////////////////////////////////////// +/// \brief It returns true if all variables have been found inside TRestDataSet +/// +Bool_t TRestComponent::VariablesOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fVariables) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; +} + +Bool_t TRestComponent::WeightsOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fWeights) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; } From 50c841aba1f2a4902fde841f338dad054d742677 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 10:41:17 +0000 Subject: [PATCH 010/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/analysis/src/TRestDataSetOdds.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/analysis/src/TRestDataSetOdds.cxx b/source/framework/analysis/src/TRestDataSetOdds.cxx index 48bb8b5ac..46970da3d 100644 --- a/source/framework/analysis/src/TRestDataSetOdds.cxx +++ b/source/framework/analysis/src/TRestDataSetOdds.cxx @@ -239,7 +239,7 @@ void TRestDataSetOdds::ComputeLogOdds() { std::string totName = ""; for (const auto& [obsName, histo] : fHistos) { const std::string oddsName = "odds_" + obsName; - auto GetLogOdds = [& histo = histo](double val) { + auto GetLogOdds = [&histo = histo](double val) { double odds = histo->GetBinContent(histo->GetXaxis()->FindBin(val)); if (odds == 0) return 1000.; return log(1. - odds) - log(odds); From 2302666b7f0a1aa95ac2fcf346c842f514f9870d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 25 May 2023 12:50:19 +0200 Subject: [PATCH 011/174] TRestDataSet. Fixing compilation issue --- source/framework/core/inc/TRestDataSet.h | 1 - 1 file changed, 1 deletion(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index bc0d762db..0cf5dbd64 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -121,7 +121,6 @@ class TRestDataSet : public TRestMetadata { RESTError << "Tree has not been yet initialized" << RESTendl; RESTError << "You should invoke TRestDataSet::GenerateDataSet() or " << RESTendl; RESTError << "TRestDataSet::Import( fname ) before trying to access the tree" << RESTendl; - << RESTendl; } return fTree; } From 1a57bc4edd78197c93e8a49ffbe96f6c937950ee Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 May 2023 16:45:05 +0200 Subject: [PATCH 012/174] TRestComponent. Adding capability to import several files --- .../sensitivity/inc/TRestComponent.h | 4 +- .../sensitivity/src/TRestComponent.cxx | 39 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index fd44be9ed..584ec8a8f 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -60,7 +60,7 @@ class TRestComponent : public TRestMetadata { ////////// /// The filename of the dataset used - std::string fDataSetFileName = ""; //< + std::vector fDataSetFileNames; //< /// The dataset used to initialize the distribution TRestDataSet fDataSet; //! @@ -91,7 +91,7 @@ class TRestComponent : public TRestMetadata { void InitFromConfigFile() override; public: - Bool_t LoadDataSet(std::string fname); + Bool_t LoadDataSets(); /// This method should go to TRestDataSetComponent Bool_t IsDataSetLoaded() { return fDataSetLoaded; } diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 888fc2e37..0fffa385c 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -171,8 +171,14 @@ void TRestComponent::InitFromConfigFile() { ele = GetNextElement(ele); } - if (fDataSetFileName != "") { - fDataSetLoaded = LoadDataSet(fDataSetFileName); + ele = GetElement("dataset"); + while (ele != nullptr) { + fDataSetFileNames.push_back(GetParameter("file", ele, "")); + ele = GetNextElement(ele); + } + + if (!fDataSetFileNames.empty()) { + fDataSetLoaded = LoadDataSets(); } else { RESTWarning << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" @@ -180,19 +186,27 @@ void TRestComponent::InitFromConfigFile() { } } -Bool_t TRestComponent::LoadDataSet(std::string fname) { - std::string fullFileName = SearchFile(fDataSetFileName); - if (fullFileName.empty()) { - RESTError << "TRestComponent::LoadDataSet. Error loading file : " << fDataSetFileName << RESTendl; - RESTError << "Does the file exist?" << RESTendl; - RESTError << "You may use ` fullFileNames; + for (const auto& name : fDataSetFileNames) { + std::string fileName = SearchFile(name); + if (fileName.empty()) { + RESTError << "TRestComponent::LoadDataSet. Error loading file : " << name << RESTendl; + RESTError << "Does the file exist?" << RESTendl; + RESTError << "You may use ` Date: Mon, 10 Jul 2023 12:05:22 +0200 Subject: [PATCH 013/174] TRestComponent::fParametricVariables is now a vector --- source/framework/sensitivity/inc/TRestComponent.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 584ec8a8f..281a96c59 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -43,12 +43,11 @@ class TRestComponent : public TRestMetadata { /// The number of bins in which we should divide each variable std::vector fNbins; //< - /// A list with the branches that will be used to construct the distribution density + /// A list with the branches that will be used to weight the distribution density std::vector fWeights; //< - /// It does not contribute to define the density distribution but allows to parametrize a set of - /// distribution densities - std::string fParametricVariable = ""; //< + /// It is used to parametrize a set of distribution densities + std::vector fParametricVariables = ""; //< /// It defines the nodes of the parametrization std::vector fParametrizationNodes; //< From 27719f4c459db4ba34323db21fd1116a64aba335 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 19 Jul 2023 09:59:45 +0200 Subject: [PATCH 014/174] TRestComponent. Only a parameter is allowed now --- .../sensitivity/inc/TRestComponent.h | 11 ++--- .../sensitivity/src/TRestComponent.cxx | 40 +++++++++---------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 281a96c59..34bfac256 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -46,14 +46,11 @@ class TRestComponent : public TRestMetadata { /// A list with the branches that will be used to weight the distribution density std::vector fWeights; //< - /// It is used to parametrize a set of distribution densities - std::vector fParametricVariables = ""; //< + /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass) + std::string fParameter = ""; //< - /// It defines the nodes of the parametrization - std::vector fParametrizationNodes; //< - - /// It defines the binning between the parametrization nodes - Int_t fParametrizationBinning = 0; //< + /// It defines the nodes of the parametrization (Initialized by the dataset) + std::vector fParameterizationNodes; //< ////////// This should be implemented in TRestDataSetComponent ////////// diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 0fffa385c..fefbf56d6 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -126,20 +126,18 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " === Weights === " << RESTendl; for (const auto& wName : fWeights) RESTMetadata << "- " << wName << RESTendl; - if (!fParametricVariable.empty()) { + if (!fParameter.empty()) { RESTMetadata << " " << RESTendl; - RESTMetadata << " === Parametrization === " << RESTendl; - RESTMetadata << "- Parametric variable : " << fParametricVariable << RESTendl; + RESTMetadata << " === Parameterization === " << RESTendl; + RESTMetadata << "- Parameter : " << fParameter << RESTendl; RESTMetadata << " - Parametric nodes : "; - for (const auto& node : fParametrizationNodes) { + for (const auto& node : fParameterizationNodes) { RESTMetadata << node << " "; } RESTMetadata << RESTendl; } - RESTMetadata << " - Parametrization binning : " << fParametrizationBinning << RESTendl; - RESTMetadata << "----" << RESTendl; } @@ -152,32 +150,30 @@ void TRestComponent::InitFromConfigFile() { auto ele = GetElement("variable"); while (ele != nullptr) { std::string name = GetParameter("name", ele, ""); - TVector2 v = Get2DVectorParameterWithUnits("range", ele); - Int_t bins = StringToInteger(GetParameter("bins", ele, "100")); - - fVariables.push_back(name); - fRanges.push_back(v); - fNbins.push_back(bins); - - ele = GetNextElement(ele); - } - - ele = GetElement("parametricVariable"); - while (ele != nullptr) { - fParametricVariable = GetParameter("name", ele, ""); - fParametrizationNodes = StringToElements(GetParameter("nodes", ele, "-1"), ","); - fParametrizationBinning = StringToInteger(GetParameter("bins", ele, "100")); + TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); + Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); + + if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { + RESTWarning << "TRestComponent::fVariable. Problem with definition." << RESTendl; + RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins + << RESTendl; + } else { + fVariables.push_back(name); + fRanges.push_back(v); + fNbins.push_back(bins); + } ele = GetNextElement(ele); } ele = GetElement("dataset"); while (ele != nullptr) { - fDataSetFileNames.push_back(GetParameter("file", ele, "")); + fDataSetFileNames.push_back(GetParameter("filename", ele, "")); ele = GetNextElement(ele); } if (!fDataSetFileNames.empty()) { + RESTInfo << "Loading datasets" << RESTendl; fDataSetLoaded = LoadDataSets(); } else { RESTWarning From 47d8ad9b4e4b711a4979d44edb5cfe88e5486107 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 20 Jul 2023 11:26:58 +0200 Subject: [PATCH 015/174] TRestDataSet::Import will not be set as merged if there is only one file --- source/framework/core/src/TRestDataSet.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 430aba8aa..7d05756f4 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -943,6 +943,11 @@ void TRestDataSet::Import(std::vector fileNames) { if (fileNames.size() == 0) return; + if (fileNames.size() == 1) { + this->Import(fileNames[0]); + return; + } + TFile* file = TFile::Open(fileNames[0].c_str(), "READ"); if (file != nullptr) { TIter nextkey(file->GetListOfKeys()); From 4fbcde22ccebea07b794698620f420c06e50ed87 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 20 Jul 2023 19:59:33 +0200 Subject: [PATCH 016/174] TRestMetadata::SetError now receives an argument to fix the maximum number of print outs --- source/framework/core/inc/TRestMetadata.h | 4 ++-- source/framework/core/src/TRestMetadata.cxx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/framework/core/inc/TRestMetadata.h b/source/framework/core/inc/TRestMetadata.h index e1e1910ef..8c1c169c8 100644 --- a/source/framework/core/inc/TRestMetadata.h +++ b/source/framework/core/inc/TRestMetadata.h @@ -216,10 +216,10 @@ class TRestMetadata : public TNamed { void AddLog(std::string log = "", bool print = true); /// A metadata class may use this method to signal that something went wrong - void SetError(std::string message = "", bool print = true); + void SetError(std::string message = "", bool print = true, int maxPrint = 5); /// A metadata class may use this method to signal that something went wrong - void SetWarning(std::string message = "", bool print = true); + void SetWarning(std::string message = "", bool print = true, int maxPrint = 5); /// Returns a std::string containing the error message TString GetErrorMessage(); diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index 35e9d6fc6..2d3e1bba3 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -2617,23 +2617,23 @@ void TRestMetadata::AddLog(string log, bool print) { } } -void TRestMetadata::SetError(string message, bool print) { +void TRestMetadata::SetError(string message, bool print, int maxPrint) { fError = true; fNErrors++; if (message != "") { fErrorMessage += message + "\n"; - if (print) { + if (print && fNErrors < maxPrint) { cout << message << endl; } } } -void TRestMetadata::SetWarning(string message, bool print) { +void TRestMetadata::SetWarning(string message, bool print, int maxPrint) { fWarning = true; fNWarnings++; if (message != "") { fWarningMessage += message + "\n"; - if (print) { + if (print && fNWarnings < maxPrint) { RESTWarning << message << RESTendl; } } From f297a9342eaa67d4ca0515b95899eb2e01f943fa Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 21 Jul 2023 07:44:41 +0200 Subject: [PATCH 017/174] TRestStringHelper::DoubleToString. Default precision is now %8.6e --- source/framework/tools/inc/TRestStringHelper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/tools/inc/TRestStringHelper.h b/source/framework/tools/inc/TRestStringHelper.h index d0ffad473..7e5f91ebc 100644 --- a/source/framework/tools/inc/TRestStringHelper.h +++ b/source/framework/tools/inc/TRestStringHelper.h @@ -35,7 +35,7 @@ Float_t StringToFloat(std::string in); Double_t StringToDouble(std::string in); Int_t StringToInteger(std::string in); std::string IntegerToString(Int_t n, std::string format = "%d"); -std::string DoubleToString(Double_t d, std::string format = "%4.2lf"); +std::string DoubleToString(Double_t d, std::string format = "%8.6e"); Bool_t StringToBool(std::string booleanString); Long64_t StringToLong(std::string in); TVector3 StringTo3DVector(std::string in); From a77dda771f1679598b732316383411f26994ea22 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 21 Jul 2023 07:45:42 +0200 Subject: [PATCH 018/174] TRestComponent. Implemented parameter node extraction from dataset --- .../sensitivity/inc/TRestComponent.h | 10 +++- .../sensitivity/src/TRestComponent.cxx | 57 ++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 34bfac256..ae76ca0b2 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -49,9 +49,12 @@ class TRestComponent : public TRestMetadata { /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass) std::string fParameter = ""; //< - /// It defines the nodes of the parametrization (Initialized by the dataset) + /// It defines the nodes of the parameterization (Initialized by the dataset) std::vector fParameterizationNodes; //< + /// It defines the statistics of each parameterization node (Initialized by the dataset) + std::vector fNodeStatistics; //< + ////////// This should be implemented in TRestDataSetComponent ////////// @@ -80,6 +83,9 @@ class TRestComponent : public TRestMetadata { /// A pointer to the component distribution // THnD* fDistribution = nullptr; //! + std::vector ExtractParameterizationNodes(); + std::vector ExtractNodeStatistics(); + Bool_t VariablesOk(); Bool_t WeightsOk(); @@ -96,6 +102,8 @@ class TRestComponent : public TRestMetadata { void PrintMetadata() override; + void PrintStatistics(); + void Initialize() override; TRestComponent(const char* configFilename); TRestComponent(const char* cfgFileName, const std::string& name); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index fefbf56d6..7fe98250a 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -131,16 +131,39 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " === Parameterization === " << RESTendl; RESTMetadata << "- Parameter : " << fParameter << RESTendl; - RESTMetadata << " - Parametric nodes : "; + RESTMetadata << " - Parametric nodes : " << fParameterizationNodes.size() << RESTendl; + RESTMetadata << " " << RESTendl; + RESTMetadata << " Use : this->PrintStatistics() for additional info" << RESTendl; + /* + RESTMetadata << " Values : "; + int n = 0; for (const auto& node : fParameterizationNodes) { - RESTMetadata << node << " "; + if ( n == 0 ) RESTMetadata << node; + else RESTMetadata << ", " << node; + n++; + if( n % 10 == 0 ) RESTMetadata << RESTendl; } RESTMetadata << RESTendl; + */ } RESTMetadata << "----" << RESTendl; } +void TRestComponent::PrintStatistics() { + auto nEv = fDataSet.GetDataFrame().Count(); + std::cout << "Total counts : " << *nEv << std::endl; + std::cout << std::endl; + + fNodeStatistics = ExtractNodeStatistics(); + std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; + int n = 0; + for (const auto& p : fParameterizationNodes) { + std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; + n++; + } +} + ///////////////////////////////////////////// /// \brief It customizes the retrieval of XML data values of this class /// @@ -175,6 +198,8 @@ void TRestComponent::InitFromConfigFile() { if (!fDataSetFileNames.empty()) { RESTInfo << "Loading datasets" << RESTendl; fDataSetLoaded = LoadDataSets(); + + fParameterizationNodes = ExtractParameterizationNodes(); } else { RESTWarning << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" @@ -182,6 +207,34 @@ void TRestComponent::InitFromConfigFile() { } } +std::vector TRestComponent::ExtractParameterizationNodes() { + auto parValues = fDataSet.GetDataFrame().Take(fParameter); + std::vector vs; + for (const auto v : parValues) vs.push_back(v); + + std::vector::iterator ip; + ip = std::unique(vs.begin(), vs.begin() + vs.size()); + vs.resize(std::distance(vs.begin(), ip)); + std::sort(vs.begin(), vs.end()); + ip = std::unique(vs.begin(), vs.end()); + vs.resize(std::distance(vs.begin(), ip)); + + return vs; +} + +std::vector TRestComponent::ExtractNodeStatistics() { + if (!fNodeStatistics.empty()) return fNodeStatistics; + + std::cout << "Counting statistics for each node ..." << std::endl; + std::vector stats; + for (const auto& p : fParameterizationNodes) { + std::string filter = fParameter + " == " + DoubleToString(p); + auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); + stats.push_back(*nEv); + } + return stats; +} + ///////////////////////////////////////////// /// \brief A method responsible to import a list of TRestDataSet into fDataSet /// From 8ef39217a33e5ebed2ff18bfb4ff335e3a89f6e2 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 21 Jul 2023 11:12:48 +0200 Subject: [PATCH 019/174] TRestDataSet. fColumnNameExpressions added to operator= --- source/framework/core/inc/TRestDataSet.h | 3 ++- source/framework/core/src/TRestDataSet.cxx | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 3501b432c..a5077f1d1 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -103,7 +103,7 @@ class TRestDataSet : public TRestMetadata { std::vector fImportedFiles; //< /// A list of new columns together with its corresponding expressions added to the dataset - std::vector> fColumnNameExpressions; + std::vector> fColumnNameExpressions; //< /// The resulting RDF::RNode object after initialization ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0); //! @@ -161,6 +161,7 @@ class TRestDataSet : public TRestMetadata { inline auto GetFilterLowerThan() const { return fFilterLowerThan; } inline auto GetFilterEqualsTo() const { return fFilterEqualsTo; } inline auto GetQuantity() const { return fQuantity; } + inline auto GetAddedColumns() const { return fColumnNameExpressions; } inline auto GetCut() const { return fCut; } inline auto IsMergedDataSet() const { return fMergedDataset; } diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 7d05756f4..e5529965c 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -607,8 +607,11 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto& [cName, cExpression] : fColumnNameExpressions) - RESTMetadata << " - Name : " << cName << " Expression: " << cExpression << RESTendl; + for (const auto& [cName, cExpression] : fColumnNameExpressions) { + RESTMetadata << " - Name : " << cName << RESTendl; + RESTMetadata << " - Expression: " << cExpression << RESTendl; + RESTMetadata << " " << RESTendl; + } } if (fMergedDataset) { @@ -878,6 +881,7 @@ TRestDataSet& TRestDataSet::operator=(TRestDataSet& dS) { fFilterLowerThan = dS.GetFilterLowerThan(); fFilterEqualsTo = dS.GetFilterEqualsTo(); fQuantity = dS.GetQuantity(); + fColumnNameExpressions = dS.GetAddedColumns(); fTotalDuration = dS.GetTotalTimeInSeconds(); fFileSelection = dS.GetFileSelection(); fCut = dS.GetCut(); From 89e6c04eb8c139aeb0a84654c03d4106a375aab9 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 21 Jul 2023 13:31:12 +0200 Subject: [PATCH 020/174] TRestComponent. Removing comments --- source/framework/sensitivity/src/TRestComponent.cxx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 7fe98250a..1198bc6fa 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -134,17 +134,6 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " - Parametric nodes : " << fParameterizationNodes.size() << RESTendl; RESTMetadata << " " << RESTendl; RESTMetadata << " Use : this->PrintStatistics() for additional info" << RESTendl; - /* - RESTMetadata << " Values : "; - int n = 0; - for (const auto& node : fParameterizationNodes) { - if ( n == 0 ) RESTMetadata << node; - else RESTMetadata << ", " << node; - n++; - if( n % 10 == 0 ) RESTMetadata << RESTendl; - } - RESTMetadata << RESTendl; - */ } RESTMetadata << "----" << RESTendl; From 0fe4b8956c6899e4d1a2c13c7bfc34615a717ffe Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 23 Jul 2023 18:53:13 +0200 Subject: [PATCH 021/174] TRestComponent. Added THnSparse initialization --- .../sensitivity/inc/TRestComponent.h | 5 ++ .../sensitivity/src/TRestComponent.cxx | 69 ++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index ae76ca0b2..fab00fb91 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,6 +23,7 @@ #ifndef REST_TRestComponent #define REST_TRestComponent +#include #include "TRestDataSet.h" #include "TRestMetadata.h" @@ -61,6 +62,9 @@ class TRestComponent : public TRestMetadata { /// The filename of the dataset used std::vector fDataSetFileNames; //< + /// The generated N-dimensional PDF + std::map fPDFs; //< + /// The dataset used to initialize the distribution TRestDataSet fDataSet; //! @@ -85,6 +89,7 @@ class TRestComponent : public TRestMetadata { std::vector ExtractParameterizationNodes(); std::vector ExtractNodeStatistics(); + void InitializeSparseHistograms(); Bool_t VariablesOk(); Bool_t WeightsOk(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 1198bc6fa..441b3a910 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -189,6 +189,7 @@ void TRestComponent::InitFromConfigFile() { fDataSetLoaded = LoadDataSets(); fParameterizationNodes = ExtractParameterizationNodes(); + InitializeSparseHistograms(); } else { RESTWarning << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" @@ -196,7 +197,63 @@ void TRestComponent::InitFromConfigFile() { } } +///////////////////////////////////////////// +/// \brief +/// +void TRestComponent::InitializeSparseHistograms() { + // fNodeStatistics = ExtractNodeStatistics(); + + RESTInfo << "Initializing Sparse histograms" << RESTendl; + for (const auto& node : fParameterizationNodes) { + std::string filter = fParameter + " == " + DoubleToString(node); + RESTInfo << "Creating THnSparse for parameter " << fParameter << ": " << DoubleToString(node) + << RESTendl; + ROOT::RDF::RNode df = fDataSet.GetDataFrame().Filter(filter); + + Int_t* bins = new Int_t[fNbins.size()]; + Double_t* xmin = new Double_t[fNbins.size()]; + Double_t* xmax = new Double_t[fNbins.size()]; + + for (size_t n = 0; n < fNbins.size(); n++) { + bins[n] = fNbins[n]; + xmin[n] = fRanges[n].X(); + xmax[n] = fRanges[n].Y(); + } + + TString hName = fParameter + "_" + DoubleToString(node); + THnSparseD* sparse = new THnSparseD(hName, hName, fNbins.size(), bins, xmin, xmax); + + std::vector > data; + for (const auto& v : fVariables) { + auto parValues = fDataSet.GetDataFrame().Take(v); + data.push_back(*parValues); + } + + Double_t* values = new Double_t[fVariables.size()]; + if (!data.empty()) + for (size_t m = 0; m < data[0].size(); m++) + for (size_t v = 0; v < fVariables.size(); v++) { + values[v] = data[v][m]; + sparse->Fill(values); + } + delete[] values; + } + + if (fParameterizationNodes.empty()) { + std::cout << "We use the full dataset" << std::endl; + } +} + +///////////////////////////////////////////// +/// \brief It returns a vector with all the different values found on +/// the dataset column for the user given parameterization variable. +/// +/// If fParameterizationNodes has already been initialized it will +/// directly return its value. +/// std::vector TRestComponent::ExtractParameterizationNodes() { + if (!fParameterizationNodes.empty()) return fParameterizationNodes; + auto parValues = fDataSet.GetDataFrame().Take(fParameter); std::vector vs; for (const auto v : parValues) vs.push_back(v); @@ -211,10 +268,18 @@ std::vector TRestComponent::ExtractParameterizationNodes() { return vs; } +///////////////////////////////////////////// +/// \brief It returns a vector with the number of entries found for each +/// parameterization node. +/// +/// If fNodeStatistics has already been initialized it will +/// directly return its value. +/// std::vector TRestComponent::ExtractNodeStatistics() { if (!fNodeStatistics.empty()) return fNodeStatistics; - std::cout << "Counting statistics for each node ..." << std::endl; + RESTInfo << "Counting statistics for each node ..." << RESTendl; + RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; std::vector stats; for (const auto& p : fParameterizationNodes) { std::string filter = fParameter + " == " + DoubleToString(p); @@ -226,6 +291,8 @@ std::vector TRestComponent::ExtractNodeStatistics() { ///////////////////////////////////////////// /// \brief A method responsible to import a list of TRestDataSet into fDataSet +/// and check that the variables and weights defined by the user can be found +/// inside the dataset. /// Bool_t TRestComponent::LoadDataSets() { std::vector fullFileNames; From 4345360141c923f179fccfe3a48d3d53c2ba4884 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 23 Jul 2023 16:53:36 +0000 Subject: [PATCH 022/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index fab00fb91..b4d07a6a3 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -24,6 +24,7 @@ #define REST_TRestComponent #include + #include "TRestDataSet.h" #include "TRestMetadata.h" From f9c804275f198b348ba951a48680b6fdb6d2736d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 24 Jul 2023 07:33:43 +0200 Subject: [PATCH 023/174] restRoot. Increasing restRoot verbose level to info --- source/bin/restRoot.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index e59bfb97d..b78d377b8 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -25,6 +25,10 @@ int main(int argc, char* argv[]) { // set the env and debug status setenv("REST_VERSION", REST_RELEASE, 1); + printf("Setting verbose level to info. You may change level using `restRoot -v N`.\n"); + printf("Use `restRoot --help` for additional info.\n"); + gVerbose = StringToVerboseLevel("2"); + Bool_t loadMacros = false; for (int i = 1; i < argc; i++) { char* c = &argv[i][0]; From 027e077940252eb4d6976b7b84477a94802a6c74 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 24 Jul 2023 11:38:07 +0200 Subject: [PATCH 024/174] TRestComponent. Fixing sparse generation and adding GetHistogram methgods --- .../sensitivity/inc/TRestComponent.h | 22 ++- .../sensitivity/src/TRestComponent.cxx | 126 ++++++++++++++++-- 2 files changed, 133 insertions(+), 15 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index b4d07a6a3..7879b74a4 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -63,14 +63,17 @@ class TRestComponent : public TRestMetadata { /// The filename of the dataset used std::vector fDataSetFileNames; //< - /// The generated N-dimensional PDF - std::map fPDFs; //< + /// The generated N-dimensional variable space density for a given node + std::vector fNodeDensity; //< + + /// The generated N-dimensional variable space density for the complete dataset + THnSparse* fTotalDensity = nullptr; //< /// The dataset used to initialize the distribution TRestDataSet fDataSet; //! /// It is true of the dataset was loaded without issues - Bool_t fDataSetLoaded = false; + Bool_t fDataSetLoaded = false; //! ////////// ////////// This should be implemented in TRestDataSetComponent @@ -88,14 +91,16 @@ class TRestComponent : public TRestMetadata { /// A pointer to the component distribution // THnD* fDistribution = nullptr; //! + protected: std::vector ExtractParameterizationNodes(); std::vector ExtractNodeStatistics(); - void InitializeSparseHistograms(); + void GenerateSparseHistograms(); Bool_t VariablesOk(); Bool_t WeightsOk(); - protected: + Int_t GetVariableIndex(std::string varName); + void InitFromConfigFile() override; public: @@ -106,9 +111,16 @@ class TRestComponent : public TRestMetadata { Double_t GetRate(std::vector point); + THnSparse* GetDensityForNode(Double_t value); + + TH1D* GetHistogram(Double_t node, std::string varName); + TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); + TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); + void PrintMetadata() override; void PrintStatistics(); + void PrintNodes(); void Initialize() override; TRestComponent(const char* configFilename); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 441b3a910..50584d0cf 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -42,6 +42,8 @@ #include "TKey.h" +#include + ClassImp(TRestComponent); /////////////////////////////////////////////// @@ -139,12 +141,16 @@ void TRestComponent::PrintMetadata() { RESTMetadata << "----" << RESTendl; } +///////////////////////////////////////////// +/// \brief It prints out the statistics available for each parametric node +/// void TRestComponent::PrintStatistics() { - auto nEv = fDataSet.GetDataFrame().Count(); - std::cout << "Total counts : " << *nEv << std::endl; + if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); + + auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); + std::cout << "Total counts : " << result << std::endl; std::cout << std::endl; - fNodeStatistics = ExtractNodeStatistics(); std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; int n = 0; for (const auto& p : fParameterizationNodes) { @@ -153,6 +159,15 @@ void TRestComponent::PrintStatistics() { } } +///////////////////////////////////////////// +/// \brief It prints out on screen the values of the parametric node +/// +void TRestComponent::PrintNodes() { + std::cout << " - Number of nodes : " << fParameterizationNodes.size() << std::endl; + for (const auto& x : fParameterizationNodes) std::cout << x; + std::cout << std::endl; +} + ///////////////////////////////////////////// /// \brief It customizes the retrieval of XML data values of this class /// @@ -189,7 +204,7 @@ void TRestComponent::InitFromConfigFile() { fDataSetLoaded = LoadDataSets(); fParameterizationNodes = ExtractParameterizationNodes(); - InitializeSparseHistograms(); + GenerateSparseHistograms(); } else { RESTWarning << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" @@ -200,10 +215,16 @@ void TRestComponent::InitFromConfigFile() { ///////////////////////////////////////////// /// \brief /// -void TRestComponent::InitializeSparseHistograms() { - // fNodeStatistics = ExtractNodeStatistics(); +void TRestComponent::GenerateSparseHistograms() { + fNodeStatistics = ExtractNodeStatistics(); - RESTInfo << "Initializing Sparse histograms" << RESTendl; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponent::GenerateSparseHistograms. Dataset has not been initialized!" + << RESTendl; + return; + } + + RESTInfo << "Generating Sparse histograms" << RESTendl; for (const auto& node : fParameterizationNodes) { std::string filter = fParameter + " == " + DoubleToString(node); RESTInfo << "Creating THnSparse for parameter " << fParameter << ": " << DoubleToString(node) @@ -225,7 +246,7 @@ void TRestComponent::InitializeSparseHistograms() { std::vector > data; for (const auto& v : fVariables) { - auto parValues = fDataSet.GetDataFrame().Take(v); + auto parValues = df.Take(v); data.push_back(*parValues); } @@ -237,11 +258,85 @@ void TRestComponent::InitializeSparseHistograms() { sparse->Fill(values); } delete[] values; + + fNodeDensity.push_back(sparse); } if (fParameterizationNodes.empty()) { std::cout << "We use the full dataset" << std::endl; + std::cout << "Not implemented yet!" << std::endl; + } +} + +///////////////////////////////////////////// +/// \brief It returns the position of the fVariable element for +/// the variable name given by argument. +/// +Int_t TRestComponent::GetVariableIndex(std::string varName) { + int n = 0; + for (const auto& v : fVariables) { + if (v == varName) return n; + n++; + } + + return -1; +} + +///////////////////////////////////////////// +/// \brief +/// +THnSparse* TRestComponent::GetDensityForNode(Double_t node) { + int n = 0; + for (const auto& x : fParameterizationNodes) { + if (x == node) { + return fNodeDensity[n]; + } + n++; } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH1D* TRestComponent::GetHistogram(Double_t node, std::string varName) { + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 2-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH2D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 3-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH3D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2, + std::string varName3) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); + + return nullptr; } ///////////////////////////////////////////// @@ -254,8 +349,14 @@ void TRestComponent::InitializeSparseHistograms() { std::vector TRestComponent::ExtractParameterizationNodes() { if (!fParameterizationNodes.empty()) return fParameterizationNodes; - auto parValues = fDataSet.GetDataFrame().Take(fParameter); std::vector vs; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponent::ExtractParameterizationNodes. Dataset has not been initialized!" + << RESTendl; + return vs; + } + + auto parValues = fDataSet.GetDataFrame().Take(fParameter); for (const auto v : parValues) vs.push_back(v); std::vector::iterator ip; @@ -278,9 +379,14 @@ std::vector TRestComponent::ExtractParameterizationNodes() { std::vector TRestComponent::ExtractNodeStatistics() { if (!fNodeStatistics.empty()) return fNodeStatistics; + std::vector stats; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponent::ExtractNodeStatistics. Dataset has not been initialized!" << RESTendl; + return stats; + } + RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; - std::vector stats; for (const auto& p : fParameterizationNodes) { std::string filter = fParameter + " == " + DoubleToString(p); auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); From eda8da31a157ecdcdb9b224e0e473f96177d38e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:39:53 +0000 Subject: [PATCH 025/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestComponent.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 50584d0cf..741f951df 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -40,10 +40,10 @@ /// #include "TRestComponent.h" -#include "TKey.h" - #include +#include "TKey.h" + ClassImp(TRestComponent); /////////////////////////////////////////////// From f9ec39ff45e24faa522223c0dd60f3cb6f22979b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 29 Jul 2023 13:04:44 +0200 Subject: [PATCH 026/174] TRestDataSet. Adding an option to enable MT. Default is ST --- source/framework/core/inc/TRestDataSet.h | 4 ++++ source/framework/core/src/TRestDataSet.cxx | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index a5077f1d1..4bf5e1226 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -105,6 +105,10 @@ class TRestDataSet : public TRestMetadata { /// A list of new columns together with its corresponding expressions added to the dataset std::vector> fColumnNameExpressions; //< + /// A flag to enable Multithreading during dataframe generation + Bool_t fMT = false; //< + + inline auto GetAddedColumns() const { return fColumnNameExpressions; } /// The resulting RDF::RNode object after initialization ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0); //! diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index e1174146a..9eb5e790c 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -283,8 +283,6 @@ TRestDataSet::TRestDataSet() { Initialize(); } /// TRestDataSet::TRestDataSet(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); - - if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); } /////////////////////////////////////////////// @@ -343,7 +341,7 @@ void TRestDataSet::GenerateDataSet() { std::sort(finalList.begin(), finalList.end()); finalList.erase(std::unique(finalList.begin(), finalList.end()), finalList.end()); - ROOT::EnableImplicitMT(); + if (fMT) ROOT::EnableImplicitMT(); RESTInfo << "Initializing dataset" << RESTendl; fDataSet = ROOT::RDataFrame("AnalysisTree", fFileSelection); @@ -641,6 +639,12 @@ void TRestDataSet::PrintMetadata() { for (const auto& fn : fImportedFiles) RESTMetadata << " - " << fn << RESTendl; } + RESTMetadata << " " << RESTendl; + if (fMT) + RESTMetadata << " - Multithreading was enabled" << RESTendl; + else + RESTMetadata << " - Multithreading was NOT enabled" << RESTendl; + RESTMetadata << "----" << RESTendl; } @@ -925,8 +929,6 @@ void TRestDataSet::Import(const std::string& fileName) { if (REST_Reflection::GetClassQuick(kName.c_str()) != nullptr && REST_Reflection::GetClassQuick(kName.c_str())->InheritsFrom("TRestDataSet")) { dS = file->Get(key->GetName()); - if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) - dS->PrintMetadata(); *this = *dS; } } From fb8c542ce229344a76e28ad429745979015df4ca Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 29 Jul 2023 16:31:09 +0200 Subject: [PATCH 027/174] TRestComponent. Introducing Histograms and active node --- source/framework/core/inc/TRestDataSet.h | 1 - .../sensitivity/inc/TRestComponent.h | 18 +- .../sensitivity/src/TRestComponent.cxx | 171 ++++++++++++++---- 3 files changed, 152 insertions(+), 38 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 4bf5e1226..fc24b7df5 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -108,7 +108,6 @@ class TRestDataSet : public TRestMetadata { /// A flag to enable Multithreading during dataframe generation Bool_t fMT = false; //< - inline auto GetAddedColumns() const { return fColumnNameExpressions; } /// The resulting RDF::RNode object after initialization ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0); //! diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 7879b74a4..ef40e7f32 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,7 +23,7 @@ #ifndef REST_TRestComponent #define REST_TRestComponent -#include +#include #include "TRestDataSet.h" #include "TRestMetadata.h" @@ -64,10 +64,13 @@ class TRestComponent : public TRestMetadata { std::vector fDataSetFileNames; //< /// The generated N-dimensional variable space density for a given node - std::vector fNodeDensity; //< + std::vector fNodeDensity; //< + + /// Methods will return the density of the active node + Int_t fActiveNode = -1; //< /// The generated N-dimensional variable space density for the complete dataset - THnSparse* fTotalDensity = nullptr; //< + // THnD* fTotalDensity = nullptr; //< /// The dataset used to initialize the distribution TRestDataSet fDataSet; //! @@ -111,12 +114,19 @@ class TRestComponent : public TRestMetadata { Double_t GetRate(std::vector point); - THnSparse* GetDensityForNode(Double_t value); + Int_t SetActiveNode(Double_t node); + + THnD* GetDensityForNode(Double_t value); + THnD* GetDensityForActiveNode(); TH1D* GetHistogram(Double_t node, std::string varName); TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); + TH1D* GetHistogram(std::string varName); + TH2D* GetHistogram(std::string varName1, std::string varName2); + TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); + void PrintMetadata() override; void PrintStatistics(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 741f951df..542bea6b1 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -73,9 +73,8 @@ TRestComponent::TRestComponent(const char* configFilename) : TRestMetadata(confi /// TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { + Initialize(); LoadConfigFromFile(fConfigFileName, name); - - if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); } /////////////////////////////////////////////// @@ -98,8 +97,7 @@ void TRestComponent::Initialize() { SetSectionName(this->ClassName()); } /// of the distribution. /// Double_t TRestComponent::GetRate(std::vector point) { - // we get the rate in seconds at the corresponding bin - return 0.0; + return GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); } ///////////////////////////////////////////// @@ -133,9 +131,9 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " === Parameterization === " << RESTendl; RESTMetadata << "- Parameter : " << fParameter << RESTendl; - RESTMetadata << " - Parametric nodes : " << fParameterizationNodes.size() << RESTendl; + RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl; RESTMetadata << " " << RESTendl; - RESTMetadata << " Use : this->PrintStatistics() for additional info" << RESTendl; + RESTMetadata << " Use : PrintStatistics() or PrintNodes() for additional info" << RESTendl; } RESTMetadata << "----" << RESTendl; @@ -164,7 +162,7 @@ void TRestComponent::PrintStatistics() { /// void TRestComponent::PrintNodes() { std::cout << " - Number of nodes : " << fParameterizationNodes.size() << std::endl; - for (const auto& x : fParameterizationNodes) std::cout << x; + for (const auto& x : fParameterizationNodes) std::cout << x << " "; std::cout << std::endl; } @@ -198,18 +196,6 @@ void TRestComponent::InitFromConfigFile() { fDataSetFileNames.push_back(GetParameter("filename", ele, "")); ele = GetNextElement(ele); } - - if (!fDataSetFileNames.empty()) { - RESTInfo << "Loading datasets" << RESTendl; - fDataSetLoaded = LoadDataSets(); - - fParameterizationNodes = ExtractParameterizationNodes(); - GenerateSparseHistograms(); - } else { - RESTWarning - << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" - << RESTendl; - } } ///////////////////////////////////////////// @@ -224,12 +210,36 @@ void TRestComponent::GenerateSparseHistograms() { return; } + if (fParameterizationNodes.empty()) { + RESTWarning << "Nodes have not been defined" << RESTendl; + RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; + fParameterizationNodes.push_back(-137); + } + RESTInfo << "Generating Sparse histograms" << RESTendl; + int nIndex = 0; for (const auto& node : fParameterizationNodes) { - std::string filter = fParameter + " == " + DoubleToString(node); - RESTInfo << "Creating THnSparse for parameter " << fParameter << ": " << DoubleToString(node) - << RESTendl; - ROOT::RDF::RNode df = fDataSet.GetDataFrame().Filter(filter); + ROOT::RDF::RNode df = ROOT::RDataFrame(0); + if (fParameterizationNodes.size() == 1 && node == -137) { + RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; + df = fDataSet.GetDataFrame(); + fParameterizationNodes.clear(); + } else { + RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) + << RESTendl; + std::string filter = fParameter + " == " + DoubleToString(node); + df = fDataSet.GetDataFrame(); + // df = fDataSet.GetDataFrame().Filter(filter); + } + + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + auto wValues = df.Define("componentWeight", weightsStr).Take("componentWeight"); + std::vector weightValues = *wValues; Int_t* bins = new Int_t[fNbins.size()]; Double_t* xmin = new Double_t[fNbins.size()]; @@ -242,7 +252,10 @@ void TRestComponent::GenerateSparseHistograms() { } TString hName = fParameter + "_" + DoubleToString(node); - THnSparseD* sparse = new THnSparseD(hName, hName, fNbins.size(), bins, xmin, xmax); + + /// It is named sparse because I tried to use first THnSparse. + /// See root-forum post: https://root-forum.cern.ch/t/problems-using-thnsparse-projection/55829/3 + THnD* sparse = new THnD(hName, hName, fNbins.size(), bins, xmin, xmax); std::vector > data; for (const auto& v : fVariables) { @@ -251,20 +264,19 @@ void TRestComponent::GenerateSparseHistograms() { } Double_t* values = new Double_t[fVariables.size()]; + std::cout << "N-values filled : " << data[0].size() << std::endl; if (!data.empty()) - for (size_t m = 0; m < data[0].size(); m++) + for (size_t m = 0; m < data[0].size(); m++) { for (size_t v = 0; v < fVariables.size(); v++) { values[v] = data[v][m]; - sparse->Fill(values); } + sparse->Fill(values); // weightValues[m]/fNodeStatistics[nIndex]); + } delete[] values; fNodeDensity.push_back(sparse); - } - - if (fParameterizationNodes.empty()) { - std::cout << "We use the full dataset" << std::endl; - std::cout << "Not implemented yet!" << std::endl; + fActiveNode = nIndex; + nIndex++; } } @@ -282,10 +294,31 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { return -1; } +///////////////////////////////////////////// +/// \brief It returns the position of the fParameterizationNodes +/// element for the variable name given by argument. +/// +Int_t TRestComponent::SetActiveNode(Double_t node) { + int n = 0; + for (const auto& v : fParameterizationNodes) { + if (v == node) { + fActiveNode = n; + return fActiveNode; + } + n++; + } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + RESTError << "Keeping previous node as active : " << fParameterizationNodes[fActiveNode] << RESTendl; + PrintNodes(); + + return fActiveNode; +} + ///////////////////////////////////////////// /// \brief /// -THnSparse* TRestComponent::GetDensityForNode(Double_t node) { +THnD* TRestComponent::GetDensityForNode(Double_t node) { int n = 0; for (const auto& x : fParameterizationNodes) { if (x == node) { @@ -299,6 +332,17 @@ THnSparse* TRestComponent::GetDensityForNode(Double_t node) { return nullptr; } +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponent::GetDensityForActiveNode() { + if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; + + RESTError << "The active node is invalid" << RESTendl; + PrintNodes(); + return nullptr; +} + ///////////////////////////////////////////// /// \brief It returns a 1-dimensional projected histogram for the variable names /// provided in the argument @@ -311,6 +355,19 @@ TH1D* TRestComponent::GetHistogram(Double_t node, std::string varName) { return nullptr; } +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH1D* TRestComponent::GetHistogram(std::string varName) { + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); + + return nullptr; +} + ///////////////////////////////////////////// /// \brief It returns the 2-dimensional projected histogram for the variable names /// provided in the argument @@ -324,6 +381,21 @@ TH2D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::str return nullptr; } +///////////////////////////////////////////// +/// \brief It returns a 2-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH2D* TRestComponent::GetHistogram(std::string varName1, std::string varName2) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); + + return nullptr; +} + ///////////////////////////////////////////// /// \brief It returns the 3-dimensional projected histogram for the variable names /// provided in the argument @@ -339,6 +411,22 @@ TH3D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::str return nullptr; } +///////////////////////////////////////////// +/// \brief It returns a 3-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH3D* TRestComponent::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); + + return nullptr; +} + ///////////////////////////////////////////// /// \brief It returns a vector with all the different values found on /// the dataset column for the user given parameterization variable. @@ -401,6 +489,16 @@ std::vector TRestComponent::ExtractNodeStatistics() { /// inside the dataset. /// Bool_t TRestComponent::LoadDataSets() { + if (fDataSetFileNames.empty()) { + RESTWarning + << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" + << RESTendl; + fDataSetLoaded = false; + return fDataSetLoaded; + } + + RESTInfo << "Loading datasets" << RESTendl; + std::vector fullFileNames; for (const auto& name : fDataSetFileNames) { std::string fileName = SearchFile(name); @@ -414,6 +512,7 @@ Bool_t TRestComponent::LoadDataSets() { } fDataSet.Import(fullFileNames); + fDataSetLoaded = true; if (fDataSet.GetTree() == nullptr) { RESTError << "Problem loading dataset from file list :" << RESTendl; @@ -423,7 +522,13 @@ Bool_t TRestComponent::LoadDataSets() { if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); - return (VariablesOk() && WeightsOk()); + if (VariablesOk() && WeightsOk()) { + fParameterizationNodes = ExtractParameterizationNodes(); + GenerateSparseHistograms(); + return fDataSetLoaded; + } + + return fDataSetLoaded; } ///////////////////////////////////////////// From c3fb4dbd6e766254bdb4d8e88db9b43af4057861 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 8 Aug 2023 18:44:30 +0200 Subject: [PATCH 028/174] TRestComponent::FillHistograms finally implemented --- .../sensitivity/inc/TRestComponent.h | 2 +- .../sensitivity/src/TRestComponent.cxx | 58 ++++++++----------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index ef40e7f32..54902c9e5 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -97,7 +97,7 @@ class TRestComponent : public TRestMetadata { protected: std::vector ExtractParameterizationNodes(); std::vector ExtractNodeStatistics(); - void GenerateSparseHistograms(); + void FillHistograms(); Bool_t VariablesOk(); Bool_t WeightsOk(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 542bea6b1..83f20d8d3 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -145,6 +145,12 @@ void TRestComponent::PrintMetadata() { void TRestComponent::PrintStatistics() { if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); + if (!IsDataSetLoaded()) { + RESTWarning << "TRestComponent::PrintStatistics. No dataset loaded." << RESTendl; + RESTWarning << "Invoking TRestComponent::LoadDataSets might solve the problem" << RESTendl; + return; + } + auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); std::cout << "Total counts : " << result << std::endl; std::cout << std::endl; @@ -199,14 +205,14 @@ void TRestComponent::InitFromConfigFile() { } ///////////////////////////////////////////// -/// \brief +/// \brief It will produce a histogram with the distribution defined using the +/// variables and the weights for each of the parameter nodes. /// -void TRestComponent::GenerateSparseHistograms() { +void TRestComponent::FillHistograms() { fNodeStatistics = ExtractNodeStatistics(); if (!IsDataSetLoaded()) { - RESTError << "TRestComponent::GenerateSparseHistograms. Dataset has not been initialized!" - << RESTendl; + RESTError << "TRestComponent::FillHistograms. Dataset has not been initialized!" << RESTendl; return; } @@ -228,18 +234,8 @@ void TRestComponent::GenerateSparseHistograms() { RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) << RESTendl; std::string filter = fParameter + " == " + DoubleToString(node); - df = fDataSet.GetDataFrame(); - // df = fDataSet.GetDataFrame().Filter(filter); - } - - std::string weightsStr = ""; - for (size_t n = 0; n < fWeights.size(); n++) { - if (n > 0) weightsStr += "*"; - - weightsStr += fWeights[n]; + df = fDataSet.GetDataFrame().Filter(filter); } - auto wValues = df.Define("componentWeight", weightsStr).Take("componentWeight"); - std::vector weightValues = *wValues; Int_t* bins = new Int_t[fNbins.size()]; Double_t* xmin = new Double_t[fNbins.size()]; @@ -253,28 +249,22 @@ void TRestComponent::GenerateSparseHistograms() { TString hName = fParameter + "_" + DoubleToString(node); - /// It is named sparse because I tried to use first THnSparse. - /// See root-forum post: https://root-forum.cern.ch/t/problems-using-thnsparse-projection/55829/3 - THnD* sparse = new THnD(hName, hName, fNbins.size(), bins, xmin, xmax); + std::vector varsAndWeight = fVariables; + if (!fWeights.empty()) { + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; - std::vector > data; - for (const auto& v : fVariables) { - auto parValues = df.Take(v); - data.push_back(*parValues); + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); } - Double_t* values = new Double_t[fVariables.size()]; - std::cout << "N-values filled : " << data[0].size() << std::endl; - if (!data.empty()) - for (size_t m = 0; m < data[0].size(); m++) { - for (size_t v = 0; v < fVariables.size(); v++) { - values[v] = data[v][m]; - } - sparse->Fill(values); // weightValues[m]/fNodeStatistics[nIndex]); - } - delete[] values; + auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); + THnD* hNd = new THnD(*hn); - fNodeDensity.push_back(sparse); + fNodeDensity.push_back(hNd); fActiveNode = nIndex; nIndex++; } @@ -524,7 +514,7 @@ Bool_t TRestComponent::LoadDataSets() { if (VariablesOk() && WeightsOk()) { fParameterizationNodes = ExtractParameterizationNodes(); - GenerateSparseHistograms(); + FillHistograms(); return fDataSetLoaded; } From 7d709e78476ef467e32cfc29b7a1f64a000fedb3 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 9 Aug 2023 06:46:19 +0200 Subject: [PATCH 029/174] TRestDataSet. Increasing class version --- source/framework/core/inc/TRestDataSet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index fc24b7df5..2b518da33 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -190,6 +190,6 @@ class TRestDataSet : public TRestMetadata { TRestDataSet(const char* cfgFileName, const std::string& name = ""); ~TRestDataSet(); - ClassDefOverride(TRestDataSet, 5); + ClassDefOverride(TRestDataSet, 6); }; #endif From 8d8460694ab36471b86611b04958821de63778a6 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 8 Sep 2023 09:51:41 +0200 Subject: [PATCH 030/174] Solving conflicts --- source/libraries/axion | 2 +- source/libraries/connectors | 2 +- source/libraries/detector | 2 +- source/libraries/geant4 | 2 +- source/libraries/raw | 2 +- source/libraries/track | 2 +- source/packages/restG4 | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/libraries/axion b/source/libraries/axion index 19cf96f69..d36acca17 160000 --- a/source/libraries/axion +++ b/source/libraries/axion @@ -1 +1 @@ -Subproject commit 19cf96f69416fc517c7350ded9678471242fad2d +Subproject commit d36acca177d665e2425c3c3afc8412bd06cc343d diff --git a/source/libraries/connectors b/source/libraries/connectors index f1cb2ad8a..4053c789e 160000 --- a/source/libraries/connectors +++ b/source/libraries/connectors @@ -1 +1 @@ -Subproject commit f1cb2ad8a63eb2469e2371d812973b22c598dead +Subproject commit 4053c789e53be296d934310c7d7631b3347236dd diff --git a/source/libraries/detector b/source/libraries/detector index 11822a7d2..53b99225e 160000 --- a/source/libraries/detector +++ b/source/libraries/detector @@ -1 +1 @@ -Subproject commit 11822a7d2168d3f4f7094eb04b7cfbcc95b4b06a +Subproject commit 53b99225ee31bcfe0b8d119a182a148b02e9ce4b diff --git a/source/libraries/geant4 b/source/libraries/geant4 index 8a10aa171..ed8266d5e 160000 --- a/source/libraries/geant4 +++ b/source/libraries/geant4 @@ -1 +1 @@ -Subproject commit 8a10aa171931c63befd246fe32e2c945bf914e56 +Subproject commit ed8266d5eb265ea5e7c01b6c95fac5f5a747bf30 diff --git a/source/libraries/raw b/source/libraries/raw index 608d4f327..952c92d85 160000 --- a/source/libraries/raw +++ b/source/libraries/raw @@ -1 +1 @@ -Subproject commit 608d4f3272650119199985eaf3cd197e284d58e5 +Subproject commit 952c92d85d8d6fd47f3aeedc4357032ce3ab8ee8 diff --git a/source/libraries/track b/source/libraries/track index 59e023fb4..b7f0e27fa 160000 --- a/source/libraries/track +++ b/source/libraries/track @@ -1 +1 @@ -Subproject commit 59e023fb4f12eea37a36088bb56dc89222f8c93e +Subproject commit b7f0e27fa1e71ed81d7f96ff2566a98b7fa55d30 diff --git a/source/packages/restG4 b/source/packages/restG4 index ca5ef72d4..126bebc62 160000 --- a/source/packages/restG4 +++ b/source/packages/restG4 @@ -1 +1 @@ -Subproject commit ca5ef72d46f164eabe69c41883d57027bd5b91a2 +Subproject commit 126bebc62f2f5fd925f7e41ce2ec7dc4278af5ae From 05d77ebfc50f00143460f112c752943c5b200626 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 3 Oct 2023 08:34:37 +0200 Subject: [PATCH 031/174] TRestComponent. Adding a total rate method --- .../sensitivity/inc/TRestComponent.h | 1 + .../sensitivity/src/TRestComponent.cxx | 36 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 54902c9e5..d3f39d0f4 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -113,6 +113,7 @@ class TRestComponent : public TRestMetadata { Bool_t IsDataSetLoaded() { return fDataSetLoaded; } Double_t GetRate(std::vector point); + Double_t GetTotalRate(); Int_t SetActiveNode(Double_t node); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 83f20d8d3..06adef7df 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -93,11 +93,43 @@ void TRestComponent::Initialize() { SetSectionName(this->ClassName()); } /// generated distribution or formula evaluated at the position of the parameter /// space given by point. /// +/// The density should be normalized to the corresponding parameter space. During +/// the component construction, **the user is responsible** to initialize the component +/// with the appropriate units. For example, if the parameter space is 2 spatial +/// dimensions and 1 energy dimension, the contribution of each cell or event to +/// the component will be expressed in mm-2 keV-1 which are the default units for +/// distance and energy. +/// /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. /// Double_t TRestComponent::GetRate(std::vector point) { - return GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); + Double_t density = + GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); + + Double_t norm = 1; + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return norm * density; +} + +/////////////////////////////////////////////// +/// \brief This method integrates the rate to all the parameter space defined in the density function. +/// The result will be returned in s-1. +/// +Double_t TRestComponent::GetTotalRate() { + THnD* dHist = GetDensityForActiveNode(); + + Double_t integral = 0; + if (dHist != nullptr) integral = dHist->ComputeIntegral(); + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) + integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return integral; } ///////////////////////////////////////////// @@ -222,7 +254,7 @@ void TRestComponent::FillHistograms() { fParameterizationNodes.push_back(-137); } - RESTInfo << "Generating Sparse histograms" << RESTendl; + RESTInfo << "Generating N-dim histograms" << RESTendl; int nIndex = 0; for (const auto& node : fParameterizationNodes) { ROOT::RDF::RNode df = ROOT::RDataFrame(0); From 68afa2818fe5b7d2c39c05231a3182f09861c902 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 3 Oct 2023 08:38:22 +0200 Subject: [PATCH 032/174] startup. StringToVector will skip empty strings --- source/framework/core/src/startup.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/core/src/startup.cpp b/source/framework/core/src/startup.cpp index 426698f2c..c236ad47f 100644 --- a/source/framework/core/src/startup.cpp +++ b/source/framework/core/src/startup.cpp @@ -272,6 +272,9 @@ string VectorToString(vector vec) { template vector StringToVector(string vec) { vector result; + + if ( vec.empty() ) return result; + if (vec[0] == '{' && vec[vec.size() - 1] == '}') { vec.erase(vec.begin()); vec.erase(vec.end() - 1); @@ -289,6 +292,7 @@ vector StringToVector(string vec) { } else { cout << "Startup. StringToVector. Illegal format!" << endl; + cout << "The vector string is : " << vec << endl; cout << "A vector should be defined using brackets and comma separated elements: {a,b,c,d}" << endl; return vector{}; } From 11601634e3bbc1a68e5e2e81bc73b579ef17f63f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 06:39:03 +0000 Subject: [PATCH 033/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/src/startup.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/core/src/startup.cpp b/source/framework/core/src/startup.cpp index c236ad47f..5fc7cf5f2 100644 --- a/source/framework/core/src/startup.cpp +++ b/source/framework/core/src/startup.cpp @@ -273,7 +273,7 @@ template vector StringToVector(string vec) { vector result; - if ( vec.empty() ) return result; + if (vec.empty()) return result; if (vec[0] == '{' && vec[vec.size() - 1] == '}') { vec.erase(vec.begin()); @@ -292,7 +292,7 @@ vector StringToVector(string vec) { } else { cout << "Startup. StringToVector. Illegal format!" << endl; - cout << "The vector string is : " << vec << endl; + cout << "The vector string is : " << vec << endl; cout << "A vector should be defined using brackets and comma separated elements: {a,b,c,d}" << endl; return vector{}; } From 41605acb84d4dc68e7623373cfa80c62de186879 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 12 Oct 2023 10:54:07 +0200 Subject: [PATCH 034/174] restRoot. Avoiding arguments being interpreted as a file --- source/bin/restRoot.cxx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index b78d377b8..aeb1bf45e 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -83,9 +83,8 @@ int main(int argc, char* argv[]) { if (loadMacros) { if (!silent) printf("= Loading macros ...\n"); vector macroFiles; - const vector patterns = { - REST_PATH + "/macros/REST_*.C", // framework - REST_PATH + "/macros/*/REST_*.C" // libraries + const vector patterns = {REST_PATH + "/macros/REST_*.C", // framework + REST_PATH + "/macros/*/REST_*.C" // libraries }; for (const auto& pattern : patterns) { for (const auto& macroFile : TRestTools::GetFilesMatchingPattern(pattern)) { @@ -104,6 +103,10 @@ int main(int argc, char* argv[]) { for (int i = 1; i < argc; i++) { const string opt = (string)argv[i]; + if (opt.at(0) == ('-') && opt.length() > 1 && opt.at(1) == ('-')) { + i++; + continue; + } if (opt.at(0) == ('-')) continue; if (opt.find("http") == string::npos && !TRestTools::fileExists(opt)) { From 2bc4a5939d17170644211bf14da22e589e4471a0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:57:29 +0000 Subject: [PATCH 035/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/bin/restRoot.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index aeb1bf45e..17e2a1e0a 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -83,8 +83,9 @@ int main(int argc, char* argv[]) { if (loadMacros) { if (!silent) printf("= Loading macros ...\n"); vector macroFiles; - const vector patterns = {REST_PATH + "/macros/REST_*.C", // framework - REST_PATH + "/macros/*/REST_*.C" // libraries + const vector patterns = { + REST_PATH + "/macros/REST_*.C", // framework + REST_PATH + "/macros/*/REST_*.C" // libraries }; for (const auto& pattern : patterns) { for (const auto& macroFile : TRestTools::GetFilesMatchingPattern(pattern)) { From 0aad6921703a8ee4a4471bbefe01ca0c3fca3d8d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 8 Nov 2023 12:11:22 +0100 Subject: [PATCH 036/174] TRestDataSet::EnableMultiThreading added. Now restRoot imports with MT disabled --- macros/REST_OpenInputFile.C | 1 + source/framework/core/inc/TRestDataSet.h | 2 ++ source/framework/core/src/TRestDataSet.cxx | 30 ++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/macros/REST_OpenInputFile.C b/macros/REST_OpenInputFile.C index 1bf55e0a5..e6ca5bfc6 100644 --- a/macros/REST_OpenInputFile.C +++ b/macros/REST_OpenInputFile.C @@ -48,6 +48,7 @@ void REST_OpenInputFile(const std::string& fileName) { printf("%s\n\n", " - dSet->GetTree()->GetEntries()"); if (dSet) delete dSet; dSet = new TRestDataSet(); + dSet->EnableMultiThreading(false); dSet->Import(fileName); } else { printf("\n%s is not a valid TRestRun or TRestDataSet\n", fileName.c_str()); diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 2b518da33..a60c9eb12 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -128,6 +128,8 @@ class TRestDataSet : public TRestMetadata { void SetDataFrame(const ROOT::RDF::RNode& dS) { fDataSet = dS; } + void EnableMultiThreading(Bool_t enable = true) { fMT = enable; } + /// Gives access to the tree TTree* GetTree() const { if (fTree == nullptr) { diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 9eb5e790c..dedd5c7cd 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -341,7 +341,10 @@ void TRestDataSet::GenerateDataSet() { std::sort(finalList.begin(), finalList.end()); finalList.erase(std::unique(finalList.begin(), finalList.end()), finalList.end()); - if (fMT) ROOT::EnableImplicitMT(); + if (fMT) + ROOT::EnableImplicitMT(); + else + ROOT::DisableImplicitMT(); RESTInfo << "Initializing dataset" << RESTendl; fDataSet = ROOT::RDataFrame("AnalysisTree", fFileSelection); @@ -350,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -381,8 +384,7 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" - << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; return fFileSelection; } @@ -437,7 +439,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -494,7 +496,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto& [param, condition] : paramCut) { + for (const auto & [ param, condition ] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -541,7 +543,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const& [name, properties] : fQuantity) + for (auto const & [ name, properties ] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -607,7 +609,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const& [name, properties] : fQuantity) { + for (auto const & [ name, properties ] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -619,7 +621,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -792,8 +794,7 @@ void TRestDataSet::Export(const std::string& filename) { if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" - << RESTendl; + "exporting to ASCII table" << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -828,7 +829,7 @@ void TRestDataSet::Export(const std::string& filename) { } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } @@ -939,7 +940,10 @@ void TRestDataSet::Import(const std::string& fileName) { return; } - ROOT::EnableImplicitMT(); + if (fMT) + ROOT::EnableImplicitMT(); + else + ROOT::DisableImplicitMT(); fDataSet = ROOT::RDataFrame("AnalysisTree", fileName); From 247f79ef93f53b942971f476ca2755c29fb0a194 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 08:12:56 +0000 Subject: [PATCH 037/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/src/TRestDataSet.cxx | 20 ++++++----- source/framework/core/src/TRestMetadata.cxx | 22 ++++++++----- .../framework/core/src/TRestProcessRunner.cxx | 22 +++++++------ source/framework/core/src/TRestRun.cxx | 33 ++++++++++++------- source/framework/tools/inc/TRestTools.h | 10 ++---- source/framework/tools/src/TRestTools.cxx | 11 ++++--- 6 files changed, 69 insertions(+), 49 deletions(-) diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 7eb7a0549..192598ef6 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -353,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -384,7 +384,8 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" + << RESTendl; return fFileSelection; } @@ -439,7 +440,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -496,7 +497,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto & [ param, condition ] : paramCut) { + for (const auto& [param, condition] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -543,7 +544,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const & [ name, properties ] : fQuantity) + for (auto const& [name, properties] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -609,7 +610,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const & [ name, properties ] : fQuantity) { + for (auto const& [name, properties] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -621,7 +622,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -794,7 +795,8 @@ void TRestDataSet::Export(const std::string& filename) { if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" << RESTendl; + "exporting to ASCII table" + << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -829,7 +831,7 @@ void TRestDataSet::Export(const std::string& filename) { } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index a58497559..718f62daa 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -1146,9 +1146,10 @@ void TRestMetadata::ReplaceForLoopVars(TiXmlElement* e, map forL } } - e->SetAttribute(name, ReplaceMathematicalExpressions(outputBuffer, 0, - "Please, check parameter name: " + parName + - " (ReplaceForLoopVars)").c_str()); + e->SetAttribute(name, ReplaceMathematicalExpressions( + outputBuffer, 0, + "Please, check parameter name: " + parName + " (ReplaceForLoopVars)") + .c_str()); } attr = attr->Next(); @@ -1305,7 +1306,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { TiXmlElement* ele = GetElementFromFile(filename); if (ele == nullptr) { RESTError << "TRestMetadata::ExpandIncludeFile. No xml elements contained in the include " - "file \"" << filename << "\"" << RESTendl; + "file \"" + << filename << "\"" << RESTendl; exit(1); } while (ele != nullptr) { @@ -1387,7 +1389,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { if (remoteele == nullptr) { RESTWarning << "Cannot find the needed xml section in " - "include file!" << RESTendl; + "include file!" + << RESTendl; RESTWarning << "type: \"" << type << "\" , name: \"" << name << "\" . Skipping" << RESTendl; RESTWarning << RESTendl; @@ -2270,7 +2273,8 @@ TString TRestMetadata::GetLibraryVersion() { return fLibraryVersion; } void TRestMetadata::ReSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = REST_RELEASE; } @@ -2282,7 +2286,8 @@ void TRestMetadata::ReSetVersion() { void TRestMetadata::UnSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = -1; fCommit = -1; @@ -2547,7 +2552,8 @@ void TRestMetadata::ReadOneParameter(string name, string value) { } else { RESTWarning << this->ClassName() << " find unit definition in parameter: " << name << ", but the corresponding data member doesn't support it. Data " - "member type: " << datamember.type << RESTendl; + "member type: " + << datamember.type << RESTendl; datamember.ParseString(value); } } else { diff --git a/source/framework/core/src/TRestProcessRunner.cxx b/source/framework/core/src/TRestProcessRunner.cxx index 3dcdf9370..75758a91a 100644 --- a/source/framework/core/src/TRestProcessRunner.cxx +++ b/source/framework/core/src/TRestProcessRunner.cxx @@ -118,7 +118,8 @@ void TRestProcessRunner::BeginOfInit() { if (fRunInfo == nullptr) { RESTError << "File IO has not been specified, " << RESTendl; RESTError << "please make sure the \"TRestFiles\" section is ahead of the " - "\"TRestProcessRunner\" section" << RESTendl; + "\"TRestProcessRunner\" section" + << RESTendl; exit(0); } } else { @@ -526,12 +527,13 @@ void TRestProcessRunner::RunProcess() { #ifdef TIME_MEASUREMENT RESTInfo << "Total processing time : " << ((Double_t)deltaTime) / 1000. << " ms" << RESTendl; - RESTInfo << "Average read time from disk (per event) : " << ((Double_t)readTime) / fProcessedEvents / - 1000. << " ms" << RESTendl; - RESTInfo << "Average process time (per event) : " << ((Double_t)(deltaTime - readTime - writeTime)) / - fProcessedEvents / 1000. << " ms" << RESTendl; - RESTInfo << "Average write time to disk (per event) : " << ((Double_t)writeTime) / fProcessedEvents / - 1000. << " ms" << RESTendl; + RESTInfo << "Average read time from disk (per event) : " + << ((Double_t)readTime) / fProcessedEvents / 1000. << " ms" << RESTendl; + RESTInfo << "Average process time (per event) : " + << ((Double_t)(deltaTime - readTime - writeTime)) / fProcessedEvents / 1000. << " ms" + << RESTendl; + RESTInfo << "Average write time to disk (per event) : " + << ((Double_t)writeTime) / fProcessedEvents / 1000. << " ms" << RESTendl; RESTInfo << "=" << RESTendl; #endif @@ -1086,15 +1088,15 @@ void TRestProcessRunner::PrintProcessedEvents(Int_t rateE) { double prog = 0; if (fEventsToProcess == REST_MAXIMUM_EVENTS && fRunInfo->GetFileProcess() != nullptr) - // Nevents is unknown, reading external data file + // Nevents is unknown, reading external data file { prog = fRunInfo->GetBytesReaded() / (double)fRunInfo->GetTotalBytes() * 100; } else if (fRunInfo->GetFileProcess() != nullptr) - // Nevents is known, reading external data file + // Nevents is known, reading external data file { prog = fProcessedEvents / (double)fEventsToProcess * 100; } else if (fEventsToProcess == REST_MAXIMUM_EVENTS) - // Nevents is unknown, reading root file + // Nevents is unknown, reading root file { prog = fRunInfo->GetCurrentEntry() / (double)inputtreeentries * 100; } else { diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index 23984f38c..a99cde3e5 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -148,7 +148,8 @@ void TRestRun::InitFromConfigFile() { } if (ToUpper(runNstr) == "AUTO" && ToUpper(inputName) == "AUTO") { RESTError << "TRestRun: run number and input file name cannot both be " - "\"AUTO\"" << RESTendl; + "\"AUTO\"" + << RESTendl; exit(1); } @@ -272,7 +273,8 @@ void TRestRun::InitFromConfigFile() { ImportMetadata(e->Attribute("file"), e->Attribute("name"), e->Attribute("type"), true); } else { RESTWarning << "Wrong definition of addMetadata! Metadata name or file name " - "is not given!" << RESTendl; + "is not given!" + << RESTendl; } } else if (Count(key, "TRest") > 0) { if (e->Attribute("file") != nullptr && TRestTools::isRootFile(e->Attribute("file"))) { @@ -402,7 +404,8 @@ void TRestRun::OpenInputFile(const TString& filename, const string& mode) { ReadInputFileMetadata(); } else { RESTWarning << "-- W : The metadata version found on input file is lower " - "than 2.2.1!" << RESTendl; + "than 2.2.1!" + << RESTendl; RESTWarning << "-- W : metadata from input file will not be read" << RESTendl; } } @@ -603,10 +606,12 @@ void TRestRun::ReadInputFileTrees() { if (fInputEvent == nullptr) { RESTError << "TRestRun:OpenInputFile. Cannot initialize input event, event " - "tree not read" << RESTendl; + "tree not read" + << RESTendl; RESTError << "Please install corresponding libraries to provide root dictionaries for " - "class reading." << RESTendl; + "class reading." + << RESTendl; return; } @@ -621,7 +626,8 @@ void TRestRun::ReadInputFileTrees() { string brname = (string)fInputEvent->ClassName() + "Branch"; if (fEventTree->GetBranch(brname.c_str()) == nullptr) { RESTWarning << "REST WARNING (OpenInputFile) : No matched event branch " - "inside file : " << filename << RESTendl; + "inside file : " + << filename << RESTendl; RESTWarning << "Branch required: " << brname << RESTendl; } else { fEventTree->SetBranchAddress(brname.c_str(), &fInputEvent); @@ -830,7 +836,8 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetEvent, TRestAnalysisTree* targetT if (fEventTree->IsA() == TChain::Class()) { Long64_t entry = fEventTree->LoadTree(fCurrentEvent); fBytesRead += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( - fEventBranchLoc))->GetEntry(entry); + fEventBranchLoc)) + ->GetEntry(entry); } else { fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) @@ -1256,7 +1263,8 @@ void TRestRun::SetInputEvent(TRestEvent* event) { break; } else if (i == branches->GetLast()) { RESTWarning << "REST Warning : (TRestRun) cannot find corresponding " - "branch in event tree!" << RESTendl; + "branch in event tree!" + << RESTendl; RESTWarning << "Event Type : " << event->ClassName() << RESTendl; RESTWarning << "Input event not set!" << RESTendl; } @@ -1304,7 +1312,8 @@ void TRestRun::ImportMetadata(const TString& File, const TString& name, const TS // TODO give error in case we try to obtain a class that is not TRestMetadata if (type == "" && name == "") { RESTError << "(ImportMetadata) : metadata type and name is not " - "specified!" << RESTendl; + "specified!" + << RESTendl; return; } @@ -1570,7 +1579,8 @@ TRestMetadata* TRestRun::GetMetadataClass(const TString& type, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadataClass() : The object to import is " - "not inherited from TRestMetadata" << RESTendl; + "not inherited from TRestMetadata" + << RESTendl; } } } @@ -1600,7 +1610,8 @@ TRestMetadata* TRestRun::GetMetadata(const TString& name, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadata() : The object to import is not " - "inherited from TRestMetadata" << RESTendl; + "inherited from TRestMetadata" + << RESTendl; } } } diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index eb7a44b97..58836b4bf 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void) x +#define UNUSED(x) (void)x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -179,14 +179,10 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { - ENERGY, - LENGTH, - TIME -}; +enum Quantities { ENERGY, LENGTH, TIME }; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 1a3186340..0af3237fa 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -819,13 +819,15 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" + << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" + << endl; exit(1); } path /= userHomePath; @@ -1030,7 +1032,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof() : + case std::streambuf::traits_type::eof(): // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1235,7 +1237,8 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" << RESTendl; + "http://gifna.unizar.es/rest-forum/" + << RESTendl; return -1; } From 99c7fe65ac8e1aef78e067448fd1cc7367d11f3b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 9 Nov 2023 16:11:45 +0100 Subject: [PATCH 038/174] TRestComponentDataSet and TRestComponentFormula added (WIP) --- .../sensitivity/inc/TRestComponentDataSet.h | 101 +++ .../sensitivity/inc/TRestComponentFormula.h | 103 +++ .../sensitivity/src/TRestComponentDataSet.cxx | 573 +++++++++++++++++ .../sensitivity/src/TRestComponentFormula.cxx | 593 ++++++++++++++++++ 4 files changed, 1370 insertions(+) create mode 100644 source/framework/sensitivity/inc/TRestComponentDataSet.h create mode 100644 source/framework/sensitivity/inc/TRestComponentFormula.h create mode 100644 source/framework/sensitivity/src/TRestComponentDataSet.cxx create mode 100644 source/framework/sensitivity/src/TRestComponentFormula.cxx diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h new file mode 100644 index 000000000..305cab1da --- /dev/null +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -0,0 +1,101 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestComponentDataSet +#define REST_TRestComponentDataSet + +#include + +#include "TRestDataSet.h" +#include "TRestComponent.h" + +/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) +class TRestComponentDataSet : public TRestComponent { + private: + /// A list with the dataset columns used to weight the distribution density and define rate + std::vector fWeights; //< + + /// It defines the statistics of each parameterization node (Initialized by the dataset) + std::vector fNodeStatistics; //< + + /// The filename of the dataset used + std::vector fDataSetFileNames; //< + + /// The generated N-dimensional variable space density for a given node + std::vector fNodeDensity; //< + + /// TODO we need to define multiple datasets and weigth. The weight will be used + /// to create a model, such as weighting different background contaminations or + /// different signal coupling contributions. + /// TODO Then we probably need here a `std::vector ` and another vector + /// with the weights (isotope activity/flux/etc). + + /// The dataset used to initialize the distribution + TRestDataSet fDataSet; //! + + /// It is true of the dataset was loaded without issues + Bool_t fDataSetLoaded = false; //! + + Bool_t ValidDataSet(); + + protected: + std::vector ExtractParameterizationNodes(); + std::vector ExtractNodeStatistics(); + void FillHistograms(); + + Bool_t VariablesOk(); + Bool_t WeightsOk(); + + public: + Bool_t LoadDataSets(); + + /// This method should go to TRestComponentDataSet + Bool_t IsDataSetLoaded() { return fDataSetLoaded; } + + Double_t GetRate(std::vector point) override; + Double_t GetTotalRate() override; + + THnD* GetDensityForNode(Double_t value); + THnD* GetDensityForActiveNode(); + THnD* GetDensity() { return GetDensityForActiveNode(); } + + TH1D* GetHistogram(Double_t node, std::string varName); + TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); + TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); + + TH1D* GetHistogram(std::string varName); + TH2D* GetHistogram(std::string varName1, std::string varName2); + TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); + + void PrintStatistics(); + + void PrintMetadata() override; + void Initialize() override; + void InitFromConfigFile() override; + + TRestComponentDataSet(); + TRestComponentDataSet(const char* cfgFileName, const std::string& name); + ~TRestComponentDataSet(); + + ClassDefOverride(TRestComponentDataSet, 1); +}; +#endif diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h new file mode 100644 index 000000000..4c521cf4e --- /dev/null +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -0,0 +1,103 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestComponentFormula +#define REST_TRestComponentFormula + +#include + +#include "TRestDataSet.h" +#include "TRestComponent.h" + +/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) +class TRestComponentFormula : public TRestComponent { + private: + /// A list with the branches that will be used to create the distribution space + std::vector fVariables; //< + + /// The range of each of the variables used to create the PDF distribution + std::vector fRanges; //< + + /// The number of bins in which we should divide each variable + std::vector fNbins; //< + + /// A list with the branches that will be used to weight the distribution density + std::vector fWeights; //< + + /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass) + std::string fParameter = ""; //< + + /// It defines the nodes of the parameterization (Initialized by the dataset) + std::vector fParameterizationNodes; //< + + /// It defines the statistics of each parameterization node (Initialized by the dataset) + std::vector fNodeStatistics; //< + + /// The function used to initialize the distribution + /// std::string fFunction = ""; //! + /// + /// The function used to initialize the distribution + /// TFormula fFormula; //! + + /// A pointer to the component distribution + // THnD* fDistribution = nullptr; //! + + protected: + std::vector ExtractParameterizationNodes(); + std::vector ExtractNodeStatistics(); + void FillHistograms(); + + Bool_t VariablesOk(); + Bool_t WeightsOk(); + + Int_t GetVariableIndex(std::string varName); + + void InitFromConfigFile() override; + + public: + Bool_t LoadDataSets(); + + /// This method should go to TRestDataSetComponent + Bool_t IsDataSetLoaded() { return fDataSetLoaded; } + + Double_t GetRate(std::vector point) override; + Double_t GetTotalRate() override; + + Int_t SetActiveNode(Double_t node); + + THnD* GetDensityForNode(Double_t value); + THnD* GetDensityForActiveNode(); + + void PrintMetadata() override; + + void PrintStatistics(); + void PrintNodes(); + + void Initialize() override; + TRestComponentFormula(const char* configFilename); + TRestComponentFormula(const char* cfgFileName, const std::string& name); + TRestComponentFormula(); + ~TRestComponentFormula(); + + ClassDefOverride(TRestComponentFormula, 1); +}; +#endif diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx new file mode 100644 index 000000000..ddf59599c --- /dev/null +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -0,0 +1,573 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// This class allows to ... +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2023-December: First implementation of TRestComponentDataSet +/// Javier Galan +/// +/// \class TRestComponentDataSet +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestComponentDataSet.h" + +#include + +#include "TKey.h" + +ClassImp(TRestComponentDataSet); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestComponentDataSet::TRestComponentDataSet() { Initialize(); } + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestComponentDataSet::~TRestComponentDataSet() {} + +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestComponentDataSet::TRestComponentDataSet(const char* cfgFileName, const std::string& name) + : TRestComponent(cfgFileName) { + + Initialize(); + + LoadConfigFromFile(fConfigFileName, name); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); +} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestComponentDataSet::Initialize() { + TRestComponent::Initialize(); + + SetSectionName(this->ClassName()); +} + +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The density should be normalized to the corresponding parameter space. During +/// the component construction, **the user is responsible** to initialize the component +/// with the appropriate units. For example, if the parameter space is 2 spatial +/// dimensions and 1 energy dimension, the contribution of each cell or event to +/// the component will be expressed in mm-2 keV-1 which are the default units for +/// distance and energy. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +Double_t TRestComponentDataSet::GetRate(std::vector point) { + + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentDataSet::GetRate. Dataset has not been loaded" << RESTendl; + RESTError << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; + + RESTInfo << "Trying to load datasets" << RESTendl; + LoadDataSets(); + if (IsDataSetLoaded()) + RESTInfo << "Sucess!" << RESTendl; + else + return 0; + } + + if (HasNodes() && fActiveNode == -1) { + RESTError << "TRestComponentDataSet::GetRate. Active node has not been defined" << RESTendl; + return 0; + } + + Double_t density = + GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); + + Double_t norm = 1; + + // Perhaps this value could be stored internally + // for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return norm * density; +} + +/////////////////////////////////////////////// +/// \brief This method integrates the rate to all the parameter space defined in the density function. +/// The result will be returned in s-1. +/// +Double_t TRestComponentDataSet::GetTotalRate() { + THnD* dHist = GetDensityForActiveNode(); + + Double_t integral = 0; + if (dHist != nullptr) integral = dHist->ComputeIntegral(); + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) + integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return integral; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestComponentDataSet::PrintMetadata() { + TRestComponent::PrintMetadata(); + + if (fVariables.size() != fRanges.size()) + RESTWarning << "The number of variables does not match with the number of defined ranges!" + << RESTendl; + + else if (fVariables.size() != fNbins.size()) + RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; + else { + int n = 0; + RESTMetadata << " === Variables === " << RESTendl; + for (const auto& varName : fVariables) { + RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() + << ") bins: " << fNbins[n] << RESTendl; + n++; + } + } + + RESTMetadata << "----" << RESTendl; +} + +///////////////////////////////////////////// +/// \brief It prints out the statistics available for each parametric node +/// +void TRestComponentDataSet::PrintStatistics() { + if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); + + if (!IsDataSetLoaded()) { + RESTWarning << "TRestComponentDataSet::PrintStatistics. No dataset loaded." << RESTendl; + RESTWarning << "Invoking TRestComponentDataSet::LoadDataSets might solve the problem" << RESTendl; + return; + } + + auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); + std::cout << "Total counts : " << result << std::endl; + std::cout << std::endl; + + std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; + int n = 0; + for (const auto& p : fParameterizationNodes) { + std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; + n++; + } +} + +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class +/// +void TRestComponentDataSet::InitFromConfigFile() { + TRestComponent::InitFromConfigFile(); + + auto ele = GetElement("dataset"); + while (ele != nullptr) { + fDataSetFileNames.push_back(GetParameter("filename", ele, "")); + ele = GetNextElement(ele); + } +} + +///////////////////////////////////////////// +/// \brief It will produce a histogram with the distribution defined using the +/// variables and the weights for each of the parameter nodes. +/// +void TRestComponentDataSet::FillHistograms() { + fNodeStatistics = ExtractNodeStatistics(); + + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentDataSet::FillHistograms. Dataset has not been initialized!" << RESTendl; + return; + } + + if (fParameterizationNodes.empty()) { + RESTWarning << "Nodes have not been defined" << RESTendl; + RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; + fParameterizationNodes.push_back(-137); + } + + RESTInfo << "Generating N-dim histograms" << RESTendl; + int nIndex = 0; + for (const auto& node : fParameterizationNodes) { + ROOT::RDF::RNode df = ROOT::RDataFrame(0); + if (fParameterizationNodes.size() == 1 && node == -137) { + RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; + df = fDataSet.GetDataFrame(); + fParameterizationNodes.clear(); + } else { + RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) + << RESTendl; + std::string filter = fParameter + " == " + DoubleToString(node); + df = fDataSet.GetDataFrame().Filter(filter); + } + + Int_t* bins = new Int_t[fNbins.size()]; + Double_t* xmin = new Double_t[fNbins.size()]; + Double_t* xmax = new Double_t[fNbins.size()]; + + for (size_t n = 0; n < fNbins.size(); n++) { + bins[n] = fNbins[n]; + xmin[n] = fRanges[n].X(); + xmax[n] = fRanges[n].Y(); + } + + TString hName = fParameter + "_" + DoubleToString(node); + if (fParameterizationNodes.empty()) hName = "full"; + + std::vector varsAndWeight = fVariables; + /* +if (!fWeights.empty()) { + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); +} + */ + + auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); + THnD* hNd = new THnD(*hn); + + fNodeDensity.push_back(hNd); + fActiveNode = nIndex; + nIndex++; + } +} + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponentDataSet::GetDensityForNode(Double_t node) { + int n = 0; + for (const auto& x : fParameterizationNodes) { + if (x == node) { + return fNodeDensity[n]; + } + n++; + } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponentDataSet::GetDensityForActiveNode() { + if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; + + RESTError << "The active node is invalid" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH1D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH1D* TRestComponentDataSet::GetHistogram(std::string varName) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 2-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH2D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 2-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH2D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 3-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH3D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2, + std::string varName3) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 3-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH3D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { + if (!ValidDataSet()) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a vector with all the different values found on +/// the dataset column for the user given parameterization variable. +/// +/// If fParameterizationNodes has already been initialized it will +/// directly return its value. +/// +std::vector TRestComponentDataSet::ExtractParameterizationNodes() { + if (!fParameterizationNodes.empty()) return fParameterizationNodes; + + std::vector vs; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentDataSet::ExtractParameterizationNodes. Dataset has not been initialized!" + << RESTendl; + return vs; + } + + auto parValues = fDataSet.GetDataFrame().Take(fParameter); + for (const auto v : parValues) vs.push_back(v); + + std::vector::iterator ip; + ip = std::unique(vs.begin(), vs.begin() + vs.size()); + vs.resize(std::distance(vs.begin(), ip)); + std::sort(vs.begin(), vs.end()); + ip = std::unique(vs.begin(), vs.end()); + vs.resize(std::distance(vs.begin(), ip)); + + return vs; +} + +///////////////////////////////////////////// +/// \brief It returns a vector with the number of entries found for each +/// parameterization node. +/// +/// If fNodeStatistics has already been initialized it will +/// directly return its value. +/// +std::vector TRestComponentDataSet::ExtractNodeStatistics() { + if (!fNodeStatistics.empty()) return fNodeStatistics; + + std::vector stats; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentDataSet::ExtractNodeStatistics. Dataset has not been initialized!" + << RESTendl; + return stats; + } + + RESTInfo << "Counting statistics for each node ..." << RESTendl; + RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; + for (const auto& p : fParameterizationNodes) { + std::string filter = fParameter + " == " + DoubleToString(p); + auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); + stats.push_back(*nEv); + } + return stats; +} + +///////////////////////////////////////////// +/// \brief A method responsible to import a list of TRestDataSet into fDataSet +/// and check that the variables and weights defined by the user can be found +/// inside the dataset. +/// +Bool_t TRestComponentDataSet::LoadDataSets() { + if (fDataSetFileNames.empty()) { + RESTWarning << "Dataset filename was not defined. You may still use " + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + fDataSetLoaded = false; + return fDataSetLoaded; + } + + RESTInfo << "Loading datasets" << RESTendl; + + std::vector fullFileNames; + for (const auto& name : fDataSetFileNames) { + // TODO we get here a list of files. However, we will need to weight each dataset with a factor + // to consider the contribution of each background component. + // Of course, we could previously take a factor into account already in the dataset, through the + // definition of a new column. But being this way would allow us to play around with the + // background model without having to regenerate the dataset. + std::string fileName = SearchFile(name); + if (fileName.empty()) { + RESTError << "TRestComponentDataSet::LoadDataSet. Error loading file : " << name << RESTendl; + RESTError << "Does the file exist?" << RESTendl; + RESTError << "You may use ` = TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); + + if (VariablesOk() && WeightsOk()) { + fParameterizationNodes = ExtractParameterizationNodes(); + FillHistograms(); + return fDataSetLoaded; + } + + return fDataSetLoaded; +} + +///////////////////////////////////////////// +/// \brief It returns true if all variables have been found inside TRestDataSet +/// +Bool_t TRestComponentDataSet::VariablesOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fVariables) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; +} + +///////////////////////////////////////////// +/// \brief It returns true if all weights have been found inside TRestDataSet +/// +Bool_t TRestComponentDataSet::WeightsOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fWeights) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; +} + +///////////////////////////////////////////// +/// \brief Takes care of initializing datasets if have not been initialized. +/// On sucess it returns true. +/// +Bool_t TRestComponentDataSet::ValidDataSet() { + if (!IsDataSetLoaded()) { + RESTWarning << "TRestComponentDataSet::GetRate. Dataset has not been loaded" << RESTendl; + RESTWarning << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; + + RESTInfo << "Trying to load datasets" << RESTendl; + LoadDataSets(); + if (IsDataSetLoaded()) { + RESTInfo << "Sucess!" << RESTendl; + } else { + RESTError << "Failed loading datasets" << RESTendl; + return false; + } + } + + if (HasNodes() && fActiveNode == -1) { + RESTError << "TRestComponentDataSet::GetRate. Active node has not been defined" << RESTendl; + return false; + } + return true; +} diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx new file mode 100644 index 000000000..f8eb58f97 --- /dev/null +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -0,0 +1,593 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// This class allows to ... +/// +/// +///---------------------------------------------------------------------- +/// +/// REST-for-Physics - Software for Rare Event Searches Toolkit +/// +/// History of developments: +/// +/// 2023-December: First implementation of TRestComponentFormula +/// Javier Galan +/// +/// \class TRestComponentFormula +/// \author: Javier Galan (javier.galan.lacarra@cern.ch) +/// +///
+/// +#include "TRestComponentFormula.h" + +#include + +#include "TKey.h" + +ClassImp(TRestComponentFormula); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestComponentFormula::TRestComponentFormula() { Initialize(); } + +TRestComponentFormula::TRestComponentFormula(const char* configFilename) : TRestComponent(configFilename) { + Initialize(); + + LoadConfigFromFile(fConfigFileName); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); +} + +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestComponentFormula::TRestComponentFormula(const char* cfgFileName, const std::string& name) + : TRestComponent(cfgFileName) { + + Initialize(); + + LoadConfigFromFile(fConfigFileName, name); +} + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestComponentFormula::~TRestComponentFormula() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestComponentFormula::Initialize() { + + TRestComponent::Initialize(); + + SetSectionName(this->ClassName()); +} + +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The density should be normalized to the corresponding parameter space. During +/// the component construction, **the user is responsible** to initialize the component +/// with the appropriate units. For example, if the parameter space is 2 spatial +/// dimensions and 1 energy dimension, the contribution of each cell or event to +/// the component will be expressed in mm-2 keV-1 which are the default units for +/// distance and energy. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +Double_t TRestComponentFormula::GetRate(std::vector point) { + Double_t density = + GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); + + Double_t norm = 1; + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return norm * density; +} + +/////////////////////////////////////////////// +/// \brief This method integrates the rate to all the parameter space defined in the density function. +/// The result will be returned in s-1. +/// +Double_t TRestComponentFormula::GetTotalRate() { + THnD* dHist = GetDensityForActiveNode(); + + Double_t integral = 0; + if (dHist != nullptr) integral = dHist->ComputeIntegral(); + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) + integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return integral; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestComponentFormula::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + if (fVariables.size() != fRanges.size()) + RESTWarning << "The number of variables does not match with the number of defined ranges!" + << RESTendl; + + else if (fVariables.size() != fNbins.size()) + RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; + else { + int n = 0; + RESTMetadata << " === Variables === " << RESTendl; + for (const auto& varName : fVariables) { + RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() + << ") bins: " << fNbins[n] << RESTendl; + n++; + } + } + + RESTMetadata << " " << RESTendl; + RESTMetadata << " === Weights === " << RESTendl; + for (const auto& wName : fWeights) RESTMetadata << "- " << wName << RESTendl; + + if (!fParameter.empty()) { + RESTMetadata << " " << RESTendl; + RESTMetadata << " === Parameterization === " << RESTendl; + RESTMetadata << "- Parameter : " << fParameter << RESTendl; + + RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl; + RESTMetadata << " " << RESTendl; + RESTMetadata << " Use : PrintStatistics() or PrintNodes() for additional info" << RESTendl; + } + + RESTMetadata << "----" << RESTendl; +} + +///////////////////////////////////////////// +/// \brief It prints out the statistics available for each parametric node +/// +void TRestComponentFormula::PrintStatistics() { + if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); + + if (!IsDataSetLoaded()) { + RESTWarning << "TRestComponentFormula::PrintStatistics. No dataset loaded." << RESTendl; + RESTWarning << "Invoking TRestComponentFormula::LoadDataSets might solve the problem" << RESTendl; + return; + } + + auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); + std::cout << "Total counts : " << result << std::endl; + std::cout << std::endl; + + std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; + int n = 0; + for (const auto& p : fParameterizationNodes) { + std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; + n++; + } +} + +///////////////////////////////////////////// +/// \brief It prints out on screen the values of the parametric node +/// +void TRestComponentFormula::PrintNodes() { + std::cout << " - Number of nodes : " << fParameterizationNodes.size() << std::endl; + for (const auto& x : fParameterizationNodes) std::cout << x << " "; + std::cout << std::endl; +} + +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class +/// +void TRestComponentFormula::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + auto ele = GetElement("variable"); + while (ele != nullptr) { + std::string name = GetParameter("name", ele, ""); + TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); + Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); + + if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { + RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl; + RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins + << RESTendl; + } else { + fVariables.push_back(name); + fRanges.push_back(v); + fNbins.push_back(bins); + } + + ele = GetNextElement(ele); + } + + ele = GetElement("dataset"); + while (ele != nullptr) { + fDataSetFileNames.push_back(GetParameter("filename", ele, "")); + ele = GetNextElement(ele); + } +} + +///////////////////////////////////////////// +/// \brief It will produce a histogram with the distribution defined using the +/// variables and the weights for each of the parameter nodes. +/// +void TRestComponentFormula::FillHistograms() { + fNodeStatistics = ExtractNodeStatistics(); + + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentFormula::FillHistograms. Dataset has not been initialized!" << RESTendl; + return; + } + + if (fParameterizationNodes.empty()) { + RESTWarning << "Nodes have not been defined" << RESTendl; + RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; + fParameterizationNodes.push_back(-137); + } + + RESTInfo << "Generating N-dim histograms" << RESTendl; + int nIndex = 0; + for (const auto& node : fParameterizationNodes) { + ROOT::RDF::RNode df = ROOT::RDataFrame(0); + if (fParameterizationNodes.size() == 1 && node == -137) { + RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; + df = fDataSet.GetDataFrame(); + fParameterizationNodes.clear(); + } else { + RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) + << RESTendl; + std::string filter = fParameter + " == " + DoubleToString(node); + df = fDataSet.GetDataFrame().Filter(filter); + } + + Int_t* bins = new Int_t[fNbins.size()]; + Double_t* xmin = new Double_t[fNbins.size()]; + Double_t* xmax = new Double_t[fNbins.size()]; + + for (size_t n = 0; n < fNbins.size(); n++) { + bins[n] = fNbins[n]; + xmin[n] = fRanges[n].X(); + xmax[n] = fRanges[n].Y(); + } + + TString hName = fParameter + "_" + DoubleToString(node); + + std::vector varsAndWeight = fVariables; + if (!fWeights.empty()) { + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); + } + + auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); + THnD* hNd = new THnD(*hn); + + fNodeDensity.push_back(hNd); + fActiveNode = nIndex; + nIndex++; + } +} + +///////////////////////////////////////////// +/// \brief It returns the position of the fVariable element for +/// the variable name given by argument. +/// +Int_t TRestComponentFormula::GetVariableIndex(std::string varName) { + int n = 0; + for (const auto& v : fVariables) { + if (v == varName) return n; + n++; + } + + return -1; +} + +///////////////////////////////////////////// +/// \brief It returns the position of the fParameterizationNodes +/// element for the variable name given by argument. +/// +Int_t TRestComponentFormula::SetActiveNode(Double_t node) { + int n = 0; + for (const auto& v : fParameterizationNodes) { + if (v == node) { + fActiveNode = n; + return fActiveNode; + } + n++; + } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + RESTError << "Keeping previous node as active : " << fParameterizationNodes[fActiveNode] << RESTendl; + PrintNodes(); + + return fActiveNode; +} + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponentFormula::GetDensityForNode(Double_t node) { + int n = 0; + for (const auto& x : fParameterizationNodes) { + if (x == node) { + return fNodeDensity[n]; + } + n++; + } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponentFormula::GetDensityForActiveNode() { + if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; + + RESTError << "The active node is invalid" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH1D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName) { + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH1D* TRestComponentFormula::GetHistogram(std::string varName) { + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 2-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH2D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName1, std::string varName2) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 2-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH2D* TRestComponentFormula::GetHistogram(std::string varName1, std::string varName2) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 3-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH3D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName1, std::string varName2, + std::string varName3) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 3-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH3D* TRestComponentFormula::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a vector with all the different values found on +/// the dataset column for the user given parameterization variable. +/// +/// If fParameterizationNodes has already been initialized it will +/// directly return its value. +/// +std::vector TRestComponentFormula::ExtractParameterizationNodes() { + if (!fParameterizationNodes.empty()) return fParameterizationNodes; + + std::vector vs; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentFormula::ExtractParameterizationNodes. Dataset has not been initialized!" + << RESTendl; + return vs; + } + + auto parValues = fDataSet.GetDataFrame().Take(fParameter); + for (const auto v : parValues) vs.push_back(v); + + std::vector::iterator ip; + ip = std::unique(vs.begin(), vs.begin() + vs.size()); + vs.resize(std::distance(vs.begin(), ip)); + std::sort(vs.begin(), vs.end()); + ip = std::unique(vs.begin(), vs.end()); + vs.resize(std::distance(vs.begin(), ip)); + + return vs; +} + +///////////////////////////////////////////// +/// \brief It returns a vector with the number of entries found for each +/// parameterization node. +/// +/// If fNodeStatistics has already been initialized it will +/// directly return its value. +/// +std::vector TRestComponentFormula::ExtractNodeStatistics() { + if (!fNodeStatistics.empty()) return fNodeStatistics; + + std::vector stats; + if (!IsDataSetLoaded()) { + RESTError << "TRestComponentFormula::ExtractNodeStatistics. Dataset has not been initialized!" + << RESTendl; + return stats; + } + + RESTInfo << "Counting statistics for each node ..." << RESTendl; + RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; + for (const auto& p : fParameterizationNodes) { + std::string filter = fParameter + " == " + DoubleToString(p); + auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); + stats.push_back(*nEv); + } + return stats; +} + +///////////////////////////////////////////// +/// \brief A method responsible to import a list of TRestDataSet into fDataSet +/// and check that the variables and weights defined by the user can be found +/// inside the dataset. +/// +Bool_t TRestComponentFormula::LoadDataSets() { + if (fDataSetFileNames.empty()) { + RESTWarning << "Dataset filename was not defined. You may still use " + "TRestComponentFormula::LoadDataSet( filename );" << RESTendl; + fDataSetLoaded = false; + return fDataSetLoaded; + } + + RESTInfo << "Loading datasets" << RESTendl; + + std::vector fullFileNames; + for (const auto& name : fDataSetFileNames) { + std::string fileName = SearchFile(name); + if (fileName.empty()) { + RESTError << "TRestComponentFormula::LoadDataSet. Error loading file : " << name << RESTendl; + RESTError << "Does the file exist?" << RESTendl; + RESTError << "You may use ` = TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); + + if (VariablesOk() && WeightsOk()) { + fParameterizationNodes = ExtractParameterizationNodes(); + FillHistograms(); + return fDataSetLoaded; + } + + return fDataSetLoaded; +} + +///////////////////////////////////////////// +/// \brief It returns true if all variables have been found inside TRestDataSet +/// +Bool_t TRestComponentFormula::VariablesOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fVariables) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; +} + +///////////////////////////////////////////// +/// \brief It returns true if all weights have been found inside TRestDataSet +/// +Bool_t TRestComponentFormula::WeightsOk() { + Bool_t ok = true; + std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); + + for (const auto var : fWeights) + if (std::count(cNames.begin(), cNames.end(), var) == 0) { + RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; + ok = false; + } + return ok; +} From 70da4faf69e3df864c48596149b40cb312e6f095 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 15:13:13 +0000 Subject: [PATCH 039/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponentDataSet.h | 2 +- source/framework/sensitivity/inc/TRestComponentFormula.h | 2 +- source/framework/sensitivity/src/TRestComponentDataSet.cxx | 5 ++--- source/framework/sensitivity/src/TRestComponentFormula.cxx | 5 ++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 305cab1da..1788730b1 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -25,8 +25,8 @@ #include -#include "TRestDataSet.h" #include "TRestComponent.h" +#include "TRestDataSet.h" /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponentDataSet : public TRestComponent { diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index 4c521cf4e..64c3126cd 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -25,8 +25,8 @@ #include -#include "TRestDataSet.h" #include "TRestComponent.h" +#include "TRestDataSet.h" /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponentFormula : public TRestComponent { diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index ddf59599c..ecce57a39 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -72,7 +72,6 @@ TRestComponentDataSet::~TRestComponentDataSet() {} /// TRestComponentDataSet::TRestComponentDataSet(const char* cfgFileName, const std::string& name) : TRestComponent(cfgFileName) { - Initialize(); LoadConfigFromFile(fConfigFileName, name); @@ -106,7 +105,6 @@ void TRestComponentDataSet::Initialize() { /// of the distribution. /// Double_t TRestComponentDataSet::GetRate(std::vector point) { - if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::GetRate. Dataset has not been loaded" << RESTendl; RESTError << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; @@ -472,7 +470,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index f8eb58f97..40f05f5fe 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -75,7 +75,6 @@ TRestComponentFormula::TRestComponentFormula(const char* configFilename) : TRest /// TRestComponentFormula::TRestComponentFormula(const char* cfgFileName, const std::string& name) : TRestComponent(cfgFileName) { - Initialize(); LoadConfigFromFile(fConfigFileName, name); @@ -91,7 +90,6 @@ TRestComponentFormula::~TRestComponentFormula() {} /// (or observables) that have been defined by the user. /// void TRestComponentFormula::Initialize() { - TRestComponent::Initialize(); SetSectionName(this->ClassName()); @@ -523,7 +521,8 @@ std::vector TRestComponentFormula::ExtractNodeStatistics() { Bool_t TRestComponentFormula::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentFormula::LoadDataSet( filename );" << RESTendl; + "TRestComponentFormula::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From ff3728f51788ca06ece5427476ec4d2fcc8c33f1 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 10 Nov 2023 10:36:11 +0100 Subject: [PATCH 040/174] TRestDataSet. Removing replicated code lines --- source/framework/core/inc/TRestDataSet.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 785e434ca..01796873f 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -108,10 +108,6 @@ class TRestDataSet : public TRestMetadata { /// A flag to enable Multithreading during dataframe generation Bool_t fMT = false; //< - /// A flag to enable Multithreading during dataframe generation - Bool_t fMT = false; //< - - inline auto GetAddedColumns() const { return fColumnNameExpressions; } /// The resulting RDF::RNode object after initialization ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0); //! From 6d2850522cf04e179dfa0588bd8b14d56c78a1a3 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 10 Nov 2023 10:48:20 +0100 Subject: [PATCH 041/174] TRestComponentDataSet and TRestComponentFormula. Fixing values --- .../sensitivity/inc/TRestComponent.h | 84 +--- .../sensitivity/inc/TRestComponentDataSet.h | 1 + .../sensitivity/inc/TRestComponentFormula.h | 43 -- .../sensitivity/src/TRestComponent.cxx | 421 +--------------- .../sensitivity/src/TRestComponentFormula.cxx | 459 +----------------- 5 files changed, 35 insertions(+), 973 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index d3f39d0f4..061c89801 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -30,12 +30,8 @@ /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { - private: - //// This will not be necessary the day TRestComponent is a pure abstract class. - //// We will create an instance of TRestDataSetComponent or TRestFormulaComponent - /// It defines how the distribution is initialized (dataset/formula) - std::string fType = "dataset"; + protected: /// A list with the branches that will be used to create the distribution space std::vector fVariables; //< @@ -45,88 +41,40 @@ class TRestComponent : public TRestMetadata { /// The number of bins in which we should divide each variable std::vector fNbins; //< - /// A list with the branches that will be used to weight the distribution density - std::vector fWeights; //< - /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass) std::string fParameter = ""; //< /// It defines the nodes of the parameterization (Initialized by the dataset) std::vector fParameterizationNodes; //< - /// It defines the statistics of each parameterization node (Initialized by the dataset) - std::vector fNodeStatistics; //< - - ////////// This should be implemented in TRestDataSetComponent - ////////// - - /// The filename of the dataset used - std::vector fDataSetFileNames; //< - - /// The generated N-dimensional variable space density for a given node - std::vector fNodeDensity; //< - - /// Methods will return the density of the active node + /// It is used to define the node that will be accessed for rate retrieval Int_t fActiveNode = -1; //< - /// The generated N-dimensional variable space density for the complete dataset - // THnD* fTotalDensity = nullptr; //< - - /// The dataset used to initialize the distribution - TRestDataSet fDataSet; //! - - /// It is true of the dataset was loaded without issues - Bool_t fDataSetLoaded = false; //! - - ////////// - ////////// This should be implemented in TRestDataSetComponent - - ////////// This should be implemented in TRestFormulaComponent - ////////// - /// The function used to initialize the distribution - /// std::string fFunction = ""; //! - /// - /// The function used to initialize the distribution - /// TFormula fFormula; //! - ////////// - ////////// This should be implemented in TRestFormulaComponent - - /// A pointer to the component distribution - // THnD* fDistribution = nullptr; //! - - protected: - std::vector ExtractParameterizationNodes(); - std::vector ExtractNodeStatistics(); - void FillHistograms(); - - Bool_t VariablesOk(); - Bool_t WeightsOk(); + /// It returns true if any nodes have been defined. + Bool_t HasNodes() { return !fParameterizationNodes.empty(); } Int_t GetVariableIndex(std::string varName); void InitFromConfigFile() override; public: - Bool_t LoadDataSets(); - - /// This method should go to TRestDataSetComponent - Bool_t IsDataSetLoaded() { return fDataSetLoaded; } - - Double_t GetRate(std::vector point); - Double_t GetTotalRate(); + virtual Double_t GetRate(std::vector point) = 0; + virtual Double_t GetTotalRate() = 0; Int_t SetActiveNode(Double_t node); - THnD* GetDensityForNode(Double_t value); - THnD* GetDensityForActiveNode(); + /* +THnD* GetDensityForNode(Double_t value); +THnD* GetDensityForActiveNode(); - TH1D* GetHistogram(Double_t node, std::string varName); - TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); - TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); +TH1D* GetHistogram(Double_t node, std::string varName); +TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); +TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); - TH1D* GetHistogram(std::string varName); - TH2D* GetHistogram(std::string varName1, std::string varName2); - TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); +TH1D* GetHistogram(std::string varName); +TH2D* GetHistogram(std::string varName1, std::string varName2); +TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); + */ void PrintMetadata() override; diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 305cab1da..0b3865b1f 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -46,6 +46,7 @@ class TRestComponentDataSet : public TRestComponent { /// TODO we need to define multiple datasets and weigth. The weight will be used /// to create a model, such as weighting different background contaminations or /// different signal coupling contributions. + /// TODO Then we probably need here a `std::vector ` and another vector /// with the weights (isotope activity/flux/etc). diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index 4c521cf4e..94d4e5c77 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -31,27 +31,6 @@ /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponentFormula : public TRestComponent { private: - /// A list with the branches that will be used to create the distribution space - std::vector fVariables; //< - - /// The range of each of the variables used to create the PDF distribution - std::vector fRanges; //< - - /// The number of bins in which we should divide each variable - std::vector fNbins; //< - - /// A list with the branches that will be used to weight the distribution density - std::vector fWeights; //< - - /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass) - std::string fParameter = ""; //< - - /// It defines the nodes of the parameterization (Initialized by the dataset) - std::vector fParameterizationNodes; //< - - /// It defines the statistics of each parameterization node (Initialized by the dataset) - std::vector fNodeStatistics; //< - /// The function used to initialize the distribution /// std::string fFunction = ""; //! /// @@ -62,36 +41,14 @@ class TRestComponentFormula : public TRestComponent { // THnD* fDistribution = nullptr; //! protected: - std::vector ExtractParameterizationNodes(); - std::vector ExtractNodeStatistics(); - void FillHistograms(); - - Bool_t VariablesOk(); - Bool_t WeightsOk(); - - Int_t GetVariableIndex(std::string varName); - void InitFromConfigFile() override; public: - Bool_t LoadDataSets(); - - /// This method should go to TRestDataSetComponent - Bool_t IsDataSetLoaded() { return fDataSetLoaded; } - Double_t GetRate(std::vector point) override; Double_t GetTotalRate() override; - Int_t SetActiveNode(Double_t node); - - THnD* GetDensityForNode(Double_t value); - THnD* GetDensityForActiveNode(); - void PrintMetadata() override; - void PrintStatistics(); - void PrintNodes(); - void Initialize() override; TRestComponentFormula(const char* configFilename); TRestComponentFormula(const char* cfgFileName, const std::string& name); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 06adef7df..af866d17d 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -88,48 +88,18 @@ TRestComponent::~TRestComponent() {} /// void TRestComponent::Initialize() { SetSectionName(this->ClassName()); } -/////////////////////////////////////////////// -/// \brief It returns the intensity/rate (in seconds) corresponding to the -/// generated distribution or formula evaluated at the position of the parameter -/// space given by point. -/// -/// The density should be normalized to the corresponding parameter space. During -/// the component construction, **the user is responsible** to initialize the component -/// with the appropriate units. For example, if the parameter space is 2 spatial -/// dimensions and 1 energy dimension, the contribution of each cell or event to -/// the component will be expressed in mm-2 keV-1 which are the default units for -/// distance and energy. -/// -/// The size of the point vector must have the same dimension as the dimensions -/// of the distribution. +/////////////////////////////////////////// +/// \brief It returns the position of the fVariable element for the variable +/// name given by argument. /// -Double_t TRestComponent::GetRate(std::vector point) { - Double_t density = - GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); - - Double_t norm = 1; - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; - - return norm * density; -} - -/////////////////////////////////////////////// -/// \brief This method integrates the rate to all the parameter space defined in the density function. -/// The result will be returned in s-1. -/// -Double_t TRestComponent::GetTotalRate() { - THnD* dHist = GetDensityForActiveNode(); - - Double_t integral = 0; - if (dHist != nullptr) integral = dHist->ComputeIntegral(); - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) - integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; +Int_t TRestComponent::GetVariableIndex(std::string varName) { + int n = 0; + for (const auto& v : fVariables) { + if (v == varName) return n; + n++; + } - return integral; + return -1; } ///////////////////////////////////////////// @@ -142,22 +112,16 @@ void TRestComponent::PrintMetadata() { RESTWarning << "The number of variables does not match with the number of defined ranges!" << RESTendl; - else if (fVariables.size() != fNbins.size()) - RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; - else { + else if (!fVariables.empty()) { int n = 0; RESTMetadata << " === Variables === " << RESTendl; for (const auto& varName : fVariables) { RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() - << ") bins: " << fNbins[n] << RESTendl; + << ")" << RESTendl; n++; } } - RESTMetadata << " " << RESTendl; - RESTMetadata << " === Weights === " << RESTendl; - for (const auto& wName : fWeights) RESTMetadata << "- " << wName << RESTendl; - if (!fParameter.empty()) { RESTMetadata << " " << RESTendl; RESTMetadata << " === Parameterization === " << RESTendl; @@ -165,36 +129,13 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl; RESTMetadata << " " << RESTendl; - RESTMetadata << " Use : PrintStatistics() or PrintNodes() for additional info" << RESTendl; + RESTMetadata << " Use : PrintNodes() for additional info" << RESTendl; + RESTMetadata << " Use : PrintStatistics() if this is a TRestComponentDataSet" << RESTendl; } RESTMetadata << "----" << RESTendl; } -///////////////////////////////////////////// -/// \brief It prints out the statistics available for each parametric node -/// -void TRestComponent::PrintStatistics() { - if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); - - if (!IsDataSetLoaded()) { - RESTWarning << "TRestComponent::PrintStatistics. No dataset loaded." << RESTendl; - RESTWarning << "Invoking TRestComponent::LoadDataSets might solve the problem" << RESTendl; - return; - } - - auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); - std::cout << "Total counts : " << result << std::endl; - std::cout << std::endl; - - std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; - int n = 0; - for (const auto& p : fParameterizationNodes) { - std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; - n++; - } -} - ///////////////////////////////////////////// /// \brief It prints out on screen the values of the parametric node /// @@ -217,7 +158,7 @@ void TRestComponent::InitFromConfigFile() { Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { - RESTWarning << "TRestComponent::fVariable. Problem with definition." << RESTendl; + RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl; RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins << RESTendl; } else { @@ -228,92 +169,6 @@ void TRestComponent::InitFromConfigFile() { ele = GetNextElement(ele); } - - ele = GetElement("dataset"); - while (ele != nullptr) { - fDataSetFileNames.push_back(GetParameter("filename", ele, "")); - ele = GetNextElement(ele); - } -} - -///////////////////////////////////////////// -/// \brief It will produce a histogram with the distribution defined using the -/// variables and the weights for each of the parameter nodes. -/// -void TRestComponent::FillHistograms() { - fNodeStatistics = ExtractNodeStatistics(); - - if (!IsDataSetLoaded()) { - RESTError << "TRestComponent::FillHistograms. Dataset has not been initialized!" << RESTendl; - return; - } - - if (fParameterizationNodes.empty()) { - RESTWarning << "Nodes have not been defined" << RESTendl; - RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; - fParameterizationNodes.push_back(-137); - } - - RESTInfo << "Generating N-dim histograms" << RESTendl; - int nIndex = 0; - for (const auto& node : fParameterizationNodes) { - ROOT::RDF::RNode df = ROOT::RDataFrame(0); - if (fParameterizationNodes.size() == 1 && node == -137) { - RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; - df = fDataSet.GetDataFrame(); - fParameterizationNodes.clear(); - } else { - RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) - << RESTendl; - std::string filter = fParameter + " == " + DoubleToString(node); - df = fDataSet.GetDataFrame().Filter(filter); - } - - Int_t* bins = new Int_t[fNbins.size()]; - Double_t* xmin = new Double_t[fNbins.size()]; - Double_t* xmax = new Double_t[fNbins.size()]; - - for (size_t n = 0; n < fNbins.size(); n++) { - bins[n] = fNbins[n]; - xmin[n] = fRanges[n].X(); - xmax[n] = fRanges[n].Y(); - } - - TString hName = fParameter + "_" + DoubleToString(node); - - std::vector varsAndWeight = fVariables; - if (!fWeights.empty()) { - std::string weightsStr = ""; - for (size_t n = 0; n < fWeights.size(); n++) { - if (n > 0) weightsStr += "*"; - - weightsStr += fWeights[n]; - } - df = df.Define("componentWeight", weightsStr); - varsAndWeight.push_back("componentWeight"); - } - - auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); - THnD* hNd = new THnD(*hn); - - fNodeDensity.push_back(hNd); - fActiveNode = nIndex; - nIndex++; - } -} - -///////////////////////////////////////////// -/// \brief It returns the position of the fVariable element for -/// the variable name given by argument. -/// -Int_t TRestComponent::GetVariableIndex(std::string varName) { - int n = 0; - for (const auto& v : fVariables) { - if (v == varName) return n; - n++; - } - - return -1; } ///////////////////////////////////////////// @@ -336,249 +191,3 @@ Int_t TRestComponent::SetActiveNode(Double_t node) { return fActiveNode; } - -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponent::GetDensityForNode(Double_t node) { - int n = 0; - for (const auto& x : fParameterizationNodes) { - if (x == node) { - return fNodeDensity[n]; - } - n++; - } - - RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponent::GetDensityForActiveNode() { - if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; - - RESTError << "The active node is invalid" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH1D* TRestComponent::GetHistogram(Double_t node, std::string varName) { - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH1D* TRestComponent::GetHistogram(std::string varName) { - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 2-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH2D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 2-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH2D* TRestComponent::GetHistogram(std::string varName1, std::string varName2) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 3-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH3D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2, - std::string varName3) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 3-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH3D* TRestComponent::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a vector with all the different values found on -/// the dataset column for the user given parameterization variable. -/// -/// If fParameterizationNodes has already been initialized it will -/// directly return its value. -/// -std::vector TRestComponent::ExtractParameterizationNodes() { - if (!fParameterizationNodes.empty()) return fParameterizationNodes; - - std::vector vs; - if (!IsDataSetLoaded()) { - RESTError << "TRestComponent::ExtractParameterizationNodes. Dataset has not been initialized!" - << RESTendl; - return vs; - } - - auto parValues = fDataSet.GetDataFrame().Take(fParameter); - for (const auto v : parValues) vs.push_back(v); - - std::vector::iterator ip; - ip = std::unique(vs.begin(), vs.begin() + vs.size()); - vs.resize(std::distance(vs.begin(), ip)); - std::sort(vs.begin(), vs.end()); - ip = std::unique(vs.begin(), vs.end()); - vs.resize(std::distance(vs.begin(), ip)); - - return vs; -} - -///////////////////////////////////////////// -/// \brief It returns a vector with the number of entries found for each -/// parameterization node. -/// -/// If fNodeStatistics has already been initialized it will -/// directly return its value. -/// -std::vector TRestComponent::ExtractNodeStatistics() { - if (!fNodeStatistics.empty()) return fNodeStatistics; - - std::vector stats; - if (!IsDataSetLoaded()) { - RESTError << "TRestComponent::ExtractNodeStatistics. Dataset has not been initialized!" << RESTendl; - return stats; - } - - RESTInfo << "Counting statistics for each node ..." << RESTendl; - RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; - for (const auto& p : fParameterizationNodes) { - std::string filter = fParameter + " == " + DoubleToString(p); - auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); - stats.push_back(*nEv); - } - return stats; -} - -///////////////////////////////////////////// -/// \brief A method responsible to import a list of TRestDataSet into fDataSet -/// and check that the variables and weights defined by the user can be found -/// inside the dataset. -/// -Bool_t TRestComponent::LoadDataSets() { - if (fDataSetFileNames.empty()) { - RESTWarning - << "Dataset filename was not defined. You may still use TRestComponent::LoadDataSet( filename );" - << RESTendl; - fDataSetLoaded = false; - return fDataSetLoaded; - } - - RESTInfo << "Loading datasets" << RESTendl; - - std::vector fullFileNames; - for (const auto& name : fDataSetFileNames) { - std::string fileName = SearchFile(name); - if (fileName.empty()) { - RESTError << "TRestComponent::LoadDataSet. Error loading file : " << name << RESTendl; - RESTError << "Does the file exist?" << RESTendl; - RESTError << "You may use ` = TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); - - if (VariablesOk() && WeightsOk()) { - fParameterizationNodes = ExtractParameterizationNodes(); - FillHistograms(); - return fDataSetLoaded; - } - - return fDataSetLoaded; -} - -///////////////////////////////////////////// -/// \brief It returns true if all variables have been found inside TRestDataSet -/// -Bool_t TRestComponent::VariablesOk() { - Bool_t ok = true; - std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - - for (const auto var : fVariables) - if (std::count(cNames.begin(), cNames.end(), var) == 0) { - RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; - ok = false; - } - return ok; -} - -///////////////////////////////////////////// -/// \brief It returns true if all weights have been found inside TRestDataSet -/// -Bool_t TRestComponent::WeightsOk() { - Bool_t ok = true; - std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - - for (const auto var : fWeights) - if (std::count(cNames.begin(), cNames.end(), var) == 0) { - RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; - ok = false; - } - return ok; -} diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index f8eb58f97..5845f4fb9 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -112,31 +112,15 @@ void TRestComponentFormula::Initialize() { /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. /// -Double_t TRestComponentFormula::GetRate(std::vector point) { - Double_t density = - GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); - - Double_t norm = 1; - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; - - return norm * density; -} +Double_t TRestComponentFormula::GetRate(std::vector point) { return 0.0; } /////////////////////////////////////////////// /// \brief This method integrates the rate to all the parameter space defined in the density function. /// The result will be returned in s-1. /// Double_t TRestComponentFormula::GetTotalRate() { - THnD* dHist = GetDensityForActiveNode(); Double_t integral = 0; - if (dHist != nullptr) integral = dHist->ComputeIntegral(); - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) - integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; return integral; } @@ -145,449 +129,12 @@ Double_t TRestComponentFormula::GetTotalRate() { /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// void TRestComponentFormula::PrintMetadata() { - TRestMetadata::PrintMetadata(); - - if (fVariables.size() != fRanges.size()) - RESTWarning << "The number of variables does not match with the number of defined ranges!" - << RESTendl; - - else if (fVariables.size() != fNbins.size()) - RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; - else { - int n = 0; - RESTMetadata << " === Variables === " << RESTendl; - for (const auto& varName : fVariables) { - RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() - << ") bins: " << fNbins[n] << RESTendl; - n++; - } - } - - RESTMetadata << " " << RESTendl; - RESTMetadata << " === Weights === " << RESTendl; - for (const auto& wName : fWeights) RESTMetadata << "- " << wName << RESTendl; - - if (!fParameter.empty()) { - RESTMetadata << " " << RESTendl; - RESTMetadata << " === Parameterization === " << RESTendl; - RESTMetadata << "- Parameter : " << fParameter << RESTendl; - - RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl; - RESTMetadata << " " << RESTendl; - RESTMetadata << " Use : PrintStatistics() or PrintNodes() for additional info" << RESTendl; - } + TRestComponent::PrintMetadata(); RESTMetadata << "----" << RESTendl; } -///////////////////////////////////////////// -/// \brief It prints out the statistics available for each parametric node -/// -void TRestComponentFormula::PrintStatistics() { - if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); - - if (!IsDataSetLoaded()) { - RESTWarning << "TRestComponentFormula::PrintStatistics. No dataset loaded." << RESTendl; - RESTWarning << "Invoking TRestComponentFormula::LoadDataSets might solve the problem" << RESTendl; - return; - } - - auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); - std::cout << "Total counts : " << result << std::endl; - std::cout << std::endl; - - std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; - int n = 0; - for (const auto& p : fParameterizationNodes) { - std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; - n++; - } -} - -///////////////////////////////////////////// -/// \brief It prints out on screen the values of the parametric node -/// -void TRestComponentFormula::PrintNodes() { - std::cout << " - Number of nodes : " << fParameterizationNodes.size() << std::endl; - for (const auto& x : fParameterizationNodes) std::cout << x << " "; - std::cout << std::endl; -} - ///////////////////////////////////////////// /// \brief It customizes the retrieval of XML data values of this class /// -void TRestComponentFormula::InitFromConfigFile() { - TRestMetadata::InitFromConfigFile(); - - auto ele = GetElement("variable"); - while (ele != nullptr) { - std::string name = GetParameter("name", ele, ""); - TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); - Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); - - if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { - RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl; - RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins - << RESTendl; - } else { - fVariables.push_back(name); - fRanges.push_back(v); - fNbins.push_back(bins); - } - - ele = GetNextElement(ele); - } - - ele = GetElement("dataset"); - while (ele != nullptr) { - fDataSetFileNames.push_back(GetParameter("filename", ele, "")); - ele = GetNextElement(ele); - } -} - -///////////////////////////////////////////// -/// \brief It will produce a histogram with the distribution defined using the -/// variables and the weights for each of the parameter nodes. -/// -void TRestComponentFormula::FillHistograms() { - fNodeStatistics = ExtractNodeStatistics(); - - if (!IsDataSetLoaded()) { - RESTError << "TRestComponentFormula::FillHistograms. Dataset has not been initialized!" << RESTendl; - return; - } - - if (fParameterizationNodes.empty()) { - RESTWarning << "Nodes have not been defined" << RESTendl; - RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; - fParameterizationNodes.push_back(-137); - } - - RESTInfo << "Generating N-dim histograms" << RESTendl; - int nIndex = 0; - for (const auto& node : fParameterizationNodes) { - ROOT::RDF::RNode df = ROOT::RDataFrame(0); - if (fParameterizationNodes.size() == 1 && node == -137) { - RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; - df = fDataSet.GetDataFrame(); - fParameterizationNodes.clear(); - } else { - RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) - << RESTendl; - std::string filter = fParameter + " == " + DoubleToString(node); - df = fDataSet.GetDataFrame().Filter(filter); - } - - Int_t* bins = new Int_t[fNbins.size()]; - Double_t* xmin = new Double_t[fNbins.size()]; - Double_t* xmax = new Double_t[fNbins.size()]; - - for (size_t n = 0; n < fNbins.size(); n++) { - bins[n] = fNbins[n]; - xmin[n] = fRanges[n].X(); - xmax[n] = fRanges[n].Y(); - } - - TString hName = fParameter + "_" + DoubleToString(node); - - std::vector varsAndWeight = fVariables; - if (!fWeights.empty()) { - std::string weightsStr = ""; - for (size_t n = 0; n < fWeights.size(); n++) { - if (n > 0) weightsStr += "*"; - - weightsStr += fWeights[n]; - } - df = df.Define("componentWeight", weightsStr); - varsAndWeight.push_back("componentWeight"); - } - - auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); - THnD* hNd = new THnD(*hn); - - fNodeDensity.push_back(hNd); - fActiveNode = nIndex; - nIndex++; - } -} - -///////////////////////////////////////////// -/// \brief It returns the position of the fVariable element for -/// the variable name given by argument. -/// -Int_t TRestComponentFormula::GetVariableIndex(std::string varName) { - int n = 0; - for (const auto& v : fVariables) { - if (v == varName) return n; - n++; - } - - return -1; -} - -///////////////////////////////////////////// -/// \brief It returns the position of the fParameterizationNodes -/// element for the variable name given by argument. -/// -Int_t TRestComponentFormula::SetActiveNode(Double_t node) { - int n = 0; - for (const auto& v : fParameterizationNodes) { - if (v == node) { - fActiveNode = n; - return fActiveNode; - } - n++; - } - - RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; - RESTError << "Keeping previous node as active : " << fParameterizationNodes[fActiveNode] << RESTendl; - PrintNodes(); - - return fActiveNode; -} - -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponentFormula::GetDensityForNode(Double_t node) { - int n = 0; - for (const auto& x : fParameterizationNodes) { - if (x == node) { - return fNodeDensity[n]; - } - n++; - } - - RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponentFormula::GetDensityForActiveNode() { - if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; - - RESTError << "The active node is invalid" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH1D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName) { - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH1D* TRestComponentFormula::GetHistogram(std::string varName) { - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 2-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH2D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName1, std::string varName2) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 2-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH2D* TRestComponentFormula::GetHistogram(std::string varName1, std::string varName2) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 3-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH3D* TRestComponentFormula::GetHistogram(Double_t node, std::string varName1, std::string varName2, - std::string varName3) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 3-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH3D* TRestComponentFormula::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a vector with all the different values found on -/// the dataset column for the user given parameterization variable. -/// -/// If fParameterizationNodes has already been initialized it will -/// directly return its value. -/// -std::vector TRestComponentFormula::ExtractParameterizationNodes() { - if (!fParameterizationNodes.empty()) return fParameterizationNodes; - - std::vector vs; - if (!IsDataSetLoaded()) { - RESTError << "TRestComponentFormula::ExtractParameterizationNodes. Dataset has not been initialized!" - << RESTendl; - return vs; - } - - auto parValues = fDataSet.GetDataFrame().Take(fParameter); - for (const auto v : parValues) vs.push_back(v); - - std::vector::iterator ip; - ip = std::unique(vs.begin(), vs.begin() + vs.size()); - vs.resize(std::distance(vs.begin(), ip)); - std::sort(vs.begin(), vs.end()); - ip = std::unique(vs.begin(), vs.end()); - vs.resize(std::distance(vs.begin(), ip)); - - return vs; -} - -///////////////////////////////////////////// -/// \brief It returns a vector with the number of entries found for each -/// parameterization node. -/// -/// If fNodeStatistics has already been initialized it will -/// directly return its value. -/// -std::vector TRestComponentFormula::ExtractNodeStatistics() { - if (!fNodeStatistics.empty()) return fNodeStatistics; - - std::vector stats; - if (!IsDataSetLoaded()) { - RESTError << "TRestComponentFormula::ExtractNodeStatistics. Dataset has not been initialized!" - << RESTendl; - return stats; - } - - RESTInfo << "Counting statistics for each node ..." << RESTendl; - RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; - for (const auto& p : fParameterizationNodes) { - std::string filter = fParameter + " == " + DoubleToString(p); - auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); - stats.push_back(*nEv); - } - return stats; -} - -///////////////////////////////////////////// -/// \brief A method responsible to import a list of TRestDataSet into fDataSet -/// and check that the variables and weights defined by the user can be found -/// inside the dataset. -/// -Bool_t TRestComponentFormula::LoadDataSets() { - if (fDataSetFileNames.empty()) { - RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentFormula::LoadDataSet( filename );" << RESTendl; - fDataSetLoaded = false; - return fDataSetLoaded; - } - - RESTInfo << "Loading datasets" << RESTendl; - - std::vector fullFileNames; - for (const auto& name : fDataSetFileNames) { - std::string fileName = SearchFile(name); - if (fileName.empty()) { - RESTError << "TRestComponentFormula::LoadDataSet. Error loading file : " << name << RESTendl; - RESTError << "Does the file exist?" << RESTendl; - RESTError << "You may use ` = TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata(); - - if (VariablesOk() && WeightsOk()) { - fParameterizationNodes = ExtractParameterizationNodes(); - FillHistograms(); - return fDataSetLoaded; - } - - return fDataSetLoaded; -} - -///////////////////////////////////////////// -/// \brief It returns true if all variables have been found inside TRestDataSet -/// -Bool_t TRestComponentFormula::VariablesOk() { - Bool_t ok = true; - std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - - for (const auto var : fVariables) - if (std::count(cNames.begin(), cNames.end(), var) == 0) { - RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; - ok = false; - } - return ok; -} - -///////////////////////////////////////////// -/// \brief It returns true if all weights have been found inside TRestDataSet -/// -Bool_t TRestComponentFormula::WeightsOk() { - Bool_t ok = true; - std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - - for (const auto var : fWeights) - if (std::count(cNames.begin(), cNames.end(), var) == 0) { - RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; - ok = false; - } - return ok; -} +void TRestComponentFormula::InitFromConfigFile() { TRestComponent::InitFromConfigFile(); } From 35d627ef6f73afc63d6096ce323bfe492a6e034c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 09:50:08 +0000 Subject: [PATCH 042/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 1 - source/framework/sensitivity/src/TRestComponentDataSet.cxx | 3 ++- source/framework/sensitivity/src/TRestComponentFormula.cxx | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 061c89801..1b90700bf 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -30,7 +30,6 @@ /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { - protected: /// A list with the branches that will be used to create the distribution space std::vector fVariables; //< diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 433fea0e2..ecce57a39 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -470,7 +470,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 7526ad849..1085dcb08 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -117,7 +117,6 @@ Double_t TRestComponentFormula::GetRate(std::vector point) { return 0. /// The result will be returned in s-1. /// Double_t TRestComponentFormula::GetTotalRate() { - Double_t integral = 0; return integral; From 321ed6cc42ec378cdeba3e1613f6be3ab9250a3d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 10 Nov 2023 12:23:55 +0100 Subject: [PATCH 043/174] REST_OpenInputFile.C Fixing output style --- macros/REST_OpenInputFile.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/REST_OpenInputFile.C b/macros/REST_OpenInputFile.C index 3087791c2..78f0560e9 100644 --- a/macros/REST_OpenInputFile.C +++ b/macros/REST_OpenInputFile.C @@ -45,7 +45,7 @@ void REST_OpenInputFile(const std::string& fileName) { printf("\n%s\n", "The dataset is ready. You may now access the dataset using:"); printf("\n%s\n", " - dSet->PrintMetadata()"); printf("%s\n", " - dSet->GetDataFrame().GetColumnNames()"); - printf("%s\n\n", " - dSet->GetTree()->GetEntries()"); + printf("%s\n", " - dSet->GetTree()->GetEntries()"); printf("%s\n\n", " - dSet->GetDataFrame().Display({\"colName1,colName2\"})->Print()"); if (dSet) delete dSet; dSet = new TRestDataSet(); From c4fc5d3e07230438ccfae6b4966d53171cac59f4 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 10 Nov 2023 12:25:02 +0100 Subject: [PATCH 044/174] TRestComponent. Proper implementation of metadata inhereted constructors --- source/framework/sensitivity/inc/TRestComponent.h | 3 +-- .../framework/sensitivity/inc/TRestComponentFormula.h | 1 - source/framework/sensitivity/src/TRestComponent.cxx | 10 ++-------- .../sensitivity/src/TRestComponentFormula.cxx | 10 ++-------- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 061c89801..9889abd79 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -82,8 +82,7 @@ TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varNa void PrintNodes(); void Initialize() override; - TRestComponent(const char* configFilename); - TRestComponent(const char* cfgFileName, const std::string& name); + TRestComponent(const char* cfgFileName, const std::string& name = ""); TRestComponent(); ~TRestComponent(); diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index c0e3d06d5..c05961c65 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -50,7 +50,6 @@ class TRestComponentFormula : public TRestComponent { void PrintMetadata() override; void Initialize() override; - TRestComponentFormula(const char* configFilename); TRestComponentFormula(const char* cfgFileName, const std::string& name); TRestComponentFormula(); ~TRestComponentFormula(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index af866d17d..054605db7 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -51,12 +51,6 @@ ClassImp(TRestComponent); /// TRestComponent::TRestComponent() { Initialize(); } -TRestComponent::TRestComponent(const char* configFilename) : TRestMetadata(configFilename) { - Initialize(); - - LoadConfigFromFile(fConfigFileName); -} - ///////////////////////////////////////////// /// \brief Constructor loading data from a config file /// @@ -73,8 +67,8 @@ TRestComponent::TRestComponent(const char* configFilename) : TRestMetadata(confi /// TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { - Initialize(); - LoadConfigFromFile(fConfigFileName, name); + RESTDebug << "Entering TRestComponent constructor( cfgFileName, name )" << RESTendl; + RESTDebug << "File: " << cfgFileName << " Name: " << name << RESTendl; } /////////////////////////////////////////////// diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 7526ad849..43c06a6eb 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -51,14 +51,6 @@ ClassImp(TRestComponentFormula); /// TRestComponentFormula::TRestComponentFormula() { Initialize(); } -TRestComponentFormula::TRestComponentFormula(const char* configFilename) : TRestComponent(configFilename) { - Initialize(); - - LoadConfigFromFile(fConfigFileName); - - if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); -} - ///////////////////////////////////////////// /// \brief Constructor loading data from a config file /// @@ -78,6 +70,8 @@ TRestComponentFormula::TRestComponentFormula(const char* cfgFileName, const std: Initialize(); LoadConfigFromFile(fConfigFileName, name); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); } /////////////////////////////////////////////// From 59b0ef0c4e26cd885a3d1fb1fef0d8df52ef117b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 22 Nov 2023 09:31:31 +0100 Subject: [PATCH 045/174] TRestComponent. Adding methods to recover the active node data member --- source/framework/sensitivity/inc/TRestComponent.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index a0daff02a..55930a34b 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -61,6 +61,8 @@ class TRestComponent : public TRestMetadata { virtual Double_t GetTotalRate() = 0; Int_t SetActiveNode(Double_t node); + Int_t GetActiveNode() { return fActiveNode; } + Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } /* THnD* GetDensityForNode(Double_t value); From f05e4829e21beff0ae3bbd8cc83928f358f8844d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 22 Nov 2023 09:32:14 +0100 Subject: [PATCH 046/174] TRestComponentDataSet improving PrintMetadata output --- .../sensitivity/src/TRestComponent.cxx | 1 - .../sensitivity/src/TRestComponentDataSet.cxx | 33 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 054605db7..dd846bd2f 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -124,7 +124,6 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl; RESTMetadata << " " << RESTendl; RESTMetadata << " Use : PrintNodes() for additional info" << RESTendl; - RESTMetadata << " Use : PrintStatistics() if this is a TRestComponentDataSet" << RESTendl; } RESTMetadata << "----" << RESTendl; diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index ecce57a39..d49f73685 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -173,6 +173,13 @@ void TRestComponentDataSet::PrintMetadata() { } RESTMetadata << "----" << RESTendl; + + if (!fParameter.empty() && fParameterizationNodes.empty()) { + RESTMetadata << "This component has no nodes!" << RESTendl; + RESTMetadata << " Use: LoadDataSets() to initialize the nodes" << RESTendl; + } + RESTMetadata << " Use : PrintStatistics() to check node statistics" << RESTendl; + RESTMetadata << "----" << RESTendl; } ///////////////////////////////////////////// @@ -259,17 +266,18 @@ void TRestComponentDataSet::FillHistograms() { if (fParameterizationNodes.empty()) hName = "full"; std::vector varsAndWeight = fVariables; - /* -if (!fWeights.empty()) { - std::string weightsStr = ""; - for (size_t n = 0; n < fWeights.size(); n++) { - if (n > 0) weightsStr += "*"; - weightsStr += fWeights[n]; - } - df = df.Define("componentWeight", weightsStr); - varsAndWeight.push_back("componentWeight"); -} + /* + if (!fWeights.empty()) { + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); + } */ auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); @@ -415,6 +423,8 @@ TH3D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varN std::vector TRestComponentDataSet::ExtractParameterizationNodes() { if (!fParameterizationNodes.empty()) return fParameterizationNodes; + RESTInfo << "Extracting parameterization nodes" << RESTendl; + std::vector vs; if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::ExtractParameterizationNodes. Dataset has not been initialized!" @@ -470,8 +480,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 547b4a8ab0168817bcad25cc37523813e19dfdd7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 08:32:44 +0000 Subject: [PATCH 047/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestComponentDataSet.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index d49f73685..87a4a5218 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -480,7 +480,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 6208b68fc34b297ae87a0c86df0caad17d0adc68 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 23 Nov 2023 12:39:26 +0100 Subject: [PATCH 048/174] TRestRun::GetEntries returns now fSavedEntries if analysis tree is not found --- macros/REST_CheckRunFileList.C | 17 +++++++++--- source/framework/core/src/TRestRun.cxx | 36 ++++++++++---------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/macros/REST_CheckRunFileList.C b/macros/REST_CheckRunFileList.C index eba5324da..28d7e09a2 100644 --- a/macros/REST_CheckRunFileList.C +++ b/macros/REST_CheckRunFileList.C @@ -54,11 +54,12 @@ Int_t REST_CheckRunFileList(TString namePattern, Int_t N = 100000) { } RESTLog << "Run time (hours) : " << run->GetRunLength() / 3600. << RESTendl; - if (run->GetRunLength() > 0) totalTime += run->GetRunLength() / 3600.; + RESTLog << "Entries : " << run->GetEntries() << RESTendl; - if (run->GetEndTimestamp() == 0 || run->GetRunLength() < 0) { + if (run->GetEndTimestamp() == 0 || run->GetRunLength() < 0 || run->GetEntries() == 0) { filesNotWellClosed.push_back(filename); - } + } else if (run->GetRunLength() > 0) + totalTime += run->GetRunLength() / 3600.; delete run; @@ -70,7 +71,15 @@ Int_t REST_CheckRunFileList(TString namePattern, Int_t N = 100000) { RESTLog << "---------------------" << RESTendl; RESTLog << "Files not well closed" << RESTendl; RESTLog << "---------------------" << RESTendl; - for (int i = 0; i < filesNotWellClosed.size(); i++) RESTLog << filesNotWellClosed[i] << RESTendl; + for (int i = 0; i < filesNotWellClosed.size(); i++) { + RESTLog << filesNotWellClosed[i] << RESTendl; + if (purge) fs::remove(filesNotWellClosed[i]); + } + + if (purge) { + RESTLog << "---------------------------" << RESTendl; + RESTLog << "The files have been removed" << RESTendl; + } } RESTLog << "------------------------------" << RESTendl; diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index a99cde3e5..eb01804e0 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -148,8 +148,7 @@ void TRestRun::InitFromConfigFile() { } if (ToUpper(runNstr) == "AUTO" && ToUpper(inputName) == "AUTO") { RESTError << "TRestRun: run number and input file name cannot both be " - "\"AUTO\"" - << RESTendl; + "\"AUTO\"" << RESTendl; exit(1); } @@ -273,8 +272,7 @@ void TRestRun::InitFromConfigFile() { ImportMetadata(e->Attribute("file"), e->Attribute("name"), e->Attribute("type"), true); } else { RESTWarning << "Wrong definition of addMetadata! Metadata name or file name " - "is not given!" - << RESTendl; + "is not given!" << RESTendl; } } else if (Count(key, "TRest") > 0) { if (e->Attribute("file") != nullptr && TRestTools::isRootFile(e->Attribute("file"))) { @@ -404,8 +402,7 @@ void TRestRun::OpenInputFile(const TString& filename, const string& mode) { ReadInputFileMetadata(); } else { RESTWarning << "-- W : The metadata version found on input file is lower " - "than 2.2.1!" - << RESTendl; + "than 2.2.1!" << RESTendl; RESTWarning << "-- W : metadata from input file will not be read" << RESTendl; } } @@ -606,12 +603,10 @@ void TRestRun::ReadInputFileTrees() { if (fInputEvent == nullptr) { RESTError << "TRestRun:OpenInputFile. Cannot initialize input event, event " - "tree not read" - << RESTendl; + "tree not read" << RESTendl; RESTError << "Please install corresponding libraries to provide root dictionaries for " - "class reading." - << RESTendl; + "class reading." << RESTendl; return; } @@ -626,8 +621,7 @@ void TRestRun::ReadInputFileTrees() { string brname = (string)fInputEvent->ClassName() + "Branch"; if (fEventTree->GetBranch(brname.c_str()) == nullptr) { RESTWarning << "REST WARNING (OpenInputFile) : No matched event branch " - "inside file : " - << filename << RESTendl; + "inside file : " << filename << RESTendl; RESTWarning << "Branch required: " << brname << RESTendl; } else { fEventTree->SetBranchAddress(brname.c_str(), &fInputEvent); @@ -836,8 +830,7 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetEvent, TRestAnalysisTree* targetT if (fEventTree->IsA() == TChain::Class()) { Long64_t entry = fEventTree->LoadTree(fCurrentEvent); fBytesRead += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( - fEventBranchLoc)) - ->GetEntry(entry); + fEventBranchLoc))->GetEntry(entry); } else { fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) @@ -1263,8 +1256,7 @@ void TRestRun::SetInputEvent(TRestEvent* event) { break; } else if (i == branches->GetLast()) { RESTWarning << "REST Warning : (TRestRun) cannot find corresponding " - "branch in event tree!" - << RESTendl; + "branch in event tree!" << RESTendl; RESTWarning << "Event Type : " << event->ClassName() << RESTendl; RESTWarning << "Input event not set!" << RESTendl; } @@ -1312,8 +1304,7 @@ void TRestRun::ImportMetadata(const TString& File, const TString& name, const TS // TODO give error in case we try to obtain a class that is not TRestMetadata if (type == "" && name == "") { RESTError << "(ImportMetadata) : metadata type and name is not " - "specified!" - << RESTendl; + "specified!" << RESTendl; return; } @@ -1367,7 +1358,8 @@ Long64_t TRestRun::GetEntries() const { if (fAnalysisTree != nullptr) { return fAnalysisTree->GetEntries(); } - return REST_MAXIMUM_EVENTS; + return fEntriesSaved; + // return REST_MAXIMUM_EVENTS; } // Getters @@ -1579,8 +1571,7 @@ TRestMetadata* TRestRun::GetMetadataClass(const TString& type, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadataClass() : The object to import is " - "not inherited from TRestMetadata" - << RESTendl; + "not inherited from TRestMetadata" << RESTendl; } } } @@ -1610,8 +1601,7 @@ TRestMetadata* TRestRun::GetMetadata(const TString& name, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadata() : The object to import is not " - "inherited from TRestMetadata" - << RESTendl; + "inherited from TRestMetadata" << RESTendl; } } } From 3e60f5ca00f0cd89bc1a400fbf5fb0917e8497dd Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 24 Nov 2023 09:18:55 +0100 Subject: [PATCH 049/174] TRestComponent::ValidNode method added --- source/framework/sensitivity/inc/TRestComponent.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 55930a34b..5bbdbbc58 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -52,6 +52,13 @@ class TRestComponent : public TRestMetadata { /// It returns true if any nodes have been defined. Bool_t HasNodes() { return !fParameterizationNodes.empty(); } + /// It returns true if the node has been properly identified + Bool_t ValidNode(Double_t node) { + SetActiveNode(node); + if (GetActiveNode() >= 0) return true; + return false; + } + Int_t GetVariableIndex(std::string varName); void InitFromConfigFile() override; From 8a585f4e372a9313341294fe30e1574cabcf453f Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 24 Nov 2023 09:20:02 +0100 Subject: [PATCH 050/174] TRestComponentDataSet. Now it is allowed to recover the component without dataset --- .../sensitivity/src/TRestComponentDataSet.cxx | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 87a4a5218..06fed1918 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -105,8 +105,8 @@ void TRestComponentDataSet::Initialize() { /// of the distribution. /// Double_t TRestComponentDataSet::GetRate(std::vector point) { - if (!IsDataSetLoaded()) { - RESTError << "TRestComponentDataSet::GetRate. Dataset has not been loaded" << RESTendl; + if (!HasNodes()) { + RESTError << "TRestComponentDataSet::GetRate. The component has no nodes!" << RESTendl; RESTError << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; RESTInfo << "Trying to load datasets" << RESTendl; @@ -188,8 +188,9 @@ void TRestComponentDataSet::PrintMetadata() { void TRestComponentDataSet::PrintStatistics() { if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); - if (!IsDataSetLoaded()) { - RESTWarning << "TRestComponentDataSet::PrintStatistics. No dataset loaded." << RESTendl; + if (!HasNodes() && !IsDataSetLoaded()) { + RESTWarning << "TRestComponentDataSet::PrintStatistics. Empty nodes and no dataset loaded!" + << RESTendl; RESTWarning << "Invoking TRestComponentDataSet::LoadDataSets might solve the problem" << RESTendl; return; } @@ -241,6 +242,8 @@ void TRestComponentDataSet::FillHistograms() { int nIndex = 0; for (const auto& node : fParameterizationNodes) { ROOT::RDF::RNode df = ROOT::RDataFrame(0); + //// Yet not tested in the case when we want to define a unique node without filters + //// Needs to be improved if (fParameterizationNodes.size() == 1 && node == -137) { RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; df = fDataSet.GetDataFrame(); @@ -322,7 +325,7 @@ THnD* TRestComponentDataSet::GetDensityForActiveNode() { /// provided in the argument /// TH1D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName) { - if (!ValidDataSet()) return nullptr; + if (!ValidNode(node)) return nullptr; Int_t v1 = GetVariableIndex(varName); @@ -337,7 +340,7 @@ TH1D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName) { /// the active node. /// TH1D* TRestComponentDataSet::GetHistogram(std::string varName) { - if (!ValidDataSet()) return nullptr; + if (fActiveNode < 0) return nullptr; Int_t v1 = GetVariableIndex(varName); @@ -351,7 +354,7 @@ TH1D* TRestComponentDataSet::GetHistogram(std::string varName) { /// provided in the argument /// TH2D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2) { - if (!ValidDataSet()) return nullptr; + if (!ValidNode(node)) return nullptr; Int_t v1 = GetVariableIndex(varName1); Int_t v2 = GetVariableIndex(varName2); @@ -367,7 +370,7 @@ TH2D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, s /// the active node. /// TH2D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2) { - if (!ValidDataSet()) return nullptr; + if (fActiveNode < 0) return nullptr; Int_t v1 = GetVariableIndex(varName1); Int_t v2 = GetVariableIndex(varName2); @@ -384,7 +387,7 @@ TH2D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varN /// TH3D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3) { - if (!ValidDataSet()) return nullptr; + if (!ValidNode(node)) return nullptr; Int_t v1 = GetVariableIndex(varName1); Int_t v2 = GetVariableIndex(varName2); @@ -401,7 +404,7 @@ TH3D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, s /// the active node. /// TH3D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { - if (!ValidDataSet()) return nullptr; + if (fActiveNode < 0) return nullptr; Int_t v1 = GetVariableIndex(varName1); Int_t v2 = GetVariableIndex(varName2); @@ -480,8 +483,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } @@ -561,7 +563,7 @@ Bool_t TRestComponentDataSet::WeightsOk() { /// Bool_t TRestComponentDataSet::ValidDataSet() { if (!IsDataSetLoaded()) { - RESTWarning << "TRestComponentDataSet::GetRate. Dataset has not been loaded" << RESTendl; + RESTWarning << "TRestComponentDataSet::ValidDataSet. Dataset has not been loaded" << RESTendl; RESTWarning << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; RESTInfo << "Trying to load datasets" << RESTendl; @@ -575,7 +577,7 @@ Bool_t TRestComponentDataSet::ValidDataSet() { } if (HasNodes() && fActiveNode == -1) { - RESTError << "TRestComponentDataSet::GetRate. Active node has not been defined" << RESTendl; + RESTError << "TRestComponentDataSet::ValidDataSet. Active node has not been defined" << RESTendl; return false; } return true; From e59e5004baa8999a4195a7ce5ae063603da7b5a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:20:49 +0000 Subject: [PATCH 051/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/src/TRestRun.cxx | 33 ++++++++++++------- .../sensitivity/src/TRestComponentDataSet.cxx | 3 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index eb01804e0..af011ab61 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -148,7 +148,8 @@ void TRestRun::InitFromConfigFile() { } if (ToUpper(runNstr) == "AUTO" && ToUpper(inputName) == "AUTO") { RESTError << "TRestRun: run number and input file name cannot both be " - "\"AUTO\"" << RESTendl; + "\"AUTO\"" + << RESTendl; exit(1); } @@ -272,7 +273,8 @@ void TRestRun::InitFromConfigFile() { ImportMetadata(e->Attribute("file"), e->Attribute("name"), e->Attribute("type"), true); } else { RESTWarning << "Wrong definition of addMetadata! Metadata name or file name " - "is not given!" << RESTendl; + "is not given!" + << RESTendl; } } else if (Count(key, "TRest") > 0) { if (e->Attribute("file") != nullptr && TRestTools::isRootFile(e->Attribute("file"))) { @@ -402,7 +404,8 @@ void TRestRun::OpenInputFile(const TString& filename, const string& mode) { ReadInputFileMetadata(); } else { RESTWarning << "-- W : The metadata version found on input file is lower " - "than 2.2.1!" << RESTendl; + "than 2.2.1!" + << RESTendl; RESTWarning << "-- W : metadata from input file will not be read" << RESTendl; } } @@ -603,10 +606,12 @@ void TRestRun::ReadInputFileTrees() { if (fInputEvent == nullptr) { RESTError << "TRestRun:OpenInputFile. Cannot initialize input event, event " - "tree not read" << RESTendl; + "tree not read" + << RESTendl; RESTError << "Please install corresponding libraries to provide root dictionaries for " - "class reading." << RESTendl; + "class reading." + << RESTendl; return; } @@ -621,7 +626,8 @@ void TRestRun::ReadInputFileTrees() { string brname = (string)fInputEvent->ClassName() + "Branch"; if (fEventTree->GetBranch(brname.c_str()) == nullptr) { RESTWarning << "REST WARNING (OpenInputFile) : No matched event branch " - "inside file : " << filename << RESTendl; + "inside file : " + << filename << RESTendl; RESTWarning << "Branch required: " << brname << RESTendl; } else { fEventTree->SetBranchAddress(brname.c_str(), &fInputEvent); @@ -830,7 +836,8 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetEvent, TRestAnalysisTree* targetT if (fEventTree->IsA() == TChain::Class()) { Long64_t entry = fEventTree->LoadTree(fCurrentEvent); fBytesRead += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( - fEventBranchLoc))->GetEntry(entry); + fEventBranchLoc)) + ->GetEntry(entry); } else { fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) @@ -1256,7 +1263,8 @@ void TRestRun::SetInputEvent(TRestEvent* event) { break; } else if (i == branches->GetLast()) { RESTWarning << "REST Warning : (TRestRun) cannot find corresponding " - "branch in event tree!" << RESTendl; + "branch in event tree!" + << RESTendl; RESTWarning << "Event Type : " << event->ClassName() << RESTendl; RESTWarning << "Input event not set!" << RESTendl; } @@ -1304,7 +1312,8 @@ void TRestRun::ImportMetadata(const TString& File, const TString& name, const TS // TODO give error in case we try to obtain a class that is not TRestMetadata if (type == "" && name == "") { RESTError << "(ImportMetadata) : metadata type and name is not " - "specified!" << RESTendl; + "specified!" + << RESTendl; return; } @@ -1571,7 +1580,8 @@ TRestMetadata* TRestRun::GetMetadataClass(const TString& type, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadataClass() : The object to import is " - "not inherited from TRestMetadata" << RESTendl; + "not inherited from TRestMetadata" + << RESTendl; } } } @@ -1601,7 +1611,8 @@ TRestMetadata* TRestRun::GetMetadata(const TString& name, TFile* file) { return metadata; } else { RESTWarning << "TRestRun::GetMetadata() : The object to import is not " - "inherited from TRestMetadata" << RESTendl; + "inherited from TRestMetadata" + << RESTendl; } } } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 06fed1918..e44257729 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -483,7 +483,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From d9e4beeccd86ddc94021e4c100e1b80a2efa6e4e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 25 Nov 2023 21:02:33 +0100 Subject: [PATCH 052/174] TRestComponentDataSet::GetHistogram methods have been simplified --- .../sensitivity/src/TRestComponentDataSet.cxx | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 06fed1918..5d560c19f 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -325,13 +325,8 @@ THnD* TRestComponentDataSet::GetDensityForActiveNode() { /// provided in the argument /// TH1D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName) { - if (!ValidNode(node)) return nullptr; - - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0) return GetDensityForNode(node)->Projection(v1); - - return nullptr; + SetActiveNode(node); + return GetHistogram(varName); } ///////////////////////////////////////////// @@ -354,14 +349,8 @@ TH1D* TRestComponentDataSet::GetHistogram(std::string varName) { /// provided in the argument /// TH2D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2) { - if (!ValidNode(node)) return nullptr; - - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) return GetDensityForNode(node)->Projection(v1, v2); - - return nullptr; + SetActiveNode(node); + return GetHistogram(varName1, varName2); } ///////////////////////////////////////////// @@ -387,15 +376,8 @@ TH2D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varN /// TH3D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3) { - if (!ValidNode(node)) return nullptr; - - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) return GetDensityForNode(node)->Projection(v1, v2, v3); - - return nullptr; + SetActiveNode(node); + return GetHistogram(varName1, varName2, varName3); } ///////////////////////////////////////////// From 3164d778f22dc7eb542f680b25ffbd0e6a9b0bc5 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 27 Nov 2023 14:06:39 +0100 Subject: [PATCH 053/174] TRestComponentDataSet. Final version with Nsim normalization and weights --- .../sensitivity/inc/TRestComponentDataSet.h | 4 +- .../sensitivity/src/TRestComponent.cxx | 6 +- .../sensitivity/src/TRestComponentDataSet.cxx | 59 ++++++++----------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index f3bbaf734..c621cd9e0 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -34,8 +34,8 @@ class TRestComponentDataSet : public TRestComponent { /// A list with the dataset columns used to weight the distribution density and define rate std::vector fWeights; //< - /// It defines the statistics of each parameterization node (Initialized by the dataset) - std::vector fNodeStatistics; //< + /// It defines the number of entries for each parameterization node (Initialized by the dataset) + std::vector fNSimPerNode; //< /// The filename of the dataset used std::vector fDataSetFileNames; //< diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index dd846bd2f..e18291e02 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -106,12 +106,14 @@ void TRestComponent::PrintMetadata() { RESTWarning << "The number of variables does not match with the number of defined ranges!" << RESTendl; - else if (!fVariables.empty()) { + else if (fVariables.size() != fNbins.size()) + RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; + else { int n = 0; RESTMetadata << " === Variables === " << RESTendl; for (const auto& varName : fVariables) { RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() - << ")" << RESTendl; + << ") bins: " << fNbins[n] << RESTendl; n++; } } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 5d560c19f..3d05f9977 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -156,28 +156,20 @@ Double_t TRestComponentDataSet::GetTotalRate() { void TRestComponentDataSet::PrintMetadata() { TRestComponent::PrintMetadata(); - if (fVariables.size() != fRanges.size()) - RESTWarning << "The number of variables does not match with the number of defined ranges!" - << RESTendl; - - else if (fVariables.size() != fNbins.size()) - RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl; - else { - int n = 0; - RESTMetadata << " === Variables === " << RESTendl; - for (const auto& varName : fVariables) { - RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y() - << ") bins: " << fNbins[n] << RESTendl; - n++; - } - } - - RESTMetadata << "----" << RESTendl; - if (!fParameter.empty() && fParameterizationNodes.empty()) { RESTMetadata << "This component has no nodes!" << RESTendl; RESTMetadata << " Use: LoadDataSets() to initialize the nodes" << RESTendl; } + + if (!fWeights.empty()) { + RESTMetadata << " " << RESTendl; + RESTMetadata << " == Weights ==" << RESTendl; + + for (const auto& x : fWeights) RESTMetadata << "- " << x << RESTendl; + + RESTMetadata << " " << RESTendl; + } + RESTMetadata << " Use : PrintStatistics() to check node statistics" << RESTendl; RESTMetadata << "----" << RESTendl; } @@ -186,7 +178,7 @@ void TRestComponentDataSet::PrintMetadata() { /// \brief It prints out the statistics available for each parametric node /// void TRestComponentDataSet::PrintStatistics() { - if (fNodeStatistics.empty() && IsDataSetLoaded()) fNodeStatistics = ExtractNodeStatistics(); + if (fNSimPerNode.empty() && IsDataSetLoaded()) fNSimPerNode = ExtractNodeStatistics(); if (!HasNodes() && !IsDataSetLoaded()) { RESTWarning << "TRestComponentDataSet::PrintStatistics. Empty nodes and no dataset loaded!" @@ -195,14 +187,14 @@ void TRestComponentDataSet::PrintStatistics() { return; } - auto result = std::accumulate(fNodeStatistics.begin(), fNodeStatistics.end(), 0); + auto result = std::accumulate(fNSimPerNode.begin(), fNSimPerNode.end(), 0); std::cout << "Total counts : " << result << std::endl; std::cout << std::endl; std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; int n = 0; for (const auto& p : fParameterizationNodes) { - std::cout << " - Value : " << p << " Counts: " << fNodeStatistics[n] << std::endl; + std::cout << " - Value : " << p << " Counts: " << fNSimPerNode[n] << std::endl; n++; } } @@ -225,7 +217,7 @@ void TRestComponentDataSet::InitFromConfigFile() { /// variables and the weights for each of the parameter nodes. /// void TRestComponentDataSet::FillHistograms() { - fNodeStatistics = ExtractNodeStatistics(); + fNSimPerNode = ExtractNodeStatistics(); if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::FillHistograms. Dataset has not been initialized!" << RESTendl; @@ -270,21 +262,20 @@ void TRestComponentDataSet::FillHistograms() { std::vector varsAndWeight = fVariables; - /* if (!fWeights.empty()) { - std::string weightsStr = ""; - for (size_t n = 0; n < fWeights.size(); n++) { - if (n > 0) weightsStr += "*"; - - weightsStr += fWeights[n]; - } - df = df.Define("componentWeight", weightsStr); - varsAndWeight.push_back("componentWeight"); + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); } - */ auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); THnD* hNd = new THnD(*hn); + hNd->Scale(1. / fNSimPerNode[nIndex]); fNodeDensity.push_back(hNd); fActiveNode = nIndex; @@ -434,11 +425,11 @@ std::vector TRestComponentDataSet::ExtractParameterizationNodes() { /// \brief It returns a vector with the number of entries found for each /// parameterization node. /// -/// If fNodeStatistics has already been initialized it will +/// If fNSimPerNode has already been initialized it will /// directly return its value. /// std::vector TRestComponentDataSet::ExtractNodeStatistics() { - if (!fNodeStatistics.empty()) return fNodeStatistics; + if (!fNSimPerNode.empty()) return fNSimPerNode; std::vector stats; if (!IsDataSetLoaded()) { From c66016e20d55a9d2bfe5a6ffa764cad44d455749 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 28 Nov 2023 11:21:45 +0100 Subject: [PATCH 054/174] TRestComponentDataSet::DrawComponent adding method to visualize density distributions --- .../sensitivity/inc/TRestComponent.h | 4 ++ .../sensitivity/inc/TRestComponentDataSet.h | 3 + .../sensitivity/src/TRestComponentDataSet.cxx | 67 ++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 5bbdbbc58..f852fd6f5 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -24,6 +24,7 @@ #define REST_TRestComponent #include +#include #include "TRestDataSet.h" #include "TRestMetadata.h" @@ -49,6 +50,9 @@ class TRestComponent : public TRestMetadata { /// It is used to define the node that will be accessed for rate retrieval Int_t fActiveNode = -1; //< + /// A canvas for drawing the active node component + TCanvas* fCanvas = nullptr; //! + /// It returns true if any nodes have been defined. Bool_t HasNodes() { return !fParameterizationNodes.empty(); } diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index c621cd9e0..b6b1cafd6 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -75,6 +75,9 @@ class TRestComponentDataSet : public TRestComponent { Double_t GetRate(std::vector point) override; Double_t GetTotalRate() override; + TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, + Int_t reduction = 1); + THnD* GetDensityForNode(Double_t value); THnD* GetDensityForActiveNode(); THnD* GetDensity() { return GetDensityForActiveNode(); } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index f82a6d573..47bd14716 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -150,12 +150,76 @@ Double_t TRestComponentDataSet::GetTotalRate() { return integral; } +/////////////////////////////////////////////// +/// \brief +/// +TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVariables, + std::vector scanVariables, Int_t reduction) { + if (drawVariables.size() > 2) { + RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be drawn cannot " + "be more than 2!" << RESTendl; + return fCanvas; + } + + if (scanVariables.size() > 2) { + RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be scanned " + "cannot be more than 2!" << RESTendl; + return fCanvas; + } + + if (scanVariables.size() == 0) { + RESTError << "TRestComponentDataSet::DrawComponent. Needs at least one scan variable!" << RESTendl; + return fCanvas; + } + + if (drawVariables.size() == 0) { + RESTError << "TRestComponentDataSet::DrawComponent. Needs at least one variable to be drawn!" + << RESTendl; + return fCanvas; + } + + // Initializing canvas window + if (fCanvas != nullptr) { + delete fCanvas; + fCanvas = nullptr; + } + + std::vector scanIndexes; + for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x)); + + std::vector variableIndexes; + for (const auto& x : drawVariables) variableIndexes.push_back(GetVariableIndex(x)); + + if (scanIndexes.size() == 1) { + std::vector canvasDivisions = TRestTools::CanvasDivisions(fNbins[scanIndexes[0]]); + + std::cout << "- " << + } else if (scanIndexes.size() == 2) { + } + + fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, 640, 480); + /* + fCanvas->Divide((Int_t)fCanvasDivisions.X(), (Int_t)fCanvasDivisions.Y(), + fCanvasDivisionMargins.X(), fCanvasDivisionMargins.Y()); + */ + return fCanvas; +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// void TRestComponentDataSet::PrintMetadata() { TRestComponent::PrintMetadata(); + if (!fDataSetFileNames.empty()) { + RESTMetadata << " " << RESTendl; + RESTMetadata << " == Dataset filenames ==" << RESTendl; + + for (const auto& x : fDataSetFileNames) RESTMetadata << "- " << x << RESTendl; + + RESTMetadata << " " << RESTendl; + } + if (!fParameter.empty() && fParameterizationNodes.empty()) { RESTMetadata << "This component has no nodes!" << RESTendl; RESTMetadata << " Use: LoadDataSets() to initialize the nodes" << RESTendl; @@ -456,8 +520,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 86d28a67b94bd8872cfbc53dc8e21426644b03cb Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 28 Nov 2023 11:25:48 +0100 Subject: [PATCH 055/174] TRestTools::CanvasDivisions method added --- source/framework/tools/inc/TRestTools.h | 12 +++-- source/framework/tools/src/TRestTools.cxx | 63 ++++++++++++++++++++--- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 58836b4bf..5cf0c71d1 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void)x +#define UNUSED(x) (void) x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -131,6 +131,8 @@ class TRestTools { static std::string POSTRequest(const std::string& url, const std::map& keys); static void ChangeDirectory(const std::string& toDirectory); + + static std::vector CanvasDivisions(int n); }; namespace REST_InitTools { @@ -179,10 +181,14 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { ENERGY, LENGTH, TIME }; +enum Quantities { + ENERGY, + LENGTH, + TIME +}; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 0af3237fa..75167c436 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -819,15 +819,13 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" - << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" - << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; exit(1); } path /= userHomePath; @@ -1032,7 +1030,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof(): + case std::streambuf::traits_type::eof() : // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1237,8 +1235,7 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" - << RESTendl; + "http://gifna.unizar.es/rest-forum/" << RESTendl; return -1; } @@ -1249,6 +1246,58 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho void TRestTools::ChangeDirectory(const string& toDirectory) { filesystem::current_path(toDirectory); } +/////////////////////////////////////////////// +/// \brief It returns a vector with 2 components {a,b}, the components satisfy that `a x b = n`, +/// being the ratio a/b as close to 1 as possible. +/// +/// This method can be used to help dividing a canvas that will contain a number `n` of plots. +/// +/// If `n` is a prime number, then the pair generated will be `n x 1`. +/// +std::vector TRestTools::CanvasDivisions(int n) { + std::vector r; + for (int i = 2; i * i <= n; i += 1 + (i > 2)) { + while ((n % i) == 0) { + r.push_back(i); + n /= i; + } + } + if (n != 1) r.push_back(n); + + while (r.size() > 2) { + // We multiply the 2 lowest elements and + // replace the elements in the vector by the result + auto min1 = std::min_element(r.begin(), r.end()); + int low1 = *min1; + + // Remove the first element equal to min1 (efficient way) + auto it = std::find(r.begin(), r.end(), low1); + if (it != r.end()) { + std::iter_swap(it, r.end() - 1); + r.erase(r.end() - 1); + } + + auto min2 = std::min_element(r.begin(), r.end()); + int low2 = *min2; + + // Remove the first element equal to min2 (efficient way) + it = std::find(r.begin(), r.end(), low2); + if (it != r.end()) { + std::iter_swap(it, r.end() - 1); + r.erase(r.end() - 1); + } + + int resultado = low1 * low2; + r.push_back(resultado); + } + + std::sort(r.begin(), r.end()); + + if (r.size() == 1) r.push_back(1); + + return r; +} + string ValueWithQuantity::ToString() const { string unit; auto value = fValue; From 0032383ffbd38f5927556ad0cf56153d2a4cf7a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:26:08 +0000 Subject: [PATCH 056/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 2 +- .../framework/sensitivity/src/TRestComponentDataSet.cxx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index f852fd6f5..91d756e61 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,8 +23,8 @@ #ifndef REST_TRestComponent #define REST_TRestComponent -#include #include +#include #include "TRestDataSet.h" #include "TRestMetadata.h" diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 47bd14716..38552fda7 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -157,13 +157,15 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria std::vector scanVariables, Int_t reduction) { if (drawVariables.size() > 2) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be drawn cannot " - "be more than 2!" << RESTendl; + "be more than 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be scanned " - "cannot be more than 2!" << RESTendl; + "cannot be more than 2!" + << RESTendl; return fCanvas; } @@ -520,7 +522,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 399f712350c0281bb1b89636fa865bc20babf09f Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 1 Dec 2023 22:16:39 +0100 Subject: [PATCH 057/174] TRestComponentDataSet::DrawComponent final implementation --- .../sensitivity/inc/TRestComponentDataSet.h | 2 +- .../sensitivity/src/TRestComponentDataSet.cxx | 147 ++++++++++++++---- 2 files changed, 120 insertions(+), 29 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index b6b1cafd6..89f93d0d1 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -76,7 +76,7 @@ class TRestComponentDataSet : public TRestComponent { Double_t GetTotalRate() override; TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, - Int_t reduction = 1); + Int_t binScanSize = 1, TString drawOption = ""); THnD* GetDensityForNode(Double_t value); THnD* GetDensityForActiveNode(); diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 47bd14716..61577b42b 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -42,7 +42,8 @@ #include -#include "TKey.h" +#include +#include ClassImp(TRestComponentDataSet); @@ -154,54 +155,144 @@ Double_t TRestComponentDataSet::GetTotalRate() { /// \brief /// TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVariables, - std::vector scanVariables, Int_t reduction) { - if (drawVariables.size() > 2) { - RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be drawn cannot " - "be more than 2!" << RESTendl; + std::vector scanVariables, Int_t binScanSize, + TString drawOption) { + if (drawVariables.size() > 2 || drawVariables.size() == 0) { + RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " + "be 1 or 2!" << RESTendl; return fCanvas; } - if (scanVariables.size() > 2) { - RESTError << "TRestComponentDataSet::DrawComponent. The number of variables that can be scanned " - "cannot be more than 2!" << RESTendl; + if (scanVariables.size() > 2 || scanVariables.size() == 0) { + RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " + "be 1 or 2!" << RESTendl; return fCanvas; } - if (scanVariables.size() == 0) { - RESTError << "TRestComponentDataSet::DrawComponent. Needs at least one scan variable!" << RESTendl; - return fCanvas; + //// Checking the number of plots to be generated + std::vector scanIndexes; + for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x)); + + Int_t nPlots = 1; + size_t n = 0; + for (const auto& x : scanIndexes) { + if (fNbins[x] % binScanSize != 0) { + RESTWarning << "The variable " << scanVariables[n] << " contains " << fNbins[x] + << " bins and it doesnt match with a bin size " << binScanSize << RESTendl; + RESTWarning << "The bin size must be a multiple of the number of bins." << RESTendl; + RESTWarning << "Redefining bin size to 1." << RESTendl; + binScanSize = 1; + } + nPlots *= fNbins[x] / binScanSize; + n++; } - if (drawVariables.size() == 0) { - RESTError << "TRestComponentDataSet::DrawComponent. Needs at least one variable to be drawn!" - << RESTendl; - return fCanvas; + /// Finding canvas division scheme + Int_t nPlotsX = 0; + Int_t nPlotsY = 0; + + if (scanIndexes.size() == 2) { + nPlotsX = fNbins[scanIndexes[0]] / binScanSize; + nPlotsY = fNbins[scanIndexes[1]] / binScanSize; + } else { + nPlotsX = TRestTools::CanvasDivisions(nPlots)[1]; + nPlotsY = TRestTools::CanvasDivisions(nPlots)[0]; } - // Initializing canvas window + RESTInfo << "Number of plots to be generated: " << nPlots << RESTendl; + RESTInfo << "Canvas size : " << nPlotsX << " x " << nPlotsY << RESTendl; + + //// Setting up the canvas with the appropriate number of divisions if (fCanvas != nullptr) { delete fCanvas; fCanvas = nullptr; } - std::vector scanIndexes; - for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x)); + fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, nPlotsX * 640, nPlotsY * 480); + fCanvas->Divide(nPlotsX, nPlotsY, 0.01, 0.01); std::vector variableIndexes; for (const auto& x : drawVariables) variableIndexes.push_back(GetVariableIndex(x)); - if (scanIndexes.size() == 1) { - std::vector canvasDivisions = TRestTools::CanvasDivisions(fNbins[scanIndexes[0]]); + for (int n = 0; n < nPlotsX; n++) + for (int m = 0; m < nPlotsY; m++) { + fCanvas->cd(n * nPlotsY + m + 1); - std::cout << "- " << - } else if (scanIndexes.size() == 2) { - } + THnD* hnd = GetDensity(); + + int binXo = binScanSize * n + 1; + int binXf = binScanSize * n + binScanSize; + int binYo = binScanSize * m + 1; + int binYf = binScanSize * m + binScanSize; + + if (scanVariables.size() == 2) { + hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); + hnd->GetAxis(scanIndexes[1])->SetRange(binYo, binYf); + } else if (scanVariables.size() == 1) { + binXo = binScanSize * nPlotsY * n + binScanSize * m + 1; + binXf = binScanSize * nPlotsY * n + binScanSize * m + binScanSize; + hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); + } + + if (variableIndexes.size() == 1) { + TH1D* h1 = hnd->Projection(variableIndexes[0]); + std::string hName; + + if (scanIndexes.size() == 2) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + + IntegerToString(binYf) + ") "; + + if (scanIndexes.size() == 1) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") "; + + TH1D* newh = (TH1D*)h1->Clone(hName.c_str()); + newh->SetTitle(hName.c_str()); + newh->SetStats(false); + newh->GetXaxis()->SetTitle((TString)drawVariables[0]); + newh->SetMarkerStyle(kFullCircle); + newh->Draw("PLC PMC"); + + TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); + TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); + textLatex->SetNDC(); + textLatex->SetTextColor(1); + textLatex->SetTextSize(0.05); + textLatex->Draw("same"); + delete h1; + } + + if (variableIndexes.size() == 2) { + TH2D* h2 = hnd->Projection(variableIndexes[0], variableIndexes[1]); + + std::string hName; + if (scanIndexes.size() == 2) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + + IntegerToString(binYf) + ") "; + + if (scanIndexes.size() == 1) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") "; + + TH2D* newh = (TH2D*)h2->Clone(hName.c_str()); + newh->SetStats(false); + newh->GetXaxis()->SetTitle((TString)drawVariables[0]); + newh->GetYaxis()->SetTitle((TString)drawVariables[1]); + newh->SetTitle(hName.c_str()); + newh->Draw(drawOption); + + TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); + TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); + textLatex->SetNDC(); + textLatex->SetTextColor(1); + textLatex->SetTextSize(0.05); + textLatex->Draw("same"); + delete h2; + } + } - fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, 640, 480); - /* - fCanvas->Divide((Int_t)fCanvasDivisions.X(), (Int_t)fCanvasDivisions.Y(), - fCanvasDivisionMargins.X(), fCanvasDivisionMargins.Y()); - */ return fCanvas; } From c354aebad45128b705d22a52a7369c327904b6da Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 1 Dec 2023 23:02:17 +0100 Subject: [PATCH 058/174] TRestComponentDataSet. Adding some documentation parts --- doc/doxygen/images/component_hitmap.png | Bin 0 -> 279670 bytes doc/doxygen/images/component_spectra.png | Bin 0 -> 186027 bytes .../sensitivity/src/TRestComponentDataSet.cxx | 48 +++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 doc/doxygen/images/component_hitmap.png create mode 100644 doc/doxygen/images/component_spectra.png diff --git a/doc/doxygen/images/component_hitmap.png b/doc/doxygen/images/component_hitmap.png new file mode 100644 index 0000000000000000000000000000000000000000..ba18c0a1c3e3d695772ff8ce77ba0eff67e57fa4 GIT binary patch literal 279670 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ೠQ{`= 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¨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ξn~=or9va+ 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�CZ; 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 literal 0 HcmV?d00001 diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 61577b42b..c7357e0a4 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -21,8 +21,52 @@ *************************************************************************/ ///////////////////////////////////////////////////////////////////////// -/// This class allows to ... -/// +/// This class ... +/// +/// \code +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// \endcode +/// +/// \code +/// restRoot +/// [0] TRestComponentDataSet comp("components.rml", "agSignal_vacuum"); +/// [1] comp.LoadDataSets() +/// [2] TFile *f = TFile::Open("vacuumComponent.root", "RECREATE"); +/// [3] comp.Write("agSignal_vacuum"); +/// \endcode +/// +/// \code +/// restRoot vacuumComponents.root +/// [0] TCanvas *c = agVacuum->DrawComponent( { "final_posX", "final_posY"}, {"final_energy"}, 2); +/// [1] c->Print("component_hitmaps.png"); +/// \endcode +/// +/// \htmlonly \endhtmlonly +/// ![A 2-dimensional histogram scan versus the `final_energy` observable, generated by the DrawComponent +/// method](component_hitmap.png) +/// +/// \code +/// restRoot vacuumComponents.root +/// [0] TCanvas *c = agVacuum->DrawComponent( { "final_energy"}, {"final_posX", "final_posY"}, 2); +/// [1] c->Print("component_hitmaps.png"); +/// \endcode +/// +/// In both cases each plot will regroup 2 bins. +/// +/// \htmlonly \endhtmlonly +/// ![A 1-dimensional histogram scan versus the `final_posX` and `final_posY` observables, generated by the +/// DrawComponent method](component_spectra.png) /// ///---------------------------------------------------------------------- /// From 9a1c33897734d1e59fd039507f1c333fb8b3665c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:04:12 +0000 Subject: [PATCH 059/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../sensitivity/src/TRestComponentDataSet.cxx | 13 ++++++++----- source/framework/tools/inc/TRestTools.h | 10 +++------- source/framework/tools/src/TRestTools.cxx | 11 +++++++---- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index c7357e0a4..d05ffa15f 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -84,11 +84,11 @@ /// #include "TRestComponentDataSet.h" -#include - #include #include +#include + ClassImp(TRestComponentDataSet); /////////////////////////////////////////////// @@ -203,13 +203,15 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } @@ -655,7 +657,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 5cf0c71d1..5ce2e2aac 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void) x +#define UNUSED(x) (void)x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -181,14 +181,10 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { - ENERGY, - LENGTH, - TIME -}; +enum Quantities { ENERGY, LENGTH, TIME }; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 75167c436..9b8f3a458 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -819,13 +819,15 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" + << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" + << endl; exit(1); } path /= userHomePath; @@ -1030,7 +1032,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof() : + case std::streambuf::traits_type::eof(): // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1235,7 +1237,8 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" << RESTendl; + "http://gifna.unizar.es/rest-forum/" + << RESTendl; return -1; } From b30707d105c4140b9c9ff6e9ed71b46fbd5abf42 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 11:49:52 +0100 Subject: [PATCH 060/174] TRestComponent::GetDimensions method added --- source/framework/sensitivity/inc/TRestComponent.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 91d756e61..606f9d303 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -71,6 +71,8 @@ class TRestComponent : public TRestMetadata { virtual Double_t GetRate(std::vector point) = 0; virtual Double_t GetTotalRate() = 0; + size_t GetDimensions() { return fVariables.size(); } + Int_t SetActiveNode(Double_t node); Int_t GetActiveNode() { return fActiveNode; } Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } From 886fbde00b2cc48464a2eb1ee140aa9607af6d4e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 13:47:28 +0100 Subject: [PATCH 061/174] TRestComponent::GetNormalizedRate method added --- .../framework/sensitivity/inc/TRestComponent.h | 14 +------------- .../sensitivity/src/TRestComponent.cxx | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 606f9d303..ffaa0d3c3 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -68,6 +68,7 @@ class TRestComponent : public TRestMetadata { void InitFromConfigFile() override; public: + Double_t GetNormalizedRate(std::vector point); virtual Double_t GetRate(std::vector point) = 0; virtual Double_t GetTotalRate() = 0; @@ -77,19 +78,6 @@ class TRestComponent : public TRestMetadata { Int_t GetActiveNode() { return fActiveNode; } Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } - /* -THnD* GetDensityForNode(Double_t value); -THnD* GetDensityForActiveNode(); - -TH1D* GetHistogram(Double_t node, std::string varName); -TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); -TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); - -TH1D* GetHistogram(std::string varName); -TH2D* GetHistogram(std::string varName1, std::string varName2); -TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); - */ - void PrintMetadata() override; void PrintStatistics(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index e18291e02..54562dc70 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -96,6 +96,24 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { return -1; } +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The rate returned by the TRestComponent::GetRate method will be normalized +/// to the corresponding parameter space. Thus, if the parameter consists of +/// 2-spatial dimensions and 1-energy dimension, the returned rate will be +/// expressed in standard REST units as, s-1 mm-2 keV-1. +/// +Double_t TRestComponent::GetNormalizedRate(std::vector point) { + + Double_t normFactor = 1; + for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); + + return normFactor * GetRate(point); +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// From 63cdd79f5ab7d0022050566d94af74d108acf6f1 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 13:48:30 +0100 Subject: [PATCH 062/174] TRestComponentDataSet. Implementing interpolation into GetRate and other improvements --- .../sensitivity/src/TRestComponentDataSet.cxx | 106 ++++++++++++++---- 1 file changed, 86 insertions(+), 20 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index d05ffa15f..0146c62f6 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -139,17 +139,30 @@ void TRestComponentDataSet::Initialize() { /// generated distribution or formula evaluated at the position of the parameter /// space given by point. /// -/// The density should be normalized to the corresponding parameter space. During -/// the component construction, **the user is responsible** to initialize the component -/// with the appropriate units. For example, if the parameter space is 2 spatial -/// dimensions and 1 energy dimension, the contribution of each cell or event to -/// the component will be expressed in mm-2 keV-1 which are the default units for -/// distance and energy. +/// The rate will be normalized to the corresponding parameter space. Thus, if +/// the parameter consists of 2-spatial dimensions and 1-energy dimension, the +/// returned rate will be expressed in standard REST units as, s-1 mm-2 keV-1. /// /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. /// +/// If interpolation is enabled (which is the default) the rate will be evaluated +/// using interpolation with neighbor histogram cells. +/// +/// Interpolation technique extracted from: +/// https://math.stackexchange.com/questions/1342364/formula-for-n-dimensional-linear-interpolation +/// +/// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥 +/// Double_t TRestComponentDataSet::GetRate(std::vector point) { + + if (point.size() != GetDimensions()) { + RESTError << "The size of the point given is : " << point.size() << RESTendl; + RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl; + RESTError << "Point must have the same dimensions as the distribution" << RESTendl; + return 0; + } + if (!HasNodes()) { RESTError << "TRestComponentDataSet::GetRate. The component has no nodes!" << RESTendl; RESTError << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; @@ -167,15 +180,55 @@ Double_t TRestComponentDataSet::GetRate(std::vector point) { return 0; } - Double_t density = - GetDensityForActiveNode()->GetBinContent(GetDensityForActiveNode()->GetBin(point.data())); + Int_t centerBin[GetDimensions()]; + Double_t centralDensity = GetDensity()->GetBinContent(GetDensity()->GetBin(point.data()), centerBin); + if (!Interpolation()) return centralDensity; + + std::vector direction; + std::vector nDist; + for (size_t dim = 0; dim < GetDimensions(); dim++) { + Double_t x1 = GetBinCenter(dim, centerBin[dim] - 1); + Double_t x2 = GetBinCenter(dim, centerBin[dim] + 1); + + if (centerBin[dim] == 1 || centerBin[dim] == fNbins[dim]) { + direction.push_back(0); + nDist.push_back(0); + } else if (x2 - point[dim] > point[dim] - x1) { + // we chose left bin (x1) since it is closer than right bin + direction.push_back(-1); + nDist.push_back(1 - 2 * (point[dim] - x1) / (x2 - x1)); + } else { + direction.push_back(1); + nDist.push_back(1 - 2 * (x2 - point[dim]) / (x2 - x1)); + } + } - Double_t norm = 1; + // In 3-dimensions we got 8 points to interpolate + // In 4-dimensions we would get 16 points to interpolate + // ... + Int_t nPoints = (Int_t)TMath::Power(2, (Int_t)GetDimensions()); + + Double_t sum = 0; + for (int n = 0; n < nPoints; n++) { + std::vector cell = TRestTools::IntegerToBinary(n, GetDimensions()); + + Double_t weightDistance = 1; + int cont = 0; + for (const auto& c : cell) { + if (c == 0) + weightDistance *= (1 - nDist[cont]); + else + weightDistance *= nDist[cont]; + cont++; + } - // Perhaps this value could be stored internally - // for (size_t n = 0; n < fNbins.size(); n++) norm = norm * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + for (size_t k = 0; k < cell.size(); k++) cell[k] = cell[k] * direction[k] + centerBin[k]; - return norm * density; + Double_t density = GetDensity()->GetBinContent(cell.data()); + sum += density * weightDistance; + } + + return sum; } /////////////////////////////////////////////// @@ -196,22 +249,35 @@ Double_t TRestComponentDataSet::GetTotalRate() { } /////////////////////////////////////////////// -/// \brief +/// \brief It returns the bin center of the given component dimension. +/// +/// It required implementation since I did not find a method inside THnD. Surprising. +/// +Double_t TRestComponentDataSet::GetBinCenter(Int_t nDim, const Int_t bin) { + return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim]; +} + +/////////////////////////////////////////////// +/// \brief A method allowing to draw a series of plots representing the density distributions. +/// +/// The method will produce 1- or 2-dimensional histograms of the `drawVariables` given in the +/// argument. A third scan variable must be provided in order to show the distribution slices +/// along the scan variable. +/// +/// The binScanSize argument can be used to define the binSize of the scanning variables. /// TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVariables, std::vector scanVariables, Int_t binScanSize, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -262,7 +328,8 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria for (int n = 0; n < nPlotsX; n++) for (int m = 0; m < nPlotsY; m++) { - fCanvas->cd(n * nPlotsY + m + 1); + TPad* pad = (TPad*)fCanvas->cd(n * nPlotsY + m + 1); + pad->SetFixedAspectRatio(true); THnD* hnd = GetDensity(); @@ -657,8 +724,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 3b05dc707fa523b8f240d6c1661867218a2ca9c4 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 13:50:03 +0100 Subject: [PATCH 063/174] TRestComponentDataSet. Adding interpolation --- .../sensitivity/inc/TRestComponentDataSet.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 89f93d0d1..6de1f4385 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -43,6 +43,9 @@ class TRestComponentDataSet : public TRestComponent { /// The generated N-dimensional variable space density for a given node std::vector fNodeDensity; //< + /// Enables or disables the interpolation at TRestComponentDataSet::GetRate + Bool_t fInterpolation = true; + /// TODO we need to define multiple datasets and weigth. The weight will be used /// to create a model, such as weighting different background contaminations or /// different signal coupling contributions. @@ -68,13 +71,17 @@ class TRestComponentDataSet : public TRestComponent { public: Bool_t LoadDataSets(); - - /// This method should go to TRestComponentDataSet Bool_t IsDataSetLoaded() { return fDataSetLoaded; } + Bool_t Interpolation() { return fInterpolation; } + void EnableInterpolation() { fInterpolation = true; } + void DisableInterpolation() { fInterpolation = false; } + Double_t GetRate(std::vector point) override; Double_t GetTotalRate() override; + Double_t GetBinCenter(Int_t nDim, const Int_t bin); + TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, Int_t binScanSize = 1, TString drawOption = ""); @@ -100,6 +107,6 @@ class TRestComponentDataSet : public TRestComponent { TRestComponentDataSet(const char* cfgFileName, const std::string& name); ~TRestComponentDataSet(); - ClassDefOverride(TRestComponentDataSet, 1); + ClassDefOverride(TRestComponentDataSet, 2); }; #endif From 9e12d89bfb07a8a38bc82d93ee7e212f4eec52f4 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 13:50:47 +0100 Subject: [PATCH 064/174] TRestStringHelper::IntegerToBinary method added --- .../framework/tools/inc/TRestStringHelper.h | 1 + .../framework/tools/src/TRestStringHelper.cxx | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/source/framework/tools/inc/TRestStringHelper.h b/source/framework/tools/inc/TRestStringHelper.h index 7e5f91ebc..c34c8cbcf 100644 --- a/source/framework/tools/inc/TRestStringHelper.h +++ b/source/framework/tools/inc/TRestStringHelper.h @@ -35,6 +35,7 @@ Float_t StringToFloat(std::string in); Double_t StringToDouble(std::string in); Int_t StringToInteger(std::string in); std::string IntegerToString(Int_t n, std::string format = "%d"); +std::vector IntegerToBinary(int number, size_t dimension = 0); std::string DoubleToString(Double_t d, std::string format = "%8.6e"); Bool_t StringToBool(std::string booleanString); Long64_t StringToLong(std::string in); diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 427991acb..30734efb8 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -633,6 +633,37 @@ Int_t REST_StringHelper::StringToInteger(string in) { /// string REST_StringHelper::IntegerToString(Int_t n, string format) { return Form(format.c_str(), n); } +/////////////////////////////////////////////// +/// \brief It returns an integer vector with the binary digits decomposed. +/// +/// Example: IntegerToBinary(7) will return { 1, 1, 1 }. +/// +/// Optionally we can fix the minimum number of digits to be returned, so that +/// it will be filled with zeros to the left. +/// +/// Example: IntegerToBinary(9,8) will return { 0, 0, 0, 0, 1, 0, 0, 1 }. +/// +std::vector REST_StringHelper::IntegerToBinary(int number, size_t dimension) { + std::vector binaryNumber; + + if (number == 0) { + binaryNumber.insert(binaryNumber.begin(), dimension, 0); + if (binaryNumber.empty()) binaryNumber.push_back(0); + return binaryNumber; + } + + while (number > 0) { + int digit = number % 2; + binaryNumber.insert(binaryNumber.begin(), digit); + number /= 2; + } + + if (dimension > binaryNumber.size()) + binaryNumber.insert(binaryNumber.begin(), dimension - binaryNumber.size(), 0); + + return binaryNumber; +} + /////////////////////////////////////////////// /// \brief Gets a string from a double /// From cc6e256cc107f762df74522e2eb828e4a2687d58 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 5 Dec 2023 13:58:17 +0100 Subject: [PATCH 065/174] TRestComponentDataSet. Updating to REST_StringHelper::IntegerToBinary method --- source/framework/sensitivity/src/TRestComponentDataSet.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 0146c62f6..5c68fcda6 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -210,7 +210,7 @@ Double_t TRestComponentDataSet::GetRate(std::vector point) { Double_t sum = 0; for (int n = 0; n < nPoints; n++) { - std::vector cell = TRestTools::IntegerToBinary(n, GetDimensions()); + std::vector cell = REST_StringHelper::IntegerToBinary(n, GetDimensions()); Double_t weightDistance = 1; int cont = 0; From c25dd2403f3bc0fc97798d1bcfc8a3cf0624e8b0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:58:54 +0000 Subject: [PATCH 066/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestComponent.cxx | 1 - .../sensitivity/src/TRestComponentDataSet.cxx | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 54562dc70..a23c36ae4 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -107,7 +107,6 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { /// expressed in standard REST units as, s-1 mm-2 keV-1. /// Double_t TRestComponent::GetNormalizedRate(std::vector point) { - Double_t normFactor = 1; for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 5c68fcda6..d35962398 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -155,7 +155,6 @@ void TRestComponentDataSet::Initialize() { /// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥 /// Double_t TRestComponentDataSet::GetRate(std::vector point) { - if (point.size() != GetDimensions()) { RESTError << "The size of the point given is : " << point.size() << RESTendl; RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl; @@ -271,13 +270,15 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } @@ -724,7 +725,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From fc1704a0dd7598e57aabfa840ced0259d2a09122 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Fri, 15 Dec 2023 08:49:27 +0100 Subject: [PATCH 067/174] TRestResponse implementation (WIP) --- .../sensitivity/inc/TRestComponent.h | 12 +- .../framework/sensitivity/inc/TRestResponse.h | 47 +++++- .../sensitivity/src/TRestComponent.cxx | 31 ++++ .../sensitivity/src/TRestResponse.cxx | 159 +++++++++++++++++- 4 files changed, 244 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index ffaa0d3c3..cf4801a84 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -26,6 +26,7 @@ #include #include +#include "TRestResponse.h" #include "TRestDataSet.h" #include "TRestMetadata.h" @@ -50,6 +51,9 @@ class TRestComponent : public TRestMetadata { /// It is used to define the node that will be accessed for rate retrieval Int_t fActiveNode = -1; //< + /// + TRestResponse* fResponse = nullptr; //< + /// A canvas for drawing the active node component TCanvas* fCanvas = nullptr; //! @@ -69,15 +73,19 @@ class TRestComponent : public TRestMetadata { public: Double_t GetNormalizedRate(std::vector point); + Double_t GetResponseRate(std::vector point); + virtual Double_t GetRate(std::vector point) = 0; virtual Double_t GetTotalRate() = 0; size_t GetDimensions() { return fVariables.size(); } - Int_t SetActiveNode(Double_t node); Int_t GetActiveNode() { return fActiveNode; } + Int_t SetActiveNode(Double_t node); Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } + void LoadResponse(const TRestResponse& resp); + void PrintMetadata() override; void PrintStatistics(); @@ -88,6 +96,6 @@ class TRestComponent : public TRestMetadata { TRestComponent(); ~TRestComponent(); - ClassDefOverride(TRestComponent, 1); + ClassDefOverride(TRestComponent, 2); }; #endif diff --git a/source/framework/sensitivity/inc/TRestResponse.h b/source/framework/sensitivity/inc/TRestResponse.h index a47d3d23c..4e3ba8bc3 100644 --- a/source/framework/sensitivity/inc/TRestResponse.h +++ b/source/framework/sensitivity/inc/TRestResponse.h @@ -25,16 +25,59 @@ #include "TRestMetadata.h" -/// A response matrix that might be applied to a given signal variable inside TRestResponse +/// A response matrix that might be applied to a given component inside a TRestComponent class TRestResponse : public TRestMetadata { private: - // TODO Add here the response matrix. Probably a TH2D + /// The filename used to import the response matrix + std::string fFilename = ""; + + /// It defines the variable name for which the response should be applied to + std::string fVariable = ""; + + /// First element of the response matrix (input/incident, output/detected) + TVector2 fOrigin = TVector2(0, 0); + + /// The resolution of the response matrix (binning) + Double_t fBinSize = 0.1; //< + + /// The response matrix + std::vector> fResponseMatrix; //< + + /// Determines if the response matrix has been transposed + Bool_t fTransposed = false; public: + void SetBinSize(Double_t bSize) { fBinSize = bSize; } + void SetResponseFilename(std::string responseFile) { fFilename = responseFile; } + void SetOrigin(const TVector2& v) { fOrigin = v; } + void SetVariable(const std::string& var) { fVariable = var; } + + Double_t GetBinSize() const { return fBinSize; } + std::string GetResponseFilename() const { return fFilename; } + TVector2 GetOrigin() const { return fOrigin; } + std::string GetVariable() const { return fVariable; } + + TVector2 GetInputRange() const { + return TVector2(fOrigin.X(), fOrigin.X() + fResponseMatrix[0].size() * fBinSize); + } + + TVector2 GetOutputRange() const { + return TVector2(fOrigin.Y(), fOrigin.Y() + fResponseMatrix.size() * fBinSize); + } + void Initialize() override; + void LoadResponse(Bool_t transpose = true); + + std::vector> GetResponse(Double_t input); + + void PrintResponseMatrix(Int_t fromRow, Int_t toRow); + void PrintMetadata() override; + std::vector> GetMatrix() const { return fResponseMatrix; } + + TRestResponse(const char* cfgFileName, const std::string& name = ""); TRestResponse(); ~TRestResponse(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index a23c36ae4..d5f5228af 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -113,6 +113,37 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { return normFactor * GetRate(point); } +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The rate returned by the TRestComponent::GetRate method will be normalized +/// to the corresponding parameter space. Thus, if the parameter consists of +/// 2-spatial dimensions and 1-energy dimension, the returned rate will be +/// expressed in standard REST units as, s-1 mm-2 keV-1. +/// +Double_t TRestComponent::GetResponseRate(std::vector point) { + Double_t normFactor = 1; + for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); + + return normFactor * GetRate(point); +} + +/////////////////////////////////////////////// +/// \brief +/// +void TRestComponent::LoadResponse(const TRestResponse& resp) { + if (fResponse) { + delete fResponse; + fResponse = nullptr; + } + + fResponse = (TRestResponse*)resp.Clone("response"); + + fResponse->PrintMetadata(); +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// diff --git a/source/framework/sensitivity/src/TRestResponse.cxx b/source/framework/sensitivity/src/TRestResponse.cxx index 7211db5b8..95d485097 100644 --- a/source/framework/sensitivity/src/TRestResponse.cxx +++ b/source/framework/sensitivity/src/TRestResponse.cxx @@ -30,7 +30,7 @@ /// /// History of developments: /// -/// 2022-December: First implementation of TRestResponse +/// 2023-December: First implementation of TRestResponse /// Javier Galan /// /// \class TRestResponse @@ -47,6 +47,28 @@ ClassImp(TRestResponse); /// TRestResponse::TRestResponse() { Initialize(); } +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestResponse::TRestResponse(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { + Initialize(); + + LoadConfigFromFile(fConfigFileName, name); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); +} + /////////////////////////////////////////////// /// \brief Default destructor /// @@ -58,11 +80,146 @@ TRestResponse::~TRestResponse() {} /// void TRestResponse::Initialize() { SetSectionName(this->ClassName()); } +/////////////////////////////////////////////// +/// \brief It loads into the fResponseMatrix data member the response from a file. +/// +/// For the moment only binary data files with format .N???f have been implemented. +/// +/// The response file should be arranged in a way that the more internal `std::vector` +/// (row) inside the `std::vector ` table corresponds to a specific +/// transformed energy (output). For example, if we have the incident particle energy +/// (input) and the expected detected energy response (output) for that energy, then +/// each row in the matrix corresponds to a detected energy and each element of that +/// row defines the fraction of incident energies that contributed to that detected +/// energy. +/// +/// Thats why we may need to transpose the matrix, so that when we can extract a row +/// (detected energy) from the matrix directly, such as fMatrix[n], and we just get +/// the vector that should convolute with the Signal(Ei) that is a vector of signals +/// as a function of incident energy. The resulting scalar product will give the +/// expected signal at the detection energy. +/// +void TRestResponse::LoadResponse(Bool_t transpose) { + if (fFilename == "") { + RESTError << "TRestResponse::LoadResponse. The response filename was not defined" << RESTendl; + return; + } + + std::string fullFilename = SearchFile(fFilename); + if (fullFilename.empty()) { + RESTError << "TRestResponse::LoadResponse. The response filename was not found!" << RESTendl; + RESTError << "Filename : " << fFilename << RESTendl; + RESTError << "You may want to define definition" << RESTendl; + return; + } + + std::string extension = TRestTools::GetFileNameExtension(fFilename); + if (!extension.empty() && extension[0] == 'N' && extension.back() == 'f') { + TRestTools::ReadBinaryTable(fullFilename, fResponseMatrix); + + fTransposed = false; + if (transpose) { + fTransposed = transpose; + TRestTools::TransposeTable(fResponseMatrix); + } + + return; + } + + RESTError << "Extension format - " << extension << " - not recognized!" << RESTendl; +} + +///////////////////////////////////////////// +/// \brief This method will return a vector of std::pair, each pair will contain the +/// output/frequency/energy value for the corresponding response. +/// +/// The output value will be mapped following the binning and the origin given on the +/// metadata members. +/// +std::vector> TRestResponse::GetResponse(Double_t input) { + + std::vector> response; + + if (fResponseMatrix.empty()) { + RESTError << "TRestResponse::GetResponse. Response matrix has not been loaded yet!" << RESTendl; + return response; + } + + if (input < GetInputRange().X() || input > GetInputRange().Y()) { + RESTError << "TRestResponse::GetResponse. The input value " << input << " is outside range!" + << RESTendl; + return response; + } + + Int_t binLeft = (Int_t)((input - fBinSize / 2. - fOrigin.X()) / fBinSize); + Int_t binRight = binLeft + 1; + + Double_t distLeft = (input - fBinSize / 2. + fOrigin.X()) - binLeft * fBinSize; + + if (input <= GetInputRange().X() + fBinSize / 2. || input >= GetInputRange().Y() - fBinSize / 2.) + binRight = binLeft; + + /* + std::cout << "Top : " << GetInputRange().Y() - fBinSize/2. << std::endl; + std::cout << "binLeft : " << binLeft << std::endl; + std::cout << "binRight : " << binRight << std::endl; + std::cout << "dLeft : " << distLeft << std::endl; + std::cout << "dLeft/fBinSize : " << distLeft/fBinSize << std::endl; + std::cout << "1 - distLeft/fBinSize : " << 1 - distLeft/fBinSize << std::endl; + */ + + for (std::size_t n = 0; n < fResponseMatrix[binLeft].size(); n++) { + Double_t output = fOrigin.Y() + ((double)n + 0.5) * fBinSize; + Double_t value = fResponseMatrix[binLeft][n] * (1 - distLeft / fBinSize) + + fResponseMatrix[binRight][n] * distLeft / fBinSize; + + std::pair outp{output, value}; + + response.push_back(outp); + + /* + std::cout << "n: " << n << " output : " << output << std::endl; + std::cout << "response: " << response << std::endl; + */ + } + + return response; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestResponse::PrintResponseMatrix(Int_t fromRow = 0, Int_t toRow = 0) { + TRestTools::PrintTable(fResponseMatrix, fromRow, toRow); +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// void TRestResponse::PrintMetadata() { TRestMetadata::PrintMetadata(); + RESTMetadata << "Response file : " << fFilename << RESTendl; + RESTMetadata << "Variable : " << fVariable << RESTendl; + RESTMetadata << "Bin size : " << fBinSize << RESTendl; + RESTMetadata << " " << RESTendl; + + if (!fResponseMatrix.empty()) { + RESTMetadata << "Response matrix has been loaded" << RESTendl; + RESTMetadata << " - Number of columns: " << fResponseMatrix[0].size() << RESTendl; + RESTMetadata << " - Number of rows : " << fResponseMatrix.size() << RESTendl; + RESTMetadata << " - Input range : " << GetInputRange().X() << " - " << GetInputRange().Y() + << RESTendl; + RESTMetadata << " - Output range : " << GetOutputRange().X() << " - " << GetOutputRange().Y() + << RESTendl; + + if (fTransposed) { + RESTMetadata << " " << RESTendl; + RESTMetadata << "Original matrix was transposed" << RESTendl; + } + } else { + RESTMetadata << "Response matrix has NOT been loaded" << RESTendl; + RESTMetadata << "Try calling TRestResponse::LoadResponse()" << RESTendl; + } RESTMetadata << "----" << RESTendl; } From 3c6f7d72a48f7797d526170211171c244dca4170 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 07:49:48 +0000 Subject: [PATCH 068/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 2 +- source/framework/sensitivity/src/TRestResponse.cxx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index cf4801a84..28655d703 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -26,9 +26,9 @@ #include #include -#include "TRestResponse.h" #include "TRestDataSet.h" #include "TRestMetadata.h" +#include "TRestResponse.h" /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { diff --git a/source/framework/sensitivity/src/TRestResponse.cxx b/source/framework/sensitivity/src/TRestResponse.cxx index 95d485097..76079e915 100644 --- a/source/framework/sensitivity/src/TRestResponse.cxx +++ b/source/framework/sensitivity/src/TRestResponse.cxx @@ -137,7 +137,6 @@ void TRestResponse::LoadResponse(Bool_t transpose) { /// metadata members. /// std::vector> TRestResponse::GetResponse(Double_t input) { - std::vector> response; if (fResponseMatrix.empty()) { From a1cd2226f2cdac6f0a54f7ff4d7ed2975e2a5c98 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 04:51:16 +0000 Subject: [PATCH 069/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../analysis/inc/TRestDataSetGainMap.h | 4 ++-- .../analysis/src/TRestDataSetGainMap.cxx | 21 +++++++++++-------- .../analysis/src/TRestDataSetOdds.cxx | 6 +++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/source/framework/analysis/inc/TRestDataSetGainMap.h b/source/framework/analysis/inc/TRestDataSetGainMap.h index 6a85d6a80..50000a01b 100644 --- a/source/framework/analysis/inc/TRestDataSetGainMap.h +++ b/source/framework/analysis/inc/TRestDataSetGainMap.h @@ -205,12 +205,12 @@ class TRestDataSetGainMap : public TRestMetadata { void Initialize(); Module() {} - Module(const TRestDataSetGainMap& parent) : p(&parent) {}; + Module(const TRestDataSetGainMap& parent) : p(&parent){}; Module(const TRestDataSetGainMap& parent, const Int_t planeId, const Int_t moduleId) : p(&parent) { SetPlaneId(planeId); SetModuleId(moduleId); }; - ~Module() {}; + ~Module(){}; }; }; #endif diff --git a/source/framework/analysis/src/TRestDataSetGainMap.cxx b/source/framework/analysis/src/TRestDataSetGainMap.cxx index 9c31d617e..53a5fa0a0 100644 --- a/source/framework/analysis/src/TRestDataSetGainMap.cxx +++ b/source/framework/analysis/src/TRestDataSetGainMap.cxx @@ -487,7 +487,7 @@ std::pair TRestDataSetGainMap::Module::GetIndexMatrix(const double x, /// \param y A const double that defines the y position on the detector plane. /// double TRestDataSetGainMap::Module::GetSlope(const double x, const double y) const { - auto[ index_x, index_y ] = GetIndexMatrix(x, y); + auto [index_x, index_y] = GetIndexMatrix(x, y); if (fSlope.empty()) { RESTError << "Calibration slope matrix is empty. Returning 0" << p->RESTendl; return 0; @@ -509,7 +509,7 @@ double TRestDataSetGainMap::Module::GetSlope(const double x, const double y) con /// \param y A const double that defines the y position on the detector plane. /// double TRestDataSetGainMap::Module::GetIntercept(const double x, const double y) const { - auto[ index_x, index_y ] = GetIndexMatrix(x, y); + auto [index_x, index_y] = GetIndexMatrix(x, y); if (fIntercept.empty()) { RESTError << "Calibration constant matrix is empty. Returning 0" << p->RESTendl; return 0; @@ -558,7 +558,8 @@ void TRestDataSetGainMap::Module::SetSplitX(const std::set& splitX) { } if (!fSlope.empty()) RESTWarning << "SetSplitX: changing split but current gain map and calibration paremeters correspond " - "to previous splitting. Use GenerateGainMap() to update them." << p->RESTendl; + "to previous splitting. Use GenerateGainMap() to update them." + << p->RESTendl; fSplitX = splitX; fNumberOfSegmentsX = fSplitX.size() - 1; } @@ -590,7 +591,8 @@ void TRestDataSetGainMap::Module::SetSplitY(const std::set& splitY) { } if (!fSlope.empty()) RESTWarning << "SetSplitY: changing split but current gain map and calibration paremeters correspond " - "to previous splitting. Use GenerateGainMap() to update them." << p->RESTendl; + "to previous splitting. Use GenerateGainMap() to update them." + << p->RESTendl; fSplitY = splitY; fNumberOfSegmentsY = fSplitY.size() - 1; } @@ -697,10 +699,11 @@ void TRestDataSetGainMap::Module::GenerateGainMap() { if (!fDefinitionCut.empty()) segment_cut += "&&" + fDefinitionCut; if (segment_cut.empty()) segment_cut = "1"; RESTExtreme << "Segment[" << i << "][" << j << "] cut: " << segment_cut << p->RESTendl; - auto histo = dataSet.GetDataFrame().Filter(segment_cut).Histo1D( - {"temp", "", h[i][j]->GetNbinsX(), - h[i][j]->GetXaxis()->GetXmin(), h[i][j]->GetXaxis()->GetXmax()}, - GetObservable()); + auto histo = dataSet.GetDataFrame() + .Filter(segment_cut) + .Histo1D({"temp", "", h[i][j]->GetNbinsX(), h[i][j]->GetXaxis()->GetXmin(), + h[i][j]->GetXaxis()->GetXmax()}, + GetObservable()); std::unique_ptr hpunt = std::unique_ptr(static_cast(histo->Clone())); h[i][j]->Add(hpunt.get()); hpunt.reset(); // delete hpunt; @@ -826,7 +829,7 @@ void TRestDataSetGainMap::Module::GenerateGainMap() { /// void TRestDataSetGainMap::Module::Refit(const TVector2& position, const double energyPeak, const TVector2& range) { - auto[ index_x, index_y ] = GetIndexMatrix(position.X(), position.Y()); + auto [index_x, index_y] = GetIndexMatrix(position.X(), position.Y()); int peakNumber = -1; for (size_t i = 0; i < fEnergyPeaks.size(); i++) if (fEnergyPeaks.at(i) == energyPeak) { diff --git a/source/framework/analysis/src/TRestDataSetOdds.cxx b/source/framework/analysis/src/TRestDataSetOdds.cxx index 19b3ae63b..f0d59bb99 100644 --- a/source/framework/analysis/src/TRestDataSetOdds.cxx +++ b/source/framework/analysis/src/TRestDataSetOdds.cxx @@ -242,7 +242,7 @@ void TRestDataSetOdds::ComputeLogOdds() { auto df = dataSet.GetDataFrame(); std::string totName = ""; RESTDebug << "Computing log odds from " << fDataSetName << RESTendl; - for (const auto & [ obsName, histo ] : fHistos) { + for (const auto& [obsName, histo] : fHistos) { const std::string oddsName = "odds_" + obsName; auto GetLogOdds = [&histo = histo](double val) { double odds = histo->GetBinContent(histo->GetXaxis()->FindBin(val)); @@ -275,7 +275,7 @@ void TRestDataSetOdds::ComputeLogOdds() { TFile* f = TFile::Open(fOutputFileName.c_str(), "UPDATE"); this->Write(); RESTDebug << "Writing histograms to " << fOutputFileName << RESTendl; - for (const auto & [ obsName, histo ] : fHistos) histo->Write(); + for (const auto& [obsName, histo] : fHistos) histo->Write(); f->Close(); } } @@ -303,7 +303,7 @@ void TRestDataSetOdds::SetOddsObservables(const std::vector Date: Sun, 17 Dec 2023 06:37:30 +0100 Subject: [PATCH 070/174] TRestComponentDataSet. Trying to fix pipeline --- .../sensitivity/src/TRestComponentDataSet.cxx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index d35962398..06d6e27d7 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -270,15 +270,13 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -725,8 +723,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } @@ -777,7 +774,7 @@ Bool_t TRestComponentDataSet::VariablesOk() { Bool_t ok = true; std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - for (const auto var : fVariables) + for (const auto& var : fVariables) if (std::count(cNames.begin(), cNames.end(), var) == 0) { RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl; ok = false; @@ -792,7 +789,7 @@ Bool_t TRestComponentDataSet::WeightsOk() { Bool_t ok = true; std::vector cNames = fDataSet.GetDataFrame().GetColumnNames(); - for (const auto var : fWeights) + for (const auto& var : fWeights) if (std::count(cNames.begin(), cNames.end(), var) == 0) { RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl; ok = false; From dfdd7ad740db293ce617b9203b1ee3d1f30cc535 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 05:38:04 +0000 Subject: [PATCH 071/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../framework/sensitivity/src/TRestComponentDataSet.cxx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 06d6e27d7..c6e1ad4eb 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -270,13 +270,15 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } @@ -723,7 +725,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From a0c37fce2c99ac42dac5db9771baba865f345433 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 24 Dec 2023 11:38:09 +0100 Subject: [PATCH 072/174] TRestResponse. Adding fInterpolate data member to allow to enable/disable interpolation --- .../framework/sensitivity/inc/TRestResponse.h | 8 +++++- .../sensitivity/src/TRestResponse.cxx | 26 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestResponse.h b/source/framework/sensitivity/inc/TRestResponse.h index 4e3ba8bc3..25bdb4ed1 100644 --- a/source/framework/sensitivity/inc/TRestResponse.h +++ b/source/framework/sensitivity/inc/TRestResponse.h @@ -44,7 +44,10 @@ class TRestResponse : public TRestMetadata { std::vector> fResponseMatrix; //< /// Determines if the response matrix has been transposed - Bool_t fTransposed = false; + Bool_t fTransposed = false; //< + + /// It allows to decide if the returned response should be interpolated (default:false) + Bool_t fInterpolation = false; //! public: void SetBinSize(Double_t bSize) { fBinSize = bSize; } @@ -52,6 +55,9 @@ class TRestResponse : public TRestMetadata { void SetOrigin(const TVector2& v) { fOrigin = v; } void SetVariable(const std::string& var) { fVariable = var; } + Bool_t ApplyInterpolation() { return fInterpolation; } + void Interpolate(Bool_t interpolate = true) { fInterpolation = interpolate; } + Double_t GetBinSize() const { return fBinSize; } std::string GetResponseFilename() const { return fFilename; } TVector2 GetOrigin() const { return fOrigin; } diff --git a/source/framework/sensitivity/src/TRestResponse.cxx b/source/framework/sensitivity/src/TRestResponse.cxx index 76079e915..1c9c769bf 100644 --- a/source/framework/sensitivity/src/TRestResponse.cxx +++ b/source/framework/sensitivity/src/TRestResponse.cxx @@ -131,7 +131,8 @@ void TRestResponse::LoadResponse(Bool_t transpose) { ///////////////////////////////////////////// /// \brief This method will return a vector of std::pair, each pair will contain the -/// output/frequency/energy value for the corresponding response. +/// output energy together with the corresponding response (or efficiency), for the +/// given input energy. /// /// The output value will be mapped following the binning and the origin given on the /// metadata members. @@ -150,6 +151,21 @@ std::vector> TRestResponse::GetResponse(Double_t i return response; } + if (!fInterpolation) { + Int_t bin = (Int_t)((input - fOrigin.X()) / fBinSize); + + for (std::size_t n = 0; n < fResponseMatrix[bin].size(); n++) { + Double_t output = fOrigin.Y() + ((double)n + 0.5) * fBinSize; + Double_t value = fResponseMatrix[bin][n]; + + std::pair outp{output, value}; + + response.push_back(outp); + } + + return response; + } + Int_t binLeft = (Int_t)((input - fBinSize / 2. - fOrigin.X()) / fBinSize); Int_t binRight = binLeft + 1; @@ -169,6 +185,7 @@ std::vector> TRestResponse::GetResponse(Double_t i for (std::size_t n = 0; n < fResponseMatrix[binLeft].size(); n++) { Double_t output = fOrigin.Y() + ((double)n + 0.5) * fBinSize; + Double_t value = fResponseMatrix[binLeft][n] * (1 - distLeft / fBinSize) + fResponseMatrix[binRight][n] * distLeft / fBinSize; @@ -220,5 +237,12 @@ void TRestResponse::PrintMetadata() { RESTMetadata << "Response matrix has NOT been loaded" << RESTendl; RESTMetadata << "Try calling TRestResponse::LoadResponse()" << RESTendl; } + if (fInterpolation) { + RESTMetadata << " " << RESTendl; + RESTMetadata << "Interpolation is enabled" << RESTendl; + } else { + RESTMetadata << " " << RESTendl; + RESTMetadata << "Interpolation is disabled" << RESTendl; + } RESTMetadata << "----" << RESTendl; } From 7a040dd1212ebd178b82446c6fbf900555c35993 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 30 Dec 2023 19:23:35 +0100 Subject: [PATCH 073/174] TRestTools::GetFilesMatchingPattern now accepts an option to avoid max argument limit when using find command --- source/framework/tools/inc/TRestTools.h | 12 ++++--- source/framework/tools/src/TRestTools.cxx | 40 ++++++++++++++++------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 5ce2e2aac..3ce628d04 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void)x +#define UNUSED(x) (void) x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -119,7 +119,7 @@ class TRestTools { static std::string GetPureFileName(const std::string& fullPathFileName); static std::string SearchFileInPath(std::vector path, std::string filename); static bool CheckFileIsAccessible(const std::string&); - static std::vector GetFilesMatchingPattern(std::string pattern); + static std::vector GetFilesMatchingPattern(std::string pattern, bool unlimited = false); static int ConvertVersionCode(std::string in); static std::istream& GetLine(std::istream& is, std::string& t); @@ -181,10 +181,14 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { ENERGY, LENGTH, TIME }; +enum Quantities { + ENERGY, + LENGTH, + TIME +}; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 9b8f3a458..452656638 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -819,15 +819,13 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" - << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" - << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; exit(1); } path /= userHomePath; @@ -915,7 +913,11 @@ bool TRestTools::CheckFileIsAccessible(const std::string& filename) { /// \brief Returns a list of files whose name match the pattern string. Key word /// is "*". e.g. abc00*.root /// -vector TRestTools::GetFilesMatchingPattern(string pattern) { +/// Argument unlimited will fix an issue with the number of files being to high. +/// However, it causes issues when searching/listing the macros. +/// The default value for unlimited is `false`. +/// +vector TRestTools::GetFilesMatchingPattern(string pattern, bool unlimited) { std::vector outputFileNames; if (pattern != "") { vector items = Split(pattern, "\n"); @@ -944,11 +946,26 @@ vector TRestTools::GetFilesMatchingPattern(string pattern) { } } #else - string a = Execute("find " + item); - auto b = Split(a, "\n"); + auto path_name = SeparatePathAndName(item); + if (unlimited) { + std::string currentDir = filesystem::current_path(); + std::cout << "Current dir:" << currentDir << std::endl; + ChangeDirectory(path_name.first); + string a = Execute("find -type f -name \'" + path_name.second + "\'"); + ChangeDirectory(currentDir); + auto b = Split(a, "\n"); + + for (unsigned int i = 0; i < b.size(); i++) { + outputFileNames.push_back(path_name.first + "/" + b[i]); + } - for (unsigned int i = 0; i < b.size(); i++) { - outputFileNames.push_back(b[i]); + } else { + string a = Execute("find " + item); + auto b = Split(a, "\n"); + + for (unsigned int i = 0; i < b.size(); i++) { + outputFileNames.push_back(b[i]); + } } #endif @@ -1032,7 +1049,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof(): + case std::streambuf::traits_type::eof() : // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1237,8 +1254,7 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" - << RESTendl; + "http://gifna.unizar.es/rest-forum/" << RESTendl; return -1; } From f669d572a27ea2e913ead77a60a9a64d1bc2f255 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 30 Dec 2023 19:33:32 +0100 Subject: [PATCH 074/174] TRestTools. Removing debug output --- source/framework/tools/src/TRestTools.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 452656638..5e07566ad 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -949,7 +949,6 @@ vector TRestTools::GetFilesMatchingPattern(string pattern, bool unlimite auto path_name = SeparatePathAndName(item); if (unlimited) { std::string currentDir = filesystem::current_path(); - std::cout << "Current dir:" << currentDir << std::endl; ChangeDirectory(path_name.first); string a = Execute("find -type f -name \'" + path_name.second + "\'"); ChangeDirectory(currentDir); From 8a6e9195a632cd8fb9a5e8ff37121f4d497b906c Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 30 Dec 2023 19:34:14 +0100 Subject: [PATCH 075/174] REST_CheckValidRuns.C. Now uses GetFilesMatchingPattern that avoids issue with max arguments --- macros/REST_CheckValidRuns.C | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/macros/REST_CheckValidRuns.C b/macros/REST_CheckValidRuns.C index 43e240eb2..18592be6e 100644 --- a/macros/REST_CheckValidRuns.C +++ b/macros/REST_CheckValidRuns.C @@ -37,15 +37,14 @@ namespace fs = std::filesystem; //*** CAUTION: Be aware that any non-REST file in the list will be removed if you use purge=1 //*** //******************************************************************************************************* -Int_t REST_CheckValidRuns(TString namePattern, Bool_t purge = false) { +Int_t REST_CheckValidRuns(std::string namePattern, Bool_t purge = false) { TGeoManager::SetVerboseLevel(0); vector filesNotWellClosed; TRestStringOutput RESTLog; - string a = TRestTools::Execute((string)("ls -d -1 " + namePattern)); - vector b = Split(a, "\n"); + std::vector b = TRestTools::GetFilesMatchingPattern(namePattern, true); Double_t totalTime = 0; int cont = 0; From c50c5661a0b6b18e1eaa4c9f6f52a47ff804daef Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 30 Dec 2023 18:34:33 +0000 Subject: [PATCH 076/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/tools/inc/TRestTools.h | 10 +++------- source/framework/tools/src/TRestTools.cxx | 11 +++++++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 3ce628d04..826ce5137 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void) x +#define UNUSED(x) (void)x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -181,14 +181,10 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { - ENERGY, - LENGTH, - TIME -}; +enum Quantities { ENERGY, LENGTH, TIME }; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 5e07566ad..5763c53dc 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -819,13 +819,15 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" + << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" + << endl; exit(1); } path /= userHomePath; @@ -1048,7 +1050,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof() : + case std::streambuf::traits_type::eof(): // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1253,7 +1255,8 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" << RESTendl; + "http://gifna.unizar.es/rest-forum/" + << RESTendl; return -1; } From 6cb114668d865b0d0d32ab6171682556846a7159 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 30 Dec 2023 19:40:42 +0100 Subject: [PATCH 077/174] restRoot. Adding a HINT restRootMacros --- source/bin/restRoot.cxx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index 17e2a1e0a..8ca1ff22f 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -61,7 +61,7 @@ int main(int argc, char* argv[]) { printf("\n"); printf(" restRoot --m [0,1]\n"); printf("\n"); - printf(" Option 0 will disable macro loading. Option 1 is the default.\n"); + printf(" Option 0 will disable macro loading. Option 0 is the default.\n"); printf("\n"); exit(0); } @@ -81,11 +81,21 @@ int main(int argc, char* argv[]) { gROOT->ProcessLine("#include "); gROOT->ProcessLine("#include "); if (loadMacros) { - if (!silent) printf("= Loading macros ...\n"); + if (!silent) { + printf("= Loading macros ...\n"); + printf( + "= HINT. Loading all macros may require a long time till you get access to the interactive " + "shell\n"); + printf("= HINT. You may use `restListMacros` outside `restRoot` to check the available macros\n"); + printf( + "= HINT. Then, you may execute the macro externally by using: `restManager MacroName " + "[ARGUMENTS]\n"); + printf("= HINT. `MacroName` is the name of the macro after removing the macro name header\n"); + printf("= HINT. E.g. REST_Detector_XYZ(arg1,arg2) may be called as: restManager XYZ arg1 arg2\n"); + } vector macroFiles; - const vector patterns = { - REST_PATH + "/macros/REST_*.C", // framework - REST_PATH + "/macros/*/REST_*.C" // libraries + const vector patterns = {REST_PATH + "/macros/REST_*.C", // framework + REST_PATH + "/macros/*/REST_*.C" // libraries }; for (const auto& pattern : patterns) { for (const auto& macroFile : TRestTools::GetFilesMatchingPattern(pattern)) { From 70ab0e586d808710ab96e7f8cf0fb23a2681ea83 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 9 Jan 2024 09:48:26 +0100 Subject: [PATCH 078/174] TRestDataSet. Improving output --- source/framework/core/src/TRestDataSet.cxx | 33 ++++++++++------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 192598ef6..7994a9cec 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -353,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -384,22 +384,18 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" - << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; return fFileSelection; } std::vector fileNames = TRestTools::GetFilesMatchingPattern(fFilePattern); - if (!fileNames.empty()) { - RESTInfo << "TRestDataSet::FileSelection. Starting file selection." << RESTendl; - RESTInfo << "Total files : " << fileNames.size() << RESTendl; - RESTInfo << "This process may take long computation time in case there are many files." << RESTendl; - } + RESTInfo << "TRestDataSet::FileSelection. Starting file selection." << RESTendl; + RESTInfo << "Total files : " << fileNames.size() << RESTendl; + RESTInfo << "This process may take long computation time in case there are many files." << RESTendl; fTotalDuration = 0; - std::cout << "Total files : " << fileNames.size() << std::endl; - std::cout << "Processing file selection ."; + std::cout << "Processing file selection."; int cnt = 1; for (const auto& file : fileNames) { if (cnt % 100 == 0) { @@ -413,6 +409,7 @@ std::vector TRestDataSet::FileSelection() { double runEnd = run.GetEndTimestamp(); if (runStart < time_stamp_start || runEnd > time_stamp_end) { + RESTInfo << "Rejecting file out of date range: " << file << RESTendl; continue; } @@ -440,7 +437,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -497,7 +494,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto& [param, condition] : paramCut) { + for (const auto & [ param, condition ] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -544,7 +541,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const& [name, properties] : fQuantity) + for (auto const & [ name, properties ] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -610,7 +607,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const& [name, properties] : fQuantity) { + for (auto const & [ name, properties ] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -622,7 +619,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -795,8 +792,7 @@ void TRestDataSet::Export(const std::string& filename) { if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" - << RESTendl; + "exporting to ASCII table" << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -831,7 +827,7 @@ void TRestDataSet::Export(const std::string& filename) { } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } @@ -882,6 +878,7 @@ void TRestDataSet::Export(const std::string& filename) { RESTWarning << "TRestDataSet::Export. Extension " << TRestTools::GetFileNameExtension(filename) << " not recognized" << RESTendl; } + RESTInfo << "Dataset generated: " << filename << RESTendl; } /////////////////////////////////////////////// From 3f992fc755cccc30c27e675bac46e61705304764 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 11 Jan 2024 16:42:53 +0100 Subject: [PATCH 079/174] TRestComponentDataSet::ExtractNodeStatistics. Adding a precision to the parameter value --- .../sensitivity/inc/TRestComponentDataSet.h | 2 +- .../sensitivity/src/TRestComponentDataSet.cxx | 34 +++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 6de1f4385..865f1cb09 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -63,7 +63,7 @@ class TRestComponentDataSet : public TRestComponent { protected: std::vector ExtractParameterizationNodes(); - std::vector ExtractNodeStatistics(); + std::vector ExtractNodeStatistics(Double_t precision = 0.00000001); void FillHistograms(); Bool_t VariablesOk(); diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index c6e1ad4eb..8e877aa90 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -146,8 +146,8 @@ void TRestComponentDataSet::Initialize() { /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. /// -/// If interpolation is enabled (which is the default) the rate will be evaluated -/// using interpolation with neighbor histogram cells. +/// If interpolation is enabled (which is disabled by default) the rate will be +/// evaluated using interpolation with neighbour histogram cells. /// /// Interpolation technique extracted from: /// https://math.stackexchange.com/questions/1342364/formula-for-n-dimensional-linear-interpolation @@ -270,15 +270,13 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -457,13 +455,13 @@ void TRestComponentDataSet::PrintStatistics() { } auto result = std::accumulate(fNSimPerNode.begin(), fNSimPerNode.end(), 0); - std::cout << "Total counts : " << result << std::endl; + RESTInfo << "Total counts : " << result << RESTendl; std::cout << std::endl; - std::cout << " Parameter node statistics (" << fParameter << ")" << std::endl; + RESTInfo << " Parameter node statistics (" << fParameter << ")" << RESTendl; int n = 0; for (const auto& p : fParameterizationNodes) { - std::cout << " - Value : " << p << " Counts: " << fNSimPerNode[n] << std::endl; + RESTInfo << " - Value : " << p << " Counts: " << fNSimPerNode[n] << RESTendl; n++; } } @@ -694,10 +692,12 @@ std::vector TRestComponentDataSet::ExtractParameterizationNodes() { /// \brief It returns a vector with the number of entries found for each /// parameterization node. /// -/// If fNSimPerNode has already been initialized it will -/// directly return its value. +/// If fNSimPerNode has already been initialized it will directly return its value. /// -std::vector TRestComponentDataSet::ExtractNodeStatistics() { +/// The argument precision will be used to include a thin range where to select +/// the node values. +/// +std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precision) { if (!fNSimPerNode.empty()) return fNSimPerNode; std::vector stats; @@ -710,8 +710,13 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; for (const auto& p : fParameterizationNodes) { - std::string filter = fParameter + " == " + DoubleToString(p); + Double_t pUp = p + precision / 2; + Double_t pDown = p - precision / 2; + std::string filter = + fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); + RESTInfo << "Counting stats for : " << fParameter << " = " << p << RESTendl; auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); + RESTInfo << "Counts found : " << *nEv << RESTendl; stats.push_back(*nEv); } return stats; @@ -725,8 +730,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From baab26cddbeac0500be0aceeb0771292bf3af49c Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 11 Jan 2024 16:44:40 +0100 Subject: [PATCH 080/174] TRestComponent::GetRateWithResponse method implemented --- .../sensitivity/inc/TRestComponent.h | 9 ++- .../sensitivity/src/TRestComponent.cxx | 68 ++++++++++++++++++- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 28655d703..3483dbb11 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -51,7 +51,7 @@ class TRestComponent : public TRestMetadata { /// It is used to define the node that will be accessed for rate retrieval Int_t fActiveNode = -1; //< - /// + /// A pointer to the detector response TRestResponse* fResponse = nullptr; //< /// A canvas for drawing the active node component @@ -72,12 +72,15 @@ class TRestComponent : public TRestMetadata { void InitFromConfigFile() override; public: - Double_t GetNormalizedRate(std::vector point); - Double_t GetResponseRate(std::vector point); + TRestResponse* GetResponse() const { return fResponse; } virtual Double_t GetRate(std::vector point) = 0; virtual Double_t GetTotalRate() = 0; + Double_t GetNormalizedRate(std::vector point); + Double_t GetRateWithResponse(std::vector point); + Double_t GetNormalizedRateWithResponse(std::vector point); + size_t GetDimensions() { return fVariables.size(); } Int_t GetActiveNode() { return fActiveNode; } diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index d5f5228af..d914b3d41 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -108,11 +108,57 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { /// Double_t TRestComponent::GetNormalizedRate(std::vector point) { Double_t normFactor = 1; - for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); + for (size_t n = 0; n < GetDimensions(); n++) { + std::cout << "bins: " << fNbins[n] << std::endl; + std::cout << "rangeX " << fRanges[n].X() << std::endl; + std::cout << "rangeY " << fRanges[n].Y() << std::endl; + normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); + } return normFactor * GetRate(point); } +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. +/// +/// The response matrix (if defined) will be used to convolute the expected rate. +/// The TRestResponse metadata class defines the variable where the response will +/// be applied. +/// +Double_t TRestComponent::GetRateWithResponse(std::vector point) { + + if (!fResponse) { + RESTError << "TRestComponent::GetRateWithResponse. Response has not been defined!" << RESTendl; + RESTError << "You may directly call GetRate in order to get the raw expected rate without warning." + << RESTendl; + return GetRate(point); + } + + std::string responseVariable = fResponse->GetVariable(); + Int_t respVarIndex = GetVariableIndex(responseVariable); + + if (respVarIndex == -1) { + RESTError << "The response variable `" << responseVariable << "`, defined inside TRestResponse," + << RESTendl; + RESTError << "could not be found inside the component." << RESTendl; + RESTError << "Please, check the component variable names." << RESTendl; + return 0; + } + + std::vector > response = fResponse->GetResponse(point[respVarIndex]); + + Double_t rate = 0; + for (const auto& resp : response) { + std::vector newPoint = point; + newPoint[respVarIndex] = resp.first; + rate += resp.second * GetRate(newPoint); + } + + return rate; +} + /////////////////////////////////////////////// /// \brief It returns the intensity/rate (in seconds) corresponding to the /// generated distribution or formula evaluated at the position of the parameter @@ -123,11 +169,11 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { /// 2-spatial dimensions and 1-energy dimension, the returned rate will be /// expressed in standard REST units as, s-1 mm-2 keV-1. /// -Double_t TRestComponent::GetResponseRate(std::vector point) { +Double_t TRestComponent::GetNormalizedRateWithResponse(std::vector point) { Double_t normFactor = 1; for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); - return normFactor * GetRate(point); + return normFactor * GetRateWithResponse(point); } /////////////////////////////////////////////// @@ -140,6 +186,7 @@ void TRestComponent::LoadResponse(const TRestResponse& resp) { } fResponse = (TRestResponse*)resp.Clone("response"); + if (fResponse) fResponse->LoadResponse(); fResponse->PrintMetadata(); } @@ -176,6 +223,13 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " Use : PrintNodes() for additional info" << RESTendl; } + if (fResponse) { + RESTMetadata << " " << RESTendl; + RESTMetadata << "A response matrix was loaded inside the component" << RESTendl; + RESTMetadata << "You may get more details using TRestComponent::GetResponse()->PrintMetadata()" + << RESTendl; + } + RESTMetadata << "----" << RESTendl; } @@ -212,6 +266,14 @@ void TRestComponent::InitFromConfigFile() { ele = GetNextElement(ele); } + + if (fResponse) { + delete fResponse; + fResponse = nullptr; + } + + fResponse = (TRestResponse*)this->InstantiateChildMetadata("Response"); + if (fResponse) fResponse->LoadResponse(); } ///////////////////////////////////////////// From fd4c0a9c70061dfc08503d6960d0c15fd22dc566 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:46:01 +0000 Subject: [PATCH 081/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/bin/restRoot.cxx | 5 +++-- source/framework/core/src/TRestDataSet.cxx | 20 ++++++++++--------- .../sensitivity/src/TRestComponent.cxx | 1 - .../sensitivity/src/TRestComponentDataSet.cxx | 9 ++++++--- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index 8ca1ff22f..758516f47 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -94,8 +94,9 @@ int main(int argc, char* argv[]) { printf("= HINT. E.g. REST_Detector_XYZ(arg1,arg2) may be called as: restManager XYZ arg1 arg2\n"); } vector macroFiles; - const vector patterns = {REST_PATH + "/macros/REST_*.C", // framework - REST_PATH + "/macros/*/REST_*.C" // libraries + const vector patterns = { + REST_PATH + "/macros/REST_*.C", // framework + REST_PATH + "/macros/*/REST_*.C" // libraries }; for (const auto& pattern : patterns) { for (const auto& macroFile : TRestTools::GetFilesMatchingPattern(pattern)) { diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 7994a9cec..1774251ae 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -353,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -384,7 +384,8 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" + << RESTendl; return fFileSelection; } @@ -437,7 +438,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -494,7 +495,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto & [ param, condition ] : paramCut) { + for (const auto& [param, condition] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -541,7 +542,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const & [ name, properties ] : fQuantity) + for (auto const& [name, properties] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -607,7 +608,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const & [ name, properties ] : fQuantity) { + for (auto const& [name, properties] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -619,7 +620,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -792,7 +793,8 @@ void TRestDataSet::Export(const std::string& filename) { if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" << RESTendl; + "exporting to ASCII table" + << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -827,7 +829,7 @@ void TRestDataSet::Export(const std::string& filename) { } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index d914b3d41..50611f19b 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -128,7 +128,6 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { /// be applied. /// Double_t TRestComponent::GetRateWithResponse(std::vector point) { - if (!fResponse) { RESTError << "TRestComponent::GetRateWithResponse. Response has not been defined!" << RESTendl; RESTError << "You may directly call GetRate in order to get the raw expected rate without warning." diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 8e877aa90..4b8fafbcf 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -270,13 +270,15 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } @@ -730,7 +732,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precisi Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" + << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From b66097dd8ba5e9fa142e902f8eeb1ad43a9aa8dc Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 11 Jan 2024 16:50:25 +0100 Subject: [PATCH 082/174] REST_AddComponent macro added to automatize addition of components to a single file --- macros/REST_AddComponent.C | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 macros/REST_AddComponent.C diff --git a/macros/REST_AddComponent.C b/macros/REST_AddComponent.C new file mode 100644 index 000000000..d59468fdb --- /dev/null +++ b/macros/REST_AddComponent.C @@ -0,0 +1,46 @@ +#include "TRestComponent.h" +#include "TRestTask.h" + +#ifndef RestTask_AddComponent +#define RestTask_AddComponent + +//******************************************************************************************************* +//*** Description: This macro will load from an RML the component chosen in the arguments and it +//*** will write it inside the file given as outputFile +//*** +//*** -------------- +//*** Usage: restManager AddComponent components.rml sectionName [outputFile] [componentName] [update] +//*** +//*** Arguments description: +//*** +//*** - cfgFile: The RML configuration file where the component definition can be found. +//*** - sectionName: The section name used to select a component inside the RML file. +//*** - outputFile: The file where the component is written, by default is components.root. +//*** - componentName: This argument allows to change the component name stored in the output file. +//*** By default it will take the same value as section name. +//*** - update: If disabled it will create a new file erasing any other previously added components. +//*** It is enabled by default. +//*** +//******************************************************************************************************* + +Int_t REST_AddComponent(std::string cfgFile, std::string sectionName, + std::string outputFile = "components.root", std::string componentName = "", + Bool_t update = true) { + TRestComponentDataSet comp(cfgFile.c_str(), sectionName.c_str()); + comp.LoadDataSets(); + + TFile *f; + if (update) + f = TFile::Open(outputFile.c_str(), "UPDATE"); + else + f = TFile::Open(outputFile.c_str(), "RECREATE"); + + if (componentName == "") componentName = sectionName; + + comp.Write(componentName.c_str()); + + f->Close(); + + return 0; +} +#endif From 72205b83a17fd0c2b2eb563b74e882d24a8593f5 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 20:03:44 +0100 Subject: [PATCH 083/174] TRestSystemOfUnits. Adding official headers --- .../framework/core/inc/TRestSystemOfUnits.h | 45 +++++++++++++------ .../framework/core/src/TRestSystemOfUnits.cxx | 34 ++++++++++++-- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index 782979d52..2a4484951 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -1,15 +1,24 @@ -///______________________________________________________________________________ -///______________________________________________________________________________ -///______________________________________________________________________________ -/// -/// -/// RESTSoft : Software for Rare Event Searches with TPCs -/// -/// TRestSystemOfUnits.h -/// -/// Feb 2016: First concept -/// author : Javier Galan -///_______________________________________________________________________________ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see http://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see http://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ #ifndef RestCore_TRestSystemOfUnits #define RestCore_TRestSystemOfUnits @@ -33,7 +42,17 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; +enum Physical_Unit { + Energy, + Time, + Length, + Mass, + Voltage, + MagneticField, + Pressure, + Angle, + NOT_A_UNIT = -1 +}; class TRestSystemOfUnits { private: diff --git a/source/framework/core/src/TRestSystemOfUnits.cxx b/source/framework/core/src/TRestSystemOfUnits.cxx index ef01c59fc..ad8a58ea4 100644 --- a/source/framework/core/src/TRestSystemOfUnits.cxx +++ b/source/framework/core/src/TRestSystemOfUnits.cxx @@ -1,3 +1,25 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see http://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see http://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + #include #include @@ -45,10 +67,16 @@ map> __ListOfRESTUnits; // name, {type, scale} /// /// History of developments: /// -/// 2017-Nov: First concept and implementation of REST_Units namespace. -/// \author Javier Galan +/// 2016-Feb: First concept of REST standard system of units +/// Javier Galán +/// +/// 2017-Aug: Major upgrades +/// Kaixiang Ni /// +/// \class TRestSystemOfUnits /// \namespace REST_Units +/// \author Javier Galan +/// \author Kaixiang Ni /// ///
    namespace REST_Units { @@ -280,7 +308,7 @@ TRestSystemOfUnits::TRestSystemOfUnits(string unitsStr) { orderprefix = -1; } else if (unitsStr[pos1 - 1] == '-' || unitsStr[pos1 - 1] == '*') { } else { - RESTWarning << "illegeal unit combiner \"" << unitsStr[pos1 - 1] << "\"" << RESTendl; + RESTWarning << "illegal unit combiner \"" << unitsStr[pos1 - 1] << "\"" << RESTendl; } } From 93893a4eabba662c8a99a5eb784a76c5f22b216d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 20:04:34 +0100 Subject: [PATCH 084/174] TRestComponent. Cleaning up debug output --- source/framework/sensitivity/src/TRestComponent.cxx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index d914b3d41..13c0a78fc 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -101,17 +101,15 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { /// generated distribution or formula evaluated at the position of the parameter /// space given by point. /// -/// The rate returned by the TRestComponent::GetRate method will be normalized -/// to the corresponding parameter space. Thus, if the parameter consists of +/// The rate returned by the TRestComponent::GetRate method is integrated to the +/// cell size for the given parameter space binning and range. This method will +/// return the normalized value. Thus, if the parameter consists of /// 2-spatial dimensions and 1-energy dimension, the returned rate will be /// expressed in standard REST units as, s-1 mm-2 keV-1. /// Double_t TRestComponent::GetNormalizedRate(std::vector point) { Double_t normFactor = 1; for (size_t n = 0; n < GetDimensions(); n++) { - std::cout << "bins: " << fNbins[n] << std::endl; - std::cout << "rangeX " << fRanges[n].X() << std::endl; - std::cout << "rangeY " << fRanges[n].Y() << std::endl; normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); } From 53adcf2b15baaf4625a1637f6fe6f24a7ee98491 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 20:05:21 +0100 Subject: [PATCH 085/174] REST_StringHelper::Replace documentation fix --- source/framework/tools/src/TRestStringHelper.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 30734efb8..101e56a99 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -479,7 +479,7 @@ Int_t REST_StringHelper::DiffString(const string& source, const string& target) } /////////////////////////////////////////////// -/// \brief Replace every occurences of **thisSring** by **byThisString** inside +/// \brief Replace any occurences of **thisSring** by **byThisString** inside /// string **in**. /// string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition, From 731c3fbbcc87ce312f62c16395fd3b6c31d9db94 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 20:19:29 +0100 Subject: [PATCH 086/174] TRestComponentDataSet reviewed method documentation --- .../framework/sensitivity/src/TRestComponentDataSet.cxx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 8e877aa90..d48468ac6 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -137,11 +137,9 @@ void TRestComponentDataSet::Initialize() { /////////////////////////////////////////////// /// \brief It returns the intensity/rate (in seconds) corresponding to the /// generated distribution or formula evaluated at the position of the parameter -/// space given by point. -/// -/// The rate will be normalized to the corresponding parameter space. Thus, if -/// the parameter consists of 2-spatial dimensions and 1-energy dimension, the -/// returned rate will be expressed in standard REST units as, s-1 mm-2 keV-1. +/// space given by point. The returned rate is integrated to the granularity +/// of the parameter space (cell size). To get a normalized rate use +/// TRestComponent::GetNormalizedRate. /// /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. From 362f05e702221b67dfaf277d458dd766537252ed Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 20:56:00 +0100 Subject: [PATCH 087/174] TRestComponent. Reviewing method documentation --- .../framework/sensitivity/src/TRestComponent.cxx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 13c0a78fc..1bc2e58fe 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -128,9 +128,6 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { Double_t TRestComponent::GetRateWithResponse(std::vector point) { if (!fResponse) { - RESTError << "TRestComponent::GetRateWithResponse. Response has not been defined!" << RESTendl; - RESTError << "You may directly call GetRate in order to get the raw expected rate without warning." - << RESTendl; return GetRate(point); } @@ -167,6 +164,17 @@ Double_t TRestComponent::GetRateWithResponse(std::vector point) { /// 2-spatial dimensions and 1-energy dimension, the returned rate will be /// expressed in standard REST units as, s-1 mm-2 keV-1. /// +/// The returned value may be recovered back with the desired units using +/// the REST_Units namespace. +/// +/// \code +/// component->GetNormalizedRateWithResponse( {0,0,0} ) * units("cm^-2*keV^-1") +/// \endcode +/// +/// The response matrix (if defined) will be used to convolute the expected rate. +/// The TRestResponse metadata class defines the variable where the response will +/// be applied. +/// Double_t TRestComponent::GetNormalizedRateWithResponse(std::vector point) { Double_t normFactor = 1; for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); From 23fe44acbe95e7dae0f3c5182c2710f83026de9a Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 21:01:44 +0100 Subject: [PATCH 088/174] TRestComponentFormula. First implementation of. --- .../sensitivity/inc/TRestComponentFormula.h | 17 ++--- .../sensitivity/src/TRestComponentFormula.cxx | 75 ++++++++++++++++--- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index c05961c65..22dc19c1c 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -23,22 +23,19 @@ #ifndef REST_TRestComponentFormula #define REST_TRestComponentFormula -#include +#include #include "TRestComponent.h" #include "TRestDataSet.h" -/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) +/// It defines an analytical component model distribution in a given parameter space (tipically x,y,en) class TRestComponentFormula : public TRestComponent { private: - /// The function used to initialize the distribution - /// std::string fFunction = ""; //! - /// - /// The function used to initialize the distribution - /// TFormula fFormula; //! - - /// A pointer to the component distribution - // THnD* fDistribution = nullptr; //! + /// A vector of formulas that will be added up to integrate a given rate + std::vector fFormulas; + + /// The formulas should be expressed in the following units + std::string fUnits = "cm^-2*keV^-1"; //< protected: void InitFromConfigFile() override; diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index f3039cab3..89c20b9ec 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -94,17 +94,30 @@ void TRestComponentFormula::Initialize() { /// generated distribution or formula evaluated at the position of the parameter /// space given by point. /// -/// The density should be normalized to the corresponding parameter space. During -/// the component construction, **the user is responsible** to initialize the component -/// with the appropriate units. For example, if the parameter space is 2 spatial -/// dimensions and 1 energy dimension, the contribution of each cell or event to -/// the component will be expressed in mm-2 keV-1 which are the default units for -/// distance and energy. -/// /// The size of the point vector must have the same dimension as the dimensions /// of the distribution. /// -Double_t TRestComponentFormula::GetRate(std::vector point) { return 0.0; } +Double_t TRestComponentFormula::GetRate(std::vector point) { + + if (fVariables.size() != point.size()) { + RESTError << "Point should have same dimensions as number of variables!" << RESTendl; + return 0; + } + + Double_t result = 0; + for (auto& formula : fFormulas) { + for (size_t n = 0; n < fVariables.size(); n++) formula.SetParameter(fVariables[n].c_str(), point[n]); + + result += formula.EvalPar(nullptr); + } + + Double_t normFactor = 1; + for (size_t n = 0; n < GetDimensions(); n++) { + normFactor *= (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + } + + return normFactor * result / units(fUnits); +} /////////////////////////////////////////////// /// \brief This method integrates the rate to all the parameter space defined in the density function. @@ -122,10 +135,54 @@ Double_t TRestComponentFormula::GetTotalRate() { void TRestComponentFormula::PrintMetadata() { TRestComponent::PrintMetadata(); + RESTMetadata << " " << RESTendl; + RESTMetadata << "Formula units: " << fUnits << RESTendl; + + if (!fFormulas.empty()) { + RESTMetadata << " " << RESTendl; + RESTMetadata << " == Term expressions implemented inside the component ==" << RESTendl; + + for (const auto& x : fFormulas) + RESTMetadata << "- " << x.GetName() << " = " << x.GetExpFormula() << RESTendl; + + RESTMetadata << " " << RESTendl; + } + RESTMetadata << "----" << RESTendl; } ///////////////////////////////////////////// /// \brief It customizes the retrieval of XML data values of this class /// -void TRestComponentFormula::InitFromConfigFile() { TRestComponent::InitFromConfigFile(); } +void TRestComponentFormula::InitFromConfigFile() { + TRestComponent::InitFromConfigFile(); + + if (!fFormulas.empty()) return; + + auto ele = GetElement("formula"); + while (ele != nullptr) { + std::string name = GetParameter("name", ele, ""); + std::string expression = GetParameter("expression", ele, ""); + + if (expression.empty()) { + RESTWarning << "TRestComponentFormula::InitFromConfigFile. Invalid formula" << RESTendl; + } else { + TFormula formula(name.c_str(), expression.c_str()); + + for (Int_t n = 0; n < formula.GetNpar(); n++) { + if (std::find(fVariables.begin(), fVariables.end(), formula.GetParName(n)) == + fVariables.end()) { + RESTError << "Variable : " << formula.GetParName(n) << " not found in component! " + << RESTendl; + RESTError << "TRestComponentFormula evaluation will lead to wrong results!" << RESTendl; + } + } + + for (const auto& varName : fVariables) formula.SetParameter(varName.c_str(), 0.0); + + fFormulas.push_back(formula); + } + + ele = GetNextElement(ele); + } +} From a4061d1edd967727101d1649d96ea06618861d97 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sat, 13 Jan 2024 21:25:10 +0100 Subject: [PATCH 089/174] TRestComponent. Introducing GetRawRate method. GetRate will return the rate with or without response --- .../sensitivity/inc/TRestComponent.h | 5 ++- .../sensitivity/inc/TRestComponentDataSet.h | 4 +-- .../sensitivity/inc/TRestComponentFormula.h | 2 +- .../sensitivity/src/TRestComponent.cxx | 34 ++++--------------- .../sensitivity/src/TRestComponentDataSet.cxx | 11 +++--- .../sensitivity/src/TRestComponentFormula.cxx | 12 +++---- 6 files changed, 20 insertions(+), 48 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 3483dbb11..25747c0fa 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -74,12 +74,11 @@ class TRestComponent : public TRestMetadata { public: TRestResponse* GetResponse() const { return fResponse; } - virtual Double_t GetRate(std::vector point) = 0; + virtual Double_t GetRawRate(std::vector point) = 0; virtual Double_t GetTotalRate() = 0; Double_t GetNormalizedRate(std::vector point); - Double_t GetRateWithResponse(std::vector point); - Double_t GetNormalizedRateWithResponse(std::vector point); + Double_t GetRate(std::vector point); size_t GetDimensions() { return fVariables.size(); } diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 865f1cb09..0782e881c 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -43,7 +43,7 @@ class TRestComponentDataSet : public TRestComponent { /// The generated N-dimensional variable space density for a given node std::vector fNodeDensity; //< - /// Enables or disables the interpolation at TRestComponentDataSet::GetRate + /// Enables or disables the interpolation at TRestComponentDataSet::GetRawRate Bool_t fInterpolation = true; /// TODO we need to define multiple datasets and weigth. The weight will be used @@ -77,7 +77,7 @@ class TRestComponentDataSet : public TRestComponent { void EnableInterpolation() { fInterpolation = true; } void DisableInterpolation() { fInterpolation = false; } - Double_t GetRate(std::vector point) override; + Double_t GetRawRate(std::vector point) override; Double_t GetTotalRate() override; Double_t GetBinCenter(Int_t nDim, const Int_t bin); diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index 22dc19c1c..224f9d563 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -41,7 +41,7 @@ class TRestComponentFormula : public TRestComponent { void InitFromConfigFile() override; public: - Double_t GetRate(std::vector point) override; + Double_t GetRawRate(std::vector point) override; Double_t GetTotalRate() override; void PrintMetadata() override; diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index c0fa88b2a..6596ab19e 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -96,26 +96,6 @@ Int_t TRestComponent::GetVariableIndex(std::string varName) { return -1; } -/////////////////////////////////////////////// -/// \brief It returns the intensity/rate (in seconds) corresponding to the -/// generated distribution or formula evaluated at the position of the parameter -/// space given by point. -/// -/// The rate returned by the TRestComponent::GetRate method is integrated to the -/// cell size for the given parameter space binning and range. This method will -/// return the normalized value. Thus, if the parameter consists of -/// 2-spatial dimensions and 1-energy dimension, the returned rate will be -/// expressed in standard REST units as, s-1 mm-2 keV-1. -/// -Double_t TRestComponent::GetNormalizedRate(std::vector point) { - Double_t normFactor = 1; - for (size_t n = 0; n < GetDimensions(); n++) { - normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); - } - - return normFactor * GetRate(point); -} - /////////////////////////////////////////////// /// \brief It returns the intensity/rate (in seconds) corresponding to the /// generated distribution or formula evaluated at the position of the parameter @@ -125,9 +105,9 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { /// The TRestResponse metadata class defines the variable where the response will /// be applied. /// -Double_t TRestComponent::GetRateWithResponse(std::vector point) { +Double_t TRestComponent::GetRate(std::vector point) { if (!fResponse) { - return GetRate(point); + return GetRawRate(point); } std::string responseVariable = fResponse->GetVariable(); @@ -147,7 +127,7 @@ Double_t TRestComponent::GetRateWithResponse(std::vector point) { for (const auto& resp : response) { std::vector newPoint = point; newPoint[respVarIndex] = resp.first; - rate += resp.second * GetRate(newPoint); + rate += resp.second * GetRawRate(newPoint); } return rate; @@ -158,7 +138,7 @@ Double_t TRestComponent::GetRateWithResponse(std::vector point) { /// generated distribution or formula evaluated at the position of the parameter /// space given by point. /// -/// The rate returned by the TRestComponent::GetRate method will be normalized +/// The rate returned by the TRestComponent::GetRawRate method will be normalized /// to the corresponding parameter space. Thus, if the parameter consists of /// 2-spatial dimensions and 1-energy dimension, the returned rate will be /// expressed in standard REST units as, s-1 mm-2 keV-1. @@ -167,18 +147,18 @@ Double_t TRestComponent::GetRateWithResponse(std::vector point) { /// the REST_Units namespace. /// /// \code -/// component->GetNormalizedRateWithResponse( {0,0,0} ) * units("cm^-2*keV^-1") +/// component->GetNormalizedRate( {0,0,0} ) * units("cm^-2*keV^-1") /// \endcode /// /// The response matrix (if defined) will be used to convolute the expected rate. /// The TRestResponse metadata class defines the variable where the response will /// be applied. /// -Double_t TRestComponent::GetNormalizedRateWithResponse(std::vector point) { +Double_t TRestComponent::GetNormalizedRate(std::vector point) { Double_t normFactor = 1; for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X()); - return normFactor * GetRateWithResponse(point); + return normFactor * GetRate(point); } /////////////////////////////////////////////// diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 313c43af4..4fa1418f1 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -152,7 +152,7 @@ void TRestComponentDataSet::Initialize() { /// /// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥 /// -Double_t TRestComponentDataSet::GetRate(std::vector point) { +Double_t TRestComponentDataSet::GetRawRate(std::vector point) { if (point.size() != GetDimensions()) { RESTError << "The size of the point given is : " << point.size() << RESTendl; RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl; @@ -268,15 +268,13 @@ TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVaria TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -730,8 +728,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precisi Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" - << RESTendl; + "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 89c20b9ec..df36a9f77 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -92,12 +92,12 @@ void TRestComponentFormula::Initialize() { /////////////////////////////////////////////// /// \brief It returns the intensity/rate (in seconds) corresponding to the /// generated distribution or formula evaluated at the position of the parameter -/// space given by point. +/// space given by point and integrated to the parameter space cell volume. /// /// The size of the point vector must have the same dimension as the dimensions -/// of the distribution. +/// of the variables of the distribution. /// -Double_t TRestComponentFormula::GetRate(std::vector point) { +Double_t TRestComponentFormula::GetRawRate(std::vector point) { if (fVariables.size() != point.size()) { RESTError << "Point should have same dimensions as number of variables!" << RESTendl; @@ -123,11 +123,7 @@ Double_t TRestComponentFormula::GetRate(std::vector point) { /// \brief This method integrates the rate to all the parameter space defined in the density function. /// The result will be returned in s-1. /// -Double_t TRestComponentFormula::GetTotalRate() { - Double_t integral = 0; - - return integral; -} +Double_t TRestComponentFormula::GetTotalRate() { return 0.0; } ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux From c9a2455adb7b1bb9060e51ac76561df2ce298904 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 15 Jan 2024 10:35:17 +0100 Subject: [PATCH 090/174] TRestMetadata. We ignore units inside string parameters --- source/framework/core/src/TRestMetadata.cxx | 24 +++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index 718f62daa..29e23ecba 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -1146,10 +1146,9 @@ void TRestMetadata::ReplaceForLoopVars(TiXmlElement* e, map forL } } - e->SetAttribute(name, ReplaceMathematicalExpressions( - outputBuffer, 0, - "Please, check parameter name: " + parName + " (ReplaceForLoopVars)") - .c_str()); + e->SetAttribute(name, ReplaceMathematicalExpressions(outputBuffer, 0, + "Please, check parameter name: " + parName + + " (ReplaceForLoopVars)").c_str()); } attr = attr->Next(); @@ -1306,8 +1305,7 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { TiXmlElement* ele = GetElementFromFile(filename); if (ele == nullptr) { RESTError << "TRestMetadata::ExpandIncludeFile. No xml elements contained in the include " - "file \"" - << filename << "\"" << RESTendl; + "file \"" << filename << "\"" << RESTendl; exit(1); } while (ele != nullptr) { @@ -1389,8 +1387,7 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { if (remoteele == nullptr) { RESTWarning << "Cannot find the needed xml section in " - "include file!" - << RESTendl; + "include file!" << RESTendl; RESTWarning << "type: \"" << type << "\" , name: \"" << name << "\" . Skipping" << RESTendl; RESTWarning << RESTendl; @@ -2273,8 +2270,7 @@ TString TRestMetadata::GetLibraryVersion() { return fLibraryVersion; } void TRestMetadata::ReSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" - << RESTendl; + "for a class!" << RESTendl; else { fVersion = REST_RELEASE; } @@ -2286,8 +2282,7 @@ void TRestMetadata::ReSetVersion() { void TRestMetadata::UnSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" - << RESTendl; + "for a class!" << RESTendl; else { fVersion = -1; fCommit = -1; @@ -2549,11 +2544,12 @@ void TRestMetadata::ReadOneParameter(string name, string value) { Double_t valueY = REST_Units::ConvertValueToRESTUnits(value.Y(), unit); Double_t valueZ = REST_Units::ConvertValueToRESTUnits(value.Z(), unit); *(TVector3*)datamember = TVector3(valueX, valueY, valueZ); + } else if (datamember.type == "string") { + // We just ignore this case } else { RESTWarning << this->ClassName() << " find unit definition in parameter: " << name << ", but the corresponding data member doesn't support it. Data " - "member type: " - << datamember.type << RESTendl; + "member type: " << datamember.type << RESTendl; datamember.ParseString(value); } } else { From ecabfcbf3a1a2ab5b5e5c024585b53cda610d2c3 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 15 Jan 2024 10:37:19 +0100 Subject: [PATCH 091/174] TRestComponent. Histograms are now a common component --- .../sensitivity/inc/TRestComponent.h | 41 +- .../sensitivity/src/TRestComponent.cxx | 393 +++++++++++++++++- 2 files changed, 421 insertions(+), 13 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 25747c0fa..befb1d856 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -32,7 +32,11 @@ /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { + protected: + /// It defines the component type (unknown/signal/background) + std::string fNature = "unknown"; //< + /// A list with the branches that will be used to create the distribution space std::vector fVariables; //< @@ -51,6 +55,12 @@ class TRestComponent : public TRestMetadata { /// It is used to define the node that will be accessed for rate retrieval Int_t fActiveNode = -1; //< + /// The generated N-dimensional variable space density for a given node + std::vector fNodeDensity; //< + + /// Enables or disables the interpolation at TRestComponentDataSet::GetRawRate + Bool_t fInterpolation = true; //< + /// A pointer to the detector response TRestResponse* fResponse = nullptr; //< @@ -71,11 +81,19 @@ class TRestComponent : public TRestMetadata { void InitFromConfigFile() override; + virtual void FillHistograms(Double_t precision = 0.01) = 0; + public: + std::string GetNature() const { return fNature; } TRestResponse* GetResponse() const { return fResponse; } - virtual Double_t GetRawRate(std::vector point) = 0; - virtual Double_t GetTotalRate() = 0; + Double_t GetRawRate(std::vector point); + Double_t GetTotalRate(); + + Double_t GetBinCenter(Int_t nDim, const Int_t bin); + + TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, + Int_t binScanSize = 1, TString drawOption = ""); Double_t GetNormalizedRate(std::vector point); Double_t GetRate(std::vector point); @@ -86,6 +104,22 @@ class TRestComponent : public TRestMetadata { Int_t SetActiveNode(Double_t node); Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } + Bool_t Interpolation() { return fInterpolation; } + void EnableInterpolation() { fInterpolation = true; } + void DisableInterpolation() { fInterpolation = false; } + + THnD* GetDensityForNode(Double_t value); + THnD* GetDensityForActiveNode(); + THnD* GetDensity() { return GetDensityForActiveNode(); } + + TH1D* GetHistogram(Double_t node, std::string varName); + TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); + TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); + + TH1D* GetHistogram(std::string varName); + TH2D* GetHistogram(std::string varName1, std::string varName2); + TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); + void LoadResponse(const TRestResponse& resp); void PrintMetadata() override; @@ -93,11 +127,10 @@ class TRestComponent : public TRestMetadata { void PrintStatistics(); void PrintNodes(); - void Initialize() override; TRestComponent(const char* cfgFileName, const std::string& name = ""); TRestComponent(); ~TRestComponent(); - ClassDefOverride(TRestComponent, 2); + ClassDefOverride(TRestComponent, 3); }; #endif diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 6596ab19e..9e0e5cdf6 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -41,15 +41,15 @@ #include "TRestComponent.h" #include - -#include "TKey.h" +#include +#include ClassImp(TRestComponent); /////////////////////////////////////////////// /// \brief Default constructor /// -TRestComponent::TRestComponent() { Initialize(); } +TRestComponent::TRestComponent() {} ///////////////////////////////////////////// /// \brief Constructor loading data from a config file @@ -76,12 +76,6 @@ TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name) /// TRestComponent::~TRestComponent() {} -/////////////////////////////////////////////// -/// \brief It will initialize the data frame with the filelist and column names -/// (or observables) that have been defined by the user. -/// -void TRestComponent::Initialize() { SetSectionName(this->ClassName()); } - /////////////////////////////////////////// /// \brief It returns the position of the fVariable element for the variable /// name given by argument. @@ -161,6 +155,278 @@ Double_t TRestComponent::GetNormalizedRate(std::vector point) { return normFactor * GetRate(point); } +/////////////////////////////////////////////// +/// \brief It returns the intensity/rate (in seconds) corresponding to the +/// generated distribution or formula evaluated at the position of the parameter +/// space given by point. The returned rate is integrated to the granularity +/// of the parameter space (cell size). To get a normalized rate use +/// TRestComponent::GetNormalizedRate. +/// +/// The size of the point vector must have the same dimension as the dimensions +/// of the distribution. +/// +/// If interpolation is enabled (which is disabled by default) the rate will be +/// evaluated using interpolation with neighbour histogram cells. +/// +/// Interpolation technique extracted from: +/// https://math.stackexchange.com/questions/1342364/formula-for-n-dimensional-linear-interpolation +/// +/// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥 +/// +Double_t TRestComponent::GetRawRate(std::vector point) { + if (point.size() != GetDimensions()) { + RESTError << "The size of the point given is : " << point.size() << RESTendl; + RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl; + RESTError << "Point must have the same dimension as the distribution" << RESTendl; + return 0; + } + + if (!HasNodes()) { + RESTError << "TRestComponent::GetRawRate. The component has no nodes!" << RESTendl; + RESTError << "Try calling TRestComponent::Initialize" << RESTendl; + + RESTInfo << "Trying to initialize" << RESTendl; + Initialize(); + if (HasNodes()) + RESTInfo << "Sucess!" << RESTendl; + else + return 0; + } + + if (HasNodes() && fActiveNode == -1) { + RESTError << "TRestComponent::GetRawRate. Active node has not been defined" << RESTendl; + return 0; + } + + Int_t centerBin[GetDimensions()]; + Double_t centralDensity = GetDensity()->GetBinContent(GetDensity()->GetBin(point.data()), centerBin); + if (!Interpolation()) return centralDensity; + + std::vector direction; + std::vector nDist; + for (size_t dim = 0; dim < GetDimensions(); dim++) { + Double_t x1 = GetBinCenter(dim, centerBin[dim] - 1); + Double_t x2 = GetBinCenter(dim, centerBin[dim] + 1); + + if (centerBin[dim] == 1 || centerBin[dim] == fNbins[dim]) { + direction.push_back(0); + nDist.push_back(0); + } else if (x2 - point[dim] > point[dim] - x1) { + // we chose left bin (x1) since it is closer than right bin + direction.push_back(-1); + nDist.push_back(1 - 2 * (point[dim] - x1) / (x2 - x1)); + } else { + direction.push_back(1); + nDist.push_back(1 - 2 * (x2 - point[dim]) / (x2 - x1)); + } + } + + // In 3-dimensions we got 8 points to interpolate + // In 4-dimensions we would get 16 points to interpolate + // ... + Int_t nPoints = (Int_t)TMath::Power(2, (Int_t)GetDimensions()); + + Double_t sum = 0; + for (int n = 0; n < nPoints; n++) { + std::vector cell = REST_StringHelper::IntegerToBinary(n, GetDimensions()); + + Double_t weightDistance = 1; + int cont = 0; + for (const auto& c : cell) { + if (c == 0) + weightDistance *= (1 - nDist[cont]); + else + weightDistance *= nDist[cont]; + cont++; + } + + for (size_t k = 0; k < cell.size(); k++) cell[k] = cell[k] * direction[k] + centerBin[k]; + + Double_t density = GetDensity()->GetBinContent(cell.data()); + sum += density * weightDistance; + } + + return sum; +} + +/////////////////////////////////////////////// +/// \brief This method integrates the rate to all the parameter space defined in the density function. +/// The result will be returned in s-1. +/// +Double_t TRestComponent::GetTotalRate() { + THnD* dHist = GetDensityForActiveNode(); + + Double_t integral = 0; + if (dHist != nullptr) integral = dHist->ComputeIntegral(); + + // Perhaps this value could be stored internally + for (size_t n = 0; n < fNbins.size(); n++) + integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + + return integral; +} + +/////////////////////////////////////////////// +/// \brief It returns the bin center of the given component dimension. +/// +/// It required implementation since I did not find a method inside THnD. Surprising. +/// +Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) { + return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim]; +} + +/////////////////////////////////////////////// +/// \brief A method allowing to draw a series of plots representing the density distributions. +/// +/// The method will produce 1- or 2-dimensional histograms of the `drawVariables` given in the +/// argument. A third scan variable must be provided in order to show the distribution slices +/// along the scan variable. +/// +/// The binScanSize argument can be used to define the binSize of the scanning variables. +/// +TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, + std::vector scanVariables, Int_t binScanSize, + TString drawOption) { + if (drawVariables.size() > 2 || drawVariables.size() == 0) { + RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " + "be 1 or 2!" << RESTendl; + return fCanvas; + } + + if (scanVariables.size() > 2 || scanVariables.size() == 0) { + RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " + "be 1 or 2!" << RESTendl; + return fCanvas; + } + + //// Checking the number of plots to be generated + std::vector scanIndexes; + for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x)); + + Int_t nPlots = 1; + size_t n = 0; + for (const auto& x : scanIndexes) { + if (fNbins[x] % binScanSize != 0) { + RESTWarning << "The variable " << scanVariables[n] << " contains " << fNbins[x] + << " bins and it doesnt match with a bin size " << binScanSize << RESTendl; + RESTWarning << "The bin size must be a multiple of the number of bins." << RESTendl; + RESTWarning << "Redefining bin size to 1." << RESTendl; + binScanSize = 1; + } + nPlots *= fNbins[x] / binScanSize; + n++; + } + + /// Finding canvas division scheme + Int_t nPlotsX = 0; + Int_t nPlotsY = 0; + + if (scanIndexes.size() == 2) { + nPlotsX = fNbins[scanIndexes[0]] / binScanSize; + nPlotsY = fNbins[scanIndexes[1]] / binScanSize; + } else { + nPlotsX = TRestTools::CanvasDivisions(nPlots)[1]; + nPlotsY = TRestTools::CanvasDivisions(nPlots)[0]; + } + + RESTInfo << "Number of plots to be generated: " << nPlots << RESTendl; + RESTInfo << "Canvas size : " << nPlotsX << " x " << nPlotsY << RESTendl; + + //// Setting up the canvas with the appropriate number of divisions + if (fCanvas != nullptr) { + delete fCanvas; + fCanvas = nullptr; + } + + fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, nPlotsX * 640, nPlotsY * 480); + fCanvas->Divide(nPlotsX, nPlotsY, 0.01, 0.01); + + std::vector variableIndexes; + for (const auto& x : drawVariables) variableIndexes.push_back(GetVariableIndex(x)); + + for (int n = 0; n < nPlotsX; n++) + for (int m = 0; m < nPlotsY; m++) { + TPad* pad = (TPad*)fCanvas->cd(n * nPlotsY + m + 1); + pad->SetFixedAspectRatio(true); + + THnD* hnd = GetDensity(); + + int binXo = binScanSize * n + 1; + int binXf = binScanSize * n + binScanSize; + int binYo = binScanSize * m + 1; + int binYf = binScanSize * m + binScanSize; + + if (scanVariables.size() == 2) { + hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); + hnd->GetAxis(scanIndexes[1])->SetRange(binYo, binYf); + } else if (scanVariables.size() == 1) { + binXo = binScanSize * nPlotsY * n + binScanSize * m + 1; + binXf = binScanSize * nPlotsY * n + binScanSize * m + binScanSize; + hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); + } + + if (variableIndexes.size() == 1) { + TH1D* h1 = hnd->Projection(variableIndexes[0]); + std::string hName; + + if (scanIndexes.size() == 2) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + + IntegerToString(binYf) + ") "; + + if (scanIndexes.size() == 1) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") "; + + TH1D* newh = (TH1D*)h1->Clone(hName.c_str()); + newh->SetTitle(hName.c_str()); + newh->SetStats(false); + newh->GetXaxis()->SetTitle((TString)drawVariables[0]); + newh->SetMarkerStyle(kFullCircle); + newh->Draw("PLC PMC"); + + TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); + TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); + textLatex->SetNDC(); + textLatex->SetTextColor(1); + textLatex->SetTextSize(0.05); + textLatex->Draw("same"); + delete h1; + } + + if (variableIndexes.size() == 2) { + TH2D* h2 = hnd->Projection(variableIndexes[0], variableIndexes[1]); + + std::string hName; + if (scanIndexes.size() == 2) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + + IntegerToString(binYf) + ") "; + + if (scanIndexes.size() == 1) + hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + + ") "; + + TH2D* newh = (TH2D*)h2->Clone(hName.c_str()); + newh->SetStats(false); + newh->GetXaxis()->SetTitle((TString)drawVariables[0]); + newh->GetYaxis()->SetTitle((TString)drawVariables[1]); + newh->SetTitle(hName.c_str()); + newh->Draw(drawOption); + + TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); + TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); + textLatex->SetNDC(); + textLatex->SetTextColor(1); + textLatex->SetTextSize(0.05); + textLatex->Draw("same"); + delete h2; + } + } + + return fCanvas; +} + /////////////////////////////////////////////// /// \brief /// @@ -182,6 +448,9 @@ void TRestComponent::LoadResponse(const TRestResponse& resp) { void TRestComponent::PrintMetadata() { TRestMetadata::PrintMetadata(); + RESTMetadata << "Component nature : " << fNature << RESTendl; + RESTMetadata << " " << RESTendl; + if (fVariables.size() != fRanges.size()) RESTWarning << "The number of variables does not match with the number of defined ranges!" << RESTendl; @@ -281,3 +550,109 @@ Int_t TRestComponent::SetActiveNode(Double_t node) { return fActiveNode; } + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponent::GetDensityForNode(Double_t node) { + int n = 0; + for (const auto& x : fParameterizationNodes) { + if (x == node) { + return fNodeDensity[n]; + } + n++; + } + + RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief +/// +THnD* TRestComponent::GetDensityForActiveNode() { + if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; + + RESTError << "The active node is invalid" << RESTendl; + PrintNodes(); + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH1D* TRestComponent::GetHistogram(Double_t node, std::string varName) { + SetActiveNode(node); + return GetHistogram(varName); +} + +///////////////////////////////////////////// +/// \brief It returns a 1-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH1D* TRestComponent::GetHistogram(std::string varName) { + if (fActiveNode < 0) return nullptr; + + Int_t v1 = GetVariableIndex(varName); + + if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 2-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH2D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2) { + SetActiveNode(node); + return GetHistogram(varName1, varName2); +} + +///////////////////////////////////////////// +/// \brief It returns a 2-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH2D* TRestComponent::GetHistogram(std::string varName1, std::string varName2) { + if (fActiveNode < 0) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + + if (v1 >= 0 && v2 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); + + return nullptr; +} + +///////////////////////////////////////////// +/// \brief It returns the 3-dimensional projected histogram for the variable names +/// provided in the argument +/// +TH3D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2, + std::string varName3) { + SetActiveNode(node); + return GetHistogram(varName1, varName2, varName3); +} + +///////////////////////////////////////////// +/// \brief It returns a 3-dimensional projected histogram for the variable names +/// provided in the argument. It will recover the histogram corresponding to +/// the active node. +/// +TH3D* TRestComponent::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { + if (fActiveNode < 0) return nullptr; + + Int_t v1 = GetVariableIndex(varName1); + Int_t v2 = GetVariableIndex(varName2); + Int_t v3 = GetVariableIndex(varName3); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0) + if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); + + return nullptr; +} From 679e695ed3154bf67617f74c482639e5463ddda8 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 15 Jan 2024 10:38:29 +0100 Subject: [PATCH 092/174] TRestComponentDataSet. Removing related histogram methods --- .../sensitivity/inc/TRestComponentDataSet.h | 36 +- .../sensitivity/src/TRestComponentDataSet.cxx | 405 +----------------- 2 files changed, 19 insertions(+), 422 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 0782e881c..6ee671c84 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -40,12 +40,6 @@ class TRestComponentDataSet : public TRestComponent { /// The filename of the dataset used std::vector fDataSetFileNames; //< - /// The generated N-dimensional variable space density for a given node - std::vector fNodeDensity; //< - - /// Enables or disables the interpolation at TRestComponentDataSet::GetRawRate - Bool_t fInterpolation = true; - /// TODO we need to define multiple datasets and weigth. The weight will be used /// to create a model, such as weighting different background contaminations or /// different signal coupling contributions. @@ -63,8 +57,8 @@ class TRestComponentDataSet : public TRestComponent { protected: std::vector ExtractParameterizationNodes(); - std::vector ExtractNodeStatistics(Double_t precision = 0.00000001); - void FillHistograms(); + std::vector ExtractNodeStatistics(Double_t precision = 0.01); + void FillHistograms(Double_t precision = 0.01) override; Bool_t VariablesOk(); Bool_t WeightsOk(); @@ -73,30 +67,6 @@ class TRestComponentDataSet : public TRestComponent { Bool_t LoadDataSets(); Bool_t IsDataSetLoaded() { return fDataSetLoaded; } - Bool_t Interpolation() { return fInterpolation; } - void EnableInterpolation() { fInterpolation = true; } - void DisableInterpolation() { fInterpolation = false; } - - Double_t GetRawRate(std::vector point) override; - Double_t GetTotalRate() override; - - Double_t GetBinCenter(Int_t nDim, const Int_t bin); - - TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, - Int_t binScanSize = 1, TString drawOption = ""); - - THnD* GetDensityForNode(Double_t value); - THnD* GetDensityForActiveNode(); - THnD* GetDensity() { return GetDensityForActiveNode(); } - - TH1D* GetHistogram(Double_t node, std::string varName); - TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2); - TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3); - - TH1D* GetHistogram(std::string varName); - TH2D* GetHistogram(std::string varName1, std::string varName2); - TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); - void PrintStatistics(); void PrintMetadata() override; @@ -107,6 +77,6 @@ class TRestComponentDataSet : public TRestComponent { TRestComponentDataSet(const char* cfgFileName, const std::string& name); ~TRestComponentDataSet(); - ClassDefOverride(TRestComponentDataSet, 2); + ClassDefOverride(TRestComponentDataSet, 3); }; #endif diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 4fa1418f1..e229f6390 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -85,8 +85,6 @@ #include "TRestComponentDataSet.h" #include -#include - #include ClassImp(TRestComponentDataSet); @@ -94,7 +92,7 @@ ClassImp(TRestComponentDataSet); /////////////////////////////////////////////// /// \brief Default constructor /// -TRestComponentDataSet::TRestComponentDataSet() { Initialize(); } +TRestComponentDataSet::TRestComponentDataSet() {} /////////////////////////////////////////////// /// \brief Default destructor @@ -117,7 +115,6 @@ TRestComponentDataSet::~TRestComponentDataSet() {} /// TRestComponentDataSet::TRestComponentDataSet(const char* cfgFileName, const std::string& name) : TRestComponent(cfgFileName) { - Initialize(); LoadConfigFromFile(fConfigFileName, name); @@ -132,278 +129,8 @@ void TRestComponentDataSet::Initialize() { TRestComponent::Initialize(); SetSectionName(this->ClassName()); -} - -/////////////////////////////////////////////// -/// \brief It returns the intensity/rate (in seconds) corresponding to the -/// generated distribution or formula evaluated at the position of the parameter -/// space given by point. The returned rate is integrated to the granularity -/// of the parameter space (cell size). To get a normalized rate use -/// TRestComponent::GetNormalizedRate. -/// -/// The size of the point vector must have the same dimension as the dimensions -/// of the distribution. -/// -/// If interpolation is enabled (which is disabled by default) the rate will be -/// evaluated using interpolation with neighbour histogram cells. -/// -/// Interpolation technique extracted from: -/// https://math.stackexchange.com/questions/1342364/formula-for-n-dimensional-linear-interpolation -/// -/// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥 -/// -Double_t TRestComponentDataSet::GetRawRate(std::vector point) { - if (point.size() != GetDimensions()) { - RESTError << "The size of the point given is : " << point.size() << RESTendl; - RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl; - RESTError << "Point must have the same dimensions as the distribution" << RESTendl; - return 0; - } - - if (!HasNodes()) { - RESTError << "TRestComponentDataSet::GetRate. The component has no nodes!" << RESTendl; - RESTError << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; - - RESTInfo << "Trying to load datasets" << RESTendl; - LoadDataSets(); - if (IsDataSetLoaded()) - RESTInfo << "Sucess!" << RESTendl; - else - return 0; - } - if (HasNodes() && fActiveNode == -1) { - RESTError << "TRestComponentDataSet::GetRate. Active node has not been defined" << RESTendl; - return 0; - } - - Int_t centerBin[GetDimensions()]; - Double_t centralDensity = GetDensity()->GetBinContent(GetDensity()->GetBin(point.data()), centerBin); - if (!Interpolation()) return centralDensity; - - std::vector direction; - std::vector nDist; - for (size_t dim = 0; dim < GetDimensions(); dim++) { - Double_t x1 = GetBinCenter(dim, centerBin[dim] - 1); - Double_t x2 = GetBinCenter(dim, centerBin[dim] + 1); - - if (centerBin[dim] == 1 || centerBin[dim] == fNbins[dim]) { - direction.push_back(0); - nDist.push_back(0); - } else if (x2 - point[dim] > point[dim] - x1) { - // we chose left bin (x1) since it is closer than right bin - direction.push_back(-1); - nDist.push_back(1 - 2 * (point[dim] - x1) / (x2 - x1)); - } else { - direction.push_back(1); - nDist.push_back(1 - 2 * (x2 - point[dim]) / (x2 - x1)); - } - } - - // In 3-dimensions we got 8 points to interpolate - // In 4-dimensions we would get 16 points to interpolate - // ... - Int_t nPoints = (Int_t)TMath::Power(2, (Int_t)GetDimensions()); - - Double_t sum = 0; - for (int n = 0; n < nPoints; n++) { - std::vector cell = REST_StringHelper::IntegerToBinary(n, GetDimensions()); - - Double_t weightDistance = 1; - int cont = 0; - for (const auto& c : cell) { - if (c == 0) - weightDistance *= (1 - nDist[cont]); - else - weightDistance *= nDist[cont]; - cont++; - } - - for (size_t k = 0; k < cell.size(); k++) cell[k] = cell[k] * direction[k] + centerBin[k]; - - Double_t density = GetDensity()->GetBinContent(cell.data()); - sum += density * weightDistance; - } - - return sum; -} - -/////////////////////////////////////////////// -/// \brief This method integrates the rate to all the parameter space defined in the density function. -/// The result will be returned in s-1. -/// -Double_t TRestComponentDataSet::GetTotalRate() { - THnD* dHist = GetDensityForActiveNode(); - - Double_t integral = 0; - if (dHist != nullptr) integral = dHist->ComputeIntegral(); - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) - integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; - - return integral; -} - -/////////////////////////////////////////////// -/// \brief It returns the bin center of the given component dimension. -/// -/// It required implementation since I did not find a method inside THnD. Surprising. -/// -Double_t TRestComponentDataSet::GetBinCenter(Int_t nDim, const Int_t bin) { - return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim]; -} - -/////////////////////////////////////////////// -/// \brief A method allowing to draw a series of plots representing the density distributions. -/// -/// The method will produce 1- or 2-dimensional histograms of the `drawVariables` given in the -/// argument. A third scan variable must be provided in order to show the distribution slices -/// along the scan variable. -/// -/// The binScanSize argument can be used to define the binSize of the scanning variables. -/// -TCanvas* TRestComponentDataSet::DrawComponent(std::vector drawVariables, - std::vector scanVariables, Int_t binScanSize, - TString drawOption) { - if (drawVariables.size() > 2 || drawVariables.size() == 0) { - RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; - return fCanvas; - } - - if (scanVariables.size() > 2 || scanVariables.size() == 0) { - RESTError << "TRestComponentDataSet::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; - return fCanvas; - } - - //// Checking the number of plots to be generated - std::vector scanIndexes; - for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x)); - - Int_t nPlots = 1; - size_t n = 0; - for (const auto& x : scanIndexes) { - if (fNbins[x] % binScanSize != 0) { - RESTWarning << "The variable " << scanVariables[n] << " contains " << fNbins[x] - << " bins and it doesnt match with a bin size " << binScanSize << RESTendl; - RESTWarning << "The bin size must be a multiple of the number of bins." << RESTendl; - RESTWarning << "Redefining bin size to 1." << RESTendl; - binScanSize = 1; - } - nPlots *= fNbins[x] / binScanSize; - n++; - } - - /// Finding canvas division scheme - Int_t nPlotsX = 0; - Int_t nPlotsY = 0; - - if (scanIndexes.size() == 2) { - nPlotsX = fNbins[scanIndexes[0]] / binScanSize; - nPlotsY = fNbins[scanIndexes[1]] / binScanSize; - } else { - nPlotsX = TRestTools::CanvasDivisions(nPlots)[1]; - nPlotsY = TRestTools::CanvasDivisions(nPlots)[0]; - } - - RESTInfo << "Number of plots to be generated: " << nPlots << RESTendl; - RESTInfo << "Canvas size : " << nPlotsX << " x " << nPlotsY << RESTendl; - - //// Setting up the canvas with the appropriate number of divisions - if (fCanvas != nullptr) { - delete fCanvas; - fCanvas = nullptr; - } - - fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, nPlotsX * 640, nPlotsY * 480); - fCanvas->Divide(nPlotsX, nPlotsY, 0.01, 0.01); - - std::vector variableIndexes; - for (const auto& x : drawVariables) variableIndexes.push_back(GetVariableIndex(x)); - - for (int n = 0; n < nPlotsX; n++) - for (int m = 0; m < nPlotsY; m++) { - TPad* pad = (TPad*)fCanvas->cd(n * nPlotsY + m + 1); - pad->SetFixedAspectRatio(true); - - THnD* hnd = GetDensity(); - - int binXo = binScanSize * n + 1; - int binXf = binScanSize * n + binScanSize; - int binYo = binScanSize * m + 1; - int binYf = binScanSize * m + binScanSize; - - if (scanVariables.size() == 2) { - hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); - hnd->GetAxis(scanIndexes[1])->SetRange(binYo, binYf); - } else if (scanVariables.size() == 1) { - binXo = binScanSize * nPlotsY * n + binScanSize * m + 1; - binXf = binScanSize * nPlotsY * n + binScanSize * m + binScanSize; - hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf); - } - - if (variableIndexes.size() == 1) { - TH1D* h1 = hnd->Projection(variableIndexes[0]); - std::string hName; - - if (scanIndexes.size() == 2) - hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + - ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + - IntegerToString(binYf) + ") "; - - if (scanIndexes.size() == 1) - hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + - ") "; - - TH1D* newh = (TH1D*)h1->Clone(hName.c_str()); - newh->SetTitle(hName.c_str()); - newh->SetStats(false); - newh->GetXaxis()->SetTitle((TString)drawVariables[0]); - newh->SetMarkerStyle(kFullCircle); - newh->Draw("PLC PMC"); - - TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); - TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); - textLatex->SetNDC(); - textLatex->SetTextColor(1); - textLatex->SetTextSize(0.05); - textLatex->Draw("same"); - delete h1; - } - - if (variableIndexes.size() == 2) { - TH2D* h2 = hnd->Projection(variableIndexes[0], variableIndexes[1]); - - std::string hName; - if (scanIndexes.size() == 2) - hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + - ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " + - IntegerToString(binYf) + ") "; - - if (scanIndexes.size() == 1) - hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) + - ") "; - - TH2D* newh = (TH2D*)h2->Clone(hName.c_str()); - newh->SetStats(false); - newh->GetXaxis()->SetTitle((TString)drawVariables[0]); - newh->GetYaxis()->SetTitle((TString)drawVariables[1]); - newh->SetTitle(hName.c_str()); - newh->Draw(drawOption); - - TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries()); - TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr); - textLatex->SetNDC(); - textLatex->SetTextColor(1); - textLatex->SetTextSize(0.05); - textLatex->Draw("same"); - delete h2; - } - } - - return fCanvas; + LoadDataSets(); } ///////////////////////////////////////////// @@ -475,14 +202,18 @@ void TRestComponentDataSet::InitFromConfigFile() { fDataSetFileNames.push_back(GetParameter("filename", ele, "")); ele = GetNextElement(ele); } + + if (!fDataSetFileNames.empty()) Initialize(); } ///////////////////////////////////////////// /// \brief It will produce a histogram with the distribution defined using the /// variables and the weights for each of the parameter nodes. /// -void TRestComponentDataSet::FillHistograms() { - fNSimPerNode = ExtractNodeStatistics(); +/// The precision is used to define the active node +/// +void TRestComponentDataSet::FillHistograms(Double_t precision) { + fNSimPerNode = ExtractNodeStatistics(precision); if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::FillHistograms. Dataset has not been initialized!" << RESTendl; @@ -508,7 +239,10 @@ void TRestComponentDataSet::FillHistograms() { } else { RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) << RESTendl; - std::string filter = fParameter + " == " + DoubleToString(node); + Double_t pUp = node * (1 + precision / 2); + Double_t pDown = node * (1 - precision / 2); + std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + + DoubleToString(pDown); df = fDataSet.GetDataFrame().Filter(filter); } @@ -548,112 +282,6 @@ void TRestComponentDataSet::FillHistograms() { } } -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponentDataSet::GetDensityForNode(Double_t node) { - int n = 0; - for (const auto& x : fParameterizationNodes) { - if (x == node) { - return fNodeDensity[n]; - } - n++; - } - - RESTError << "Parametric node : " << node << " was not found in component" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief -/// -THnD* TRestComponentDataSet::GetDensityForActiveNode() { - if (fActiveNode >= 0) return fNodeDensity[fActiveNode]; - - RESTError << "The active node is invalid" << RESTendl; - PrintNodes(); - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH1D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName) { - SetActiveNode(node); - return GetHistogram(varName); -} - -///////////////////////////////////////////// -/// \brief It returns a 1-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH1D* TRestComponentDataSet::GetHistogram(std::string varName) { - if (fActiveNode < 0) return nullptr; - - Int_t v1 = GetVariableIndex(varName); - - if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 2-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH2D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2) { - SetActiveNode(node); - return GetHistogram(varName1, varName2); -} - -///////////////////////////////////////////// -/// \brief It returns a 2-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH2D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2) { - if (fActiveNode < 0) return nullptr; - - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - - if (v1 >= 0 && v2 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2); - - return nullptr; -} - -///////////////////////////////////////////// -/// \brief It returns the 3-dimensional projected histogram for the variable names -/// provided in the argument -/// -TH3D* TRestComponentDataSet::GetHistogram(Double_t node, std::string varName1, std::string varName2, - std::string varName3) { - SetActiveNode(node); - return GetHistogram(varName1, varName2, varName3); -} - -///////////////////////////////////////////// -/// \brief It returns a 3-dimensional projected histogram for the variable names -/// provided in the argument. It will recover the histogram corresponding to -/// the active node. -/// -TH3D* TRestComponentDataSet::GetHistogram(std::string varName1, std::string varName2, std::string varName3) { - if (fActiveNode < 0) return nullptr; - - Int_t v1 = GetVariableIndex(varName1); - Int_t v2 = GetVariableIndex(varName2); - Int_t v3 = GetVariableIndex(varName3); - - if (v1 >= 0 && v2 >= 0 && v3 >= 0) - if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3); - - return nullptr; -} - ///////////////////////////////////////////// /// \brief It returns a vector with all the different values found on /// the dataset column for the user given parameterization variable. @@ -693,7 +321,8 @@ std::vector TRestComponentDataSet::ExtractParameterizationNodes() { /// If fNSimPerNode has already been initialized it will directly return its value. /// /// The argument precision will be used to include a thin range where to select -/// the node values. +/// the node values. The value defines the range with a fraction proportional to +/// the parameter value. /// std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precision) { if (!fNSimPerNode.empty()) return fNSimPerNode; @@ -708,8 +337,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precisi RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; for (const auto& p : fParameterizationNodes) { - Double_t pUp = p + precision / 2; - Double_t pDown = p - precision / 2; + Double_t pUp = p * (1 + precision / 2); + Double_t pDown = p * (1 - precision / 2); std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); RESTInfo << "Counting stats for : " << fParameter << " = " << p << RESTendl; @@ -727,8 +356,6 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precisi /// Bool_t TRestComponentDataSet::LoadDataSets() { if (fDataSetFileNames.empty()) { - RESTWarning << "Dataset filename was not defined. You may still use " - "TRestComponentDataSet::LoadDataSet( filename );" << RESTendl; fDataSetLoaded = false; return fDataSetLoaded; } From 3c9f06931d5bedf681cca5b13546804a13c01846 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 15 Jan 2024 10:42:04 +0100 Subject: [PATCH 093/174] TRestComponentFormula::FillHistograms has been implemented --- .../sensitivity/inc/TRestComponentFormula.h | 5 +- .../sensitivity/src/TRestComponentFormula.cxx | 95 +++++++++++++++++-- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index 224f9d563..3ea49d7b2 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -40,9 +40,10 @@ class TRestComponentFormula : public TRestComponent { protected: void InitFromConfigFile() override; + void FillHistograms(Double_t precision = 0.01) override; + public: - Double_t GetRawRate(std::vector point) override; - Double_t GetTotalRate() override; + Double_t GetFormulaRate(std::vector point); void PrintMetadata() override; diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index df36a9f77..5dcf1e14c 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -24,6 +24,20 @@ /// This class allows to ... /// /// +/// +/// +/// +/// +/// +/// +/// +/// // A spatial gaussian component contribution (energy normalized) +/// +/// // A flat contribution +/// +/// +/// ///---------------------------------------------------------------------- /// /// REST-for-Physics - Software for Rare Event Searches Toolkit @@ -87,17 +101,19 @@ void TRestComponentFormula::Initialize() { TRestComponent::Initialize(); SetSectionName(this->ClassName()); + + FillHistograms(); } /////////////////////////////////////////////// /// \brief It returns the intensity/rate (in seconds) corresponding to the -/// generated distribution or formula evaluated at the position of the parameter -/// space given by point and integrated to the parameter space cell volume. +/// formula evaluated at the position of the parameter space given by point +/// and integrated to the parameter space cell volume. /// /// The size of the point vector must have the same dimension as the dimensions /// of the variables of the distribution. /// -Double_t TRestComponentFormula::GetRawRate(std::vector point) { +Double_t TRestComponentFormula::GetFormulaRate(std::vector point) { if (fVariables.size() != point.size()) { RESTError << "Point should have same dimensions as number of variables!" << RESTendl; @@ -119,11 +135,74 @@ Double_t TRestComponentFormula::GetRawRate(std::vector point) { return normFactor * result / units(fUnits); } -/////////////////////////////////////////////// -/// \brief This method integrates the rate to all the parameter space defined in the density function. -/// The result will be returned in s-1. +///////////////////////////////////////////// +/// \brief It will produce a histogram with the distribution using the formula +/// contributions. +/// +/// For the moment this method will just fill one node (without fParameter). But +/// if the component expression depends on the node parameter it might require +/// further development. /// -Double_t TRestComponentFormula::GetTotalRate() { return 0.0; } +void TRestComponentFormula::FillHistograms(Double_t precision) { + + if (fFormulas.empty()) return; + + if (fParameterizationNodes.empty()) { + RESTWarning << "Nodes have not been defined" << RESTendl; + RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; + fParameterizationNodes.push_back(-137); + } + + RESTInfo << "Generating N-dim histogram" << RESTendl; + + TString hName = "formula"; + + Int_t* bins = new Int_t[fNbins.size()]; + Double_t* xlow = new Double_t[fNbins.size()]; + Double_t* xhigh = new Double_t[fNbins.size()]; + + for (size_t n = 0; n < fNbins.size(); n++) { + bins[n] = fNbins[n]; + xlow[n] = fRanges[n].X(); + xhigh[n] = fRanges[n].Y(); + } + + THnD* hNd = new THnD(hName, hName, fNbins.size(), bins, xlow, xhigh); + + // Calculate the bin width in each dimension + std::vector binWidths; + for (size_t i = 0; i < fNbins.size(); ++i) { + double width = static_cast(xhigh[i] - xlow[i]) / bins[i]; + binWidths.push_back(width); + } + + // Nested loop to iterate over each bin and print its center + std::vector binIndices(fNbins.size(), 0); // Initialize bin indices to 0 in each dimension + + bool carry = false; + while (!carry) { + // Calculate the center of the current bin in each dimension + std::vector binCenter; + for (size_t i = 0; i < fNbins.size(); ++i) + binCenter.push_back(xlow[i] + (binIndices[i] + 0.5) * binWidths[i]); + + hNd->Fill(binCenter.data(), GetFormulaRate(binCenter)); + + // Update bin indices for the next iteration + carry = true; + for (size_t i = 0; i < fNbins.size(); ++i) { + binIndices[i]++; + if (binIndices[i] < bins[i]) { + carry = false; + break; + } + binIndices[i] = 0; + } + } + + fNodeDensity.push_back(hNd); + fActiveNode = 0; // For the moment only 1-node! +} ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux @@ -181,4 +260,6 @@ void TRestComponentFormula::InitFromConfigFile() { ele = GetNextElement(ele); } + + if (!fFormulas.empty()) Initialize(); } From 84d71dbbce4e5bf54b0fa716d0bf86c04f191877 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 15 Jan 2024 10:45:53 +0100 Subject: [PATCH 094/174] TRestComponentDataSet::LoadDataSets is now protected --- source/framework/sensitivity/inc/TRestComponentDataSet.h | 3 ++- source/framework/sensitivity/src/TRestComponentDataSet.cxx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 6ee671c84..c8dca1321 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -63,8 +63,9 @@ class TRestComponentDataSet : public TRestComponent { Bool_t VariablesOk(); Bool_t WeightsOk(); - public: Bool_t LoadDataSets(); + + public: Bool_t IsDataSetLoaded() { return fDataSetLoaded; } void PrintStatistics(); diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index e229f6390..906fa0890 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -175,7 +175,7 @@ void TRestComponentDataSet::PrintStatistics() { if (!HasNodes() && !IsDataSetLoaded()) { RESTWarning << "TRestComponentDataSet::PrintStatistics. Empty nodes and no dataset loaded!" << RESTendl; - RESTWarning << "Invoking TRestComponentDataSet::LoadDataSets might solve the problem" << RESTendl; + RESTWarning << "Invoking TRestComponentDataSet::Initialize() might solve the problem" << RESTendl; return; } @@ -436,7 +436,7 @@ Bool_t TRestComponentDataSet::WeightsOk() { Bool_t TRestComponentDataSet::ValidDataSet() { if (!IsDataSetLoaded()) { RESTWarning << "TRestComponentDataSet::ValidDataSet. Dataset has not been loaded" << RESTendl; - RESTWarning << "Try calling TRestComponentDataSet::LoadDataSets" << RESTendl; + RESTWarning << "Try calling TRestComponentDataSet::Initialize()" << RESTendl; RESTInfo << "Trying to load datasets" << RESTendl; LoadDataSets(); From 12211a477271e8c85dca2f9174af795ed43fe7a4 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 10:13:35 +0100 Subject: [PATCH 095/174] TRestComponent::GetRandom/GetMonteCarloDataFrame methods added --- .../sensitivity/inc/TRestComponent.h | 4 ++ .../sensitivity/src/TRestComponent.cxx | 72 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index befb1d856..ed4478f20 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -120,6 +120,10 @@ class TRestComponent : public TRestMetadata { TH2D* GetHistogram(std::string varName1, std::string varName2); TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); + std::vector GetRandom(); + + ROOT::RDF::RNode GetMonteCarloDataFrame(Int_t N = 100); + void LoadResponse(const TRestResponse& resp); void PrintMetadata() override; diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 9e0e5cdf6..20f202e46 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -44,6 +44,9 @@ #include #include +#include +#include + ClassImp(TRestComponent); /////////////////////////////////////////////// @@ -275,6 +278,75 @@ Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) { return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim]; } +std::vector TRestComponent::GetRandom() { + Double_t* tuple = new Double_t[GetDimensions()]; + GetDensity()->GetRandom(tuple); + + std::vector result; + for (size_t n = 0; n < GetDimensions(); n++) result.push_back(tuple[n]); + return result; +} + +ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { + // Create a RDataFrame with the specified columns + ROOT::RDF::RNode df = ROOT::RDataFrame(N); + + // Function to fill the RDataFrame using GetRandom method + auto fillDataFrame = [this]() { + auto randomValues = GetRandom(); + + // Check if the size of randomValues matches the size of columnNames + if (randomValues.size() != fVariables.size()) { + throw std::runtime_error("Mismatch in sizes of fVariables and randomValues"); + } + }; + + // Fill the RDataFrame with values from GetRandom + for (size_t i = 0; i < fVariables.size(); ++i) { + df = df.Define(fVariables[i], [&randomValues, &i]() { return randomValues[i]; }); + } + + // Apply the fillDataFrame function to each entry in the RDataFrame + df.Foreach(fillDataFrame); + + std::cout << df.GetColumnNames().size() << std::endl; + + for (const auto& x : df.GetColumnNames()) std::cout << x << std::endl; + + // Return the RNode (RDataFrame) + return df; +} + +/* +ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame( Int_t N ) +{ + // Create a data frame with 100 rows + ROOT::RDataFrame df(100); + + // Define a new column `x` that contains random numbers + //auto rdf_x = rdf.Define("x", [](){ return fBackground->GetRandom()[0]; }); + + std::vector trackingCount = GetRandom(); + //auto rdf_x = rdf.Define("random_value", [this]() { return GetRandom()[0]; }); + + // Fill the RDataFrame with correlated values + for (size_t i = 0; i < fVariables.size(); ++i) { + const auto& column_name = fVariables[i]; + std::cout << "Name :" << column_name << std::endl; + df.Define(column_name, [trackingCount, i]() { return trackingCount[i]; }); + } + + for( const auto &c: df.GetColumnNames() ) + std::cout << c << std::endl; + auto a = df.Count(); + std::cout << *a << std::endl; + + df.Display({"final_posX"})->Print(); + + return df; +} +*/ + /////////////////////////////////////////////// /// \brief A method allowing to draw a series of plots representing the density distributions. /// From 52dfa14fae1081ccb5dd2c31dd7a38d16f16f122 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:14:28 +0000 Subject: [PATCH 096/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- macros/REST_AddComponent.C | 2 +- .../framework/core/inc/TRestSystemOfUnits.h | 12 +--------- source/framework/core/src/TRestMetadata.cxx | 22 ++++++++++++------- .../sensitivity/inc/TRestComponent.h | 1 - .../sensitivity/src/TRestComponent.cxx | 8 ++++--- .../sensitivity/src/TRestComponentDataSet.cxx | 2 +- .../sensitivity/src/TRestComponentFormula.cxx | 2 -- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/macros/REST_AddComponent.C b/macros/REST_AddComponent.C index d59468fdb..45ce642a7 100644 --- a/macros/REST_AddComponent.C +++ b/macros/REST_AddComponent.C @@ -29,7 +29,7 @@ Int_t REST_AddComponent(std::string cfgFile, std::string sectionName, TRestComponentDataSet comp(cfgFile.c_str(), sectionName.c_str()); comp.LoadDataSets(); - TFile *f; + TFile* f; if (update) f = TFile::Open(outputFile.c_str(), "UPDATE"); else diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index 2a4484951..648f80171 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -42,17 +42,7 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { - Energy, - Time, - Length, - Mass, - Voltage, - MagneticField, - Pressure, - Angle, - NOT_A_UNIT = -1 -}; +enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; class TRestSystemOfUnits { private: diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index 29e23ecba..ef6bf09de 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -1146,9 +1146,10 @@ void TRestMetadata::ReplaceForLoopVars(TiXmlElement* e, map forL } } - e->SetAttribute(name, ReplaceMathematicalExpressions(outputBuffer, 0, - "Please, check parameter name: " + parName + - " (ReplaceForLoopVars)").c_str()); + e->SetAttribute(name, ReplaceMathematicalExpressions( + outputBuffer, 0, + "Please, check parameter name: " + parName + " (ReplaceForLoopVars)") + .c_str()); } attr = attr->Next(); @@ -1305,7 +1306,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { TiXmlElement* ele = GetElementFromFile(filename); if (ele == nullptr) { RESTError << "TRestMetadata::ExpandIncludeFile. No xml elements contained in the include " - "file \"" << filename << "\"" << RESTendl; + "file \"" + << filename << "\"" << RESTendl; exit(1); } while (ele != nullptr) { @@ -1387,7 +1389,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { if (remoteele == nullptr) { RESTWarning << "Cannot find the needed xml section in " - "include file!" << RESTendl; + "include file!" + << RESTendl; RESTWarning << "type: \"" << type << "\" , name: \"" << name << "\" . Skipping" << RESTendl; RESTWarning << RESTendl; @@ -2270,7 +2273,8 @@ TString TRestMetadata::GetLibraryVersion() { return fLibraryVersion; } void TRestMetadata::ReSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = REST_RELEASE; } @@ -2282,7 +2286,8 @@ void TRestMetadata::ReSetVersion() { void TRestMetadata::UnSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = -1; fCommit = -1; @@ -2549,7 +2554,8 @@ void TRestMetadata::ReadOneParameter(string name, string value) { } else { RESTWarning << this->ClassName() << " find unit definition in parameter: " << name << ", but the corresponding data member doesn't support it. Data " - "member type: " << datamember.type << RESTendl; + "member type: " + << datamember.type << RESTendl; datamember.ParseString(value); } } else { diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index ed4478f20..585631ee8 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -32,7 +32,6 @@ /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { - protected: /// It defines the component type (unknown/signal/background) std::string fNature = "unknown"; //< diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 20f202e46..47ff25667 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -40,12 +40,12 @@ /// #include "TRestComponent.h" -#include #include #include #include #include +#include ClassImp(TRestComponent); @@ -361,13 +361,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 906fa0890..8a537401d 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -85,6 +85,7 @@ #include "TRestComponentDataSet.h" #include + #include ClassImp(TRestComponentDataSet); @@ -115,7 +116,6 @@ TRestComponentDataSet::~TRestComponentDataSet() {} /// TRestComponentDataSet::TRestComponentDataSet(const char* cfgFileName, const std::string& name) : TRestComponent(cfgFileName) { - LoadConfigFromFile(fConfigFileName, name); if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 5dcf1e14c..8c71dab06 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -114,7 +114,6 @@ void TRestComponentFormula::Initialize() { /// of the variables of the distribution. /// Double_t TRestComponentFormula::GetFormulaRate(std::vector point) { - if (fVariables.size() != point.size()) { RESTError << "Point should have same dimensions as number of variables!" << RESTendl; return 0; @@ -144,7 +143,6 @@ Double_t TRestComponentFormula::GetFormulaRate(std::vector point) { /// further development. /// void TRestComponentFormula::FillHistograms(Double_t precision) { - if (fFormulas.empty()) return; if (fParameterizationNodes.empty()) { From 63a04bc31d21af56698f311ea94e441034ac926b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 10:14:51 +0100 Subject: [PATCH 097/174] TRestComponentDataSet::FillHistograms will skip if histograms are already defined --- source/framework/sensitivity/src/TRestComponentDataSet.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 906fa0890..5a0c608e8 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -213,6 +213,8 @@ void TRestComponentDataSet::InitFromConfigFile() { /// The precision is used to define the active node /// void TRestComponentDataSet::FillHistograms(Double_t precision) { + if (!fNodeDensity.empty()) return; + fNSimPerNode = ExtractNodeStatistics(precision); if (!IsDataSetLoaded()) { From d03b98abc696fc6c389c415213e4e9e74ebedee7 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 10:15:31 +0100 Subject: [PATCH 098/174] TRestComponentFormula. Removing non-sense warning --- .../sensitivity/src/TRestComponentFormula.cxx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 5dcf1e14c..8a9cf60b4 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -147,13 +147,7 @@ void TRestComponentFormula::FillHistograms(Double_t precision) { if (fFormulas.empty()) return; - if (fParameterizationNodes.empty()) { - RESTWarning << "Nodes have not been defined" << RESTendl; - RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl; - fParameterizationNodes.push_back(-137); - } - - RESTInfo << "Generating N-dim histogram" << RESTendl; + RESTInfo << "Generating N-dim histogram for " << GetName() << RESTendl; TString hName = "formula"; @@ -260,6 +254,4 @@ void TRestComponentFormula::InitFromConfigFile() { ele = GetNextElement(ele); } - - if (!fFormulas.empty()) Initialize(); } From cc412a411ec8d802cdfe6ceec3041416f9bccb37 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 10:16:15 +0100 Subject: [PATCH 099/174] TRestExperiment implementation (WIP) --- .../sensitivity/inc/TRestExperiment.h | 27 ++++-- .../sensitivity/src/TRestExperiment.cxx | 88 +++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 36fc10992..1fe0db4b5 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -23,24 +23,39 @@ #ifndef REST_TRestExperiment #define REST_TRestExperiment -#include "TRestDataSet.h" #include "TRestMetadata.h" -#include "TRestModel.h" +#include "TRestDataSet.h" +#include "TRestComponent.h" /// It includes a model definition and experimental data used to obtain a final experimental sensitivity class TRestExperiment : public TRestMetadata { private: - /// It contains the model definition, including signal and background - TRestModel* fModel = nullptr; //< + /// The exposure time. If 0 it will be extracted from the tracking dataset + Double_t fExposureTime = 0; //< + + /// A pointer to the background component + TRestComponent* fBackground = nullptr; //< + + /// A pointer to the signal component + TRestComponent* fSignal = nullptr; //< - /// It contains the experimental data to be compared with the model - TRestDataSet fExperimentalData; //< + /// It contains the experimental data (should contain same columns as the components) + TRestDataSet fTrackingData; //< + + /// If enabled the tracking data will be MC-generated following background compatibility + Bool_t fMockTracking = false; //< + + protected: + void InitFromConfigFile() override; public: + void GenerateMockDataSet(); + void Initialize() override; void PrintMetadata() override; + TRestExperiment(const char* cfgFileName, const std::string& name = ""); TRestExperiment(); ~TRestExperiment(); diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index c67c5e3ee..fb2373aa7 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -52,17 +52,105 @@ TRestExperiment::TRestExperiment() { Initialize(); } /// TRestExperiment::~TRestExperiment() {} +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestExperiment::TRestExperiment(const char* cfgFileName, const std::string& name) + : TRestMetadata(cfgFileName) { + LoadConfigFromFile(fConfigFileName, name); +} + /////////////////////////////////////////////// /// \brief It will initialize the data frame with the filelist and column names /// (or observables) that have been defined by the user. /// void TRestExperiment::Initialize() { SetSectionName(this->ClassName()); } +void TRestExperiment::GenerateMockDataSet() { + if (!fBackground) { + RESTError << "TRestExperiment::GenerateMockData. Background component was not initialized!" + << RESTendl; + return; + } + + ROOT::RDF::RNode df = fBackground->GetMonteCarloDataFrame(10); + + df.Display({"final_posX"})->Print(); +} + +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class +/// +void TRestExperiment::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + int cont = 0; + TRestComponent* comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + while (comp != nullptr) { + if (ToLower(comp->GetNature()) == "background") + fBackground = comp; + else if (ToLower(comp->GetNature()) == "signal") + fSignal = comp; + else + RESTWarning << "TRestExperiment::InitFromConfigFile. Unknown component!" << RESTendl; + + cont++; + comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + } + + auto ele = GetElement("addComponent"); + if (ele != nullptr) { + std::string filename = GetParameter("filename", ele, ""); + std::string component = GetParameter("component", ele, ""); + + if (filename.empty()) + RESTWarning << "TRestExperiment. There is a problem with `filename` definition inside Get(component.c_str()); + if (comp) { + if (ToLower(comp->GetNature()) == "signal") + fSignal = comp; + else if (ToLower(comp->GetNature()) == "background") + fBackground = comp; + else + RESTError << "TRestExperiment::InitFromConfigFile. Component : " << component + << ". Nature unknown!" << RESTendl; + } else + RESTError << "TRestExperiment::InitFromConfigFile. Component : " << component + << " not found! File : " << filename << RESTendl; + } + } + } +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// void TRestExperiment::PrintMetadata() { TRestMetadata::PrintMetadata(); + if (fSignal) RESTMetadata << "Signal component : " << fSignal->GetName() << RESTendl; + + if (fBackground) RESTMetadata << "Background component : " << fBackground->GetName() << RESTendl; + RESTMetadata << "----" << RESTendl; } From 436bbc66b5a5d55c0d2de1d6d1f8d895758f112f Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 10:17:17 +0100 Subject: [PATCH 100/174] REST_AddComponent.C updating to new TRestComponent scheme --- macros/REST_AddComponent.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/REST_AddComponent.C b/macros/REST_AddComponent.C index d59468fdb..9bf7b979c 100644 --- a/macros/REST_AddComponent.C +++ b/macros/REST_AddComponent.C @@ -27,7 +27,7 @@ Int_t REST_AddComponent(std::string cfgFile, std::string sectionName, std::string outputFile = "components.root", std::string componentName = "", Bool_t update = true) { TRestComponentDataSet comp(cfgFile.c_str(), sectionName.c_str()); - comp.LoadDataSets(); + comp.Initialize(); TFile *f; if (update) From 10ed5ee50d82822ccac1ad6b6741388ec867a9be Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:17:49 +0000 Subject: [PATCH 101/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestExperiment.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 1fe0db4b5..e4d07e027 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -23,9 +23,9 @@ #ifndef REST_TRestExperiment #define REST_TRestExperiment -#include "TRestMetadata.h" -#include "TRestDataSet.h" #include "TRestComponent.h" +#include "TRestDataSet.h" +#include "TRestMetadata.h" /// It includes a model definition and experimental data used to obtain a final experimental sensitivity class TRestExperiment : public TRestMetadata { From c6fe4ea4b1beee93ed3d7fd7aff54370262b7c17 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 13:57:43 +0100 Subject: [PATCH 102/174] Update source/framework/sensitivity/src/TRestComponent.cxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Antonio García <80903717+juanangp@users.noreply.github.com> --- .../sensitivity/src/TRestComponent.cxx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 47ff25667..3897db8ea 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -292,23 +292,20 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { ROOT::RDF::RNode df = ROOT::RDataFrame(N); // Function to fill the RDataFrame using GetRandom method - auto fillDataFrame = [this]() { + auto fillRndm = [this]() { auto randomValues = GetRandom(); + return randomValues; + } + df.Define("Rndm", fillRndm) - // Check if the size of randomValues matches the size of columnNames - if (randomValues.size() != fVariables.size()) { - throw std::runtime_error("Mismatch in sizes of fVariables and randomValues"); - } - }; - - // Fill the RDataFrame with values from GetRandom for (size_t i = 0; i < fVariables.size(); ++i) { - df = df.Define(fVariables[i], [&randomValues, &i]() { return randomValues[i]; }); + auto varName = fVariables[i]; + auto FillRand = [&i = i]( const ROOT::RVec randomValues){ + return randomValues[i]; + } + df = df.Define(varName, FillRand, "Rndm"); } - // Apply the fillDataFrame function to each entry in the RDataFrame - df.Foreach(fillDataFrame); - std::cout << df.GetColumnNames().size() << std::endl; for (const auto& x : df.GetColumnNames()) std::cout << x << std::endl; From befd7a76121eb721008b3ebf374e18c31210a695 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:57:58 +0000 Subject: [PATCH 103/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../sensitivity/src/TRestComponent.cxx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 3897db8ea..ed30d9e17 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -292,18 +292,16 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { ROOT::RDF::RNode df = ROOT::RDataFrame(N); // Function to fill the RDataFrame using GetRandom method - auto fillRndm = [this]() { - auto randomValues = GetRandom(); - return randomValues; - } - df.Define("Rndm", fillRndm) + auto fillRndm = + [this]() { + auto randomValues = GetRandom(); + return randomValues; + } df.Define("Rndm", fillRndm) - for (size_t i = 0; i < fVariables.size(); ++i) { + for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i]( const ROOT::RVec randomValues){ - return randomValues[i]; - } - df = df.Define(varName, FillRand, "Rndm"); + auto FillRand = [&i = i](const ROOT::RVec randomValues) { return randomValues[i]; } df = + df.Define(varName, FillRand, "Rndm"); } std::cout << df.GetColumnNames().size() << std::endl; From 52e4f1a43a988f4847f277a22d80586865985e22 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 14:21:37 +0100 Subject: [PATCH 104/174] TRestComponent::GetMonteCarloDataFrame fixing compilation --- .../sensitivity/src/TRestComponent.cxx | 56 ++++--------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index ed30d9e17..11dab501a 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -292,56 +292,26 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { ROOT::RDF::RNode df = ROOT::RDataFrame(N); // Function to fill the RDataFrame using GetRandom method - auto fillRndm = - [this]() { - auto randomValues = GetRandom(); - return randomValues; - } df.Define("Rndm", fillRndm) + auto fillRndm = [this]() { + auto randomValues = GetRandom(); + return randomValues; + }; + df = df.Define("Rndm", fillRndm); - for (size_t i = 0; i < fVariables.size(); ++i) { + for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i](const ROOT::RVec randomValues) { return randomValues[i]; } df = - df.Define(varName, FillRand, "Rndm"); + auto FillRand = [&i = i](const std::vector randomValues) { + return randomValues[i]; + }; + df = df.Define(varName, FillRand, {"Rndm"}); } std::cout << df.GetColumnNames().size() << std::endl; - for (const auto& x : df.GetColumnNames()) std::cout << x << std::endl; - // Return the RNode (RDataFrame) return df; } -/* -ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame( Int_t N ) -{ - // Create a data frame with 100 rows - ROOT::RDataFrame df(100); - - // Define a new column `x` that contains random numbers - //auto rdf_x = rdf.Define("x", [](){ return fBackground->GetRandom()[0]; }); - - std::vector trackingCount = GetRandom(); - //auto rdf_x = rdf.Define("random_value", [this]() { return GetRandom()[0]; }); - - // Fill the RDataFrame with correlated values - for (size_t i = 0; i < fVariables.size(); ++i) { - const auto& column_name = fVariables[i]; - std::cout << "Name :" << column_name << std::endl; - df.Define(column_name, [trackingCount, i]() { return trackingCount[i]; }); - } - - for( const auto &c: df.GetColumnNames() ) - std::cout << c << std::endl; - auto a = df.Count(); - std::cout << *a << std::endl; - - df.Display({"final_posX"})->Print(); - - return df; -} -*/ - /////////////////////////////////////////////// /// \brief A method allowing to draw a series of plots representing the density distributions. /// @@ -356,15 +326,13 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } From 0aa88639a4e95f0ef588fb79cd5c15b68a78fed6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:21:56 +0000 Subject: [PATCH 105/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestComponent.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 11dab501a..b750cb14f 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -300,9 +300,7 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i](const std::vector randomValues) { - return randomValues[i]; - }; + auto FillRand = [&i = i](const std::vector randomValues) { return randomValues[i]; }; df = df.Define(varName, FillRand, {"Rndm"}); } @@ -326,13 +324,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } From b02e0f855f090b37a1d5d314879baeb2d8e35d2b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 16:32:58 +0100 Subject: [PATCH 106/174] TRestComponent::GetRandom now returns a RVecD --- source/framework/sensitivity/inc/TRestComponent.h | 5 ++++- source/framework/sensitivity/src/TRestComponent.cxx | 8 +++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 585631ee8..7ba03d292 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -30,6 +30,9 @@ #include "TRestMetadata.h" #include "TRestResponse.h" +#include +#include + /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { protected: @@ -119,7 +122,7 @@ class TRestComponent : public TRestMetadata { TH2D* GetHistogram(std::string varName1, std::string varName2); TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3); - std::vector GetRandom(); + ROOT::RVecD GetRandom(); ROOT::RDF::RNode GetMonteCarloDataFrame(Int_t N = 100); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 11dab501a..fa9c0a524 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -43,8 +43,6 @@ #include #include -#include -#include #include ClassImp(TRestComponent); @@ -278,13 +276,13 @@ Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) { return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim]; } -std::vector TRestComponent::GetRandom() { +ROOT::RVecD TRestComponent::GetRandom() { Double_t* tuple = new Double_t[GetDimensions()]; GetDensity()->GetRandom(tuple); std::vector result; for (size_t n = 0; n < GetDimensions(); n++) result.push_back(tuple[n]); - return result; + return (ROOT::RVecD)result; } ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { @@ -300,7 +298,7 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i](const std::vector randomValues) { + auto FillRand = [&i = i](const ROOT::RVecD & randomValues) { return randomValues[i]; }; df = df.Define(varName, FillRand, {"Rndm"}); From 7df784864c92b0a3d1b691eb5245efa06f630fd3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:36:30 +0000 Subject: [PATCH 107/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/inc/TRestComponent.h | 6 +++--- source/framework/sensitivity/src/TRestComponent.cxx | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 7ba03d292..6cc12daff 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -26,13 +26,13 @@ #include #include +#include +#include + #include "TRestDataSet.h" #include "TRestMetadata.h" #include "TRestResponse.h" -#include -#include - /// It defines a background/signal model distribution in a given parameter space (tipically x,y,en) class TRestComponent : public TRestMetadata { protected: diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index fa9c0a524..39fdf1013 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -298,9 +298,7 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i](const ROOT::RVecD & randomValues) { - return randomValues[i]; - }; + auto FillRand = [&i = i](const ROOT::RVecD& randomValues) { return randomValues[i]; }; df = df.Define(varName, FillRand, {"Rndm"}); } @@ -324,13 +322,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } From e96053625ebcb36dedc8605db5baf53e6c5a8240 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 21:38:06 +0100 Subject: [PATCH 108/174] TRestComponent::GetMonteCarloDataFrame fixed --- .../sensitivity/src/TRestComponent.cxx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index fa9c0a524..e845a81f5 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -280,33 +280,28 @@ ROOT::RVecD TRestComponent::GetRandom() { Double_t* tuple = new Double_t[GetDimensions()]; GetDensity()->GetRandom(tuple); - std::vector result; + ROOT::RVecD result; for (size_t n = 0; n < GetDimensions(); n++) result.push_back(tuple[n]); - return (ROOT::RVecD)result; + return result; } ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { - // Create a RDataFrame with the specified columns ROOT::RDF::RNode df = ROOT::RDataFrame(N); // Function to fill the RDataFrame using GetRandom method - auto fillRndm = [this]() { - auto randomValues = GetRandom(); + auto fillRndm = [&]() { + ROOT::RVecD randomValues = GetRandom(); return randomValues; }; df = df.Define("Rndm", fillRndm); + // Creating dedicated columns for each GetRandom component for (size_t i = 0; i < fVariables.size(); ++i) { auto varName = fVariables[i]; - auto FillRand = [&i = i](const ROOT::RVecD & randomValues) { - return randomValues[i]; - }; + auto FillRand = [i](const ROOT::RVecD& randomValues) { return randomValues[i]; }; df = df.Define(varName, FillRand, {"Rndm"}); } - std::cout << df.GetColumnNames().size() << std::endl; - for (const auto& x : df.GetColumnNames()) std::cout << x << std::endl; - return df; } From f8492c17143adf89441aa8d721bb608e8ef1c89f Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 16 Jan 2024 21:38:33 +0100 Subject: [PATCH 109/174] TRestComponentFormula::PrintMetadata fixed --- source/framework/sensitivity/src/TRestComponentFormula.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index ec9a2954f..0130926e1 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -207,7 +207,7 @@ void TRestComponentFormula::PrintMetadata() { if (!fFormulas.empty()) { RESTMetadata << " " << RESTendl; - RESTMetadata << " == Term expressions implemented inside the component ==" << RESTendl; + RESTMetadata << " == Contributions implemented inside the component ==" << RESTendl; for (const auto& x : fFormulas) RESTMetadata << "- " << x.GetName() << " = " << x.GetExpFormula() << RESTendl; From e9d91232075d2c7d1b3a68552c0cc90e732fb63b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 20:44:48 +0000 Subject: [PATCH 110/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestComponent.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index e845a81f5..21a640867 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -319,13 +319,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } From bb032f69598df628dd117f36b0a9fed1830c8950 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 17 Jan 2024 11:17:43 +0100 Subject: [PATCH 111/174] TRestComponent::HasDensity method and condition added --- source/framework/sensitivity/inc/TRestComponent.h | 2 ++ source/framework/sensitivity/src/TRestComponent.cxx | 2 +- source/framework/sensitivity/src/TRestComponentFormula.cxx | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 6cc12daff..33628e6cf 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -72,6 +72,8 @@ class TRestComponent : public TRestMetadata { /// It returns true if any nodes have been defined. Bool_t HasNodes() { return !fParameterizationNodes.empty(); } + Bool_t HasDensity() { return !fNodeDensity.empty(); } + /// It returns true if the node has been properly identified Bool_t ValidNode(Double_t node) { SetActiveNode(node); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index e845a81f5..fefbdd9bb 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -182,7 +182,7 @@ Double_t TRestComponent::GetRawRate(std::vector point) { return 0; } - if (!HasNodes()) { + if (!HasNodes() && !HasDensity()) { RESTError << "TRestComponent::GetRawRate. The component has no nodes!" << RESTendl; RESTError << "Try calling TRestComponent::Initialize" << RESTendl; diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 0130926e1..731c0300c 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -192,6 +192,7 @@ void TRestComponentFormula::FillHistograms(Double_t precision) { } } + fNodeDensity.clear(); fNodeDensity.push_back(hNd); fActiveNode = 0; // For the moment only 1-node! } From 8f1e36ac068c14b41b1afa170e1614a181abdf0a Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 17 Jan 2024 11:37:19 +0100 Subject: [PATCH 112/174] TRestComponent::GetTotalRate. Fixing integration --- source/framework/sensitivity/src/TRestComponent.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index fefbdd9bb..35c41b5e2 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -258,11 +258,10 @@ Double_t TRestComponent::GetTotalRate() { THnD* dHist = GetDensityForActiveNode(); Double_t integral = 0; - if (dHist != nullptr) integral = dHist->ComputeIntegral(); - - // Perhaps this value could be stored internally - for (size_t n = 0; n < fNbins.size(); n++) - integral = integral * (fRanges[n].Y() - fRanges[n].X()) / fNbins[n]; + if (dHist != nullptr) { + TH1D* h1 = dHist->Projection(0); + integral = h1->Integral(); + } return integral; } From c78ff02af8990cc9a3dea5d684af3b714d41e95d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 17 Jan 2024 19:20:46 +0100 Subject: [PATCH 113/174] TRestExperiment::GetSignal/Background methods added --- source/framework/sensitivity/inc/TRestExperiment.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index e4d07e027..779da0c1d 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -30,7 +30,7 @@ /// It includes a model definition and experimental data used to obtain a final experimental sensitivity class TRestExperiment : public TRestMetadata { private: - /// The exposure time. If 0 it will be extracted from the tracking dataset + /// The exposure time. If 0 it will be extracted from the tracking dataset (In us, standard REST unit) Double_t fExposureTime = 0; //< /// A pointer to the background component @@ -51,6 +51,9 @@ class TRestExperiment : public TRestMetadata { public: void GenerateMockDataSet(); + TRestComponent* GetBackground() const { return fBackground; } + TRestComponent* GetSignal() const { return fSignal; } + void Initialize() override; void PrintMetadata() override; From d4bd8a58592cca593cdf3f6bafb78ac758b40754 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 17 Jan 2024 19:21:16 +0100 Subject: [PATCH 114/174] TRestComponent removing TH1 to avoid potential memory leak --- source/framework/sensitivity/src/TRestComponent.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 35c41b5e2..2945321bb 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -261,6 +261,7 @@ Double_t TRestComponent::GetTotalRate() { if (dHist != nullptr) { TH1D* h1 = dHist->Projection(0); integral = h1->Integral(); + delete h1; } return integral; From 06bef738fe3b8b9c2d482600bbc0f1dc8e26d23c Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 18 Jan 2024 15:06:16 +0100 Subject: [PATCH 115/174] TRestDataSet::SetDataSet adding a method to define a external dataframe --- source/framework/core/inc/TRestDataSet.h | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 01796873f..938b57de1 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -82,7 +82,7 @@ class TRestDataSet : public TRestMetadata { std::map fQuantity; //< /// Parameter cuts over the selected dataset - TRestCut* fCut = nullptr; + TRestCut* fCut = nullptr; //< /// The total integrated run time of selected files Double_t fTotalDuration = 0; //< @@ -99,6 +99,9 @@ class TRestDataSet : public TRestMetadata { /// It keeps track if the generated dataset is a pure dataset or a merged one Bool_t fMergedDataset = false; //< + // If the dataframe was defined externally it will be true + Bool_t fExternal = false; + /// The list of dataset files imported std::vector fImportedFiles; //< @@ -122,16 +125,24 @@ class TRestDataSet : public TRestMetadata { public: /// Gives access to the RDataFrame ROOT::RDF::RNode GetDataFrame() const { - if (fTree == nullptr) RESTWarning << "DataFrame has not been yet initialized" << RESTendl; + if (!fExternal && fTree == nullptr) + RESTWarning << "DataFrame has not been yet initialized" << RESTendl; return fDataSet; } - void SetDataFrame(const ROOT::RDF::RNode& dS) { fDataSet = dS; } - void EnableMultiThreading(Bool_t enable = true) { fMT = enable; } /// Gives access to the tree TTree* GetTree() const { + + if (fTree == nullptr && fExternal) { + RESTInfo << "The tree is not accessible. Only GetDataFrame can be used in an externally " + "generated dataset" << RESTendl; + RESTInfo << "You may write a tree using GetDataFrame()->Snapshot(\"MyTree\", \"output.root\");" + << RESTendl; + return fTree; + } + if (fTree == nullptr) { RESTError << "Tree has not been yet initialized" << RESTendl; RESTError << "You should invoke TRestDataSet::GenerateDataSet() or " << RESTendl; @@ -174,6 +185,12 @@ class TRestDataSet : public TRestMetadata { inline void SetFilePattern(const std::string& pattern) { fFilePattern = pattern; } inline void SetQuantity(const std::map& quantity) { fQuantity = quantity; } + void SetTotalTimeInSeconds(Double_t seconds) { fTotalDuration = seconds; } + void SetDataFrame(const ROOT::RDF::RNode& dS) { + fDataSet = dS; + fExternal = true; + } + TRestDataSet& operator=(TRestDataSet& dS); Bool_t Merge(const TRestDataSet& dS); void Import(const std::string& fileName); @@ -181,7 +198,6 @@ class TRestDataSet : public TRestMetadata { void Export(const std::string& filename); ROOT::RDF::RNode MakeCut(const TRestCut* cut); - ROOT::RDF::RNode DefineColumn(const std::string& columnName, const std::string& formula); void PrintMetadata() override; From 41418023b2cffec955313f414b3a7c5ed87a379d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 18 Jan 2024 15:10:10 +0100 Subject: [PATCH 116/174] TRestExperiment::GenerateMockDataSet method implemented --- .../sensitivity/inc/TRestExperiment.h | 9 ++++++ .../sensitivity/src/TRestExperiment.cxx | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 779da0c1d..7ce78e71d 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -23,6 +23,8 @@ #ifndef REST_TRestExperiment #define REST_TRestExperiment +#include + #include "TRestComponent.h" #include "TRestDataSet.h" #include "TRestMetadata.h" @@ -45,6 +47,12 @@ class TRestExperiment : public TRestMetadata { /// If enabled the tracking data will be MC-generated following background compatibility Bool_t fMockTracking = false; //< + /// Internal process random generator + TRandom3* fRandom = nullptr; //! + + /// Seed used in random generator + Int_t fSeed = 0; //< + protected: void InitFromConfigFile() override; @@ -53,6 +61,7 @@ class TRestExperiment : public TRestMetadata { TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } + ROOT::RDF::RNode GetTrackingData() { return fTrackingData.GetDataFrame(); } void Initialize() override; diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index fb2373aa7..0861502ce 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -75,7 +75,18 @@ TRestExperiment::TRestExperiment(const char* cfgFileName, const std::string& nam /// \brief It will initialize the data frame with the filelist and column names /// (or observables) that have been defined by the user. /// -void TRestExperiment::Initialize() { SetSectionName(this->ClassName()); } +void TRestExperiment::Initialize() { + + SetSectionName(this->ClassName()); + + if (!fRandom) { + delete fRandom; + fRandom = nullptr; + } + + fRandom = new TRandom3(fSeed); + fSeed = fRandom->TRandom::GetSeed(); +} void TRestExperiment::GenerateMockDataSet() { if (!fBackground) { @@ -84,9 +95,19 @@ void TRestExperiment::GenerateMockDataSet() { return; } - ROOT::RDF::RNode df = fBackground->GetMonteCarloDataFrame(10); + if (fExposureTime <= 0) { + RESTError << "The experimental exposure time has not been defined" << RESTendl; + RESTError << "This time is required to create the mock dataset" << RESTendl; + } + + Double_t meanCounts = GetBackground()->GetTotalRate() * fExposureTime * units("s"); + + Int_t N = fRandom->Poisson(meanCounts); - df.Display({"final_posX"})->Print(); + ROOT::RDF::RNode df = fBackground->GetMonteCarloDataFrame(N); + + fTrackingData.SetDataFrame(df); + fTrackingData.SetTotalTimeInSeconds(fExposureTime * units("s")); } ///////////////////////////////////////////// @@ -140,6 +161,8 @@ void TRestExperiment::InitFromConfigFile() { } } } + + Initialize(); } ///////////////////////////////////////////// From 9d0ab8c8b659318c90097fe3dda7c7fae67f9823 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Thu, 18 Jan 2024 15:59:16 +0100 Subject: [PATCH 117/174] TRestDataSet. Fixing seg. fault when recovering keys --- source/framework/core/inc/TRestDataSet.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 938b57de1..d5accd421 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -99,9 +99,6 @@ class TRestDataSet : public TRestMetadata { /// It keeps track if the generated dataset is a pure dataset or a merged one Bool_t fMergedDataset = false; //< - // If the dataframe was defined externally it will be true - Bool_t fExternal = false; - /// The list of dataset files imported std::vector fImportedFiles; //< @@ -111,6 +108,9 @@ class TRestDataSet : public TRestMetadata { /// A flag to enable Multithreading during dataframe generation Bool_t fMT = false; //< + // If the dataframe was defined externally it will be true + Bool_t fExternal = false; //< + /// The resulting RDF::RNode object after initialization ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0); //! @@ -209,6 +209,6 @@ class TRestDataSet : public TRestMetadata { TRestDataSet(const char* cfgFileName, const std::string& name = ""); ~TRestDataSet(); - ClassDefOverride(TRestDataSet, 6); + ClassDefOverride(TRestDataSet, 7); }; #endif From e3563f529e5d5ced3fb00de79398089d6ff67d5d Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:20:05 +0100 Subject: [PATCH 118/174] Adding macros REST_AddComponentDataSet and REST_AddComponentFormula --- ...Component.C => REST_AddComponentDataSet.C} | 8 +- .../{TRestModel.h => TRestExperimentList.h} | 48 +++++++---- ...TRestModel.cxx => TRestExperimentList.cxx} | 82 +++++++++++++------ 3 files changed, 91 insertions(+), 47 deletions(-) rename macros/{REST_AddComponent.C => REST_AddComponentDataSet.C} (81%) rename source/framework/sensitivity/inc/{TRestModel.h => TRestExperimentList.h} (53%) rename source/framework/sensitivity/src/{TRestModel.cxx => TRestExperimentList.cxx} (54%) diff --git a/macros/REST_AddComponent.C b/macros/REST_AddComponentDataSet.C similarity index 81% rename from macros/REST_AddComponent.C rename to macros/REST_AddComponentDataSet.C index db41ac927..f0c60044c 100644 --- a/macros/REST_AddComponent.C +++ b/macros/REST_AddComponentDataSet.C @@ -9,7 +9,7 @@ //*** will write it inside the file given as outputFile //*** //*** -------------- -//*** Usage: restManager AddComponent components.rml sectionName [outputFile] [componentName] [update] +//*** Usage: restManager AddComponentDataSet components.rml sectionName [outputFile] [componentName] [update] //*** //*** Arguments description: //*** @@ -23,9 +23,9 @@ //*** //******************************************************************************************************* -Int_t REST_AddComponent(std::string cfgFile, std::string sectionName, - std::string outputFile = "components.root", std::string componentName = "", - Bool_t update = true) { +Int_t REST_AddComponentDataSet(std::string cfgFile, std::string sectionName, + std::string outputFile = "components.root", std::string componentName = "", + Bool_t update = true) { TRestComponentDataSet comp(cfgFile.c_str(), sectionName.c_str()); comp.Initialize(); diff --git a/source/framework/sensitivity/inc/TRestModel.h b/source/framework/sensitivity/inc/TRestExperimentList.h similarity index 53% rename from source/framework/sensitivity/inc/TRestModel.h rename to source/framework/sensitivity/inc/TRestExperimentList.h index 3aebbd7b0..1a43e84ed 100644 --- a/source/framework/sensitivity/inc/TRestModel.h +++ b/source/framework/sensitivity/inc/TRestExperimentList.h @@ -20,35 +20,51 @@ * For the list of contributors see $REST_PATH/CREDITS. * *************************************************************************/ -#ifndef REST_TRestModel -#define REST_TRestModel +#ifndef REST_TRestExperimentList +#define REST_TRestExperimentList -#include "TRestComponent.h" +#include "TRestExperiment.h" #include "TRestMetadata.h" -/// A combination of signal and background components that build a complete signal and background model -class TRestModel : public TRestMetadata { +/// A helper metadata class to create a list of TRestExperiment instances +class TRestExperimentList : public TRestMetadata { private: - // TODO At some point we may want to add here a coupling for each signal component + /// A fullpath filename pattern helping to initialize the component files vector + std::string fComponentPattern = ""; //< - /// A vector that includes the signal components in this model - std::vector fSignal; //< + /// A vector with filenames containing the components + std::vector fComponentFiles; //< - /// A vector that includes the background components in this model - std::vector fBackground; + /// A fullpath filename pattern helping to initialize the dataset files vector + std::string fDataSetPattern = ""; //< + + /// A vector with filenames containing the datasets with experimental data + std::vector fDataSetFilenames; //< + + /// A file where we define experiment components, exposureTime, and tracking data of each experiment + std::string fExperimentsFile = ""; //< Exposure/TrackingData - SignalComponent - BackgroundComponent + + /// A table with the experiment file information + std::vector > fExperimentsTable; //< + + /// A vector with a list of experiments includes the background components in this model + std::vector fExperiments; //< + + protected: + void InitFromConfigFile() override; public: void Initialize() override; - Double_t GetSignal(std::vector point); - - Double_t GetBackground(std::vector point); + std::vector GetExperiments() { return fExperiments; } void PrintMetadata() override; - TRestModel(); - ~TRestModel(); + TRestExperimentList(const char* cfgFileName, const std::string& name); + + TRestExperimentList(); + ~TRestExperimentList(); - ClassDefOverride(TRestModel, 1); + ClassDefOverride(TRestExperimentList, 1); }; #endif diff --git a/source/framework/sensitivity/src/TRestModel.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx similarity index 54% rename from source/framework/sensitivity/src/TRestModel.cxx rename to source/framework/sensitivity/src/TRestExperimentList.cxx index 5dab1d460..403ea297e 100644 --- a/source/framework/sensitivity/src/TRestModel.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -30,62 +30,90 @@ /// /// History of developments: /// -/// 2022-December: First implementation of TRestModel +/// 2022-December: First implementation of TRestExperimentList /// Javier Galan /// -/// \class TRestModel +/// \class TRestExperimentList /// \author: Javier Galan (javier.galan.lacarra@cern.ch) /// ///
    /// -#include "TRestModel.h" +#include "TRestExperimentList.h" -ClassImp(TRestModel); +ClassImp(TRestExperimentList); /////////////////////////////////////////////// /// \brief Default constructor /// -TRestModel::TRestModel() { Initialize(); } +TRestExperimentList::TRestExperimentList() { Initialize(); } /////////////////////////////////////////////// /// \brief Default destructor /// -TRestModel::~TRestModel() {} +TRestExperimentList::~TRestExperimentList() {} -/////////////////////////////////////////////// -/// \brief It will initialize the data frame with the filelist and column names -/// (or observables) that have been defined by the user. +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file /// -void TRestModel::Initialize() { SetSectionName(this->ClassName()); } - -/////////////////////////////////////////////// -/// \brief It returns the intensity/rate (in seconds) corresponding to the -/// combined background. +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. /// -/// The size of the point vector must have the same dimension as the dimensions -/// of the distribution. +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. /// -Double_t TRestModel::GetBackground(std::vector point) { - // we get the rate in seconds at the corresponding bin - return 0.0; +TRestExperimentList::TRestExperimentList(const char* cfgFileName, const std::string& name) + : TRestMetadata(cfgFileName) { + LoadConfigFromFile(fConfigFileName, name); } /////////////////////////////////////////////// -/// \brief It returns the intensity/rate (in seconds) corresponding to the -/// combined signal. +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. /// -/// The size of the point vector must have the same dimension as the dimensions -/// of the distribution. +void TRestExperimentList::Initialize() { SetSectionName(this->ClassName()); } + +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class /// -Double_t TRestModel::GetSignal(std::vector point) { - // we get the rate in seconds at the corresponding bin - return 0.0; +void TRestExperimentList::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + /* +auto ele = GetElement("variable"); +while (ele != nullptr) { + std::string name = GetParameter("name", ele, ""); + TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); + Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); + + if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { + RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl; + RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins + << RESTendl; + } else { + fVariables.push_back(name); + fRanges.push_back(v); + fNbins.push_back(bins); + } + + ele = GetNextElement(ele); +} + */ + + if (!fExperimentsFile.empty()) { + TRestTools::ReadASCIITable(fExperimentsFile, fExperimentsTable); + + TRestTools::PrintTable(fExperimentsTable, 0, 3); + } } ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// -void TRestModel::PrintMetadata() { +void TRestExperimentList::PrintMetadata() { TRestMetadata::PrintMetadata(); RESTMetadata << "----" << RESTendl; From 2fcf575d5346aa164a31f526311b1add085e1990 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:24:04 +0100 Subject: [PATCH 119/174] TRestTools. Adding support for pure std::string tables --- source/framework/tools/inc/TRestTools.h | 12 +++-- source/framework/tools/src/TRestTools.cxx | 65 ++++++++++++++++++++--- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 826ce5137..5d688dc66 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void)x +#define UNUSED(x) (void) x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -63,6 +63,8 @@ class TRestTools { Int_t skipLines = 0, std::string separator = "\t"); static int ReadASCIITable(std::string fName, std::vector>& data, Int_t skipLines = 0, std::string separator = "\t"); + static int ReadASCIITable(std::string fName, std::vector>& data, + Int_t skipLines = 0, std::string separator = "\t"); static int ReadCSVFile(std::string fName, std::vector>& data, Int_t skipLines = 0); static int ReadCSVFile(std::string fName, std::vector>& data, Int_t skipLines = 0); @@ -181,10 +183,14 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { ENERGY, LENGTH, TIME }; +enum Quantities { + ENERGY, + LENGTH, + TIME +}; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 5763c53dc..78b06cdcd 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -174,6 +174,8 @@ template int TRestTools::PrintTable(std::vector> data, template int TRestTools::PrintTable(std::vector> data, Int_t start, Int_t end); template int TRestTools::PrintTable(std::vector> data, Int_t start, Int_t end); +template int TRestTools::PrintTable(std::vector> data, Int_t start, + Int_t end); /////////////////////////////////////////////// /// \brief Writes the contents of the vector table given as argument to `fname`. @@ -507,6 +509,58 @@ template std::vector TRestTools::GetColumnFromTable( template std::vector TRestTools::GetColumnFromTable( const std::vector>& data, unsigned int column); +template std::vector TRestTools::GetColumnFromTable( + const std::vector>& data, unsigned int column); + +/////////////////////////////////////////////// +/// \brief Reads an ASCII file containing a table with values +/// +/// This method will open the file fName. This file should contain a tabulated +/// ASCII table containing any format values. The values on the table will be +/// loaded in the matrix provided through the argument `data`. The content of +/// `data` will be cleared in this method. +/// +/// If any header in the file is present, it should be skipped using the argument +///`skipLines` or preceding any line inside the header using `#`. +/// +/// This table will just split the ASCII elements inside a std::string matrix +/// +int TRestTools::ReadASCIITable(string fName, std::vector>& data, Int_t skipLines, + std::string separator) { + if (!TRestTools::isValidFile((string)fName)) { + cout << "TRestTools::ReadASCIITable. Error" << endl; + cout << "Cannot open file : " << fName << endl; + return 0; + } + + data.clear(); + + std::ifstream fin(fName); + + // First we create a table with string values + std::vector> values; + + for (string line; std::getline(fin, line);) { + if (skipLines > 0) { + skipLines--; + continue; + } + + if (line.find("#") == string::npos) { + std::istringstream in(line); + + std::string token; + std::vector tokens; + while (std::getline(in, token, (char)separator[0])) { + tokens.push_back(token); + } + data.push_back(tokens); + } + } + + return 1; +} + /////////////////////////////////////////////// /// \brief Reads an ASCII file containing a table with values /// @@ -819,15 +873,13 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" - << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" - << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; exit(1); } path /= userHomePath; @@ -1050,7 +1102,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof(): + case std::streambuf::traits_type::eof() : // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1255,8 +1307,7 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" - << RESTendl; + "http://gifna.unizar.es/rest-forum/" << RESTendl; return -1; } From 419d731862961528c86bf461e0cd22fad82f6584 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:51:00 +0100 Subject: [PATCH 120/174] TRestComponent. Adding protection in case of no-initialization --- source/framework/sensitivity/src/TRestComponent.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 2945321bb..446e43665 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -278,6 +278,14 @@ Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) { ROOT::RVecD TRestComponent::GetRandom() { Double_t* tuple = new Double_t[GetDimensions()]; + + if (!GetDensity()) { + for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); + RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " + "TRestComponent::Initialize()." << RESTendl; + return result; + } + GetDensity()->GetRandom(tuple); ROOT::RVecD result; From ced4782290a41c9df737dbab92b75b5b916c241b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:57:38 +0100 Subject: [PATCH 121/174] TRestComponent. Fixing compilation issue --- source/framework/sensitivity/src/TRestComponent.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 446e43665..72df8a776 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -279,6 +279,7 @@ Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) { ROOT::RVecD TRestComponent::GetRandom() { Double_t* tuple = new Double_t[GetDimensions()]; + ROOT::RVecD result; if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " @@ -288,7 +289,6 @@ ROOT::RVecD TRestComponent::GetRandom() { GetDensity()->GetRandom(tuple); - ROOT::RVecD result; for (size_t n = 0; n < GetDimensions(); n++) result.push_back(tuple[n]); return result; } From 5af6ab38fe4d4b27a51f8ea4e696e059e5a18613 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:59:37 +0100 Subject: [PATCH 122/174] TRestExperiment. Implemented import of experimental dataset --- .../sensitivity/inc/TRestExperiment.h | 13 +++++--- .../sensitivity/src/TRestExperiment.cxx | 33 ++++++++++++++++--- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 7ce78e71d..2d285d687 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -41,11 +41,14 @@ class TRestExperiment : public TRestMetadata { /// A pointer to the signal component TRestComponent* fSignal = nullptr; //< + /// It defines the filename used to load the dataset + std::string fDataFile = ""; + /// It contains the experimental data (should contain same columns as the components) - TRestDataSet fTrackingData; //< + TRestDataSet fExperimentalData; //< - /// If enabled the tracking data will be MC-generated following background compatibility - Bool_t fMockTracking = false; //< + /// If enabled it means that the experimental data was MC-generated + Bool_t fMockData = false; //< /// Internal process random generator TRandom3* fRandom = nullptr; //! @@ -59,9 +62,11 @@ class TRestExperiment : public TRestMetadata { public: void GenerateMockDataSet(); + Bool_t IsMockData() { return fMockData; } + TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } - ROOT::RDF::RNode GetTrackingData() { return fTrackingData.GetDataFrame(); } + ROOT::RDF::RNode GetExperimentalData() { return fExperimentalData.GetDataFrame(); } void Initialize() override; diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 0861502ce..f728eb481 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -106,8 +106,10 @@ void TRestExperiment::GenerateMockDataSet() { ROOT::RDF::RNode df = fBackground->GetMonteCarloDataFrame(N); - fTrackingData.SetDataFrame(df); - fTrackingData.SetTotalTimeInSeconds(fExposureTime * units("s")); + fExperimentalData.SetDataFrame(df); + fExperimentalData.SetTotalTimeInSeconds(fExposureTime * units("s")); + + fMockData = true; } ///////////////////////////////////////////// @@ -136,11 +138,12 @@ void TRestExperiment::InitFromConfigFile() { std::string component = GetParameter("component", ele, ""); if (filename.empty()) - RESTWarning << "TRestExperiment. There is a problem with `filename` definition inside 0 && fDataFile.empty()) { + GenerateMockDataSet(); + } else if (fExposureTime == 0 && !fDataFile.empty()) { + fDataFile = SearchFile(fDataFile); + fExperimentalData.Import(fDataFile); + fExperimentalData.SetTotalTimeInSeconds(fExposureTime); + + fMockData = false; + + /// TODO : We need to check here that the experimental data got the same variables as the components. + /// Or we need to create a way to define which are the column names to be used in the dataset + } else { + RESTError << "The exposure time is not zero and the experimental data filename was defined!" + << RESTendl; + RESTError << "The exposure time will be defined by the dataset duration. Set exposure time to zero," + << RESTendl; + RESTError << " or do not define a dataset to generate mock data using the exposure time given" + << RESTendl; + } + Initialize(); } From 282ac441e15ca7936623d29836ab22146eed77a1 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 17:59:56 +0100 Subject: [PATCH 123/174] REST_AddComponentFormula.C added --- macros/REST_AddComponentFormula.C | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 macros/REST_AddComponentFormula.C diff --git a/macros/REST_AddComponentFormula.C b/macros/REST_AddComponentFormula.C new file mode 100644 index 000000000..940866c37 --- /dev/null +++ b/macros/REST_AddComponentFormula.C @@ -0,0 +1,46 @@ +#include "TRestComponent.h" +#include "TRestTask.h" + +#ifndef RestTask_AddComponentFormula +#define RestTask_AddComponentFormula + +//******************************************************************************************************* +//*** Description: This macro will load from an RML the component chosen in the arguments and it +//*** will write it inside the file given as outputFile +//*** +//*** -------------- +//*** Usage: restManager AddComponentFormula components.rml sectionName [outputFile] [componentName] [update] +//*** +//*** Arguments description: +//*** +//*** - cfgFile: The RML configuration file where the component definition can be found. +//*** - sectionName: The section name used to select a component inside the RML file. +//*** - outputFile: The file where the component is written, by default is components.root. +//*** - componentName: This argument allows to change the component name stored in the output file. +//*** By default it will take the same value as section name. +//*** - update: If disabled it will create a new file erasing any other previously added components. +//*** It is enabled by default. +//*** +//******************************************************************************************************* + +Int_t REST_AddComponentFormula(std::string cfgFile, std::string sectionName, + std::string outputFile = "components.root", std::string componentName = "", + Bool_t update = true) { + TRestComponentFormula comp(cfgFile.c_str(), sectionName.c_str()); + comp.Initialize(); + + TFile* f; + if (update) + f = TFile::Open(outputFile.c_str(), "UPDATE"); + else + f = TFile::Open(outputFile.c_str(), "RECREATE"); + + if (componentName == "") componentName = sectionName; + + comp.Write(componentName.c_str()); + + f->Close(); + + return 0; +} +#endif From 7ca1d828a0c04cc14700ee692865b841c606eb8b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:00:31 +0000 Subject: [PATCH 124/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/inc/TRestDataSet.h | 4 ++-- source/framework/sensitivity/src/TRestComponent.cxx | 3 ++- source/framework/sensitivity/src/TRestExperiment.cxx | 1 - source/framework/tools/inc/TRestTools.h | 10 +++------- source/framework/tools/src/TRestTools.cxx | 11 +++++++---- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index d5accd421..3ecc99bd6 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -134,10 +134,10 @@ class TRestDataSet : public TRestMetadata { /// Gives access to the tree TTree* GetTree() const { - if (fTree == nullptr && fExternal) { RESTInfo << "The tree is not accessible. Only GetDataFrame can be used in an externally " - "generated dataset" << RESTendl; + "generated dataset" + << RESTendl; RESTInfo << "You may write a tree using GetDataFrame()->Snapshot(\"MyTree\", \"output.root\");" << RESTendl; return fTree; diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index b7d3486cb..35f786d56 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -283,7 +283,8 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." << RESTendl; + "TRestComponent::Initialize()." + << RESTendl; return result; } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index f728eb481..1c1748d84 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -76,7 +76,6 @@ TRestExperiment::TRestExperiment(const char* cfgFileName, const std::string& nam /// (or observables) that have been defined by the user. /// void TRestExperiment::Initialize() { - SetSectionName(this->ClassName()); if (!fRandom) { diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h index 5d688dc66..34e1e18ab 100644 --- a/source/framework/tools/inc/TRestTools.h +++ b/source/framework/tools/inc/TRestTools.h @@ -32,7 +32,7 @@ #include #include -#define UNUSED(x) (void) x +#define UNUSED(x) (void)x #ifdef WIN32 #define EXTERN_DEF __declspec(dllimport) @@ -183,14 +183,10 @@ inline void SetInitLevel(T* name, int level) { } // namespace REST_InitTools -enum Quantities { - ENERGY, - LENGTH, - TIME -}; +enum Quantities { ENERGY, LENGTH, TIME }; class ValueWithQuantity { public: - ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity) {}; + ValueWithQuantity(double value, Quantities quantity) : fValue(value), fQuantity(quantity){}; double GetValue() const { return fValue; } std::string ToString() const; diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx index 78b06cdcd..9502be51a 100644 --- a/source/framework/tools/src/TRestTools.cxx +++ b/source/framework/tools/src/TRestTools.cxx @@ -873,13 +873,15 @@ string TRestTools::ToAbsoluteName(const string& filename) { const auto envVariableHome = getenv("HOME"); if (envVariableHome == nullptr) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable does not exist" << endl; + "cannot resolve ~ because 'HOME' env variable does not exist" + << endl; exit(1); } const auto userHomePath = filesystem::path(envVariableHome); if (userHomePath.empty()) { cout << "TRestTools::ToAbsoluteName - ERROR - " - "cannot resolve ~ because 'HOME' env variable is not set to a valid value" << endl; + "cannot resolve ~ because 'HOME' env variable is not set to a valid value" + << endl; exit(1); } path /= userHomePath; @@ -1102,7 +1104,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) { case '\r': if (sb->sgetc() == '\n') sb->sbumpc(); return is; - case std::streambuf::traits_type::eof() : + case std::streambuf::traits_type::eof(): // Also handle the case when the last line has no line ending if (t.empty()) is.setstate(std::ios::eofbit); return is; @@ -1307,7 +1309,8 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho RESTError << __PRETTY_FUNCTION__ << RESTendl; RESTError << "problem copying gases definitions to remote server" << RESTendl; RESTError << "Please report this problem at " - "http://gifna.unizar.es/rest-forum/" << RESTendl; + "http://gifna.unizar.es/rest-forum/" + << RESTendl; return -1; } From 6ab10ce52902ee8700b3ce896aa27f8fdfb265b0 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 18:25:05 +0100 Subject: [PATCH 125/174] TRestExperiment::SetExperimentalDataSetFile helper method added --- source/framework/sensitivity/inc/TRestExperiment.h | 11 +++++++++++ source/framework/sensitivity/src/TRestExperiment.cxx | 7 +------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 2d285d687..7497ca8d9 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -64,6 +64,17 @@ class TRestExperiment : public TRestMetadata { Bool_t IsMockData() { return fMockData; } + void SetExperimentalDataSetFile(const std::string& filename) { + fDataFile = SearchFile(filename); + fExperimentalData.Import(fDataFile); + fExposureTime = fExperimentalData.GetTotalTimeInSeconds() * units("s"); + + fMockData = false; + + /// TODO : We need to check here that the experimental data got the same variables as the components. + /// Or we need to create a way to define which are the column names to be used in the dataset + } + TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } ROOT::RDF::RNode GetExperimentalData() { return fExperimentalData.GetDataFrame(); } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index f728eb481..000b374e3 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -168,14 +168,9 @@ void TRestExperiment::InitFromConfigFile() { if (fExposureTime > 0 && fDataFile.empty()) { GenerateMockDataSet(); } else if (fExposureTime == 0 && !fDataFile.empty()) { - fDataFile = SearchFile(fDataFile); - fExperimentalData.Import(fDataFile); - fExperimentalData.SetTotalTimeInSeconds(fExposureTime); - fMockData = false; + SetExperimentalDataSetFile(fDataFile); - /// TODO : We need to check here that the experimental data got the same variables as the components. - /// Or we need to create a way to define which are the column names to be used in the dataset } else { RESTError << "The exposure time is not zero and the experimental data filename was defined!" << RESTendl; From c25314f82eaa7fdb63213be2fc638923b8e78dae Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 21 Jan 2024 23:50:49 +0100 Subject: [PATCH 126/174] TRestExperimentList. Table may be built with any number of columns. Exposure/Signal/Background) --- .../sensitivity/inc/TRestExperimentList.h | 9 +++ .../sensitivity/src/TRestExperimentList.cxx | 79 +++++++++++++------ 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperimentList.h b/source/framework/sensitivity/inc/TRestExperimentList.h index 1a43e84ed..ec747d205 100644 --- a/source/framework/sensitivity/inc/TRestExperimentList.h +++ b/source/framework/sensitivity/inc/TRestExperimentList.h @@ -50,6 +50,15 @@ class TRestExperimentList : public TRestMetadata { /// A vector with a list of experiments includes the background components in this model std::vector fExperiments; //< + /// If not zero this will be the common exposure time in micro-seconds (standard REST units) + Double_t fExposureTime = 0; + + /// If not null this will be the common signal used in each experiment + TRestComponent* fSignal = nullptr; //< + + /// If not null this will be the common signal used in each experiment + TRestComponent* fBackground = nullptr; //< + protected: void InitFromConfigFile() override; diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 403ea297e..1cbc24a0d 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -82,31 +82,64 @@ void TRestExperimentList::Initialize() { SetSectionName(this->ClassName()); } void TRestExperimentList::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); - /* -auto ele = GetElement("variable"); -while (ele != nullptr) { - std::string name = GetParameter("name", ele, ""); - TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); - Int_t bins = StringToInteger(GetParameter("bins", ele, "0")); - - if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) { - RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl; - RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins - << RESTendl; - } else { - fVariables.push_back(name); - fRanges.push_back(v); - fNbins.push_back(bins); - } - - ele = GetNextElement(ele); -} - */ - if (!fExperimentsFile.empty()) { TRestTools::ReadASCIITable(fExperimentsFile, fExperimentsTable); - TRestTools::PrintTable(fExperimentsTable, 0, 3); + for (auto& row : fExperimentsTable) + for (auto& el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el); + + if (fExperimentsTable.empty()) { + RESTError << "TRestExperimentList::InitFromConfigFile. The experiments table is empty!" + << RESTendl; + return; + } + + Int_t nTableColumns = fExperimentsTable[0].size(); + + int cont = 0; + TRestComponent* comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + while (comp != nullptr) { + if (ToLower(comp->GetNature()) == "background") + fBackground = comp; + else if (ToLower(comp->GetNature()) == "signal") + fSignal = comp; + else + RESTWarning << "TRestExperimentList::InitFromConfigFile. Unknown component!" << RESTendl; + + cont++; + comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + } + + Int_t nExpectedColumns = 3; + if (fSignal) nExpectedColumns--; + if (fBackground) nExpectedColumns--; + if (fExposureTime > 0) nExpectedColumns--; + + if (nExpectedColumns == 0) { + RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " + "(Exposure/Background/Signal)" << RESTendl; + return; + } + + if (nExpectedColumns != nTableColumns) { + std::string expectedColumns = ""; + if (fExposureTime == 0) expectedColumns += "exposure"; + if (!fSignal) { + if (fExposureTime == 0) expectedColumns += "/"; + expectedColumns += "signal"; + } + if (!fBackground) { + if (fExposureTime == 0 || !fSignal) expectedColumns += "/"; + expectedColumns += "background"; + } + + RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " + "the number of table columns" << RESTendl; + RESTError << "Number of table columns : " << nTableColumns << RESTendl; + RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; + RESTError << "Expected columns : " << expectedColumns << RESTendl; + return; + } } } @@ -116,5 +149,7 @@ while (ele != nullptr) { void TRestExperimentList::PrintMetadata() { TRestMetadata::PrintMetadata(); + RESTMetadata << "Number of experiments loaded: " << fExperiments.size() << RESTendl; + RESTMetadata << "----" << RESTendl; } From 52b2d821dc5d46a59d59e6532bc7ed45d2feb55f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:51:17 +0000 Subject: [PATCH 127/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/sensitivity/src/TRestExperiment.cxx | 1 - source/framework/sensitivity/src/TRestExperimentList.cxx | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 38245d34e..cee7cbe06 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -167,7 +167,6 @@ void TRestExperiment::InitFromConfigFile() { if (fExposureTime > 0 && fDataFile.empty()) { GenerateMockDataSet(); } else if (fExposureTime == 0 && !fDataFile.empty()) { - SetExperimentalDataSetFile(fDataFile); } else { diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 1cbc24a0d..64a4b0c31 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -117,7 +117,8 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" << RESTendl; + "(Exposure/Background/Signal)" + << RESTendl; return; } @@ -134,7 +135,8 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" << RESTendl; + "the number of table columns" + << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; From 983699270e62a5d32edb7779d9c108413b34e4cd Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 23 Jan 2024 12:17:50 +0100 Subject: [PATCH 128/174] TRestAnalysisTree::GetDblObservableValue. Adding float type --- source/framework/core/src/TRestAnalysisTree.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index 01d2c6622..1d61f67e6 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -623,6 +623,7 @@ Double_t TRestAnalysisTree::GetDblObservableValue(Int_t n) { } if (GetObservableType(n) == "int") return GetObservableValue(n); + if (GetObservableType(n) == "float") return GetObservableValue(n); if (GetObservableType(n) == "double") return GetObservableValue(n); cout << "TRestAnalysisTree::GetDblObservableValue. Type " << GetObservableType(n) @@ -1241,8 +1242,7 @@ Bool_t TRestAnalysisTree::AddChainFile(const string& _file) { } RESTWarning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file has different " - "run id!" - << RESTendl; + "run id!" << RESTendl; } RESTWarning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file is empty!" << RESTendl; From d2f8b24b4554b088f850760a9b9a4ac450efb33f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:39:32 +0000 Subject: [PATCH 129/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/src/TRestAnalysisTree.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index 1d61f67e6..d52950b24 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -1242,7 +1242,8 @@ Bool_t TRestAnalysisTree::AddChainFile(const string& _file) { } RESTWarning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file has different " - "run id!" << RESTendl; + "run id!" + << RESTendl; } RESTWarning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file is empty!" << RESTendl; From d69293c76a2db6b6683f2b055ad203e9065d9fea Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 23 Jan 2024 14:44:16 +0100 Subject: [PATCH 130/174] TRestSystemOfUnits. Fixing time units scaling leading to wrong results --- .../framework/core/inc/TRestSystemOfUnits.h | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index 648f80171..7c4cfd204 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -42,7 +42,17 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; +enum Physical_Unit { + Energy, + Time, + Length, + Mass, + Voltage, + MagneticField, + Pressure, + Angle, + NOT_A_UNIT = -1 +}; class TRestSystemOfUnits { private: @@ -114,11 +124,11 @@ AddUnit(us, REST_Units::Time, 1.); AddUnit(ms, REST_Units::Time, 1.e-3); AddUnit(s, REST_Units::Time, 1.e-6); AddUnit(Hz, REST_Units::Time, 1.e6); -AddUnit(minu, REST_Units::Time, 1.67e-8); -AddUnit(hr, REST_Units::Time, 2.78e-10); -AddUnit(day, REST_Units::Time, 1.16e-11); -AddUnit(mon, REST_Units::Time, 3.85e-13); -AddUnit(yr, REST_Units::Time, 3.17e-14); +AddUnit(minu, REST_Units::Time, 1.e-6 / 60.); +AddUnit(hr, REST_Units::Time, 1e-6 / 3600.); +AddUnit(day, REST_Units::Time, 1e-6 / 3600. / 24.); +AddUnit(mon, REST_Units::Time, 1e-6 / 3600. / 24. / 30); +AddUnit(yr, REST_Units::Time, 1e-6 / 3600. / 24. / 365.25); // length unit multiplier AddUnit(nm, REST_Units::Length, 1e6); From eeb379cdcc7f55799590417ce7779d6b7c06afba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:44:46 +0000 Subject: [PATCH 131/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/inc/TRestSystemOfUnits.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index 7c4cfd204..58ce4e94a 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -42,17 +42,7 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { - Energy, - Time, - Length, - Mass, - Voltage, - MagneticField, - Pressure, - Angle, - NOT_A_UNIT = -1 -}; +enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; class TRestSystemOfUnits { private: From d1d9db74a753e39136eb4628d25bc8ee150788c2 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 11:15:06 +0100 Subject: [PATCH 132/174] TRestComponent::SetActiveNode added with integer index --- source/framework/sensitivity/inc/TRestComponent.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 33628e6cf..7b873cf2c 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -106,6 +106,10 @@ class TRestComponent : public TRestMetadata { Int_t GetActiveNode() { return fActiveNode; } Int_t SetActiveNode(Double_t node); + Int_t SetActiveNode(Int_t n) { + fActiveNode = n; + return fActiveNode; + } Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } Bool_t Interpolation() { return fInterpolation; } From a2e08f65055e1886787d66f421d22ec1969129d6 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 11:28:00 +0100 Subject: [PATCH 133/174] TRestExperiment. Adding setters --- source/framework/sensitivity/inc/TRestExperiment.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 7497ca8d9..dcb7fbb99 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -54,7 +54,7 @@ class TRestExperiment : public TRestMetadata { TRandom3* fRandom = nullptr; //! /// Seed used in random generator - Int_t fSeed = 0; //< + UInt_t fSeed = 0; //< protected: void InitFromConfigFile() override; @@ -64,6 +64,10 @@ class TRestExperiment : public TRestMetadata { Bool_t IsMockData() { return fMockData; } + void SetExposureInSeconds(const Double_t exposure) { fExposureTime = exposure / units("s"); } + void SetSignal(TRestComponent* comp) { fSignal = comp; } + void SetBackground(TRestComponent* comp) { fBackground = comp; } + void SetExperimentalDataSetFile(const std::string& filename) { fDataFile = SearchFile(filename); fExperimentalData.Import(fDataFile); @@ -72,9 +76,11 @@ class TRestExperiment : public TRestMetadata { fMockData = false; /// TODO : We need to check here that the experimental data got the same variables as the components. - /// Or we need to create a way to define which are the column names to be used in the dataset + /// Or we need to create a way to connect the column names to be used in the dataset with the + /// variables } + Double_t GetExposureInSeconds() { return fExposureTime * units("s"); } TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } ROOT::RDF::RNode GetExperimentalData() { return fExperimentalData.GetDataFrame(); } From f69e1295a44131f259c18e0a6c414e9604c2afee Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 11:35:09 +0100 Subject: [PATCH 134/174] TRestExperimentList::GetComponent method added --- .../sensitivity/inc/TRestExperimentList.h | 24 ++++- .../sensitivity/src/TRestExperimentList.cxx | 99 +++++++++++++++++-- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperimentList.h b/source/framework/sensitivity/inc/TRestExperimentList.h index ec747d205..acb8e6220 100644 --- a/source/framework/sensitivity/inc/TRestExperimentList.h +++ b/source/framework/sensitivity/inc/TRestExperimentList.h @@ -48,28 +48,42 @@ class TRestExperimentList : public TRestMetadata { std::vector > fExperimentsTable; //< /// A vector with a list of experiments includes the background components in this model - std::vector fExperiments; //< + std::vector fExperiments; //< /// If not zero this will be the common exposure time in micro-seconds (standard REST units) Double_t fExposureTime = 0; /// If not null this will be the common signal used in each experiment - TRestComponent* fSignal = nullptr; //< + TRestComponent *fSignal = nullptr; //< /// If not null this will be the common signal used in each experiment - TRestComponent* fBackground = nullptr; //< + TRestComponent *fBackground = nullptr; //< protected: + TRestComponent *GetComponent(std::string compName); + void InitFromConfigFile() override; public: void Initialize() override; - std::vector GetExperiments() { return fExperiments; } + void SetExposure(const Double_t &exposure) { fExposureTime = exposure; } + void SetSignal(TRestComponent *comp) { fSignal = comp; } + void SetBackground(TRestComponent *comp) { fBackground = comp; } + + std::vector GetExperiments() { return fExperiments; } + TRestExperiment *GetExperiment(const size_t &n) { + if (n >= GetNumberOfExperiments()) + return nullptr; + else + return fExperiments[n]; + } + + size_t GetNumberOfExperiments() { return fExperiments.size(); } void PrintMetadata() override; - TRestExperimentList(const char* cfgFileName, const std::string& name); + TRestExperimentList(const char *cfgFileName, const std::string &name); TRestExperimentList(); ~TRestExperimentList(); diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 64a4b0c31..25b5dfa7e 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -65,7 +65,7 @@ TRestExperimentList::~TRestExperimentList() {} /// \param cfgFileName A const char* giving the path to an RML file. /// \param name The name of the specific metadata. /// -TRestExperimentList::TRestExperimentList(const char* cfgFileName, const std::string& name) +TRestExperimentList::TRestExperimentList(const char *cfgFileName, const std::string &name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -82,11 +82,11 @@ void TRestExperimentList::Initialize() { SetSectionName(this->ClassName()); } void TRestExperimentList::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); - if (!fExperimentsFile.empty()) { + if (!fExperimentsFile.empty() && fExperiments.empty()) { TRestTools::ReadASCIITable(fExperimentsFile, fExperimentsTable); - for (auto& row : fExperimentsTable) - for (auto& el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el); + for (auto &row : fExperimentsTable) + for (auto &el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el); if (fExperimentsTable.empty()) { RESTError << "TRestExperimentList::InitFromConfigFile. The experiments table is empty!" @@ -97,7 +97,7 @@ void TRestExperimentList::InitFromConfigFile() { Int_t nTableColumns = fExperimentsTable[0].size(); int cont = 0; - TRestComponent* comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + TRestComponent *comp = (TRestComponent *)this->InstantiateChildMetadata(cont, "Component"); while (comp != nullptr) { if (ToLower(comp->GetNature()) == "background") fBackground = comp; @@ -107,7 +107,7 @@ void TRestExperimentList::InitFromConfigFile() { RESTWarning << "TRestExperimentList::InitFromConfigFile. Unknown component!" << RESTendl; cont++; - comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); + comp = (TRestComponent *)this->InstantiateChildMetadata(cont, "Component"); } Int_t nExpectedColumns = 3; @@ -117,8 +117,7 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" - << RESTendl; + "(Exposure/Background/Signal)" << RESTendl; return; } @@ -135,14 +134,94 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" - << RESTendl; + "the number of table columns" << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; return; } + + fComponentFiles = TRestTools::GetFilesMatchingPattern(fComponentPattern); + + Bool_t generateMockData = false; + for (const auto &experimentRow : fExperimentsTable) { + TRestExperiment *experiment = new TRestExperiment(); + + std::string rowStr = ""; + for (const auto &el : experimentRow) { + rowStr += el + " "; + } + + RESTInfo << "Loading experiment: " << rowStr << RESTendl; + + int column = 0; + if (fExposureTime == 0) { + if (REST_StringHelper::isANumber(experimentRow[column])) { + experiment->SetExposureInSeconds( + REST_StringHelper::StringToDouble(experimentRow[column])); + // We will generate mock data once we load the background component + generateMockData = true; + } else if (TRestTools::isRootFile(experimentRow[column])) { + // We load the file with the dataset into the experimental data + std::string fname = SearchFile(experimentRow[column]); + experiment->SetExperimentalDataSetFile(fname); + RESTWarning << "Loading experimental data havent been tested yet!" << RESTendl; + RESTWarning + << "It might require further development. Remove these lines once it works smooth!" + << RESTendl; + } else { + RESTError << experimentRow[column] << " is not a exposure time or an experimental dataset" + << RESTendl; + } + column++; + } else { + experiment->SetExposureInSeconds(fExposureTime * units("s")); + // We will generate mock data once we load the background component + generateMockData = true; + } + + if (!fSignal) { + TRestComponent *sgnl = (TRestComponent *)GetComponent(experimentRow[column])->Clone(); + experiment->SetSignal(sgnl); + column++; + } else { + experiment->SetSignal(fSignal); + } + + if (!fBackground) { + TRestComponent *bck = (TRestComponent *)GetComponent(experimentRow[column])->Clone(); + experiment->SetBackground(bck); + } else { + experiment->SetBackground(fBackground); + } + + if (generateMockData) { + RESTInfo << "Generating mock dataset" << RESTendl; + experiment->GenerateMockDataSet(); + } + + fExperiments.push_back(experiment); + } + } +} + +TRestComponent *TRestExperimentList::GetComponent(std::string compName) { + TRestComponent *component = nullptr; + for (const auto &c : fComponentFiles) { + TFile *f = TFile::Open(c.c_str(), "READ"); + TObject *obj = f->Get((TString)compName); + + if (!obj) continue; + + if (obj->InheritsFrom("TRestComponent")) { + return (TRestComponent *)obj; + } else { + RESTError << "An object named : " << compName + << " exists inside the file, but it does not inherit from TRestComponent" << RESTendl; + } } + + return component; } ///////////////////////////////////////////// From 8ed5880a6b02db635c7bb937d50db2d2ac94ee57 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 11:35:54 +0100 Subject: [PATCH 135/174] TRestExperiment::PrintMetadata implemented --- .../sensitivity/src/TRestExperiment.cxx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index cee7cbe06..e6d59951a 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -187,9 +187,27 @@ void TRestExperiment::InitFromConfigFile() { void TRestExperiment::PrintMetadata() { TRestMetadata::PrintMetadata(); - if (fSignal) RESTMetadata << "Signal component : " << fSignal->GetName() << RESTendl; + RESTMetadata << "Random seed : " << fSeed << RESTendl; + RESTMetadata << " " << RESTendl; + if (fExposureTime > 0) + RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " s" << RESTendl; + + if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; + + if (fBackground) RESTMetadata << " - Background component : " << fBackground->GetName() << RESTendl; + + if (fMockData) { + RESTMetadata << " " << RESTendl; + if (fMockData) + RESTMetadata << "The dataset was MC-generated" << RESTendl; + else { + RESTMetadata << "The dataset was loaded from an existing dataset file" << RESTendl; + if (!fDataFile.empty()) + RESTMetadata << " - Experimental dataset file : " << fDataFile << RESTendl; + } + } - if (fBackground) RESTMetadata << "Background component : " << fBackground->GetName() << RESTendl; + RESTMetadata << " - Experimental counts : " << *fExperimentalData.GetDataFrame().Count() << RESTendl; RESTMetadata << "----" << RESTendl; } From af0536a1e630e55861ef1ab5ceb3e4b81c77f7d7 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 19:05:51 +0100 Subject: [PATCH 136/174] TRestComponent. Adding fPrecision --- .../sensitivity/inc/TRestComponent.h | 24 +++++++++++-------- .../sensitivity/inc/TRestComponentDataSet.h | 4 ++-- .../sensitivity/inc/TRestComponentFormula.h | 2 +- .../sensitivity/src/TRestComponentDataSet.cxx | 18 +++++++------- .../sensitivity/src/TRestComponentFormula.cxx | 2 +- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 7b873cf2c..b0c82db98 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -66,6 +66,9 @@ class TRestComponent : public TRestMetadata { /// A pointer to the detector response TRestResponse* fResponse = nullptr; //< + /// A precision used to select the node value with a given range defined as a fraction of the value + Float_t fPrecision = 0.01; + /// A canvas for drawing the active node component TCanvas* fCanvas = nullptr; //! @@ -85,32 +88,30 @@ class TRestComponent : public TRestMetadata { void InitFromConfigFile() override; - virtual void FillHistograms(Double_t precision = 0.01) = 0; + virtual void FillHistograms() = 0; public: std::string GetNature() const { return fNature; } TRestResponse* GetResponse() const { return fResponse; } + Float_t GetPrecision() { return fPrecision; } + size_t GetDimensions() { return fVariables.size(); } + Int_t GetActiveNode() { return fActiveNode; } + Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } Double_t GetRawRate(std::vector point); Double_t GetTotalRate(); - - Double_t GetBinCenter(Int_t nDim, const Int_t bin); - - TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, - Int_t binScanSize = 1, TString drawOption = ""); - Double_t GetNormalizedRate(std::vector point); Double_t GetRate(std::vector point); - size_t GetDimensions() { return fVariables.size(); } + Double_t GetBinCenter(Int_t nDim, const Int_t bin); + + void SetPrecision(const Float_t& pr) { fPrecision = pr; } - Int_t GetActiveNode() { return fActiveNode; } Int_t SetActiveNode(Double_t node); Int_t SetActiveNode(Int_t n) { fActiveNode = n; return fActiveNode; } - Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } Bool_t Interpolation() { return fInterpolation; } void EnableInterpolation() { fInterpolation = true; } @@ -132,6 +133,9 @@ class TRestComponent : public TRestMetadata { ROOT::RDF::RNode GetMonteCarloDataFrame(Int_t N = 100); + TCanvas* DrawComponent(std::vector drawVariables, std::vector scanVariables, + Int_t binScanSize = 1, TString drawOption = ""); + void LoadResponse(const TRestResponse& resp); void PrintMetadata() override; diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index c8dca1321..3348bbe99 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -57,8 +57,8 @@ class TRestComponentDataSet : public TRestComponent { protected: std::vector ExtractParameterizationNodes(); - std::vector ExtractNodeStatistics(Double_t precision = 0.01); - void FillHistograms(Double_t precision = 0.01) override; + std::vector ExtractNodeStatistics(); + void FillHistograms() override; Bool_t VariablesOk(); Bool_t WeightsOk(); diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h index 3ea49d7b2..9f9046824 100644 --- a/source/framework/sensitivity/inc/TRestComponentFormula.h +++ b/source/framework/sensitivity/inc/TRestComponentFormula.h @@ -40,7 +40,7 @@ class TRestComponentFormula : public TRestComponent { protected: void InitFromConfigFile() override; - void FillHistograms(Double_t precision = 0.01) override; + void FillHistograms() override; public: Double_t GetFormulaRate(std::vector point); diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 56d8e06df..e2c0f74f5 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -210,12 +210,12 @@ void TRestComponentDataSet::InitFromConfigFile() { /// \brief It will produce a histogram with the distribution defined using the /// variables and the weights for each of the parameter nodes. /// -/// The precision is used to define the active node +/// fPrecision is used to define the active node /// -void TRestComponentDataSet::FillHistograms(Double_t precision) { +void TRestComponentDataSet::FillHistograms() { if (!fNodeDensity.empty()) return; - fNSimPerNode = ExtractNodeStatistics(precision); + fNSimPerNode = ExtractNodeStatistics(); if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::FillHistograms. Dataset has not been initialized!" << RESTendl; @@ -241,8 +241,8 @@ void TRestComponentDataSet::FillHistograms(Double_t precision) { } else { RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) << RESTendl; - Double_t pUp = node * (1 + precision / 2); - Double_t pDown = node * (1 - precision / 2); + Double_t pUp = node * (1 + fPrecision / 2); + Double_t pDown = node * (1 - fPrecision / 2); std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); df = fDataSet.GetDataFrame().Filter(filter); @@ -322,11 +322,11 @@ std::vector TRestComponentDataSet::ExtractParameterizationNodes() { /// /// If fNSimPerNode has already been initialized it will directly return its value. /// -/// The argument precision will be used to include a thin range where to select +/// fPrecision will be used to include a thin range where to select /// the node values. The value defines the range with a fraction proportional to /// the parameter value. /// -std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precision) { +std::vector TRestComponentDataSet::ExtractNodeStatistics() { if (!fNSimPerNode.empty()) return fNSimPerNode; std::vector stats; @@ -339,8 +339,8 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics(Double_t precisi RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; for (const auto& p : fParameterizationNodes) { - Double_t pUp = p * (1 + precision / 2); - Double_t pDown = p * (1 - precision / 2); + Double_t pUp = p * (1 + fPrecision / 2); + Double_t pDown = p * (1 - fPrecision / 2); std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); RESTInfo << "Counting stats for : " << fParameter << " = " << p << RESTendl; diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 731c0300c..48166d1d8 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -142,7 +142,7 @@ Double_t TRestComponentFormula::GetFormulaRate(std::vector point) { /// if the component expression depends on the node parameter it might require /// further development. /// -void TRestComponentFormula::FillHistograms(Double_t precision) { +void TRestComponentFormula::FillHistograms() { if (fFormulas.empty()) return; RESTInfo << "Generating N-dim histogram for " << GetName() << RESTendl; From 1f0fa93259b15a00fd21c787ca54bb17e56d8263 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 24 Jan 2024 19:16:23 +0100 Subject: [PATCH 137/174] TRestComponent::SetActiveNode uses now the fPrecision value --- source/framework/sensitivity/src/TRestComponent.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 35f786d56..c56fd577d 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -283,8 +283,7 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." - << RESTendl; + "TRestComponent::Initialize()." << RESTendl; return result; } @@ -328,15 +327,13 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -578,7 +575,9 @@ void TRestComponent::InitFromConfigFile() { Int_t TRestComponent::SetActiveNode(Double_t node) { int n = 0; for (const auto& v : fParameterizationNodes) { - if (v == node) { + Double_t pUp = node * (1 + fPrecision / 2); + Double_t pDown = node * (1 - fPrecision / 2); + if (v > pDown && v < pUp) { fActiveNode = n; return fActiveNode; } From a3017b1c96609eea1a439a09ea735e6f1cd747ef Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 28 Jan 2024 22:36:14 +0100 Subject: [PATCH 138/174] TRestSensitivity::InitFromConfigFile. Experiments are being read --- .../sensitivity/inc/TRestSensitivity.h | 16 ++++++- .../sensitivity/src/TRestSensitivity.cxx | 47 ++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h index c8ad6f0db..44b67bbc4 100644 --- a/source/framework/sensitivity/inc/TRestSensitivity.h +++ b/source/framework/sensitivity/inc/TRestSensitivity.h @@ -29,13 +29,27 @@ class TRestSensitivity : public TRestMetadata { private: /// A list of experimental conditions included to get a final sensitivity plot - std::vector fExperiment; //< + std::vector fExperiments; //< + + protected: + void InitFromConfigFile() override; public: void Initialize() override; + std::vector GetExperiments() { return fExperiments; } + TRestExperiment *GetExperiment(const size_t &n) { + if (n >= GetNumberOfExperiments()) + return nullptr; + else + return fExperiments[n]; + } + + size_t GetNumberOfExperiments() { return fExperiments.size(); } + void PrintMetadata() override; + TRestSensitivity(const char *cfgFileName, const std::string &name = ""); TRestSensitivity(); ~TRestSensitivity(); diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index 84503f12a..714cb8ad2 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -38,7 +38,8 @@ /// ///
    /// -#include "TRestSensitivity.h" +#include +#include ClassImp(TRestSensitivity); @@ -52,12 +53,56 @@ TRestSensitivity::TRestSensitivity() { Initialize(); } /// TRestSensitivity::~TRestSensitivity() {} +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) + : TRestMetadata(cfgFileName) { + LoadConfigFromFile(fConfigFileName, name); +} + /////////////////////////////////////////////// /// \brief It will initialize the data frame with the filelist and column names /// (or observables) that have been defined by the user. /// void TRestSensitivity::Initialize() { SetSectionName(this->ClassName()); } +///////////////////////////////////////////// +/// \brief It customizes the retrieval of XML data values of this class +/// +void TRestSensitivity::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + int cont = 0; + TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + while (metadata != nullptr) { + + cont++; + if (metadata->InheritsFrom("TRestExperimentList")) { + TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; + std::vector exList = experimentsList->GetExperiments(); + fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); + } else if (metadata->InheritsFrom("TRestExperiment")) { + fExperiments.push_back((TRestExperiment *)metadata); + } + + metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + } + + Initialize(); +} + ///////////////////////////////////////////// /// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux /// From 4aa0526a13c62865d50ac9feb847451f22c59784 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 28 Jan 2024 22:37:42 +0100 Subject: [PATCH 139/174] TRestExperiment. fDataSet renamed to fExperimentalDataSet --- source/framework/sensitivity/inc/TRestExperiment.h | 6 +++--- source/framework/sensitivity/src/TRestExperiment.cxx | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index dcb7fbb99..aebdc674e 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -42,7 +42,7 @@ class TRestExperiment : public TRestMetadata { TRestComponent* fSignal = nullptr; //< /// It defines the filename used to load the dataset - std::string fDataFile = ""; + std::string fExperimentalDataSet = ""; /// It contains the experimental data (should contain same columns as the components) TRestDataSet fExperimentalData; //< @@ -69,8 +69,8 @@ class TRestExperiment : public TRestMetadata { void SetBackground(TRestComponent* comp) { fBackground = comp; } void SetExperimentalDataSetFile(const std::string& filename) { - fDataFile = SearchFile(filename); - fExperimentalData.Import(fDataFile); + fExperimentalDataSet = SearchFile(filename); + fExperimentalData.Import(fExperimentalDataSet); fExposureTime = fExperimentalData.GetTotalTimeInSeconds() * units("s"); fMockData = false; diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index e6d59951a..50049a5cd 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -164,10 +164,10 @@ void TRestExperiment::InitFromConfigFile() { } } - if (fExposureTime > 0 && fDataFile.empty()) { + if (fExposureTime > 0 && fExperimentalDataSet.empty()) { GenerateMockDataSet(); - } else if (fExposureTime == 0 && !fDataFile.empty()) { - SetExperimentalDataSetFile(fDataFile); + } else if (fExposureTime == 0 && !fExperimentalDataSet.empty()) { + SetExperimentalDataSetFile(fExperimentalDataSet); } else { RESTError << "The exposure time is not zero and the experimental data filename was defined!" @@ -202,8 +202,8 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "The dataset was MC-generated" << RESTendl; else { RESTMetadata << "The dataset was loaded from an existing dataset file" << RESTendl; - if (!fDataFile.empty()) - RESTMetadata << " - Experimental dataset file : " << fDataFile << RESTendl; + if (!fExperimentalDataSet.empty()) + RESTMetadata << " - Experimental dataset file : " << fExperimentalDataSet << RESTendl; } } From 597951f2a5a93d5f7ddbec200aa9569341657b75 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 28 Jan 2024 22:40:10 +0100 Subject: [PATCH 140/174] TRestComponentDataSet::fSamples added to allow reducing the dataset size used to build the component --- .../sensitivity/inc/TRestComponentDataSet.h | 9 +++++- .../sensitivity/src/TRestComponentDataSet.cxx | 28 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 3348bbe99..698758b51 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -34,12 +34,19 @@ class TRestComponentDataSet : public TRestComponent { /// A list with the dataset columns used to weight the distribution density and define rate std::vector fWeights; //< - /// It defines the number of entries for each parameterization node (Initialized by the dataset) + /// It defines the number of entries in the sample for each parameterization node (Initialized by the + /// dataset) std::vector fNSimPerNode; //< + /// It defines the total number of entries for each parameterization node (Initialized by the dataset) + std::vector fTotalSamples; //< + /// The filename of the dataset used std::vector fDataSetFileNames; //< + /// It introduces a fixed number of samples (if 0 it will take all available samples) + Int_t fSamples = 0; + /// TODO we need to define multiple datasets and weigth. The weight will be used /// to create a model, such as weighting different background contaminations or /// different signal coupling contributions. diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index e2c0f74f5..fbcc1f3e9 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -139,6 +139,8 @@ void TRestComponentDataSet::Initialize() { void TRestComponentDataSet::PrintMetadata() { TRestComponent::PrintMetadata(); + if (fSamples) RESTMetadata << "Data subset samples : " << fSamples << RESTendl; + if (!fDataSetFileNames.empty()) { RESTMetadata << " " << RESTendl; RESTMetadata << " == Dataset filenames ==" << RESTendl; @@ -231,12 +233,19 @@ void TRestComponentDataSet::FillHistograms() { RESTInfo << "Generating N-dim histograms" << RESTendl; int nIndex = 0; for (const auto& node : fParameterizationNodes) { + Int_t from = 0; + Int_t to = 0; + if (fSamples > 0 && fTotalSamples[nIndex] - fSamples > 0) { + from = fRandom->Integer(fTotalSamples[nIndex] - fSamples); + to = from + fSamples; + } + ROOT::RDF::RNode df = ROOT::RDataFrame(0); //// Yet not tested in the case when we want to define a unique node without filters //// Needs to be improved if (fParameterizationNodes.size() == 1 && node == -137) { RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl; - df = fDataSet.GetDataFrame(); + df = fDataSet.GetDataFrame().Range(from, to); fParameterizationNodes.clear(); } else { RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) @@ -245,7 +254,7 @@ void TRestComponentDataSet::FillHistograms() { Double_t pDown = node * (1 - fPrecision / 2); std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); - df = fDataSet.GetDataFrame().Filter(filter); + df = fDataSet.GetDataFrame().Filter(filter).Range(from, to); } Int_t* bins = new Int_t[fNbins.size()]; @@ -329,6 +338,8 @@ std::vector TRestComponentDataSet::ExtractParameterizationNodes() { std::vector TRestComponentDataSet::ExtractNodeStatistics() { if (!fNSimPerNode.empty()) return fNSimPerNode; + fTotalSamples.clear(); + std::vector stats; if (!IsDataSetLoaded()) { RESTError << "TRestComponentDataSet::ExtractNodeStatistics. Dataset has not been initialized!" @@ -339,13 +350,24 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; for (const auto& p : fParameterizationNodes) { + Double_t pUp = p * (1 + fPrecision / 2); Double_t pDown = p * (1 - fPrecision / 2); std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); RESTInfo << "Counting stats for : " << fParameter << " = " << p << RESTendl; auto nEv = fDataSet.GetDataFrame().Filter(filter).Count(); - RESTInfo << "Counts found : " << *nEv << RESTendl; + fTotalSamples.push_back(*nEv); + RESTInfo << "Total entries for " << fParameter << ":" << p << " = " << *nEv << RESTendl; + if (fSamples != 0) { + nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count(); + } + + if ((Int_t) * nEv < fSamples) { + RESTWarning << "The number of requested samples (" << fSamples + << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl; + } + RESTInfo << "Samples to be used for " << fParameter << ":" << p << " = " << *nEv << RESTendl; stats.push_back(*nEv); } return stats; From 07f611ff5935960f1d830ab6310cf657d7e30dbb Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 28 Jan 2024 22:41:09 +0100 Subject: [PATCH 141/174] TRestComponent. Adding precision and random numbers --- .../sensitivity/inc/TRestComponent.h | 14 +++++++++++-- .../sensitivity/src/TRestComponent.cxx | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index b0c82db98..5c220707a 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,6 +23,8 @@ #ifndef REST_TRestComponent #define REST_TRestComponent +#include + #include #include @@ -67,7 +69,13 @@ class TRestComponent : public TRestMetadata { TRestResponse* fResponse = nullptr; //< /// A precision used to select the node value with a given range defined as a fraction of the value - Float_t fPrecision = 0.01; + Float_t fPrecision = 0.01; //< + + /// Internal process random generator + TRandom3* fRandom = nullptr; //! + + /// Seed used in random generator + UInt_t fSeed = 0; //< /// A canvas for drawing the active node component TCanvas* fCanvas = nullptr; //! @@ -91,6 +99,8 @@ class TRestComponent : public TRestMetadata { virtual void FillHistograms() = 0; public: + void Initialize() override; + std::string GetNature() const { return fNature; } TRestResponse* GetResponse() const { return fResponse; } Float_t GetPrecision() { return fPrecision; } @@ -147,6 +157,6 @@ class TRestComponent : public TRestMetadata { TRestComponent(); ~TRestComponent(); - ClassDefOverride(TRestComponent, 3); + ClassDefOverride(TRestComponent, 4); }; #endif diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index c56fd577d..db849f8c9 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -77,6 +77,24 @@ TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name) /// TRestComponent::~TRestComponent() {} +/////////////////////////////////////////////// +/// \brief It initializes the random number. We avoid to define the section name +/// here since we will never define a TRestComponent section in our RML file, +/// since this class is pure virtual. It will be the inherited class the +/// responsible to define the section name. +/// +void TRestComponent::Initialize() { + // SetSectionName(this->ClassName()); + + if (!fRandom) { + delete fRandom; + fRandom = nullptr; + } + + fRandom = new TRandom3(fSeed); + fSeed = fRandom->TRandom::GetSeed(); +} + /////////////////////////////////////////// /// \brief It returns the position of the fVariable element for the variable /// name given by argument. @@ -489,6 +507,9 @@ void TRestComponent::PrintMetadata() { RESTMetadata << "Component nature : " << fNature << RESTendl; RESTMetadata << " " << RESTendl; + RESTMetadata << "Random seed : " << fSeed << RESTendl; + RESTMetadata << " " << RESTendl; + if (fVariables.size() != fRanges.size()) RESTWarning << "The number of variables does not match with the number of defined ranges!" << RESTendl; From 3cdcf5f5d895398d62068396c31e5d01e4843d48 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:41:42 +0000 Subject: [PATCH 142/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../sensitivity/inc/TRestComponent.h | 3 +- .../sensitivity/inc/TRestExperimentList.h | 20 +++++----- .../sensitivity/inc/TRestSensitivity.h | 8 ++-- .../sensitivity/src/TRestComponent.cxx | 9 +++-- .../sensitivity/src/TRestComponentDataSet.cxx | 3 +- .../sensitivity/src/TRestExperiment.cxx | 2 +- .../sensitivity/src/TRestExperimentList.cxx | 38 ++++++++++--------- .../sensitivity/src/TRestSensitivity.cxx | 15 ++++---- 8 files changed, 50 insertions(+), 48 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 5c220707a..dce197d2d 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -23,10 +23,9 @@ #ifndef REST_TRestComponent #define REST_TRestComponent -#include - #include #include +#include #include #include diff --git a/source/framework/sensitivity/inc/TRestExperimentList.h b/source/framework/sensitivity/inc/TRestExperimentList.h index acb8e6220..5d1737e40 100644 --- a/source/framework/sensitivity/inc/TRestExperimentList.h +++ b/source/framework/sensitivity/inc/TRestExperimentList.h @@ -48,31 +48,31 @@ class TRestExperimentList : public TRestMetadata { std::vector > fExperimentsTable; //< /// A vector with a list of experiments includes the background components in this model - std::vector fExperiments; //< + std::vector fExperiments; //< /// If not zero this will be the common exposure time in micro-seconds (standard REST units) Double_t fExposureTime = 0; /// If not null this will be the common signal used in each experiment - TRestComponent *fSignal = nullptr; //< + TRestComponent* fSignal = nullptr; //< /// If not null this will be the common signal used in each experiment - TRestComponent *fBackground = nullptr; //< + TRestComponent* fBackground = nullptr; //< protected: - TRestComponent *GetComponent(std::string compName); + TRestComponent* GetComponent(std::string compName); void InitFromConfigFile() override; public: void Initialize() override; - void SetExposure(const Double_t &exposure) { fExposureTime = exposure; } - void SetSignal(TRestComponent *comp) { fSignal = comp; } - void SetBackground(TRestComponent *comp) { fBackground = comp; } + void SetExposure(const Double_t& exposure) { fExposureTime = exposure; } + void SetSignal(TRestComponent* comp) { fSignal = comp; } + void SetBackground(TRestComponent* comp) { fBackground = comp; } - std::vector GetExperiments() { return fExperiments; } - TRestExperiment *GetExperiment(const size_t &n) { + std::vector GetExperiments() { return fExperiments; } + TRestExperiment* GetExperiment(const size_t& n) { if (n >= GetNumberOfExperiments()) return nullptr; else @@ -83,7 +83,7 @@ class TRestExperimentList : public TRestMetadata { void PrintMetadata() override; - TRestExperimentList(const char *cfgFileName, const std::string &name); + TRestExperimentList(const char* cfgFileName, const std::string& name); TRestExperimentList(); ~TRestExperimentList(); diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h index 44b67bbc4..41bbe84bb 100644 --- a/source/framework/sensitivity/inc/TRestSensitivity.h +++ b/source/framework/sensitivity/inc/TRestSensitivity.h @@ -29,7 +29,7 @@ class TRestSensitivity : public TRestMetadata { private: /// A list of experimental conditions included to get a final sensitivity plot - std::vector fExperiments; //< + std::vector fExperiments; //< protected: void InitFromConfigFile() override; @@ -37,8 +37,8 @@ class TRestSensitivity : public TRestMetadata { public: void Initialize() override; - std::vector GetExperiments() { return fExperiments; } - TRestExperiment *GetExperiment(const size_t &n) { + std::vector GetExperiments() { return fExperiments; } + TRestExperiment* GetExperiment(const size_t& n) { if (n >= GetNumberOfExperiments()) return nullptr; else @@ -49,7 +49,7 @@ class TRestSensitivity : public TRestMetadata { void PrintMetadata() override; - TRestSensitivity(const char *cfgFileName, const std::string &name = ""); + TRestSensitivity(const char* cfgFileName, const std::string& name = ""); TRestSensitivity(); ~TRestSensitivity(); diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index db849f8c9..ef9b26189 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -301,7 +301,8 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." << RESTendl; + "TRestComponent::Initialize()." + << RESTendl; return result; } @@ -345,13 +346,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index fbcc1f3e9..01fa3c079 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -350,7 +350,6 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { RESTInfo << "Counting statistics for each node ..." << RESTendl; RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl; for (const auto& p : fParameterizationNodes) { - Double_t pUp = p * (1 + fPrecision / 2); Double_t pDown = p * (1 - fPrecision / 2); std::string filter = @@ -363,7 +362,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count(); } - if ((Int_t) * nEv < fSamples) { + if ((Int_t)*nEv < fSamples) { RESTWarning << "The number of requested samples (" << fSamples << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl; } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 50049a5cd..917efdb5d 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -190,7 +190,7 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "Random seed : " << fSeed << RESTendl; RESTMetadata << " " << RESTendl; if (fExposureTime > 0) - RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " s" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("s") << " s" << RESTendl; if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 25b5dfa7e..6fc681339 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -65,7 +65,7 @@ TRestExperimentList::~TRestExperimentList() {} /// \param cfgFileName A const char* giving the path to an RML file. /// \param name The name of the specific metadata. /// -TRestExperimentList::TRestExperimentList(const char *cfgFileName, const std::string &name) +TRestExperimentList::TRestExperimentList(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -85,8 +85,8 @@ void TRestExperimentList::InitFromConfigFile() { if (!fExperimentsFile.empty() && fExperiments.empty()) { TRestTools::ReadASCIITable(fExperimentsFile, fExperimentsTable); - for (auto &row : fExperimentsTable) - for (auto &el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el); + for (auto& row : fExperimentsTable) + for (auto& el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el); if (fExperimentsTable.empty()) { RESTError << "TRestExperimentList::InitFromConfigFile. The experiments table is empty!" @@ -97,7 +97,7 @@ void TRestExperimentList::InitFromConfigFile() { Int_t nTableColumns = fExperimentsTable[0].size(); int cont = 0; - TRestComponent *comp = (TRestComponent *)this->InstantiateChildMetadata(cont, "Component"); + TRestComponent* comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); while (comp != nullptr) { if (ToLower(comp->GetNature()) == "background") fBackground = comp; @@ -107,7 +107,7 @@ void TRestExperimentList::InitFromConfigFile() { RESTWarning << "TRestExperimentList::InitFromConfigFile. Unknown component!" << RESTendl; cont++; - comp = (TRestComponent *)this->InstantiateChildMetadata(cont, "Component"); + comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component"); } Int_t nExpectedColumns = 3; @@ -117,7 +117,8 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" << RESTendl; + "(Exposure/Background/Signal)" + << RESTendl; return; } @@ -134,7 +135,8 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" << RESTendl; + "the number of table columns" + << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; @@ -144,11 +146,11 @@ void TRestExperimentList::InitFromConfigFile() { fComponentFiles = TRestTools::GetFilesMatchingPattern(fComponentPattern); Bool_t generateMockData = false; - for (const auto &experimentRow : fExperimentsTable) { - TRestExperiment *experiment = new TRestExperiment(); + for (const auto& experimentRow : fExperimentsTable) { + TRestExperiment* experiment = new TRestExperiment(); std::string rowStr = ""; - for (const auto &el : experimentRow) { + for (const auto& el : experimentRow) { rowStr += el + " "; } @@ -181,7 +183,7 @@ void TRestExperimentList::InitFromConfigFile() { } if (!fSignal) { - TRestComponent *sgnl = (TRestComponent *)GetComponent(experimentRow[column])->Clone(); + TRestComponent* sgnl = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); experiment->SetSignal(sgnl); column++; } else { @@ -189,7 +191,7 @@ void TRestExperimentList::InitFromConfigFile() { } if (!fBackground) { - TRestComponent *bck = (TRestComponent *)GetComponent(experimentRow[column])->Clone(); + TRestComponent* bck = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); experiment->SetBackground(bck); } else { experiment->SetBackground(fBackground); @@ -205,16 +207,16 @@ void TRestExperimentList::InitFromConfigFile() { } } -TRestComponent *TRestExperimentList::GetComponent(std::string compName) { - TRestComponent *component = nullptr; - for (const auto &c : fComponentFiles) { - TFile *f = TFile::Open(c.c_str(), "READ"); - TObject *obj = f->Get((TString)compName); +TRestComponent* TRestExperimentList::GetComponent(std::string compName) { + TRestComponent* component = nullptr; + for (const auto& c : fComponentFiles) { + TFile* f = TFile::Open(c.c_str(), "READ"); + TObject* obj = f->Get((TString)compName); if (!obj) continue; if (obj->InheritsFrom("TRestComponent")) { - return (TRestComponent *)obj; + return (TRestComponent*)obj; } else { RESTError << "An object named : " << compName << " exists inside the file, but it does not inherit from TRestComponent" << RESTendl; diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index 714cb8ad2..7ea97ae6d 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -38,8 +38,8 @@ /// ///
    /// -#include #include +#include ClassImp(TRestSensitivity); @@ -67,7 +67,7 @@ TRestSensitivity::~TRestSensitivity() {} /// \param name The name of the specific metadata. It will be used to find the /// corresponding TRestAxionMagneticField section inside the RML. /// -TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) +TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -85,19 +85,18 @@ void TRestSensitivity::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); int cont = 0; - TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); while (metadata != nullptr) { - cont++; if (metadata->InheritsFrom("TRestExperimentList")) { - TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; - std::vector exList = experimentsList->GetExperiments(); + TRestExperimentList* experimentsList = (TRestExperimentList*)metadata; + std::vector exList = experimentsList->GetExperiments(); fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); } else if (metadata->InheritsFrom("TRestExperiment")) { - fExperiments.push_back((TRestExperiment *)metadata); + fExperiments.push_back((TRestExperiment*)metadata); } - metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); } Initialize(); From cad9af247f676253c3153c6c0c66cd3204542c61 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 29 Jan 2024 11:25:55 +0100 Subject: [PATCH 143/174] TRestDataSet::Export fixed a bug and added posibility to exclude columns --- source/framework/core/inc/TRestDataSet.h | 5 +- source/framework/core/src/TRestDataSet.cxx | 58 +++++++++++++++++----- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 3ecc99bd6..84b072190 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -136,8 +136,7 @@ class TRestDataSet : public TRestMetadata { TTree* GetTree() const { if (fTree == nullptr && fExternal) { RESTInfo << "The tree is not accessible. Only GetDataFrame can be used in an externally " - "generated dataset" - << RESTendl; + "generated dataset" << RESTendl; RESTInfo << "You may write a tree using GetDataFrame()->Snapshot(\"MyTree\", \"output.root\");" << RESTendl; return fTree; @@ -195,7 +194,7 @@ class TRestDataSet : public TRestMetadata { Bool_t Merge(const TRestDataSet& dS); void Import(const std::string& fileName); void Import(std::vector fileNames); - void Export(const std::string& filename); + void Export(const std::string& filename, std::vector excludeColumns = {}); ROOT::RDF::RNode MakeCut(const TRestCut* cut); ROOT::RDF::RNode DefineColumn(const std::string& columnName, const std::string& formula); diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index 1774251ae..c07ec988d 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -353,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -384,8 +384,7 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" - << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; return fFileSelection; } @@ -438,7 +437,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -495,7 +494,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto& [param, condition] : paramCut) { + for (const auto & [ param, condition ] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -542,7 +541,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const& [name, properties] : fQuantity) + for (auto const & [ name, properties ] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -608,7 +607,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const& [name, properties] : fQuantity) { + for (auto const & [ name, properties ] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -620,7 +619,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto& [cName, cExpression] : fColumnNameExpressions) { + for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -781,10 +780,42 @@ void TRestDataSet::InitFromConfigFile() { /// Snapshot of the current dataset, i.e. in standard TTree format, together with a copy /// of the TRestDataSet instance that contains the conditions used to generate the dataset. /// -void TRestDataSet::Export(const std::string& filename) { +void TRestDataSet::Export(const std::string& filename, std::vector excludeColumns) { RESTInfo << "Exporting dataset" << RESTendl; + + std::vector columns = fDataSet.GetColumnNames(); + if (!excludeColumns.empty()) { + columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != + excludeColumns.end(); + }), + columns.end()); + + RESTInfo << "Re-Generating snapshot." << RESTendl; + std::string user = getenv("USER"); + std::string fOutName = "/tmp/rest_output_" + user + ".root"; + fDataSet.Snapshot("AnalysisTree", fOutName, columns); + + RESTInfo << "Re-importing analysis tree." << RESTendl; + fDataSet = ROOT::RDataFrame("AnalysisTree", fOutName); + + TFile* f = TFile::Open(fOutName.c_str()); + fTree = (TChain*)f->Get("AnalysisTree"); + } + if (TRestTools::GetFileNameExtension(filename) == "txt" || TRestTools::GetFileNameExtension(filename) == "csv") { + + if (excludeColumns.empty()) { + RESTInfo << "Re-Generating snapshot." << RESTendl; + std::string user = getenv("USER"); + std::string fOutName = "/tmp/rest_output_" + user + ".root"; + fDataSet.Snapshot("AnalysisTree", fOutName); + + TFile* f = TFile::Open(fOutName.c_str()); + fTree = (TChain*)f->Get("AnalysisTree"); + } + std::vector dataTypes; for (int n = 0; n < fTree->GetListOfBranches()->GetEntries(); n++) { std::string bName = fTree->GetListOfBranches()->At(n)->GetName(); @@ -793,8 +824,7 @@ void TRestDataSet::Export(const std::string& filename) { if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" - << RESTendl; + "exporting to ASCII table" << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -829,7 +859,7 @@ void TRestDataSet::Export(const std::string& filename) { } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto& [name, properties] : fQuantity) { + for (auto & [ name, properties ] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } @@ -874,7 +904,9 @@ void TRestDataSet::Export(const std::string& filename) { fDataSet.Snapshot("AnalysisTree", filename); TFile* f = TFile::Open(filename.c_str(), "UPDATE"); - this->Write(); + std::string name = this->GetName(); + if (name.empty()) name = "mock"; + this->Write(name.c_str()); f->Close(); } else { RESTWarning << "TRestDataSet::Export. Extension " << TRestTools::GetFileNameExtension(filename) From ec9d1bd35e3e08045a7bc38ffee932add50ae635 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 29 Jan 2024 11:40:56 +0100 Subject: [PATCH 144/174] TRestComponent::GetMonteCarloDataFrame. Now Rndm auxiliar column is removed from returned df --- .../framework/sensitivity/src/TRestComponent.cxx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index db849f8c9..d90eeb2e5 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -328,6 +328,21 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { df = df.Define(varName, FillRand, {"Rndm"}); } + /* Excluding Rndm from df */ + std::vector columns = df.GetColumnNames(); + std::vector excludeColumns = {"Rndm"}; + columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != + excludeColumns.end(); + }), + columns.end()); + + std::string user = getenv("USER"); + std::string fOutName = "/tmp/rest_output_" + user + ".root"; + df.Snapshot("AnalysisTree", fOutName, columns); + + df = ROOT::RDataFrame("AnalysisTree", fOutName); + return df; } From d97347f082950260c515d9a0d79e43055badb3a3 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 29 Jan 2024 11:41:45 +0100 Subject: [PATCH 145/174] TRestExperiment::GetExperimentalDataSet/DataFrame methods added --- source/framework/sensitivity/inc/TRestExperiment.h | 3 ++- source/framework/sensitivity/src/TRestExperiment.cxx | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index aebdc674e..d3cde91c7 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -83,7 +83,8 @@ class TRestExperiment : public TRestMetadata { Double_t GetExposureInSeconds() { return fExposureTime * units("s"); } TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } - ROOT::RDF::RNode GetExperimentalData() { return fExperimentalData.GetDataFrame(); } + TRestDataSet& GetExperimentalDataSet() { return fExperimentalData; } + ROOT::RDF::RNode GetExperimentalDataFrame() { return fExperimentalData.GetDataFrame(); } void Initialize() override; diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 50049a5cd..79d857d96 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -189,8 +189,11 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "Random seed : " << fSeed << RESTendl; RESTMetadata << " " << RESTendl; - if (fExposureTime > 0) - RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " s" << RESTendl; + if (fExposureTime > 0) { + RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " seconds" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime* units("hr") << " hours" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime* units("day") << " days" << RESTendl; + } if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; From 30a53e47b910426bcf31da951b2f1db97046b392 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:44:25 +0000 Subject: [PATCH 146/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- source/framework/core/inc/TRestDataSet.h | 3 +- source/framework/core/src/TRestDataSet.cxx | 30 ++++++++++--------- .../sensitivity/src/TRestComponent.cxx | 18 ++++++----- .../sensitivity/src/TRestComponentDataSet.cxx | 2 +- .../sensitivity/src/TRestExperiment.cxx | 6 ++-- .../sensitivity/src/TRestExperimentList.cxx | 6 ++-- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h index 84b072190..483351510 100644 --- a/source/framework/core/inc/TRestDataSet.h +++ b/source/framework/core/inc/TRestDataSet.h @@ -136,7 +136,8 @@ class TRestDataSet : public TRestMetadata { TTree* GetTree() const { if (fTree == nullptr && fExternal) { RESTInfo << "The tree is not accessible. Only GetDataFrame can be used in an externally " - "generated dataset" << RESTendl; + "generated dataset" + << RESTendl; RESTInfo << "You may write a tree using GetDataFrame()->Snapshot(\"MyTree\", \"output.root\");" << RESTendl; return fTree; diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx index c07ec988d..56011722a 100644 --- a/source/framework/core/src/TRestDataSet.cxx +++ b/source/framework/core/src/TRestDataSet.cxx @@ -353,7 +353,7 @@ void TRestDataSet::GenerateDataSet() { fDataSet = MakeCut(fCut); // Adding new user columns added to the dataset - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTInfo << "Adding column to dataset: " << cName << RESTendl; finalList.emplace_back(cName); fDataSet = DefineColumn(cName, cExpression); @@ -384,7 +384,8 @@ std::vector TRestDataSet::FileSelection() { if (!time_stamp_end || !time_stamp_start) { RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " - "REST_StringHelper::StringToTimeStamp documentation for valid formats" << RESTendl; + "REST_StringHelper::StringToTimeStamp documentation for valid formats" + << RESTendl; return fFileSelection; } @@ -437,7 +438,7 @@ std::vector TRestDataSet::FileSelection() { if (!accept) continue; Double_t acc = 0; - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { std::string value = run.ReplaceMetadataMembers(properties.metadata); const Double_t val = REST_StringHelper::StringToDouble(value); @@ -494,7 +495,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) { auto paramCut = cut->GetParamCut(); auto obsList = df.GetColumnNames(); - for (const auto & [ param, condition ] : paramCut) { + for (const auto& [param, condition] : paramCut) { if (std::find(obsList.begin(), obsList.end(), param) != obsList.end()) { std::string pCut = param + condition; RESTDebug << "Applying cut " << pCut << RESTendl; @@ -541,7 +542,7 @@ ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const auto df = fDataSet; std::string evalFormula = formula; - for (auto const & [ name, properties ] : fQuantity) + for (auto const& [name, properties] : fQuantity) evalFormula = REST_StringHelper::Replace(evalFormula, name, properties.value); df = df.Define(columnName, evalFormula); @@ -607,7 +608,7 @@ void TRestDataSet::PrintMetadata() { RESTMetadata << " Relevant quantities: " << RESTendl; RESTMetadata << " -------------------- " << RESTendl; - for (auto const & [ name, properties ] : fQuantity) { + for (auto const& [name, properties] : fQuantity) { RESTMetadata << " - Name : " << name << ". Value : " << properties.value << ". Strategy: " << properties.strategy << RESTendl; RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; @@ -619,7 +620,7 @@ void TRestDataSet::PrintMetadata() { if (!fColumnNameExpressions.empty()) { RESTMetadata << " New columns added to generated dataframe: " << RESTendl; RESTMetadata << " ---------------------------------------- " << RESTendl; - for (const auto & [ cName, cExpression ] : fColumnNameExpressions) { + for (const auto& [cName, cExpression] : fColumnNameExpressions) { RESTMetadata << " - Name : " << cName << RESTendl; RESTMetadata << " - Expression: " << cExpression << RESTendl; RESTMetadata << " " << RESTendl; @@ -785,10 +786,11 @@ void TRestDataSet::Export(const std::string& filename, std::vector std::vector columns = fDataSet.GetColumnNames(); if (!excludeColumns.empty()) { - columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { - return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != - excludeColumns.end(); - }), + columns.erase(std::remove_if(columns.begin(), columns.end(), + [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), + elem) != excludeColumns.end(); + }), columns.end()); RESTInfo << "Re-Generating snapshot." << RESTendl; @@ -805,7 +807,6 @@ void TRestDataSet::Export(const std::string& filename, std::vector if (TRestTools::GetFileNameExtension(filename) == "txt" || TRestTools::GetFileNameExtension(filename) == "csv") { - if (excludeColumns.empty()) { RESTInfo << "Re-Generating snapshot." << RESTendl; std::string user = getenv("USER"); @@ -824,7 +825,8 @@ void TRestDataSet::Export(const std::string& filename, std::vector if (type != "Double_t" && type != "Int_t") { RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; RESTError << "Only Int_t and Double_t types are allowed for " - "exporting to ASCII table" << RESTendl; + "exporting to ASCII table" + << RESTendl; RESTError << "File will not be generated" << RESTendl; return; } @@ -859,7 +861,7 @@ void TRestDataSet::Export(const std::string& filename, std::vector } fprintf(f, "###\n"); fprintf(f, "### Relevant quantities: \n"); - for (auto & [ name, properties ] : fQuantity) { + for (auto& [name, properties] : fQuantity) { fprintf(f, "### - %s : %s - %s\n", name.c_str(), properties.value.c_str(), properties.description.c_str()); } diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index d90eeb2e5..980c5324b 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -301,7 +301,8 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." << RESTendl; + "TRestComponent::Initialize()." + << RESTendl; return result; } @@ -331,10 +332,11 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { /* Excluding Rndm from df */ std::vector columns = df.GetColumnNames(); std::vector excludeColumns = {"Rndm"}; - columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { - return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != - excludeColumns.end(); - }), + columns.erase(std::remove_if(columns.begin(), columns.end(), + [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != + excludeColumns.end(); + }), columns.end()); std::string user = getenv("USER"); @@ -360,13 +362,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 39e7e7b41..01fa3c079 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -362,7 +362,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count(); } - if ((Int_t) * nEv < fSamples) { + if ((Int_t)*nEv < fSamples) { RESTWarning << "The number of requested samples (" << fSamples << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl; } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 79d857d96..93ff85912 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -190,9 +190,9 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "Random seed : " << fSeed << RESTendl; RESTMetadata << " " << RESTendl; if (fExposureTime > 0) { - RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " seconds" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime* units("hr") << " hours" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime* units("day") << " days" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("s") << " seconds" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("hr") << " hours" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("day") << " days" << RESTendl; } if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 19dddeb40..6fc681339 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -117,7 +117,8 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" << RESTendl; + "(Exposure/Background/Signal)" + << RESTendl; return; } @@ -134,7 +135,8 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" << RESTendl; + "the number of table columns" + << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; From 13c1d88eb1b777bb978af149a9daa9c7c39d7155 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 29 Jan 2024 16:34:24 +0100 Subject: [PATCH 147/174] TRestComponentFormula::FillHistograms. Adding a remark --- source/framework/sensitivity/src/TRestComponentFormula.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx index 48166d1d8..06c8ef3d6 100644 --- a/source/framework/sensitivity/src/TRestComponentFormula.cxx +++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx @@ -142,6 +142,10 @@ Double_t TRestComponentFormula::GetFormulaRate(std::vector point) { /// if the component expression depends on the node parameter it might require /// further development. /// +/// TODO: The histogram is filled just by evaluating the formula, but it would +/// be more realistic that we fill the histograms with a number N of entries +/// that mimic a MC generation scheme similar to TRestComponentDataSet. +/// void TRestComponentFormula::FillHistograms() { if (fFormulas.empty()) return; From 2f24402b58fd949ac3e668ebcc2b364764638872 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 29 Jan 2024 16:35:38 +0100 Subject: [PATCH 148/174] TRestComponent::RegenerateHistograms. It refills the histograms with a different seed, i.e. a different dataset sample --- source/framework/sensitivity/inc/TRestComponent.h | 1 + .../framework/sensitivity/src/TRestComponent.cxx | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index dce197d2d..76f06f1bb 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -99,6 +99,7 @@ class TRestComponent : public TRestMetadata { public: void Initialize() override; + void RegenerateHistograms(UInt_t seed = 0); std::string GetNature() const { return fNature; } TRestResponse* GetResponse() const { return fResponse; } diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index d90eeb2e5..8cf6f9aad 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -95,6 +95,21 @@ void TRestComponent::Initialize() { fSeed = fRandom->TRandom::GetSeed(); } +///////////////////////////////////////////// +/// \brief It will produce a histogram with the distribution defined using the +/// variables and the weights for each of the parameter nodes. +/// +/// fPrecision is used to define the active node +/// +void TRestComponent::RegenerateHistograms(UInt_t seed) { + fNodeDensity.clear(); + + fSeed = seed; + TRestComponent::Initialize(); + + FillHistograms(); +} + /////////////////////////////////////////// /// \brief It returns the position of the fVariable element for the variable /// name given by argument. From 18588aa47c6ff6303803add79520a464df69c87e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 09:33:02 +0100 Subject: [PATCH 149/174] TRestComponent. Adding getters --- source/framework/sensitivity/inc/TRestComponent.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 76f06f1bb..651891fe1 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -108,6 +108,10 @@ class TRestComponent : public TRestMetadata { Int_t GetActiveNode() { return fActiveNode; } Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } + std::vector GetVariables() const { return fVariables; } + std::vector GetRanges() const { return fRanges; } + std::vector GetNbins() const { return fNbins; } + Double_t GetRawRate(std::vector point); Double_t GetTotalRate(); Double_t GetNormalizedRate(std::vector point); From 93db557db6e4c7dfe349936ec57db25f9ea82d2e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 09:33:49 +0100 Subject: [PATCH 150/174] TRestExperiment::PrintExperimentalData added --- source/framework/sensitivity/inc/TRestExperiment.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index d3cde91c7..33d373e71 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -80,12 +80,14 @@ class TRestExperiment : public TRestMetadata { /// variables } - Double_t GetExposureInSeconds() { return fExposureTime * units("s"); } + Double_t GetExposureInSeconds() const { return fExposureTime * units("s"); } TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } TRestDataSet& GetExperimentalDataSet() { return fExperimentalData; } ROOT::RDF::RNode GetExperimentalDataFrame() { return fExperimentalData.GetDataFrame(); } + void PrintExperimentalData() { GetExperimentalDataFrame().Display("")->Print(); } + void Initialize() override; void PrintMetadata() override; From 43a21aa4fd4c56ae0acd1bf54625afc3a8be74ac Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 09:34:47 +0100 Subject: [PATCH 151/174] TRestExperiment. Validating that variable definitions are the same in signal and background components --- .../sensitivity/src/TRestExperiment.cxx | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 79d857d96..1276ee9d6 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -168,14 +168,44 @@ void TRestExperiment::InitFromConfigFile() { GenerateMockDataSet(); } else if (fExposureTime == 0 && !fExperimentalDataSet.empty()) { SetExperimentalDataSetFile(fExperimentalDataSet); - } else { - RESTError << "The exposure time is not zero and the experimental data filename was defined!" - << RESTendl; - RESTError << "The exposure time will be defined by the dataset duration. Set exposure time to zero," - << RESTendl; - RESTError << " or do not define a dataset to generate mock data using the exposure time given" + RESTWarning << "The exposure time is not zero but a experimental data filename was defined!" + << RESTendl; + RESTWarning << "The exposure time will be recovered from the dataset duration. To avoid confusion is" + << RESTendl; + RESTWarning << "better that you set exposure time to zero inside the RML definition," << RESTendl; + RESTError + << " or do not define a dataset if you wish to generate mock data using the exposure time given" + << RESTendl; + } + + /// Checking that signal/background/tracking got the same variable names and ranges + if (fSignal->GetVariables() != fBackground->GetVariables()) { + RESTError << "TRestExperiment : " << GetName() << RESTendl; + RESTError << "Background and signal components got different variable names or variable ordering!" << RESTendl; + RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl; + return; + } + + if (fSignal->GetNbins() != fBackground->GetNbins()) { + RESTError << "TRestExperiment : " << GetName() << RESTendl; + RESTError << "Background and signal components got different binning values!" << RESTendl; + RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl; + return; + } + + cont = 0; + std::vector bckRanges = fBackground->GetRanges(); + std::vector sgnlRanges = fSignal->GetRanges(); + for (const TVector2& sRange : sgnlRanges) { + if (sRange.X() != bckRanges[cont].X() || sRange.Y() != bckRanges[cont].Y()) { + RESTError << "TRestExperiment : " << GetName() << RESTendl; + RESTError << "Background and signal components got different range definitions!" << RESTendl; + RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl; + return; + } + cont++; } Initialize(); From 408e2d62a27cd5edaa586af2de7a82c226727df9 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 17:27:51 +0100 Subject: [PATCH 152/174] TRestMetadata. Now is not pure abstract anymore to avoid problems with TRestDataSet copy constructor --- source/framework/core/inc/TRestMetadata.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/core/inc/TRestMetadata.h b/source/framework/core/inc/TRestMetadata.h index d87a60139..561256f99 100644 --- a/source/framework/core/inc/TRestMetadata.h +++ b/source/framework/core/inc/TRestMetadata.h @@ -332,8 +332,8 @@ class TRestMetadata : public TNamed { ~TRestMetadata(); // Making class constructors protected to keep this class abstract - TRestMetadata& operator=(const TRestMetadata&) = delete; - TRestMetadata(const TRestMetadata&) = delete; + TRestMetadata& operator=(const TRestMetadata&); + TRestMetadata(const TRestMetadata&); /// Call CINT to generate streamers for this class ClassDef(TRestMetadata, 9); From 770aa43993efbb60310cbf21a7856ac0d6860d94 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 17:28:39 +0100 Subject: [PATCH 153/174] TRestExperiment::SetExperimentalDataSetFile. Improving method --- .../sensitivity/inc/TRestExperiment.h | 22 +++++--------- .../sensitivity/src/TRestExperiment.cxx | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index 33d373e71..c853908fa 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -50,6 +50,9 @@ class TRestExperiment : public TRestMetadata { /// If enabled it means that the experimental data was MC-generated Bool_t fMockData = false; //< + /// Only if it is true we will be able to calculate the LogLikelihood + Bool_t fDataReady = false; //< + /// Internal process random generator TRandom3* fRandom = nullptr; //! @@ -62,29 +65,20 @@ class TRestExperiment : public TRestMetadata { public: void GenerateMockDataSet(); - Bool_t IsMockData() { return fMockData; } + Bool_t IsMockData() const { return fMockData; } + Bool_t IsDataReady() const { return fDataReady; } void SetExposureInSeconds(const Double_t exposure) { fExposureTime = exposure / units("s"); } void SetSignal(TRestComponent* comp) { fSignal = comp; } void SetBackground(TRestComponent* comp) { fBackground = comp; } - void SetExperimentalDataSetFile(const std::string& filename) { - fExperimentalDataSet = SearchFile(filename); - fExperimentalData.Import(fExperimentalDataSet); - fExposureTime = fExperimentalData.GetTotalTimeInSeconds() * units("s"); - - fMockData = false; - - /// TODO : We need to check here that the experimental data got the same variables as the components. - /// Or we need to create a way to connect the column names to be used in the dataset with the - /// variables - } + void SetExperimentalDataSetFile(const std::string& filename); Double_t GetExposureInSeconds() const { return fExposureTime * units("s"); } TRestComponent* GetBackground() const { return fBackground; } TRestComponent* GetSignal() const { return fSignal; } - TRestDataSet& GetExperimentalDataSet() { return fExperimentalData; } - ROOT::RDF::RNode GetExperimentalDataFrame() { return fExperimentalData.GetDataFrame(); } + TRestDataSet GetExperimentalDataSet() const { return fExperimentalData; } + ROOT::RDF::RNode GetExperimentalDataFrame() const { return fExperimentalData.GetDataFrame(); } void PrintExperimentalData() { GetExperimentalDataFrame().Display("")->Print(); } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 1276ee9d6..b4394cde8 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -109,6 +109,35 @@ void TRestExperiment::GenerateMockDataSet() { fExperimentalData.SetTotalTimeInSeconds(fExposureTime * units("s")); fMockData = true; + fDataReady = true; +} + +void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { + fExperimentalDataSet = SearchFile(filename); + fExperimentalData.Import(fExperimentalDataSet); + + /// fExposureTime is in standard REST units : us + fExposureTime = fExperimentalData.GetTotalTimeInSeconds() / units("s"); + + fMockData = false; + fDataReady = true; + + if (!fSignal || !fBackground) { + RESTWarning << "TRestExperiment::SetExperimentalDataSetFile. Signal and background components must " + "be available before atempt to load experimental data" << RESTendl; + fDataReady = false; + return; + } + + std::vector columns = fExperimentalData.GetDataFrame().GetColumnNames(); + for (const auto& v : fSignal->GetVariables()) { + if (std::find(columns.begin(), columns.end(), v) == columns.end()) { + RESTError << "TRestExperiment::SetExperimentalDataSetFile a component variable was not found in " + "the dataset!" << RESTendl; + fDataReady = false; + return; + } + } } ///////////////////////////////////////////// From bc0f6f1ba6b974e7d1a52bc3d3f9d35d14aa3147 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 17:33:36 +0100 Subject: [PATCH 154/174] TRestSensitivity. Adding methods UnbinnedLogLikelihood, ApproachByFactor, and GetCoupling --- .../sensitivity/inc/TRestSensitivity.h | 5 + .../sensitivity/src/TRestSensitivity.cxx | 116 +++++++++++++++++- 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h index 41bbe84bb..8455fbc70 100644 --- a/source/framework/sensitivity/inc/TRestSensitivity.h +++ b/source/framework/sensitivity/inc/TRestSensitivity.h @@ -34,9 +34,14 @@ class TRestSensitivity : public TRestMetadata { protected: void InitFromConfigFile() override; + Double_t UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t g4 = 0); + Double_t ApproachByFactor(Double_t g4, Double_t chi0, Double_t target, Double_t factor); + public: void Initialize() override; + Double_t GetCoupling(Double_t sigma = 2, Double_t precision = 0.01); + std::vector GetExperiments() { return fExperiments; } TRestExperiment* GetExperiment(const size_t& n) { if (n >= GetNumberOfExperiments()) diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index 7ea97ae6d..0361a16b9 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -67,7 +67,7 @@ TRestSensitivity::~TRestSensitivity() {} /// \param name The name of the specific metadata. It will be used to find the /// corresponding TRestAxionMagneticField section inside the RML. /// -TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name) +TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -78,6 +78,110 @@ TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& n /// void TRestSensitivity::Initialize() { SetSectionName(this->ClassName()); } +/////////////////////////////////////////////// +/// \brief It will return a value of the coupling, g4, such that (chi-chi0) gets +/// closer to the target value given by argument. The factor will be used to +/// increase or decrease the coupling, and evaluate the likelihood. +/// +Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t target, Double_t factor) { + if (factor == 1) { + return 0; + } + + /// Coarse movement to get to Chi2 above target + Double_t Chi2 = 0; + do { + Chi2 = 0; + for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + + g4 = factor * g4; + } while (Chi2 - chi0 < target); + g4 = g4 / factor; + + /// Coarse movement to get to Chi2 below target (/2) + do { + Chi2 = 0; + for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + + g4 = g4 / factor; + } while (Chi2 - chi0 > target); + + return g4 * factor; +} + +/////////////////////////////////////////////// +/// \brief It will return the coupling value for which Chi=sigma +/// +Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { + Double_t Chi2_0 = 0; + for (const auto &exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); + + Double_t target = sigma * sigma; + + Double_t g4 = 0.5; + + g4 = ApproachByFactor(g4, Chi2_0, target, 2); + g4 = ApproachByFactor(g4, Chi2_0, target, 1.2); + g4 = ApproachByFactor(g4, Chi2_0, target, 1.02); + g4 = ApproachByFactor(g4, Chi2_0, target, 1.0002); + + return g4; +} + +/////////////////////////////////////////////// +/// \brief It returns the Log(L) for the experiment and coupling given by argument. +/// +Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experiment, Double_t g4) { + Double_t lhood = 0; + if (!experiment->IsDataReady()) { + RESTError << "TRestSensitivity::UnbinnedLogLikelihood. Experiment " << experiment->GetName() + << " is not ready!" << RESTendl; + return lhood; + } + + Double_t signal = g4 * experiment->GetSignal()->GetTotalRate() * experiment->GetExposureInSeconds(); + + lhood = -signal; + + /* + std::cout << "Total rate: " << experiment->GetSignal()->GetTotalRate() << std::endl; + std::cout << "Exposure: " << experiment->GetExposureInSeconds() << std::endl; + std::cout << "Signal : " << signal << std::endl; + */ + + if (ROOT::IsImplicitMTEnabled()) ROOT::DisableImplicitMT(); + + std::vector> trackingData; + for (const auto &var : experiment->GetSignal()->GetVariables()) { + auto values = experiment->GetExperimentalDataFrame().Take(var); + std::vector vDbl = std::move(*values); + trackingData.push_back(vDbl); + } + + for (size_t n = 0; n < trackingData[0].size(); n++) { + std::vector point; + for (size_t m = 0; m < trackingData.size(); m++) point.push_back(trackingData[m][n]); + + Double_t bckRate = experiment->GetBackground()->GetRate(point); + Double_t sgnlRate = experiment->GetSignal()->GetRate(point); + + Double_t expectedRate = bckRate + g4 * sgnlRate; + lhood += TMath::Log(expectedRate); + } + + /* + if( lhood == 0 ) + GetChar(); + + if (isinf(lhood) || isnan(lhood)) { + cout << "IS INF" << endl; + GetChar(); + } + */ + + return lhood; +} + ///////////////////////////////////////////// /// \brief It customizes the retrieval of XML data values of this class /// @@ -85,18 +189,18 @@ void TRestSensitivity::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); int cont = 0; - TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); + TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); while (metadata != nullptr) { cont++; if (metadata->InheritsFrom("TRestExperimentList")) { - TRestExperimentList* experimentsList = (TRestExperimentList*)metadata; - std::vector exList = experimentsList->GetExperiments(); + TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; + std::vector exList = experimentsList->GetExperiments(); fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); } else if (metadata->InheritsFrom("TRestExperiment")) { - fExperiments.push_back((TRestExperiment*)metadata); + fExperiments.push_back((TRestExperiment *)metadata); } - metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); + metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); } Initialize(); From e28f08d8d9f2bfdd732af4832074dd9d0be68bd8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:35:38 +0000 Subject: [PATCH 155/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../sensitivity/src/TRestExperiment.cxx | 6 +++-- .../sensitivity/src/TRestSensitivity.cxx | 22 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index beff55ef8..0caba207e 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -124,7 +124,8 @@ void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { if (!fSignal || !fBackground) { RESTWarning << "TRestExperiment::SetExperimentalDataSetFile. Signal and background components must " - "be available before atempt to load experimental data" << RESTendl; + "be available before atempt to load experimental data" + << RESTendl; fDataReady = false; return; } @@ -133,7 +134,8 @@ void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { for (const auto& v : fSignal->GetVariables()) { if (std::find(columns.begin(), columns.end(), v) == columns.end()) { RESTError << "TRestExperiment::SetExperimentalDataSetFile a component variable was not found in " - "the dataset!" << RESTendl; + "the dataset!" + << RESTendl; fDataReady = false; return; } diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index 0361a16b9..b2595298d 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -67,7 +67,7 @@ TRestSensitivity::~TRestSensitivity() {} /// \param name The name of the specific metadata. It will be used to find the /// corresponding TRestAxionMagneticField section inside the RML. /// -TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) +TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -92,7 +92,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t Double_t Chi2 = 0; do { Chi2 = 0; - for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = factor * g4; } while (Chi2 - chi0 < target); @@ -101,7 +101,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Coarse movement to get to Chi2 below target (/2) do { Chi2 = 0; - for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = g4 / factor; } while (Chi2 - chi0 > target); @@ -114,7 +114,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { Double_t Chi2_0 = 0; - for (const auto &exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); + for (const auto& exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); Double_t target = sigma * sigma; @@ -131,7 +131,7 @@ Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { /////////////////////////////////////////////// /// \brief It returns the Log(L) for the experiment and coupling given by argument. /// -Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experiment, Double_t g4) { +Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t g4) { Double_t lhood = 0; if (!experiment->IsDataReady()) { RESTError << "TRestSensitivity::UnbinnedLogLikelihood. Experiment " << experiment->GetName() @@ -152,7 +152,7 @@ Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experime if (ROOT::IsImplicitMTEnabled()) ROOT::DisableImplicitMT(); std::vector> trackingData; - for (const auto &var : experiment->GetSignal()->GetVariables()) { + for (const auto& var : experiment->GetSignal()->GetVariables()) { auto values = experiment->GetExperimentalDataFrame().Take(var); std::vector vDbl = std::move(*values); trackingData.push_back(vDbl); @@ -189,18 +189,18 @@ void TRestSensitivity::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); int cont = 0; - TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); while (metadata != nullptr) { cont++; if (metadata->InheritsFrom("TRestExperimentList")) { - TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; - std::vector exList = experimentsList->GetExperiments(); + TRestExperimentList* experimentsList = (TRestExperimentList*)metadata; + std::vector exList = experimentsList->GetExperiments(); fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); } else if (metadata->InheritsFrom("TRestExperiment")) { - fExperiments.push_back((TRestExperiment *)metadata); + fExperiments.push_back((TRestExperiment*)metadata); } - metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); } Initialize(); From 44b869f3d21845605a616fd7b0370912c2a9d71e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Tue, 30 Jan 2024 22:03:27 +0100 Subject: [PATCH 156/174] TRestComponentDataSet::RegenerateActiveNodeDensity method added --- .../sensitivity/inc/TRestComponent.h | 2 + .../sensitivity/inc/TRestComponentDataSet.h | 2 + .../sensitivity/src/TRestComponentDataSet.cxx | 67 ++++++++++++++++++- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 651891fe1..369607f57 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -101,6 +101,8 @@ class TRestComponent : public TRestMetadata { void Initialize() override; void RegenerateHistograms(UInt_t seed = 0); + virtual void RegenerateActiveNodeDensity() {} + std::string GetNature() const { return fNature; } TRestResponse* GetResponse() const { return fResponse; } Float_t GetPrecision() { return fPrecision; } diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index 698758b51..c63a9ea7f 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -63,6 +63,8 @@ class TRestComponentDataSet : public TRestComponent { Bool_t ValidDataSet(); protected: + void RegenerateActiveNodeDensity() override; + std::vector ExtractParameterizationNodes(); std::vector ExtractNodeStatistics(); void FillHistograms() override; diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 01fa3c079..d82b1383d 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -293,6 +293,71 @@ void TRestComponentDataSet::FillHistograms() { } } +///////////////////////////////////////////// +/// \brief It will regenerate the density histogram for the active node. It is +/// practical in the case when the number of samples fSamples is lower than the total +/// number of samples. The density distribution will be then re-generated with a +/// different random sample. +/// +void TRestComponentDataSet::RegenerateActiveNodeDensity() { + + if (fActiveNode >= 0 && fNodeDensity[fActiveNode]) { + delete fNodeDensity[fActiveNode]; + } else { + RESTError << "TRestComponentDataSet::RegenerateActiveNode. Active node undefined!" << RESTendl; + return; + } + + Int_t from = 0; + Int_t to = 0; + if (fSamples > 0 && fTotalSamples[fActiveNode] - fSamples > 0) { + from = fRandom->Integer(fTotalSamples[fActiveNode] - fSamples); + to = from + fSamples; + } + + Double_t node = GetActiveNodeValue(); + RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) << RESTendl; + + ROOT::RDF::RNode df = ROOT::RDataFrame(0); + Double_t pUp = node * (1 + fPrecision / 2); + Double_t pDown = node * (1 - fPrecision / 2); + std::string filter = + fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown); + df = fDataSet.GetDataFrame().Filter(filter).Range(from, to); + + Int_t* bins = new Int_t[fNbins.size()]; + Double_t* xmin = new Double_t[fNbins.size()]; + Double_t* xmax = new Double_t[fNbins.size()]; + + for (size_t n = 0; n < fNbins.size(); n++) { + bins[n] = fNbins[n]; + xmin[n] = fRanges[n].X(); + xmax[n] = fRanges[n].Y(); + } + + TString hName = fParameter + "_" + DoubleToString(node); + if (fParameterizationNodes.empty()) hName = "full"; + + std::vector varsAndWeight = fVariables; + + if (!fWeights.empty()) { + std::string weightsStr = ""; + for (size_t n = 0; n < fWeights.size(); n++) { + if (n > 0) weightsStr += "*"; + + weightsStr += fWeights[n]; + } + df = df.Define("componentWeight", weightsStr); + varsAndWeight.push_back("componentWeight"); + } + + auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight); + THnD* hNd = new THnD(*hn); + hNd->Scale(1. / fNSimPerNode[fActiveNode]); + + fNodeDensity[fActiveNode] = hNd; +} + ///////////////////////////////////////////// /// \brief It returns a vector with all the different values found on /// the dataset column for the user given parameterization variable. @@ -362,7 +427,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count(); } - if ((Int_t)*nEv < fSamples) { + if ((Int_t) * nEv < fSamples) { RESTWarning << "The number of requested samples (" << fSamples << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl; } From c62eb23ef3474c86c1ba826c8fc88a961868ed37 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 31 Jan 2024 11:37:27 +0100 Subject: [PATCH 157/174] TRestSystemOfUnits. Additional time alias added --- source/framework/core/inc/TRestSystemOfUnits.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index 58ce4e94a..a592888f6 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -42,7 +42,17 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; +enum Physical_Unit { + Energy, + Time, + Length, + Mass, + Voltage, + MagneticField, + Pressure, + Angle, + NOT_A_UNIT = -1 +}; class TRestSystemOfUnits { private: @@ -115,10 +125,15 @@ AddUnit(ms, REST_Units::Time, 1.e-3); AddUnit(s, REST_Units::Time, 1.e-6); AddUnit(Hz, REST_Units::Time, 1.e6); AddUnit(minu, REST_Units::Time, 1.e-6 / 60.); +AddUnit(minutes, REST_Units::Time, 1.e-6 / 60.); AddUnit(hr, REST_Units::Time, 1e-6 / 3600.); +AddUnit(hours, REST_Units::Time, 1e-6 / 3600.); AddUnit(day, REST_Units::Time, 1e-6 / 3600. / 24.); +AddUnit(days, REST_Units::Time, 1e-6 / 3600. / 24.); AddUnit(mon, REST_Units::Time, 1e-6 / 3600. / 24. / 30); +AddUnit(months, REST_Units::Time, 1e-6 / 3600. / 24. / 30); AddUnit(yr, REST_Units::Time, 1e-6 / 3600. / 24. / 365.25); +AddUnit(years, REST_Units::Time, 1e-6 / 3600. / 24. / 365.25); // length unit multiplier AddUnit(nm, REST_Units::Length, 1e6); From c79da7de0e6a15f3fdbd9bd9d8cce44dbddb93e7 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 31 Jan 2024 11:38:02 +0100 Subject: [PATCH 158/174] TRestMetadata adding copy constructor implementation --- source/framework/core/src/TRestMetadata.cxx | 44 ++++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index ef6bf09de..3c2ff979f 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -512,6 +512,28 @@ TRestMetadata::TRestMetadata() : RESTendl(this) { #endif } +TRestMetadata::TRestMetadata(const TRestMetadata&) : RESTendl(this) { + fStore = true; + fElementGlobal = nullptr; + fElement = nullptr; + fVerboseLevel = gVerbose; + fVariables.clear(); + fConstants.clear(); + fHostmgr = nullptr; + + fConfigFileName = "null"; + configBuffer = ""; + RESTMetadata.setlength(100); + +#ifdef WIN32 + fOfficialRelease = true; + fCleanState = true; +#else + if (TRestTools::Execute("rest-config --release") == "Yes") fOfficialRelease = true; + if (TRestTools::Execute("rest-config --clean") == "Yes") fCleanState = true; +#endif +} + /////////////////////////////////////////////// /// \brief constructor /// @@ -1146,10 +1168,9 @@ void TRestMetadata::ReplaceForLoopVars(TiXmlElement* e, map forL } } - e->SetAttribute(name, ReplaceMathematicalExpressions( - outputBuffer, 0, - "Please, check parameter name: " + parName + " (ReplaceForLoopVars)") - .c_str()); + e->SetAttribute(name, ReplaceMathematicalExpressions(outputBuffer, 0, + "Please, check parameter name: " + parName + + " (ReplaceForLoopVars)").c_str()); } attr = attr->Next(); @@ -1306,8 +1327,7 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { TiXmlElement* ele = GetElementFromFile(filename); if (ele == nullptr) { RESTError << "TRestMetadata::ExpandIncludeFile. No xml elements contained in the include " - "file \"" - << filename << "\"" << RESTendl; + "file \"" << filename << "\"" << RESTendl; exit(1); } while (ele != nullptr) { @@ -1389,8 +1409,7 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { if (remoteele == nullptr) { RESTWarning << "Cannot find the needed xml section in " - "include file!" - << RESTendl; + "include file!" << RESTendl; RESTWarning << "type: \"" << type << "\" , name: \"" << name << "\" . Skipping" << RESTendl; RESTWarning << RESTendl; @@ -2273,8 +2292,7 @@ TString TRestMetadata::GetLibraryVersion() { return fLibraryVersion; } void TRestMetadata::ReSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" - << RESTendl; + "for a class!" << RESTendl; else { fVersion = REST_RELEASE; } @@ -2286,8 +2304,7 @@ void TRestMetadata::ReSetVersion() { void TRestMetadata::UnSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" - << RESTendl; + "for a class!" << RESTendl; else { fVersion = -1; fCommit = -1; @@ -2554,8 +2571,7 @@ void TRestMetadata::ReadOneParameter(string name, string value) { } else { RESTWarning << this->ClassName() << " find unit definition in parameter: " << name << ", but the corresponding data member doesn't support it. Data " - "member type: " - << datamember.type << RESTendl; + "member type: " << datamember.type << RESTendl; datamember.ParseString(value); } } else { From b7dcec4ecc0d55f0275454dcbd895ecdf70fa6b5 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Wed, 31 Jan 2024 11:38:48 +0100 Subject: [PATCH 159/174] TRestComponent. variable XML-element renamed to cVariable --- .../sensitivity/src/TRestComponent.cxx | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index dea0b3ff2..894186b9a 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -316,8 +316,7 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." - << RESTendl; + "TRestComponent::Initialize()." << RESTendl; return result; } @@ -347,11 +346,10 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { /* Excluding Rndm from df */ std::vector columns = df.GetColumnNames(); std::vector excludeColumns = {"Rndm"}; - columns.erase(std::remove_if(columns.begin(), columns.end(), - [&excludeColumns](std::string elem) { - return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != - excludeColumns.end(); - }), + columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != + excludeColumns.end(); + }), columns.end()); std::string user = getenv("USER"); @@ -377,15 +375,13 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" - << RESTendl; + "be 1 or 2!" << RESTendl; return fCanvas; } @@ -595,7 +591,7 @@ void TRestComponent::PrintNodes() { void TRestComponent::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); - auto ele = GetElement("variable"); + auto ele = GetElement("cVariable"); while (ele != nullptr) { std::string name = GetParameter("name", ele, ""); TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1)); From 23d0577965740635ff849cb356dcfbaee4dc1f4a Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Sun, 11 Feb 2024 17:33:53 +0100 Subject: [PATCH 160/174] CMakeLists. Adding GSL libraries --- CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d4e7928f..22c01f563 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,19 @@ else () set(REST_MPFR OFF) endif (${REST_MPFR} MATCHES "ON") +#### GSL #### +# Find GSL +find_package(GSL REQUIRED) + +# Include GSL directories +set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) + +# Link GSL libraries +set(external_libs ${external_libs};${GSL_LIBRARIES} ) + +message ( STATUS "Found GSL libraries : ${GSL_LIBRARIES}") +message ( STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") + # CURL ##### find_library(CURL_LIB curl) if (NOT ${CURL_LIB} STREQUAL "CURL_LIB-NOTFOUND") From 662b7e13049d4ba4d7f4c8156c48fc9f540c95fb Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 19:54:55 +0100 Subject: [PATCH 161/174] REST_OpenInputFile.C updating output when openning dataset --- macros/REST_OpenInputFile.C | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/macros/REST_OpenInputFile.C b/macros/REST_OpenInputFile.C index 78f0560e9..7812d8f5f 100644 --- a/macros/REST_OpenInputFile.C +++ b/macros/REST_OpenInputFile.C @@ -45,7 +45,8 @@ void REST_OpenInputFile(const std::string& fileName) { printf("\n%s\n", "The dataset is ready. You may now access the dataset using:"); printf("\n%s\n", " - dSet->PrintMetadata()"); printf("%s\n", " - dSet->GetDataFrame().GetColumnNames()"); - printf("%s\n", " - dSet->GetTree()->GetEntries()"); + printf("%s\n\n", " - dSet->GetTree()->GetEntries()"); + printf("%s\n", " - dSet->GetDataFrame().Display(\"\")->Print()"); printf("%s\n\n", " - dSet->GetDataFrame().Display({\"colName1,colName2\"})->Print()"); if (dSet) delete dSet; dSet = new TRestDataSet(); From 498cb0e60c6b1d212ee48eb240440c52693a3a9b Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 20:44:03 +0100 Subject: [PATCH 162/174] TRestExperiment. Adding protection against seg.fault --- .../sensitivity/src/TRestExperiment.cxx | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 0caba207e..541f51fc6 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -112,7 +112,7 @@ void TRestExperiment::GenerateMockDataSet() { fDataReady = true; } -void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { +void TRestExperiment::SetExperimentalDataSet(const std::string& filename) { fExperimentalDataSet = SearchFile(filename); fExperimentalData.Import(fExperimentalDataSet); @@ -123,9 +123,8 @@ void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { fDataReady = true; if (!fSignal || !fBackground) { - RESTWarning << "TRestExperiment::SetExperimentalDataSetFile. Signal and background components must " - "be available before atempt to load experimental data" - << RESTendl; + RESTWarning << "TRestExperiment::SetExperimentalDataSet. Signal and background components must " + "be available before atempt to load experimental data" << RESTendl; fDataReady = false; return; } @@ -134,8 +133,7 @@ void TRestExperiment::SetExperimentalDataSetFile(const std::string& filename) { for (const auto& v : fSignal->GetVariables()) { if (std::find(columns.begin(), columns.end(), v) == columns.end()) { RESTError << "TRestExperiment::SetExperimentalDataSetFile a component variable was not found in " - "the dataset!" - << RESTendl; + "the dataset!" << RESTendl; fDataReady = false; return; } @@ -198,7 +196,7 @@ void TRestExperiment::InitFromConfigFile() { if (fExposureTime > 0 && fExperimentalDataSet.empty()) { GenerateMockDataSet(); } else if (fExposureTime == 0 && !fExperimentalDataSet.empty()) { - SetExperimentalDataSetFile(fExperimentalDataSet); + SetExperimentalDataSet(fExperimentalDataSet); } else { RESTWarning << "The exposure time is not zero but a experimental data filename was defined!" << RESTendl; @@ -210,6 +208,18 @@ void TRestExperiment::InitFromConfigFile() { << RESTendl; } + if (!fSignal) { + RESTError << "TRestExperiment : " << GetName() << RESTendl; + RESTError << "There was a problem initiazing the signal component!" << RESTendl; + return; + } + + if (!fBackground) { + RESTError << "TRestExperiment : " << GetName() << RESTendl; + RESTError << "There was a problem initiazing the background component!" << RESTendl; + return; + } + /// Checking that signal/background/tracking got the same variable names and ranges if (fSignal->GetVariables() != fBackground->GetVariables()) { RESTError << "TRestExperiment : " << GetName() << RESTendl; @@ -251,9 +261,9 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "Random seed : " << fSeed << RESTendl; RESTMetadata << " " << RESTendl; if (fExposureTime > 0) { - RESTMetadata << " - Exposure time : " << fExposureTime * units("s") << " seconds" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime * units("hr") << " hours" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime * units("day") << " days" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " seconds" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime* units("hr") << " hours" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime* units("day") << " days" << RESTendl; } if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; From 9c8392807d228d870658f63d1094b218db7d2fa8 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:29:47 +0100 Subject: [PATCH 163/174] TRestExperimentList. Adding protections against seg.fault --- .../sensitivity/inc/TRestExperiment.h | 2 +- .../sensitivity/src/TRestExperimentList.cxx | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h index c853908fa..7e25014f2 100644 --- a/source/framework/sensitivity/inc/TRestExperiment.h +++ b/source/framework/sensitivity/inc/TRestExperiment.h @@ -72,7 +72,7 @@ class TRestExperiment : public TRestMetadata { void SetSignal(TRestComponent* comp) { fSignal = comp; } void SetBackground(TRestComponent* comp) { fBackground = comp; } - void SetExperimentalDataSetFile(const std::string& filename); + void SetExperimentalDataSet(const std::string& filename); Double_t GetExposureInSeconds() const { return fExposureTime * units("s"); } TRestComponent* GetBackground() const { return fBackground; } diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index 6fc681339..baa9336a3 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -117,8 +117,7 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" - << RESTendl; + "(Exposure/Background/Signal)" << RESTendl; return; } @@ -135,8 +134,7 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" - << RESTendl; + "the number of table columns" << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; @@ -154,7 +152,7 @@ void TRestExperimentList::InitFromConfigFile() { rowStr += el + " "; } - RESTInfo << "Loading experiment: " << rowStr << RESTendl; + RESTInfo << "TRestExperimentList. Loading experiment: " << rowStr << RESTendl; int column = 0; if (fExposureTime == 0) { @@ -166,7 +164,7 @@ void TRestExperimentList::InitFromConfigFile() { } else if (TRestTools::isRootFile(experimentRow[column])) { // We load the file with the dataset into the experimental data std::string fname = SearchFile(experimentRow[column]); - experiment->SetExperimentalDataSetFile(fname); + experiment->SetExperimentalDataSet(fname); RESTWarning << "Loading experimental data havent been tested yet!" << RESTendl; RESTWarning << "It might require further development. Remove these lines once it works smooth!" @@ -183,22 +181,32 @@ void TRestExperimentList::InitFromConfigFile() { } if (!fSignal) { - TRestComponent* sgnl = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); - experiment->SetSignal(sgnl); + if (GetComponent(experimentRow[column])) { + TRestComponent* sgnl = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); + experiment->SetSignal(sgnl); + } else { + RESTError << "TRestExperimentList. Signal component : " << experimentRow[column] + << " not found!" << RESTendl; + } column++; } else { experiment->SetSignal(fSignal); } if (!fBackground) { - TRestComponent* bck = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); - experiment->SetBackground(bck); + if (GetComponent(experimentRow[column])) { + TRestComponent* bck = (TRestComponent*)GetComponent(experimentRow[column])->Clone(); + experiment->SetBackground(bck); + } else { + RESTError << "TRestExperimentList. Background component : " << experimentRow[column] + << " not found!" << RESTendl; + } } else { experiment->SetBackground(fBackground); } if (generateMockData) { - RESTInfo << "Generating mock dataset" << RESTendl; + RESTInfo << "TRestExperimentList. Generating mock dataset" << RESTendl; experiment->GenerateMockDataSet(); } From 30c9b3486cc6fa8b04eb57853094188e142a95f5 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:35:07 +0100 Subject: [PATCH 164/174] TRestSensitivity::SignalStatisticalTest method added to evaluate signal impact on coupling error --- .../sensitivity/src/TRestSensitivity.cxx | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index b2595298d..0d8de1828 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -67,7 +67,7 @@ TRestSensitivity::~TRestSensitivity() {} /// \param name The name of the specific metadata. It will be used to find the /// corresponding TRestAxionMagneticField section inside the RML. /// -TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name) +TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -92,7 +92,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t Double_t Chi2 = 0; do { Chi2 = 0; - for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = factor * g4; } while (Chi2 - chi0 < target); @@ -101,7 +101,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Coarse movement to get to Chi2 below target (/2) do { Chi2 = 0; - for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = g4 / factor; } while (Chi2 - chi0 > target); @@ -114,7 +114,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { Double_t Chi2_0 = 0; - for (const auto& exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); + for (const auto &exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); Double_t target = sigma * sigma; @@ -131,7 +131,7 @@ Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { /////////////////////////////////////////////// /// \brief It returns the Log(L) for the experiment and coupling given by argument. /// -Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t g4) { +Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experiment, Double_t g4) { Double_t lhood = 0; if (!experiment->IsDataReady()) { RESTError << "TRestSensitivity::UnbinnedLogLikelihood. Experiment " << experiment->GetName() @@ -143,16 +143,10 @@ Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experime lhood = -signal; - /* - std::cout << "Total rate: " << experiment->GetSignal()->GetTotalRate() << std::endl; - std::cout << "Exposure: " << experiment->GetExposureInSeconds() << std::endl; - std::cout << "Signal : " << signal << std::endl; - */ - if (ROOT::IsImplicitMTEnabled()) ROOT::DisableImplicitMT(); std::vector> trackingData; - for (const auto& var : experiment->GetSignal()->GetVariables()) { + for (const auto &var : experiment->GetSignal()->GetVariables()) { auto values = experiment->GetExperimentalDataFrame().Take(var); std::vector vDbl = std::move(*values); trackingData.push_back(vDbl); @@ -169,17 +163,31 @@ Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experime lhood += TMath::Log(expectedRate); } - /* - if( lhood == 0 ) - GetChar(); + return lhood; +} + +/////////////////////////////////////////////// +/// \brief +/// +TH1D *TRestSensitivity::SignalStatisticalTest(Int_t N) { + + std::vector couplings; + for (int n = 0; n < N; n++) { + for (const auto &exp : fExperiments) exp->GetSignal()->RegenerateActiveNodeDensity(); - if (isinf(lhood) || isnan(lhood)) { - cout << "IS INF" << endl; - GetChar(); + Double_t coupling = TMath::Sqrt(TMath::Sqrt(GetCoupling())); + couplings.push_back(coupling); } - */ - return lhood; + // Directly assign the minimum and maximum values + double min_value = *std::min_element(couplings.begin(), couplings.end()); + double max_value = *std::max_element(couplings.begin(), couplings.end()); + + if (fSignalTest) delete fSignalTest; + fSignalTest = new TH1D("SignalTest", "A signal test", 100, 0.9 * min_value, 1.1 * max_value); + for (const auto &coup : couplings) fSignalTest->Fill(coup); + + return fSignalTest; } ///////////////////////////////////////////// @@ -189,18 +197,18 @@ void TRestSensitivity::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); int cont = 0; - TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); + TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); while (metadata != nullptr) { cont++; if (metadata->InheritsFrom("TRestExperimentList")) { - TRestExperimentList* experimentsList = (TRestExperimentList*)metadata; - std::vector exList = experimentsList->GetExperiments(); + TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; + std::vector exList = experimentsList->GetExperiments(); fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); } else if (metadata->InheritsFrom("TRestExperiment")) { - fExperiments.push_back((TRestExperiment*)metadata); + fExperiments.push_back((TRestExperiment *)metadata); } - metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); + metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); } Initialize(); From 27e34488a40af8f8fa2d217301bfa63dc52ce42a Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:35:36 +0100 Subject: [PATCH 165/174] TRestSensitivity::SignalStatisticalTest adding prototyping --- source/framework/sensitivity/inc/TRestSensitivity.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h index 8455fbc70..56c9d0bd6 100644 --- a/source/framework/sensitivity/inc/TRestSensitivity.h +++ b/source/framework/sensitivity/inc/TRestSensitivity.h @@ -31,6 +31,8 @@ class TRestSensitivity : public TRestMetadata { /// A list of experimental conditions included to get a final sensitivity plot std::vector fExperiments; //< + TH1D* fSignalTest = nullptr; + protected: void InitFromConfigFile() override; @@ -42,6 +44,8 @@ class TRestSensitivity : public TRestMetadata { Double_t GetCoupling(Double_t sigma = 2, Double_t precision = 0.01); + TH1D* SignalStatisticalTest(Int_t N); + std::vector GetExperiments() { return fExperiments; } TRestExperiment* GetExperiment(const size_t& n) { if (n >= GetNumberOfExperiments()) From 7a7e2d01b5cd4a213f1a140f722664237987368e Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:36:42 +0100 Subject: [PATCH 166/174] TRestComponent::fSamples allows to restrict the statistics use to build the component --- source/framework/sensitivity/inc/TRestComponent.h | 8 +++++++- source/framework/sensitivity/src/TRestComponent.cxx | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h index 369607f57..1dfd614dc 100644 --- a/source/framework/sensitivity/inc/TRestComponent.h +++ b/source/framework/sensitivity/inc/TRestComponent.h @@ -61,6 +61,9 @@ class TRestComponent : public TRestMetadata { /// The generated N-dimensional variable space density for a given node std::vector fNodeDensity; //< + /// It introduces a fixed number of samples (if 0 it will take all available samples) + Int_t fSamples = 0; //< + /// Enables or disables the interpolation at TRestComponentDataSet::GetRawRate Bool_t fInterpolation = true; //< @@ -107,6 +110,7 @@ class TRestComponent : public TRestMetadata { TRestResponse* GetResponse() const { return fResponse; } Float_t GetPrecision() { return fPrecision; } size_t GetDimensions() { return fVariables.size(); } + Int_t GetSamples() { return fSamples; } Int_t GetActiveNode() { return fActiveNode; } Double_t GetActiveNodeValue() { return fParameterizationNodes[fActiveNode]; } @@ -129,6 +133,8 @@ class TRestComponent : public TRestMetadata { return fActiveNode; } + void SetSamples(Int_t samples) { fSamples = samples; } + Bool_t Interpolation() { return fInterpolation; } void EnableInterpolation() { fInterpolation = true; } void DisableInterpolation() { fInterpolation = false; } @@ -163,6 +169,6 @@ class TRestComponent : public TRestMetadata { TRestComponent(); ~TRestComponent(); - ClassDefOverride(TRestComponent, 4); + ClassDefOverride(TRestComponent, 5); }; #endif diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index 894186b9a..f530c57bf 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -538,6 +538,7 @@ void TRestComponent::PrintMetadata() { RESTMetadata << " " << RESTendl; RESTMetadata << "Random seed : " << fSeed << RESTendl; + if (fSamples) RESTMetadata << "Samples : " << fSamples << RESTendl; RESTMetadata << " " << RESTendl; if (fVariables.size() != fRanges.size()) @@ -610,6 +611,9 @@ void TRestComponent::InitFromConfigFile() { ele = GetNextElement(ele); } + if (fNbins.size() == 0) + RESTError << "TRestComponent::InitFromConfigFile. No cVariables where found!" << RESTendl; + if (fResponse) { delete fResponse; fResponse = nullptr; From 56a3c41172fde6f9f600f7fbe7fac8fdc09ae093 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:37:37 +0100 Subject: [PATCH 167/174] TRestPhysics removing dummy comment --- source/framework/tools/inc/TRestPhysics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/tools/inc/TRestPhysics.h b/source/framework/tools/inc/TRestPhysics.h index dca1f0637..e48018eda 100644 --- a/source/framework/tools/inc/TRestPhysics.h +++ b/source/framework/tools/inc/TRestPhysics.h @@ -49,7 +49,7 @@ constexpr double kBoltzman = 1.380E-23; constexpr double hPlanck = 1.054E-34; /// A meter in eV -constexpr double PhMeterIneV = 5067731.236453719; // 8.0655447654281218E5;// 506.773123645372; +constexpr double PhMeterIneV = 5067731.236453719; /// A second in eV (using natural units) constexpr double secondIneV = 1519225802531030.2; /// Electron charge in natural units From 0da0e1343085315aadf031e7928421eb05a6bc06 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:38:46 +0100 Subject: [PATCH 168/174] TRestComponentDataSet. Revisiting fSamples implementation --- .../framework/sensitivity/inc/TRestComponentDataSet.h | 3 --- .../sensitivity/src/TRestComponentDataSet.cxx | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h index c63a9ea7f..9a2154f05 100644 --- a/source/framework/sensitivity/inc/TRestComponentDataSet.h +++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h @@ -44,9 +44,6 @@ class TRestComponentDataSet : public TRestComponent { /// The filename of the dataset used std::vector fDataSetFileNames; //< - /// It introduces a fixed number of samples (if 0 it will take all available samples) - Int_t fSamples = 0; - /// TODO we need to define multiple datasets and weigth. The weight will be used /// to create a model, such as weighting different background contaminations or /// different signal coupling contributions. diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index d82b1383d..3684ca33a 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -139,8 +139,6 @@ void TRestComponentDataSet::Initialize() { void TRestComponentDataSet::PrintMetadata() { TRestComponent::PrintMetadata(); - if (fSamples) RESTMetadata << "Data subset samples : " << fSamples << RESTendl; - if (!fDataSetFileNames.empty()) { RESTMetadata << " " << RESTendl; RESTMetadata << " == Dataset filenames ==" << RESTendl; @@ -217,6 +215,13 @@ void TRestComponentDataSet::InitFromConfigFile() { void TRestComponentDataSet::FillHistograms() { if (!fNodeDensity.empty()) return; + if (fNbins.size() == 0) { + RESTError + << "TRestComponentDataSet::FillHistograms. Trying to fill histograms but no variables found!" + << RESTendl; + return; + } + fNSimPerNode = ExtractNodeStatistics(); if (!IsDataSetLoaded()) { @@ -238,6 +243,7 @@ void TRestComponentDataSet::FillHistograms() { if (fSamples > 0 && fTotalSamples[nIndex] - fSamples > 0) { from = fRandom->Integer(fTotalSamples[nIndex] - fSamples); to = from + fSamples; + fNSimPerNode[nIndex] = fSamples; } ROOT::RDF::RNode df = ROOT::RDataFrame(0); @@ -313,6 +319,7 @@ void TRestComponentDataSet::RegenerateActiveNodeDensity() { if (fSamples > 0 && fTotalSamples[fActiveNode] - fSamples > 0) { from = fRandom->Integer(fTotalSamples[fActiveNode] - fSamples); to = from + fSamples; + fNSimPerNode[fActiveNode] = fSamples; } Double_t node = GetActiveNodeValue(); From 37820d2537c8e98c9d0feedf607764fd49135c02 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:39:30 +0100 Subject: [PATCH 169/174] Updating axion submodule to v2.2 --- source/libraries/axion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libraries/axion b/source/libraries/axion index 21375385f..3800d90f4 160000 --- a/source/libraries/axion +++ b/source/libraries/axion @@ -1 +1 @@ -Subproject commit 21375385fc4cad92bda60ba29f641b9afad9339e +Subproject commit 3800d90f41622ec0c465f7b4d4c5f384e4d9433a From 68f296fa978f98159dc71a83843765b1c00fa633 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 21:44:28 +0100 Subject: [PATCH 170/174] Updating legacy library to v1.0 --- source/libraries/legacy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libraries/legacy b/source/libraries/legacy index 831424307..caa4e2540 160000 --- a/source/libraries/legacy +++ b/source/libraries/legacy @@ -1 +1 @@ -Subproject commit 83142430760cb35c2b3c597b24c1d884a01c0fff +Subproject commit caa4e2540f87b33071b1ae8e21f277aa69e60861 From d8ec95bebcabe7b7618385a4c07d485bb40ddbc4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:45:20 +0000 Subject: [PATCH 171/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CMakeLists.txt | 9 +++--- .../framework/core/inc/TRestSystemOfUnits.h | 12 +------- source/framework/core/src/TRestMetadata.cxx | 22 +++++++++----- .../sensitivity/src/TRestComponent.cxx | 18 +++++++----- .../sensitivity/src/TRestComponentDataSet.cxx | 3 +- .../sensitivity/src/TRestExperiment.cxx | 12 ++++---- .../sensitivity/src/TRestExperimentList.cxx | 6 ++-- .../sensitivity/src/TRestSensitivity.cxx | 29 +++++++++---------- 8 files changed, 56 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22c01f563..3bc7fa182 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,18 +188,17 @@ else () set(REST_MPFR OFF) endif (${REST_MPFR} MATCHES "ON") -#### GSL #### -# Find GSL +# GSL #### Find GSL find_package(GSL REQUIRED) # Include GSL directories set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) # Link GSL libraries -set(external_libs ${external_libs};${GSL_LIBRARIES} ) +set(external_libs ${external_libs};${GSL_LIBRARIES}) -message ( STATUS "Found GSL libraries : ${GSL_LIBRARIES}") -message ( STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") +message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}") +message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") # CURL ##### find_library(CURL_LIB curl) diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h index a592888f6..5d21fe2dd 100644 --- a/source/framework/core/inc/TRestSystemOfUnits.h +++ b/source/framework/core/inc/TRestSystemOfUnits.h @@ -42,17 +42,7 @@ namespace REST_Units { // We use more common physics units instead of SI unit -enum Physical_Unit { - Energy, - Time, - Length, - Mass, - Voltage, - MagneticField, - Pressure, - Angle, - NOT_A_UNIT = -1 -}; +enum Physical_Unit { Energy, Time, Length, Mass, Voltage, MagneticField, Pressure, Angle, NOT_A_UNIT = -1 }; class TRestSystemOfUnits { private: diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx index 3c2ff979f..e5e598b41 100644 --- a/source/framework/core/src/TRestMetadata.cxx +++ b/source/framework/core/src/TRestMetadata.cxx @@ -1168,9 +1168,10 @@ void TRestMetadata::ReplaceForLoopVars(TiXmlElement* e, map forL } } - e->SetAttribute(name, ReplaceMathematicalExpressions(outputBuffer, 0, - "Please, check parameter name: " + parName + - " (ReplaceForLoopVars)").c_str()); + e->SetAttribute(name, ReplaceMathematicalExpressions( + outputBuffer, 0, + "Please, check parameter name: " + parName + " (ReplaceForLoopVars)") + .c_str()); } attr = attr->Next(); @@ -1327,7 +1328,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { TiXmlElement* ele = GetElementFromFile(filename); if (ele == nullptr) { RESTError << "TRestMetadata::ExpandIncludeFile. No xml elements contained in the include " - "file \"" << filename << "\"" << RESTendl; + "file \"" + << filename << "\"" << RESTendl; exit(1); } while (ele != nullptr) { @@ -1409,7 +1411,8 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) { if (remoteele == nullptr) { RESTWarning << "Cannot find the needed xml section in " - "include file!" << RESTendl; + "include file!" + << RESTendl; RESTWarning << "type: \"" << type << "\" , name: \"" << name << "\" . Skipping" << RESTendl; RESTWarning << RESTendl; @@ -2292,7 +2295,8 @@ TString TRestMetadata::GetLibraryVersion() { return fLibraryVersion; } void TRestMetadata::ReSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = REST_RELEASE; } @@ -2304,7 +2308,8 @@ void TRestMetadata::ReSetVersion() { void TRestMetadata::UnSetVersion() { if (!this->InheritsFrom("TRestRun")) RESTError << "version is a static value, you cannot set version " - "for a class!" << RESTendl; + "for a class!" + << RESTendl; else { fVersion = -1; fCommit = -1; @@ -2571,7 +2576,8 @@ void TRestMetadata::ReadOneParameter(string name, string value) { } else { RESTWarning << this->ClassName() << " find unit definition in parameter: " << name << ", but the corresponding data member doesn't support it. Data " - "member type: " << datamember.type << RESTendl; + "member type: " + << datamember.type << RESTendl; datamember.ParseString(value); } } else { diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx index f530c57bf..516bf8104 100644 --- a/source/framework/sensitivity/src/TRestComponent.cxx +++ b/source/framework/sensitivity/src/TRestComponent.cxx @@ -316,7 +316,8 @@ ROOT::RVecD TRestComponent::GetRandom() { if (!GetDensity()) { for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0); RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use " - "TRestComponent::Initialize()." << RESTendl; + "TRestComponent::Initialize()." + << RESTendl; return result; } @@ -346,10 +347,11 @@ ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) { /* Excluding Rndm from df */ std::vector columns = df.GetColumnNames(); std::vector excludeColumns = {"Rndm"}; - columns.erase(std::remove_if(columns.begin(), columns.end(), [&excludeColumns](std::string elem) { - return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != - excludeColumns.end(); - }), + columns.erase(std::remove_if(columns.begin(), columns.end(), + [&excludeColumns](std::string elem) { + return std::find(excludeColumns.begin(), excludeColumns.end(), elem) != + excludeColumns.end(); + }), columns.end()); std::string user = getenv("USER"); @@ -375,13 +377,15 @@ TCanvas* TRestComponent::DrawComponent(std::vector drawVariables, TString drawOption) { if (drawVariables.size() > 2 || drawVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } if (scanVariables.size() > 2 || scanVariables.size() == 0) { RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must " - "be 1 or 2!" << RESTendl; + "be 1 or 2!" + << RESTendl; return fCanvas; } diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx index 3684ca33a..951018acc 100644 --- a/source/framework/sensitivity/src/TRestComponentDataSet.cxx +++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx @@ -306,7 +306,6 @@ void TRestComponentDataSet::FillHistograms() { /// different random sample. /// void TRestComponentDataSet::RegenerateActiveNodeDensity() { - if (fActiveNode >= 0 && fNodeDensity[fActiveNode]) { delete fNodeDensity[fActiveNode]; } else { @@ -434,7 +433,7 @@ std::vector TRestComponentDataSet::ExtractNodeStatistics() { nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count(); } - if ((Int_t) * nEv < fSamples) { + if ((Int_t)*nEv < fSamples) { RESTWarning << "The number of requested samples (" << fSamples << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl; } diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx index 541f51fc6..674c391c9 100644 --- a/source/framework/sensitivity/src/TRestExperiment.cxx +++ b/source/framework/sensitivity/src/TRestExperiment.cxx @@ -124,7 +124,8 @@ void TRestExperiment::SetExperimentalDataSet(const std::string& filename) { if (!fSignal || !fBackground) { RESTWarning << "TRestExperiment::SetExperimentalDataSet. Signal and background components must " - "be available before atempt to load experimental data" << RESTendl; + "be available before atempt to load experimental data" + << RESTendl; fDataReady = false; return; } @@ -133,7 +134,8 @@ void TRestExperiment::SetExperimentalDataSet(const std::string& filename) { for (const auto& v : fSignal->GetVariables()) { if (std::find(columns.begin(), columns.end(), v) == columns.end()) { RESTError << "TRestExperiment::SetExperimentalDataSetFile a component variable was not found in " - "the dataset!" << RESTendl; + "the dataset!" + << RESTendl; fDataReady = false; return; } @@ -261,9 +263,9 @@ void TRestExperiment::PrintMetadata() { RESTMetadata << "Random seed : " << fSeed << RESTendl; RESTMetadata << " " << RESTendl; if (fExposureTime > 0) { - RESTMetadata << " - Exposure time : " << fExposureTime* units("s") << " seconds" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime* units("hr") << " hours" << RESTendl; - RESTMetadata << " - Exposure time : " << fExposureTime* units("day") << " days" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("s") << " seconds" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("hr") << " hours" << RESTendl; + RESTMetadata << " - Exposure time : " << fExposureTime * units("day") << " days" << RESTendl; } if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl; diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx index baa9336a3..ab773d024 100644 --- a/source/framework/sensitivity/src/TRestExperimentList.cxx +++ b/source/framework/sensitivity/src/TRestExperimentList.cxx @@ -117,7 +117,8 @@ void TRestExperimentList::InitFromConfigFile() { if (nExpectedColumns == 0) { RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! " - "(Exposure/Background/Signal)" << RESTendl; + "(Exposure/Background/Signal)" + << RESTendl; return; } @@ -134,7 +135,8 @@ void TRestExperimentList::InitFromConfigFile() { } RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match " - "the number of table columns" << RESTendl; + "the number of table columns" + << RESTendl; RESTError << "Number of table columns : " << nTableColumns << RESTendl; RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl; RESTError << "Expected columns : " << expectedColumns << RESTendl; diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx index 0d8de1828..12fe749d6 100644 --- a/source/framework/sensitivity/src/TRestSensitivity.cxx +++ b/source/framework/sensitivity/src/TRestSensitivity.cxx @@ -67,7 +67,7 @@ TRestSensitivity::~TRestSensitivity() {} /// \param name The name of the specific metadata. It will be used to find the /// corresponding TRestAxionMagneticField section inside the RML. /// -TRestSensitivity::TRestSensitivity(const char *cfgFileName, const std::string &name) +TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { LoadConfigFromFile(fConfigFileName, name); } @@ -92,7 +92,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t Double_t Chi2 = 0; do { Chi2 = 0; - for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = factor * g4; } while (Chi2 - chi0 < target); @@ -101,7 +101,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Coarse movement to get to Chi2 below target (/2) do { Chi2 = 0; - for (const auto &exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); + for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, g4); g4 = g4 / factor; } while (Chi2 - chi0 > target); @@ -114,7 +114,7 @@ Double_t TRestSensitivity::ApproachByFactor(Double_t g4, Double_t chi0, Double_t /// Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { Double_t Chi2_0 = 0; - for (const auto &exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); + for (const auto& exp : fExperiments) Chi2_0 += -2 * UnbinnedLogLikelihood(exp, 0); Double_t target = sigma * sigma; @@ -131,7 +131,7 @@ Double_t TRestSensitivity::GetCoupling(Double_t sigma, Double_t precision) { /////////////////////////////////////////////// /// \brief It returns the Log(L) for the experiment and coupling given by argument. /// -Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experiment, Double_t g4) { +Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t g4) { Double_t lhood = 0; if (!experiment->IsDataReady()) { RESTError << "TRestSensitivity::UnbinnedLogLikelihood. Experiment " << experiment->GetName() @@ -146,7 +146,7 @@ Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experime if (ROOT::IsImplicitMTEnabled()) ROOT::DisableImplicitMT(); std::vector> trackingData; - for (const auto &var : experiment->GetSignal()->GetVariables()) { + for (const auto& var : experiment->GetSignal()->GetVariables()) { auto values = experiment->GetExperimentalDataFrame().Take(var); std::vector vDbl = std::move(*values); trackingData.push_back(vDbl); @@ -169,11 +169,10 @@ Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment *experime /////////////////////////////////////////////// /// \brief /// -TH1D *TRestSensitivity::SignalStatisticalTest(Int_t N) { - +TH1D* TRestSensitivity::SignalStatisticalTest(Int_t N) { std::vector couplings; for (int n = 0; n < N; n++) { - for (const auto &exp : fExperiments) exp->GetSignal()->RegenerateActiveNodeDensity(); + for (const auto& exp : fExperiments) exp->GetSignal()->RegenerateActiveNodeDensity(); Double_t coupling = TMath::Sqrt(TMath::Sqrt(GetCoupling())); couplings.push_back(coupling); @@ -185,7 +184,7 @@ TH1D *TRestSensitivity::SignalStatisticalTest(Int_t N) { if (fSignalTest) delete fSignalTest; fSignalTest = new TH1D("SignalTest", "A signal test", 100, 0.9 * min_value, 1.1 * max_value); - for (const auto &coup : couplings) fSignalTest->Fill(coup); + for (const auto& coup : couplings) fSignalTest->Fill(coup); return fSignalTest; } @@ -197,18 +196,18 @@ void TRestSensitivity::InitFromConfigFile() { TRestMetadata::InitFromConfigFile(); int cont = 0; - TRestMetadata *metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); while (metadata != nullptr) { cont++; if (metadata->InheritsFrom("TRestExperimentList")) { - TRestExperimentList *experimentsList = (TRestExperimentList *)metadata; - std::vector exList = experimentsList->GetExperiments(); + TRestExperimentList* experimentsList = (TRestExperimentList*)metadata; + std::vector exList = experimentsList->GetExperiments(); fExperiments.insert(fExperiments.end(), exList.begin(), exList.end()); } else if (metadata->InheritsFrom("TRestExperiment")) { - fExperiments.push_back((TRestExperiment *)metadata); + fExperiments.push_back((TRestExperiment*)metadata); } - metadata = (TRestMetadata *)this->InstantiateChildMetadata(cont, "Experiment"); + metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment"); } Initialize(); From a1ff7cdbbcf7d6d2545307d67525fbde0d09a2d9 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 23:02:36 +0100 Subject: [PATCH 172/174] CMakeLists.txt. GSL will only be searched for in case we are linking axionlib --- CMakeLists.txt | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bc7fa182..14b3bfb77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,17 +188,24 @@ else () set(REST_MPFR OFF) endif (${REST_MPFR} MATCHES "ON") -# GSL #### Find GSL -find_package(GSL REQUIRED) +if( ((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) OR (${RESTLIB_AXION} MATCHES "ON") ) + # GSL #### Find GSL + find_package(GSL REQUIRED) -# Include GSL directories -set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) + # Include GSL directories + set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) -# Link GSL libraries -set(external_libs ${external_libs};${GSL_LIBRARIES}) + # Link GSL libraries + set(external_libs ${external_libs};${GSL_LIBRARIES}) -message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}") -message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") + message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}") + message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") + + set(REST_GSL ON) +else () + set(REST_GSL OFF) + message(STATUS "GSL libraries not required") +endif( ((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) OR (${RESTLIB_AXION} MATCHES "ON") ) # CURL ##### find_library(CURL_LIB curl) From af542c03cc03ccf8e0abf3a2309e88db5268dbc1 Mon Sep 17 00:00:00 2001 From: Javier Galan Date: Mon, 12 Feb 2024 23:03:05 +0100 Subject: [PATCH 173/174] Updating TRestVersion.h header to v2.4.2 --- source/framework/core/inc/TRestVersion.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/framework/core/inc/TRestVersion.h b/source/framework/core/inc/TRestVersion.h index 3cfdf91aa..d7d1c8f66 100644 --- a/source/framework/core/inc/TRestVersion.h +++ b/source/framework/core/inc/TRestVersion.h @@ -12,12 +12,12 @@ * #endif * */ -#define REST_RELEASE "2.4.1" -#define REST_RELEASE_DATE "Sat Dec 16" -#define REST_RELEASE_TIME "11:14:21 CET 2023" -#define REST_RELEASE_NAME "Igor G. Irastorza" -#define REST_GIT_COMMIT "6b9d0650" -#define REST_VERSION_CODE 132097 +#define REST_RELEASE "2.4.2" +#define REST_RELEASE_DATE "Mon Feb 12" +#define REST_RELEASE_TIME "22:23:31 CET 2024" +#define REST_RELEASE_NAME "Henry Primakoff" +#define REST_GIT_COMMIT "d8ec95be" +#define REST_VERSION_CODE 132098 #define REST_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) #define REST_SCHEMA_EVOLUTION "ON" #endif From 4ed69d32b9757a56867832a39e3dddd8cd1ab566 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 22:04:46 +0000 Subject: [PATCH 174/174] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CMakeLists.txt | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14b3bfb77..f58e2e85e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,24 +188,26 @@ else () set(REST_MPFR OFF) endif (${REST_MPFR} MATCHES "ON") -if( ((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) OR (${RESTLIB_AXION} MATCHES "ON") ) - # GSL #### Find GSL - find_package(GSL REQUIRED) +if (((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) + OR (${RESTLIB_AXION} MATCHES "ON")) + # GSL #### Find GSL + find_package(GSL REQUIRED) - # Include GSL directories - set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) + # Include GSL directories + set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS}) - # Link GSL libraries - set(external_libs ${external_libs};${GSL_LIBRARIES}) + # Link GSL libraries + set(external_libs ${external_libs};${GSL_LIBRARIES}) - message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}") - message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") + message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}") + message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}") - set(REST_GSL ON) + set(REST_GSL ON) else () - set(REST_GSL OFF) - message(STATUS "GSL libraries not required") -endif( ((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) OR (${RESTLIB_AXION} MATCHES "ON") ) + set(REST_GSL OFF) + message(STATUS "GSL libraries not required") +endif (((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION)) + OR (${RESTLIB_AXION} MATCHES "ON")) # CURL ##### find_library(CURL_LIB curl)