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