Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:16:19

0001 //  Implementation of class PHNodeIOManager
0002 //  Author: Matthias Messer
0003 
0004 #include "PHNodeIOManager.h"
0005 #include "PHCompositeNode.h"
0006 #include "PHIODataNode.h"
0007 #include "PHNodeIterator.h"
0008 #include "phooldefs.h"
0009 
0010 #include <TBranch.h>  // for TBranch
0011 #include <TBranchElement.h>
0012 #include <TBranchObject.h>
0013 #include <TClass.h>
0014 #include <TDirectory.h>  // for TDirectory
0015 #include <TFile.h>
0016 #include <TLeafObject.h>
0017 #include <TObjArray.h>  // for TObjArray
0018 #include <TObject.h>
0019 #include <TROOT.h>
0020 #include <TSystem.h>
0021 #include <TTree.h>
0022 #include <TTreeCache.h>
0023 
0024 #include <boost/algorithm/string.hpp>
0025 
0026 #include <cassert>
0027 #include <cstdlib>
0028 #include <iostream>
0029 #include <sstream>
0030 #include <string>
0031 #include <utility>
0032 #include <vector>
0033 
0034 PHNodeIOManager::PHNodeIOManager(const std::string& f,
0035                                  const PHAccessType a)
0036 {
0037   isFunctionalFlag = setFile(f, "titled by PHOOL", a) ? 1 : 0;
0038 }
0039 
0040 PHNodeIOManager::PHNodeIOManager(const std::string& f, const std::string& title,
0041                                  const PHAccessType a)
0042   : isFunctionalFlag(setFile(f, title, a) ? 1 : 0)
0043 {
0044 }
0045 
0046 PHNodeIOManager::PHNodeIOManager(const std::string& f, const PHAccessType a,
0047                                  const PHTreeType treeindex)
0048 {
0049   if (treeindex != PHEventTree)
0050   {
0051     std::ostringstream temp;
0052     temp << TreeName << treeindex;  // create e.g. T1
0053     TreeName = temp.str();
0054   }
0055   isFunctionalFlag = setFile(f, "titled by PHOOL", a) ? 1 : 0;
0056 }
0057 
0058 PHNodeIOManager::~PHNodeIOManager()
0059 {
0060   closeFile();
0061   delete file;
0062 }
0063 
0064 void PHNodeIOManager::closeFile()
0065 {
0066   if (file)
0067   {
0068     if (accessMode == PHWrite || accessMode == PHUpdate)
0069     {
0070       file->Write();
0071     }
0072     file->Close();
0073   }
0074 }
0075 
0076 bool PHNodeIOManager::setFile(const std::string& f, const std::string& title,
0077                               const PHAccessType a)
0078 {
0079   filename = f;
0080   accessMode = a;
0081   if (file)
0082   {
0083     if (file->IsOpen())
0084     {
0085       closeFile();
0086     }
0087     delete file;
0088     file = nullptr;
0089   }
0090   std::string currdir = gDirectory->GetPath();
0091   gROOT->cd();
0092   switch (accessMode)
0093   {
0094   case PHWrite:
0095     file = TFile::Open(filename.c_str(), "RECREATE", title.c_str());
0096     if (!file)
0097     {
0098       return false;
0099     }
0100     file->SetCompressionSettings(m_CompressionSetting);
0101     tree = new TTree(TreeName.c_str(), title.c_str());
0102     TTree::SetMaxTreeSize(900000000000LL);  // set max size to ~900 GB
0103     gROOT->cd(currdir.c_str());
0104     return true;
0105     break;
0106   case PHReadOnly:
0107     file = TFile::Open(filename.c_str());
0108     tree = nullptr;
0109     if (!file)
0110     {
0111       return false;
0112     }
0113     selectObjectToRead("*", true);
0114     gROOT->cd(currdir.c_str());
0115     return true;
0116     break;
0117   case PHUpdate:
0118     file = TFile::Open(filename.c_str(), "UPDATE", title.c_str());
0119     if (!file)
0120     {
0121       return false;
0122     }
0123     file->SetCompressionSettings(m_CompressionSetting);
0124     tree = new TTree(TreeName.c_str(), title.c_str());
0125     gROOT->cd(currdir.c_str());
0126     return true;
0127     break;
0128   default:
0129     std::cout << PHWHERE << "Invalid Access mode " << accessMode << std::endl;
0130     break;
0131   }
0132 
0133   return false;
0134 }
0135 
0136 bool PHNodeIOManager::write(PHCompositeNode* topNode)
0137 {
0138   // The write function of the PHCompositeNode topNode will
0139   // recursively call the write functions of its subnodes, thus
0140   // constructing the path-string which is then stored as name of the
0141   // Root-branch corresponding to the data of each PHRootIODataNode.
0142   topNode->write(this);
0143 
0144   // Now all PHRootIODataNodes should have called the write function
0145   // of this I/O-manager and thus created their branch. The tree can
0146   // be filled.
0147   if (file && tree)
0148   {
0149     tree->Fill();
0150     eventNumber++;
0151     return true;
0152   }
0153 
0154   return false;
0155 }
0156 
0157 bool PHNodeIOManager::write(TObject** data, const std::string& path, int nodebuffersize, int nodesplitlevel)
0158 {
0159   if (file && tree)
0160   {
0161     TBranch* thisBranch = tree->GetBranch(path.c_str());
0162     if (!thisBranch)
0163     {
0164       int use_splitlevel = splitlevel;
0165       int use_buffersize = buffersize;
0166       // the buffersize and splitlevel are set on the first call
0167       // when the branch is created, the values come from the caller
0168       // which is the node which writes itself
0169       if (splitlevel == std::numeric_limits<int>::min())
0170       {
0171         use_splitlevel = nodesplitlevel;
0172       }
0173       if (buffersize == std::numeric_limits<int>::min())
0174       {
0175         use_buffersize = nodebuffersize;
0176       }
0177       tree->Branch(path.c_str(), (*data)->ClassName(),
0178                    data, use_buffersize, use_splitlevel);
0179     }
0180     else
0181     {
0182       thisBranch->SetAddress(data);
0183     }
0184     return true;
0185   }
0186 
0187   return false;
0188 }
0189 
0190 bool PHNodeIOManager::read(size_t requestedEvent)
0191 {
0192   return readEventFromFile(requestedEvent);
0193 }
0194 
0195 PHCompositeNode*
0196 PHNodeIOManager::read(PHCompositeNode* topNode, size_t requestedEvent)
0197 {
0198   // No tree means we have not yet looked at the file,
0199   // so we'll reconstruct the node tree now.
0200   if (!tree)
0201   {
0202     topNode = reconstructNodeTree(topNode);
0203   }
0204 
0205   // If everything worked, there should be a tree now.
0206   if (tree && readEventFromFile(requestedEvent))
0207   {
0208     return topNode;
0209   }
0210 
0211   return nullptr;
0212 }
0213 
0214 void PHNodeIOManager::print() const
0215 {
0216   if (file)
0217   {
0218     if (accessMode == PHReadOnly)
0219     {
0220       std::cout << "PHNodeIOManager reading  " << filename << std::endl;
0221     }
0222     else
0223     {
0224       std::cout << "PHNodeIOManager writing  " << filename << std::endl;
0225     }
0226   }
0227   if (file && tree)
0228   {
0229     tree->Print();
0230   }
0231   std::cout << "\n\nList of selected objects to read:" << std::endl;
0232   std::map<std::string, bool>::const_iterator classiter;
0233   for (classiter = objectToRead.begin(); classiter != objectToRead.end(); ++classiter)
0234   {
0235     std::cout << classiter->first << " is set to " << classiter->second << std::endl;
0236   }
0237 }
0238 
0239 std::string
0240 PHNodeIOManager::getBranchClassName(TBranch* branch)
0241 {
0242   // OK. Here all the game is to find out the name of the type
0243   // contained in this branch.  In ROOT pre-3.01/05 versions, all
0244   // branches we used were of the same type = TBranchObject, so that
0245   // was easy.  Since version 3.01/05 ROOT introduced new branch style
0246   // with some TBranchElement objects. So far so good.  The problem is
0247   // that I did not find a common way to grab the typename of the
0248   // object contained in those branches, so I hereby use some durty if
0249   // { } else if { } ...
0250 
0251 #if ROOT_VERSION_CODE >= ROOT_VERSION(3, 01, 5)
0252   TBranchElement* be = dynamic_cast<TBranchElement*>(branch);
0253 
0254   if (be)
0255   {
0256     // TBranchElement has a nice GetClassName() method for us :
0257     return be->GetClassName();
0258   }
0259 #endif
0260 
0261   TBranchObject* bo = dynamic_cast<TBranchObject*>(branch);
0262   if (bo)
0263   {
0264     // For this one we need to go down a little before getting the
0265     // name...
0266       // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
0267     TLeafObject* leaf = static_cast<TLeafObject*>(branch->GetLeaf(branch->GetName()));
0268     assert(leaf != nullptr);
0269     return leaf->GetTypeName();
0270   }
0271   std::cout << PHWHERE << "Fatal error, dynamic cast of TBranchObject failed" << std::endl;
0272   gSystem->Exit(1);
0273   exit(1);  // the compiler does not know gSystem->Exit() quits, needs exit to avoid warning
0274 }
0275 
0276 bool PHNodeIOManager::readEventFromFile(size_t requestedEvent)
0277 {
0278   // Se non c'e niente, non possiamo fare niente.  Logisch, n'est ce
0279   // pas?
0280   if (!tree)
0281   {
0282     std::cout << PHWHERE << " Tree not initialized" << std::endl;
0283     return false;
0284   }
0285 
0286   int bytesRead;
0287 
0288   // Due to the current implementation of TBuffer>>(Long_t) we need
0289   // to cd() in the current file before trying to fetch any event,
0290   // otherwise mixing of reading 2.25/03 DST with writing some
0291   // 3.01/05 trees will fail.
0292   std::string currdir = gDirectory->GetPath();
0293   TFile* file_ptr = gFile;  // save current gFile
0294   file->cd();
0295 
0296   if (requestedEvent)
0297   {
0298     bytesRead = tree->GetEvent(requestedEvent);
0299     if (bytesRead)
0300     {
0301       eventNumber = requestedEvent + 1;
0302     }
0303   }
0304   else
0305   {
0306     bytesRead = tree->GetEvent(eventNumber++);
0307   }
0308 
0309   gFile = file_ptr;  // recover gFile
0310   gROOT->cd(currdir.c_str());
0311 
0312   if (!bytesRead)
0313   {
0314     return false;
0315   }
0316   if (bytesRead == -1)
0317   {
0318     std::cout << PHWHERE << "Error: Input TTree corrupt, exiting now" << std::endl;
0319     exit(1);
0320   }
0321   return true;
0322 }
0323 
0324 int PHNodeIOManager::readSpecific(size_t requestedEvent, const std::string& objectName)
0325 {
0326   // objectName should be one of the valid branch name of the "T" TTree, and
0327   // should be one of the branches selected by selectObjectToRead() method.
0328   // No wildcard allowed for the moment.
0329   std::map<std::string, TBranch*>::const_iterator p = fBranches.find(objectName);
0330 
0331   if (p != fBranches.end())
0332   {
0333     TBranch* branch = p->second;
0334     if (branch)
0335     {
0336       return branch->GetEvent(requestedEvent);
0337     }
0338   }
0339   else
0340   {
0341     std::cout << PHWHERE << "Cannot find "
0342               << objectName << " in TBranch" << std::endl;
0343   }
0344   return 0;
0345 }
0346 
0347 PHCompositeNode*
0348 PHNodeIOManager::reconstructNodeTree(PHCompositeNode* topNode)
0349 {
0350   if (!file)
0351   {
0352     if (filename.empty())
0353     {
0354       std::cout << PHWHERE << "filename was never set" << std::endl;
0355     }
0356     else
0357     {
0358       std::cout << PHWHERE << "TFile " << filename << " NULL pointer" << std::endl;
0359     }
0360     return nullptr;
0361   }
0362 
0363   file->GetObject(TreeName.c_str(),tree);
0364 
0365   if (!tree)
0366   {
0367     std::cout << PHWHERE << "PHNodeIOManager::reconstructNodeTree : Root Tree "
0368               << TreeName << " not found in file " << file->GetName() << std::endl;
0369     return nullptr;
0370   }
0371   // ROOT sucks, we need a unique name for the tree so we can open multiple
0372   // files. So we take the memory location of the file pointer which
0373   // should be unique within this process to create it
0374   std::ostringstream nname;
0375   nname << TreeName << file;
0376 
0377   tree->SetName(nname.str().c_str());
0378 
0379   // Select the branches according to objectToRead
0380   std::map<std::string, bool>::const_iterator it;
0381 
0382   if (tree->GetNbranches() > 0)
0383   {
0384     for (it = objectToRead.begin(); it != objectToRead.end(); ++it)
0385     {
0386       tree->SetBranchStatus((it->first).c_str(),
0387                             static_cast<bool>(it->second));
0388     }
0389   }
0390   // The file contains a TTree with a list of the TBranchObjects
0391   // attached to it.
0392   TObjArray* branchArray = tree->GetListOfBranches();
0393 
0394   // We need these in the loops down below...
0395   size_t i;
0396   size_t j;
0397 
0398   // If a topNode was provided, we can feed the iterator with it.
0399   if (!topNode)
0400   {
0401     topNode = new PHCompositeNode("TOP");  // create topNode if we got a null pointer
0402   }
0403   PHNodeIterator nodeIter(topNode);
0404 
0405   // Loop over all branches in the tree. Each branch-name contains the
0406   // full 'path' of composite-nodes in the original node tree. We
0407   // split the name and reconstruct the tree.
0408   std::string delimeters = phooldefs::branchpathdelim + phooldefs::legacypathdelims;  // add old backslash for backward compat
0409   for (i = 0; i < (size_t) (branchArray->GetEntriesFast()); i++)
0410   {
0411     std::string branchname = (*branchArray)[i]->GetName();
0412     std::vector<std::string> splitvec;
0413     boost::split(splitvec, branchname, boost::is_any_of(delimeters));
0414     for (size_t ia = 1; ia < splitvec.size() - 1; ia++)  // -1 so we skip the node name
0415     {
0416       if (!nodeIter.cd(splitvec[ia]))
0417       {
0418         nodeIter.addNode(new PHCompositeNode(splitvec[ia]));
0419         nodeIter.cd(splitvec[ia]);
0420       }
0421     }
0422     TBranch* thisBranch = (TBranch*) ((*branchArray)[i]);
0423 
0424     // Skip non-selected branches
0425     if (thisBranch->TestBit(kDoNotProcess))
0426     {
0427       continue;
0428     }
0429 
0430     std::string branchClassName = getBranchClassName(thisBranch);
0431     std::string branchName = thisBranch->GetName();
0432     fBranches[branchName] = thisBranch;
0433 
0434     assert(gROOT != nullptr);
0435     TClass* thisClass = gROOT->GetClass(branchClassName.c_str());
0436 
0437     if (!thisClass)
0438     {
0439       std::cout << PHWHERE << std::endl;
0440       std::cout << "Missing Class: " << branchClassName << std::endl;
0441       std::cout << "Did you forget to load the shared library which contains "
0442                 << branchClassName << "?" << std::endl;
0443     }
0444     // it does not make sense to continue - the code coredumps
0445     // later if a class is not loaded
0446     assert(thisClass != nullptr);
0447 
0448     PHIODataNode<TObject>* newIODataNode = static_cast<PHIODataNode<TObject>*>(nodeIter.findFirst("PHIODataNode", *splitvec.rbegin()));// NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
0449     if (!newIODataNode)
0450     {
0451       TObject* newTObject = static_cast<TObject*>(thisClass->New());
0452       newIODataNode = new PHIODataNode<TObject>(newTObject, *splitvec.rbegin());
0453       nodeIter.addNode(newIODataNode);
0454     }
0455     else
0456     {
0457       TObject* oldobject = newIODataNode->getData();
0458       std::string oldclass = oldobject->ClassName();
0459       if (oldclass != branchClassName)
0460       {
0461         std::cout << "You only have to worry if you get this message when reading parallel files"
0462                   << std::endl
0463                   << "if you get this when opening the 2nd, 3rd,... file" << std::endl
0464                   << "It looks like your objects are not of the same version in these files" << std::endl;
0465         std::cout << PHWHERE << "Found object " << oldobject->ClassName()
0466                   << " in node tree but the  file "
0467                   << filename << " contains a " << branchClassName
0468                   << " object. The object will be replaced without harming you" << std::endl;
0469         std::cout << "CAVEAT: If you use local copies of pointers to data nodes" << std::endl
0470                   << "instead of searching the node tree you are in trouble now" << std::endl;
0471         delete newIODataNode;
0472         TObject* newTObject = static_cast<TObject*>(thisClass->New());
0473         newIODataNode = new PHIODataNode<TObject>(newTObject, *splitvec.rbegin());
0474         nodeIter.addNode(newIODataNode);
0475       }
0476     }
0477 
0478     if (thisClass->InheritsFrom("PHObject"))
0479     {
0480       newIODataNode->setObjectType("PHObject");
0481     }
0482     else
0483     {
0484       std::cout << PHWHERE << branchClassName.c_str()
0485                 << " inherits neither from PHTable nor from PHObject"
0486                 << " setting type to PHObject" << std::endl;
0487       newIODataNode->setObjectType("PHObject");
0488     }
0489     thisBranch->SetAddress(&(newIODataNode->data));
0490     for (j = 1; j < splitvec.size() - 1; j++)
0491     {
0492       nodeIter.cd("..");
0493     }
0494   }
0495   return topNode;
0496 }
0497 
0498 void PHNodeIOManager::selectObjectToRead(const std::string& objectName, bool readit)
0499 {
0500   objectToRead[objectName] = readit;
0501 
0502   // If tree is already open, loop over map and set branch status
0503   if (tree)
0504   {
0505     std::map<std::string, bool>::const_iterator it;
0506 
0507     for (it = objectToRead.begin(); it != objectToRead.end(); ++it)
0508     {
0509       tree->SetBranchStatus((it->first).c_str(),
0510                             static_cast<bool>(it->second));
0511     }
0512   }
0513   return;
0514 }
0515 
0516 bool PHNodeIOManager::isSelected(const std::string& objectName)
0517 {
0518   std::map<std::string, TBranch*>::const_iterator p = fBranches.find(objectName);
0519 
0520   return p != fBranches.end();
0521 }
0522 
0523 bool PHNodeIOManager::SetCompressionSetting(const int level)
0524 {
0525   if (level < 0)
0526   {
0527     return false;
0528   }
0529   m_CompressionSetting = level;
0530   if (file)
0531   {
0532     file->SetCompressionSettings(m_CompressionSetting);
0533   }
0534 
0535   return true;
0536 }
0537 
0538 uint64_t
0539 PHNodeIOManager::GetBytesWritten()
0540 {
0541   if (file)
0542   {
0543     return file->GetBytesWritten();
0544   }
0545   return 0.;
0546 }
0547 
0548 uint64_t
0549 PHNodeIOManager::GetFileSize()
0550 {
0551   if (file)
0552   {
0553     return file->GetSize();
0554   }
0555   return 0.;
0556 }
0557 
0558 std::map<std::string, TBranch*>*
0559 PHNodeIOManager::GetBranchMap()
0560 {
0561   FillBranchMap();
0562   return &fBranches;
0563 }
0564 
0565 int PHNodeIOManager::FillBranchMap()
0566 {
0567   if (fBranches.empty())
0568   {
0569     TTree* treetmp = nullptr;
0570     file->GetObject(TreeName.c_str(),treetmp);
0571     if (treetmp)
0572     {
0573       TObjArray* branchArray = treetmp->GetListOfBranches();
0574       for (size_t i = 0; i < (size_t) (branchArray->GetEntriesFast()); i++)
0575       {
0576         TBranch* thisBranch = (TBranch*) ((*branchArray)[i]);
0577         std::string branchName = (*branchArray)[i]->GetName();
0578         fBranches[branchName] = thisBranch;
0579       }
0580     }
0581     else
0582     {
0583       std::cout << PHWHERE << " No Root Tree " << TreeName
0584                 << " on file " << filename << std::endl;
0585       return -1;
0586     }
0587   }
0588   return 0;
0589 }
0590 
0591 bool PHNodeIOManager::NodeExist(const std::string& nodename)
0592 {
0593   if (fBranches.empty())
0594   {
0595     FillBranchMap();
0596   }
0597   std::string delimeters = phooldefs::branchpathdelim + phooldefs::legacypathdelims;  // add old backslash for backward compat
0598   for (auto& fBranche : fBranches)
0599   {
0600     std::vector<std::string> splitvec;
0601     boost::split(splitvec, fBranche.first, boost::is_any_of(delimeters));
0602     if (splitvec.back() == nodename)
0603     {
0604       return true;
0605     }
0606   }
0607   return false;
0608 }
0609 
0610 void PHNodeIOManager::DisableReadCache()
0611 {
0612   if (file)
0613   {
0614     file->SetCacheRead(nullptr);
0615   }
0616   return;
0617 }