Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:19:51

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