3 //**************************************************************************
4 //* This file is property of and copyright by the ALICE HLT Project *
5 //* ALICE Experiment at CERN, All rights reserved. *
7 //* Primary Authors: Timur Pocheptsov <Timur.Pocheptsov@cern.ch> *
8 //* Matthias Richter <Matthias.Richter@cern.ch>
9 //* for The ALICE HLT Project. *
11 //* Permission to use, copy, modify and distribute this software and its *
12 //* documentation strictly for non-commercial purposes is hereby granted *
13 //* without fee, provided that the above copyright notice appears in all *
14 //* copies and that both the copyright notice and this permission notice *
15 //* appear in the supporting documentation. The authors make no claims *
16 //* about the suitability of this software for any purpose. It is *
17 //* provided "as is" without express or implied warranty. *
18 //**************************************************************************
20 /// @file AliHLTTTreeProcessor.cxx
21 /// @author Timur Pocheptsov, Matthias Richter
23 /// @brief Generic component for data collection in a TTree
28 #include "AliHLTTTreeProcessor.h"
29 #include "AliHLTErrorGuard.h"
30 #include "TDirectory.h"
35 #include "TStopwatch.h"
40 /** ROOT macro for the implementation of ROOT specific class methods */
41 ClassImp(AliHLTTTreeProcessor)
43 AliHLTTTreeProcessor::AliHLTTTreeProcessor()
47 fMaxEntries(kMaxEntries),
48 fPublishInterval(kInterval),
55 fForcedEventsCount(0),
56 fSkippedEventsCount(0),
62 // see header file for class documentation
64 // refer to README to build package
66 // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt
69 const AliHLTUInt32_t AliHLTTTreeProcessor::fgkTimeScale=1000000; // ticks per second
71 AliHLTTTreeProcessor::~AliHLTTTreeProcessor()
73 // see header file for class documentation
76 AliHLTComponentDataType AliHLTTTreeProcessor::GetOutputDataType()
78 // get the component output data type
79 return kAliHLTDataTypeHistogram;
82 void AliHLTTTreeProcessor::GetOutputDataSize(unsigned long& constBase, double& inputMultiplier)
84 // get the output size estimator
86 if (!fDefinitions.size()) {
87 HLTError("Can not calculate output data size, no histogram definitions were provided");
92 for (list_const_iterator i = fDefinitions.begin(); i != fDefinitions.end(); ++i)
93 constBase += i->GetSize();
98 int AliHLTTTreeProcessor::DoInit(int argc, const char** argv)
101 // ask child to create the tree.
104 // component configuration
105 //Stage 1: default initialization.
106 //"Default" (for derived component) histograms.
107 FillHistogramDefinitions();
109 fMaxEntries = kMaxEntries;
110 fPublishInterval = kInterval;
113 TString cdbPath("HLT/ConfigHLT/");
114 cdbPath += GetComponentID();
116 iResult = ConfigureFromCDBTObjString(cdbPath);
120 //Stage 3: command line arguments.
121 if (argc && (iResult = ConfigureFromArgumentString(argc, argv)) < 0)
124 // calculating a unique id from the hostname and process id
125 // used for identifying output of multiple components
126 TUUID guid = GenerateGUID();
133 fUniqueId = bufAsInt[0];
136 // originally foreseen to pass the arguments to the function, however
137 // this is not appropriate. Argument scan via overloaded function
138 // ScanConfigurationArgument
139 std::auto_ptr<TTree> ptr(CreateTree(0, NULL));
141 ptr->SetDirectory(0);
142 ptr->SetCircular(fMaxEntries);
143 fTree = ptr.release();
144 } else //No way to process error correctly - error is unknown here.
147 HLTError("fTree pointer must be null before DoInit call");
151 if (iResult>=0 && fMaxEventTime>0) {
152 fpEventTimer=new TStopwatch;
154 fpEventTimer->Reset();
156 fpCycleTimer=new TStopwatch;
158 fpCycleTimer->Reset();
161 fSkippedEventsCount=0;
166 int AliHLTTTreeProcessor::DoDeinit()
171 fDefinitions.clear();
173 if (fpEventTimer) delete fpEventTimer;
175 if (fpCycleTimer) delete fpCycleTimer;
181 int AliHLTTTreeProcessor::DoEvent(const AliHLTComponentEventData& evtData, AliHLTComponentTriggerData& trigData)
183 //Process event and publish histograms.
184 AliHLTUInt32_t eventType=0;
185 if (!IsDataEvent(&eventType) && eventType!=gkAliEventTypeEndOfRun) return 0;
187 //I'm pretty sure, that if fTree == 0 (DoInit failed) DoEvent is not called.
188 //But interface itself does not force you to call DoInit before DoEvent, so,
189 //I make this check explicit.
191 HLTError("fTree is a null pointer, try to call AliHLTTTreeProcessor::DoInit first.");
192 return -EINVAL;//-ENULLTREE? :)
195 AliHLTUInt32_t averageEventTime=0;
196 AliHLTUInt32_t averageCycleTime=0;
200 bool bDoFilling=true;
201 bool bDoPublishing=false;
202 const int cycleResetInterval=1000;
203 if (fpEventTimer && fpCycleTimer) {
204 averageEventTime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale)/(GetEventCount()+1);
205 fillingtime=int(fpEventTimer->RealTime()*fgkTimeScale);
206 publishtime=fillingtime;
207 fpEventTimer->Start(kFALSE);
208 fpCycleTimer->Stop();
209 averageCycleTime=AliHLTUInt32_t(fpCycleTimer->RealTime()*fgkTimeScale)/((GetEventCount()%cycleResetInterval)+1);
210 // adapt processing to 3/4 of the max time
211 bDoFilling=4*averageEventTime<3*fMaxEventTime ||
212 (averageEventTime<fCycleTimeFactor*averageCycleTime && fpCycleTimer->RealTime()>fIgnoreCycleTime);
213 if (fNofEventsForce>0 && fForcedEventsCount<fNofEventsForce) {
214 fForcedEventsCount++;
219 // FIXME: there is still an unclear increase in memory consumption, even if the number of entries
220 // in the tree is restricted. Valgrind studies did not show an obvious memory leak. This is likely
221 // to be caused by something deep in the Root TTree functionality and needs to be studied in detail.
223 gSystem->GetProcInfo(&ProcInfo);
224 if (ProcInfo.fMemResident>fMaxMemory) bDoFilling=false;
226 // process input data blocks and fill the tree
228 if (eventType!=gkAliEventTypeEndOfRun) {
229 if (bDoFilling) {iResult=FillTree(fTree, evtData, trigData); fNewEventsCount++;}
230 else fSkippedEventsCount++;
233 fpEventTimer->Stop();
234 fillingtime=int(fpEventTimer->RealTime()*fgkTimeScale)-fillingtime;
235 if (fillingtime<0) fillingtime=0;
236 fpEventTimer->Start(kFALSE);
240 ALIHLTERRORGUARD(5, "FillTree failed with %d, first event %d", iResult, GetEventCount());
246 if (( time.Get() - fLastTime > fPublishInterval && fNewEventsCount>0) ||
247 eventType==gkAliEventTypeEndOfRun) {
248 if ((bDoPublishing=fLastTime>0)) { // publish earliest after the first interval but set the timer
250 for (list_const_iterator i = fDefinitions.begin(); i != fDefinitions.end(); ++i) {
251 if (TH1* h = CreateHistogram(*i)) {
252 //I do not care about errors here - since I'm not able
253 //to rollback changes.
254 // TODO: in case of -ENOSPC et the size of the last object by calling
255 // GetLastObjectSize() and accumulate the necessary output buffer size
256 PushBack(h, GetOriginDataType(), GetDataSpec());
260 unsigned eventcount=GetEventCount()+1;
261 HLTBenchmark("publishing %d histograms, %d entries in tree, %d new events since last publishing, accumulated %d of %d events (%.1f%%)", fDefinitions.size(), fTree->GetEntriesFast(), fNewEventsCount, eventcount-fSkippedEventsCount, eventcount, eventcount>0?(100*float(eventcount-fSkippedEventsCount)/eventcount):0);
263 HLTBenchmark("current memory usage %d %d", ProcInfo.fMemResident, ProcInfo.fMemVirtual);
266 fLastTime=time.Get();
268 // choose a random offset at beginning to equalize traffic for multiple instances
270 gRandom->SetSeed(fUniqueId);
271 fLastTime-=gRandom->Integer(fPublishInterval);
276 fpEventTimer->Stop();
277 publishtime=int(fpEventTimer->RealTime()*fgkTimeScale)-publishtime;
278 if (publishtime>fillingtime) publishtime-=fillingtime;
281 averageEventTime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale)/(GetEventCount()+1);
283 // info output once every 5 seconds
284 static UInt_t lastTime=0;
285 if (time.Get()-lastTime>5 ||
286 eventType==gkAliEventTypeEndOfRun ||
289 unsigned eventcount=GetEventCount()+1;
290 HLTBenchmark("filling time %d us, publishing time %d, average total processing time %d us, cycle time %d us, accumulated %d of %d events (%.1f%%)", fillingtime, publishtime, averageEventTime, averageCycleTime, eventcount-fSkippedEventsCount, eventcount, eventcount>0?(100*float(eventcount-fSkippedEventsCount)/eventcount):0);
294 bool bReset=(GetEventCount()%cycleResetInterval)==0;
295 fpCycleTimer->Start(bReset);
301 int AliHLTTTreeProcessor::ScanConfigurationArgument(int argc, const char** argv)
303 // scan one argument and its parameters from the list
304 // return number of processed entries.
305 // possible arguments:
306 // -maxentries number
308 // -histogram name -size number -expression expression [-title expression ] -cut expression ][-opt option]
309 // As soon as "-histogram" found, -size and -expression and -outtype are required,
310 // cut and option can be omitted.
314 std::list<AliHLTHistogramDefinition> newDefs;
315 AliHLTHistogramDefinition def;
318 int maxEntries = fMaxEntries;
321 const TString argument(argv[i]);
323 if (argument.CompareTo("-maxentries") == 0) { //1. Max entries argument for TTree.
325 HLTError("Numeric value for '-maxentries' is expected");
328 //Next must be a number.
329 //TString returns 0 (number) even if string contains non-numeric symbols.
330 maxEntries = TString(argv[i + 1]).Atoi();
331 if (maxEntries <= 0) {
332 HLTError("Bad value for '-maxentries': %d", maxEntries);
337 } else if (argument.CompareTo("-interval") == 0) { //2. Interval argument for publishing.
339 HLTError("Numeric value for '-interval' is expected");
343 const Int_t interval = TString(argv[i + 1]).Atoi();
345 HLTError("Bad value for '-interval' argument: %d", interval);
349 fPublishInterval = interval;
352 } else if (argument.CompareTo("-maxeventtime") == 0) { // max average processing time in us
354 HLTError("Numeric value for '-maxeventtime' is expected");
358 const Int_t time = TString(argv[i + 1]).Atoi();
360 HLTError("Bad value for '-maxeventtime' argument: %d", time);
364 fMaxEventTime = time;
367 } else if (argument.CompareTo("-forced-events") == 0) { // number of forced events
369 HLTError("Numeric value for '-forced-events' is expected");
373 const Int_t count = TString(argv[i + 1]).Atoi();
375 HLTError("Bad value for '-forced-events' argument: %d", count);
379 fNofEventsForce = count;
380 fForcedEventsCount=0;
383 } else if (argument.CompareTo("-ignore-cycletime") == 0) { // ignore cycle time for n sec
385 HLTError("Numeric value for '-ignore-cycletime' is expected");
389 const Int_t time = TString(argv[i + 1]).Atoi();
391 HLTError("Bad value for '-ignore-cycletime' argument: %d", time);
395 fIgnoreCycleTime = time;
397 } else if (argument.CompareTo("-maxmemory") == 0) { // maximum of memory in kByte to be used by the component
399 HLTError("Numeric value for '-maxmemory' is expected");
403 const Int_t mem = TString(argv[i + 1]).Atoi();
405 HLTError("Bad value for '-maxmemory' argument: %d", time);
411 } else if (argument.CompareTo("-cycletime-factor") == 0) { // weight factor for cycle time
413 HLTError("Numeric value for '-cycletime-factor' is expected");
417 const Float_t factor = TString(argv[i + 1]).Atof();
419 HLTError("Bad value for '-cycletime-factor' argument: %f", factor);
423 fCycleTimeFactor = factor;
425 } else if (argument.CompareTo("-histogram") == 0) { //3. Histogramm definition.
426 const int nParsed = ParseHistogramDefinition(argc, argv, i, def);
430 newDefs.push_back(def);
434 HLTError("Unknown argument %s", argument.Data());
439 if (maxEntries != fMaxEntries) {
440 fMaxEntries = maxEntries;
443 fTree->SetCircular(fMaxEntries);
448 fDefinitions.swap(newDefs);
453 TH1* AliHLTTTreeProcessor::CreateHistogram(const AliHLTHistogramDefinition& d)
456 // create a histogram from the tree
458 HLTError("fTree is a null pointer, try to call AliHLTTTreeProcessor::DoInit first.");
462 TString histName(d.GetName());
463 if (!histName.Contains("(")) {
464 //Without number of bins, the histogram will be "fixed"
465 //and most of values can go to underflow/overflow bins,
466 //since kCanRebin will be false.
467 histName += TString::Format("(%d)", Int_t(kDefaultNBins));
470 const Long64_t rez = fTree->Project(histName.Data(), d.GetExpression().Data(), d.GetCut().Data(), d.GetDrawOption().Data());
473 HLTError("TTree::Project failed");
477 //Now, cut off the binning part of a name
478 histName = histName(0, histName.Index("("));
479 TH1 * hist = dynamic_cast<TH1*>(gDirectory->Get(histName.Data()));
481 const TString msg(Form("Hist %s is a null pointer, selection was %s, strange name or hist's type\n", histName.Data(), d.GetExpression().Data()));
482 HLTError(msg.Data());
483 }else if (d.GetDrawOption().Length()) {
484 hist->SetOption(d.GetDrawOption().Data());
487 //Reformatting the histogram name
488 TString str2=d.GetCut().Data();
489 str2.ReplaceAll("Track_", "");
490 str2.ReplaceAll("&&", " ");
491 str2 = histName+" "+str2;
492 hist->SetTitle(str2);
494 if(d.GetTitle().Length()){
496 //removing underscore
498 string str=d.GetTitle().Data();
499 found=str.find_first_of("_");
500 if(!(d.GetExpression().CompareTo("Track_pt"))){
501 found=str.find_first_of("_",found+1);
505 sprintf(axis,"%s",str.c_str());
507 hist->SetXTitle(axis);
508 hist->GetXaxis()->CenterTitle();
513 int AliHLTTTreeProcessor::ParseHistogramDefinition(int argc, const char** argv, int pos, AliHLTHistogramDefinition& dst)const
515 //Histogram-definition:
516 // -histogram name -size number -expression expression [-title expression][-cut expression][-opt option]
518 //at pos we have '-histogram', at pos + 1 must be the name.
519 if (pos + 1 == argc) {
520 HLTError("Bad histogram definition, histogram name is expected");
524 dst.SetName(argv[pos + 1]);
527 //At pos must be '-size', and number at pos + 1.
528 if (pos == argc || TString(argv[pos]).CompareTo("-size")) {
529 HLTError("Bad histogram definition, '-size' is expected");
533 if (pos + 1 == argc) {
534 HLTError("Bad histogram definition, size is expected");
538 dst.SetSize(TString(argv[pos + 1]).Atoi());
539 if (dst.GetSize() <= 0) {
540 HLTError("Bad histogram definition, positive size is required");
545 //At pos must be '-expression', and expression at pos + 1.
546 if (pos == argc || TString(argv[pos]).CompareTo("-expression")) {
547 HLTError("Bad histogram definition, '-expression' is expected");
551 if (pos + 1 == argc) {
552 HLTError("Bad histogram definition, expression is expected");
556 dst.SetExpression(argv[pos + 1]);
562 dst.SetDrawOption("");
564 //remaining options can be the title, cut and Draw option.
565 //title must be first
566 if (pos + 1 >= argc){
569 if (TString(argv[pos]).CompareTo("-title") == 0) {
570 dst.SetTitle(argv[pos + 1]);
575 //cut must be second.
579 if (TString(argv[pos]).CompareTo("-cut") == 0) {
580 dst.SetCut(argv[pos + 1]);
588 if (TString(argv[pos]).CompareTo("-opt") == 0) {
589 dst.SetDrawOption(argv[pos + 1]);