source: trunk/MagicSoft/Mars/mbase/MReadTree.cc@ 1343

Last change on this file since 1343 was 1340, checked in by tbretz, 23 years ago
*** empty log message ***
File size: 24.2 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz 12/2000 <mailto:tbretz@uni-sw.gwdg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2002
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26// //
27// MReadTree //
28// //
29// This tasks opens all branches in a specified tree and creates the //
30// corresponding parameter containers if not already existing in the //
31// parameter list. //
32// //
33// The Process function reads one events from the tree. To go through the //
34// events of one tree make sure that the event number is increased from //
35// outside. It makes also possible to go back by decreasing the number. //
36// //
37// If you don't want to start reading the first event you have to call //
38// MReadTree::SetEventNum after instantiating your MReadTree-object. //
39// //
40// To make reading much faster (up to a factor of 10 to 20) you can //
41// ensure that only the data you are really processing is enabled by //
42// calling MReadTree::UseLeaf. //
43// //
44// If the chain switches from one file to another file all //
45// TObject::Notify() functions are called of TObject objects which were //
46// added to the Notifier list view MReadTree::AddNotify. If MReadTree //
47// is the owner (viw MReadTree::SetOwner) all this objects are deleted //
48// by the destructor of MReadTree //
49// //
50/////////////////////////////////////////////////////////////////////////////
51#include "MReadTree.h"
52
53#include <fstream.h>
54
55#include <TFile.h> // TFile::GetName
56#include <TChain.h>
57#include <TSystem.h> // gSystem->ExpandPath
58#include <TGProgressBar.h>
59#include <TChainElement.h>
60#include <TOrdCollection.h>
61
62#include "MLog.h"
63#include "MLogManip.h"
64
65#include "MTime.h"
66#include "MFilter.h"
67#include "MParList.h"
68#include "MTaskList.h"
69
70ClassImp(MReadTree);
71
72class MChain : public TChain
73{
74private:
75 Bool_t fNotified;
76
77public:
78 MChain() : TChain(), fNotified(kFALSE) {}
79 MChain(const char *name, const char *title="") : TChain(name, title), fNotified(kFALSE) {}
80
81 void ResetTree() { fTree = 0; }
82
83 virtual Bool_t Notify() { fNotified = kTRUE; return kTRUE; }
84 virtual void SetNotify(TObject *obj) { fNotify = obj; fNotified = kFALSE; }
85
86 Int_t LoadTree(Int_t entry)
87 {
88 //
89 // This is the code from TChain::LoadTree but skips the
90 // notification in LoadTree. If LoadTree raises the notification
91 // a flag is set and the notification is done by hand. If it
92 // has not been successfull -15 is returned.
93 // This is to support return values from Notify()/Reinit()
94 //
95 TObject *notify = GetNotify();
96
97 SetNotify(this);
98
99 Int_t rc = TChain::LoadTree(entry);
100
101 if (rc >= 0 && fNotified && notify)
102 if (!notify->Notify())
103 rc = -15;
104
105 SetNotify(notify);
106
107 return rc;
108 }
109};
110
111// --------------------------------------------------------------------------
112//
113// Default constructor. It creates an TChain instance which represents the
114// the Tree you want to read and adds the given file (if you gave one).
115// More files can be added using MReadTree::AddFile.
116// Also an empty veto list is created. This list is used if you want to
117// veto (disable or "don't enable") a branch in the tree, it vetos also
118// the creation of the corresponding object.
119// An empty list of TObjects are also created. This objects are called
120// at any time the TChain starts to read from another file.
121//
122MReadTree::MReadTree(const char *tname, const char *fname,
123 const char *name, const char *title)
124 : fNumEntry(0), fBranchChoosing(kFALSE), fAutoEnable(kTRUE), fProgress(NULL)
125{
126 fName = name ? name : "MReadTree";
127 fTitle = title ? title : "Task to loop over all events in one single tree";
128
129 fVetoList = new TList;
130 fVetoList->SetOwner();
131
132 fNotify = new TList;
133
134 //
135 // open the input stream
136 //
137 fChain = new MChain(tname);
138
139 // root 3.02:
140 // In TChain::Addfile remove the limitation that the file name must contain
141 // the string ".root". ".root" is necessary only in case one wants to specify
142 // a Tree in a subdirectory of a Root file with eg, the format:
143
144 if (fname)
145 fChain->Add(fname);
146}
147
148// --------------------------------------------------------------------------
149//
150// Destructor. It deletes the TChain and veto list object
151//
152MReadTree::~MReadTree()
153{
154#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,00)
155 if (fChain->GetFile())
156 delete fChain->GetFile();
157#endif
158 //
159 // Delete all the pointers to pointers to the objects where the
160 // branche data gets stored.
161 //
162 TIter Next(fChain->GetStatus());
163
164 TChainElement *element = NULL;
165 while ((element=(TChainElement*)Next()))
166 delete (MParContainer**)element->GetBaddress();
167
168 //
169 // Delete the chain and the veto list
170 //
171 delete fChain;
172 delete fNotify;
173 delete fVetoList;
174}
175
176// --------------------------------------------------------------------------
177//
178// If the owner flag is set all TObjects which are scheduled via
179// AddNotify are deleted by the destructor of MReadTree
180//
181void MReadTree::SetOwner(Bool_t flag)
182{
183 flag ? fNotify->SetBit(kIsOwner) : fNotify->ResetBit(kIsOwner);
184}
185
186// --------------------------------------------------------------------------
187//
188// This function is called each time MReadTree changes the file to read
189// from. It calls all TObject::Notify() functions which are scheduled
190// via AddNotify.
191//
192Bool_t MReadTree::Notify()
193{
194 *fLog << inf << "MReadTree: Notify '" << fChain->GetName() << "' ";
195 *fLog << "(before processing event #" << GetEventNum()-1 << ")" << endl;
196
197 //fNotify->Notify();
198
199 return kTRUE;
200}
201
202// --------------------------------------------------------------------------
203//
204// If you want to read the given tree over several files you must add
205// the files here before PreProcess is called. Be careful: If the tree
206// doesn't have the same contents (branches) it may confuse your
207// program (trees which are are not existing in later files are not read
208// anymore, tree wich are not existing in the first file are never read)
209//
210// Name may use the wildcarding notation, eg "xxx*.root" means all files
211// starting with xxx in the current file system directory.
212//
213// AddFile returns the number of files added to the chain.
214//
215Int_t MReadTree::AddFile(const char *fname)
216{
217 //
218 // FIXME! A check is missing whether the file already exists or not.
219 //
220 //
221 // returns the number of file which were added
222 //
223 return fChain->Add(fname);
224}
225
226// --------------------------------------------------------------------------
227//
228// This function is called if Branch choosing method should get enabled.
229// Branch choosing means, that only the enabled branches are read into
230// memory. To use an enableing scheme we have to disable all branches first.
231// This is done, if this function is called the first time.
232//
233void MReadTree::EnableBranchChoosing()
234{
235 if (fBranchChoosing)
236 return;
237
238 *fLog << inf << "Branch choosing method enabled (only enabled branches are read)." << endl;
239 fChain->SetBranchStatus("*", kFALSE);
240 fBranchChoosing = kTRUE;
241}
242
243// --------------------------------------------------------------------------
244//
245// The first time this function is called all branches are disabled.
246// The given branch is enabled. By enabling only the branches you
247// are processing you can speed up your calculation many times (up to
248// a factor of 10 or 20)
249//
250void MReadTree::EnableBranch(const char *name)
251{
252 EnableBranchChoosing();
253
254 TNamed branch(name, "");
255 SetBranchStatus(&branch, kTRUE);
256}
257
258// --------------------------------------------------------------------------
259//
260// Set branch status of branch name
261//
262void MReadTree::SetBranchStatus(const char *name, Bool_t status)
263{
264 fChain->SetBranchStatus(name, status);
265
266 *fLog << inf << (status ? "Enabled" : "Disabled");
267 *fLog << " subbranch '" << name << "'." << endl;
268}
269
270// --------------------------------------------------------------------------
271//
272// Checks whether a branch with the given name exists in the chain
273// and sets the branch status of this branch corresponding to status.
274//
275void MReadTree::SetBranchStatus(TObject *branch, Bool_t status)
276{
277 //
278 // Get branch name
279 //
280 const char *name = branch->GetName();
281
282 //
283 // Check whether this branch really exists
284 //
285 if (fChain->GetBranch(name))
286 SetBranchStatus(name, status);
287
288 //
289 // Remove trailing '.' if one and try to enable the subbranch without
290 // the master barnch name. This is to be compatible with older mars
291 // and camera files.
292 //
293 const char *dot = strrchr(name, '.');
294 if (!dot)
295 return;
296
297 if (fChain->GetBranch(dot+1))
298 SetBranchStatus(dot+1, status);
299}
300
301// --------------------------------------------------------------------------
302//
303// Set the status of all branches in the list to status.
304//
305void MReadTree::SetBranchStatus(const TList *list, Bool_t status)
306{
307 //
308 // Loop over all subbranches in this master branch
309 //
310 TIter Next(list);
311
312 TObject *obj;
313 while ((obj=Next()))
314 SetBranchStatus(obj, status);
315}
316
317// --------------------------------------------------------------------------
318//
319// This is the implementation of the Auto Enabling Scheme.
320// For more information see MTask::AddBranchToList.
321// This function loops over all tasks and its filters in the tasklist
322// and enables all branches which are requested by the tasks and its
323// filters.
324//
325// To enable 'unknown' branches which are not in the branchlist of
326// the tasks you can call EnableBranch
327//
328void MReadTree::EnableBranches(MParList *plist)
329{
330 //
331 // check whether branch choosing must be switched on
332 //
333 EnableBranchChoosing();
334
335 //
336 // request the tasklist from the parameter list.
337 // FIXME: Tasklist can have a different name
338 //
339 const MTaskList *tlist = (MTaskList*)plist->FindObject("MTaskList");
340 if (!tlist)
341 {
342 *fLog << warn << "Cannot use auto enabeling scheme for branches. 'MTaskList' not found." << endl;
343 return;
344 }
345
346 //
347 // This loop is not necessary. We could do it like in the commented
348 // loop below. But this loop makes sure, that we don't try to enable
349 // one branch several times. This would not harm, but we would get
350 // an output for each attempt. To have several outputs for one subbranch
351 // may confuse the user, this we don't want.
352 // This loop creates a new list of subbranches and for each branch
353 // which is added we check before whether it already exists or not.
354 //
355 TList list;
356
357 MTask *task;
358 TIter NextTask(tlist->GetList());
359 while ((task=(MTask*)NextTask()))
360 {
361 TObject *obj;
362
363 TIter NextTBranch(task->GetListOfBranches());
364 while ((obj=NextTBranch()))
365 if (!list.FindObject(obj->GetName()))
366 list.Add(obj);
367
368 const MFilter *filter = task->GetFilter();
369
370 if (!filter)
371 continue;
372
373 TIter NextFBranch(filter->GetListOfBranches());
374 while ((obj=NextFBranch()))
375 if (!list.FindObject(obj->GetName()))
376 list.Add(obj);
377 }
378
379 SetBranchStatus(&list, kTRUE);
380/*
381 //
382 // Loop over all tasks iand its filters n the task list.
383 //
384 MTask *task;
385 TIter NextTask(tlist->GetList());
386 while ((task=(MTask*)NextTask()))
387 {
388 SetBranchStatus(task->GetListOfBranches(), kTRUE);
389
390 const MFilter *filter = task->GetFilter();
391 if (!filter)
392 continue;
393
394 SetBranchStatus(filter->GetListOfBranches(), kTRUE);
395
396 }
397*/
398}
399
400// --------------------------------------------------------------------------
401//
402// The disables all subbranches of the given master branch.
403//
404void MReadTree::DisableSubBranches(TBranch *branch)
405{
406 //
407 // This is not necessary, it would work without. But the output
408 // may confuse the user...
409 //
410 if (fAutoEnable || fBranchChoosing)
411 return;
412
413 SetBranchStatus(branch->GetListOfBranches(), kFALSE);
414}
415
416// --------------------------------------------------------------------------
417//
418// The PreProcess loops (till now) over the branches in the given tree.
419// It checks if the corresponding containers (containers with the same
420// name than the branch name) are existing in the Parameter Container List.
421// If not, a container of objec type 'branch-name' is created (everything
422// after the last semicolon in the branch name is stripped). Only
423// branches which don't have a veto (see VetoBranch) are enabled If the
424// object isn't found in the root dictionary (a list of classes known by the
425// root environment) the branch is skipped and an error message is printed
426// out.
427//
428Bool_t MReadTree::PreProcess(MParList *pList)
429{
430 //
431 // Make sure, that all the following calls doesn't result in
432 // Notifications. This may be dangerous, because the notified
433 // tasks are not preprocessed.
434 //
435 fChain->SetNotify(NULL);
436
437 //
438 // get number of events in this tree
439 //
440 fNumEntries = (UInt_t)fChain->GetEntries();
441
442 if (!fNumEntries)
443 {
444 *fLog << warn << dbginf << "No entries found in file(s)" << endl;
445 return kFALSE;
446 }
447
448 //
449 // output logging information
450 //
451 *fLog << inf << fNumEntries << " entries found in file(s)." << endl;
452
453 //
454 // Get all branches of this tree and
455 // create the Iterator to loop over all branches
456 //
457 TIter Next(fChain->GetListOfBranches());
458 TBranch *branch=NULL;
459
460 Int_t num=0;
461 //
462 // loop over all tasks for processing
463 //
464 while ( (branch=(TBranch*)Next()) )
465 {
466 //
467 // Get Name of Branch and Object
468 //
469 const char *bname = branch->GetName();
470
471 TString oname(bname);
472 if (oname.EndsWith("."))
473 oname.Remove(oname.Length()-1);
474
475 //
476 // Check if enabeling the branch is allowed
477 //
478 if (fVetoList->FindObject(oname))
479 {
480 *fLog << inf << "Master branch " << bname << " has veto... skipped." << endl;
481 DisableSubBranches(branch);
482 continue;
483 }
484
485 //
486 // Create a pointer to the pointer to the object in which the
487 // branch data is stored. The pointers are stored in the TChain
488 // object and we get the pointers from there to delete it.
489 //
490 MParContainer **pcont= new MParContainer*;
491
492#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
493 const char *classname = oname;
494#else
495 const char *classname = branch->GetClassName();
496#endif
497
498 //
499 // check if object is existing in the list
500 //
501 *pcont=pList->FindCreateObj(classname, oname);
502
503 if (!*pcont)
504 {
505 //
506 // if class is not existing in the (root) environment
507 // we cannot proceed reading this branch
508 //
509 *fLog << warn << dbginf << "Warning: Class '" << classname;
510 *fLog << "' for " << oname << " not existing in dictionary. Branch skipped." << endl;
511 DisableSubBranches(branch);
512 continue;
513 }
514
515 //
516 // Check whether a Pointer to a pointer already exists, if
517 // we created one already delete it.
518 //
519 TChainElement *element = (TChainElement*)fChain->GetStatus()->FindObject(bname);
520 if (element)
521 delete (MParContainer**)element->GetBaddress();
522
523 //
524 // here pcont is a pointer the to container in which the data from
525 // the actual branch should be stored - enable branch.
526 //
527 fChain->SetBranchAddress(bname, pcont);
528
529 *fLog << inf << "Master branch address " << bname << " [";
530 *fLog << classname << "] setup for reading." << endl;
531
532 //*fLog << "Branch " << bname << " autodel: " << (int)branch->IsAutoDelete() << endl;
533 //branch->SetAutoDelete();
534
535 num++;
536 }
537
538 *fLog << inf << "MReadTree setup " << num << " master branches addresses." << endl;
539
540 //
541 // If auto enabling scheme isn't disabled, do auto enabling
542 //
543 if (fAutoEnable)
544 EnableBranches(pList);
545
546 //
547 // If a progress bar is given set its range.
548 //
549 if (fProgress)
550 fProgress->SetRange(0, fNumEntries);
551
552 //
553 // Now we can start notifying. Reset tree makes sure, that TChain thinks
554 // that the correct file is not yet initialized and reinitilizes it
555 // as soon as the first event is read. This is necessary to call
556 // the notifiers when the first event is read, but after the
557 // PreProcess-function.
558 //
559 fChain->ResetTree();
560 fChain->SetNotify(this);
561
562 return kTRUE;
563}
564
565// --------------------------------------------------------------------------
566//
567// Set the ready to save flag of all containers which branchaddresses are
568// set for. This is necessary to copy data.
569//
570void MReadTree::SetReadyToSave(Bool_t flag)
571{
572 TIter Next(fChain->GetStatus());
573
574 TChainElement *element = NULL;
575 while ((element=(TChainElement*)Next()))
576 {
577 //
578 // Check whether the branch is enabled
579 //
580 if (!element->GetStatus())
581 continue;
582
583 //
584 // Get the pointer to the pointer of the corresponding container
585 //
586 MParContainer **pcont = (MParContainer**)element->GetBaddress();
587
588 //
589 // Check whether the pointer is not NULL
590 //
591 if (!pcont || !*pcont)
592 continue;
593
594 //
595 // Set the ready to save status of the container.
596 //
597 (*pcont)->SetReadyToSave(flag);
598 }
599
600 //
601 // Set the ready to save status of this task (used?), too
602 //
603 MTask::SetReadyToSave(flag);
604}
605
606// --------------------------------------------------------------------------
607//
608// The Process-function reads one event from the tree (this contains all
609// enabled branches) and increases the position in the file by one event.
610// (Remark: The position can also be set by some member functions
611// If the end of the file is reached the Eventloop is stopped.
612//
613#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
614#include "MRawEvtData.h"
615#endif
616Bool_t MReadTree::Process()
617{
618 //
619 // This is necessary due to a bug in TChain::LoadTree in root.
620 // will be fixed in 3.03
621 //
622#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,01)
623 if (fNumEntry >= fNumEntries)
624 return kFALSE;
625#endif
626
627#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
628 //
629 // This fixes 99.9% of a memory leak using a root version prior
630 // to 3.02/??
631 //
632 TChainElement *element=NULL;
633 TIter Next(fChain->GetStatus());
634 while ((element=(TChainElement*)Next()))
635 {
636 MParContainer **c = (MParContainer**)element->GetBaddress();
637 if (!c) continue;
638 if ((*c)->InheritsFrom(MRawEvtData::Class()))
639 ((MRawEvtData*)(*c))->DeletePixels(kFALSE);
640
641 }
642#endif
643
644 Bool_t rc = fChain->GetEntry(fNumEntry++) != 0;
645
646 if (rc)
647 SetReadyToSave();
648
649 return rc;
650}
651
652// --------------------------------------------------------------------------
653//
654// Get the Event with the current EventNumber fNumEntry
655//
656Bool_t MReadTree::GetEvent()
657{
658 Bool_t rc = fChain->GetEntry(fNumEntry) != 0;
659
660 if (rc)
661 SetReadyToSave();
662
663 return rc;
664}
665
666// --------------------------------------------------------------------------
667//
668// Decrease the number of the event which is read by Process() next
669// by one or more
670//
671Bool_t MReadTree::DecEventNum(UInt_t dec)
672{
673 if (fNumEntry-dec >= fNumEntries)
674 {
675 *fLog << warn << "MReadTree::DecEventNum: WARNING - Event " << fNumEntry << "-";
676 *fLog << dec << "=" << (Int_t)fNumEntry-dec << " out of Range." << endl;
677 return kFALSE;
678 }
679
680 fNumEntry -= dec;
681 return kTRUE;
682}
683
684// --------------------------------------------------------------------------
685//
686// Increase the number of the event which is read by Process() next
687// by one or more
688//
689Bool_t MReadTree::IncEventNum(UInt_t inc)
690{
691 if (fNumEntry+inc >= fNumEntries)
692 {
693 *fLog << warn << "MReadTree::IncEventNum: WARNING - Event " << fNumEntry << "+";
694 *fLog << inc << "=" << (Int_t)fNumEntry+inc << " out of Range." << endl;
695 return kFALSE;
696 }
697
698 fNumEntry += inc;
699 return kTRUE;
700}
701
702// --------------------------------------------------------------------------
703//
704// This function makes Process() read event number nr next
705//
706// Remark: You can use this function after instatiating you MReadTree-object
707// to set the event number from which you want to start reading.
708//
709Bool_t MReadTree::SetEventNum(UInt_t nr)
710{
711 if (nr >= fNumEntries)
712 {
713 *fLog << warn << "MReadTree::SetEventNum: WARNING - " << nr << " out of Range." << endl;
714 return kFALSE;
715 }
716
717 fNumEntry = nr;
718 return kTRUE;
719}
720
721// --------------------------------------------------------------------------
722//
723// For the branch with the given name:
724// 1) no object is automatically created
725// 2) the branch address for this branch is not set
726// (because we lack the object, see 1)
727// 3) The whole branch (exactly: all its subbranches) are disabled
728// this means are not read in memory by TTree:GetEntry
729//
730void MReadTree::VetoBranch(const char *name)
731{
732 fVetoList->Add(new TNamed(name, ""));
733}
734
735// --------------------------------------------------------------------------
736//
737// Return the name of the file we are actually reading from.
738//
739TString MReadTree::GetFileName() const
740{
741 const TFile *file = fChain->GetFile();
742
743 if (!file)
744 return TString("<unknown>");
745
746 TString name(file->GetName());
747 name.Remove(0, name.Last('/')+1);
748 return name;
749}
750
751// --------------------------------------------------------------------------
752//
753// Return the number of the file in the chain, -1 in case of an error
754//
755Int_t MReadTree::GetFileIndex() const
756{
757 return fChain->GetTreeNumber();
758 /*
759 const TString filename = fChain->GetFile()->GetName();
760
761 int i=0;
762 TObject *file = NULL;
763
764 TIter Next(fChain->GetListOfFiles());
765 while ((file=Next()))
766 {
767 if (filename==gSystem->ExpandPathName(file->GetTitle()))
768 return i;
769 i++;
770 }
771 return -1;
772 */
773}
774
775// --------------------------------------------------------------------------
776//
777// This schedules a TObject which Notify(9 function is called in case
778// of MReadTree (TChain) switches from one file in the chain to another
779// one.
780//
781void MReadTree::AddNotify(TObject *obj)
782{
783 fNotify->Add(obj);
784}
785
786void MReadTree::Print(Option_t *o) const
787{
788 *fLog << all << GetDescriptor() << dec << endl;
789 *fLog << setfill('-') << setw(strlen(GetDescriptor())) << "" << endl;
790 *fLog << " Files [Tree]:" << endl;
791
792 int i = 0;
793 TIter Next(fChain->GetListOfFiles());
794 TObject *obj = NULL;
795 while ((obj=Next()))
796 *fLog << " " << i++ << ") " << obj->GetTitle() << " [" << obj->GetName() << "]" << endl;
797
798 *fLog << " Total Number of Entries: " << fNumEntries << endl;
799 *fLog << " Next Entry to read: " << fNumEntry << endl;
800}
Note: See TracBrowser for help on using the repository browser.