New task to check the quality of the muon reconstruction and macros to run it. New...
authormartinez <martinez@f7af4fe6-9843-0410-8265-dc069ae4e863>
Fri, 25 Jun 2010 10:17:00 +0000 (10:17 +0000)
committermartinez <martinez@f7af4fe6-9843-0410-8265-dc069ae4e863>
Fri, 25 Jun 2010 10:17:00 +0000 (10:17 +0000)
PWG3/muon/AddTaskMuonQA.C [new file with mode: 0644]
PWG3/muon/AliAnalysisTaskMuonQA.cxx [new file with mode: 0644]
PWG3/muon/AliAnalysisTaskMuonQA.h [new file with mode: 0644]
PWG3/muon/AliCounterCollection.cxx [new file with mode: 0644]
PWG3/muon/AliCounterCollection.h [new file with mode: 0644]
PWG3/muon/RunMuonQA.C [new file with mode: 0644]

diff --git a/PWG3/muon/AddTaskMuonQA.C b/PWG3/muon/AddTaskMuonQA.C
new file mode 100644 (file)
index 0000000..ae15de5
--- /dev/null
@@ -0,0 +1,54 @@
+AliAnalysisTaskMuonQA *AddTaskMuonQA(Bool_t selectPhysics = kTRUE, Short_t selectCharge = 0)
+{
+  /// Add AliAnalysisTaskMuonQA to the train (Philippe Pillot)
+  
+  
+  // Get the pointer to the existing analysis manager via the static access method.
+  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
+  if(!mgr) { 
+    Error("AddTaskMuonQA","AliAnalysisManager not set!");
+    return NULL;
+  }
+  
+  // This task run on ESDs
+  TString type = mgr->GetInputEventHandler()->GetDataType();
+  if (!type.Contains("ESD")) {
+    Error("AddTaskMuonQA", "ESD input handler needed!");
+    return NULL;
+  }
+  
+  // Create and configure task
+  AliAnalysisTaskMuonQA *task = new AliAnalysisTaskMuonQA("MuonQA");
+  if (!task) {
+    Error("AddTaskMuonQA", "Muon QA task cannot be created!");
+    return NULL;
+  }
+  task->SelectPhysics(selectPhysics);
+  task->SelectCharge(selectCharge);
+  
+  // Add task to analysis manager
+  mgr->AddTask(task);
+  
+  // Connect input container
+  mgr->ConnectInput(task, 0, mgr->GetCommonInputContainer());
+  
+  // Define output file directory
+  TString outputfile = AliAnalysisManager::GetCommonFileName();
+  if ( outputfile.IsNull() ) {
+    Error("AddTaskMuonQA", "Common output file is not defined!");
+    return NULL;
+  }
+  outputfile += ":MUON_QA";
+  
+  // Create and connect output containers
+  AliAnalysisDataContainer *cout_histo1 = mgr->CreateContainer("general", TObjArray::Class(), AliAnalysisManager::kOutputContainer, outputfile);
+  AliAnalysisDataContainer *cout_histo2 = mgr->CreateContainer("expert", TObjArray::Class(), AliAnalysisManager::kOutputContainer, outputfile);
+  AliAnalysisDataContainer *cout_trackStat = mgr->CreateContainer("trackCounters", AliCounterCollection::Class(), AliAnalysisManager::kOutputContainer, outputfile);
+  AliAnalysisDataContainer *cout_eventStat = mgr->CreateContainer("eventCounters", AliCounterCollection::Class(), AliAnalysisManager::kOutputContainer, outputfile);
+  mgr->ConnectOutput(task, 1, cout_histo1);
+  mgr->ConnectOutput(task, 2, cout_histo2);
+  mgr->ConnectOutput(task, 3, cout_trackStat);
+  mgr->ConnectOutput(task, 4, cout_eventStat);
+  
+  return task;
+}   
diff --git a/PWG3/muon/AliAnalysisTaskMuonQA.cxx b/PWG3/muon/AliAnalysisTaskMuonQA.cxx
new file mode 100644 (file)
index 0000000..cdc1222
--- /dev/null
@@ -0,0 +1,549 @@
+/**************************************************************************
+ * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
+ *                                                                        *
+ * Author: The ALICE Off-line Project.                                    *
+ * Contributors are mentioned in the code where appropriate.              *
+ *                                                                        *
+ * Permission to use, copy, modify and distribute this software and its   *
+ * documentation strictly for non-commercial purposes is hereby granted   *
+ * without fee, provided that the above copyright notice appears in all   *
+ * copies and that both the copyright notice and this permission notice   *
+ * appear in the supporting documentation. The authors make no claims     *
+ * about the suitability of this software for any purpose. It is          *
+ * provided "as is" without express or implied warranty.                  *
+ **************************************************************************/
+
+#include <Riostream.h>
+
+// ROOT includes
+#include "TH1F.h"
+#include "TH2F.h"
+#include "TCanvas.h"
+#include "TROOT.h"
+#include "TString.h"
+#include "TObjArray.h"
+#include "TMath.h"
+#include "TFile.h"
+
+// STEER includes
+#include "AliESDEvent.h"
+#include "AliESDMuonTrack.h"
+#include "AliESDMuonCluster.h"
+#include "AliESDInputHandler.h"
+
+// ANALYSIS includes
+#include "AliAnalysisTaskSE.h"
+#include "AliAnalysisDataSlot.h"
+#include "AliAnalysisManager.h"
+#include "AliAnalysisTaskMuonQA.h"
+#include "AliCounterCollection.h"
+
+ClassImp(AliAnalysisTaskMuonQA)
+
+const Int_t AliAnalysisTaskMuonQA::nCh = 10;
+
+const Int_t AliAnalysisTaskMuonQA::nDE = 1100;
+
+const Float_t AliAnalysisTaskMuonQA::dMax[5] = {176.6, 229.0, 308.84, 418.2,  522.0}; // cm
+
+const Int_t AliAnalysisTaskMuonQA::fgkNTriggerClass = 10;
+
+const char* AliAnalysisTaskMuonQA::fgkTriggerClass[10] =
+{
+  "CBEAMB-ABCE-NOPF-ALL",
+  "CSMBB-ABCE-NOPF-ALL",
+  "CINT1A-ABCE-NOPF-ALL",
+  "CINT1B-ABCE-NOPF-ALL",
+  "CINT1C-ABCE-NOPF-ALL",
+  "CINT1-E-NOPF-ALL",
+  "CMUS1A-ABCE-NOPF-MUON",
+  "CMUS1B-ABCE-NOPF-MUON",
+  "CMUS1C-ABCE-NOPF-MUON",
+  "CMUS1-E-NOPF-MUON"
+};
+
+const char* AliAnalysisTaskMuonQA::fgkTriggerShortName[11] =
+{
+  "CBEAMB",
+  "CSMBB",
+  "CINT1A",
+  "CINT1B",
+  "CINT1C",
+  "CINT1-E",
+  "CMUS1A",
+  "CMUS1B",
+  "CMUS1C",
+  "CMUS1-E",
+  "Other"
+};
+
+//________________________________________________________________________
+AliAnalysisTaskMuonQA::AliAnalysisTaskMuonQA(const char *name) :
+  AliAnalysisTaskSE(name), 
+  fList(0x0),
+  fListExpert(0x0),
+  fTrackCounters(0x0),
+  fEventCounters(0x0),
+  fSelectCharge(0),
+  fSelectPhysics(kFALSE)
+{
+  /// Constructor
+  
+  // Output slot #1 writes into a TObjArray container
+  DefineOutput(1,TObjArray::Class());
+  // Output slot #2 writes into a TObjArray container
+  DefineOutput(2,TObjArray::Class());
+  // Output slot #3 writes track counters
+  DefineOutput(3,AliCounterCollection::Class());
+  // Output slot #4 writes event counters
+  DefineOutput(4,AliCounterCollection::Class());
+}
+
+//________________________________________________________________________
+AliAnalysisTaskMuonQA::~AliAnalysisTaskMuonQA()
+{
+  /// Destructor
+  delete fList;
+  delete fListExpert;
+  delete fTrackCounters;
+  delete fEventCounters;
+}
+
+//___________________________________________________________________________
+void AliAnalysisTaskMuonQA::UserCreateOutputObjects()
+{
+  /// Create histograms and counters
+  
+  fList = new TObjArray(2000);
+  fList->SetOwner();
+  fListExpert = new TObjArray(2000);
+  fListExpert->SetOwner();
+  
+  // track info
+  TH1F* hNTracks = new TH1F("hNTracks", "number of tracks;n_{tracks}", 20, 0., 20.);
+  fList->AddAtAndExpand(hNTracks, kNTracks);
+  
+  TH1F* hMatchTrig = new TH1F("hMatchTrig", "number of tracks matched with trigger;n_{tracks}", 20, 0., 20.);
+  fList->AddAtAndExpand(hMatchTrig, kMatchTrig);
+  
+  TH1F* hSign = new TH1F("hSign", "track sign;sign", 3, -1.5, 1.5);
+  fList->AddAtAndExpand(hSign, kSign);
+  
+  TH1F* hDCA = new TH1F("hDCA", "DCA distribution;DCA (cm)", 500, 0., 500.);
+  fList->AddAtAndExpand(hDCA, kDCA);
+  
+  TH1F* hP = new TH1F("hP", "momentum distribution;p (GeV/c)", 300, 0., 300.);
+  fList->AddAtAndExpand(hP, kP);
+  
+  TH1F* hPt = new TH1F("hPt", "transverse momentum distribution;p_{t} (GeV/c)", 300, 0., 30);
+  fList->AddAtAndExpand(hPt, kPt);
+  
+  TH1F* hRapidity = new TH1F("hRapidity", "rapidity distribution;rapidity", 200, -4.5, -2.);
+  fList->AddAtAndExpand(hRapidity, kRapidity);
+  
+  TH1F* hThetaX = new TH1F("hThetaX", "#theta_{X} distribution;#theta_{X} (degree)", 360, -180., 180);
+  fList->AddAtAndExpand(hThetaX, kThetaX);
+  
+  TH1F* hThetaY = new TH1F("hThetaY", "#theta_{Y} distribution;#theta_{Y} (degree)", 360, -180., 180);
+  fList->AddAtAndExpand(hThetaY, kThetaY);
+  
+  TH1F* hChi2 = new TH1F("hChi2", "normalized #chi^{2} distribution;#chi^{2} / ndf", 500, 0., 50.);
+  fList->AddAtAndExpand(hChi2, kChi2);
+  
+  TH1F* hProbChi2 = new TH1F("hProbChi2", "distribution of probability of #chi^{2};prob(#chi^{2})", 100, 0., 1.);
+  fList->AddAtAndExpand(hProbChi2, kProbChi2);
+  
+  // cluster info
+  TH1F* hNClustersPerTrack = new TH1F("hNClustersPerTrack", "number of associated clusters per track;n_{clusters}", 20, 0., 20.);
+  fList->AddAtAndExpand(hNClustersPerTrack, kNClustersPerTrack);
+  
+  TH1F* hNChamberHitPerTrack = new TH1F("hNChamberHitPerTrack", "number of chambers hit per track;n_{chamber hit}", 15, 0., 15.);
+  fList->AddAtAndExpand(hNChamberHitPerTrack, kNChamberHitPerTrack);
+  
+  TH1F* hNClustersPerCh = new TH1F("hNClustersPerCh", "averaged number of clusters per chamber per track;chamber ID;<n_{clusters}>", nCh, -0.5, nCh-0.5);
+  hNClustersPerCh->Sumw2();
+  hNClustersPerCh->SetOption("P");
+  hNClustersPerCh->SetMarkerStyle(kFullDotMedium);
+  hNClustersPerCh->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hNClustersPerCh, kNClustersPerCh);
+  
+  TH1F* hNClustersPerDE = new TH1F("hNClustersPerDE", "averaged number of clusters per DE per track;DetElem ID;<n_{clusters}>", nDE+1, -0.5, nDE+0.5);
+  hNClustersPerDE->Sumw2();
+  hNClustersPerDE->SetOption("P");
+  hNClustersPerDE->SetMarkerStyle(kFullDotMedium);
+  hNClustersPerDE->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hNClustersPerDE, kNClustersPerDE);
+  
+  for (Int_t i = 0; i < nCh; i++) {
+    Float_t rMax = 0.5*dMax[i/2];
+    TH2F* hClusterHitMapInCh = new TH2F(Form("hClusterHitMapInCh%d",i+1), Form("cluster position distribution in chamber %d;X (cm);Y (cm)",i+1),
+                                      100, -rMax, rMax, 100, -rMax, rMax);
+    fListExpert->AddAtAndExpand(hClusterHitMapInCh, kClusterHitMapInCh+i);
+    
+    TH1F* hClusterChargeInCh = new TH1F(Form("hClusterChargeInCh%d",i+1), Form("cluster charge distribution in chamber %d;charge (fC)",i+1), 100, 0., 1000.);
+    fListExpert->AddAtAndExpand(hClusterChargeInCh, kClusterChargeInCh+i);
+    
+    TH1F* hClusterSizeInCh = new TH1F(Form("hClusterSizeInCh%d",i+1), Form("cluster size distribution in chamber %d;size (n_{pads})",i+1), 200, 0., 200.);
+    fListExpert->AddAtAndExpand(hClusterSizeInCh, kClusterSizeInCh+i);
+  }
+  
+  TH2F* hClusterChargePerDE = new TH2F("hClusterChargePerDE", "cluster charge distribution per DE;DetElem ID;charge (fC)", nDE+1, -0.5, nDE+0.5, 100, 0., 1000.);
+  fListExpert->AddAtAndExpand(hClusterChargePerDE, kClusterChargePerDE);
+  
+  TH2F* hClusterSizePerDE = new TH2F("hClusterSizePerDE", "cluster size distribution per DE;DetElem ID;size (n_{pads})", nDE+1, -0.5, nDE+0.5, 200, 0., 200.);
+  fListExpert->AddAtAndExpand(hClusterSizePerDE, kClusterSizePerDE);
+  
+  TH1F* hClusterChargePerChMean = new TH1F("hClusterChargePerChMean", "cluster mean charge per chamber;chamber ID;<charge> (fC)", nCh, -0.5, nCh-0.5);
+  hClusterChargePerChMean->SetOption("P");
+  hClusterChargePerChMean->SetMarkerStyle(kFullDotMedium);
+  hClusterChargePerChMean->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterChargePerChMean, kClusterChargePerChMean);
+  
+  TH1F* hClusterChargePerChSigma = new TH1F("hClusterChargePerChSigma", "cluster charge dispersion per chamber;chamber ID;#sigma_{charge} (fC)", nCh, -0.5, nCh-0.5);
+  hClusterChargePerChSigma->SetOption("P");
+  hClusterChargePerChSigma->SetMarkerStyle(kFullDotMedium);
+  hClusterChargePerChSigma->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterChargePerChSigma, kClusterChargePerChSigma);
+  
+  TH1F* hClusterChargePerDEMean = new TH1F("hClusterChargePerDEMean", "cluster mean charge per DE;DetElem ID;<charge> (fC)", nDE+1, -0.5, nDE+0.5);
+  hClusterChargePerDEMean->SetOption("P");
+  hClusterChargePerDEMean->SetMarkerStyle(kFullDotMedium);
+  hClusterChargePerDEMean->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterChargePerDEMean, kClusterChargePerDEMean);
+  
+  TH1F* hClusterChargePerDESigma = new TH1F("hClusterChargePerDESigma", "cluster charge dispersion per DE;DetElem ID;#sigma_{charge} (fC)", nDE+1, -0.5, nDE+0.5);
+  hClusterChargePerDESigma->SetOption("P");
+  hClusterChargePerDESigma->SetMarkerStyle(kFullDotMedium);
+  hClusterChargePerDESigma->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterChargePerDESigma, kClusterChargePerDESigma);
+  
+  TH1F* hClusterSizePerChMean = new TH1F("hClusterSizePerChMean", "cluster mean size per chamber;chamber ID;<size> (n_{pads})", nCh, -0.5, nCh-0.5);
+  hClusterSizePerChMean->SetOption("P");
+  hClusterSizePerChMean->SetMarkerStyle(kFullDotMedium);
+  hClusterSizePerChMean->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterSizePerChMean, kClusterSizePerChMean);
+  
+  TH1F* hClusterSizePerChSigma = new TH1F("hClusterSizePerChSigma", "cluster size dispersion per chamber;chamber ID;#sigma_{size} (n_{pads})", nCh, -0.5, nCh-0.5);
+  hClusterSizePerChSigma->SetOption("P");
+  hClusterSizePerChSigma->SetMarkerStyle(kFullDotMedium);
+  hClusterSizePerChSigma->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterSizePerChSigma, kClusterSizePerChSigma);
+  
+  TH1F* hClusterSizePerDEMean = new TH1F("hClusterSizePerDEMean", "cluster mean size per DE;DetElem ID;<size> (n_{pads})", nDE+1, -0.5, nDE+0.5);
+  hClusterSizePerDEMean->SetOption("P");
+  hClusterSizePerDEMean->SetMarkerStyle(kFullDotMedium);
+  hClusterSizePerDEMean->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterSizePerDEMean, kClusterSizePerDEMean);
+  
+  TH1F* hClusterSizePerDESigma = new TH1F("hClusterSizePerDESigma", "cluster size dispersion per DE;DetElem ID;#sigma_{size} (n_{pads})", nDE+1, -0.5, nDE+0.5);
+  hClusterSizePerDESigma->SetOption("P");
+  hClusterSizePerDESigma->SetMarkerStyle(kFullDotMedium);
+  hClusterSizePerDESigma->SetMarkerColor(kBlue);
+  fList->AddAtAndExpand(hClusterSizePerDESigma, kClusterSizePerDESigma);
+  
+  // initialize track counters
+  fTrackCounters = new AliCounterCollection("trackCounters");
+  fTrackCounters->AddRubric("track", "tracker/trigger/matched/any");
+  TString triggerClassNames = "/";
+  for (Int_t i=0; i<=AliAnalysisTaskMuonQA::fgkNTriggerClass; i++)
+    triggerClassNames += Form("%s/",AliAnalysisTaskMuonQA::fgkTriggerShortName[i]);
+  triggerClassNames += "any/";
+  fTrackCounters->AddRubric("trigger", triggerClassNames.Data());
+  fTrackCounters->AddRubric("run", 1000000);
+  fTrackCounters->AddRubric("selected", "yes/no");
+  fTrackCounters->AddRubric("triggerRO", "good/bad");
+  fTrackCounters->Init();
+  
+  // initialize event counters
+  fEventCounters = new AliCounterCollection("eventCounters");
+  fEventCounters->AddRubric("event", "muon/any");
+  fEventCounters->AddRubric("trigger", triggerClassNames.Data());
+  fEventCounters->AddRubric("run", 1000000);
+  fEventCounters->AddRubric("selected", "yes/no");
+  fEventCounters->AddRubric("triggerRO", "good/bad");
+  fEventCounters->Init();
+  
+  // Post data at least once per task to ensure data synchronisation (required for merging)
+  PostData(1, fList);
+  PostData(2, fListExpert);
+  PostData(3, fTrackCounters);
+  PostData(4, fEventCounters);
+}
+
+//________________________________________________________________________
+void AliAnalysisTaskMuonQA::UserExec(Option_t *)
+{
+  /// Called for each event
+  
+  // check physics selection
+  Bool_t isPhysicsSelected = (fInputHandler && fInputHandler->IsEventSelected());
+  TString selected = isPhysicsSelected ? "selected:yes" : "selected:no";
+  
+  AliESDEvent* fESD = dynamic_cast<AliESDEvent*>(InputEvent());
+  if (!fESD) {
+    Printf("ERROR: fESD not available");
+    return;
+  }
+  
+  Int_t nTracks = (Int_t) fESD->GetNumberOfMuonTracks(); 
+  Int_t nTrackerTracks = 0;
+  Int_t nSelectedTrackerTracks = 0;
+  Int_t nTriggerTracks = 0;
+  Int_t nTrackMatchTrig = 0;
+  Int_t nSelectedTrackMatchTrig = 0;
+  
+  // loop over tracks and fill histograms
+  for (Int_t iTrack = 0; iTrack < nTracks; ++iTrack) {
+    
+    // --- fill counters for all tracks ---
+    
+    // get the ESD track and skip "ghosts"
+    AliESDMuonTrack* esdTrack = fESD->GetMuonTrack(iTrack);
+    if (!esdTrack->ContainTrackerData()) {
+      nTriggerTracks++;
+      continue;
+    }
+    
+    nTrackerTracks++;
+    
+    if (esdTrack->ContainTriggerData()) {
+      nTriggerTracks++;
+      nTrackMatchTrig++;
+    }
+    
+    // --- apply selections and fill histograms with selected tracks ---
+    
+    // select on "physics" before filling histograms
+    if (fSelectPhysics && !isPhysicsSelected) continue;
+    
+    // select on track charge
+    if (fSelectCharge*esdTrack->Charge() < 0) continue;
+    
+    nSelectedTrackerTracks++;
+    if (esdTrack->ContainTriggerData()) nSelectedTrackMatchTrig++;
+    
+    ((TH1F*)fList->UncheckedAt(kP))->Fill(esdTrack->P());
+    ((TH1F*)fList->UncheckedAt(kPt))->Fill(esdTrack->Pt());
+    ((TH1F*)fList->UncheckedAt(kRapidity))->Fill(esdTrack->Y());
+    Int_t ndf = 2 * esdTrack->GetNHit() - 5;
+    ((TH1F*)fList->UncheckedAt(kChi2))->Fill(esdTrack->GetChi2()/ndf);
+    ((TH1F*)fList->UncheckedAt(kProbChi2))->Fill(TMath::Prob(esdTrack->GetChi2(),ndf));
+    ((TH1F*)fList->UncheckedAt(kThetaX))->Fill(ChangeThetaRange(esdTrack->GetThetaXUncorrected()));
+    ((TH1F*)fList->UncheckedAt(kThetaY))->Fill(ChangeThetaRange(esdTrack->GetThetaYUncorrected()));
+    ((TH1F*)fList->UncheckedAt(kNClustersPerTrack))->Fill(esdTrack->GetNHit());
+    ((TH1F*)fList->UncheckedAt(kSign))->Fill(esdTrack->Charge());
+    ((TH1F*)fList->UncheckedAt(kDCA))->Fill(esdTrack->GetDCA());
+    
+    Int_t nChamberHit = 0;
+    for (Int_t ich=0; ich<10; ich++) if (esdTrack->IsInMuonClusterMap(ich)) nChamberHit++;
+    ((TH1F*)fList->UncheckedAt(kNChamberHitPerTrack))->Fill(nChamberHit);
+    
+    // what follows concern clusters
+    if(!esdTrack->ClustersStored()) continue;
+    
+    AliESDMuonCluster *esdCluster = (AliESDMuonCluster*) esdTrack->GetClusters().First();
+    while (esdCluster) {
+      
+      Int_t chId = esdCluster->GetChamberId();
+      Int_t deId = esdCluster->GetDetElemId();
+      
+      ((TH1F*)fList->UncheckedAt(kNClustersPerCh))->Fill(chId);
+      ((TH1F*)fList->UncheckedAt(kNClustersPerDE))->Fill(deId);
+      
+      ((TH1F*)fListExpert->UncheckedAt(kClusterHitMapInCh+chId))->Fill(esdCluster->GetX(), esdCluster->GetY());
+      
+      ((TH1F*)fListExpert->UncheckedAt(kClusterChargeInCh+chId))->Fill(esdCluster->GetCharge());
+      ((TH1F*)fListExpert->UncheckedAt(kClusterChargePerDE))->Fill(deId, esdCluster->GetCharge());
+      
+      if (esdCluster->PadsStored()) { // discard clusters with pad not stored in ESD
+        ((TH1F*)fListExpert->UncheckedAt(kClusterSizeInCh+chId))->Fill(esdCluster->GetNPads());
+       ((TH1F*)fListExpert->UncheckedAt(kClusterSizePerDE))->Fill(deId, esdCluster->GetNPads());
+      }
+      
+      esdCluster = (AliESDMuonCluster*) esdTrack->GetClusters().After(esdCluster);
+    }
+    
+  }
+  
+  ((TH1F*)fList->UncheckedAt(kNTracks))->Fill(nSelectedTrackerTracks);
+  ((TH1F*)fList->UncheckedAt(kMatchTrig))->Fill(nSelectedTrackMatchTrig);
+  
+  // fill event counters
+  TString triggerRO = (nTriggerTracks < 10) ? "triggerRO:good" : "triggerRO:bad";
+  
+  fEventCounters->Count(Form("event:any/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+  
+  Bool_t triggerFired = kFALSE;
+  for (Int_t i=0; i<10; i++) {
+    if (fESD->IsTriggerClassFired(AliAnalysisTaskMuonQA::fgkTriggerClass[i])) {
+      fEventCounters->Count(Form("event:any/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+      triggerFired = kTRUE;
+    }
+  }
+  if (!triggerFired) {
+    fEventCounters->Count(Form("event:any/trigger:other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+  }
+  
+  if (nTracks > 0) {
+    
+    // fill event counters
+    fEventCounters->Count(Form("event:muon/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+    
+    // fill track counters
+    fTrackCounters->Count(Form("track:tracker/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks);
+    fTrackCounters->Count(Form("track:trigger/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTriggerTracks);
+    fTrackCounters->Count(Form("track:matched/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackMatchTrig);
+    fTrackCounters->Count(Form("track:any/trigger:any/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks+nTriggerTracks);
+    
+    Bool_t triggerFiredForTrack = kFALSE;
+    for (Int_t i=0; i<AliAnalysisTaskMuonQA::fgkNTriggerClass; i++) {
+      
+      if (fESD->IsTriggerClassFired(AliAnalysisTaskMuonQA::fgkTriggerClass[i])) {
+       
+       // fill event counters
+       fEventCounters->Count(Form("event:muon/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+       
+       // fill track counters
+       fTrackCounters->Count(Form("track:tracker/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks);
+       fTrackCounters->Count(Form("track:trigger/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTriggerTracks);
+       fTrackCounters->Count(Form("track:matched/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackMatchTrig);
+       fTrackCounters->Count(Form("track:any/trigger:%s/run:%d/%s/%s", AliAnalysisTaskMuonQA::fgkTriggerShortName[i], fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks+nTriggerTracks);
+       
+       triggerFiredForTrack = kTRUE;
+       
+      }
+      
+    }
+    
+    if (!triggerFiredForTrack) {
+      
+      // fill event counters
+      fEventCounters->Count(Form("event:muon/trigger:other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()));
+      
+      // fill track counters
+      fTrackCounters->Count(Form("track:tracker/trigger:Other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks);
+      fTrackCounters->Count(Form("track:trigger/trigger:Other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTriggerTracks);
+      fTrackCounters->Count(Form("track:matched/trigger:Other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackMatchTrig);
+      fTrackCounters->Count(Form("track:any/trigger:Other/run:%d/%s/%s", fCurrentRunNumber, selected.Data(), triggerRO.Data()), nTrackerTracks+nTriggerTracks);
+      
+    }
+    
+  }
+  
+  // Post final data. It will be written to a file with option "RECREATE"
+  PostData(1, fList);
+  PostData(2, fListExpert);
+  PostData(3, fTrackCounters);
+  PostData(4, fEventCounters);
+}
+
+//________________________________________________________________________
+void AliAnalysisTaskMuonQA::Terminate(Option_t *)
+{
+  /// Normalize histograms
+  /// Draw result to the screen
+  /// Print statistics
+  
+  // recover output objects
+  fList = static_cast<TObjArray*> (GetOutputData(1));
+  fListExpert = static_cast<TObjArray*> (GetOutputData(2));
+  if (!fList || !fListExpert) return;
+  fTrackCounters = static_cast<AliCounterCollection*> (GetOutputData(3));
+  fEventCounters = static_cast<AliCounterCollection*> (GetOutputData(4));
+  
+  // global statistic
+  if (fTrackCounters && fEventCounters) {
+    if (!gROOT->IsBatch()) {
+      cout<<"whole statistics without selection:"<<endl;
+      fEventCounters->Print("trigger/event");
+      fTrackCounters->Print("trigger/track");
+      cout<<"whole statistics of selected events:"<<endl;
+      fEventCounters->Print("trigger/event","selected:yes");
+      fTrackCounters->Print("trigger/track","selected:yes");
+      new TCanvas();
+      fEventCounters->Draw("event","trigger","");
+      new TCanvas();
+      fTrackCounters->Draw("track","trigger","");
+      new TCanvas();
+      fEventCounters->Draw("event","trigger","selected:yes");
+      new TCanvas();
+      fTrackCounters->Draw("track","trigger","selected:yes");
+    }
+  }
+  
+  // normalize histograms and fill summary plots
+  Float_t nTracks = ((TH1F*)fList->UncheckedAt(kNClustersPerTrack))->GetEntries();
+  if (nTracks > 0.) {
+    ((TH1F*)fList->UncheckedAt(kNClustersPerCh))->Scale(1./nTracks);
+    ((TH1F*)fList->UncheckedAt(kNClustersPerDE))->Scale(1./nTracks);
+  }
+  
+  // fill summary plots per chamber
+  TH1* hClusterChargePerChMean = ((TH1F*)fList->UncheckedAt(kClusterChargePerChMean));
+  TH1* hClusterChargePerChSigma = ((TH1F*)fList->UncheckedAt(kClusterChargePerChSigma));
+  TH1* hClusterSizePerChMean = ((TH1F*)fList->UncheckedAt(kClusterSizePerChMean));
+  TH1* hClusterSizePerChSigma = ((TH1F*)fList->UncheckedAt(kClusterSizePerChSigma));
+  for (Int_t iCh = 0; iCh < nCh; iCh++) {
+    
+    TH1* hClusterChargeInCh = ((TH1F*)fListExpert->UncheckedAt(kClusterChargeInCh+iCh));
+    hClusterChargePerChMean->SetBinContent(iCh+1, hClusterChargeInCh->GetMean());
+    hClusterChargePerChMean->SetBinError(iCh+1, hClusterChargeInCh->GetMeanError());
+    hClusterChargePerChSigma->SetBinContent(iCh+1, hClusterChargeInCh->GetRMS());
+    hClusterChargePerChSigma->SetBinError(iCh+1, hClusterChargeInCh->GetRMSError());
+    
+    TH1* hClusterSizeInCh = ((TH1F*)fListExpert->UncheckedAt(kClusterSizeInCh+iCh));
+    hClusterSizePerChMean->SetBinContent(iCh+1, hClusterSizeInCh->GetMean());
+    hClusterSizePerChMean->SetBinError(iCh+1, hClusterSizeInCh->GetMeanError());
+    hClusterSizePerChSigma->SetBinContent(iCh+1, hClusterSizeInCh->GetRMS());
+    hClusterSizePerChSigma->SetBinError(iCh+1, hClusterSizeInCh->GetRMSError());
+    
+  }
+  
+  // fill summary plots per DE
+  TH2F* hClusterChargePerDE = ((TH2F*)fListExpert->UncheckedAt(kClusterChargePerDE));
+  TH1F* hClusterChargePerDEMean = ((TH1F*)fList->UncheckedAt(kClusterChargePerDEMean));
+  TH1F* hClusterChargePerDESigma = ((TH1F*)fList->UncheckedAt(kClusterChargePerDESigma));
+  TH2F* hClusterSizePerDE = ((TH2F*)fListExpert->UncheckedAt(kClusterSizePerDE));
+  TH1F* hClusterSizePerDEMean = ((TH1F*)fList->UncheckedAt(kClusterSizePerDEMean));
+  TH1F* hClusterSizePerDESigma = ((TH1F*)fList->UncheckedAt(kClusterSizePerDESigma));
+  for (Int_t iDE = 1; iDE < nDE+1; iDE++) {
+    
+    TH1D *tmp = hClusterChargePerDE->ProjectionY("tmp",iDE,iDE,"e");
+    if (tmp->GetEntries() > 10.) {
+      hClusterChargePerDEMean->SetBinContent(iDE, tmp->GetMean());
+      hClusterChargePerDEMean->SetBinError(iDE, tmp->GetMeanError());
+      hClusterChargePerDESigma->SetBinContent(iDE, tmp->GetRMS());
+      hClusterChargePerDESigma->SetBinError(iDE, tmp->GetRMSError());
+    }
+    delete tmp;
+    
+    tmp = hClusterSizePerDE->ProjectionY("tmp",iDE,iDE,"e");
+    if (tmp->GetEntries() > 10.) {
+      hClusterSizePerDEMean->SetBinContent(iDE, tmp->GetMean());
+      hClusterSizePerDEMean->SetBinError(iDE, tmp->GetMeanError());
+      hClusterSizePerDESigma->SetBinContent(iDE, tmp->GetRMS());
+      hClusterSizePerDESigma->SetBinError(iDE, tmp->GetRMSError());
+    }
+    delete tmp;
+    
+  }
+  
+  TFile *histoFile = new TFile("histo.root", "RECREATE");
+  histoFile->mkdir("general","general");
+  histoFile->cd("general");
+  fList->Write();
+  histoFile->mkdir("expert","expert");
+  histoFile->cd("expert");
+  fListExpert->Write();
+  histoFile->Close();
+  
+}
+
+//________________________________________________________________________
+Double_t AliAnalysisTaskMuonQA::ChangeThetaRange(Double_t theta)
+{
+  if(theta < -2.5) return (theta / TMath::Pi() + 1.) * 180.;
+  else if(theta > 2.5) return (theta / TMath::Pi() - 1.) * 180.;
+  else return theta / TMath::Pi() * 180.;
+}
+
diff --git a/PWG3/muon/AliAnalysisTaskMuonQA.h b/PWG3/muon/AliAnalysisTaskMuonQA.h
new file mode 100644 (file)
index 0000000..a40d512
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef ALIANALYSISTASKMUONQA_H
+#define ALIANALYSISTASKMUONQA_H
+/* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
+ * See cxx source for full Copyright notice                               */
+
+/// \ingroup muondep
+/// \class AliAnalysisTaskMuonQA
+/// \brief Quality assurance of MUON ESDs
+//Author: Philippe Pillot - SUBATECH Nantes
+
+class TObjArray;
+class AliCounterCollection;
+
+class AliAnalysisTaskMuonQA : public AliAnalysisTaskSE {
+public:
+  
+  AliAnalysisTaskMuonQA(const char *name = "MuonQA");
+  virtual ~AliAnalysisTaskMuonQA();
+  
+  virtual void   UserCreateOutputObjects();
+  virtual void   UserExec(Option_t *);
+  virtual void   Terminate(Option_t *);
+  
+  void SelectCharge(Short_t charge = 0) {fSelectCharge = charge;}
+  void SelectPhysics(Bool_t flag = kTRUE) {fSelectPhysics = flag;}
+  
+private:
+  
+  /// Not implemented
+  AliAnalysisTaskMuonQA(const AliAnalysisTaskMuonQA& rhs);
+  /// Not implemented
+  AliAnalysisTaskMuonQA& operator = (const AliAnalysisTaskMuonQA& rhs);
+  
+  Double_t ChangeThetaRange(Double_t theta);
+  
+private:
+  
+  enum EESD { 
+    kNTracks                 = 0,  ///< number of tracks
+    kMatchTrig               = 1,  ///< number of tracks matched with trigger
+    kSign                    = 2,  ///< track sign
+    kDCA                     = 3,  ///< DCA distribution
+    kP                       = 4,  ///< P distribution
+    kPt                      = 5,  ///< Pt distribution
+    kRapidity                = 6,  ///< rapidity distribution
+    kThetaX                  = 7,  ///< thetaX distribution
+    kThetaY                  = 8,  ///< thetaY distribution
+    kChi2                    = 9,  ///< normalized chi2 distribution
+    kProbChi2                = 10, ///< distribution of probability of chi2
+    
+    kNClustersPerTrack       = 11, ///< number of clusters per track
+    kNChamberHitPerTrack     = 12, ///< number of chamber hit per track
+    kNClustersPerCh          = 13, ///< number of clusters per chamber per track
+    kNClustersPerDE          = 14, ///< number of clusters per DE per track
+    kClusterHitMapInCh       = 15, ///< cluster position distribution in chamber i
+    kClusterChargeInCh       = 25, ///< cluster charge distribution in chamber i
+    kClusterChargePerChMean  = 35, ///< cluster charge per Ch: mean
+    kClusterChargePerChSigma = 36, ///< cluster charge per Ch: dispersion
+    kClusterChargePerDE      = 37, ///< cluster charge distribution per DE
+    kClusterChargePerDEMean  = 38, ///< cluster charge per DE: mean
+    kClusterChargePerDESigma = 39, ///< cluster charge per DE: dispersion
+    kClusterSizeInCh         = 40, ///< cluster size distribution in chamber i
+    kClusterSizePerChMean    = 50, ///< cluster size per Ch: mean
+    kClusterSizePerChSigma   = 51, ///< cluster size per Ch: dispersion
+    kClusterSizePerDE        = 52, ///< cluster size distribution per DE
+    kClusterSizePerDEMean    = 53, ///< cluster size per DE: mean
+    kClusterSizePerDESigma   = 54  ///< cluster size per DE: dispersion
+  };
+  
+  TObjArray*  fList;       //!< List of output object for everybody
+  TObjArray*  fListExpert; //!< List of output object for experts
+  
+  AliCounterCollection* fTrackCounters; //!< track statistics
+  AliCounterCollection* fEventCounters; //!< event statistics
+  
+  Short_t fSelectCharge;  ///< Fill histograms only with negative/position tracks (0=all)
+  Bool_t  fSelectPhysics; ///< Fill histograms only with track passing the physics selection
+  
+  static const Int_t nCh;       ///< number of tracking chambers
+  static const Int_t nDE;       ///< number of DE
+  static const Float_t dMax[5]; ///< maximum diameter of each station
+  static const Int_t fgkNTriggerClass;        ///< number of trigger class we consider
+  static const char* fgkTriggerClass[10];     ///< full trigger class name
+  static const char* fgkTriggerShortName[11]; ///< short trigger class name for counters
+  
+  ClassDef(AliAnalysisTaskMuonQA, 1);
+};
+
+#endif
+
diff --git a/PWG3/muon/AliCounterCollection.cxx b/PWG3/muon/AliCounterCollection.cxx
new file mode 100644 (file)
index 0000000..1909980
--- /dev/null
@@ -0,0 +1,1289 @@
+/**************************************************************************
+ * Copyright(c) 1998-2007, ALICE Experiment at CERN, All rights reserved. *
+ *                                                                        *
+ * Author: The ALICE Off-line Project.                                    *
+ * Contributors are mentioned in the code where appropriate.              *
+ *                                                                        *
+ * Permission to use, copy, modify and distribute this software and its   *
+ * documentation strictly for non-commercial purposes is hereby granted   *
+ * without fee, provided that the above copyright notice appears in all   *
+ * copies and that both the copyright notice and this permission notice   *
+ * appear in the supporting documentation. The authors make no claims     *
+ * about the suitability of this software for any purpose. It is          *
+ * provided "as is" without express or implied warranty.                  *
+ **************************************************************************/
+
+//-----------------------------------------------------------------------------
+/// \class AliCounterCollection
+/// 
+/// generic class to handle a collection of counters
+///
+/// \author Philippe Pillot
+//-----------------------------------------------------------------------------
+
+#include "AliCounterCollection.h"
+
+#include <AliLog.h>
+
+#include <TString.h>
+#include <TObjString.h>
+#include <TObjArray.h>
+#include <THnSparse.h>
+#include <THashList.h>
+#include <TArrayI.h>
+#include <TH1D.h>
+#include <TH2D.h>
+#include <TCollection.h>
+
+ClassImp(AliCounterCollection)
+
+//-----------------------------------------------------------------------
+AliCounterCollection::AliCounterCollection(const char* name) :
+TNamed(name,name),
+fRubrics(new THashList(10)),
+fRubricsSize(new TArrayI(10)),
+fCounters(0x0)
+{
+  /// Constructor
+  fRubrics->SetOwner();
+}
+
+//-----------------------------------------------------------------------
+AliCounterCollection::~AliCounterCollection()
+{
+  /// Destructor
+  delete fRubrics;
+  delete fRubricsSize;
+  delete fCounters;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Clear(Option_t*)
+{
+  /// Clear counters
+  fRubrics->Clear();
+  fRubricsSize->Reset();
+  delete fCounters; fCounters = 0x0;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::AddRubric(TString name, TString listOfKeyWords)
+{
+  /// Add a new rubric with the complete list of related key words separated by "/".
+  /// If the key word "any" is not defined, the overall statistics is
+  /// assumed to be the sum of the statistics under each key word.
+  
+  name.ToUpper();
+  listOfKeyWords.ToUpper();
+  
+  if (fRubrics->Contains(name.Data())) {
+    AliError(Form("rubric named %s already exist",name.Data()));
+    return;
+  }
+  
+  // add the list of autorized key words
+  TObjArray* rubric = listOfKeyWords.Tokenize("/");
+  CleanListOfStrings(rubric);
+  rubric->SetName(name.Data());
+  Int_t nRubrics = fRubrics->GetSize();
+  rubric->SetUniqueID(nRubrics);
+  fRubrics->AddLast(rubric);
+  
+  // save the number of autorized key words (expand the array if needed)
+  if (nRubrics+1 > fRubricsSize->GetSize()) fRubricsSize->Set(2*fRubricsSize->GetSize());
+  (*fRubricsSize)[nRubrics] = rubric->GetEntriesFast();
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::AddRubric(TString name, Int_t maxNKeyWords)
+{
+  /// Add a new rubric containing at maximum maxNKeyWords key words.
+  /// Key words will be added as the counters get filled until the maximum is reached.
+  /// If the key word "any" is never defined, the overall statistics is
+  /// assumed to be the sum of the statistics under each key word.
+  
+  name.ToUpper();
+  
+  if (fRubrics->Contains(name.Data())) {
+    AliError(Form("rubric named %s already exist",name.Data()));
+    return;
+  }
+  
+  // create the empty rubric
+  TObjString* rubric = new TObjString(name.Data());
+  Int_t nRubrics = fRubrics->GetSize();
+  rubric->SetUniqueID(nRubrics);
+  fRubrics->AddLast(rubric);
+  
+  // save the maximum number of autorized key words
+  if (nRubrics+1 > fRubricsSize->GetSize()) fRubricsSize->Set(2*fRubricsSize->GetSize());
+  (*fRubricsSize)[nRubrics] = maxNKeyWords;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Init()
+{
+  /// Initialize the internal counters from the added rubrics.
+  
+  // create the counters
+  delete fCounters;
+  fCounters = new THnSparseT<TArrayI>("hCounters", "hCounters", fRubrics->GetSize(), fRubricsSize->GetArray(), 0x0, 0x0);
+  
+  // loop over axis
+  TObject* rubric = 0x0;
+  TIter nextRubric(fRubrics);
+  while ((rubric = nextRubric())) {
+    TAxis* axis = fCounters->GetAxis((Int_t)rubric->GetUniqueID());
+    
+    // set axis name
+    axis->SetName(rubric->GetName());
+    
+    // set labels if already known
+    TObjArray* keyWords = dynamic_cast<TObjArray*>(rubric);
+    if (keyWords) {
+      TObjString* label = 0x0;
+      Int_t bin = 1;
+      TIter nextLabel(keyWords);
+      while ((label = static_cast<TObjString*>(nextLabel()))) axis->SetBinLabel(bin++, label->String().Data());
+    }
+    
+  }
+  
+}
+
+//-----------------------------------------------------------------------
+const Int_t* AliCounterCollection::FindBins(const TString& externalKey, Bool_t allocate, Int_t& nEmptySlots)
+{
+  /// Return the corresponding bins ordered by rubric or 0x0 if externalKey is not valid.
+  /// The externalKey format must be rubric:keyWord/rubric:keyWord/rubric:keyWord/...
+  /// If allocate = kTRUE, new key words are added to the corresponding rubric if possible.
+  /// If a rubric is not filled in, the coresponding slot contain -1 in the array.
+  /// It is the responsability of the user to delete the returned array.
+  
+  // produce an empty array of keys
+  Int_t nRubrics = fRubrics->GetSize();
+  Int_t* bins = new Int_t[nRubrics];
+  memset(bins, -1, sizeof(Int_t) * nRubrics);
+  nEmptySlots = nRubrics;
+  Bool_t isValid = kTRUE;
+  
+  // get the list of rubric:keyWord pairs
+  TObjArray* rubricKeyPairs = externalKey.Tokenize("/");
+  
+  // loop over each rubric:keyWord pair
+  TObjString* pair = 0x0;
+  TIter next(rubricKeyPairs);
+  while ((pair = static_cast<TObjString*>(next()))) {
+    
+    // get both rubric and associated key word
+    TObjArray* rubricKeyPair = pair->String().Tokenize(":");
+    
+    // check the format of the pair
+    if (rubricKeyPair->GetEntriesFast() != 2) {
+      AliError("invalid key format");
+      isValid = kFALSE;
+      delete rubricKeyPair;
+      break;
+    }
+    
+    // get the axis corresponding to that rubric
+    Int_t dim = FindDim(static_cast<TObjString*>(rubricKeyPair->UncheckedAt(0))->String());
+    if (dim < 0) {
+      isValid = kFALSE;
+      delete rubricKeyPair;
+      break;
+    }
+    
+    // find the bin corresponding to that key word
+    Int_t bin = FindBin(dim, static_cast<TObjString*>(rubricKeyPair->UncheckedAt(1))->String(), allocate);
+    if (bin < 0) {
+      isValid = kFALSE;
+      delete rubricKeyPair;
+      break;
+    }
+    
+    // check if the array of keys already contains something for that rubric
+    if (bins[dim] >= 0) {
+      AliWarning("key already given for that rubric --> ignored");
+      delete rubricKeyPair;
+      continue;
+    }
+    
+    // store the corresponding bin for that slot
+    bins[dim] = bin;
+    nEmptySlots--;
+    
+    // clean memory
+    delete rubricKeyPair;
+  }
+  
+  // delete the array in case of problem
+  if (!isValid) {
+    delete[] bins;
+    bins = 0x0;
+    nEmptySlots = nRubrics;
+  }
+  
+  // clean memory
+  delete rubricKeyPairs;
+  
+  return bins;
+}
+
+//-----------------------------------------------------------------------
+Int_t AliCounterCollection::FindDim(const TString& rubricName) const
+{
+  /// Return the dimension corresponding to that rubric (or -1 in case of failure).
+  TObject* rubric = fRubrics->FindObject(rubricName.Data());
+  if (!rubric) {
+    AliError(Form("invalid rubric: %s",rubricName.Data()));
+    return -1;
+  }
+  return (Int_t) rubric->GetUniqueID();
+}
+
+//-----------------------------------------------------------------------
+Int_t AliCounterCollection::FindBin(Int_t dim, const TString& keyWord, Bool_t allocate)
+{
+  /// Return the bin number corresponding to that key word (or -1 in case of failure).
+  /// If allocate = kTRUE, try to add the key word if possible.
+  
+  TAxis* axis = fCounters->GetAxis(dim);
+  
+  // look for the bin corresponding to keyWord
+  THashList* labels = axis->GetLabels();
+  TObjString* label = (labels) ? static_cast<TObjString*>(labels->FindObject(keyWord.Data())) : 0x0;
+  Int_t bin = (label) ? (Int_t)label->GetUniqueID() : -1;
+  
+  // in case the keyWord does not exist, try to add it if required
+  if (bin<0 && allocate) {
+    Int_t nLabels = (labels) ? labels->GetSize() : 0;
+    if (nLabels < axis->GetNbins()) {
+      bin = nLabels+1;
+      axis->SetBinLabel(bin, keyWord.Data());
+    }
+  }
+  
+  if (bin<0) AliError(Form("invalid key word: %s:%s",axis->GetName(),keyWord.Data()));
+  
+  return bin;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::CleanListOfStrings(TObjArray* list)
+{
+  /// Make sure all strings appear only once in this list
+  
+  // remove multiple-occurrence
+  Int_t nEntries = list->GetEntriesFast();
+  for (Int_t i = 0; i < nEntries; i++) {
+    TObjString* entry1 = static_cast<TObjString*>(list->UncheckedAt(i));
+    if (!entry1) continue;
+    for (Int_t j = i+1; j < nEntries; j++) {
+      TObjString* entry2 = static_cast<TObjString*>(list->UncheckedAt(j));
+      if (entry2 && entry2->IsEqual(entry1)) {
+       AliWarning(Form("multiple-occurence of string \"%s\" --> removed",entry2->String().Data()));
+       list->RemoveAt(j);
+      }
+    }
+  }
+  
+  // remove empty slots
+  list->Compress();
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Count(TString externalKey, Int_t value)
+{
+  /// Add "value" to the counter referenced by "externalKey".
+  /// The externalKey format must be rubric:keyWord/rubric:keyWord/rubric:keyWord/...
+  
+  if (value < 1) return;
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  externalKey.ToUpper();
+  
+  // convert external to internal key
+  Int_t nEmptySlots = 0;
+  const Int_t* bins = FindBins(externalKey, kTRUE, nEmptySlots);
+  if (!bins) return;
+  
+  // check for empty slots
+  if (nEmptySlots > 0) {
+    AliError("incomplete key");
+    delete[] bins;
+    return;
+  }
+  
+  // increment the corresponding counter
+  fCounters->AddBinContent(bins, (Double_t)value);
+  
+  // clean memory
+  delete[] bins;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Print(const Option_t* opt) const
+{
+  /// Print every individual counters if opt=="", else call "Print(TString rubrics=opt, TString selections="")".
+  
+  if (strcmp(opt,"")) {
+    const_cast<AliCounterCollection*>(this)->Print(opt, "");
+    return;
+  }
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  if (fCounters->GetNbins() == 0) {
+    printf("\nall counters are empty\n\n");
+    return;
+  }
+  
+  Int_t nRubrics = fCounters->GetNdimensions();
+  Int_t* bins = new Int_t[nRubrics];
+  
+  // loop over every filled counters
+  for (Long64_t i=0; i<fCounters->GetNbins(); ++i) {
+    
+    // get the content of the bin
+    Int_t value = (Int_t) fCounters->GetBinContent(i, bins);
+    
+    // build the corresponding counter name
+    TString counter;
+    for (Int_t j=0; j<nRubrics; j++) counter += Form("/%s",fCounters->GetAxis(j)->GetBinLabel(bins[j]));
+    counter += "/";
+    
+    // print value
+    printf("\n%s   %d", counter.Data(), value);
+  }
+  printf("\n\n");
+  
+  // clean memory
+  delete[] bins;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintKeyWords() const
+{
+  /// Print the full list of key words.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  // loop over rubrics
+  Int_t nRubrics = fCounters->GetNdimensions();
+  for (Int_t iDim=0; iDim<nRubrics; iDim++) {
+    TAxis* axis = fCounters->GetAxis(iDim);
+    
+    // print rubric's name
+    printf("\n%s:", axis->GetName());
+    
+    // loop over key words
+    Bool_t first = kTRUE;
+    TObjString* label = 0x0;
+    TIter nextLabel(axis->GetLabels());
+    while ((label = static_cast<TObjString*>(nextLabel()))) {
+      
+      //print key word's name
+      if (first) {
+       printf("%s", label->String().Data());
+       first = kFALSE;
+      } else printf(",%s", label->String().Data());
+      
+    }
+  }
+  printf("\n\n");
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintValue(TString selections)
+{
+  /// Print value of selected counter.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  selections.ToUpper();
+  
+  // convert external to internal key
+  Int_t nEmptySlots = 0;
+  const Int_t* selectedBins = FindBins(selections, kFALSE, nEmptySlots);
+  if (!selectedBins) return;
+  
+  // check for empty slots
+  if (nEmptySlots > 0) {
+    AliError("incomplete key");
+    delete[] selectedBins;
+    return;
+  }
+  
+  // print value
+  printf("\n%d\n\n", (Int_t) fCounters->GetBinContent(selectedBins));
+  
+  // clean memory
+  delete[] selectedBins;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Print(TString rubrics, TString selections)
+{
+  /// Print desired rubrics for the given selection:
+  /// - format of "rubrics" is rubric1/rubric2/.. (order matters only for output).
+  /// - format of "selections" is rubric:keyWord/rubric:keyWord/.. (order does not matter).
+  /// If "data" contains 1 rubric, the output will be one counter for each element of that rubric.
+  /// If "data" contains 2 rubrics, the output will be an array of counters, rubric1 vs rubric2.
+  /// If "data" contains 3 rubrics, the output will be an array rubric1 vs rubric2 for each element in rubric3.
+  /// ...
+  /// Results are integrated over rubrics not specified neither in "rubrics" nor in "selections".
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  rubrics.ToUpper();
+  selections.ToUpper();
+  
+  // get the rubrics to print
+  TObjArray* rubricsToPrint = rubrics.Tokenize("/");
+  if (rubricsToPrint->GetEntriesFast() == 0) {
+    delete rubricsToPrint;
+    return;
+  }
+  
+  // remove rubrics called twice
+  CleanListOfStrings(rubricsToPrint);
+  
+  // project counters in the rubrics to print according to the selections
+  TObject* hist = Projection(*rubricsToPrint, selections);
+  if (!hist) {
+    delete rubricsToPrint;
+    return;
+  }
+  
+  // print counters
+  Int_t nRubricsToPrint = rubricsToPrint->GetEntriesFast();
+  if (nRubricsToPrint == 1 && (static_cast<TH1D*>(hist))->Integral() > 0.)
+    PrintList(static_cast<TH1D*>(hist));
+  else if (nRubricsToPrint == 2 && (static_cast<TH2D*>(hist))->Integral() > 0.)
+    PrintArray(static_cast<TH2D*>(hist));
+  else if (nRubricsToPrint > 2 && (static_cast<THnSparse*>(hist))->GetNbins() > 0)
+    PrintListOfArrays(static_cast<THnSparse*>(hist));
+  else
+    printf("\nselected counters are empty\n\n");
+  
+  // clean memory
+  delete rubricsToPrint;
+  delete hist;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintSum(TString rubric, TString selections)
+{
+  /// Print the overall statistics under the given rubric for the given selection:
+  /// - format of "selections" is rubric:keyWord/rubric:keyWord/.. (order does not matter).
+  /// Result is integrated over rubrics not specified neither in "rubric" nor in "selections".
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  rubric.ToUpper();
+  selections.ToUpper();
+  
+  // fill the rubric to sum
+  TObjArray rubricsToSum(1);
+  rubricsToSum.SetOwner();
+  rubricsToSum.AddLast(new TObjString(rubric.Data()));
+  
+  // project counters in the rubric to sum according to the selections
+  TH1D* hist = static_cast<TH1D*>(Projection(rubricsToSum, selections));
+  if (!hist) return;
+  
+  // check for empty rubric
+  THashList* labels = hist->GetXaxis()->GetLabels();
+  if (!labels) {
+    printf("\n0\n\n");
+    return;
+  }
+  
+  // print the sum of counters under that rubric
+  TObjString* any = static_cast<TObjString*>(labels->FindObject("ANY"));
+  if (any) printf("\n%d\n\n", (Int_t) hist->GetBinContent((Int_t)any->GetUniqueID()));
+  else printf("\n%d\n\n", (Int_t) hist->Integral());
+  
+  // clean memory
+  delete hist;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintList(const TH1D* hist) const
+{
+  /// Print the content of 1D histogram as a list.
+  
+  // set the format to print labels
+  THashList* labels = hist->GetXaxis()->GetLabels();
+  TString format(Form("\n%%%ds %%9d",GetMaxLabelSize(labels)));
+  
+  // print value for each label
+  TObjString* label = 0x0;
+  TIter nextLabel(labels);
+  while ((label = static_cast<TObjString*>(nextLabel()))) {
+    Int_t bin = (Int_t) label->GetUniqueID();
+    printf(format.Data(), label->String().Data(), (Int_t) hist->GetBinContent(bin));
+  }
+  printf("\n\n");
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintArray(const TH2D* hist) const
+{
+  /// Print the content of 2D histogram as an array.
+  
+  // set the format to print labels in X direction
+  THashList* labelsX = hist->GetXaxis()->GetLabels();
+  TString formatX(Form("\n%%%ds ",GetMaxLabelSize(labelsX)));
+  
+  // set the format to print labels in Y direction and values
+  THashList* labelsY = hist->GetYaxis()->GetLabels();
+  Int_t maxLabelSizeY = TMath::Max(9, GetMaxLabelSize(labelsY));
+  TString formatYs(Form("%%%ds ",maxLabelSizeY));
+  TString formatYd(Form("%%%dd ",maxLabelSizeY));
+  
+  // print labels in Y axis
+  printf(formatX.Data()," ");
+  TObjString* labelY = 0x0;
+  TIter nextLabelY(labelsY);
+  while ((labelY = static_cast<TObjString*>(nextLabelY())))
+    printf(formatYs.Data(), labelY->String().Data());
+  
+  // fill array for each label in X axis
+  TObjString* labelX = 0x0;
+  TIter nextLabelX(labelsX);
+  while ((labelX = static_cast<TObjString*>(nextLabelX()))) {
+    Int_t binX = (Int_t) labelX->GetUniqueID();
+    
+    // print label X
+    printf(formatX.Data(), labelX->String().Data());
+    
+    // print value for each label in Y axis
+    nextLabelY.Reset();
+    while ((labelY = static_cast<TObjString*>(nextLabelY()))) {
+      Int_t binY = (Int_t) labelY->GetUniqueID();
+      printf(formatYd.Data(), (Int_t) hist->GetBinContent(binX, binY));
+    }
+  }
+  printf("\n\n");
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::PrintListOfArrays(const THnSparse* hist) const
+{
+  /// Print the content of nD histogram as a list of arrays.
+  
+  // set the format to print labels in X direction
+  THashList* labelsX = hist->GetAxis(0)->GetLabels();
+  TString formatX(Form("\n%%%ds ",GetMaxLabelSize(labelsX)));
+  
+  // set the format to print labels in Y direction and values
+  THashList* labelsY = hist->GetAxis(1)->GetLabels();
+  Int_t maxLabelSizeY = TMath::Max(9, GetMaxLabelSize(labelsY));
+  TString formatYs(Form("%%%ds ",maxLabelSizeY));
+  TString formatYd(Form("%%%dd ",maxLabelSizeY));
+  
+  // create a list containing each combination of labels refering the arrays to be printout
+  TList listOfCombis;
+  listOfCombis.SetOwner();
+  
+  // add a first empty combination
+  Int_t nDim = hist->GetNdimensions();
+  listOfCombis.AddLast(new TObjArray(nDim-2));
+  
+  // loop over the nDim-2 other rubrics
+  for (Int_t i=2; i<nDim; i++) {
+    
+    // save the last label of that rubic
+    THashList* labels = hist->GetAxis(i)->GetLabels();
+    TObjString* lastLabel = (labels) ? static_cast<TObjString*>(labels->Last()) : 0x0;
+    if (!lastLabel) return;
+    
+    // prepare iteration over the list of labels
+    TIter nextLabel(labels);
+    
+    // loop over existing combinations
+    TObjLink* lnk = listOfCombis.FirstLink();
+    while (lnk) {
+      
+      // get the current combination
+      TObjArray* currentCombi = static_cast<TObjArray*>(lnk->GetObject());
+      
+      // loop over labels in the current rubric
+      nextLabel.Reset();
+      TObjString* label = 0x0;
+      while ((label = static_cast<TObjString*>(nextLabel()))) {
+       
+       // stop at the last one
+       if (label == lastLabel) break;
+       
+       // copy the current combination, add the current label to it and add it to the list of combinations
+       TObjArray* combi = new TObjArray(*currentCombi);
+       combi->AddLast(label);
+       listOfCombis.AddBefore(lnk, combi);
+      }
+      
+      // add the last label to the current combination
+      currentCombi->AddLast(lastLabel);
+      
+      lnk = lnk->Next();
+    }
+    
+  }
+  
+  // create bin coordinates to access individual counters
+  Int_t* bins = new Int_t[nDim];
+  
+  // loop over each combination of labels
+  TObjArray* combi = 0x0;
+  TIter nextCombi(&listOfCombis);
+  while ((combi = static_cast<TObjArray*>(nextCombi()))) {
+    
+    // make the name of the combination and fill the corresponding bin coordinates
+    TString combiName = "/";
+    for (Int_t i=2; i<nDim; i++) {
+      TObjString* label = static_cast<TObjString*>(combi->UncheckedAt(i-2));
+      combiName += Form("%s/",label->String().Data());
+      bins[i] = (Int_t)label->GetUniqueID();
+    }
+    
+    // skip empty array
+    Bool_t empty = kTRUE;
+    TObjString* labelX = 0x0;
+    TObjString* labelY = 0x0;
+    TIter nextLabelX(labelsX);
+    TIter nextLabelY(labelsY);
+    while ((labelX = static_cast<TObjString*>(nextLabelX()))) {
+      bins[0] = (Int_t) labelX->GetUniqueID();
+      nextLabelY.Reset();
+      while ((labelY = static_cast<TObjString*>(nextLabelY()))) {
+       bins[1] = (Int_t) labelY->GetUniqueID();
+       if (((Int_t) hist->GetBinContent(bins)) > 0) {
+         empty = kFALSE;
+         break;
+       }
+      }
+      if (!empty) break;
+    }
+    if (empty) continue;
+    
+    // print the name of the combination of labels refering the incoming array
+    printf("\n%s:\n",combiName.Data());
+    
+    // print labels in Y axis
+    printf(formatX.Data()," ");
+    nextLabelY.Reset();
+    while ((labelY = static_cast<TObjString*>(nextLabelY())))
+      printf(formatYs.Data(), labelY->String().Data());
+    
+    // fill array for each label in X axis
+    nextLabelX.Reset();
+    while ((labelX = static_cast<TObjString*>(nextLabelX()))) {
+      bins[0] = (Int_t) labelX->GetUniqueID();
+      
+      // print label X
+      printf(formatX.Data(), labelX->String().Data());
+      
+      // print value for each label in Y axis
+      nextLabelY.Reset();
+      while ((labelY = static_cast<TObjString*>(nextLabelY()))) {
+       bins[1] = (Int_t) labelY->GetUniqueID();
+       printf(formatYd.Data(), (Int_t) hist->GetBinContent(bins));
+      }
+    }
+    printf("\n\n");
+  }
+  
+  // clean memory
+  delete[] bins;
+}
+
+//-----------------------------------------------------------------------
+Int_t AliCounterCollection::GetMaxLabelSize(THashList* labels) const
+{
+  /// Return the number of characters of the longest label.
+  Int_t maxLabelSize = 0;
+  TObjString* label = 0x0;
+  TIter nextLabel(labels);
+  while ((label = static_cast<TObjString*>(nextLabel())))
+    maxLabelSize = TMath::Max(maxLabelSize, label->String().Length());
+  return maxLabelSize;
+}
+
+//-----------------------------------------------------------------------
+TH1D* AliCounterCollection::Draw(TString rubric, TString selections)
+{
+  /// Draw counters of the rubric "rubric" for the given "selection".
+  /// Format of "selections" is rubric:keyWord/rubric:keyWord/.. (order does not matter).
+  /// Results are integrated over rubrics not specified neither in "rubric1" nor in "selections".
+  /// It is the responsability of the user to delete the returned histogram.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return 0x0;
+  }
+  
+  rubric.ToUpper();
+  selections.ToUpper();
+  
+  // fill the rubrics to print
+  TObjArray rubricsToPrint(1);
+  rubricsToPrint.SetOwner();
+  rubricsToPrint.AddLast(new TObjString(rubric.Data()));
+  
+  // project counters in the rubrics to print according to the selections
+  TH1D* hist = static_cast<TH1D*>(Projection(rubricsToPrint, selections));
+  
+  // draw counters
+  if (hist) {
+    
+    // draw histogram
+    hist->Draw("htext");
+    hist->SetStats(kFALSE);
+    
+    // set title
+    TString title = "Selections:  ";
+    selections.Remove(TString::kBoth, '/');
+    if (selections.Length() > 0) title += Form("%s/", selections.Data());
+    TObject* rub = 0x0;
+    TIter nextRubric(fRubrics);
+    while ((rub = nextRubric())) {
+      if (selections.Contains(Form("%s:",rub->GetName()))) continue;
+      if (rubricsToPrint.Contains(rub->GetName())) continue;
+      title += Form("%s:ANY/", rub->GetName());
+    }
+    title.ReplaceAll("/", "  ");
+    hist->SetTitle(title.Data());
+    
+    // draw X axis
+    TAxis* axis = hist->GetXaxis();
+    THashList* labels = axis->GetLabels();
+    Int_t nLabels = (labels) ? labels->GetSize() : 1;
+    axis->SetRange(1,nLabels);
+    axis->SetNdivisions(1,kFALSE);
+    axis->SetTitle(rubric.Data());
+    
+    // draw Y axis
+    hist->GetYaxis()->SetTitle("Counts");
+  }
+  
+  return hist;
+}
+
+//-----------------------------------------------------------------------
+TH2D* AliCounterCollection::Draw(TString rubric1, TString rubric2, TString selections)
+{
+  /// Draw counters of the "rubric1" vs "rubric2" for the given "selection".
+  /// Format of "selections" is rubric:keyWord/rubric:keyWord/.. (order does not matter).
+  /// Results are integrated over rubrics not specified neither in "rubric1", "rubric2" nor in "selections".
+  /// It is the responsability of the user to delete the returned histogram.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return 0x0;
+  }
+  
+  rubric1.ToUpper();
+  rubric2.ToUpper();
+  selections.ToUpper();
+  
+  // fill the rubrics to print
+  TObjArray rubricsToPrint(2);
+  rubricsToPrint.SetOwner();
+  rubricsToPrint.AddLast(new TObjString(rubric2.Data()));
+  rubricsToPrint.AddLast(new TObjString(rubric1.Data()));
+  
+  // project counters in the rubrics to print according to the selections
+  TH2D* hist = static_cast<TH2D*>(Projection(rubricsToPrint, selections));
+  
+  // draw counters
+  if (hist) {
+    
+    // draw histogram
+    hist->Draw("text");
+    hist->SetStats(kFALSE);
+    
+    // set title
+    TString title = "Selections:  ";
+    selections.Remove(TString::kBoth, '/');
+    if (selections.Length() > 0) title += Form("%s/", selections.Data());
+    TObject* rub = 0x0;
+    TIter nextRubric(fRubrics);
+    while ((rub = nextRubric())) {
+      if (selections.Contains(Form("%s:",rub->GetName()))) continue;
+      if (rubricsToPrint.Contains(rub->GetName())) continue;
+      title += Form("%s:ANY/", rub->GetName());
+    }
+    title.ReplaceAll("/", "  ");
+    hist->SetTitle(title.Data());
+    
+    // draw X axis
+    TAxis* axisX = hist->GetXaxis();
+    THashList* labelsX = axisX->GetLabels();
+    Int_t nLabelsX = (labelsX) ? labelsX->GetSize() : 1;
+    axisX->SetRange(1,nLabelsX);
+    axisX->SetNdivisions(1,kFALSE);
+    axisX->SetTitle(rubric2.Data());
+    
+    // draw Y axis
+    TAxis* axisY = hist->GetYaxis();
+    THashList* labelsY = axisY->GetLabels();
+    Int_t nLabelsY = (labelsY) ? labelsY->GetSize() : 1;
+    axisY->SetRange(1,nLabelsY);
+    axisY->SetNdivisions(1,kFALSE);
+    axisY->SetTitle(rubric1.Data());
+  }
+  
+  return hist;
+}
+
+//-----------------------------------------------------------------------
+TObject* AliCounterCollection::Projection(const TObjArray& data, const TString& selections)
+{
+  /// Return desired "data" for the given "selection" stored in a new histogram or 0x0 in case of failure.
+  /// The type of the histogram (TH1D, TH2D or THnSparse) depend on the number of data.
+  /// It is the responsability of the user to delete the returned histogram.
+  
+  // get the corresponding dimensions
+  Int_t nTargetDim = data.GetEntriesFast();
+  Int_t* targetDims = new Int_t[nTargetDim];
+  for (Int_t i=0; i<nTargetDim; i++) {
+    targetDims[i] = FindDim(static_cast<TObjString*>(data.UncheckedAt(i))->String());
+    if (targetDims[i] < 0) {
+      delete[] targetDims;
+      return 0x0;
+    }
+  }
+  
+  // find bins to select
+  Int_t nEmptySlots = 0;
+  const Int_t* selectedBins = FindBins(selections, kFALSE, nEmptySlots);
+  if (!selectedBins) {
+    delete[] targetDims;
+    return 0x0;
+  }
+  
+  // apply selection for each rubric
+  Int_t nRubrics = fCounters->GetNdimensions();
+  for (Int_t iDim=0; iDim<nRubrics; iDim++) {
+    TAxis* axis = fCounters->GetAxis(iDim);
+    
+    // select the desired key word
+    if (selectedBins[iDim] >= 0) axis->SetRange(selectedBins[iDim], selectedBins[iDim]);
+    
+    // or select all key words
+    else if (data.Contains(axis->GetName())) axis->SetRange();
+    
+    // or integrate over all cases
+    else {
+      THashList* labels = axis->GetLabels();
+      TObjString* label = (labels) ? static_cast<TObjString*>(labels->FindObject("ANY")) : 0x0;
+      Int_t binAny = (label) ? (Int_t)label->GetUniqueID() : -1;
+      if (binAny >= 0) axis->SetRange(binAny, binAny);
+      else axis->SetRange();
+    }
+  }
+  
+  // do projection
+  TObject* hist = 0x0;
+  if (nTargetDim == 1) {
+    
+    // project counters to TH1D
+    hist = fCounters->Projection(targetDims[0]);
+    
+    // reset bin labels lost when producing TH1D
+    if (selectedBins[targetDims[0]] >= 0)
+      static_cast<TH1D*>(hist)->GetXaxis()->SetBinLabel(1, fCounters->GetAxis(targetDims[0])->GetBinLabel(selectedBins[targetDims[0]]));
+    else {
+      TObjString* label;
+      TIter nextLabel(fCounters->GetAxis(targetDims[0])->GetLabels());
+      while ((label = static_cast<TObjString*>(nextLabel())))
+       static_cast<TH1D*>(hist)->GetXaxis()->SetBinLabel((Int_t)label->GetUniqueID(), label->String().Data());
+    }
+    
+  } else if (nTargetDim == 2) {
+    
+    // project counters to TH2D (warning X and Y inverted in THnSparse::Projection(X,Y))
+    hist = fCounters->Projection(targetDims[1], targetDims[0]);
+    
+    // reset bin labels in X axis lost when producing TH2D
+    if (selectedBins[targetDims[0]] >= 0)
+      static_cast<TH2D*>(hist)->GetXaxis()->SetBinLabel(1, fCounters->GetAxis(targetDims[0])->GetBinLabel(selectedBins[targetDims[0]]));
+    else {
+      TObjString* label;
+      TIter nextLabel(fCounters->GetAxis(targetDims[0])->GetLabels());
+      while ((label = static_cast<TObjString*>(nextLabel())))
+       static_cast<TH2D*>(hist)->GetXaxis()->SetBinLabel((Int_t)label->GetUniqueID(), label->String().Data());
+    }
+    
+    // reset bin labels in Y axis lost when producing TH2D
+    if (selectedBins[targetDims[1]] >= 0)
+      static_cast<TH2D*>(hist)->GetYaxis()->SetBinLabel(1, fCounters->GetAxis(targetDims[1])->GetBinLabel(selectedBins[targetDims[1]]));
+    else {
+      TObjString* label;
+      TIter nextLabel(fCounters->GetAxis(targetDims[1])->GetLabels());
+      while ((label = static_cast<TObjString*>(nextLabel())))
+       static_cast<TH2D*>(hist)->GetYaxis()->SetBinLabel((Int_t)label->GetUniqueID(), label->String().Data());
+    }
+    
+  } else {
+    
+    // project counters to THnSparse (labels are not lost in that case)
+    hist = fCounters->Projection(nTargetDim, targetDims);
+    
+    // reset bin labels in case only one bin has been selected
+    for (Int_t i=0; i<nTargetDim; i++) {
+      if (selectedBins[targetDims[i]] >= 0) {
+       TAxis* axis = static_cast<THnSparse*>(hist)->GetAxis(i);
+       axis->GetLabels()->Clear();
+       axis->SetBinLabel(1, fCounters->GetAxis(targetDims[i])->GetBinLabel(selectedBins[targetDims[i]]));
+      }
+    }
+    
+  }
+  
+  // clean memory
+  delete[] targetDims;
+  delete[] selectedBins;
+  
+  return hist;
+}
+
+//-----------------------------------------------------------------------
+Int_t* AliCounterCollection::CheckConsistency(const AliCounterCollection* c)
+{
+  /// Consistency check of the two counter collections. To be consistent, both counters
+  /// must have the same rubrics with the same list of authorized key words if any.
+  /// Return the correspondence between the local rubric ordering and the one of the other counter,
+  /// or 0x0 in case of problem. It is the responsability of the user to delete the returned array.
+  
+  if (!fCounters || !c->fCounters) {
+    AliError("counters are not initialized");
+    return 0x0;
+  }
+  
+  // check if the number of rubrics is the same
+  Int_t nRubrics = fRubrics->GetSize();
+  if (c->fRubrics->GetSize() != nRubrics) {
+    AliError("both counters do not contain the same number of rubrics");
+    return 0x0;
+  }
+  
+  Int_t* otherDims = new Int_t[nRubrics];
+  
+  // loop over local rubrics
+  TObject* rubric1 = 0x0;
+  TIter nextRubric(fRubrics);
+  while ((rubric1 = nextRubric())) {
+    
+    // find that rubric in the other counter
+    TObject* rubric2 = c->fRubrics->FindObject(rubric1->GetName());
+    if (!rubric2) {
+      AliError(Form("the other counter does not contain the rubric %s", rubric1->GetName()));
+      delete[] otherDims;
+      return 0x0;
+    }
+    
+    // check the list of authorized key words if any
+    TObjArray* keyWords1 = dynamic_cast<TObjArray*>(rubric1);
+    TObjArray* keyWords2 = dynamic_cast<TObjArray*>(rubric2);
+    if (keyWords1 && keyWords2) {
+      
+      // check if the number of key words is the same
+      if (keyWords1->GetEntriesFast() != keyWords2->GetEntriesFast()) {
+       AliError("that rubric does not contain the same number of authorized key words in both counters");
+       delete[] otherDims;
+       return 0x0;
+      }
+      
+      // loop over local key words
+      TObjString* keyWord = 0x0;
+      TIter nextKeyWord(keyWords1);
+      while ((keyWord = static_cast<TObjString*>(nextKeyWord()))) {
+       
+       // find that key word in the corresponding rubric of the other counter
+       if (!keyWords2->FindObject(keyWord->String().Data())) {
+         AliError(Form("rubric %s does not contain the key word %s in the other counter", rubric1->GetName(), keyWord->String().Data()));
+         delete[] otherDims;
+         return 0x0;
+       }
+       
+      }
+      
+    } else if (keyWords1 || keyWords2) {
+      
+      // that rubric has not been initialized the same way in both counter
+      if (keyWords1) {
+       AliError(Form("rubric %s of the other counter does not contain a list of authorized key words while this does", rubric1->GetName()));
+      } else {
+       AliError(Form("rubric %s of this counter does not contain a list of authorized key words while the other does", rubric1->GetName()));
+      }
+      delete[] otherDims;
+      return 0x0;
+      
+    }
+    
+    // save the correspondence of rubric IDs in both counters
+    otherDims[rubric1->GetUniqueID()] = rubric2->GetUniqueID();
+    
+  }
+  
+  return otherDims;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Add(const AliCounterCollection* counter)
+{
+  /// Add the given AliCounterCollections to this. They must have the
+  /// same rubrics with the same list of authorized key words if any.
+  
+  // check the consistency between the other counter and this and get the correspondences between rubric IDs.
+  Int_t* otherDims = CheckConsistency(counter);
+  if (!otherDims) return;
+  
+  Int_t nRubrics = fCounters->GetNdimensions();
+  Int_t* thisBins = new Int_t[nRubrics];
+  Int_t* otherBins = new Int_t[nRubrics];
+  
+  // loop over every filled bins inside the other counter
+  for (Long64_t i = 0; i < counter->fCounters->GetNbins(); i++) {
+    
+    // get the content of the bin
+    Double_t value = counter->fCounters->GetBinContent(i, otherBins);
+    
+    // convert "other" bin coordinates to "this" bin coordinates
+    Bool_t ok = kTRUE;
+    for (Int_t dim = 0; dim < nRubrics; dim++) {
+      TString label = counter->fCounters->GetAxis(otherDims[dim])->GetBinLabel(otherBins[otherDims[dim]]);
+      thisBins[dim] = FindBin(dim, label, kTRUE);
+      if (thisBins[dim] < 0) {
+       AliError("this counter is full, unable to add that key word");
+       ok = kFALSE;
+       break;
+      }
+    }
+    if (!ok) continue;
+    
+    // increment the corresponding local counter
+    fCounters->AddBinContent(thisBins, value);
+  }
+  
+  // clean memory
+  delete[] otherDims;
+  delete[] thisBins;
+  delete[] otherBins;
+}
+
+//-----------------------------------------------------------------------
+Long64_t AliCounterCollection::Merge(TCollection* list)
+{
+  /// Merge this with a list of AliCounterCollections. All AliCounterCollections provided
+  /// must have the same rubrics with the same list of authorized key words if any.
+  
+  if (!list || !fCounters) return 0;
+  if (list->IsEmpty()) return (Long64_t)fCounters->GetEntries();
+  
+  TIter next(list);
+  const TObject* obj = 0x0;
+  while ((obj = next())) {
+    
+    // check that "obj" is an object of the class AliCounterCollection
+    const AliCounterCollection* counter = dynamic_cast<const AliCounterCollection*>(obj);
+    if (!counter) {
+      AliError(Form("object named %s is not AliCounterCollection! Skipping it.", counter->GetName()));
+      continue;
+    }
+    
+    // merge counter to this one
+    Add(counter);
+    
+  }
+  
+  return (Long64_t)fCounters->GetEntries();
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Sort(Option_t* opt, Bool_t asInt)
+{
+  /// Sort rubrics defined without a list of authorized key words or all rubrics if opt=="all".
+  /// If asInt=kTRUE, key words are ordered as interger instead of alphabetically.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  Bool_t all = (!strcasecmp(opt, "all"));
+  
+  Bool_t somethingToSort = kFALSE;
+  Int_t nRubrics = fRubrics->GetSize();
+  Bool_t* rubricsToSort = new Bool_t[nRubrics];
+  memset(rubricsToSort, kFALSE, sizeof(Bool_t) * nRubrics);
+  
+  // choose rubrics to sort
+  TObject* rubric = 0x0;
+  TIter nextRubric(fRubrics);
+  while ((rubric = nextRubric())) {
+    
+    if (all || dynamic_cast<TObjString*>(rubric)) {
+      
+      // check if something to sort
+      THashList* labels = fCounters->GetAxis((Int_t)rubric->GetUniqueID())->GetLabels();
+      if (!labels || labels->GetSize() < 2) continue;
+      
+      // select that rubric
+      rubricsToSort[(Int_t)rubric->GetUniqueID()] = kTRUE;
+      somethingToSort = kTRUE;
+      
+    }
+    
+  }
+  
+  // sort selected rubrics if any
+  if (somethingToSort) Sort(rubricsToSort, asInt);
+  
+  // clean memory
+  delete[] rubricsToSort;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::SortRubric(TString rubric, Bool_t asInt)
+{
+  /// Sort only that rubric. If asInt=kTRUE, key words are ordered as interger instead of alphabetically.
+  
+  if (!fCounters) {
+    AliError("counters are not initialized");
+    return;
+  }
+  
+  rubric.ToUpper();
+  
+  // find the rubric to sort
+  Int_t dim = FindDim(rubric);
+  if (dim < 0) return;
+  
+  // check if something to sort
+  THashList* labels = fCounters->GetAxis(dim)->GetLabels();
+  if (!labels || labels->GetSize() < 2) return;
+  
+  // select that rubric
+  Int_t nRubrics = fRubrics->GetSize();
+  Bool_t* rubricsToSort = new Bool_t[nRubrics];
+  memset(rubricsToSort, kFALSE, sizeof(Bool_t) * nRubrics);
+  rubricsToSort[dim] = kTRUE;
+  
+  // sort it
+  Sort(rubricsToSort, asInt);
+  
+  // clean memory
+  delete[] rubricsToSort;
+}
+
+//-----------------------------------------------------------------------
+void AliCounterCollection::Sort(const Bool_t* rubricsToSort, Bool_t asInt)
+{
+  /// Sort labels (alphabetically or as integer) in each rubric flagged in "rubricsToSort".
+  
+  // create a new counter
+  THnSparse* oldCounters = fCounters;
+  Int_t nRubrics = fRubrics->GetSize();
+  fCounters = new THnSparseT<TArrayI>("hCounters", "hCounters", nRubrics, fRubricsSize->GetArray(), 0x0, 0x0);
+  Int_t** newBins = new Int_t*[nRubrics];
+  
+  // define the new axes
+  for (Int_t i=0; i<nRubrics; i++) {
+    TAxis* oldAxis = oldCounters->GetAxis(i);
+    TAxis* newAxis = fCounters->GetAxis(i);
+    
+    // set the name of the new axis
+    newAxis->SetName(oldAxis->GetName());
+    
+    // get old labels
+    THashList* oldLabels = oldAxis->GetLabels();
+    if (!oldLabels) continue;
+    
+    // sort them if required
+    if (rubricsToSort[i]) {
+      if (asInt) { oldLabels = SortAsInt(oldLabels); }
+      else { oldLabels->Sort(); }
+    }
+    
+    // set labels in the new axis and save the correspondence between new and old bins
+    newBins[i] = new Int_t[oldLabels->GetSize()+1];
+    TObjString* label = 0x0;
+    Int_t bin = 1;
+    TIter nextLabel(oldLabels);
+    while ((label = static_cast<TObjString*>(nextLabel()))) {
+      newAxis->SetBinLabel(bin, label->String().Data());
+      newBins[i][(Int_t)label->GetUniqueID()] = bin;
+      bin++;
+    }
+    
+    // clean memory
+    if (rubricsToSort[i] && asInt) delete oldLabels;
+  }
+  
+  // fill the new counters
+  Int_t* oldCoor = new Int_t[nRubrics];
+  Int_t* newCoor = new Int_t[nRubrics];
+  for (Long64_t i = 0; i < oldCounters->GetNbins(); i++) {
+    Double_t value = oldCounters->GetBinContent(i, oldCoor);
+    for (Int_t dim = 0; dim < nRubrics; dim++) newCoor[dim] = newBins[dim][oldCoor[dim]];    
+    fCounters->AddBinContent(newCoor, value);
+  }
+  
+  // clean memory
+  for (Int_t i=0; i<nRubrics; i++) delete[] newBins[i];
+  delete[] newBins;
+  delete[] oldCoor;
+  delete[] newCoor;
+  delete oldCounters;
+}
+
+//-----------------------------------------------------------------------
+THashList* AliCounterCollection::SortAsInt(const THashList* labels)
+{
+  /// Return a list (not owner) of labels sorted assuming they are integers.
+  /// It is the responsability of user to delete the returned list.
+  
+  THashList* sortedLabels = new THashList(labels->GetSize());
+  TIter nextSortedLabel(sortedLabels);
+  
+  // loop over labels
+  TObjString* label = 0x0;
+  TIter nextLabel(labels);
+  while ((label = static_cast<TObjString*>(nextLabel()))) {
+    
+    // find where to add it
+    TObjString* sortedLabel = 0x0;
+    nextSortedLabel.Reset();
+    while ((sortedLabel = static_cast<TObjString*>(nextSortedLabel())) &&
+          (sortedLabel->String().Atoi() <= label->String().Atoi())) {}
+    
+    // add it
+    if (sortedLabel) sortedLabels->AddBefore(sortedLabel, label);
+    else sortedLabels->AddLast(label);
+  }
+  
+  return sortedLabels;
+}
+
diff --git a/PWG3/muon/AliCounterCollection.h b/PWG3/muon/AliCounterCollection.h
new file mode 100644 (file)
index 0000000..20b523a
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef ALICOUNTERCOLLECTION_H
+#define ALICOUNTERCOLLECTION_H
+/* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
+ * See cxx source for full Copyright notice                               */
+
+/// \ingroup PWG3muon
+/// \class AliCounterCollection
+/// \brief generic class to handle a collection of counters
+// Author: Philippe Pillot
+
+#include <TNamed.h>
+
+class TString;
+class TObjArray;
+class THnSparse;
+class THashList;
+class TArrayI;
+class TH1D;
+class TH2D;
+class TCollection;
+
+class AliCounterCollection : public TNamed {
+public:
+  
+  AliCounterCollection(const char* name = "counters");
+  virtual ~AliCounterCollection();
+  
+  virtual void Clear(Option_t* = "");
+  
+  // Add a new rubric with the complete list of related key words separated by "/"
+  void AddRubric(TString name, TString listOfKeyWords);
+  // Add a new rubric containing at maximum maxNKeyWords key words
+  void AddRubric(TString name, Int_t maxNKeyWords);
+  // Initialize the internal counters from the added rubrics
+  void Init();
+  
+  // Add "value" to the counter referenced by "externalKey"
+  void Count(TString externalKey, Int_t value = 1);
+  
+  // Print every individual counters if opt=="", else call "Print(TString rubrics=opt, TString selections="")"
+  virtual void Print(const Option_t* opt = "") const;
+  // Print the full list of key words
+  void PrintKeyWords() const;
+  // Print value of selected counter
+  void PrintValue(TString selections);
+  // Print desired rubrics for the given selection
+  void Print(TString rubrics, TString selections);
+  // Print the overall statistics under the given rubric for the given selection
+  void PrintSum(TString rubric, TString selections = "");
+  
+  /// Overload TObject::Draw(Option_t*): Call "Draw(TString rubric1=opt, TString selections="")"
+  virtual void Draw(Option_t* opt = "") {Draw(opt, "");}
+  // Draw counters of the rubric "rubric1" for the given "selection"
+  TH1D* Draw(TString rubric1, TString selections);
+  // Draw counters of the "rubric1" vs "rubric2" for the given "selection"
+  TH2D* Draw(TString rubric1, TString rubric2, TString selections);
+  
+  // Add the given AliCounterCollections to this
+  void Add(const AliCounterCollection* counter);
+  
+  // Merge this with a list of AliCounterCollections
+  Long64_t Merge(TCollection* list);
+  
+  // Sort rubrics defined without a list of authorized key words or all rubrics if opt=="all"
+  void Sort(Option_t* opt = "", Bool_t asInt = kFALSE);
+  /// Sort only that rubric. If asInt=kTRUE, key words are ordered as interger instead of alphabetically
+  void SortRubric(TString rubric, Bool_t asInt = kFALSE);
+  
+private:
+  
+  /// Not implemented
+  AliCounterCollection(const AliCounterCollection& rhs);
+  /// Not implemented
+  AliCounterCollection& operator = (const AliCounterCollection& rhs);
+  
+  // Return the corresponding bins ordered by rubric or 0x0 if externalKey is not valid
+  const Int_t* FindBins(const TString& externalKey, Bool_t allocate, Int_t& nEmptySlots);
+  // Return the dimension corresponding to that rubric (or -1)
+  Int_t FindDim(const TString& rubricName) const;
+  // Return the bin number corresponding to that key word (or -1)
+  Int_t FindBin(Int_t dim, const TString& keyWord, Bool_t allocate);
+  
+  // Make sure all strings appear only once in this list
+  void CleanListOfStrings(TObjArray* list);
+  
+  // Print the content of 1D histogram as a list
+  void PrintList(const TH1D* hist) const;
+  // Print the content of 2D histogram as an array
+  void PrintArray(const TH2D* hist) const;
+  // Print the content of nD histogram as a list of arrays
+  void PrintListOfArrays(const THnSparse* hist) const;
+  
+  // Return the number of characters of the longest label
+  Int_t GetMaxLabelSize(THashList* labels) const;
+  
+  // Return desired "data" for the given "selection" stored in a new histogram or 0x0
+  TObject* Projection(const TObjArray& data, const TString& selections);
+  
+  // Consistency check of the two counter collections
+  Int_t* CheckConsistency(const AliCounterCollection* c);
+  
+  // Sort labels (alphabetically or as integer) in each rubric flagged in "rubricsToSort"
+  void Sort(const Bool_t* rubricsToSort, Bool_t asInt);
+  /// Return a list (not owner) of labels sorted assuming they are integers
+  THashList* SortAsInt(const THashList* labels);
+  
+private:
+  
+  THashList* fRubrics;        ///< list of rubrics with associated key words
+  TArrayI*   fRubricsSize;    ///< maximum number of key words in the corresponding rubric
+  THnSparse* fCounters;       ///< histogram of nRubrics dimensions used as n-dimensional counter
+  
+  ClassDef(AliCounterCollection, 1); // collection of mergeable counters
+};
+
+#endif
+
diff --git a/PWG3/muon/RunMuonQA.C b/PWG3/muon/RunMuonQA.C
new file mode 100644 (file)
index 0000000..bdd1aaf
--- /dev/null
@@ -0,0 +1,169 @@
+//--------------------------------------------------------------------------
+// Base macro for submitting muon QA analysis.
+//
+// In case it is not run with full aliroot, it needs the following libraries:
+//  - libSTEERBase.so
+//  - libESD.so
+//  - libAOD.so
+//  - libANALYSIS.so
+//  - libANALYSISalice.so
+//  - libCORRFW.so
+//  - libPWG3muon.so
+//
+// The macro reads ESDs and store outputs in standard output file (AnalysisResults.root)
+//
+// Author: Philippe Pillot - SUBATECH Nantes
+//--------------------------------------------------------------------------
+
+enum {kLocal, kInteractif_xml, kInteractif_ESDList};
+
+void RunMuonQA(TString inputFileName = "AliESDs.root", Bool_t selectPhysics = kTRUE, Short_t selectCharge = 0)
+{
+  TStopwatch timer;
+  timer.Start();
+  
+  // Check runing mode
+  Int_t mode = GetMode(inputFileName);
+  if(mode < 0){
+    Error("RunMuonQA","Please provide either an ESD root file or a collection of ESDs.");
+    return;
+  }
+  
+  // Load common libraries
+  gSystem->Load("libTree");
+  gSystem->Load("libGeom");
+  gSystem->Load("libVMC");
+  gSystem->Load("libPhysics");
+  gSystem->Load("libSTEERBase");
+  gSystem->Load("libESD");
+  gSystem->Load("libAOD");
+  gSystem->Load("libANALYSIS");
+  gSystem->Load("libANALYSISalice");
+  gSystem->Load("libCORRFW");
+  gSystem->Load("libPWG3muon");
+  
+  // Create input chain
+  TChain* chain = CreateChain(inputFileName);
+  if (!chain) return;
+  
+  // Create the analysis manager
+  AliAnalysisManager *mgr = new AliAnalysisManager("MuonQAAnalysis");
+  
+  // ESD input handler
+  AliESDInputHandler* esdH = new AliESDInputHandler();
+  esdH->SetReadFriends(kFALSE);
+  mgr->SetInputEventHandler(esdH);
+  
+  // event selection
+  gROOT->LoadMacro("$ALICE_ROOT/ANALYSIS/macros/AddTaskPhysicsSelection.C");
+  AliPhysicsSelectionTask* physicsSelection = AddTaskPhysicsSelection();
+  if(!physicsSelection) {
+    Error("RunMuonQA","AliPhysicsSelectionTask not created!");
+    return;
+  }
+  
+  // Muon QA analysis
+  gROOT->LoadMacro("$ALICE_ROOT/PWG3/muon/AddTaskMuonQA.C");
+  AliAnalysisTaskMuonQA* muonQA = AddTaskMuonQA(selectPhysics, selectCharge);
+  if(!muonQA) {
+    Error("RunMuonQA","AliAnalysisTaskMuonQA not created!");
+    return;
+  }
+  
+  // Enable debug printouts
+  //mgr->SetDebugLevel(2);
+  
+  // start local analysis
+  if (mgr->InitAnalysis()) {
+    mgr->PrintStatus();
+    mgr->StartAnalysis("local", chain);
+  }
+  
+  timer.Stop();
+  timer.Print();
+}
+
+//______________________________________________________________________________
+Int_t GetMode(TString inputFileName)
+{
+  if ( inputFileName.EndsWith(".xml") ) return kInteractif_xml;
+  else if ( inputFileName.EndsWith(".txt") ) return kInteractif_ESDList;
+  else if ( inputFileName.EndsWith(".root") ) return kLocal;
+  return -1;
+}
+
+//______________________________________________________________________________
+TChain* CreateChainFromCollection(const char *xmlfile)
+{
+  // Create a chain from the collection of tags.
+  TAlienCollection* coll = TAlienCollection::Open(xmlfile);
+  if (!coll) {
+    ::Error("CreateChainFromTags", "Cannot create an AliEn collection from %s", xmlfile);
+    return NULL;
+  }
+  
+  TGridResult* tagResult = coll->GetGridResult("",kFALSE,kFALSE);
+  AliTagAnalysis *tagAna = new AliTagAnalysis("ESD");
+  tagAna->ChainGridTags(tagResult);
+  
+  AliRunTagCuts      *runCuts = new AliRunTagCuts();
+  AliLHCTagCuts      *lhcCuts = new AliLHCTagCuts();
+  AliDetectorTagCuts *detCuts = new AliDetectorTagCuts();
+  AliEventTagCuts    *evCuts  = new AliEventTagCuts();
+  
+  // Check if the cuts configuration file was provided
+  if (!gSystem->AccessPathName("ConfigureCuts.C")) {
+    gROOT->LoadMacro("ConfigureCuts.C");
+    ConfigureCuts(runCuts, lhcCuts, detCuts, evCuts);
+  }
+  
+  TChain *chain = tagAna->QueryTags(runCuts, lhcCuts, detCuts, evCuts);
+  if (!chain || !chain->GetNtrees()) return NULL;
+  chain->ls();
+  return chain;
+}
+
+//______________________________________________________________________________
+TChain* CreateChainFromFile(const char *rootfile)
+{
+  // Create a chain using the root file.
+  TChain* chain = new TChain("esdTree");
+  chain->Add(rootfile);
+  if (!chain->GetNtrees()) return NULL;
+  chain->ls();
+  return chain;
+}
+
+//______________________________________________________________________________
+TChain* CreateChainFromESDList(const char *esdList)
+{
+  // Create a chain using tags from the run list.
+  TChain* chain = new TChain("esdTree");
+  ifstream inFile(esdList);
+  TString inFileName;
+  if (inFile.is_open()) {
+    while (! inFile.eof() ) {
+      inFileName.ReadLine(inFile,kFALSE);
+      if(!inFileName.EndsWith(".root")) continue;
+      chain->Add(inFileName.Data());
+    }
+  }
+  inFile.close();
+  if (!chain->GetNtrees()) return NULL;
+  chain->ls();
+  return chain;
+}
+
+//______________________________________________________________________________
+TChain* CreateChain(TString inputFileName)
+{
+  printf("*******************************\n");
+  printf("*** Getting the Chain       ***\n");
+  printf("*******************************\n");
+  Int_t mode = GetMode(inputFileName);
+  if(mode == kInteractif_xml) return CreateChainFromCollection(inputFileName.Data());
+  else if (mode == kInteractif_ESDList) return CreateChainFromESDList(inputFileName.Data());
+  else if (mode == kLocal) return CreateChainFromFile(inputFileName.Data());
+  else return NULL;
+}
+