Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHNodeIOManager.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHNodeIOManager.cc
1 // Implementation of class PHNodeIOManager
2 // Author: Matthias Messer
3 
4 #include "PHNodeIOManager.h"
5 #include "PHCompositeNode.h"
6 #include "PHIODataNode.h"
7 #include "PHNodeIterator.h"
8 #include "phooldefs.h"
9 
10 #include <TBranch.h> // for TBranch
11 #include <TBranchElement.h>
12 #include <TBranchObject.h>
13 #include <TClass.h>
14 #include <TDirectory.h> // for TDirectory
15 #include <TFile.h>
16 #include <TLeafObject.h>
17 #include <TObjArray.h> // for TObjArray
18 #include <TObject.h>
19 #include <TROOT.h>
20 #include <TSystem.h>
21 #include <TTree.h>
22 
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
25 #include <boost/algorithm/string.hpp>
26 #pragma GCC diagnostic pop
27 
28 #include <cassert>
29 #include <cstdlib>
30 #include <iostream>
31 #include <sstream>
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
37  const PHAccessType a)
38 {
39  isFunctionalFlag = setFile(f, "titled by PHOOL", a) ? 1 : 0;
40 }
41 
43  const PHAccessType a)
44 {
45  isFunctionalFlag = setFile(f, title, a) ? 1 : 0;
46 }
47 
49  const PHTreeType treeindex)
50 {
51  if (treeindex != PHEventTree)
52  {
53  std::ostringstream temp;
54  temp << TreeName << treeindex; // create e.g. T1
55  TreeName = temp.str();
56  }
57  isFunctionalFlag = setFile(f, "titled by PHOOL", a) ? 1 : 0;
58 }
59 
61 {
62  closeFile();
63  delete file;
64 }
65 
67 {
68  if (file)
69  {
71  {
72  file->Write();
73  }
74  file->Close();
75  }
76 }
77 
79  const PHAccessType a)
80 {
81  filename = f;
82  accessMode = a;
83  if (file)
84  {
85  if (file->IsOpen())
86  {
87  closeFile();
88  }
89  delete file;
90  file = nullptr;
91  }
92  std::string currdir = gDirectory->GetPath();
93  gROOT->cd();
94  switch (accessMode)
95  {
96  case PHWrite:
97  file = TFile::Open(filename.c_str(), "RECREATE", title.c_str());
98  if (!file)
99  {
100  return false;
101  }
102  file->SetCompressionSettings(m_CompressionSetting);
103  tree = new TTree(TreeName.c_str(), title.c_str());
104  tree->SetMaxTreeSize(900000000000LL); // set max size to ~900 GB
105  gROOT->cd(currdir.c_str());
106  return true;
107  break;
108  case PHReadOnly:
109  file = TFile::Open(filename.c_str());
110  tree = nullptr;
111  if (!file)
112  {
113  return false;
114  }
115  selectObjectToRead("*", true);
116  gROOT->cd(currdir.c_str());
117  return true;
118  break;
119  case PHUpdate:
120  file = TFile::Open(filename.c_str(), "UPDATE", title.c_str());
121  if (!file)
122  {
123  return false;
124  }
125  file->SetCompressionSettings(m_CompressionSetting);
126  tree = new TTree(TreeName.c_str(), title.c_str());
127  gROOT->cd(currdir.c_str());
128  return true;
129  break;
130  }
131 
132  return false;
133 }
134 
136 {
137  // The write function of the PHCompositeNode topNode will
138  // recursively call the write functions of its subnodes, thus
139  // constructing the path-string which is then stored as name of the
140  // Root-branch corresponding to the data of each PHRootIODataNode.
141  topNode->write(this);
142 
143  // Now all PHRootIODataNodes should have called the write function
144  // of this I/O-manager and thus created their branch. The tree can
145  // be filled.
146  if (file && tree)
147  {
148  tree->Fill();
149  eventNumber++;
150  return true;
151  }
152 
153  return false;
154 }
155 
156 bool PHNodeIOManager::write(TObject** data, const std::string& path, int buffersize, int splitlevel)
157 {
158  if (file && tree)
159  {
160  TBranch* thisBranch = tree->GetBranch(path.c_str());
161  if (!thisBranch)
162  {
163  // the buffersize and splitlevel are set on the first call
164  // when the branch is created, the values come from the caller
165  // which is the node which writes itself
166  tree->Branch(path.c_str(), (*data)->ClassName(),
167  data, buffersize, splitlevel);
168  }
169  else
170  {
171  thisBranch->SetAddress(data);
172  }
173  return true;
174  }
175 
176  return false;
177 }
178 
179 bool PHNodeIOManager::read(size_t requestedEvent)
180 {
181  if (readEventFromFile(requestedEvent))
182  {
183  return true;
184  }
185  else
186  {
187  return false;
188  }
189 }
190 
192 PHNodeIOManager::read(PHCompositeNode* topNode, size_t requestedEvent)
193 {
194  // No tree means we have not yet looked at the file,
195  // so we'll reconstruct the node tree now.
196  if (!tree)
197  {
198  topNode = reconstructNodeTree(topNode);
199  }
200 
201  // If everything worked, there should be a tree now.
202  if (tree && readEventFromFile(requestedEvent))
203  {
204  return topNode;
205  }
206  else
207  {
208  return nullptr;
209  }
210 }
211 
213 {
214  if (file)
215  {
216  if (accessMode == PHReadOnly)
217  {
218  std::cout << "PHNodeIOManager reading " << filename << std::endl;
219  }
220  else
221  {
222  std::cout << "PHNodeIOManager writing " << filename << std::endl;
223  }
224  }
225  if (file && tree)
226  {
227  tree->Print();
228  }
229  std::cout << "\n\nList of selected objects to read:" << std::endl;
230  std::map<std::string, bool>::const_iterator classiter;
231  for (classiter = objectToRead.begin(); classiter != objectToRead.end(); ++classiter)
232  {
233  std::cout << classiter->first << " is set to " << classiter->second << std::endl;
234  }
235 }
236 
239 {
240  // OK. Here all the game is to find out the name of the type
241  // contained in this branch. In ROOT pre-3.01/05 versions, all
242  // branches we used were of the same type = TBranchObject, so that
243  // was easy. Since version 3.01/05 ROOT introduced new branch style
244  // with some TBranchElement objects. So far so good. The problem is
245  // that I did not find a common way to grab the typename of the
246  // object contained in those branches, so I hereby use some durty if
247  // { } else if { } ...
248 
249 #if ROOT_VERSION_CODE >= ROOT_VERSION(3, 01, 5)
250  TBranchElement* be = dynamic_cast<TBranchElement*>(branch);
251 
252  if (be)
253  {
254  // TBranchElement has a nice GetClassName() method for us :
255  return be->GetClassName();
256  }
257 #endif
258 
259  TBranchObject* bo = dynamic_cast<TBranchObject*>(branch);
260  if (bo)
261  {
262  // For this one we need to go down a little before getting the
263  // name...
264  TLeafObject* leaf = static_cast<TLeafObject*>(branch->GetLeaf(branch->GetName()));
265  assert(leaf != nullptr);
266  return leaf->GetTypeName();
267  }
268  std::cout << PHWHERE << "Fatal error, dynamic cast of TBranchObject failed" << std::endl;
269  gSystem->Exit(1);
270  exit(1); // the compiler does not know gSystem->Exit() quits, needs exit to avoid warning
271 }
272 
273 bool PHNodeIOManager::readEventFromFile(size_t requestedEvent)
274 {
275  // Se non c'e niente, non possiamo fare niente. Logisch, n'est ce
276  // pas?
277  if (!tree)
278  {
279  PHMessage("PHNodeIOManager::readEventFromFile", PHError,
280  "Tree not initialized.");
281  return false;
282  }
283 
284  int bytesRead;
285 
286  // Due to the current implementation of TBuffer>>(Long_t) we need
287  // to cd() in the current file before trying to fetch any event,
288  // otherwise mixing of reading 2.25/03 DST with writing some
289  // 3.01/05 trees will fail.
290  std::string currdir = gDirectory->GetPath();
291  TFile* file_ptr = gFile; // save current gFile
292  file->cd();
293 
294  if (requestedEvent)
295  {
296  if ((bytesRead = tree->GetEvent(requestedEvent)))
297  {
298  eventNumber = requestedEvent + 1;
299  }
300  }
301  else
302  {
303  bytesRead = tree->GetEvent(eventNumber++);
304  }
305 
306  gFile = file_ptr; // recover gFile
307  gROOT->cd(currdir.c_str());
308 
309  if (!bytesRead)
310  {
311  return false;
312  }
313  if (bytesRead == -1)
314  {
315  std::cout << PHWHERE << "Error: Input TTree corrupt, exiting now" << std::endl;
316  exit(1);
317  }
318  return true;
319 }
320 
321 int PHNodeIOManager::readSpecific(size_t requestedEvent, const std::string& objectName)
322 {
323  // objectName should be one of the valid branch name of the "T" TTree, and
324  // should be one of the branches selected by selectObjectToRead() method.
325  // No wildcard allowed for the moment.
326  std::map<std::string, TBranch*>::const_iterator p = fBranches.find(objectName);
327 
328  if (p != fBranches.end())
329  {
330  TBranch* branch = p->second;
331  if (branch)
332  {
333  return branch->GetEvent(requestedEvent);
334  }
335  }
336  else
337  {
338  PHMessage("PHNodeIOManager::readSpecific", PHError,
339  "Unknown object name");
340  }
341  return 0;
342 }
343 
346 {
347  if (!file)
348  {
349  if (filename.empty())
350  {
351  std::cout << PHWHERE << "filename was never set" << std::endl;
352  }
353  else
354  {
355  std::cout << PHWHERE << "TFile " << filename << " NULL pointer" << std::endl;
356  }
357  return nullptr;
358  }
359 
360  tree = static_cast<TTree*>(file->Get(TreeName.c_str()));
361 
362  if (!tree)
363  {
364  std::cout << PHWHERE << "PHNodeIOManager::reconstructNodeTree : Root Tree "
365  << TreeName << " not found in file " << file->GetName() << std::endl;
366  return nullptr;
367  }
368 
369  // ROOT sucks, we need a unique name for the tree so we can open multiple
370  // files. So we take the memory location of the file pointer which
371  // should be unique within this process to create it
372  std::ostringstream nname;
373  nname << TreeName << file;
374 
375  tree->SetName(nname.str().c_str());
376 
377  // Select the branches according to objectToRead
378  std::map<std::string, bool>::const_iterator it;
379 
380  if (tree->GetNbranches() > 0)
381  {
382  for (it = objectToRead.begin(); it != objectToRead.end(); ++it)
383  {
384  tree->SetBranchStatus((it->first).c_str(),
385  static_cast<bool>(it->second));
386  }
387  }
388  // The file contains a TTree with a list of the TBranchObjects
389  // attached to it.
390  TObjArray* branchArray = tree->GetListOfBranches();
391 
392  // We need these in the loops down below...
393  size_t i, j;
394 
395  // If a topNode was provided, we can feed the iterator with it.
396  if (!topNode)
397  {
398  topNode = new PHCompositeNode("TOP"); // create topNode if we got a null pointer
399  }
400  PHNodeIterator nodeIter(topNode);
401 
402  // Loop over all branches in the tree. Each branch-name contains the
403  // full 'path' of composite-nodes in the original node tree. We
404  // split the name and reconstruct the tree.
405  std::string delimeters = phooldefs::branchpathdelim + phooldefs::legacypathdelims; // add old backslash for backward compat
406  for (i = 0; i < (size_t)(branchArray->GetEntriesFast()); i++)
407  {
408  std::string branchname = (*branchArray)[i]->GetName();
409  std::vector<std::string> splitvec;
410  boost::split(splitvec, branchname, boost::is_any_of(delimeters));
411  for (size_t ia = 1; ia < splitvec.size() - 1; ia++) // -1 so we skip the node name
412  {
413  if (!nodeIter.cd(splitvec[ia]))
414  {
415  nodeIter.addNode(new PHCompositeNode(splitvec[ia]));
416  nodeIter.cd(splitvec[ia]);
417  }
418  }
419  TBranch* thisBranch = (TBranch*) ((*branchArray)[i]);
420 
421  // Skip non-selected branches
422  if (thisBranch->TestBit(kDoNotProcess))
423  {
424  continue;
425  }
426 
427  std::string branchClassName = getBranchClassName(thisBranch);
428  std::string branchName = thisBranch->GetName();
429  fBranches[branchName] = thisBranch;
430 
431  assert(gROOT != nullptr);
432  TClass* thisClass = gROOT->GetClass(branchClassName.c_str());
433 
434  if (!thisClass)
435  {
436  std::cout << PHWHERE << std::endl;
437  std::cout << "Missing Class: " << branchClassName << std::endl;
438  std::cout << "Did you forget to load the shared library which contains "
439  << branchClassName << "?" << std::endl;
440  }
441  // it does not make sense to continue - the code coredumps
442  // later if a class is not loaded
443  assert(thisClass != nullptr);
444 
445  PHIODataNode<TObject>* newIODataNode =
446  static_cast<PHIODataNode<TObject>*>(nodeIter.findFirst("PHIODataNode", (*splitvec.rbegin()).c_str()));
447  if (!newIODataNode)
448  {
449  TObject* newTObject = static_cast<TObject*>(thisClass->New());
450  newIODataNode = new PHIODataNode<TObject>(newTObject, (*splitvec.rbegin()).c_str());
451  nodeIter.addNode(newIODataNode);
452  }
453  else
454  {
455  TObject* oldobject = newIODataNode->getData();
456  std::string oldclass = oldobject->ClassName();
457  if (oldclass != branchClassName)
458  {
459  std::cout << "You only have to worry if you get this message when reading parallel files"
460  << std::endl
461  << "if you get this when opening the 2nd, 3rd,... file" << std::endl
462  << "It looks like your objects are not of the same version in these files" << std::endl;
463  std::cout << PHWHERE << "Found object " << oldobject->ClassName()
464  << " in node tree but the file "
465  << filename << " contains a " << branchClassName
466  << " object. The object will be replaced without harming you" << std::endl;
467  std::cout << "CAVEAT: If you use local copies of pointers to data nodes" << std::endl
468  << "instead of searching the node tree you are in trouble now" << std::endl;
469  delete newIODataNode;
470  TObject* newTObject = static_cast<TObject*>(thisClass->New());
471  newIODataNode = new PHIODataNode<TObject>(newTObject, (*splitvec.rbegin()).c_str());
472  nodeIter.addNode(newIODataNode);
473  }
474  }
475 
476  if (thisClass->InheritsFrom("PHObject"))
477  {
478  newIODataNode->setObjectType("PHObject");
479  }
480  else
481  {
482  std::cout << PHWHERE << branchClassName.c_str()
483  << " inherits neither from PHTable nor from PHObject"
484  << " setting type to PHObject" << std::endl;
485  newIODataNode->setObjectType("PHObject");
486  }
487  thisBranch->SetAddress(&(newIODataNode->data));
488  for (j = 1; j < splitvec.size() - 1; j++)
489  {
490  nodeIter.cd("..");
491  }
492  }
493  return topNode;
494 }
495 
496 void PHNodeIOManager::selectObjectToRead(const std::string& objectName, bool readit)
497 {
498  objectToRead[objectName] = readit;
499 
500  // If tree is already open, loop over map and set branch status
501  if (tree)
502  {
503  std::map<std::string, bool>::const_iterator it;
504 
505  for (it = objectToRead.begin(); it != objectToRead.end(); ++it)
506  {
507  tree->SetBranchStatus((it->first).c_str(),
508  static_cast<bool>(it->second));
509  }
510  }
511  return;
512 }
513 
515 {
516  std::map<std::string, TBranch*>::const_iterator p = fBranches.find(objectName);
517 
518  if (p != fBranches.end())
519  {
520  return true;
521  }
522 
523  return false;
524 }
525 
527 {
528  if (level < 0)
529  {
530  return false;
531  }
533  if (file)
534  {
535  file->SetCompressionSettings(m_CompressionSetting);
536  }
537 
538  return true;
539 }
540 
541 uint64_t
543 {
544  if (file) return file->GetBytesWritten();
545  return 0.;
546 }
547 
548 uint64_t
550 {
551  if (file) return file->GetSize();
552  return 0.;
553 }
554 
555 std::map<std::string, TBranch*>*
557 {
558  FillBranchMap();
559  return &fBranches;
560 }
561 
563 {
564  if (fBranches.empty())
565  {
566  TTree* treetmp = static_cast<TTree*>(file->Get(TreeName.c_str()));
567  if (treetmp)
568  {
569  TObjArray* branchArray = treetmp->GetListOfBranches();
570  for (size_t i = 0; i < (size_t)(branchArray->GetEntriesFast()); i++)
571  {
572  TBranch* thisBranch = (TBranch*) ((*branchArray)[i]);
573  std::string branchName = (*branchArray)[i]->GetName();
574  fBranches[branchName] = thisBranch;
575  }
576  }
577  else
578  {
579  std::cout << PHWHERE << " No Root Tree " << TreeName
580  << " on file " << filename << std::endl;
581  return -1;
582  }
583  }
584  return 0;
585 }
586 
588 {
589  if (fBranches.empty())
590  {
591  FillBranchMap();
592  }
593  std::string delimeters = phooldefs::branchpathdelim + phooldefs::legacypathdelims; // add old backslash for backward compat
594  for (auto & fBranche : fBranches)
595  {
596  std::vector<std::string> splitvec;
597  boost::split(splitvec, fBranche.first, boost::is_any_of(delimeters));
598  if (splitvec.back() == nodename)
599  {
600  return true;
601  }
602  }
603  return false;
604 }