New developments of the analysis framework - selectorised version of the manager...
[u/mrichter/AliRoot.git] / ANALYSIS / AliAnalysisTask.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15
16 /* $Id$ */
17 // Author: Andrei Gheata, 31/05/2006
18
19 //==============================================================================
20 //   AliAnalysysTask - Class representing a basic analysis task. Any
21 // user-defined task should derive from it and implement the Exec() virtual
22 // method.
23 //==============================================================================
24 //
25 // A specific user analysis task have to derive from this class. The list of
26 // specific input and output slots have to be defined in the derived class ctor:
27 //
28 //   UserTask::UserTask(name, title)
29 //   {
30 //      DefineInput(0, TTree::Class());
31 //      DefineInput(1, TH1::Class());
32 //      ...
33 //      DefineOutput(0, TTree::Class());
34 //      DefineOutput(1, MyObject::Class());
35 //      ...
36 //   }
37 //
38 // An existing data contaner (AliAnalysisDataContainer) can be connected to the
39 // input/output slots of an analysis task. Containers should not be defined and
40 // connected by the derived analysis task, but from the level of AliAnalysisManager:
41 //
42 //   AliAnalysisManager::ConnectInput(AliAnalysisTask *task, Int_t islot,
43 //                                   AliAnalysisDataContainer *cont)
44 //   AliAnalysisManager::ConnectOutput(AliAnalysisTask *task, Int_t islot,
45 //                                    AliAnalysisDataContainer *cont)
46 // To connect a slot to a data container, the data types declared by both must
47 // match.
48 //
49 // The method Init will be called once per session at the moment when the data is
50 // available at all input slots. 
51 // The method Init() has to be overloaded by the derived class in order to:
52 // 1. Define objects that should be created only once per session (e.g. output
53 //    histograms)
54 // 2. Set the branch address or connect to a branch address in case the input
55 //    slots are connected to trees.
56 //
57 // Example:
58 // MyAnalysisTask::Init(Option_t *)
59 // {
60 //    if (!fHist1) fHist1 = new TH1F("h1", ....);
61 //    if (!fHist2) fHist2 = new TH1F("h2", ....);
62 //    fESD = GetBranchAddress(islot=0, "ESD");
63 //    if (!fESD) SetBranchAddress(islot=0, "ESD", &fESD);
64 // }
65 //
66 // The method Terminate() will be called by the framework once at the end of
67 // data processing. Overload this if needed.
68 //
69 //==============================================================================
70
71 #include "TClass.h"
72
73 #include "AliLog.h"
74 #include "AliAnalysisTask.h"
75 #include "AliAnalysisDataSlot.h"
76 #include "AliAnalysisDataContainer.h"
77
78 ClassImp(AliAnalysisTask)
79
80 //______________________________________________________________________________
81 AliAnalysisTask::AliAnalysisTask()
82                 :fReady(kFALSE),
83                  fInitialized(kFALSE),
84                  fNinputs(0),
85                  fNoutputs(0),
86                  fOutputReady(NULL),
87                  fPublishedData(NULL),
88                  fInputs(NULL),
89                  fOutputs(NULL)
90 {
91 // Default constructor.
92 }
93
94 //______________________________________________________________________________
95 AliAnalysisTask::AliAnalysisTask(const char *name, const char *title)
96                 :TTask(name,title),
97                  fReady(kFALSE),
98                  fInitialized(kFALSE),
99                  fNinputs(0),
100                  fNoutputs(0),
101                  fOutputReady(NULL),
102                  fPublishedData(NULL),
103                  fInputs(NULL),
104                  fOutputs(NULL)                 
105 {
106 // Constructor.
107    fInputs      = new TObjArray(2);
108    fOutputs     = new TObjArray(2);
109 }
110
111 //______________________________________________________________________________
112 AliAnalysisTask::AliAnalysisTask(const AliAnalysisTask &task)
113                 :TTask(task),
114                  fReady(task.fReady),
115                  fInitialized(task.fInitialized),
116                  fNinputs(task.fNinputs),
117                  fNoutputs(task.fNoutputs),                 
118                  fOutputReady(NULL),
119                  fPublishedData(NULL),
120                  fInputs(NULL),
121                  fOutputs(NULL)                 
122 {
123 // Copy ctor.
124    fInputs      = new TObjArray((fNinputs)?fNinputs:2);
125    fOutputs     = new TObjArray((fNoutputs)?fNoutputs:2);
126    fPublishedData = 0;
127    Int_t i;
128    for (i=0; i<fNinputs; i++) fInputs->AddAt(task.GetInputSlot(i),i);
129    fOutputReady = new Bool_t[(fNoutputs)?fNoutputs:2];
130    for (i=0; i<fNoutputs; i++) {
131       fOutputReady[i] = IsOutputReady(i);
132       fOutputs->AddAt(task.GetOutputSlot(i),i);
133    }   
134 }
135
136 //______________________________________________________________________________
137 AliAnalysisTask::~AliAnalysisTask()
138 {
139 // Dtor.
140    if (fInputs)  {fInputs->Delete(); delete fInputs;}
141    if (fOutputs) {fOutputs->Delete(); delete fOutputs;}
142 }   
143   
144 //______________________________________________________________________________
145 AliAnalysisTask& AliAnalysisTask::operator=(const AliAnalysisTask& task)
146 {
147 // Assignment
148    if (&task == this) return *this;
149    TTask::operator=(task);
150    fReady       = task.IsReady();
151    fInitialized = task.IsInitialized();
152    fNinputs     = task.GetNinputs();
153    fNoutputs    = task.GetNoutputs();
154    fInputs      = new TObjArray((fNinputs)?fNinputs:2);
155    fOutputs     = new TObjArray((fNoutputs)?fNoutputs:2);
156    fPublishedData = 0;
157    Int_t i;
158    for (i=0; i<fNinputs; i++) fInputs->AddAt(new AliAnalysisDataSlot(*task.GetInputSlot(i)),i);
159    fOutputReady = new Bool_t[(fNoutputs)?fNoutputs:2];
160    for (i=0; i<fNoutputs; i++) {
161       fOutputReady[i] = IsOutputReady(i);
162       fOutputs->AddAt(new AliAnalysisDataSlot(*task.GetOutputSlot(i)),i);
163    }         
164    return *this;
165 }
166
167 //______________________________________________________________________________
168 Bool_t AliAnalysisTask::AreSlotsConnected()
169 {
170 // Check if all input/output slots are connected. If this is the case fReady=true
171    fReady = kFALSE;
172    if (!fNinputs || !fNoutputs) return kFALSE;
173    Int_t i;
174    AliAnalysisDataSlot *slot;
175    for (i=0; i<fNinputs; i++) {
176       slot = (AliAnalysisDataSlot*)fInputs->At(i);
177       if (!slot) {
178          AliError(Form("Input slot %i of task %s not defined !",i,GetName()));
179          return kFALSE;
180       }   
181       if (!slot->IsConnected()) return kFALSE;
182    }   
183    for (i=0; i<fNoutputs; i++) {
184       slot = (AliAnalysisDataSlot*)fOutputs->At(i);
185       if (!slot) {
186          AliError(Form("Output slot %i of task %s not defined !",i,GetName()));
187          return kFALSE;
188       }   
189       if (!slot->IsConnected()) return kFALSE;
190    } 
191    fReady = kTRUE;  
192    return kTRUE;
193 }
194
195 //______________________________________________________________________________
196 void AliAnalysisTask::CheckNotify(Bool_t init)
197 {
198 // Check if data is available from all inputs. Change the status of the task
199 // accordingly. This method is called automatically for all tasks connected
200 // to a container where the data was published.
201    if (init) fInitialized = kFALSE;
202    for (Int_t islot=0; islot<fNinputs; islot++) {
203       if (!GetInputData(islot)) {
204          SetActive(kFALSE);
205          return;
206       }   
207    }   
208    SetActive(kTRUE);
209    if (fInitialized) return;
210    TDirectory *cursav = gDirectory;
211    Init();
212    if (cursav) cursav->cd();
213    fInitialized = kTRUE;
214 }
215
216 //______________________________________________________________________________
217 Bool_t AliAnalysisTask::ConnectInput(Int_t islot, AliAnalysisDataContainer *cont)
218 {
219 // Connect an input slot to a data container.
220    AliAnalysisDataSlot *input = GetInputSlot(islot);
221    if (!input) {
222       AliError(Form("Input slot %i not defined for analysis task %s", islot, GetName()));
223       return kFALSE;
224    }
225    // Check type matching          
226    if (!input->GetType()->InheritsFrom(cont->GetType())) {
227       AliError(Form("Data type %s for input %i of task %s not matching container %s of type %s",
228                input->GetType()->GetName(), islot, GetName(), cont->GetName(), cont->GetType()->GetName()));
229       return kFALSE;
230    }  
231    // Connect the slot to the container as input          
232    if (!input->ConnectContainer(cont)) return kFALSE;
233    // Add this to the list of container consumers
234    cont->AddConsumer(this, islot);
235    AreSlotsConnected();
236    return kTRUE;
237 }   
238
239 //______________________________________________________________________________
240 Bool_t AliAnalysisTask::ConnectOutput(Int_t islot, AliAnalysisDataContainer *cont)
241 {
242 // Connect an output slot to a data container.
243    AliAnalysisDataSlot *output = GetOutputSlot(islot);
244    if (!output) {
245       AliError(Form("Output slot %i not defined for analysis task %s", islot, GetName()));
246       return kFALSE;
247    }
248    // Check type matching          
249    if (!output->GetType()->InheritsFrom(cont->GetType())) {
250       AliError(Form("Data type %s for output %i of task %s not matching container %s of type %s",
251                output->GetType()->GetName(), islot, GetName(), cont->GetName(), cont->GetType()->GetName()));
252       return kFALSE;
253    }            
254    // Connect the slot to the container as output         
255    if (!output->ConnectContainer(cont)) return kFALSE;
256    // Declare this as the data producer
257    cont->SetProducer(this, islot);
258    AreSlotsConnected();
259    return kTRUE;
260 }   
261
262 //______________________________________________________________________________
263 void AliAnalysisTask::DefineInput(Int_t islot, TClass *type)
264 {
265 // Define an input slot and its type.
266    AliAnalysisDataSlot *input = new AliAnalysisDataSlot(type, this);
267    if (fNinputs<islot+1) fNinputs = islot+1;
268    fInputs->AddAtAndExpand(input, islot);
269 }
270
271 //______________________________________________________________________________
272 void AliAnalysisTask::DefineOutput(Int_t islot, TClass *type)
273 {
274 // Define an output slot and its type.
275    if (islot<0) {
276       AliError(Form("Cannot define negative output slot number for task %s", GetName()));
277       return;
278    }   
279    AliAnalysisDataSlot *output = new AliAnalysisDataSlot(type, this);
280    if (fNoutputs<islot+1) {
281       fNoutputs = islot+1;
282       if (fOutputReady) delete [] fOutputReady;
283       fOutputReady = new Bool_t[fNoutputs];
284       memset(fOutputReady, 0, fNoutputs*sizeof(Bool_t));
285    } 
286    fOutputs->AddAtAndExpand(output, islot);
287 }
288
289 //______________________________________________________________________________
290 TClass *AliAnalysisTask::GetInputType(Int_t islot) const
291 {
292 // Retreive type of a given input slot.
293    AliAnalysisDataSlot *input = GetInputSlot(islot);
294    if (!input) {
295       AliError(Form("Input slot %i not defined for analysis task %s", islot, GetName()));
296       return NULL;
297    }
298    return (input->GetType());
299 }
300
301 //______________________________________________________________________________
302 TClass *AliAnalysisTask::GetOutputType(Int_t islot) const
303 {
304 // Retreive type of a given output slot.
305    AliAnalysisDataSlot *output = GetOutputSlot(islot);
306    if (!output) {
307       AliError(Form("Output slot %i not defined for analysis task %s", islot, GetName()));
308       return NULL;
309    }
310    return (output->GetType());
311 }
312
313 //______________________________________________________________________________
314 TObject *AliAnalysisTask::GetInputData(Int_t islot) const
315 {
316 // Retreive input data for a slot if ready. Normally called by Exec() and
317 // the object has to be statically cast to the appropriate type.
318    AliAnalysisDataSlot *input = GetInputSlot(islot);
319    if (!input) {
320       AliError(Form("Input slot %i not defined for analysis task %s", islot, GetName()));
321       return NULL;
322    }
323    return (input->GetData()); 
324 }
325
326 //______________________________________________________________________________
327 char *AliAnalysisTask::GetBranchAddress(Int_t islot, const char *branch) const
328 {
329 // Check if a branch with a given name from the specified input is connected
330 // to some address. Call this in Init() before trying to call SetBranchAddress()
331 // since the adress may be set by other task.
332    return (char *)GetInputSlot(islot)->GetBranchAddress(branch);
333 }
334
335 //______________________________________________________________________________
336 Bool_t AliAnalysisTask::SetBranchAddress(Int_t islot, const char *branch, void *address) const
337 {
338 // Connect an object address to a branch of the specified input.
339    return GetInputSlot(islot)->SetBranchAddress(branch, address);
340 }   
341
342 //______________________________________________________________________________
343 void AliAnalysisTask::Init(Option_t *)
344 {
345 // Branch address initialization.
346 }
347
348 //______________________________________________________________________________
349 void AliAnalysisTask::Terminate(Option_t *)
350 {
351 // Method called by the framework at the end of data processing.
352 }
353
354 //______________________________________________________________________________
355 void AliAnalysisTask::OpenFile(Int_t iout, const char *name, Option_t *option) const
356 {
357 // Set data at output iout to be written in the specified file.
358    GetOutputSlot(iout)->GetContainer()->OpenFile(name, option);
359 }   
360
361 //______________________________________________________________________________
362 Bool_t AliAnalysisTask::PostData(Int_t iout, TObject *data, Option_t *option)
363 {
364 // Post output data for a given ouput slot in the corresponding data container.
365 // Published data becomes owned by the data container.
366 // If option is specified, the container connected to the output slot must have
367 // an associated file name defined. The option represents the method to open the file.
368    fPublishedData = 0;
369    AliAnalysisDataSlot *output = GetOutputSlot(iout);
370    if (!output) {
371       AliError(Form("Output slot %i not defined for analysis task %s", iout, GetName()));
372       return kFALSE;
373    }
374    if (!output->IsConnected()) {
375       AliError(Form("Output slot %i of analysis task %s not connected to any data container", iout, GetName()));
376       return kFALSE;
377    }
378    if (!fOutputReady) {
379       fOutputReady = new Bool_t[fNoutputs];
380       memset(fOutputReady, 0, fNoutputs*sizeof(Bool_t));
381    }   
382    fOutputReady[iout] = kTRUE;
383    fPublishedData = data;
384    return (output->GetContainer()->SetData(data, option));
385 }
386
387 //______________________________________________________________________________
388 void AliAnalysisTask::SetUsed(Bool_t flag)
389 {
390 // Set 'used' flag recursively to task and all daughter tasks.
391    if (TestBit(kTaskUsed)==flag) return;
392    TObject::SetBit(kTaskUsed,flag);
393    Int_t nd = fTasks->GetSize();
394    AliAnalysisTask *task;
395    for (Int_t i=0; i<nd; i++) {
396       task = (AliAnalysisTask*)fTasks->At(i);
397       task->SetUsed(flag);
398    }
399 }   
400
401 //______________________________________________________________________________
402 Bool_t AliAnalysisTask::CheckCircularDeps()
403 {
404 // Check for illegal circular dependencies, e.g. a daughter task should not have
405 // a hierarchical parent as subtask.
406    if (IsChecked()) return kTRUE;
407    SetChecked();
408    TList *tasks = GetListOfTasks();
409    Int_t ntasks = tasks->GetSize();
410    AliAnalysisTask *task;
411    for (Int_t i=0; i<ntasks; i++) {
412       task = (AliAnalysisTask*)tasks->At(i);
413       if (task->CheckCircularDeps()) return kTRUE;
414    }
415    SetChecked(kFALSE);
416    return kFALSE;
417 }   
418    
419 //______________________________________________________________________________
420 void AliAnalysisTask::PrintTask(Option_t *option, Int_t indent) const
421 {
422 // Print task info.
423    AliAnalysisTask *thistask = (AliAnalysisTask*)this;
424    TString opt(option);
425    opt.ToLower();
426    Bool_t dep = (opt.Contains("dep"))?kTRUE:kFALSE;
427    TString ind;
428    Int_t islot;
429    AliAnalysisDataContainer *cont;
430    for (Int_t i=0; i<indent; i++) ind += " ";
431    if (!dep || (dep && IsChecked())) {
432       printf("%s\n", Form("%stask: %s  ACTIVE=%i", ind.Data(), GetName(),IsActive()));
433       if (dep) thistask->SetChecked(kFALSE);
434       else {
435          for (islot=0; islot<fNinputs; islot++) {
436             printf("%s", Form("%s   INPUT #%i: %s <- ",ind.Data(),islot, GetInputType(islot)->GetName()));
437             cont = GetInputSlot(islot)->GetContainer();
438             if (cont) printf(" [%s]\n", cont->GetName());
439             else printf(" [NO CONTAINER]\n");
440          }
441          for (islot=0; islot<fNoutputs; islot++) {
442             printf("%s", Form("%s   OUTPUT #%i: %s -> ",ind.Data(),islot, GetOutputType(islot)->GetName()));
443             cont = GetOutputSlot(islot)->GetContainer();
444             if (cont) printf(" [%s]\n", cont->GetName());
445             else printf(" [NO CONTAINER]\n");
446          }            
447       }
448    }
449    PrintContainers(option, indent+3);
450 }      
451
452 //______________________________________________________________________________
453 void AliAnalysisTask::PrintContainers(Option_t *option, Int_t indent) const
454 {
455 // Print containers info.
456    AliAnalysisDataContainer *cont;
457    TString ind;
458    for (Int_t i=0; i<indent; i++) ind += " ";
459    Int_t islot;
460    for (islot=0; islot<fNoutputs; islot++) {
461       cont = GetOutputSlot(islot)->GetContainer();
462       cont->PrintContainer(option, indent);
463    }   
464 }