From 9b9eb8c4ec7f6e44ede296c28473f6a576a27ea1 Mon Sep 17 00:00:00 2001
From: Adrien Matta <matta@lpccaen.in2p3.fr>
Date: Mon, 10 May 2021 17:46:31 +0200
Subject: [PATCH] * npanalysis now support splitting tree output         -
 output one tree per detector in a folder with associated .tree           file

---
 NPLib/CMakeLists.txt                          |   6 +-
 NPLib/Core/NPDetectorManager.cxx              |   8 +-
 NPLib/Core/NPDetectorManager.h                |   8 +
 NPLib/Core/NPOptionManager.cxx                |   9 +-
 NPLib/Core/NPOptionManager.h                  |  14 +-
 NPLib/Core/RootInput.cxx                      |   4 +-
 NPLib/Core/RootInput.h                        |   4 +-
 NPLib/Core/RootOutput.cxx                     | 329 ++++++++++--------
 NPLib/Core/RootOutput.h                       |  46 ++-
 NPLib/Detectors/Minos/TMinosPhysics.cxx       |   2 +-
 NPLib/Detectors/Nebula/TNebulaPhysics.cxx     |   2 +-
 .../Detectors/Samurai/TSamuraiFDC0Physics.cxx |   2 +-
 .../Detectors/Samurai/TSamuraiFDC2Physics.cxx |   2 +-
 .../Samurai/TSamuraiHodoscopePhysics.cxx      |   2 +-
 NPLib/Utility/npanalysis.cxx                  |  11 +-
 NPSimulation/Simulation.cc                    |   3 +-
 16 files changed, 265 insertions(+), 187 deletions(-)

diff --git a/NPLib/CMakeLists.txt b/NPLib/CMakeLists.txt
index dae59391e..6f39ad175 100644
--- a/NPLib/CMakeLists.txt
+++ b/NPLib/CMakeLists.txt
@@ -11,11 +11,11 @@ include("${CMAKE_BINARY_DIR}/ressources/CMake/NPTool_CMake_Preamble.cmake")
 
 
 # Major change in the Core/Physics (affecting the detector/analysis/simulation)
-set(NPLIB_VERSION_MAJOR 2)
+set(NPLIB_VERSION_MAJOR 3)
 # Minor change in the Core/Physics (not affecting any other part)
-set(NPLIB_VERSION_MINOR 2)
+set(NPLIB_VERSION_MINOR 0)
 # Change any of the detector in NPA
-set(NPLIB_VERSION_DETA 45)
+set(NPLIB_VERSION_DETA 0)
 
 #activate Multithreading (on by default)
 if(NOT DEFINED NPMULTITHREADING)
diff --git a/NPLib/Core/NPDetectorManager.cxx b/NPLib/Core/NPDetectorManager.cxx
index 490684d23..0b000bc0b 100644
--- a/NPLib/Core/NPDetectorManager.cxx
+++ b/NPLib/Core/NPDetectorManager.cxx
@@ -88,6 +88,7 @@ NPL::DetectorManager::DetectorManager(){
   m_ShieldFrontRadius = 0 ; 
   m_ShieldBackRadius = 0 ;
   m_ShieldMaterial = "" ;
+  m_RootOutput=RootOutput::getInstance();
 }
 
 
@@ -266,7 +267,6 @@ void NPL::DetectorManager::BuildPhysicalEvent(){
   for (it =begin; it != end; ++it) {
     (it->second->*m_ClearEventPhysicsPtr)();
     (it->second->*m_BuildPhysicalPtr)();
-    if(m_FillSpectra){
       (it->second->*m_FillSpectra)();
       if(m_CheckSpectra)
         (it->second->*m_CheckSpectra)();
@@ -498,6 +498,12 @@ void NPL::DetectorManager::CheckSpectraServer(){
     std::cout <<"WARNING: requesting to check spectra server, which is not started" << std::endl; 
 
 }
+
+////////////////////////////////////////////////////////////////////////////////
+void  NPL::DetectorManager::FillOutputTree(){
+  m_RootOutput->Fill();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 bool NPL::DetectorManager::IsCryogenic() const {return  m_CryoTarget;};
 double NPL::DetectorManager::GetTargetThickness() const {return m_TargetThickness;};
diff --git a/NPLib/Core/NPDetectorManager.h b/NPLib/Core/NPDetectorManager.h
index 07e280bcc..f6f94d0c0 100644
--- a/NPLib/Core/NPDetectorManager.h
+++ b/NPLib/Core/NPDetectorManager.h
@@ -23,6 +23,7 @@
 //   NPL
 #include "NPVDetector.h"
 #include "NPSpectraServer.h"
+#include "RootOutput.h"
 // ROOT
 #include "TH1.h"
 
@@ -67,6 +68,13 @@ namespace NPL{
     private :
       NPL::SpectraServer* m_SpectraServer;
 
+
+    private:// root outpu
+      RootOutput* m_RootOutput;
+
+    public:
+      void       FillOutputTree();
+
     private:   
       // The std::map containning all detectors
       // Using a Map one can access to any detector using its name
diff --git a/NPLib/Core/NPOptionManager.cxx b/NPLib/Core/NPOptionManager.cxx
index a38c8569e..450c4ddf7 100644
--- a/NPLib/Core/NPOptionManager.cxx
+++ b/NPLib/Core/NPOptionManager.cxx
@@ -106,6 +106,8 @@ void NPOptionManager::ReadTheInputArgument(int argc, char** argv){
   fLastPhyFile = false;
   fLastResFile = false;
   fLastAnyFile = false;
+  fIsAnalysis  = false;
+  fIsSimulation= false;
   fVerboseLevel               = 1;
   fNumberOfEntryToAnalyse     = -1;
 	fFirstEntryToAnalyse        = 0;
@@ -114,7 +116,6 @@ void NPOptionManager::ReadTheInputArgument(int argc, char** argv){
   fDisableAllBranchOption = false;
   fInputPhysicalTreeOption = false;
   fGenerateHistoOption = false ;
-  fPROOFMode = false;
   fCircularTree = false;
   fOnline = false;
   fG4BatchMode = false;
@@ -158,6 +159,8 @@ void NPOptionManager::ReadTheInputArgument(int argc, char** argv){
     else if (argument == "-T" && argc >= i + 2)                   { std::string file = argv[++i] ; std::string tree = argv[++i]; CreateRunToTreatFile(file,tree);}
 
     else if (argument == "--cal" && argc >= i + 1)                fCalibrationFileName = argv[++i] ;
+    
+    else if (argument == "-S" && argc >= i + 1)                   fIsSplit=true; 
 
     else if (argument == "-C" && argc >= i + 1)                   fCalibrationFileName = argv[++i] ;
 
@@ -185,8 +188,6 @@ void NPOptionManager::ReadTheInputArgument(int argc, char** argv){
 
     else if (argument == "--generate-histo")                      fGenerateHistoOption = true ;
 
-    else if (argument == "--proof")                               fPROOFMode = true ;
-
     else if (argument == "-L")                                    fNumberOfEntryToAnalyse = atoi(argv[++i]) ;
 
     else if (argument == "--random-seed")                         fRandomSeed = atoi(argv[++i]) ;
@@ -444,12 +445,12 @@ void NPOptionManager::DisplayHelp(){
   std::cout << "\t--output -O <arg>\t\tSet arg as the Output File Name (output tree)" << std::endl ;
   std::cout << "\t--tree-name <arg>\t\tSet arg as the Output Tree Name " << std::endl ;
   std::cout << "\t--definition <definition> \tAdd <definition> to the list of definition" << std::endl  ;
+  std::cout << "\t-S \t\t\t\tOne tree output per detector" << std::endl  ;
   std::cout << "\t--verbose -V <arg>\t\tSet the verbose level, 0 for nothing, 1 for normal printout."<<std::endl;
 	std::cout  << "\t\t\t\t\tError and warning are not affected" << std::endl ;
   std::cout << std::endl << "NPAnalysis only:"<<std::endl;
   std::cout << "\t--run -R <arg>\t\t\tSet arg as the run to read file list" << std::endl  ;
   std::cout << "\t-T <name> <file>\t\tTree <name> from root file <file>" << std::endl  ;
-
   std::cout << "\t--cal -C <arg>\t\t\tSet arg as the calibration file list" << std::endl ;
   std::cout << "\t--disable-branch\t\tDisable of branch of Input tree except the one of the detector (faster)" << std::endl  ;
   std::cout << "\t--generate-histo -GH\t\tInstantiate the T*Spectra class of each detector" << std::endl ;
diff --git a/NPLib/Core/NPOptionManager.h b/NPLib/Core/NPOptionManager.h
index a793188b7..8268a4ba9 100644
--- a/NPLib/Core/NPOptionManager.h
+++ b/NPLib/Core/NPOptionManager.h
@@ -103,9 +103,12 @@ class NPOptionManager{
       bool   GetGenerateHistoOption()      {return fGenerateHistoOption;}
       bool   GetCheckHistoOption()         {return fCheckHistoOption;}
       bool   GetOnline()                   {return fOnline;}
-      bool   GetPROOF()                    {return fPROOFMode;}
       bool   GetG4BatchMode()              {return fG4BatchMode;}
-      bool   GetCircularTree()                 {return fCircularTree;}
+      bool   GetCircularTree()             {return fCircularTree;}
+      bool   IsAnalysis()                  {return fIsAnalysis;};
+      bool   IsSimulation()                {return fIsSimulation;}
+      bool   IsSplit()                     {return fIsSplit;}
+
       int    GetVerboseLevel()             {return fVerboseLevel;}
       int    GetNumberOfEntryToAnalyse()   {return fNumberOfEntryToAnalyse;} 
       int    GetFirstEntryToAnalyse()      {return fFirstEntryToAnalyse;} 
@@ -121,7 +124,8 @@ class NPOptionManager{
       void SetDetectorFile(const std::string& name)  {fDetectorFileName = name;CheckDetectorConfiguration();}
       void SetRunToReadFile(const std::string& name) {fRunToReadFileName = name;}
       void SetVerboseLevel(int VerboseLevel)         {fVerboseLevel = VerboseLevel;}
- 
+      void SetIsAnalysis(bool val=true){fIsAnalysis=val;};
+      void SetIsSimulation(bool val=true){fIsSimulation=val;}
 
    public: // user definition
       bool HasDefinition(std::string def) {return(fDefinition.find(def)!=fDefinition.end());}
@@ -143,17 +147,19 @@ class NPOptionManager{
       std::string fCalibrationFileName;
       std::string fOutputFileName;
       std::string fOutputTreeName;
+      bool   fIsSplit; // One tree per detector
       bool   fDisableAllBranchOption;
       bool   fInputPhysicalTreeOption;
       bool   fGenerateHistoOption;
       bool   fCheckHistoOption;
       bool   fOnline; // true if spectra server is started
-      bool   fPROOFMode; // if true, the system run in a pROOF environment
       bool   fLastSimFile;
       bool   fLastPhyFile;
       bool   fLastResFile;
       bool   fLastAnyFile;
       bool   fCircularTree;
+      bool   fIsAnalysis;
+      bool   fIsSimulation;
       int    fVerboseLevel; // 0 for not talk, 1 for talking
       int    fNumberOfEntryToAnalyse; // use to limit the number of analysed in NPA
       int    fFirstEntryToAnalyse; // use to set the first event analysed in NPA (total: fFirstEntryToAnalyse -> fFirstEntryToAnalyse + fNumberOfEntryToAnalyse)
diff --git a/NPLib/Core/RootInput.cxx b/NPLib/Core/RootInput.cxx
index e14e60357..b92a4f766 100644
--- a/NPLib/Core/RootInput.cxx
+++ b/NPLib/Core/RootInput.cxx
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2009-2016   this file is part of the NPTool Project         *
+ * Copyright (C) 2009-2021   this file is part of the NPTool Project         *
  *                                                                           *
  * For the licensing terms see $NPTOOL/Licence/NPTool_Licence                *
  * For the list of contributors see $NPTOOL/Licence/Contributors             *
@@ -9,7 +9,7 @@
  * Original Author: N. de Sereville  contact address: deserevi@ipno.in2p3.fr *
  *                                                                           *
  * Creation Date  : 21/07/09                                                 *
- * Last update    :                                                          *
+ * Last update    : 10/05/21 Add support for Split tree input (A. Matta)     *
  *---------------------------------------------------------------------------*
  * Decription: This class is a singleton class which deals with the ROOT     *
  *             input file and tree both for NPSimulation and NPAnalysis.     *
diff --git a/NPLib/Core/RootInput.h b/NPLib/Core/RootInput.h
index 51c479956..ab64a9a73 100644
--- a/NPLib/Core/RootInput.h
+++ b/NPLib/Core/RootInput.h
@@ -1,7 +1,7 @@
 #ifndef ROOTINPUT_HH
 #define ROOTINPUT_HH
 /*****************************************************************************
- * Copyright (C) 2009-2016    this file is part of the NPTool Project        *
+ * Copyright (C) 2009-2021   this file is part of the NPTool Project         *
  *                                                                           *
  * For the licensing terms see $NPTOOL/Licence/NPTool_Licence                *
  * For the list of contributors see $NPTOOL/Licence/Contributors             *
@@ -11,7 +11,7 @@
  * Original Author: N. de Sereville  contact address: deserevi@ipno.in2p3.fr *
  *                                                                           *
  * Creation Date  : 21/07/09                                                 *
- * Last update    :                                                          *
+ * Last update    : 10/05/21 Add support for Split tree input (A. Matta)     *
  *---------------------------------------------------------------------------*
  * Decription: This class is a singleton class which deals with the ROOT     *
  *             input file and tree both for NPSimulation and NPAnalysis.     *
diff --git a/NPLib/Core/RootOutput.cxx b/NPLib/Core/RootOutput.cxx
index 80400e84f..023353dd8 100644
--- a/NPLib/Core/RootOutput.cxx
+++ b/NPLib/Core/RootOutput.cxx
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2009-2016   this file is part of the NPTool Project         *
+ * Copyright (C) 2009-2021   this file is part of the NPTool Project         *
  *                                                                           *
  * For the licensing terms see $NPTOOL/Licence/NPTool_Licence                *
  * For the list of contributors see $NPTOOL/Licence/Contributors             *
@@ -9,13 +9,14 @@
  * Original Author: N. de Sereville  contact address: deserevi@ipno.in2p3.fr *
  *                                                                           *
  * Creation Date  : 21/07/09                                                 *
- * Last update    : 03/02/11                                                 *
+ * Last update    : 10/05/21                                                 *
  *---------------------------------------------------------------------------*
  * Decription: This class is a singleton class which deals with the ROOT     *
  *             output file and tree both for NPSimulation and NPAnalysis.    *
  *---------------------------------------------------------------------------*
  * Comment:                                                                  *
  *   + 03/02/11: Add support for TAsciiFile objects (N. de Sereville)        *
+ *   + 10/05/21: Add support for split tree output (A. Matta)                *
  *                                                                           *
  *                                                                           *
  *****************************************************************************/
@@ -31,10 +32,10 @@ using namespace std;
 
 RootOutput* RootOutput::instance = 0;
 ////////////////////////////////////////////////////////////////////////////////
-RootOutput* RootOutput::getInstance(std::string fileNameBase, std::string treeNameBase){
+RootOutput* RootOutput::getInstance(std::string fileNameBase, std::string treeNameBase,bool split){
   // A new instance of RootOutput is created if it does not exist:
   if (instance == 0) {
-    instance = new RootOutput(fileNameBase.c_str(), treeNameBase.c_str());
+    instance = new RootOutput(fileNameBase.c_str(), treeNameBase.c_str(),split);
   }
 
   // The instance of RootOutput is returned:
@@ -50,103 +51,159 @@ void RootOutput::Destroy(){
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-RootOutput::RootOutput(std::string fileNameBase, std::string treeNameBase){
-  TDirectory* currentPath= gDirectory;
+RootOutput::RootOutput(std::string fileNameBase, std::string treeNameBase,bool split){
 
+  pSplit = (split || NPOptionManager::getInstance()->IsSplit());
+  cout << endl << "/////////// ROOT Output files ///////////" << endl;
+  cout << "Initializing ouput trees and files ";
+  if(pSplit)
+    cout << "in split mode (one tree per detector)" << endl;
+  
+  pTreeName=treeNameBase;
+  pCurrentDirectory= gDirectory;
   bool analysis=false;
   bool simulation=false;
-  if(fileNameBase.find("Analysis/")!=std::string::npos){
+
+  if(NPOptionManager::getInstance()->IsAnalysis()){
     analysis = true;
-    fileNameBase.erase(0,8);
   }
-  else if(fileNameBase.find("Simulation/")!=std::string::npos){
+  else if(NPOptionManager::getInstance()->IsAnalysis()){
     simulation= true;
-    fileNameBase.erase(0,10);
   }
 
-  // The ROOT file is created
-  if(!NPOptionManager::getInstance()->GetPROOF()){
-    std::string fileName;
-    if(analysis)
-      fileName = NPOptionManager::getInstance()->GetAnalysisOutputPath();
-    else if(simulation)
-      fileName = NPOptionManager::getInstance()->GetSimulationOutputPath();
-    else
-      fileName="./";
-
-    if (fileNameBase.find("root")!=std::string::npos) fileName += fileNameBase;
-    else fileName += fileNameBase + ".root";
-
-    pRootFile = new TFile(fileName.c_str(), "RECREATE");
-
-    if(treeNameBase=="SimulatedTree"){
-      string path = getenv("NPTOOL");
-        path+="/.last_sim_file";
-      ofstream last_sim_file(path.c_str());
-      last_sim_file << "TTreeName" << endl 
-       << "  " << treeNameBase <<endl
-       << "RootFileName" <<endl
-       << "  " << fileName<<endl;
-      last_sim_file.close();
+  // Setup the base name
+  if(analysis)
+    pBaseName = NPOptionManager::getInstance()->GetAnalysisOutputPath();
+  else if(simulation)
+    pBaseName = NPOptionManager::getInstance()->GetSimulationOutputPath();
+  else
+    pBaseName="./";
+
+  pBaseName += "/"+fileNameBase;
+
+  if (fileNameBase.find("root")==std::string::npos) 
+    pBaseName += ".root";
+
+  if(pSplit){
+    // Create a folder for all the trees
+    string stripname = pBaseName;
+    stripname.erase(stripname.find(".root"),5);
+    string path = stripname.substr(0,stripname.rfind("/"));
+    string filebase = stripname.substr(stripname.rfind("/")+1);
+    string command = "mkdir -p "+stripname;
+    int res = system(command.c_str());
+    if(res!=0){
+      std::cout << "Error creating folder " << stripname << std::endl;
+      exit(1);
     }
+    // create the master file 
+    pMasterFile=stripname+"/"+filebase+".tree";
+    ofstream master(pMasterFile.c_str(),std::ofstream::trunc);
+    master.close();
+  }
+  else{
+    CreateTreeAndFile("global");
+  }
+  /////
+  // Create the last file 
+  if(treeNameBase=="SimulatedTree"){
+    string path = getenv("NPTOOL");
+    path+="/.last_sim_file";
+    ofstream last_sim_file(path.c_str());
+    last_sim_file << "Tree "<< pTreeName <<endl
+      << " " << pBaseName<<endl;
+    last_sim_file.close();
+  }
 
-    else if(treeNameBase=="PhysicsTree"){
-      string path = getenv("NPTOOL");
-        path+="/.last_phy_file";
-      ofstream last_phy_file(path.c_str());
-      last_phy_file << "TTreeName" << endl 
-       << "  " << treeNameBase <<endl
-       << "RootFileName" <<endl
-       << "  " << fileName<<endl;
-      last_phy_file.close();
-    }
+  else if(treeNameBase=="PhysicsTree"){
+    string path = getenv("NPTOOL");
+    path+="/.last_phy_file";
+    ofstream last_phy_file(path.c_str());
+    last_phy_file << "Tree "<< pTreeName<<endl
+      << " " << pBaseName <<endl;
+    last_phy_file.close();
+  }
 
+  else if(treeNameBase=="ResultTree"){
+    string path = getenv("NPTOOL");
+    path+="/.last_res_file";
+    ofstream last_res_file(path.c_str());
+    last_res_file << "Tree " << pTreeName << endl 
+      << " " << pBaseName<<endl;
+    last_res_file.close();
+  }
 
-    else if(treeNameBase=="ResultTree"){
-      string path = getenv("NPTOOL");
-      path+="/.last_res_file";
-      ofstream last_res_file(path.c_str());
-      last_res_file << "TTreeName" << endl 
-       << "  " << treeNameBase <<endl
-       << "RootFileName" <<endl
-       << "  " << fileName<<endl;
-      last_res_file.close();
-    }
-  
-    else{
-      string path = getenv("NPTOOL");
-        path+="/.last_any_file";
-      ofstream last_any_file(path.c_str());
-      last_any_file << "TTreeName" << endl 
-       << "  " << treeNameBase <<endl
-       << "RootFileName" <<endl
-       << "  " << fileName << endl;
-      last_any_file.close();
-    }
+  else{
+    string path = getenv("NPTOOL");
+    path+="/.last_any_file";
+    ofstream last_any_file(path.c_str());
+    last_any_file << "Tree " << pTreeName <<endl
+      << " " << pBaseName<< endl;
+    last_any_file.close();
+  }
 
 
+  InitAsciiFiles();
+}
 
+////////////////////////////////////////////////////////////////////////////////
+void RootOutput::CreateTreeAndFile(std::string name){
+  // Create the tree only if does not exist already
+  string file_name=pBaseName;
+  if(name!="global"){
+   string  strip= pBaseName.substr(pBaseName.rfind("/"));
+   strip = strip.substr(0,strip.rfind(".root"));
+   string  insertion= "_"+name;
+   file_name.insert(file_name.rfind(".root"),insertion);
+   file_name.insert(file_name.rfind("/"),strip);
   }
 
-  else{ // the file path must be the current directory
-    // Does not create the Output file at instantiation
-    pRootFile = 0 ;
-  }
+  if(pRootFiles.find(name)==pRootFiles.end()){
+    cout << " - Creating output file " << file_name.c_str() << endl;
+    pRootFiles[name] = new TFile(file_name.c_str(), "RECREATE");
+    pRootTrees[name] = new TTree(pTreeName.c_str(), "Data created / analysed with the nptool package");
+    pRootFiles[name]->SetCompressionLevel(1);
 
-  pRootTree = new TTree(treeNameBase.c_str(), "Data created / analysed with the NPTool package");
-  pRootFile->SetCompressionLevel(1);
-  pRootList = new TList();
+    // Init TAsciiFile objects
+    gDirectory->cd(pCurrentDirectory->GetPath()); 
 
-  // Init TAsciiFile objects
-  InitAsciiFiles();
-  gDirectory->cd(currentPath->GetPath()); 
+    if(NPOptionManager::getInstance()->GetCircularTree()){
+      cout << "Information: Output tree is set to circular mode" << endl;
+      pRootTrees[name]->SetCircular(1000); 
+    }
 
-  if(NPOptionManager::getInstance()->GetCircularTree()){
-    cout << "Information: Output tree is set to circular mode" << endl;
-    pRootTree->SetCircular(1000); 
-  }
+    if(pSplit){// Add the tree to the .tree file
+      ofstream master(pMasterFile.c_str(),std::ofstream::app);
+      file_name = file_name.substr(file_name.rfind("/")+1);
+      master << file_name.c_str() << endl;
+      master.close();
+    }
+  } 
+  //
+  /*
+     for(auto it = m_DetectorMap.begin() ; it!=m_DetectorMap.end() ;++it){
+     string insertion = "_"+it->first;
+     master << filebase << insertion << ".root" << std::endl;
+     string filename=path+"/"+filebase+"/"+filebase+insertion+".root";
+     auto file = new TFile(filename.c_str(),"RECREATE");
+     string treename = "RawTree_"+it->first;
+     auto tree = new TTree("RawTree",treename.c_str());
+     m_TreeMap[it->first]=tree;
+     m_FileMap[it->first]=file;
+     tree->SetDirectory(file);
+     std::cout << "Splitting tree: " << filename << std::endl;
+     it->second->InitBranch(tree);
+     }
+     master.close();
+
+*/
+}
+////////////////////////////////////////////////////////////////////////////////
+void RootOutput::Fill(){
+ for(auto it = pRootTrees.begin();it!=pRootTrees.end();it++){
+  it->second->Fill();
+ } 
 }
-
 
 ////////////////////////////////////////////////////////////////////////////////
 void RootOutput::InitAsciiFiles(){
@@ -159,15 +216,15 @@ void RootOutput::InitAsciiFiles(){
   pEventGenerator = new TAsciiFile();
   pEventGenerator->SetNameTitle("EventGenerator", fileNameEG.c_str());
   pEventGenerator->Append(fileNameEG.c_str());
-  pEventGenerator->Write(0,TAsciiFile::kOverwrite);
-  
+  //pEventGenerator->Write(0,TAsciiFile::kOverwrite);
+
   // Detector configuration 
   // Get file name from NPOptionManager
   std::string fileNameDC = OptionManager->GetDetectorFile();
   pDetectorConfiguration = new TAsciiFile();
   pDetectorConfiguration->SetNameTitle("DetectorConfiguration", fileNameDC.c_str());
   pDetectorConfiguration->Append(fileNameDC.c_str());
-  pDetectorConfiguration->Write(0,TAsciiFile::kOverwrite);
+  //pDetectorConfiguration->Write(0,TAsciiFile::kOverwrite);
 
   // Run to treat file
   // Get file name from NPOptionManager
@@ -176,7 +233,7 @@ void RootOutput::InitAsciiFiles(){
     std::string fileNameRT = OptionManager->GetRunToReadFile();
     pRunToTreatFile->SetNameTitle("RunToTreat", fileNameRT.c_str());
     pRunToTreatFile->Append(fileNameRT.c_str());
-    pRunToTreatFile->Write(0,TAsciiFile::kOverwrite);
+    //pRunToTreatFile->Write(0,TAsciiFile::kOverwrite);
   }
 
   // Calibration files
@@ -184,61 +241,44 @@ void RootOutput::InitAsciiFiles(){
   if (!OptionManager->IsDefault("Calibration")) {
     std::string fileNameCal = OptionManager->GetCalibrationFile();
     pCalibrationFile->SetNameTitle("Calibration", fileNameCal.c_str());
-    pCalibrationFile->Write(0,TAsciiFile::kOverwrite);
+    //pCalibrationFile->Write(0,TAsciiFile::kOverwrite);
   }
 
   // Analysis configuration files
   pAnalysisConfigFile = new TAsciiFile();
   pAnalysisConfigFile->SetNameTitle("AnalysisConfig", "AnalysisConfig");
-  pAnalysisConfigFile->Write(0,TAsciiFile::kOverwrite);
+  //pAnalysisConfigFile->Write(0,TAsciiFile::kOverwrite);
 }
 
-
-
 ////////////////////////////////////////////////////////////////////////////////
 RootOutput::~RootOutput(){ 
   // The data is written to the file and the tree is closed:
-  if (pRootFile && !NPOptionManager::getInstance()->GetPROOF()) {
-    TDirectory* currentPath= gDirectory;
-    gDirectory->cd(pRootFile->GetPath());
-   
-    // write TAsciiFile if used
-    // EventGenerator
-    if (!pEventGenerator->IsEmpty()) pEventGenerator->Write(0,TAsciiFile::kOverwrite);
-    // DetectorConfiguration
-    if (!pDetectorConfiguration->IsEmpty()) pDetectorConfiguration->Write(0,TAsciiFile::kOverwrite);
-    // CalibrationFile
-    if (!pCalibrationFile->IsEmpty()) pCalibrationFile->Write(0,TAsciiFile::kOverwrite);
-    // RunToTreatFile
-    if (!pRunToTreatFile->IsEmpty()) pRunToTreatFile->Write(0,TAsciiFile::kOverwrite);
-    // Analysis ConfigFile
-    if (!pAnalysisConfigFile->IsEmpty()) pAnalysisConfigFile->Write(0,TAsciiFile::kOverwrite);
-   
-    cout << endl;
-    cout << endl << "Root Output summary" << endl;
-    cout << "  - Number of entries in the Tree: " << pRootTree->GetEntries() << endl;
-    cout << "  - Number of bites written to file: " << pRootTree->Write(0, TObject::kOverwrite) << endl;
-    pRootFile->Flush();
-    pRootFile->Purge(1);
-
-    gDirectory->cd(currentPath->GetPath());
-    pRootFile->Close();
-  }
-
-  else if (pRootFile && NPOptionManager::getInstance()->GetPROOF()){
-    if (!pEventGenerator->IsEmpty()) pEventGenerator->Write(0,TAsciiFile::kOverwrite);
-    // DetectorConfiguration
-    if (!pDetectorConfiguration->IsEmpty()) pDetectorConfiguration->Write(0,TAsciiFile::kOverwrite);
-    // CalibrationFile
-    if (!pCalibrationFile->IsEmpty()) pCalibrationFile->Write(0,TAsciiFile::kOverwrite);
-    // RunToTreatFile
-    if (!pRunToTreatFile->IsEmpty()) pRunToTreatFile->Write(0,TAsciiFile::kOverwrite);
-    // Analysis ConfigFile
-    if (!pAnalysisConfigFile->IsEmpty()) pAnalysisConfigFile->Write(0,TAsciiFile::kOverwrite);
-  }
-
-  else if(!pRootFile && NPOptionManager::getInstance()->GetPROOF()){
-
+  if (pRootFiles.size()>0) {
+    cout << endl << endl << "Root Output summary" << endl;
+    TDirectory* pCurrentDirectory= gDirectory;
+    for(auto it = pRootFiles.begin(); it!=pRootFiles.end();it++){
+      cout << " - " <<it->first << " tree and file " << endl;
+      gDirectory->cd(it->second->GetPath());
+      // write TAsciiFile if used
+      // EventGenerator
+      if (!pEventGenerator->IsEmpty()) pEventGenerator->Write(0,TAsciiFile::kOverwrite);
+      // DetectorConfiguration
+      if (!pDetectorConfiguration->IsEmpty()) pDetectorConfiguration->Write(0,TAsciiFile::kOverwrite);
+      // CalibrationFile
+      if (!pCalibrationFile->IsEmpty()) pCalibrationFile->Write(0,TAsciiFile::kOverwrite);
+      // RunToTreatFile
+      if (!pRunToTreatFile->IsEmpty()) pRunToTreatFile->Write(0,TAsciiFile::kOverwrite);
+      // Analysis ConfigFile
+      if (!pAnalysisConfigFile->IsEmpty()) pAnalysisConfigFile->Write(0,TAsciiFile::kOverwrite);
+
+      cout << "  -> Number of entries in the " << it->first << " Tree: " << pRootTrees[it->first]->GetEntries() << endl;
+      cout << "  -> Number of bites written to file: " << pRootTrees[it->first]->Write(0, TObject::kOverwrite) << endl;
+      it->second->Flush();
+      it->second->Purge(1);
+
+      gDirectory->cd(pCurrentDirectory->GetPath());
+      it->second->Close();
+    }
   }
 
   else {
@@ -247,20 +287,27 @@ RootOutput::~RootOutput(){
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-TFile* RootOutput::InitFile(std::string fileNameBase){
-
-  if(NPOptionManager::getInstance()->GetPROOF()){
-    std::string GlobalPath = getenv("NPTOOL");
-    std::string fileName = GlobalPath + "/Outputs/Analysis/";
-    if (fileNameBase.find("root")!=std::string::npos) fileName += fileNameBase;
-    else fileName += fileNameBase + ".root";
-    pRootFile = new TFile(fileName.c_str(), "RECREATE");
-    pRootFile->Flush();
-    return pRootFile;
-  }
+TFile* RootOutput::GetFile(std::string name)  {
+  if(!pSplit)
+    name="global";
 
+  if(pRootFiles.find(name)!=pRootFiles.end())
+    return pRootFiles[name];
   else{
-    cout << "ERROR: Do not use RootOutput::InitFile without a proof environment (use --proof option to NPTool)" << endl ;
+    std::cout << "Error: Requested file for detector " << name << " does not exist" << std::endl;
     exit(1);
   }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+TTree* RootOutput::GetTree(std::string name) {
+  if(!pSplit)
+    name="global";
+
+  if(pRootTrees.find(name)==pRootTrees.end())
+    CreateTreeAndFile(name);
+  
+  return pRootTrees[name];
 }
+
diff --git a/NPLib/Core/RootOutput.h b/NPLib/Core/RootOutput.h
index 2ac1db601..d2605aa0f 100644
--- a/NPLib/Core/RootOutput.h
+++ b/NPLib/Core/RootOutput.h
@@ -1,7 +1,7 @@
 #ifndef ROOTOUTPUT_HH
 #define ROOTOUTPUT_HH
 /*****************************************************************************
- * Copyright (C) 2009-2016    this file is part of the NPTool Project        *
+ * Copyright (C) 2009-2021   this file is part of the NPTool Project         *
  *                                                                           *
  * For the licensing terms see $NPTOOL/Licence/NPTool_Licence                *
  * For the list of contributors see $NPTOOL/Licence/Contributors             *
@@ -11,18 +11,20 @@
  * Original Author: N. de Sereville  contact address: deserevi@ipno.in2p3.fr *
  *                                                                           *
  * Creation Date  : 21/07/09                                                 *
- * Last update    : 03/02/11                                                 *
+ * Last update    : 10/05/21                                                 *
  *---------------------------------------------------------------------------*
  * Decription: This class is a singleton class which deals with the ROOT     *
  *             output file and tree both for NPSimulation and NPAnalysis.    *
  *---------------------------------------------------------------------------*
  * Comment:                                                                  *
  *   + 03/02/11: Add support for TAsciiFile objects (N. de Sereville)        *
+ *   + 10/05/21: Add support for split tree output (A. Matta)                *
  *                                                                           *
  *                                                                           *
  *****************************************************************************/
 
-
+// STL
+#include <map>
 
 // NPL headers
 #include "TAsciiFile.h"
@@ -41,7 +43,8 @@ public:
   // it does not yet exist:
   // (see the constructor for an explanation of the arguments)
   static RootOutput* getInstance(std::string fileNameBase = "Simulation",
-                                 std::string treeNameBase = "SimulatedTree");
+                                 std::string treeNameBase = "SimulatedTree",
+                                 bool split = false);
   
   // The analysis class instance can be deleted by calling the Destroy
   // method (NOTE: The class destructor is protected, and can thus not be
@@ -50,8 +53,8 @@ public:
   
 protected:
   // Constructor (protected)
-  RootOutput(std::string fileNameBase, std::string treeNameBase);
-  
+  RootOutput(std::string fileNameBase, std::string treeNameBase, bool split=false);
+ 
   // Destructor (protected)
   virtual ~RootOutput();
   
@@ -65,27 +68,32 @@ private:
   
 private:
   void InitAsciiFiles();
-  
+  void CreateTreeAndFile(std::string name);
 public:
-  TFile*      GetFile()                           {return pRootFile;}
-  TTree*      GetTree()                           {return pRootTree;}
-  TList*      GetList()                           {return pRootList;}
+  TFile*      GetFile(std::string name="global") ; 
+  TTree*      GetTree(std::string name="global") ;
+  TList*      GetList(std::string name="global") ;
   TAsciiFile* GetAsciiFileEventGenerator()        {return pEventGenerator;}
   TAsciiFile* GetAsciiFileDetectorConfiguration() {return pDetectorConfiguration;}
   TAsciiFile* GetAsciiFileCalibration()           {return pCalibrationFile;}
   TAsciiFile* GetAsciiFileRunToTreat()            {return pRunToTreatFile;}
   TAsciiFile* GetAsciiFileAnalysisConfig()        {return pAnalysisConfigFile;}
-  TFile*      InitFile(std::string fileNameBase); // use only for proof environment
+  void        Fill();
   
 private:
-  TFile      *pRootFile;
-  TTree      *pRootTree;
-  TList      *pRootList;
-  TAsciiFile *pEventGenerator;
-  TAsciiFile *pDetectorConfiguration;
-  TAsciiFile *pCalibrationFile;
-  TAsciiFile *pRunToTreatFile;
-  TAsciiFile *pAnalysisConfigFile;
+  std::string                   pBaseName;
+  std::string                   pTreeName;
+  std::string                   pMasterFile;
+  TDirectory*                   pCurrentDirectory;
+  bool                          pSplit;
+  std::map<std::string, TFile*> pRootFiles;
+  std::map<std::string, TTree*> pRootTrees;
+  std::map<std::string, TList*> pRootLists;
+  TAsciiFile* pEventGenerator;
+  TAsciiFile* pDetectorConfiguration;
+  TAsciiFile* pCalibrationFile;
+  TAsciiFile* pRunToTreatFile;
+  TAsciiFile* pAnalysisConfigFile;
   
 };
 
diff --git a/NPLib/Detectors/Minos/TMinosPhysics.cxx b/NPLib/Detectors/Minos/TMinosPhysics.cxx
index d23cedc5d..3fa3a22a1 100644
--- a/NPLib/Detectors/Minos/TMinosPhysics.cxx
+++ b/NPLib/Detectors/Minos/TMinosPhysics.cxx
@@ -306,7 +306,7 @@ void TMinosPhysics::InitializeRootInputPhysics() {
 
 ///////////////////////////////////////////////////////////////////////////
 void TMinosPhysics::InitializeRootOutput() {
-  TTree* outputTree = RootOutput::getInstance()->GetTree();
+  TTree* outputTree = RootOutput::getInstance()->GetTree("Minos");
   outputTree->Branch("Minos", "TMinosPhysics", &m_EventPhysics);
 }
 
diff --git a/NPLib/Detectors/Nebula/TNebulaPhysics.cxx b/NPLib/Detectors/Nebula/TNebulaPhysics.cxx
index 0035728f3..7a74ce45a 100644
--- a/NPLib/Detectors/Nebula/TNebulaPhysics.cxx
+++ b/NPLib/Detectors/Nebula/TNebulaPhysics.cxx
@@ -313,7 +313,7 @@ void TNebulaPhysics::InitializeRootInputPhysics() {
 
 ///////////////////////////////////////////////////////////////////////////
 void TNebulaPhysics::InitializeRootOutput() {
-  TTree* outputTree = RootOutput::getInstance()->GetTree();
+  TTree* outputTree = RootOutput::getInstance()->GetTree("Nebula");
   outputTree->Branch("Nebula", "TNebulaPhysics", &m_EventPhysics);
 }
 
diff --git a/NPLib/Detectors/Samurai/TSamuraiFDC0Physics.cxx b/NPLib/Detectors/Samurai/TSamuraiFDC0Physics.cxx
index 9c049f7d3..d06456908 100644
--- a/NPLib/Detectors/Samurai/TSamuraiFDC0Physics.cxx
+++ b/NPLib/Detectors/Samurai/TSamuraiFDC0Physics.cxx
@@ -447,7 +447,7 @@ void TSamuraiFDC0Physics::InitializeRootInputPhysics(){
 
 ///////////////////////////////////////////////////////////////////////////
 void TSamuraiFDC0Physics::InitializeRootOutput(){
-  TTree* outputTree = RootOutput::getInstance()->GetTree();
+  TTree* outputTree = RootOutput::getInstance()->GetTree("SamuraiFDC0");
   outputTree->Branch( "SamuraiFDC0" , "TSamuraiFDC0Physics" , &m_EventPhysics );
 }
 
diff --git a/NPLib/Detectors/Samurai/TSamuraiFDC2Physics.cxx b/NPLib/Detectors/Samurai/TSamuraiFDC2Physics.cxx
index cd72f64f4..a443bad1a 100644
--- a/NPLib/Detectors/Samurai/TSamuraiFDC2Physics.cxx
+++ b/NPLib/Detectors/Samurai/TSamuraiFDC2Physics.cxx
@@ -471,7 +471,7 @@ void TSamuraiFDC2Physics::InitializeRootInputPhysics(){
 
 ///////////////////////////////////////////////////////////////////////////
 void TSamuraiFDC2Physics::InitializeRootOutput(){
-  TTree* outputTree = RootOutput::getInstance()->GetTree();
+  TTree* outputTree = RootOutput::getInstance()->GetTree("SamuraiFDC2");
   outputTree->Branch( "SamuraiFDC2" , "TSamuraiFDC2Physics" , &m_EventPhysics );
 }
 
diff --git a/NPLib/Detectors/Samurai/TSamuraiHodoscopePhysics.cxx b/NPLib/Detectors/Samurai/TSamuraiHodoscopePhysics.cxx
index 52de6e132..12741636a 100644
--- a/NPLib/Detectors/Samurai/TSamuraiHodoscopePhysics.cxx
+++ b/NPLib/Detectors/Samurai/TSamuraiHodoscopePhysics.cxx
@@ -313,7 +313,7 @@ void TSamuraiHodoscopePhysics::InitializeRootInputPhysics() {
 
 ///////////////////////////////////////////////////////////////////////////
 void TSamuraiHodoscopePhysics::InitializeRootOutput() {
-  TTree* outputTree = RootOutput::getInstance()->GetTree();
+  TTree* outputTree = RootOutput::getInstance()->GetTree("SamuraiHodoscope");
   outputTree->Branch("SamuraiHodoscope", "TSamuraiHodoscopePhysics", &m_EventPhysics);
 }
 
diff --git a/NPLib/Utility/npanalysis.cxx b/NPLib/Utility/npanalysis.cxx
index 9ad671f30..c33c82ffe 100644
--- a/NPLib/Utility/npanalysis.cxx
+++ b/NPLib/Utility/npanalysis.cxx
@@ -24,6 +24,7 @@ void ProgressDisplay(struct timeval& begin, struct timeval& end, unsigned long&
 int main(int argc , char** argv){
   // command line parsing
   NPOptionManager* myOptionManager = NPOptionManager::getInstance(argc,argv);
+  myOptionManager->SetIsAnalysis();
   std::string inputfilename = myOptionManager->GetRunToReadFile();
   // if input files are not given, use those from TAsciiFile
   if (myOptionManager->IsDefault("DetectorConfiguration")) {
@@ -68,8 +69,8 @@ int main(int argc , char** argv){
       OutputfileName="ResultTree";
   }
 
-  RootOutput::getInstance("Analysis/"+OutputfileName,TreeName);
-  TTree* tree= RootOutput::getInstance()->GetTree();
+  RootOutput::getInstance(OutputfileName,TreeName);
+  //TTree* tree= RootOutput::getInstance()->GetTree();
 
   // Instantiate the detector using a file
   NPL::DetectorManager* myDetector = new NPL::DetectorManager();
@@ -137,7 +138,7 @@ int main(int argc , char** argv){
         // Build the current event
         myDetector->BuildPhysicalEvent();
         // Fill the tree
-        tree->Fill();
+        myDetector->FillOutputTree();
 
         current_tree = Chain->GetTreeNumber()+1;
         //ProgressDisplay(begin,end,treated,inter,nentries,mean_rate,displayed,current_tree,total_tree);
@@ -182,7 +183,7 @@ int main(int argc , char** argv){
         // User Analysis
         UserAnalysis->TreatEvent();
         // Fill the tree      
-        tree->Fill();
+        myDetector->FillOutputTree();
       
         current_tree = Chain->GetTreeNumber()+1;
         //ProgressDisplay(begin,end,treated,inter,nentries,mean_rate,displayed,current_tree,total_tree);
@@ -216,7 +217,7 @@ int main(int argc , char** argv){
         // User Analysis
         UserAnalysis->TreatEvent();
         // Fill the tree      
-        tree->Fill();
+        myDetector->FillOutputTree();
 
         current_tree = Chain->GetTreeNumber()+1;
         //ProgressDisplay(begin,end,treated,inter,nentries,mean_rate,displayed,current_tree,total_tree);
diff --git a/NPSimulation/Simulation.cc b/NPSimulation/Simulation.cc
index ebf11bc19..76bb70d9d 100644
--- a/NPSimulation/Simulation.cc
+++ b/NPSimulation/Simulation.cc
@@ -39,6 +39,7 @@
 int main(int argc, char** argv){
     // Initialize NPOptionManager object
     NPOptionManager* OptionManager  = NPOptionManager::getInstance(argc, argv);
+    OptionManager->SetIsSimulation();
     if(OptionManager->GetVerboseLevel() > 0){
         string line;
         line.resize(80,'*');
@@ -76,7 +77,7 @@ int main(int argc, char** argv){
     ///////////////////////////////////////////////////////////////
     ///////////////// Initializing the Root Output ////////////////
     ///////////////////////////////////////////////////////////////
-    RootOutput::getInstance("Simulation/" + OptionManager->GetOutputFile());
+    RootOutput::getInstance(OptionManager->GetOutputFile());
     
     // Construct the default run manager
     G4RunManager* runManager = new G4RunManager;
-- 
GitLab